| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: ihidev.c,v 1.20 2021/08/07 16:19:11 thorpej Exp $ */ | | 1 | /* $NetBSD: ihidev.c,v 1.21 2022/01/14 22:25:49 riastradh 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 |
| @@ -44,33 +44,33 @@ | | | @@ -44,33 +44,33 @@ |
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.20 2021/08/07 16:19:11 thorpej Exp $"); | | 57 | __KERNEL_RCSID(0, "$NetBSD: ihidev.c,v 1.21 2022/01/14 22:25:49 riastradh 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 | #include <sys/workqueue.h> |
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 |
| @@ -100,34 +100,34 @@ enum { | | | @@ -100,34 +100,34 @@ enum { |
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 ihidev_intr_init(struct ihidev_softc *); |
114 | static void ihiddev_intr_fini(struct ihidev_softc *); | | 114 | static void ihidev_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_work(struct work *, 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 bool ihidev_acpi_get_info(struct ihidev_softc *); | | 128 | static bool ihidev_acpi_get_info(struct ihidev_softc *); |
129 | | | 129 | |
130 | static const struct device_compatible_entry compat_data[] = { | | 130 | static const struct device_compatible_entry compat_data[] = { |
131 | { .compat = "PNP0C50" }, | | 131 | { .compat = "PNP0C50" }, |
132 | { .compat = "ACPI0C50" }, | | 132 | { .compat = "ACPI0C50" }, |
133 | { .compat = "hid-over-i2c" }, | | 133 | { .compat = "hid-over-i2c" }, |
| @@ -150,34 +150,34 @@ static void | | | @@ -150,34 +150,34 @@ static void |
150 | ihidev_attach(device_t parent, device_t self, void *aux) | | 150 | ihidev_attach(device_t parent, device_t self, void *aux) |
151 | { | | 151 | { |
152 | struct ihidev_softc *sc = device_private(self); | | 152 | struct ihidev_softc *sc = device_private(self); |
153 | struct i2c_attach_args *ia = aux; | | 153 | struct i2c_attach_args *ia = aux; |
154 | struct ihidev_attach_arg iha; | | 154 | struct ihidev_attach_arg iha; |
155 | device_t dev; | | 155 | device_t dev; |
156 | int repid, repsz; | | 156 | int repid, repsz; |
157 | int isize; | | 157 | int isize; |
158 | int locs[IHIDBUSCF_NLOCS]; | | 158 | int locs[IHIDBUSCF_NLOCS]; |
159 | | | 159 | |
160 | sc->sc_dev = self; | | 160 | sc->sc_dev = self; |
161 | sc->sc_tag = ia->ia_tag; | | 161 | sc->sc_tag = ia->ia_tag; |
162 | sc->sc_addr = ia->ia_addr; | | 162 | sc->sc_addr = ia->ia_addr; |
163 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); | | 163 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
164 | | | 164 | |
165 | sc->sc_phandle = ia->ia_cookie; | | 165 | sc->sc_phandle = ia->ia_cookie; |
166 | if (ia->ia_cookietype != I2C_COOKIE_ACPI) { | | 166 | if (ia->ia_cookietype != I2C_COOKIE_ACPI) { |
167 | aprint_error(": unsupported device tree type\n"); | | 167 | aprint_error(": unsupported device tree type\n"); |
168 | return; | | 168 | return; |
169 | } | | 169 | } |
170 | if (! ihidev_acpi_get_info(sc)) { | | 170 | if (!ihidev_acpi_get_info(sc)) { |
171 | return; | | 171 | return; |
172 | } | | 172 | } |
173 | | | 173 | |
174 | if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL, false) || | | 174 | if (ihidev_hid_command(sc, I2C_HID_CMD_DESCR, NULL, false) || |
175 | ihidev_hid_desc_parse(sc)) { | | 175 | ihidev_hid_desc_parse(sc)) { |
176 | aprint_error(": failed fetching initial HID descriptor\n"); | | 176 | aprint_error(": failed fetching initial HID descriptor\n"); |
177 | return; | | 177 | return; |
178 | } | | 178 | } |
179 | | | 179 | |
180 | aprint_naive("\n"); | | 180 | aprint_naive("\n"); |
181 | aprint_normal(": vendor 0x%x product 0x%x, %s\n", | | 181 | aprint_normal(": vendor 0x%x product 0x%x, %s\n", |
182 | le16toh(sc->hid_desc.wVendorID), le16toh(sc->hid_desc.wProductID), | | 182 | le16toh(sc->hid_desc.wVendorID), le16toh(sc->hid_desc.wProductID), |
183 | ia->ia_name); | | 183 | ia->ia_name); |
| @@ -198,27 +198,27 @@ ihidev_attach(device_t parent, device_t | | | @@ -198,27 +198,27 @@ ihidev_attach(device_t parent, device_t |
198 | for (repid = 0; repid < sc->sc_nrepid; repid++) { | | 198 | for (repid = 0; repid < sc->sc_nrepid; repid++) { |
199 | repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, | | 199 | repsz = hid_report_size(sc->sc_report, sc->sc_reportlen, |
200 | hid_input, repid); | | 200 | hid_input, repid); |
201 | | | 201 | |
202 | isize = repsz + 2; /* two bytes for the length */ | | 202 | isize = repsz + 2; /* two bytes for the length */ |
203 | isize += (sc->sc_nrepid != 1); /* one byte for the report ID */ | | 203 | isize += (sc->sc_nrepid != 1); /* one byte for the report ID */ |
204 | if (isize > sc->sc_isize) | | 204 | if (isize > sc->sc_isize) |
205 | sc->sc_isize = isize; | | 205 | sc->sc_isize = isize; |
206 | | | 206 | |
207 | DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid, | | 207 | DPRINTF(("%s: repid %d size %d\n", sc->sc_dev.dv_xname, repid, |
208 | repsz)); | | 208 | repsz)); |
209 | } | | 209 | } |
210 | sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); | | 210 | sc->sc_ibuf = kmem_zalloc(sc->sc_isize, KM_SLEEP); |
211 | if (! ihiddev_intr_init(sc)) { | | 211 | if (!ihidev_intr_init(sc)) { |
212 | return; | | 212 | return; |
213 | } | | 213 | } |
214 | | | 214 | |
215 | iha.iaa = ia; | | 215 | iha.iaa = ia; |
216 | iha.parent = sc; | | 216 | iha.parent = sc; |
217 | | | 217 | |
218 | /* Look for a driver claiming all report IDs first. */ | | 218 | /* Look for a driver claiming all report IDs first. */ |
219 | iha.reportid = IHIDEV_CLAIM_ALLREPORTID; | | 219 | iha.reportid = IHIDEV_CLAIM_ALLREPORTID; |
220 | locs[IHIDBUSCF_REPORTID] = IHIDEV_CLAIM_ALLREPORTID; | | 220 | locs[IHIDBUSCF_REPORTID] = IHIDEV_CLAIM_ALLREPORTID; |
221 | dev = config_found(self, &iha, ihidev_print, | | 221 | dev = config_found(self, &iha, ihidev_print, |
222 | CFARGS(.submatch = ihidev_submatch, | | 222 | CFARGS(.submatch = ihidev_submatch, |
223 | .locators = locs)); | | 223 | .locators = locs)); |
224 | if (dev != NULL) { | | 224 | if (dev != NULL) { |
| @@ -235,84 +235,92 @@ ihidev_attach(device_t parent, device_t | | | @@ -235,84 +235,92 @@ ihidev_attach(device_t parent, device_t |
235 | hid_report_size(sc->sc_report, sc->sc_reportlen, | | 235 | hid_report_size(sc->sc_report, sc->sc_reportlen, |
236 | hid_feature, repid) == 0) | | 236 | hid_feature, repid) == 0) |
237 | continue; | | 237 | continue; |
238 | | | 238 | |
239 | iha.reportid = repid; | | 239 | iha.reportid = repid; |
240 | locs[IHIDBUSCF_REPORTID] = repid; | | 240 | locs[IHIDBUSCF_REPORTID] = repid; |
241 | dev = config_found(self, &iha, ihidev_print, | | 241 | dev = config_found(self, &iha, ihidev_print, |
242 | CFARGS(.submatch = ihidev_submatch, | | 242 | CFARGS(.submatch = ihidev_submatch, |
243 | .locators = locs)); | | 243 | .locators = locs)); |
244 | sc->sc_subdevs[repid] = device_private(dev); | | 244 | sc->sc_subdevs[repid] = device_private(dev); |
245 | } | | 245 | } |
246 | | | 246 | |
247 | /* power down until we're opened */ | | 247 | /* power down until we're opened */ |
248 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, false)) { | | 248 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, |
| | | 249 | false)) { |
249 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 250 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
250 | return; | | 251 | return; |
251 | } | | 252 | } |
252 | if (!pmf_device_register(self, ihidev_suspend, ihidev_resume)) | | 253 | if (!pmf_device_register(self, ihidev_suspend, ihidev_resume)) |
253 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 254 | aprint_error_dev(self, "couldn't establish power handler\n"); |
254 | } | | 255 | } |
255 | | | 256 | |
256 | static int | | 257 | static int |
257 | ihidev_detach(device_t self, int flags) | | 258 | ihidev_detach(device_t self, int flags) |
258 | { | | 259 | { |
259 | struct ihidev_softc *sc = device_private(self); | | 260 | struct ihidev_softc *sc = device_private(self); |
| | | 261 | int error; |
| | | 262 | |
| | | 263 | error = config_detach_children(self, flags); |
| | | 264 | if (error) |
| | | 265 | return error; |
| | | 266 | |
| | | 267 | pmf_device_deregister(self); |
| | | 268 | ihidev_intr_fini(sc); |
| | | 269 | |
| | | 270 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, &I2C_HID_POWER_OFF, |
| | | 271 | true)) |
| | | 272 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
260 | | | 273 | |
261 | mutex_enter(&sc->sc_intr_lock); | | | |
262 | ihiddev_intr_fini(sc); | | | |
263 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | | |
264 | &I2C_HID_POWER_OFF, true)) | | | |
265 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | | |
266 | mutex_exit(&sc->sc_intr_lock); | | | |
267 | if (sc->sc_ibuf != NULL) { | | 274 | if (sc->sc_ibuf != NULL) { |
268 | kmem_free(sc->sc_ibuf, sc->sc_isize); | | 275 | kmem_free(sc->sc_ibuf, sc->sc_isize); |
269 | sc->sc_ibuf = NULL; | | 276 | sc->sc_ibuf = NULL; |
270 | } | | 277 | } |
271 | | | 278 | |
272 | if (sc->sc_report != NULL) | | 279 | if (sc->sc_report != NULL) |
273 | kmem_free(sc->sc_report, sc->sc_reportlen); | | 280 | kmem_free(sc->sc_report, sc->sc_reportlen); |
274 | | | 281 | |
275 | pmf_device_deregister(self); | | 282 | mutex_destroy(&sc->sc_lock); |
276 | return (0); | | 283 | |
| | | 284 | return 0; |
277 | } | | 285 | } |
278 | | | 286 | |
279 | static bool | | 287 | static bool |
280 | ihidev_suspend(device_t self, const pmf_qual_t *q) | | 288 | ihidev_suspend(device_t self, const pmf_qual_t *q) |
281 | { | | 289 | { |
282 | struct ihidev_softc *sc = device_private(self); | | 290 | struct ihidev_softc *sc = device_private(self); |
283 | | | 291 | |
284 | mutex_enter(&sc->sc_intr_lock); | | 292 | mutex_enter(&sc->sc_lock); |
285 | if (sc->sc_refcnt > 0) { | | 293 | if (sc->sc_refcnt > 0) { |
286 | printf("ihidev power off\n"); | | 294 | printf("ihidev power off\n"); |
287 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, | | 295 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_POWER, |
288 | &I2C_HID_POWER_OFF, true)) | | 296 | &I2C_HID_POWER_OFF, true)) |
289 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); | | 297 | aprint_error_dev(sc->sc_dev, "failed to power down\n"); |
290 | } | | 298 | } |
291 | mutex_exit(&sc->sc_intr_lock); | | 299 | mutex_exit(&sc->sc_lock); |
292 | return true; | | 300 | return true; |
293 | } | | 301 | } |
294 | | | 302 | |
295 | static bool | | 303 | static bool |
296 | ihidev_resume(device_t self, const pmf_qual_t *q) | | 304 | ihidev_resume(device_t self, const pmf_qual_t *q) |
297 | { | | 305 | { |
298 | struct ihidev_softc *sc = device_private(self); | | 306 | struct ihidev_softc *sc = device_private(self); |
299 | | | 307 | |
300 | mutex_enter(&sc->sc_intr_lock); | | 308 | mutex_enter(&sc->sc_lock); |
301 | if (sc->sc_refcnt > 0) { | | 309 | if (sc->sc_refcnt > 0) { |
302 | printf("ihidev power reset\n"); | | 310 | printf("ihidev power reset\n"); |
303 | ihidev_reset(sc, true); | | 311 | ihidev_reset(sc, true); |
304 | } | | 312 | } |
305 | mutex_exit(&sc->sc_intr_lock); | | 313 | mutex_exit(&sc->sc_lock); |
306 | return true; | | 314 | return true; |
307 | } | | 315 | } |
308 | | | 316 | |
309 | static int | | 317 | static int |
310 | ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg, bool poll) | | 318 | ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg, bool poll) |
311 | { | | 319 | { |
312 | int i, res = 1; | | 320 | int i, res = 1; |
313 | int flags = poll ? I2C_F_POLL : 0; | | 321 | int flags = poll ? I2C_F_POLL : 0; |
314 | | | 322 | |
315 | iic_acquire_bus(sc->sc_tag, flags); | | 323 | iic_acquire_bus(sc->sc_tag, flags); |
316 | | | 324 | |
317 | switch (hidcmd) { | | 325 | switch (hidcmd) { |
318 | case I2C_HID_CMD_DESCR: { | | 326 | case I2C_HID_CMD_DESCR: { |
| @@ -636,27 +644,27 @@ ihidev_hid_desc_parse(struct ihidev_soft | | | @@ -636,27 +644,27 @@ ihidev_hid_desc_parse(struct ihidev_soft |
636 | | | 644 | |
637 | sc->sc_reportlen = le16toh(sc->hid_desc.wReportDescLength); | | 645 | sc->sc_reportlen = le16toh(sc->hid_desc.wReportDescLength); |
638 | sc->sc_report = kmem_zalloc(sc->sc_reportlen, KM_NOSLEEP); | | 646 | sc->sc_report = kmem_zalloc(sc->sc_reportlen, KM_NOSLEEP); |
639 | | | 647 | |
640 | if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0, false)) { | | 648 | if (ihidev_hid_command(sc, I2C_HID_REPORT_DESCR, 0, false)) { |
641 | aprint_error_dev(sc->sc_dev, "failed fetching HID report\n"); | | 649 | aprint_error_dev(sc->sc_dev, "failed fetching HID report\n"); |
642 | return (1); | | 650 | return (1); |
643 | } | | 651 | } |
644 | | | 652 | |
645 | return (0); | | 653 | return (0); |
646 | } | | 654 | } |
647 | | | 655 | |
648 | static bool | | 656 | static bool |
649 | ihiddev_intr_init(struct ihidev_softc *sc) | | 657 | ihidev_intr_init(struct ihidev_softc *sc) |
650 | { | | 658 | { |
651 | #if NACPICA > 0 | | 659 | #if NACPICA > 0 |
652 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; | | 660 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
653 | struct acpi_resources res; | | 661 | struct acpi_resources res; |
654 | ACPI_STATUS rv; | | 662 | ACPI_STATUS rv; |
655 | char buf[100]; | | 663 | char buf[100]; |
656 | | | 664 | |
657 | rv = acpi_resource_parse(sc->sc_dev, hdl, "_CRS", &res, | | 665 | rv = acpi_resource_parse(sc->sc_dev, hdl, "_CRS", &res, |
658 | &acpi_resource_parse_ops_quiet); | | 666 | &acpi_resource_parse_ops_quiet); |
659 | if (ACPI_FAILURE(rv)) { | | 667 | if (ACPI_FAILURE(rv)) { |
660 | aprint_error_dev(sc->sc_dev, "can't parse '_CRS'\n"); | | 668 | aprint_error_dev(sc->sc_dev, "can't parse '_CRS'\n"); |
661 | return false; | | 669 | return false; |
662 | } | | 670 | } |
| @@ -672,49 +680,50 @@ ihiddev_intr_init(struct ihidev_softc *s | | | @@ -672,49 +680,50 @@ ihiddev_intr_init(struct ihidev_softc *s |
672 | irq->ar_type == ACPI_EDGE_SENSITIVE ? IST_EDGE : IST_LEVEL; | | 680 | irq->ar_type == ACPI_EDGE_SENSITIVE ? IST_EDGE : IST_LEVEL; |
673 | | | 681 | |
674 | acpi_resource_cleanup(&res); | | 682 | acpi_resource_cleanup(&res); |
675 | | | 683 | |
676 | sc->sc_ih = acpi_intr_establish(sc->sc_dev, sc->sc_phandle, IPL_TTY, | | 684 | sc->sc_ih = acpi_intr_establish(sc->sc_dev, sc->sc_phandle, IPL_TTY, |
677 | false, ihidev_intr, sc, device_xname(sc->sc_dev)); | | 685 | false, ihidev_intr, sc, device_xname(sc->sc_dev)); |
678 | if (sc->sc_ih == NULL) { | | 686 | if (sc->sc_ih == NULL) { |
679 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); | | 687 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); |
680 | return false; | | 688 | return false; |
681 | } | | 689 | } |
682 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", | | 690 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", |
683 | acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); | | 691 | acpi_intr_string(sc->sc_ih, buf, sizeof(buf))); |
684 | | | 692 | |
685 | sc->sc_sih = softint_establish(SOFTINT_SERIAL, ihidev_softintr, sc); | | 693 | if (workqueue_create(&sc->sc_wq, device_xname(sc->sc_dev), ihidev_work, |
686 | if (sc->sc_sih == NULL) { | | 694 | sc, PRI_NONE, IPL_TTY, WQ_MPSAFE)) { |
687 | aprint_error_dev(sc->sc_dev, | | 695 | aprint_error_dev(sc->sc_dev, |
688 | "can't establish soft interrupt\n"); | | 696 | "can't establish workqueue\n"); |
689 | return false; | | 697 | return false; |
690 | } | | 698 | } |
| | | 699 | sc->sc_work_pending = 0; |
691 | | | 700 | |
692 | return true; | | 701 | return true; |
693 | #else | | 702 | #else |
694 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); | | 703 | aprint_error_dev(sc->sc_dev, "can't establish interrupt\n"); |
695 | return false; | | 704 | return false; |
696 | #endif | | 705 | #endif |
697 | } | | 706 | } |
698 | | | 707 | |
699 | static void | | 708 | static void |
700 | ihiddev_intr_fini(struct ihidev_softc *sc) | | 709 | ihidev_intr_fini(struct ihidev_softc *sc) |
701 | { | | 710 | { |
702 | #if NACPICA > 0 | | 711 | #if NACPICA > 0 |
703 | if (sc->sc_ih != NULL) { | | 712 | if (sc->sc_ih != NULL) { |
704 | acpi_intr_disestablish(sc->sc_ih); | | 713 | acpi_intr_disestablish(sc->sc_ih); |
705 | } | | 714 | } |
706 | if (sc->sc_sih != NULL) { | | 715 | if (sc->sc_wq != NULL) { |
707 | softint_disestablish(sc->sc_sih); | | 716 | workqueue_destroy(sc->sc_wq); |
708 | } | | 717 | } |
709 | #endif | | 718 | #endif |
710 | } | | 719 | } |
711 | | | 720 | |
712 | static void | | 721 | static void |
713 | ihidev_intr_mask(struct ihidev_softc * const sc) | | 722 | ihidev_intr_mask(struct ihidev_softc * const sc) |
714 | { | | 723 | { |
715 | | | 724 | |
716 | if (sc->sc_intr_type == IST_LEVEL) { | | 725 | if (sc->sc_intr_type == IST_LEVEL) { |
717 | #if NACPICA > 0 | | 726 | #if NACPICA > 0 |
718 | acpi_intr_mask(sc->sc_ih); | | 727 | acpi_intr_mask(sc->sc_ih); |
719 | #endif | | 728 | #endif |
720 | } | | 729 | } |
| @@ -726,58 +735,55 @@ ihidev_intr_unmask(struct ihidev_softc * | | | @@ -726,58 +735,55 @@ ihidev_intr_unmask(struct ihidev_softc * |
726 | | | 735 | |
727 | if (sc->sc_intr_type == IST_LEVEL) { | | 736 | if (sc->sc_intr_type == IST_LEVEL) { |
728 | #if NACPICA > 0 | | 737 | #if NACPICA > 0 |
729 | acpi_intr_unmask(sc->sc_ih); | | 738 | acpi_intr_unmask(sc->sc_ih); |
730 | #endif | | 739 | #endif |
731 | } | | 740 | } |
732 | } | | 741 | } |
733 | | | 742 | |
734 | static int | | 743 | static int |
735 | ihidev_intr(void *arg) | | 744 | ihidev_intr(void *arg) |
736 | { | | 745 | { |
737 | struct ihidev_softc * const sc = arg; | | 746 | struct ihidev_softc * const sc = arg; |
738 | | | 747 | |
739 | mutex_enter(&sc->sc_intr_lock); | | | |
740 | | | | |
741 | /* | | 748 | /* |
742 | * Schedule our soft interrupt handler. If we're using a level- | | 749 | * Schedule our work. If we're using a level-triggered |
743 | * triggered interrupt, we have to mask it off while we wait | | 750 | * interrupt, we have to mask it off while we wait for service. |
744 | * for service. | | | |
745 | */ | | 751 | */ |
746 | softint_schedule(sc->sc_sih); | | 752 | if (atomic_swap_uint(&sc->sc_work_pending, 1) == 0) |
| | | 753 | workqueue_enqueue(sc->sc_wq, &sc->sc_work, NULL); |
747 | ihidev_intr_mask(sc); | | 754 | ihidev_intr_mask(sc); |
748 | | | 755 | |
749 | mutex_exit(&sc->sc_intr_lock); | | | |
750 | | | | |
751 | return 1; | | 756 | return 1; |
752 | } | | 757 | } |
753 | | | 758 | |
754 | static void | | 759 | static void |
755 | ihidev_softintr(void *arg) | | 760 | ihidev_work(struct work *wk, void *arg) |
756 | { | | 761 | { |
757 | struct ihidev_softc * const sc = arg; | | 762 | struct ihidev_softc * const sc = arg; |
758 | struct ihidev *scd; | | 763 | struct ihidev *scd; |
759 | u_int psize; | | 764 | u_int psize; |
760 | int res, i; | | 765 | int res, i; |
761 | u_char *p; | | 766 | u_char *p; |
762 | u_int rep = 0; | | 767 | u_int rep = 0; |
763 | | | 768 | |
764 | mutex_enter(&sc->sc_intr_lock); | | 769 | atomic_store_relaxed(&sc->sc_work_pending, 0); |
| | | 770 | |
| | | 771 | mutex_enter(&sc->sc_lock); |
| | | 772 | |
765 | iic_acquire_bus(sc->sc_tag, 0); | | 773 | iic_acquire_bus(sc->sc_tag, 0); |
766 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, | | 774 | res = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, NULL, 0, |
767 | sc->sc_ibuf, sc->sc_isize, 0); | | 775 | sc->sc_ibuf, sc->sc_isize, 0); |
768 | iic_release_bus(sc->sc_tag, 0); | | 776 | iic_release_bus(sc->sc_tag, 0); |
769 | mutex_exit(&sc->sc_intr_lock); | | | |
770 | | | | |
771 | if (res != 0) | | 777 | if (res != 0) |
772 | goto out; | | 778 | goto out; |
773 | | | 779 | |
774 | /* | | 780 | /* |
775 | * 6.1.1 - First two bytes are the packet length, which must be less | | 781 | * 6.1.1 - First two bytes are the packet length, which must be less |
776 | * than or equal to wMaxInputLength | | 782 | * than or equal to wMaxInputLength |
777 | */ | | 783 | */ |
778 | psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; | | 784 | psize = sc->sc_ibuf[0] | sc->sc_ibuf[1] << 8; |
779 | if (!psize || psize > sc->sc_isize) { | | 785 | if (!psize || psize > sc->sc_isize) { |
780 | DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", | | 786 | DPRINTF(("%s: %s: invalid packet size (%d vs. %d)\n", |
781 | sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize)); | | 787 | sc->sc_dev.dv_xname, __func__, psize, sc->sc_isize)); |
782 | goto out; | | 788 | goto out; |
783 | } | | 789 | } |
| @@ -797,26 +803,28 @@ ihidev_softintr(void *arg) | | | @@ -797,26 +803,28 @@ ihidev_softintr(void *arg) |
797 | DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, | | 803 | DPRINTF(("%s: %s: hid input (rep %d):", sc->sc_dev.dv_xname, |
798 | __func__, rep)); | | 804 | __func__, rep)); |
799 | for (i = 0; i < sc->sc_isize; i++) | | 805 | for (i = 0; i < sc->sc_isize; i++) |
800 | DPRINTF((" %.2x", sc->sc_ibuf[i])); | | 806 | DPRINTF((" %.2x", sc->sc_ibuf[i])); |
801 | DPRINTF(("\n")); | | 807 | DPRINTF(("\n")); |
802 | | | 808 | |
803 | scd = sc->sc_subdevs[rep]; | | 809 | scd = sc->sc_subdevs[rep]; |
804 | if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) | | 810 | if (scd == NULL || !(scd->sc_state & IHIDEV_OPEN)) |
805 | goto out; | | 811 | goto out; |
806 | | | 812 | |
807 | scd->sc_intr(scd, p, psize); | | 813 | scd->sc_intr(scd, p, psize); |
808 | | | 814 | |
809 | out: | | 815 | out: |
| | | 816 | mutex_exit(&sc->sc_lock); |
| | | 817 | |
810 | /* | | 818 | /* |
811 | * If our interrupt is level-triggered, re-enable it now. | | 819 | * If our interrupt is level-triggered, re-enable it now. |
812 | */ | | 820 | */ |
813 | ihidev_intr_unmask(sc); | | 821 | ihidev_intr_unmask(sc); |
814 | } | | 822 | } |
815 | | | 823 | |
816 | static int | | 824 | static int |
817 | ihidev_maxrepid(void *buf, int len) | | 825 | ihidev_maxrepid(void *buf, int len) |
818 | { | | 826 | { |
819 | struct hid_data *d; | | 827 | struct hid_data *d; |
820 | struct hid_item h; | | 828 | struct hid_item h; |
821 | int maxid; | | 829 | int maxid; |
822 | | | 830 | |
| @@ -827,27 +835,27 @@ ihidev_maxrepid(void *buf, int len) | | | @@ -827,27 +835,27 @@ ihidev_maxrepid(void *buf, int len) |
827 | maxid = h.report_ID; | | 835 | maxid = h.report_ID; |
828 | hid_end_parse(d); | | 836 | hid_end_parse(d); |
829 | | | 837 | |
830 | return (maxid); | | 838 | return (maxid); |
831 | } | | 839 | } |
832 | | | 840 | |
833 | static int | | 841 | static int |
834 | ihidev_print(void *aux, const char *pnp) | | 842 | ihidev_print(void *aux, const char *pnp) |
835 | { | | 843 | { |
836 | struct ihidev_attach_arg *iha = aux; | | 844 | struct ihidev_attach_arg *iha = aux; |
837 | | | 845 | |
838 | if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) | | 846 | if (iha->reportid == IHIDEV_CLAIM_ALLREPORTID) |
839 | return (QUIET); | | 847 | return (QUIET); |
840 | | | 848 | |
841 | if (pnp) | | 849 | if (pnp) |
842 | aprint_normal("hid at %s", pnp); | | 850 | aprint_normal("hid at %s", pnp); |
843 | | | 851 | |
844 | if (iha->reportid != 0) | | 852 | if (iha->reportid != 0) |
845 | aprint_normal(" reportid %d", iha->reportid); | | 853 | aprint_normal(" reportid %d", iha->reportid); |
846 | | | 854 | |
847 | return (UNCONF); | | 855 | return (UNCONF); |
848 | } | | 856 | } |
849 | | | 857 | |
850 | static int | | 858 | static int |
851 | ihidev_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) | | 859 | ihidev_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) |
852 | { | | 860 | { |
853 | struct ihidev_attach_arg *iha = aux; | | 861 | struct ihidev_attach_arg *iha = aux; |
| @@ -964,27 +972,27 @@ ihidev_set_report(struct device *dev, in | | | @@ -964,27 +972,27 @@ ihidev_set_report(struct device *dev, in |
964 | rreq.data = data; | | 972 | rreq.data = data; |
965 | rreq.len = len; | | 973 | rreq.len = len; |
966 | | | 974 | |
967 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq, false)) { | | 975 | if (ihidev_hid_command(sc, I2C_HID_CMD_SET_REPORT, &rreq, false)) { |
968 | aprint_error_dev(sc->sc_dev, "failed setting report\n"); | | 976 | aprint_error_dev(sc->sc_dev, "failed setting report\n"); |
969 | return (1); | | 977 | return (1); |
970 | } | | 978 | } |
971 | | | 979 | |
972 | return 0; | | 980 | return 0; |
973 | } | | 981 | } |
974 | | | 982 | |
975 | static bool | | 983 | static bool |
976 | ihidev_acpi_get_info(struct ihidev_softc *sc) | | 984 | ihidev_acpi_get_info(struct ihidev_softc *sc) |
977 | { | | 985 | { |
978 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; | | 986 | ACPI_HANDLE hdl = (void *)(uintptr_t)sc->sc_phandle; |
979 | ACPI_STATUS status; | | 987 | ACPI_STATUS status; |
980 | ACPI_INTEGER val; | | 988 | ACPI_INTEGER val; |
981 | | | 989 | |
982 | /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ | | 990 | /* 3cdff6f7-4267-4555-ad05-b30a3d8938de */ |
983 | uint8_t i2c_hid_guid[] = { | | 991 | uint8_t i2c_hid_guid[] = { |
984 | 0xF7, 0xF6, 0xDF, 0x3C, | | 992 | 0xF7, 0xF6, 0xDF, 0x3C, |
985 | 0x67, 0x42, | | 993 | 0x67, 0x42, |
986 | 0x55, 0x45, | | 994 | 0x55, 0x45, |
987 | 0xAD, 0x05, | | 995 | 0xAD, 0x05, |
988 | 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, | | 996 | 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE, |
989 | }; | | 997 | }; |
990 | | | 998 | |