jcs's openbsd hax
openbsd
1/* $OpenBSD: inthid.c,v 1.1 2025/12/27 12:58:23 kettenis Exp $ */
2/*
3 * Intel HID event and 5-button array driver
4 *
5 * Copyright (c) 2018, 2020 joshua stein <jcs@jcs.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/signalvar.h>
22#include <sys/systm.h>
23#include <sys/device.h>
24#include <sys/malloc.h>
25
26#include <machine/bus.h>
27#include <machine/apmvar.h>
28
29#include <dev/acpi/acpireg.h>
30#include <dev/acpi/acpivar.h>
31#include <dev/acpi/acpidev.h>
32#include <dev/acpi/amltypes.h>
33#include <dev/acpi/dsdt.h>
34
35#include "audio.h"
36#include "wskbd.h"
37
38/* #define INTHID_DEBUG */
39
40#ifdef INTHID_DEBUG
41#define DPRINTF(x) printf x
42#else
43#define DPRINTF(x)
44#endif
45
46struct inthid_softc {
47 struct device sc_dev;
48
49 bus_space_tag_t sc_iot;
50 bus_space_handle_t sc_ioh;
51
52 struct acpi_softc *sc_acpi;
53 struct aml_node *sc_devnode;
54 int sc_5_button;
55
56 /*
57 * HEBC v1
58 * 0 - Rotation Lock, Num Lock, Home, End, Page Up, Page Down
59 * 1 - Wireless Radio Control
60 * 2 - System Power Down
61 * 3 - System Hibernate
62 * 4 - System Sleep/ System Wake
63 * 5 - Scan Next Track
64 * 6 - Scan Previous Track
65 * 7 - Stop
66 * 8 - Play/Pause
67 * 9 - Mute
68 * 10 - Volume Increment
69 * 11 - Volume Decrement
70 * 12 - Display Brightness Increment
71 * 13 - Display Brightness Decrement
72 * 14 - Lock Tablet
73 * 15 - Release Tablet
74 * 16 - Toggle Bezel
75 * 17 - 5 button array
76 * 18-31 - reserved
77 *
78 * HEBC v2
79 * 0-17 - Same as v1 version
80 * 18 – Power Button
81 * 19 - W Home Button
82 * 20 - Volume Up Button
83 * 21 - Volume Down Button
84 * 22 – Rotation Lock Button
85 * 23-31 – reserved
86 */
87 uint32_t sc_dsm_fn_mask;
88};
89
90enum {
91 INTHID_FUNC_INVALID,
92 INTHID_FUNC_BTNL,
93 INTHID_FUNC_HDMM,
94 INTHID_FUNC_HDSM,
95 INTHID_FUNC_HDEM,
96 INTHID_FUNC_BTNS,
97 INTHID_FUNC_BTNE,
98 INTHID_FUNC_HEBC_V1,
99 INTHID_FUNC_VGBS,
100 INTHID_FUNC_HEBC_V2,
101 INTHID_FUNC_MAX,
102};
103
104static const char *inthid_dsm_funcs[] = {
105 NULL,
106 "BTNL",
107 "HDMM",
108 "HDSM",
109 "HDEM",
110 "BTNS",
111 "BTNE",
112 "HEBC",
113 "VGBS",
114 "HEBC",
115};
116
117int inthid_match(struct device *, void *, void *);
118void inthid_attach(struct device *, struct device *, void *);
119void inthid_init_dsm(struct inthid_softc *);
120int inthid_button_array_enable(struct inthid_softc *, int);
121int inthid_eval(struct inthid_softc *, int, int64_t, int64_t *);
122int inthid_notify(struct aml_node *, int, void *);
123
124#if NAUDIO > 0 && NWSKBD > 0
125extern int wskbd_set_mixervolume(long, long);
126#endif
127
128const struct cfattach inthid_ca = {
129 sizeof(struct inthid_softc),
130 inthid_match,
131 inthid_attach,
132 NULL,
133 NULL,
134};
135
136struct cfdriver inthid_cd = {
137 NULL, "inthid", DV_DULL
138};
139
140const char *inthid_hids[] = {
141 "INT33D5",
142 NULL
143};
144
145/* eeec56b3-4442-408f-a792-4edd4d758054 */
146static uint8_t inthid_guid[] = {
147 0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40,
148 0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54,
149};
150
151int
152inthid_match(struct device *parent, void *match, void *aux)
153{
154 struct acpi_attach_args *aa = aux;
155 struct cfdata *cf = match;
156
157 return (acpi_matchhids(aa, inthid_hids, cf->cf_driver->cd_name));
158}
159
160void
161inthid_attach(struct device *parent, struct device *self, void *aux)
162{
163 struct inthid_softc *sc = (struct inthid_softc *)self;
164 struct acpi_attach_args *aa = aux;
165 uint64_t val;
166
167 sc->sc_acpi = (struct acpi_softc *)parent;
168 sc->sc_devnode = aa->aaa_node;
169
170 printf(": %s", sc->sc_devnode->name);
171
172 inthid_init_dsm(sc);
173
174 if (inthid_eval(sc, INTHID_FUNC_HDMM, 0, &val) != 0) {
175 printf(", failed reading mode\n");
176 return;
177 } else if (val != 0) {
178 printf(", unknown mode %lld\n", val);
179 return;
180 }
181
182 if ((inthid_eval(sc, INTHID_FUNC_HEBC_V2, 0, &val) == 0 &&
183 (val & 0x60000)) ||
184 (inthid_eval(sc, INTHID_FUNC_HEBC_V1, 0, &val) == 0 &&
185 (val & 0x20000)))
186 sc->sc_5_button = 1;
187
188 aml_register_notify(sc->sc_devnode, aa->aaa_dev, inthid_notify,
189 sc, ACPIDEV_NOPOLL);
190
191 /* enable hid set */
192 inthid_eval(sc, INTHID_FUNC_HDSM, 1, NULL);
193
194 if (sc->sc_5_button) {
195 inthid_button_array_enable(sc, 1);
196
197 if (inthid_eval(sc, INTHID_FUNC_BTNL, 0, NULL) == 0)
198 printf(", 5 button array");
199 else
200 printf(", failed enabling HID power button");
201 }
202
203 printf("\n");
204}
205
206void
207inthid_init_dsm(struct inthid_softc *sc)
208{
209 struct aml_value cmd[4], res;
210
211 sc->sc_dsm_fn_mask = 0;
212
213 if (!aml_searchname(sc->sc_devnode, "_DSM")) {
214 DPRINTF(("%s: no _DSM support\n", sc->sc_dev.dv_xname));
215 return;
216 }
217
218 bzero(&cmd, sizeof(cmd));
219 cmd[0].type = AML_OBJTYPE_BUFFER;
220 cmd[0].v_buffer = (uint8_t *)&inthid_guid;
221 cmd[0].length = sizeof(inthid_guid);
222 /* rev */
223 cmd[1].type = AML_OBJTYPE_INTEGER;
224 cmd[1].v_integer = 1;
225 cmd[1].length = 1;
226 /* func */
227 cmd[2].type = AML_OBJTYPE_INTEGER;
228 cmd[2].v_integer = 0;
229 cmd[2].length = 1;
230 /* not used */
231 cmd[3].type = AML_OBJTYPE_BUFFER;
232 cmd[3].length = 0;
233
234 if (aml_evalname(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
235 &res)) {
236 printf("%s: eval of _DSM at %s failed\n",
237 sc->sc_dev.dv_xname, aml_nodename(sc->sc_devnode));
238 return;
239 }
240
241 if (res.type != AML_OBJTYPE_BUFFER) {
242 printf("%s: bad _DSM result at %s: %d\n", sc->sc_dev.dv_xname,
243 aml_nodename(sc->sc_devnode), res.type);
244 aml_freevalue(&res);
245 return;
246 }
247
248 sc->sc_dsm_fn_mask = *res.v_buffer;
249 DPRINTF(("%s: _DSM function mask 0x%x\n", sc->sc_dev.dv_xname,
250 sc->sc_dsm_fn_mask));
251
252 aml_freevalue(&res);
253}
254
255int
256inthid_eval(struct inthid_softc *sc, int idx, int64_t arg, int64_t *ret)
257{
258 struct aml_value cmd[4], pkg, *ppkg;
259 int64_t tret;
260 const char *dsm_func;
261
262 if (idx <= INTHID_FUNC_INVALID || idx >= INTHID_FUNC_MAX) {
263 printf("%s: _DSM func index %d out of bounds\n",
264 sc->sc_dev.dv_xname, idx);
265 return 1;
266 }
267
268 dsm_func = inthid_dsm_funcs[idx];
269
270 DPRINTF(("%s: executing _DSM %s\n", sc->sc_dev.dv_xname, dsm_func));
271
272 if (!(sc->sc_dsm_fn_mask & idx)) {
273 DPRINTF(("%s: _DSM mask does not support %s (%d), executing "
274 "directly\n", sc->sc_dev.dv_xname, dsm_func, idx));
275 goto eval_direct;
276 }
277
278 bzero(&pkg, sizeof(pkg));
279 pkg.type = AML_OBJTYPE_INTEGER;
280 pkg.v_integer = arg;
281 pkg.length = 1;
282 ppkg = &pkg;
283
284 bzero(&cmd, sizeof(cmd));
285 cmd[0].type = AML_OBJTYPE_BUFFER;
286 cmd[0].v_buffer = (uint8_t *)&inthid_guid;
287 cmd[0].length = sizeof(inthid_guid);
288 /* rev */
289 cmd[1].type = AML_OBJTYPE_INTEGER;
290 cmd[1].v_integer = 1;
291 cmd[1].length = 1;
292 /* func */
293 cmd[2].type = AML_OBJTYPE_INTEGER;
294 cmd[2].v_integer = idx;
295 cmd[2].length = 1;
296 /* arg */
297 cmd[3].type = AML_OBJTYPE_PACKAGE;
298 cmd[3].length = 1;
299 cmd[3].v_package = &ppkg;
300
301 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
302 &tret)) {
303 DPRINTF(("%s: _DSM %s failed\n", sc->sc_dev.dv_xname,
304 dsm_func));
305 return 1;
306 }
307
308 DPRINTF(("%s: _DSM eval of %s succeeded\n", sc->sc_dev.dv_xname,
309 dsm_func));
310
311 if (ret != NULL)
312 *ret = tret;
313
314 return 0;
315
316eval_direct:
317 cmd[0].type = AML_OBJTYPE_INTEGER;
318 cmd[0].v_integer = arg;
319 cmd[0].length = 1;
320
321 if (aml_evalinteger(acpi_softc, sc->sc_devnode, dsm_func, 1, cmd,
322 &tret) != 0) {
323 printf("%s: exec of %s failed\n", sc->sc_dev.dv_xname,
324 dsm_func);
325 return 1;
326 }
327
328 if (ret != NULL)
329 *ret = tret;
330
331 return 0;
332}
333
334int
335inthid_button_array_enable(struct inthid_softc *sc, int enable)
336{
337 int64_t cap;
338
339 if (aml_evalinteger(acpi_softc, sc->sc_devnode, "BTNC", 0, NULL,
340 &cap) != 0) {
341 printf("%s: failed getting button array capability\n",
342 sc->sc_dev.dv_xname);
343 return 1;
344 }
345
346 if (inthid_eval(sc, INTHID_FUNC_BTNE, enable ? cap : 1, NULL) != 0) {
347 printf("%s: failed enabling button array\n",
348 sc->sc_dev.dv_xname);
349 return 1;
350 }
351
352 return 0;
353}
354
355int
356inthid_notify(struct aml_node *node, int notify_type, void *arg)
357{
358#ifdef INTHID_DEBUG
359 struct inthid_softc *sc = arg;
360
361 DPRINTF(("%s: %s: %.2x\n", sc->sc_dev.dv_xname, __func__,
362 notify_type));
363#endif
364
365 switch (notify_type) {
366 case 0xc2: /* left meta press */
367 break;
368 case 0xc3: /* left meta release */
369 break;
370 case 0xc4: /* volume up press */
371#if NAUDIO > 0 && NWSKBD > 0
372 wskbd_set_mixervolume(1, 1);
373#endif
374 break;
375 case 0xc5: /* volume up release */
376 break;
377 case 0xc6: /* volume down press */
378#if NAUDIO > 0 && NWSKBD > 0
379 wskbd_set_mixervolume(-1, 1);
380#endif
381 break;
382 case 0xc7: /* volume down release */
383 break;
384 case 0xc8: /* rotate lock toggle press */
385 break;
386 case 0xc9: /* rotate lock toggle release */
387 break;
388 case 0xce: /* power button press */
389 break;
390 case 0xcf: /* power button release */
391 break;
392 default:
393 DPRINTF(("%s: unhandled button 0x%x\n", sc->sc_dev.dv_xname,
394 notify_type));
395 }
396
397 return 0;
398}