| @@ -1,970 +1,968 @@ | | | @@ -1,970 +1,968 @@ |
1 | /* $NetBSD: ihidev.c,v 1.14 2021/01/25 13:30:20 thorpej Exp $ */ | | 1 | /* $NetBSD: ihidev.c,v 1.15 2021/01/26 01:15:39 thorpej Exp $ */ |
2 | /* $OpenBSD ihidev.c,v 1.13 2017/04/08 02:57:23 deraadt Exp $ */ | | 2 | /* $OpenBSD ihidev.c,v 1.13 2017/04/08 02:57:23 deraadt Exp $ */ |
3 | | | 3 | |
4 | /*- | | 4 | /*- |
5 | * Copyright (c) 2017 The NetBSD Foundation, Inc. | | 5 | * Copyright (c) 2017 The NetBSD Foundation, Inc. |
6 | * All rights reserved. | | 6 | * All rights reserved. |
7 | * | | 7 | * |
8 | * This code is derived from software contributed to The NetBSD Foundation | | 8 | * This code is derived from software contributed to The NetBSD Foundation |
9 | * by Manuel Bouyer. | | 9 | * by Manuel Bouyer. |
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 |
15 | * notice, this list of conditions and the following disclaimer. | | 15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright | | 16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the | | 17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. | | 18 | * documentation and/or other materials provided with the distribution. |
19 | * | | 19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
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 | * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> | | 34 | * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> |
35 | * | | 35 | * |
36 | * Permission to use, copy, modify, and distribute this software for any | | 36 | * Permission to use, copy, modify, and distribute this software for any |
37 | * purpose with or without fee is hereby granted, provided that the above | | 37 | * purpose with or without fee is hereby granted, provided that the above |
38 | * copyright notice and this permission notice appear in all copies. | | 38 | * copyright notice and this permission notice appear in all copies. |
39 | * | | 39 | * |
40 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | | 40 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
41 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | | 41 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
42 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | | 42 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
43 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | | 43 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
44 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | | 44 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
45 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | | 45 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
46 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | | 46 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
47 | */ | | 47 | */ |
48 | | | 48 | |
49 | /* | | 49 | /* |
50 | * HID-over-i2c driver | | 50 | * HID-over-i2c driver |
51 | * | | 51 | * |
52 | * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx | | 52 | * https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx |
53 | * | | 53 | * |
54 | */ | | 54 | */ |
55 | | | 55 | |
56 | #include <sys/cdefs.h> | | 56 | #include <sys/cdefs.h> |
57 | __KERNEL_RCSID(0, "$NetBSD: ihidev.c,v 1.14 2021/01/25 13:30:20 thorpej Exp $"); | | 57 | __KERNEL_RCSID(0, "$NetBSD: ihidev.c,v 1.15 2021/01/26 01:15:39 thorpej Exp $"); |
58 | | | 58 | |
59 | #include <sys/param.h> | | 59 | #include <sys/param.h> |
60 | #include <sys/systm.h> | | 60 | #include <sys/systm.h> |
61 | #include <sys/device.h> | | 61 | #include <sys/device.h> |
62 | #include <sys/kmem.h> | | 62 | #include <sys/kmem.h> |
63 | | | 63 | |
64 | | | 64 | |
65 | #include <dev/i2c/i2cvar.h> | | 65 | #include <dev/i2c/i2cvar.h> |
66 | #include <dev/i2c/ihidev.h> | | 66 | #include <dev/i2c/ihidev.h> |
67 | | | 67 | |
68 | #include <dev/hid/hid.h> | | 68 | #include <dev/hid/hid.h> |
69 | | | 69 | |
70 | #if defined(__i386__) || defined(__amd64__) | | 70 | #if defined(__i386__) || defined(__amd64__) |
71 | # include "acpica.h" | | 71 | # include "acpica.h" |
72 | #endif | | 72 | #endif |
73 | #if NACPICA > 0 | | 73 | #if NACPICA > 0 |
74 | #include <dev/acpi/acpivar.h> | | 74 | #include <dev/acpi/acpivar.h> |
75 | #include <dev/acpi/acpi_intr.h> | | 75 | #include <dev/acpi/acpi_intr.h> |
76 | #endif | | 76 | #endif |
77 | | | 77 | |
78 | #include "locators.h" | | 78 | #include "locators.h" |
79 | | | 79 | |
80 | /* #define IHIDEV_DEBUG */ | | 80 | /* #define IHIDEV_DEBUG */ |
81 | | | 81 | |
82 | #ifdef IHIDEV_DEBUG | | 82 | #ifdef IHIDEV_DEBUG |
83 | #define DPRINTF(x) printf x | | 83 | #define DPRINTF(x) printf x |
84 | #else | | 84 | #else |
85 | #define DPRINTF(x) | | 85 | #define DPRINTF(x) |
86 | #endif | | 86 | #endif |
87 | | | 87 | |
88 | /* 7.2 */ | | 88 | /* 7.2 */ |
89 | enum { | | 89 | enum { |
90 | I2C_HID_CMD_DESCR = 0x0, | | 90 | I2C_HID_CMD_DESCR = 0x0, |
91 | I2C_HID_CMD_RESET = 0x1, | | 91 | I2C_HID_CMD_RESET = 0x1, |
92 | I2C_HID_CMD_GET_REPORT = 0x2, | | 92 | I2C_HID_CMD_GET_REPORT = 0x2, |
93 | I2C_HID_CMD_SET_REPORT = 0x3, | | 93 | I2C_HID_CMD_SET_REPORT = 0x3, |
94 | I2C_HID_CMD_GET_IDLE = 0x4, | | 94 | I2C_HID_CMD_GET_IDLE = 0x4, |
95 | I2C_HID_CMD_SET_IDLE = 0x5, | | 95 | I2C_HID_CMD_SET_IDLE = 0x5, |
96 | I2C_HID_CMD_GET_PROTO = 0x6, | | 96 | I2C_HID_CMD_GET_PROTO = 0x6, |
97 | I2C_HID_CMD_SET_PROTO = 0x7, | | 97 | I2C_HID_CMD_SET_PROTO = 0x7, |
98 | I2C_HID_CMD_SET_POWER = 0x8, | | 98 | I2C_HID_CMD_SET_POWER = 0x8, |
99 | | | 99 | |
100 | /* pseudo commands */ | | 100 | /* pseudo commands */ |
101 | I2C_HID_REPORT_DESCR = 0x100, | | 101 | I2C_HID_REPORT_DESCR = 0x100, |
102 | }; | | 102 | }; |
103 | | | 103 | |
104 | static int I2C_HID_POWER_ON = 0x0; | | 104 | static int I2C_HID_POWER_ON = 0x0; |
105 | static int I2C_HID_POWER_OFF = 0x1; | | 105 | static int I2C_HID_POWER_OFF = 0x1; |
106 | | | 106 | |
107 | static int ihidev_match(device_t, cfdata_t, void *); | | 107 | static int ihidev_match(device_t, cfdata_t, void *); |
108 | static void ihidev_attach(device_t, device_t, void *); | | 108 | static void ihidev_attach(device_t, device_t, void *); |
109 | static int ihidev_detach(device_t, int); | | 109 | static int ihidev_detach(device_t, int); |
110 | CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc), | | 110 | CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc), |
111 | ihidev_match, ihidev_attach, ihidev_detach, NULL); | | 111 | ihidev_match, ihidev_attach, ihidev_detach, NULL); |
112 | | | 112 | |
113 | static bool ihiddev_intr_init(struct ihidev_softc *); | | 113 | static bool ihiddev_intr_init(struct ihidev_softc *); |
114 | static void ihiddev_intr_fini(struct ihidev_softc *); | | 114 | static void ihiddev_intr_fini(struct ihidev_softc *); |
115 | | | 115 | |
116 | static bool ihidev_suspend(device_t, const pmf_qual_t *); | | 116 | static bool ihidev_suspend(device_t, const pmf_qual_t *); |
117 | static bool ihidev_resume(device_t, const pmf_qual_t *); | | 117 | static bool ihidev_resume(device_t, const pmf_qual_t *); |
118 | static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool); | | 118 | static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool); |
119 | static int ihidev_intr(void *); | | 119 | static int ihidev_intr(void *); |
120 | static void ihidev_softintr(void *); | | 120 | static void ihidev_softintr(void *); |
121 | static int ihidev_reset(struct ihidev_softc *, bool); | | 121 | static int ihidev_reset(struct ihidev_softc *, bool); |
122 | static int ihidev_hid_desc_parse(struct ihidev_softc *); | | 122 | static int ihidev_hid_desc_parse(struct ihidev_softc *); |
123 | | | 123 | |
124 | static int ihidev_maxrepid(void *, int); | | 124 | static int ihidev_maxrepid(void *, int); |
125 | static int ihidev_print(void *, const char *); | | 125 | static int ihidev_print(void *, const char *); |
126 | static int ihidev_submatch(device_t, cfdata_t, const int *, void *); | | 126 | static int ihidev_submatch(device_t, cfdata_t, const int *, void *); |
127 | | | 127 | |
128 | static const struct device_compatible_entry compat_data[] = { | | 128 | static const struct device_compatible_entry compat_data[] = { |
129 | { .compat = "PNP0C50" }, | | | |
130 | { .compat = "ACPI0C50" }, | | | |
131 | { .compat = "hid-over-i2c" }, | | 129 | { .compat = "hid-over-i2c" }, |
132 | { } | | 130 | { } |
133 | }; | | 131 | }; |
134 | | | 132 | |
135 | static int | | 133 | static int |
136 | ihidev_match(device_t parent, cfdata_t match, void *aux) | | 134 | ihidev_match(device_t parent, cfdata_t match, void *aux) |
137 | { | | 135 | { |
138 | struct i2c_attach_args * const ia = aux; | | 136 | struct i2c_attach_args * const ia = aux; |
139 | int match_result; | | 137 | int match_result; |
140 | | | 138 | |
141 | if (iic_use_direct_match(ia, match, compat_data, &match_result)) | | 139 | if (iic_use_direct_match(ia, match, compat_data, &match_result)) |
142 | return I2C_MATCH_DIRECT_COMPATIBLE; | | 140 | return I2C_MATCH_DIRECT_COMPATIBLE; |
143 | | | 141 | |
144 | return 0; | | 142 | return 0; |
145 | } | | 143 | } |
146 | | | 144 | |
147 | static void | | 145 | static void |
148 | ihidev_attach(device_t parent, device_t self, void *aux) | | 146 | ihidev_attach(device_t parent, device_t self, void *aux) |
149 | { | | 147 | { |
150 | struct ihidev_softc *sc = device_private(self); | | 148 | struct ihidev_softc *sc = device_private(self); |
151 | struct i2c_attach_args *ia = aux; | | 149 | struct i2c_attach_args *ia = aux; |
152 | struct ihidev_attach_arg iha; | | 150 | struct ihidev_attach_arg iha; |
153 | device_t dev; | | 151 | device_t dev; |
154 | int repid, repsz; | | 152 | int repid, repsz; |
155 | int isize; | | 153 | int isize; |
156 | uint32_t v; | | 154 | uint32_t v; |
157 | int locs[IHIDBUSCF_NLOCS]; | | 155 | int locs[IHIDBUSCF_NLOCS]; |
158 | | | 156 | |
159 | | | 157 | |
160 | sc->sc_dev = self; | | 158 | sc->sc_dev = self; |
161 | sc->sc_tag = ia->ia_tag; | | 159 | sc->sc_tag = ia->ia_tag; |
162 | sc->sc_addr = ia->ia_addr; | | 160 | sc->sc_addr = ia->ia_addr; |
163 | sc->sc_phandle = ia->ia_cookie; | | 161 | sc->sc_phandle = ia->ia_cookie; |
164 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); | | 162 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); |
165 | | | 163 | |
166 | if (!prop_dictionary_get_uint32(ia->ia_prop, "hid-descr-addr", &v)) { | | 164 | if (!prop_dictionary_get_uint32(ia->ia_prop, "hid-descr-addr", &v)) { |
167 | aprint_error(": no hid-descr-addr value\n"); | | 165 | aprint_error(": no hid-descr-addr value\n"); |
168 | return; | | 166 | return; |
169 | } | | 167 | } |
170 | | | 168 | |
171 | sc->sc_hid_desc_addr = v; | | 169 | sc->sc_hid_desc_addr = v; |
172 | | | 170 | |
173 | if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL, false) || | | 171 | if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL, false) || |
174 | ihidev_hid_desc_parse(sc)) { | | 172 | ihidev_hid_desc_parse(sc)) { |
175 | aprint_error(": failed fetching initial HID descriptor\n"); | | 173 | aprint_error(": failed fetching initial HID descriptor\n"); |
176 | return; | | 174 | return; |
177 | } | | 175 | } |
178 | | | 176 | |
179 | aprint_naive("\n"); | | 177 | aprint_naive("\n"); |
180 | aprint_normal(": vendor 0x%x product 0x%x, %s\n", | | 178 | aprint_normal(": vendor 0x%x product 0x%x, %s\n", |
181 | le16toh(sc->hid_desc.wVendorID), le16toh(sc->hid_desc.wProductID), | | 179 | le16toh(sc->hid_desc.wVendorID), le16toh(sc->hid_desc.wProductID), |
182 | ia->ia_name); | | 180 | ia->ia_name); |
183 | | | 181 | |
184 | sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen); | | 182 | sc->sc_nrepid = ihidev_maxrepid(sc->sc_report, sc->sc_reportlen); |
185 | if (sc->sc_nrepid < 0) | | 183 | if (sc->sc_nrepid < 0) |
186 | return; | | 184 | return; |
187 | | | 185 | |
188 | aprint_normal_dev(self, "%d report id%s\n", sc->sc_nrepid, | | 186 | aprint_normal_dev(self, "%d report id%s\n", sc->sc_nrepid, |
189 | sc->sc_nrepid > 1 ? "s" : ""); | | 187 | sc->sc_nrepid > 1 ? "s" : ""); |
190 | | | 188 | |
191 | sc->sc_nrepid++; | | 189 | sc->sc_nrepid++; |
192 | sc->sc_subdevs = kmem_zalloc(sc->sc_nrepid * sizeof(struct ihidev *), | | 190 | sc->sc_subdevs = kmem_zalloc(sc->sc_nrepid * sizeof(struct ihidev *), |
193 | KM_SLEEP); | | 191 | KM_SLEEP); |
194 | | | 192 | |
195 | /* find largest report size and allocate memory for input buffer */ | | 193 | /* find largest report size and allocate memory for input buffer */ |
196 | sc->sc_isize = le16toh(sc->hid_desc.wMaxInputLength); | | 194 | sc->sc_isize = le16toh(sc->hid_desc.wMaxInputLength); |
197 | for (repid = 0; repid < sc->sc_nrepid; repid++) { | | 195 | for (repid = 0; repid < sc->sc_nrepid; repid++) { |
198 | repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, | | 196 | repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, |
199 | hid_input, repid); | | 197 | hid_input, repid); |
200 | | | 198 | |
201 | isize = repsz + 2; /* two bytes for the length */ | | 199 | isize = repsz + 2; /* two bytes for the length */ |
202 | isize += (sc->sc_nrepid != 1); /* one byte for the report ID */ | | 200 | isize += (sc->sc_nrepid != 1); /* one byte for the report ID */ |
203 | if (isize > sc->sc_isize) | | 201 | if (isize > sc->sc_isize) |
204 | sc->sc_isize = isize; | | 202 | sc->sc_isize = isize; |
205 | | | 203 | |
206 | DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid, | | 204 | DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid, |
207 | repsz)); | | 205 | repsz)); |
208 | } | | 206 | } |
209 | sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); | | 207 | sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); |
210 | if (! ihiddev_intr_init(sc)) { | | 208 | if (! ihiddev_intr_init(sc)) { |
211 | return; | | 209 | return; |
212 | } | | 210 | } |
213 | | | 211 | |
214 | iha.iaa = ia; | | 212 | iha.iaa = ia; |
215 | iha.parent = sc; | | 213 | iha.parent = sc; |
216 | | | 214 | |
217 | /* Look for a driver claiming all report IDs first. */ | | 215 | /* Look for a driver claiming all report IDs first. */ |
218 | iha.reportid = IHIDEV_CLAIM_ALLREPORTID; | | 216 | iha.reportid = IHIDEV_CLAIM_ALLREPORTID; |
219 | locs[IHIDBUSCF_REPORTID] = IHIDEV_CLAIM_ALLREPORTID; | | 217 | locs[IHIDBUSCF_REPORTID] = IHIDEV_CLAIM_ALLREPORTID; |
220 | dev = config_found_sm_loc(self, "ihidbus", locs, &iha, | | 218 | dev = config_found_sm_loc(self, "ihidbus", locs, &iha, |
221 | ihidev_print, ihidev_submatch); | | 219 | ihidev_print, ihidev_submatch); |
222 | if (dev != NULL) { | | 220 | if (dev != NULL) { |
223 | for (repid = 0; repid < sc->sc_nrepid; repid++) | | 221 | for (repid = 0; repid < sc->sc_nrepid; repid++) |
224 | sc->sc_subdevs[repid] = device_private(dev); | | 222 | sc->sc_subdevs[repid] = device_private(dev); |
225 | return; | | 223 | return; |
226 | } | | 224 | } |
227 | | | 225 | |
228 | for (repid = 0; repid < sc->sc_nrepid; repid++) { | | 226 | for (repid = 0; repid < sc->sc_nrepid; repid++) { |
229 | if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input, | | 227 | if (hid_report_size(sc->sc_report, sc->sc_reportlen, hid_input, |
230 | repid) == 0 && | | 228 | repid) == 0 && |
231 | hid_report_size(sc->sc_report, sc->sc_reportlen, | | 229 | hid_report_size(sc->sc_report, sc->sc_reportlen, |
232 | hid_output, repid) == 0 && | | 230 | hid_output, repid) == 0 && |
233 | hid_report_size(sc->sc_report, sc->sc_reportlen, | | 231 | hid_report_size(sc->sc_report, sc->sc_reportlen, |
234 | hid_feature, repid) == 0) | | 232 | hid_feature, repid) == 0) |
235 | continue; | | 233 | continue; |
236 | | | 234 | |
237 | iha.reportid = repid; | | 235 | iha.reportid = repid; |
238 | locs[IHIDBUSCF_REPORTID] = repid; | | 236 | locs[IHIDBUSCF_REPORTID] = repid; |
239 | dev = config_found_sm_loc(self, "ihidbus", locs, | | 237 | dev = config_found_sm_loc(self, "ihidbus", locs, |
240 | &iha, ihidev_print, ihidev_submatch); | | 238 | &iha, ihidev_print, ihidev_submatch); |
241 | sc->sc_subdevs[repid] = device_private(dev); | | 239 | sc->sc_subdevs[repid] = device_private(dev); |
242 | } | | 240 | } |
243 | | | 241 | |
244 | /* power down until we're opened */ | | 242 | /* power down until we're opened */ |
245 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, false)) { | | 243 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, false)) { |
246 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 244 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
247 | return; | | 245 | return; |
248 | } | | 246 | } |
249 | if (!pmf_device_register(self, ihidev_suspend, ihidev_resume)) | | 247 | if (!pmf_device_register(self, ihidev_suspend, ihidev_resume)) |
250 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 248 | aprint_error_dev(self, "couldn't establish power handler\n"); |
251 | } | | 249 | } |
252 | | | 250 | |
253 | static int | | 251 | static int |
254 | ihidev_detach(device_t self, int flags) | | 252 | ihidev_detach(device_t self, int flags) |
255 | { | | 253 | { |
256 | struct ihidev_softc *sc = device_private(self); | | 254 | struct ihidev_softc *sc = device_private(self); |
257 | | | 255 | |
258 | mutex_enter(&sc->sc_intr_lock); | | 256 | mutex_enter(&sc->sc_intr_lock); |
259 | ihiddev_intr_fini(sc); | | 257 | ihiddev_intr_fini(sc); |
260 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 258 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
261 | &I2C_HID_POWER_OFF, true)) | | 259 | &I2C_HID_POWER_OFF, true)) |
262 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 260 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
263 | mutex_exit(&sc->sc_intr_lock); | | 261 | mutex_exit(&sc->sc_intr_lock); |
264 | if (sc->sc_ibuf != NULL) { | | 262 | if (sc->sc_ibuf != NULL) { |
265 | kmem_free(sc->sc_ibuf, sc->sc_isize); | | 263 | kmem_free(sc->sc_ibuf, sc->sc_isize); |
266 | sc->sc_ibuf = NULL; | | 264 | sc->sc_ibuf = NULL; |
267 | } | | 265 | } |
268 | | | 266 | |
269 | if (sc->sc_report != NULL) | | 267 | if (sc->sc_report != NULL) |
270 | kmem_free(sc->sc_report, sc->sc_reportlen); | | 268 | kmem_free(sc->sc_report, sc->sc_reportlen); |
271 | | | 269 | |
272 | pmf_device_deregister(self); | | 270 | pmf_device_deregister(self); |
273 | return (0); | | 271 | return (0); |
274 | } | | 272 | } |
275 | | | 273 | |
276 | static bool | | 274 | static bool |
277 | ihidev_suspend(device_t self, const pmf_qual_t *q) | | 275 | ihidev_suspend(device_t self, const pmf_qual_t *q) |
278 | { | | 276 | { |
279 | struct ihidev_softc *sc = device_private(self); | | 277 | struct ihidev_softc *sc = device_private(self); |
280 | | | 278 | |
281 | mutex_enter(&sc->sc_intr_lock); | | 279 | mutex_enter(&sc->sc_intr_lock); |
282 | if (sc->sc_refcnt > 0) { | | 280 | if (sc->sc_refcnt > 0) { |
283 | printf("ihidev power off\n"); | | 281 | printf("ihidev power off\n"); |
284 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 282 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
285 | &I2C_HID_POWER_OFF, true)) | | 283 | &I2C_HID_POWER_OFF, true)) |
286 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 284 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
287 | } | | 285 | } |
288 | mutex_exit(&sc->sc_intr_lock); | | 286 | mutex_exit(&sc->sc_intr_lock); |
289 | return true; | | 287 | return true; |
290 | } | | 288 | } |
291 | | | 289 | |
292 | static bool | | 290 | static bool |
293 | ihidev_resume(device_t self, const pmf_qual_t *q) | | 291 | ihidev_resume(device_t self, const pmf_qual_t *q) |
294 | { | | 292 | { |
295 | struct ihidev_softc *sc = device_private(self); | | 293 | struct ihidev_softc *sc = device_private(self); |
296 | | | 294 | |
297 | mutex_enter(&sc->sc_intr_lock); | | 295 | mutex_enter(&sc->sc_intr_lock); |
298 | if (sc->sc_refcnt > 0) { | | 296 | if (sc->sc_refcnt > 0) { |
299 | printf("ihidev power reset\n"); | | 297 | printf("ihidev power reset\n"); |
300 | ihidev_reset(sc, true); | | 298 | ihidev_reset(sc, true); |
301 | } | | 299 | } |
302 | mutex_exit(&sc->sc_intr_lock); | | 300 | mutex_exit(&sc->sc_intr_lock); |
303 | return true; | | 301 | return true; |
304 | } | | 302 | } |
305 | | | 303 | |
306 | static int | | 304 | static int |
307 | ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg, bool poll) | | 305 | ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg, bool poll) |
308 | { | | 306 | { |
309 | int i, res = 1; | | 307 | int i, res = 1; |
310 | int flags = poll ? I2C_F_POLL : 0; | | 308 | int flags = poll ? I2C_F_POLL : 0; |
311 | | | 309 | |
312 | iic_acquire_bus(sc->sc_tag, flags); | | 310 | iic_acquire_bus(sc->sc_tag, flags); |
313 | | | 311 | |
314 | switch (hidcmd) { | | 312 | switch (hidcmd) { |
315 | case I2C_HID_CMD_DESCR: { | | 313 | case I2C_HID_CMD_DESCR: { |
316 | /* | | 314 | /* |
317 | * 5.2.2 - HID Descriptor Retrieval | | 315 | * 5.2.2 - HID Descriptor Retrieval |
318 | * register is passed from the controller | | 316 | * register is passed from the controller |
319 | */ | | 317 | */ |
320 | uint8_t cmd[] = { | | 318 | uint8_t cmd[] = { |
321 | htole16(sc->sc_hid_desc_addr) & 0xff, | | 319 | htole16(sc->sc_hid_desc_addr) & 0xff, |
322 | htole16(sc->sc_hid_desc_addr) >> 8, | | 320 | htole16(sc->sc_hid_desc_addr) >> 8, |
323 | }; | | 321 | }; |
324 | | | 322 | |
325 | DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n", | | 323 | DPRINTF(("%s: HID command I2C_HID_CMD_DESCR at 0x%x\n", |
326 | sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr))); | | 324 | sc->sc_dev.dv_xname, htole16(sc->sc_hid_desc_addr))); |
327 | | | 325 | |
328 | /* 20 00 */ | | 326 | /* 20 00 */ |
329 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, | | 327 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
330 | &cmd, sizeof(cmd), &sc->hid_desc_buf, | | 328 | &cmd, sizeof(cmd), &sc->hid_desc_buf, |
331 | sizeof(struct i2c_hid_desc), flags); | | 329 | sizeof(struct i2c_hid_desc), flags); |
332 | | | 330 | |
333 | DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); | | 331 | DPRINTF(("%s: HID descriptor:", sc->sc_dev.dv_xname)); |
334 | for (i = 0; i < sizeof(struct i2c_hid_desc); i++) | | 332 | for (i = 0; i < sizeof(struct i2c_hid_desc); i++) |
335 | DPRINTF((" %.2x", sc->hid_desc_buf[i])); | | 333 | DPRINTF((" %.2x", sc->hid_desc_buf[i])); |
336 | DPRINTF(("\n")); | | 334 | DPRINTF(("\n")); |
337 | | | 335 | |
338 | break; | | 336 | break; |
339 | } | | 337 | } |
340 | case I2C_HID_CMD_RESET: { | | 338 | case I2C_HID_CMD_RESET: { |
341 | uint8_t cmd[] = { | | 339 | uint8_t cmd[] = { |
342 | htole16(sc->hid_desc.wCommandRegister) & 0xff, | | 340 | htole16(sc->hid_desc.wCommandRegister) & 0xff, |
343 | htole16(sc->hid_desc.wCommandRegister) >> 8, | | 341 | htole16(sc->hid_desc.wCommandRegister) >> 8, |
344 | 0, | | 342 | 0, |
345 | I2C_HID_CMD_RESET, | | 343 | I2C_HID_CMD_RESET, |
346 | }; | | 344 | }; |
347 | | | 345 | |
348 | DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", | | 346 | DPRINTF(("%s: HID command I2C_HID_CMD_RESET\n", |
349 | sc->sc_dev.dv_xname)); | | 347 | sc->sc_dev.dv_xname)); |
350 | | | 348 | |
351 | /* 22 00 00 01 */ | | 349 | /* 22 00 00 01 */ |
352 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, | | 350 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
353 | &cmd, sizeof(cmd), NULL, 0, flags); | | 351 | &cmd, sizeof(cmd), NULL, 0, flags); |
354 | | | 352 | |
355 | break; | | 353 | break; |
356 | } | | 354 | } |
357 | case I2C_HID_CMD_GET_REPORT: { | | 355 | case I2C_HID_CMD_GET_REPORT: { |
358 | struct i2c_hid_report_request *rreq = | | 356 | struct i2c_hid_report_request *rreq = |
359 | (struct i2c_hid_report_request *)arg; | | 357 | (struct i2c_hid_report_request *)arg; |
360 | | | 358 | |
361 | uint8_t cmd[] = { | | 359 | uint8_t cmd[] = { |
362 | htole16(sc->hid_desc.wCommandRegister) & 0xff, | | 360 | htole16(sc->hid_desc.wCommandRegister) & 0xff, |
363 | htole16(sc->hid_desc.wCommandRegister) >> 8, | | 361 | htole16(sc->hid_desc.wCommandRegister) >> 8, |
364 | 0, | | 362 | 0, |
365 | I2C_HID_CMD_GET_REPORT, | | 363 | I2C_HID_CMD_GET_REPORT, |
366 | 0, 0, 0, | | 364 | 0, 0, 0, |
367 | }; | | 365 | }; |
368 | int cmdlen = 7; | | 366 | int cmdlen = 7; |
369 | int dataoff = 4; | | 367 | int dataoff = 4; |
370 | int report_id = rreq->id; | | 368 | int report_id = rreq->id; |
371 | int report_id_len = 1; | | 369 | int report_id_len = 1; |
372 | int report_len = rreq->len + 2; | | 370 | int report_len = rreq->len + 2; |
373 | int d; | | 371 | int d; |
374 | uint8_t *tmprep; | | 372 | uint8_t *tmprep; |
375 | | | 373 | |
376 | DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d " | | 374 | DPRINTF(("%s: HID command I2C_HID_CMD_GET_REPORT %d " |
377 | "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id, | | 375 | "(type %d, len %d)\n", sc->sc_dev.dv_xname, report_id, |
378 | rreq->type, rreq->len)); | | 376 | rreq->type, rreq->len)); |
379 | | | 377 | |
380 | /* | | 378 | /* |
381 | * 7.2.2.4 - "The protocol is optimized for Report < 15. If a | | 379 | * 7.2.2.4 - "The protocol is optimized for Report < 15. If a |
382 | * report ID >= 15 is necessary, then the Report ID in the Low | | 380 | * report ID >= 15 is necessary, then the Report ID in the Low |
383 | * Byte must be set to 1111 and a Third Byte is appended to the | | 381 | * Byte must be set to 1111 and a Third Byte is appended to the |
384 | * protocol. This Third Byte contains the entire/actual report | | 382 | * protocol. This Third Byte contains the entire/actual report |
385 | * ID." | | 383 | * ID." |
386 | */ | | 384 | */ |
387 | if (report_id >= 15) { | | 385 | if (report_id >= 15) { |
388 | cmd[dataoff++] = report_id; | | 386 | cmd[dataoff++] = report_id; |
389 | report_id = 15; | | 387 | report_id = 15; |
390 | report_id_len = 2; | | 388 | report_id_len = 2; |
391 | } else | | 389 | } else |
392 | cmdlen--; | | 390 | cmdlen--; |
393 | | | 391 | |
394 | cmd[2] = report_id | rreq->type << 4; | | 392 | cmd[2] = report_id | rreq->type << 4; |
395 | | | 393 | |
396 | cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff; | | 394 | cmd[dataoff++] = sc->hid_desc.wDataRegister & 0xff; |
397 | cmd[dataoff] = sc->hid_desc.wDataRegister >> 8; | | 395 | cmd[dataoff] = sc->hid_desc.wDataRegister >> 8; |
398 | | | 396 | |
399 | /* | | 397 | /* |
400 | * 7.2.2.2 - Response will be a 2-byte length value, the report | | 398 | * 7.2.2.2 - Response will be a 2-byte length value, the report |
401 | * id with length determined above, and then the report. | | 399 | * id with length determined above, and then the report. |
402 | * Allocate rreq->len + 2 + 2 bytes, read into that temporary | | 400 | * Allocate rreq->len + 2 + 2 bytes, read into that temporary |
403 | * buffer, and then copy only the report back out to | | 401 | * buffer, and then copy only the report back out to |
404 | * rreq->data. | | 402 | * rreq->data. |
405 | */ | | 403 | */ |
406 | report_len += report_id_len; | | 404 | report_len += report_id_len; |
407 | tmprep = kmem_zalloc(report_len, KM_NOSLEEP); | | 405 | tmprep = kmem_zalloc(report_len, KM_NOSLEEP); |
408 | | | 406 | |
409 | /* type 3 id 8: 22 00 38 02 23 00 */ | | 407 | /* type 3 id 8: 22 00 38 02 23 00 */ |
410 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, | | 408 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
411 | &cmd, cmdlen, tmprep, report_len, flags); | | 409 | &cmd, cmdlen, tmprep, report_len, flags); |
412 | | | 410 | |
413 | d = tmprep[0] | tmprep[1] << 8; | | 411 | d = tmprep[0] | tmprep[1] << 8; |
414 | if (d != report_len) { | | 412 | if (d != report_len) { |
415 | DPRINTF(("%s: response size %d != expected length %d\n", | | 413 | DPRINTF(("%s: response size %d != expected length %d\n", |
416 | sc->sc_dev.dv_xname, d, report_len)); | | 414 | sc->sc_dev.dv_xname, d, report_len)); |
417 | } | | 415 | } |
418 | | | 416 | |
419 | if (report_id_len == 2) | | 417 | if (report_id_len == 2) |
420 | d = tmprep[2] | tmprep[3] << 8; | | 418 | d = tmprep[2] | tmprep[3] << 8; |
421 | else | | 419 | else |
422 | d = tmprep[2]; | | 420 | d = tmprep[2]; |
423 | | | 421 | |
424 | if (d != rreq->id) { | | 422 | if (d != rreq->id) { |
425 | DPRINTF(("%s: response report id %d != %d\n", | | 423 | DPRINTF(("%s: response report id %d != %d\n", |
426 | sc->sc_dev.dv_xname, d, rreq->id)); | | 424 | sc->sc_dev.dv_xname, d, rreq->id)); |
427 | iic_release_bus(sc->sc_tag, 0); | | 425 | iic_release_bus(sc->sc_tag, 0); |
428 | kmem_free(tmprep, report_len); | | 426 | kmem_free(tmprep, report_len); |
429 | return (1); | | 427 | return (1); |
430 | } | | 428 | } |
431 | | | 429 | |
432 | DPRINTF(("%s: response:", sc->sc_dev.dv_xname)); | | 430 | DPRINTF(("%s: response:", sc->sc_dev.dv_xname)); |
433 | for (i = 0; i < report_len; i++) | | 431 | for (i = 0; i < report_len; i++) |
434 | DPRINTF((" %.2x", tmprep[i])); | | 432 | DPRINTF((" %.2x", tmprep[i])); |
435 | DPRINTF(("\n")); | | 433 | DPRINTF(("\n")); |
436 | | | 434 | |
437 | memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len); | | 435 | memcpy(rreq->data, tmprep + 2 + report_id_len, rreq->len); |
438 | kmem_free(tmprep, report_len); | | 436 | kmem_free(tmprep, report_len); |
439 | | | 437 | |
440 | break; | | 438 | break; |
441 | } | | 439 | } |
442 | case I2C_HID_CMD_SET_REPORT: { | | 440 | case I2C_HID_CMD_SET_REPORT: { |
443 | struct i2c_hid_report_request *rreq = | | 441 | struct i2c_hid_report_request *rreq = |
444 | (struct i2c_hid_report_request *)arg; | | 442 | (struct i2c_hid_report_request *)arg; |
445 | | | 443 | |
446 | uint8_t cmd[] = { | | 444 | uint8_t cmd[] = { |
447 | htole16(sc->hid_desc.wCommandRegister) & 0xff, | | 445 | htole16(sc->hid_desc.wCommandRegister) & 0xff, |
448 | htole16(sc->hid_desc.wCommandRegister) >> 8, | | 446 | htole16(sc->hid_desc.wCommandRegister) >> 8, |
449 | 0, | | 447 | 0, |
450 | I2C_HID_CMD_SET_REPORT, | | 448 | I2C_HID_CMD_SET_REPORT, |
451 | 0, 0, 0, 0, 0, 0, | | 449 | 0, 0, 0, 0, 0, 0, |
452 | }; | | 450 | }; |
453 | int cmdlen = 10; | | 451 | int cmdlen = 10; |
454 | int report_id = rreq->id; | | 452 | int report_id = rreq->id; |
455 | int report_len = 2 + (report_id ? 1 : 0) + rreq->len; | | 453 | int report_len = 2 + (report_id ? 1 : 0) + rreq->len; |
456 | int dataoff; | | 454 | int dataoff; |
457 | uint8_t *finalcmd; | | 455 | uint8_t *finalcmd; |
458 | | | 456 | |
459 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d " | | 457 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_REPORT %d " |
460 | "(type %d, len %d):", sc->sc_dev.dv_xname, report_id, | | 458 | "(type %d, len %d):", sc->sc_dev.dv_xname, report_id, |
461 | rreq->type, rreq->len)); | | 459 | rreq->type, rreq->len)); |
462 | for (i = 0; i < rreq->len; i++) | | 460 | for (i = 0; i < rreq->len; i++) |
463 | DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i])); | | 461 | DPRINTF((" %.2x", ((uint8_t *)rreq->data)[i])); |
464 | DPRINTF(("\n")); | | 462 | DPRINTF(("\n")); |
465 | | | 463 | |
466 | /* | | 464 | /* |
467 | * 7.2.2.4 - "The protocol is optimized for Report < 15. If a | | 465 | * 7.2.2.4 - "The protocol is optimized for Report < 15. If a |
468 | * report ID >= 15 is necessary, then the Report ID in the Low | | 466 | * report ID >= 15 is necessary, then the Report ID in the Low |
469 | * Byte must be set to 1111 and a Third Byte is appended to the | | 467 | * Byte must be set to 1111 and a Third Byte is appended to the |
470 | * protocol. This Third Byte contains the entire/actual report | | 468 | * protocol. This Third Byte contains the entire/actual report |
471 | * ID." | | 469 | * ID." |
472 | */ | | 470 | */ |
473 | dataoff = 4; | | 471 | dataoff = 4; |
474 | if (report_id >= 15) { | | 472 | if (report_id >= 15) { |
475 | cmd[dataoff++] = report_id; | | 473 | cmd[dataoff++] = report_id; |
476 | report_id = 15; | | 474 | report_id = 15; |
477 | } else | | 475 | } else |
478 | cmdlen--; | | 476 | cmdlen--; |
479 | | | 477 | |
480 | cmd[2] = report_id | rreq->type << 4; | | 478 | cmd[2] = report_id | rreq->type << 4; |
481 | | | 479 | |
482 | if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE) { | | 480 | if (rreq->type == I2C_HID_REPORT_TYPE_FEATURE) { |
483 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) | | 481 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) |
484 | & 0xff; | | 482 | & 0xff; |
485 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) | | 483 | cmd[dataoff++] = htole16(sc->hid_desc.wDataRegister) |
486 | >> 8; | | 484 | >> 8; |
487 | } else { | | 485 | } else { |
488 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) | | 486 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) |
489 | & 0xff; | | 487 | & 0xff; |
490 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) | | 488 | cmd[dataoff++] = htole16(sc->hid_desc.wOutputRegister) |
491 | >> 8; | | 489 | >> 8; |
492 | } | | 490 | } |
493 | | | 491 | |
494 | cmd[dataoff++] = report_len & 0xff; | | 492 | cmd[dataoff++] = report_len & 0xff; |
495 | cmd[dataoff++] = report_len >> 8; | | 493 | cmd[dataoff++] = report_len >> 8; |
496 | cmd[dataoff] = rreq->id; | | 494 | cmd[dataoff] = rreq->id; |
497 | | | 495 | |
498 | finalcmd = kmem_zalloc(cmdlen + rreq->len, KM_NOSLEEP); | | 496 | finalcmd = kmem_zalloc(cmdlen + rreq->len, KM_NOSLEEP); |
499 | | | 497 | |
500 | memcpy(finalcmd, cmd, cmdlen); | | 498 | memcpy(finalcmd, cmd, cmdlen); |
501 | memcpy(finalcmd + cmdlen, rreq->data, rreq->len); | | 499 | memcpy(finalcmd + cmdlen, rreq->data, rreq->len); |
502 | | | 500 | |
503 | /* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */ | | 501 | /* type 3 id 4: 22 00 34 03 23 00 04 00 04 03 */ |
504 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, | | 502 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
505 | finalcmd, cmdlen + rreq->len, NULL, 0, flags); | | 503 | finalcmd, cmdlen + rreq->len, NULL, 0, flags); |
506 | kmem_free(finalcmd, cmdlen + rreq->len); | | 504 | kmem_free(finalcmd, cmdlen + rreq->len); |
507 | | | 505 | |
508 | break; | | 506 | break; |
509 | } | | 507 | } |
510 | | | 508 | |
511 | case I2C_HID_CMD_SET_POWER: { | | 509 | case I2C_HID_CMD_SET_POWER: { |
512 | int power = *(int *)arg; | | 510 | int power = *(int *)arg; |
513 | uint8_t cmd[] = { | | 511 | uint8_t cmd[] = { |
514 | htole16(sc->hid_desc.wCommandRegister) & 0xff, | | 512 | htole16(sc->hid_desc.wCommandRegister) & 0xff, |
515 | htole16(sc->hid_desc.wCommandRegister) >> 8, | | 513 | htole16(sc->hid_desc.wCommandRegister) >> 8, |
516 | power, | | 514 | power, |
517 | I2C_HID_CMD_SET_POWER, | | 515 | I2C_HID_CMD_SET_POWER, |
518 | }; | | 516 | }; |
519 | | | 517 | |
520 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", | | 518 | DPRINTF(("%s: HID command I2C_HID_CMD_SET_POWER(%d)\n", |
521 | sc->sc_dev.dv_xname, power)); | | 519 | sc->sc_dev.dv_xname, power)); |
522 | | | 520 | |
523 | /* 22 00 00 08 */ | | 521 | /* 22 00 00 08 */ |
524 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, | | 522 | res = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, |
525 | &cmd, sizeof(cmd), NULL, 0, flags); | | 523 | &cmd, sizeof(cmd), NULL, 0, flags); |
526 | | | 524 | |
527 | break; | | 525 | break; |
528 | } | | 526 | } |
529 | case I2C_HID_REPORT_DESCR: { | | 527 | case I2C_HID_REPORT_DESCR: { |
530 | uint8_t cmd[] = { | | 528 | uint8_t cmd[] = { |
531 | htole16(sc->hid_desc.wReportDescRegister) & 0xff, | | 529 | htole16(sc->hid_desc.wReportDescRegister) & 0xff, |
532 | htole16(sc->hid_desc.wReportDescRegister) >> 8, | | 530 | htole16(sc->hid_desc.wReportDescRegister) >> 8, |
533 | }; | | 531 | }; |
534 | | | 532 | |
535 | DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with " | | 533 | DPRINTF(("%s: HID command I2C_HID_REPORT_DESCR at 0x%x with " |
536 | "size %d\n", sc->sc_dev.dv_xname, cmd[0], | | 534 | "size %d\n", sc->sc_dev.dv_xname, cmd[0], |
537 | sc->sc_reportlen)); | | 535 | sc->sc_reportlen)); |
538 | | | 536 | |
539 | /* 20 00 */ | | 537 | /* 20 00 */ |
540 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, | | 538 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, |
541 | &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, flags); | | 539 | &cmd, sizeof(cmd), sc->sc_report, sc->sc_reportlen, flags); |
542 | | | 540 | |
543 | DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname)); | | 541 | DPRINTF(("%s: HID report descriptor:", sc->sc_dev.dv_xname)); |
544 | for (i = 0; i < sc->sc_reportlen; i++) | | 542 | for (i = 0; i < sc->sc_reportlen; i++) |
545 | DPRINTF((" %.2x", sc->sc_report[i])); | | 543 | DPRINTF((" %.2x", sc->sc_report[i])); |
546 | DPRINTF(("\n")); | | 544 | DPRINTF(("\n")); |
547 | | | 545 | |
548 | break; | | 546 | break; |
549 | } | | 547 | } |
550 | default: | | 548 | default: |
551 | aprint_error_dev(sc->sc_dev, "unknown command %d\n", | | 549 | aprint_error_dev(sc->sc_dev, "unknown command %d\n", |
552 | hidcmd); | | 550 | hidcmd); |
553 | } | | 551 | } |
554 | | | 552 | |
555 | iic_release_bus(sc->sc_tag, flags); | | 553 | iic_release_bus(sc->sc_tag, flags); |
556 | | | 554 | |
557 | return (res); | | 555 | return (res); |
558 | } | | 556 | } |
559 | | | 557 | |
560 | static int | | 558 | static int |
561 | ihidev_reset(struct ihidev_softc *sc, bool poll) | | 559 | ihidev_reset(struct ihidev_softc *sc, bool poll) |
562 | { | | 560 | { |
563 | DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); | | 561 | DPRINTF(("%s: resetting\n", sc->sc_dev.dv_xname)); |
564 | | | 562 | |
565 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 563 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
566 | &I2C_HID_POWER_ON, poll)) { | | 564 | &I2C_HID_POWER_ON, poll)) { |
567 | aprint_error_dev(sc->sc_dev, "failed to power on\n"); | | 565 | aprint_error_dev(sc->sc_dev, "failed to power on\n"); |
568 | return (1); | | 566 | return (1); |
569 | } | | 567 | } |
570 | | | 568 | |
571 | DELAY(1000); | | 569 | DELAY(1000); |
572 | | | 570 | |
573 | if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0, poll)) { | | 571 | if (ihidev_hid_command(sc, I2C_HID_CMD_RESET, 0, poll)) { |
574 | aprint_error_dev(sc->sc_dev, "failed to reset hardware\n"); | | 572 | aprint_error_dev(sc->sc_dev, "failed to reset hardware\n"); |
575 | | | 573 | |
576 | ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 574 | ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
577 | &I2C_HID_POWER_OFF, poll); | | 575 | &I2C_HID_POWER_OFF, poll); |
578 | | | 576 | |
579 | return (1); | | 577 | return (1); |
580 | } | | 578 | } |
581 | | | 579 | |
582 | DELAY(1000); | | 580 | DELAY(1000); |
583 | | | 581 | |
584 | return (0); | | 582 | return (0); |
585 | } | | 583 | } |
586 | | | 584 | |
587 | /* | | 585 | /* |
588 | * 5.2.2 - HID Descriptor Retrieval | | 586 | * 5.2.2 - HID Descriptor Retrieval |
589 | * | | 587 | * |
590 | * parse HID Descriptor that has already been read into hid_desc with | | 588 | * parse HID Descriptor that has already been read into hid_desc with |
591 | * I2C_HID_CMD_DESCR | | 589 | * I2C_HID_CMD_DESCR |
592 | */ | | 590 | */ |
593 | static int | | 591 | static int |
594 | ihidev_hid_desc_parse(struct ihidev_softc *sc) | | 592 | ihidev_hid_desc_parse(struct ihidev_softc *sc) |
595 | { | | 593 | { |
596 | int retries = 3; | | 594 | int retries = 3; |
597 | | | 595 | |
598 | /* must be v01.00 */ | | 596 | /* must be v01.00 */ |
599 | if (le16toh(sc->hid_desc.bcdVersion) != 0x0100) { | | 597 | if (le16toh(sc->hid_desc.bcdVersion) != 0x0100) { |
600 | aprint_error_dev(sc->sc_dev, | | 598 | aprint_error_dev(sc->sc_dev, |
601 | "bad HID descriptor bcdVersion (0x%x)\n", | | 599 | "bad HID descriptor bcdVersion (0x%x)\n", |
602 | le16toh(sc->hid_desc.bcdVersion)); | | 600 | le16toh(sc->hid_desc.bcdVersion)); |
603 | return (1); | | 601 | return (1); |
604 | } | | 602 | } |
605 | | | 603 | |
606 | /* must be 30 bytes for v1.00 */ | | 604 | /* must be 30 bytes for v1.00 */ |
607 | if (le16toh(sc->hid_desc.wHIDDescLength != | | 605 | if (le16toh(sc->hid_desc.wHIDDescLength != |
608 | sizeof(struct i2c_hid_desc))) { | | 606 | sizeof(struct i2c_hid_desc))) { |
609 | aprint_error_dev(sc->sc_dev, | | 607 | aprint_error_dev(sc->sc_dev, |
610 | "bad HID descriptor size (%d != %zu)\n", | | 608 | "bad HID descriptor size (%d != %zu)\n", |
611 | le16toh(sc->hid_desc.wHIDDescLength), | | 609 | le16toh(sc->hid_desc.wHIDDescLength), |
612 | sizeof(struct i2c_hid_desc)); | | 610 | sizeof(struct i2c_hid_desc)); |
613 | return (1); | | 611 | return (1); |
614 | } | | 612 | } |
615 | | | 613 | |
616 | if (le16toh(sc->hid_desc.wReportDescLength) <= 0) { | | 614 | if (le16toh(sc->hid_desc.wReportDescLength) <= 0) { |
617 | aprint_error_dev(sc->sc_dev, | | 615 | aprint_error_dev(sc->sc_dev, |
618 | "bad HID report descriptor size (%d)\n", | | 616 | "bad HID report descriptor size (%d)\n", |
619 | le16toh(sc->hid_desc.wReportDescLength)); | | 617 | le16toh(sc->hid_desc.wReportDescLength)); |
620 | return (1); | | 618 | return (1); |
621 | } | | 619 | } |
622 | | | 620 | |
623 | while (retries-- > 0) { | | 621 | while (retries-- > 0) { |
624 | if (ihidev_reset(sc, false)) { | | 622 | if (ihidev_reset(sc, false)) { |
625 | if (retries == 0) | | 623 | if (retries == 0) |
626 | return(1); | | 624 | return(1); |
627 | | | 625 | |
628 | DELAY(1000); | | 626 | DELAY(1000); |
629 | } | | 627 | } |
630 | else | | 628 | else |
631 | break; | | 629 | break; |
632 | } | | 630 | } |
633 | | | 631 | |
634 | sc->sc_reportlen = le16toh(sc->hid_desc.wReportDescLength); | | 632 | sc->sc_reportlen = le16toh(sc->hid_desc.wReportDescLength); |
635 | sc->sc_report = kmem_zalloc(sc->sc_reportlen, KM_NOSLEEP); | | 633 | sc->sc_report = kmem_zalloc(sc->sc_reportlen, KM_NOSLEEP); |
636 | | | 634 | |
637 | if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0, false)) { | | 635 | if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0, false)) { |
638 | aprint_error_dev(sc->sc_dev, "failed fetching HID report\n"); | | 636 | aprint_error_dev(sc->sc_dev, "failed fetching HID report\n"); |
639 | return (1); | | 637 | return (1); |
640 | } | | 638 | } |
641 | | | 639 | |
642 | return (0); | | 640 | return (0); |
643 | } | | 641 | } |
644 | | | 642 | |
645 | static bool | | 643 | static bool |
646 | ihiddev_intr_init(struct ihidev_softc *sc) | | 644 | ihiddev_intr_init(struct ihidev_softc *sc) |
647 | { | | 645 | { |
648 | #if NACPICA > 0 | | 646 | #if NACPICA > 0 |
649 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; | | 647 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
650 | struct acpi_resources res; | | 648 | struct acpi_resources res; |
651 | ACPI_STATUS rv; | | 649 | ACPI_STATUS rv; |
652 | char buf[100]; | | 650 | char buf[100]; |
653 | | | 651 | |
654 | rv = acpi_resource_parse(sc->sc_dev, hdl, "_CRS", &res, | | 652 | rv = acpi_resource_parse(sc->sc_dev, hdl, "_CRS", &res, |
655 | &acpi_resource_parse_ops_quiet); | | 653 | &acpi_resource_parse_ops_quiet); |
656 | if (ACPI_FAILURE(rv)) { | | 654 | if (ACPI_FAILURE(rv)) { |
657 | aprint_error_dev(sc->sc_dev, "can't parse '_CRS'\n"); | | 655 | aprint_error_dev(sc->sc_dev, "can't parse '_CRS'\n"); |
658 | return false; | | 656 | return false; |
659 | } | | 657 | } |
660 | | | 658 | |
661 | const struct acpi_irq * const irq = acpi_res_irq(&res, 0); | | 659 | const struct acpi_irq * const irq = acpi_res_irq(&res, 0); |
662 | if (irq == NULL) { | | 660 | if (irq == NULL) { |
663 | aprint_error_dev(sc->sc_dev, "no IRQ resource\n"); | | 661 | aprint_error_dev(sc->sc_dev, "no IRQ resource\n"); |
664 | acpi_resource_cleanup(&res); | | 662 | acpi_resource_cleanup(&res); |
665 | return false; | | 663 | return false; |
666 | } | | 664 | } |
667 | | | 665 | |
668 | sc->sc_intr_type = | | 666 | sc->sc_intr_type = |
669 | irq->ar_type == ACPI_EDGE_SENSITIVE ? IST_EDGE : IST_LEVEL; | | 667 | irq->ar_type == ACPI_EDGE_SENSITIVE ? IST_EDGE : IST_LEVEL; |
670 | | | 668 | |
671 | acpi_resource_cleanup(&res); | | 669 | acpi_resource_cleanup(&res); |
672 | | | 670 | |
673 | sc->sc_ih = acpi_intr_establish(sc->sc_dev, sc->sc_phandle, IPL_TTY, | | 671 | sc->sc_ih = acpi_intr_establish(sc->sc_dev, sc->sc_phandle, IPL_TTY, |
674 | false, ihidev_intr, sc, device_xname(sc->sc_dev)); | | 672 | false, ihidev_intr, sc, device_xname(sc->sc_dev)); |
675 | if (sc->sc_ih == NULL) { | | 673 | if (sc->sc_ih == NULL) { |
676 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); | | 674 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); |
677 | return false; | | 675 | return false; |
678 | } | | 676 | } |
679 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", | | 677 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", |
680 | acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); | | 678 | acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); |
681 | | | 679 | |
682 | sc->sc_sih = softint_establish(SOFTINT_SERIAL, ihidev_softintr, sc); | | 680 | sc->sc_sih = softint_establish(SOFTINT_SERIAL, ihidev_softintr, sc); |
683 | if (sc->sc_sih == NULL) { | | 681 | if (sc->sc_sih == NULL) { |
684 | aprint_error_dev(sc->sc_dev, | | 682 | aprint_error_dev(sc->sc_dev, |
685 | "can't establish soft interrupt\n"); | | 683 | "can't establish soft interrupt\n"); |
686 | return false; | | 684 | return false; |
687 | } | | 685 | } |
688 | | | 686 | |
689 | return true; | | 687 | return true; |
690 | #else | | 688 | #else |
691 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); | | 689 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); |
692 | return false; | | 690 | return false; |
693 | #endif | | 691 | #endif |
694 | } | | 692 | } |
695 | | | 693 | |
696 | static void | | 694 | static void |
697 | ihiddev_intr_fini(struct ihidev_softc *sc) | | 695 | ihiddev_intr_fini(struct ihidev_softc *sc) |
698 | { | | 696 | { |
699 | #if NACPICA > 0 | | 697 | #if NACPICA > 0 |
700 | if (sc->sc_ih != NULL) { | | 698 | if (sc->sc_ih != NULL) { |
701 | acpi_intr_disestablish(sc->sc_ih); | | 699 | acpi_intr_disestablish(sc->sc_ih); |
702 | } | | 700 | } |
703 | if (sc->sc_sih != NULL) { | | 701 | if (sc->sc_sih != NULL) { |
704 | softint_disestablish(sc->sc_sih); | | 702 | softint_disestablish(sc->sc_sih); |
705 | } | | 703 | } |
706 | #endif | | 704 | #endif |
707 | } | | 705 | } |
708 | | | 706 | |
709 | static void | | 707 | static void |
710 | ihidev_intr_mask(struct ihidev_softc * const sc) | | 708 | ihidev_intr_mask(struct ihidev_softc * const sc) |
711 | { | | 709 | { |
712 | | | 710 | |
713 | if (sc->sc_intr_type == IST_LEVEL) { | | 711 | if (sc->sc_intr_type == IST_LEVEL) { |
714 | #if NACPICA > 0 | | 712 | #if NACPICA > 0 |
715 | acpi_intr_mask(sc->sc_ih); | | 713 | acpi_intr_mask(sc->sc_ih); |
716 | #endif | | 714 | #endif |
717 | } | | 715 | } |
718 | } | | 716 | } |
719 | | | 717 | |
720 | static void | | 718 | static void |
721 | ihidev_intr_unmask(struct ihidev_softc * const sc) | | 719 | ihidev_intr_unmask(struct ihidev_softc * const sc) |
722 | { | | 720 | { |
723 | | | 721 | |
724 | if (sc->sc_intr_type == IST_LEVEL) { | | 722 | if (sc->sc_intr_type == IST_LEVEL) { |
725 | #if NACPICA > 0 | | 723 | #if NACPICA > 0 |
726 | acpi_intr_unmask(sc->sc_ih); | | 724 | acpi_intr_unmask(sc->sc_ih); |
727 | #endif | | 725 | #endif |
728 | } | | 726 | } |
729 | } | | 727 | } |
730 | | | 728 | |
731 | static int | | 729 | static int |
732 | ihidev_intr(void *arg) | | 730 | ihidev_intr(void *arg) |
733 | { | | 731 | { |
734 | struct ihidev_softc * const sc = arg; | | 732 | struct ihidev_softc * const sc = arg; |
735 | | | 733 | |
736 | mutex_enter(&sc->sc_intr_lock); | | 734 | mutex_enter(&sc->sc_intr_lock); |
737 | | | 735 | |
738 | /* | | 736 | /* |
739 | * Schedule our soft interrupt handler. If we're using a level- | | 737 | * Schedule our soft interrupt handler. If we're using a level- |
740 | * triggered interrupt, we have to mask it off while we wait | | 738 | * triggered interrupt, we have to mask it off while we wait |
741 | * for service. | | 739 | * for service. |
742 | */ | | 740 | */ |
743 | softint_schedule(sc->sc_sih); | | 741 | softint_schedule(sc->sc_sih); |
744 | ihidev_intr_mask(sc); | | 742 | ihidev_intr_mask(sc); |
745 | | | 743 | |
746 | mutex_exit(&sc->sc_intr_lock); | | 744 | mutex_exit(&sc->sc_intr_lock); |
747 | | | 745 | |
748 | return 1; | | 746 | return 1; |
749 | } | | 747 | } |
750 | | | 748 | |
751 | static void | | 749 | static void |
752 | ihidev_softintr(void *arg) | | 750 | ihidev_softintr(void *arg) |
753 | { | | 751 | { |
754 | struct ihidev_softc * const sc = arg; | | 752 | struct ihidev_softc * const sc = arg; |
755 | struct ihidev *scd; | | 753 | struct ihidev *scd; |
756 | u_int psize; | | 754 | u_int psize; |
757 | int res, i; | | 755 | int res, i; |
758 | u_char *p; | | 756 | u_char *p; |
759 | u_int rep = 0; | | 757 | u_int rep = 0; |
760 | | | 758 | |
761 | mutex_enter(&sc->sc_intr_lock); | | 759 | mutex_enter(&sc->sc_intr_lock); |
762 | iic_acquire_bus(sc->sc_tag, 0); | | 760 | iic_acquire_bus(sc->sc_tag, 0); |
763 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, | | 761 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, |
764 | sc->sc_ibuf, sc->sc_isize, 0); | | 762 | sc->sc_ibuf, sc->sc_isize, 0); |
765 | iic_release_bus(sc->sc_tag, 0); | | 763 | iic_release_bus(sc->sc_tag, 0); |
766 | mutex_exit(&sc->sc_intr_lock); | | 764 | mutex_exit(&sc->sc_intr_lock); |
767 | | | 765 | |
768 | if (res != 0) | | 766 | if (res != 0) |
769 | goto out; | | 767 | goto out; |
770 | | | 768 | |
771 | /* | | 769 | /* |
772 | * 6.1.1 - First two bytes are the packet length, which must be less | | 770 | * 6.1.1 - First two bytes are the packet length, which must be less |
773 | * than or equal to wMaxInputLength | | 771 | * than or equal to wMaxInputLength |
774 | */ | | 772 | */ |
775 | psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; | | 773 | psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; |
776 | if (!psize || psize > sc->sc_isize) { | | 774 | if (!psize || psize > sc->sc_isize) { |
777 | DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", | | 775 | DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", |
778 | sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize)); | | 776 | sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize)); |
779 | goto out; | | 777 | goto out; |
780 | } | | 778 | } |
781 | | | 779 | |
782 | /* 3rd byte is the report id */ | | 780 | /* 3rd byte is the report id */ |
783 | p = sc->sc_ibuf + 2; | | 781 | p = sc->sc_ibuf + 2; |
784 | psize -= 2; | | 782 | psize -= 2; |
785 | if (sc->sc_nrepid != 1) | | 783 | if (sc->sc_nrepid != 1) |
786 | rep = *p++, psize--; | | 784 | rep = *p++, psize--; |
787 | | | 785 | |
788 | if (rep >= sc->sc_nrepid) { | | 786 | if (rep >= sc->sc_nrepid) { |
789 | aprint_error_dev(sc->sc_dev, "%s: bad report id %d\n", | | 787 | aprint_error_dev(sc->sc_dev, "%s: bad report id %d\n", |
790 | __func__, rep); | | 788 | __func__, rep); |
791 | goto out; | | 789 | goto out; |
792 | } | | 790 | } |
793 | | | 791 | |
794 | DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, | | 792 | DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, |
795 | __func__, rep)); | | 793 | __func__, rep)); |
796 | for (i = 0; i < sc->sc_isize; i++) | | 794 | for (i = 0; i < sc->sc_isize; i++) |
797 | DPRINTF((" %.2x", sc->sc_ibuf[i])); | | 795 | DPRINTF((" %.2x", sc->sc_ibuf[i])); |
798 | DPRINTF(("\n")); | | 796 | DPRINTF(("\n")); |
799 | | | 797 | |
800 | scd = sc->sc_subdevs[rep]; | | 798 | scd = sc->sc_subdevs[rep]; |
801 | if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) | | 799 | if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) |
802 | goto out; | | 800 | goto out; |
803 | | | 801 | |
804 | scd->sc_intr(scd, p, psize); | | 802 | scd->sc_intr(scd, p, psize); |
805 | | | 803 | |
806 | out: | | 804 | out: |
807 | /* | | 805 | /* |
808 | * If our interrupt is level-triggered, re-enable it now. | | 806 | * If our interrupt is level-triggered, re-enable it now. |
809 | */ | | 807 | */ |
810 | ihidev_intr_unmask(sc); | | 808 | ihidev_intr_unmask(sc); |
811 | } | | 809 | } |
812 | | | 810 | |
813 | static int | | 811 | static int |
814 | ihidev_maxrepid(void *buf, int len) | | 812 | ihidev_maxrepid(void *buf, int len) |
815 | { | | 813 | { |
816 | struct hid_data *d; | | 814 | struct hid_data *d; |
817 | struct hid_item h; | | 815 | struct hid_item h; |
818 | int maxid; | | 816 | int maxid; |
819 | | | 817 | |
820 | maxid = -1; | | 818 | maxid = -1; |
821 | h.report_ID = 0; | | 819 | h.report_ID = 0; |
822 | for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); ) | | 820 | for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); ) |
823 | if ((int)h.report_ID > maxid) | | 821 | if ((int)h.report_ID > maxid) |
824 | maxid = h.report_ID; | | 822 | maxid = h.report_ID; |
825 | hid_end_parse(d); | | 823 | hid_end_parse(d); |
826 | | | 824 | |
827 | return (maxid); | | 825 | return (maxid); |
828 | } | | 826 | } |
829 | | | 827 | |
830 | static int | | 828 | static int |
831 | ihidev_print(void *aux, const char *pnp) | | 829 | ihidev_print(void *aux, const char *pnp) |
832 | { | | 830 | { |
833 | struct ihidev_attach_arg *iha = aux; | | 831 | struct ihidev_attach_arg *iha = aux; |
834 | | | 832 | |
835 | if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) | | 833 | if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) |
836 | return (QUIET); | | 834 | return (QUIET); |
837 | | | 835 | |
838 | if (pnp) | | 836 | if (pnp) |
839 | aprint_normal("hid at %s", pnp); | | 837 | aprint_normal("hid at %s", pnp); |
840 | | | 838 | |
841 | if (iha->reportid != 0) | | 839 | if (iha->reportid != 0) |
842 | aprint_normal(" reportid %d", iha->reportid); | | 840 | aprint_normal(" reportid %d", iha->reportid); |
843 | | | 841 | |
844 | return (UNCONF); | | 842 | return (UNCONF); |
845 | } | | 843 | } |
846 | | | 844 | |
847 | static int | | 845 | static int |
848 | ihidev_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) | | 846 | ihidev_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) |
849 | { | | 847 | { |
850 | struct ihidev_attach_arg *iha = aux; | | 848 | struct ihidev_attach_arg *iha = aux; |
851 | | | 849 | |
852 | if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID && | | 850 | if (cf->ihidevcf_reportid != IHIDEV_UNK_REPORTID && |
853 | cf->ihidevcf_reportid != iha->reportid) | | 851 | cf->ihidevcf_reportid != iha->reportid) |
854 | return (0); | | 852 | return (0); |
855 | | | 853 | |
856 | return config_match(parent, cf, aux); | | 854 | return config_match(parent, cf, aux); |
857 | } | | 855 | } |
858 | | | 856 | |
859 | int | | 857 | int |
860 | ihidev_open(struct ihidev *scd) | | 858 | ihidev_open(struct ihidev *scd) |
861 | { | | 859 | { |
862 | struct ihidev_softc *sc = scd->sc_parent; | | 860 | struct ihidev_softc *sc = scd->sc_parent; |
863 | | | 861 | |
864 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, | | 862 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, |
865 | __func__, scd->sc_state, sc->sc_refcnt)); | | 863 | __func__, scd->sc_state, sc->sc_refcnt)); |
866 | | | 864 | |
867 | if (scd->sc_state & IHIDEV_OPEN) | | 865 | if (scd->sc_state & IHIDEV_OPEN) |
868 | return (EBUSY); | | 866 | return (EBUSY); |
869 | | | 867 | |
870 | scd->sc_state |= IHIDEV_OPEN; | | 868 | scd->sc_state |= IHIDEV_OPEN; |
871 | | | 869 | |
872 | if (sc->sc_refcnt++ || sc->sc_isize == 0) | | 870 | if (sc->sc_refcnt++ || sc->sc_isize == 0) |
873 | return (0); | | 871 | return (0); |
874 | | | 872 | |
875 | /* power on */ | | 873 | /* power on */ |
876 | ihidev_reset(sc, false); | | 874 | ihidev_reset(sc, false); |
877 | | | 875 | |
878 | return (0); | | 876 | return (0); |
879 | } | | 877 | } |
880 | | | 878 | |
881 | void | | 879 | void |
882 | ihidev_close(struct ihidev *scd) | | 880 | ihidev_close(struct ihidev *scd) |
883 | { | | 881 | { |
884 | struct ihidev_softc *sc = scd->sc_parent; | | 882 | struct ihidev_softc *sc = scd->sc_parent; |
885 | | | 883 | |
886 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, | | 884 | DPRINTF(("%s: %s: state=%d refcnt=%d\n", sc->sc_dev.dv_xname, |
887 | __func__, scd->sc_state, sc->sc_refcnt)); | | 885 | __func__, scd->sc_state, sc->sc_refcnt)); |
888 | | | 886 | |
889 | if (!(scd->sc_state & IHIDEV_OPEN)) | | 887 | if (!(scd->sc_state & IHIDEV_OPEN)) |
890 | return; | | 888 | return; |
891 | | | 889 | |
892 | scd->sc_state &= ~IHIDEV_OPEN; | | 890 | scd->sc_state &= ~IHIDEV_OPEN; |
893 | | | 891 | |
894 | if (--sc->sc_refcnt) | | 892 | if (--sc->sc_refcnt) |
895 | return; | | 893 | return; |
896 | | | 894 | |
897 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 895 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
898 | &I2C_HID_POWER_OFF, false)) | | 896 | &I2C_HID_POWER_OFF, false)) |
899 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 897 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
900 | } | | 898 | } |
901 | | | 899 | |
902 | void | | 900 | void |
903 | ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size) | | 901 | ihidev_get_report_desc(struct ihidev_softc *sc, void **desc, int *size) |
904 | { | | 902 | { |
905 | *desc = sc->sc_report; | | 903 | *desc = sc->sc_report; |
906 | *size = sc->sc_reportlen; | | 904 | *size = sc->sc_reportlen; |
907 | } | | 905 | } |
908 | | | 906 | |
909 | /* convert hid_* constants used throughout HID code to i2c HID equivalents */ | | 907 | /* convert hid_* constants used throughout HID code to i2c HID equivalents */ |
910 | int | | 908 | int |
911 | ihidev_report_type_conv(int hid_type_id) | | 909 | ihidev_report_type_conv(int hid_type_id) |
912 | { | | 910 | { |
913 | switch (hid_type_id) { | | 911 | switch (hid_type_id) { |
914 | case hid_input: | | 912 | case hid_input: |
915 | return I2C_HID_REPORT_TYPE_INPUT; | | 913 | return I2C_HID_REPORT_TYPE_INPUT; |
916 | case hid_output: | | 914 | case hid_output: |
917 | return I2C_HID_REPORT_TYPE_OUTPUT; | | 915 | return I2C_HID_REPORT_TYPE_OUTPUT; |
918 | case hid_feature: | | 916 | case hid_feature: |
919 | return I2C_HID_REPORT_TYPE_FEATURE; | | 917 | return I2C_HID_REPORT_TYPE_FEATURE; |
920 | default: | | 918 | default: |
921 | return -1; | | 919 | return -1; |
922 | } | | 920 | } |
923 | } | | 921 | } |
924 | | | 922 | |
925 | int | | 923 | int |
926 | ihidev_get_report(struct device *dev, int type, int id, void *data, int len) | | 924 | ihidev_get_report(struct device *dev, int type, int id, void *data, int len) |
927 | { | | 925 | { |
928 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; | | 926 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; |
929 | struct i2c_hid_report_request rreq; | | 927 | struct i2c_hid_report_request rreq; |
930 | int ctype; | | 928 | int ctype; |
931 | | | 929 | |
932 | if ((ctype = ihidev_report_type_conv(type)) < 0) | | 930 | if ((ctype = ihidev_report_type_conv(type)) < 0) |
933 | return (1); | | 931 | return (1); |
934 | | | 932 | |
935 | rreq.type = ctype; | | 933 | rreq.type = ctype; |
936 | rreq.id = id; | | 934 | rreq.id = id; |
937 | rreq.data = data; | | 935 | rreq.data = data; |
938 | rreq.len = len; | | 936 | rreq.len = len; |
939 | | | 937 | |
940 | if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq, false)) { | | 938 | if (ihidev_hid_command(sc, I2C_HID_CMD_GET_REPORT, &rreq, false)) { |
941 | aprint_error_dev(sc->sc_dev, "failed fetching report\n"); | | 939 | aprint_error_dev(sc->sc_dev, "failed fetching report\n"); |
942 | return (1); | | 940 | return (1); |
943 | } | | 941 | } |
944 | | | 942 | |
945 | return 0; | | 943 | return 0; |
946 | } | | 944 | } |
947 | | | 945 | |
948 | int | | 946 | int |
949 | ihidev_set_report(struct device *dev, int type, int id, void *data, | | 947 | ihidev_set_report(struct device *dev, int type, int id, void *data, |
950 | int len) | | 948 | int len) |
951 | { | | 949 | { |
952 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; | | 950 | struct ihidev_softc *sc = (struct ihidev_softc *)dev; |
953 | struct i2c_hid_report_request rreq; | | 951 | struct i2c_hid_report_request rreq; |
954 | int ctype; | | 952 | int ctype; |
955 | | | 953 | |
956 | if ((ctype = ihidev_report_type_conv(type)) < 0) | | 954 | if ((ctype = ihidev_report_type_conv(type)) < 0) |
957 | return (1); | | 955 | return (1); |
958 | | | 956 | |
959 | rreq.type = ctype; | | 957 | rreq.type = ctype; |
960 | rreq.id = id; | | 958 | rreq.id = id; |
961 | rreq.data = data; | | 959 | rreq.data = data; |
962 | rreq.len = len; | | 960 | rreq.len = len; |
963 | | | 961 | |
964 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq, false)) { | | 962 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq, false)) { |
965 | aprint_error_dev(sc->sc_dev, "failed setting report\n"); | | 963 | aprint_error_dev(sc->sc_dev, "failed setting report\n"); |
966 | return (1); | | 964 | return (1); |
967 | } | | 965 | } |
968 | | | 966 | |
969 | return 0; | | 967 | return 0; |
970 | } | | 968 | } |