Fri Jan 14 22:25:49 2022 UTC ()
ihidev(4): Fix locking and interrupt handler.

- Can't run iic_exec in softint because it does cv_wait, at least on
  some i2c controllers -- defer to workqueue instead.

- Fix violations of locking rules:
  . Do not take a lock at higher IPL than it is defined at!
  . Do not sleep under a lock!
  . Definitely do not sleep under a spin lock!
  In this case, sc_intr_lock was defined at IPL_VM but used at IPL_TTY,
  and i2c transactions -- possibly causing sleep for cv_wait -- were
  issued under it.

  But in this case, the interrupt handler needs only a single bit to
  mark whether the work is pending, so just use atomic_swap for that.

- Use an adaptive lock (IPL_NONE) for i2c transactions.

- Detach children, and do so before freeing anything.


(riastradh)
diff -r1.20 -r1.21 src/sys/dev/i2c/ihidev.c
diff -r1.5 -r1.6 src/sys/dev/i2c/ihidev.h

cvs diff -r1.20 -r1.21 src/sys/dev/i2c/ihidev.c (expand / switch to unified diff)

--- src/sys/dev/i2c/ihidev.c 2021/08/07 16:19:11 1.20
+++ src/sys/dev/i2c/ihidev.c 2022/01/14 22:25:49 1.21
@@ -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
104static int I2C_HID_POWER_ON = 0x0; 104static int I2C_HID_POWER_ON = 0x0;
105static int I2C_HID_POWER_OFF = 0x1; 105static int I2C_HID_POWER_OFF = 0x1;
106 106
107static int ihidev_match(device_t, cfdata_t, void *); 107static int ihidev_match(device_t, cfdata_t, void *);
108static void ihidev_attach(device_t, device_t, void *); 108static void ihidev_attach(device_t, device_t, void *);
109static int ihidev_detach(device_t, int); 109static int ihidev_detach(device_t, int);
110CFATTACH_DECL_NEW(ihidev, sizeof(struct ihidev_softc), 110CFATTACH_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
113static bool ihiddev_intr_init(struct ihidev_softc *); 113static bool ihidev_intr_init(struct ihidev_softc *);
114static void ihiddev_intr_fini(struct ihidev_softc *); 114static void ihidev_intr_fini(struct ihidev_softc *);
115 115
116static bool ihidev_suspend(device_t, const pmf_qual_t *); 116static bool ihidev_suspend(device_t, const pmf_qual_t *);
117static bool ihidev_resume(device_t, const pmf_qual_t *); 117static bool ihidev_resume(device_t, const pmf_qual_t *);
118static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool); 118static int ihidev_hid_command(struct ihidev_softc *, int, void *, bool);
119static int ihidev_intr(void *); 119static int ihidev_intr(void *);
120static void ihidev_softintr(void *); 120static void ihidev_work(struct work *, void *);
121static int ihidev_reset(struct ihidev_softc *, bool); 121static int ihidev_reset(struct ihidev_softc *, bool);
122static int ihidev_hid_desc_parse(struct ihidev_softc *); 122static int ihidev_hid_desc_parse(struct ihidev_softc *);
123 123
124static int ihidev_maxrepid(void *, int); 124static int ihidev_maxrepid(void *, int);
125static int ihidev_print(void *, const char *); 125static int ihidev_print(void *, const char *);
126static int ihidev_submatch(device_t, cfdata_t, const int *, void *); 126static int ihidev_submatch(device_t, cfdata_t, const int *, void *);
127 127
128static bool ihidev_acpi_get_info(struct ihidev_softc *); 128static bool ihidev_acpi_get_info(struct ihidev_softc *);
129 129
130static const struct device_compatible_entry compat_data[] = { 130static 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
150ihidev_attach(device_t parent, device_t self, void *aux) 150ihidev_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
256static int 257static int
257ihidev_detach(device_t self, int flags) 258ihidev_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
279static bool 287static bool
280ihidev_suspend(device_t self, const pmf_qual_t *q) 288ihidev_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
295static bool 303static bool
296ihidev_resume(device_t self, const pmf_qual_t *q) 304ihidev_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
309static int 317static int
310ihidev_hid_command(struct ihidev_softc *sc, int hidcmd, void *arg, bool poll) 318ihidev_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
648static bool 656static bool
649ihiddev_intr_init(struct ihidev_softc *sc) 657ihidev_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
699static void 708static void
700ihiddev_intr_fini(struct ihidev_softc *sc) 709ihidev_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
712static void 721static void
713ihidev_intr_mask(struct ihidev_softc * const sc) 722ihidev_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
734static int 743static int
735ihidev_intr(void *arg) 744ihidev_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
754static void 759static void
755ihidev_softintr(void *arg) 760ihidev_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
816static int 824static int
817ihidev_maxrepid(void *buf, int len) 825ihidev_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
833static int 841static int
834ihidev_print(void *aux, const char *pnp) 842ihidev_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
850static int 858static int
851ihidev_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux) 859ihidev_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
975static bool 983static bool
976ihidev_acpi_get_info(struct ihidev_softc *sc) 984ihidev_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

