1 /* $OpenBSD: acpihid.c,v 1.4 2022/05/29 22:03:44 jca Exp $ */
2 /*
3 * ACPI 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 ACPIHID_DEBUG */
39
40 #ifdef ACPIHID_DEBUG
41 #define DPRINTF(x) printf x
42 #else
43 #define DPRINTF(x)
44 #endif
45
46 struct acpihid_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
90 enum {
91 ACPIHID_FUNC_INVALID,
92 ACPIHID_FUNC_BTNL,
93 ACPIHID_FUNC_HDMM,
94 ACPIHID_FUNC_HDSM,
95 ACPIHID_FUNC_HDEM,
96 ACPIHID_FUNC_BTNS,
97 ACPIHID_FUNC_BTNE,
98 ACPIHID_FUNC_HEBC_V1,
99 ACPIHID_FUNC_VGBS,
100 ACPIHID_FUNC_HEBC_V2,
101 ACPIHID_FUNC_MAX,
102 };
103
104 static const char *acpihid_dsm_funcs[] = {
105 NULL,
106 "BTNL",
107 "HDMM",
108 "HDSM",
109 "HDEM",
110 "BTNS",
111 "BTNE",
112 "HEBC",
113 "VGBS",
114 "HEBC",
115 };
116
117 int acpihid_match(struct device *, void *, void *);
118 void acpihid_attach(struct device *, struct device *, void *);
119 void acpihid_init_dsm(struct acpihid_softc *);
120 int acpihid_button_array_enable(struct acpihid_softc *, int);
121 int acpihid_eval(struct acpihid_softc *, int, int64_t, int64_t *);
122 int acpihid_notify(struct aml_node *, int, void *);
123
124 #if NAUDIO > 0 && NWSKBD > 0
125 extern int wskbd_set_mixervolume(long, long);
126 #endif
127
128 const struct cfattach acpihid_ca = {
129 sizeof(struct acpihid_softc),
130 acpihid_match,
131 acpihid_attach,
132 NULL,
133 NULL,
134 };
135
136 struct cfdriver acpihid_cd = {
137 NULL, "acpihid", DV_DULL
138 };
139
140 const char *acpihid_hids[] = {
141 "INT33D5",
142 NULL
143 };
144
145 /* eeec56b3-4442-408f-a792-4edd4d758054 */
146 static uint8_t acpihid_guid[] = {
147 0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40,
148 0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54,
149 };
150
151 int
acpihid_match(struct device * parent,void * match,void * aux)152 acpihid_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, acpihid_hids, cf->cf_driver->cd_name));
158 }
159
160 void
acpihid_attach(struct device * parent,struct device * self,void * aux)161 acpihid_attach(struct device *parent, struct device *self, void *aux)
162 {
163 struct acpihid_softc *sc = (struct acpihid_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 acpihid_init_dsm(sc);
173
174 if (acpihid_eval(sc, ACPIHID_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 ((acpihid_eval(sc, ACPIHID_FUNC_HEBC_V2, 0, &val) == 0 &&
183 (val & 0x60000)) ||
184 (acpihid_eval(sc, ACPIHID_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, acpihid_notify,
189 sc, ACPIDEV_NOPOLL);
190
191 /* enable hid set */
192 acpihid_eval(sc, ACPIHID_FUNC_HDSM, 1, NULL);
193
194 if (sc->sc_5_button) {
195 acpihid_button_array_enable(sc, 1);
196
197 if (acpihid_eval(sc, ACPIHID_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
206 void
acpihid_init_dsm(struct acpihid_softc * sc)207 acpihid_init_dsm(struct acpihid_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 *)&acpihid_guid;
221 cmd[0].length = sizeof(acpihid_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
255 int
acpihid_eval(struct acpihid_softc * sc,int idx,int64_t arg,int64_t * ret)256 acpihid_eval(struct acpihid_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 <= ACPIHID_FUNC_INVALID || idx >= ACPIHID_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 = acpihid_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 *)&acpihid_guid;
287 cmd[0].length = sizeof(acpihid_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
316 eval_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
334 int
acpihid_button_array_enable(struct acpihid_softc * sc,int enable)335 acpihid_button_array_enable(struct acpihid_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 (acpihid_eval(sc, ACPIHID_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
355 int
acpihid_notify(struct aml_node * node,int notify_type,void * arg)356 acpihid_notify(struct aml_node *node, int notify_type, void *arg)
357 {
358 #ifdef ACPIHID_DEBUG
359 struct acpihid_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 }
399