cvs diff -r1.5 -r1.6 src/sys/dev/i2c/ihidev.h (expand / switch to unified diff)

--- src/sys/dev/i2c/ihidev.h 2022/01/14 21:32:27 1.5
+++ src/sys/dev/i2c/ihidev.h 2022/01/14 22:25:49 1.6
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: ihidev.h,v 1.5 2022/01/14 21:32:27 riastradh Exp $ */ 1/* $NetBSD: ihidev.h,v 1.6 2022/01/14 22:25:49 riastradh Exp $ */
2/* $OpenBSD ihidev.h,v 1.4 2016/01/31 18:24:35 jcs Exp $ */ 2/* $OpenBSD ihidev.h,v 1.4 2016/01/31 18:24:35 jcs 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
@@ -96,38 +96,41 @@ struct i2c_hid_desc { @@ -96,38 +96,41 @@ struct i2c_hid_desc {
96 uint16_t wDataRegister; 96 uint16_t wDataRegister;
97 uint16_t wVendorID; 97 uint16_t wVendorID;
98 uint16_t wProductID; 98 uint16_t wProductID;
99 uint16_t wVersionID; 99 uint16_t wVersionID;
100 uint32_t reserved; 100 uint32_t reserved;
101} __packed; 101} __packed;
102 102
103/* ihidevvar.h */ 103/* ihidevvar.h */
104 104
105#include <sys/types.h> 105#include <sys/types.h>
106 106
107#include <sys/device.h> 107#include <sys/device.h>
108#include <sys/mutex.h> 108#include <sys/mutex.h>
 109#include <sys/workqueue.h>
109 110
110#include <dev/i2c/i2cvar.h> 111#include <dev/i2c/i2cvar.h>
111 112
112struct ihidev_softc { 113struct ihidev_softc {
113 device_t sc_dev; 114 device_t sc_dev;
114 i2c_tag_t sc_tag; 115 i2c_tag_t sc_tag;
115 i2c_addr_t sc_addr; 116 i2c_addr_t sc_addr;
116 uint64_t sc_phandle; 117 uint64_t sc_phandle;
 118 kmutex_t sc_lock;
117 119
118 void * sc_ih; 120 void * sc_ih;
119 void * sc_sih; 121 struct workqueue *sc_wq;
120 kmutex_t sc_intr_lock; 122 struct work sc_work;
 123 volatile unsigned sc_work_pending;
121 int sc_intr_type; 124 int sc_intr_type;
122 125
123 u_int sc_hid_desc_addr; 126 u_int sc_hid_desc_addr;
124 union { 127 union {
125 uint8_t hid_desc_buf[sizeof(struct i2c_hid_desc)]; 128 uint8_t hid_desc_buf[sizeof(struct i2c_hid_desc)];
126 struct i2c_hid_desc hid_desc; 129 struct i2c_hid_desc hid_desc;
127 }; 130 };
128 131
129 uint8_t *sc_report; 132 uint8_t *sc_report;
130 int sc_reportlen; 133 int sc_reportlen;
131 134
132 int sc_nrepid; 135 int sc_nrepid;
133 struct ihidev **sc_subdevs; 136 struct ihidev **sc_subdevs;