xref: /openbsd/sys/dev/acpi/acpihid.c (revision 3cab2bb3)
1 /* $OpenBSD: acpihid.c,v 1.2 2020/06/02 19:26:36 jcs 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 extern int pwr_action;
129 
130 struct cfattach acpihid_ca = {
131 	sizeof(struct acpihid_softc),
132 	acpihid_match,
133 	acpihid_attach,
134 	NULL,
135 	NULL,
136 };
137 
138 struct cfdriver acpihid_cd = {
139 	NULL, "acpihid", DV_DULL
140 };
141 
142 const char *acpihid_hids[] = {
143 	"INT33D5",
144 	NULL
145 };
146 
147 /* eeec56b3-4442-408f-a792-4edd4d758054 */
148 static uint8_t acpihid_guid[] = {
149 	0xB3, 0x56, 0xEC, 0xEE, 0x42, 0x44, 0x8F, 0x40,
150 	0xA7, 0x92, 0x4E, 0xDD, 0x4D, 0x75, 0x80, 0x54,
151 };
152 
153 int
154 acpihid_match(struct device *parent, void *match, void *aux)
155 {
156 	struct acpi_attach_args	*aa = aux;
157 	struct cfdata		*cf = match;
158 
159 	return (acpi_matchhids(aa, acpihid_hids, cf->cf_driver->cd_name));
160 }
161 
162 void
163 acpihid_attach(struct device *parent, struct device *self, void *aux)
164 {
165 	struct acpihid_softc	*sc = (struct acpihid_softc *)self;
166 	struct acpi_attach_args *aa = aux;
167 	uint64_t		 val;
168 
169 	sc->sc_acpi = (struct acpi_softc *)parent;
170 	sc->sc_devnode = aa->aaa_node;
171 
172 	printf(": %s", sc->sc_devnode->name);
173 
174 	acpihid_init_dsm(sc);
175 
176 	if (acpihid_eval(sc, ACPIHID_FUNC_HDMM, 0, &val) != 0) {
177 		printf(", failed reading mode\n");
178 		return;
179 	} else if (val != 0) {
180 		printf(", unknown mode %lld\n", val);
181 		return;
182 	}
183 
184 	if ((acpihid_eval(sc, ACPIHID_FUNC_HEBC_V2, 0, &val) == 0 &&
185 	    (val & 0x60000)) ||
186 	    (acpihid_eval(sc, ACPIHID_FUNC_HEBC_V1, 0, &val) == 0 &&
187 	    (val & 0x20000)))
188 		sc->sc_5_button = 1;
189 
190 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpihid_notify,
191 	    sc, ACPIDEV_NOPOLL);
192 
193 	/* enable hid set */
194 	acpihid_eval(sc, ACPIHID_FUNC_HDSM, 1, NULL);
195 
196 	if (sc->sc_5_button) {
197 		acpihid_button_array_enable(sc, 1);
198 
199 		if (acpihid_eval(sc, ACPIHID_FUNC_BTNL, 0, NULL) == 0)
200 			printf(", 5 button array");
201 		else
202 			printf(", failed enabling HID power button");
203 	}
204 
205 	printf("\n");
206 }
207 
208 void
209 acpihid_init_dsm(struct acpihid_softc *sc)
210 {
211 	struct aml_value cmd[4], res;
212 
213 	sc->sc_dsm_fn_mask = 0;
214 
215 	if (!aml_searchname(sc->sc_devnode, "_DSM")) {
216 		DPRINTF(("%s: no _DSM support\n", sc->sc_dev.dv_xname));
217 		return;
218 	}
219 
220 	bzero(&cmd, sizeof(cmd));
221 	cmd[0].type = AML_OBJTYPE_BUFFER;
222 	cmd[0].v_buffer = (uint8_t *)&acpihid_guid;
223 	cmd[0].length = sizeof(acpihid_guid);
224 	/* rev */
225 	cmd[1].type = AML_OBJTYPE_INTEGER;
226 	cmd[1].v_integer = 1;
227 	cmd[1].length = 1;
228 	/* func */
229 	cmd[2].type = AML_OBJTYPE_INTEGER;
230 	cmd[2].v_integer = 0;
231 	cmd[2].length = 1;
232 	/* not used */
233 	cmd[3].type = AML_OBJTYPE_BUFFER;
234 	cmd[3].length = 0;
235 
236 	if (aml_evalname(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
237 	    &res)) {
238 		printf("%s: eval of _DSM at %s failed\n",
239 		    sc->sc_dev.dv_xname, aml_nodename(sc->sc_devnode));
240 		return;
241 	}
242 
243 	if (res.type != AML_OBJTYPE_BUFFER) {
244 		printf("%s: bad _DSM result at %s: %d\n", sc->sc_dev.dv_xname,
245 		    aml_nodename(sc->sc_devnode), res.type);
246 		aml_freevalue(&res);
247 		return;
248 	}
249 
250 	sc->sc_dsm_fn_mask = *res.v_buffer;
251 	DPRINTF(("%s: _DSM function mask 0x%x\n", sc->sc_dev.dv_xname,
252 	    sc->sc_dsm_fn_mask));
253 
254 	aml_freevalue(&res);
255 }
256 
257 int
258 acpihid_eval(struct acpihid_softc *sc, int idx, int64_t arg, int64_t *ret)
259 {
260 	struct aml_value cmd[4], pkg, *ppkg;
261 	int64_t tret;
262 	const char *dsm_func;
263 
264 	if (idx <= ACPIHID_FUNC_INVALID || idx >= ACPIHID_FUNC_MAX) {
265 		printf("%s: _DSM func index %d out of bounds\n",
266 		    sc->sc_dev.dv_xname, idx);
267 		return 1;
268 	}
269 
270 	dsm_func = acpihid_dsm_funcs[idx];
271 
272 	DPRINTF(("%s: executing _DSM %s\n", sc->sc_dev.dv_xname, dsm_func));
273 
274 	if (!(sc->sc_dsm_fn_mask & idx)) {
275 		DPRINTF(("%s: _DSM mask does not support %s (%d), executing "
276 		    "directly\n", sc->sc_dev.dv_xname, dsm_func, idx));
277 		goto eval_direct;
278 	}
279 
280 	bzero(&pkg, sizeof(pkg));
281 	pkg.type = AML_OBJTYPE_INTEGER;
282 	pkg.v_integer = arg;
283 	pkg.length = 1;
284 	ppkg = &pkg;
285 
286 	bzero(&cmd, sizeof(cmd));
287 	cmd[0].type = AML_OBJTYPE_BUFFER;
288 	cmd[0].v_buffer = (uint8_t *)&acpihid_guid;
289 	cmd[0].length = sizeof(acpihid_guid);
290 	/* rev */
291 	cmd[1].type = AML_OBJTYPE_INTEGER;
292 	cmd[1].v_integer = 1;
293 	cmd[1].length = 1;
294 	/* func */
295 	cmd[2].type = AML_OBJTYPE_INTEGER;
296 	cmd[2].v_integer = idx;
297 	cmd[2].length = 1;
298 	/* arg */
299 	cmd[3].type = AML_OBJTYPE_PACKAGE;
300 	cmd[3].length = 1;
301 	cmd[3].v_package = &ppkg;
302 
303 	if (aml_evalinteger(acpi_softc, sc->sc_devnode, "_DSM", 4, cmd,
304 	    &tret)) {
305 		DPRINTF(("%s: _DSM %s failed\n", sc->sc_dev.dv_xname,
306 		    dsm_func));
307 		return 1;
308 	}
309 
310 	DPRINTF(("%s: _DSM eval of %s succeeded\n", sc->sc_dev.dv_xname,
311 	    dsm_func));
312 
313 	if (ret != NULL)
314 		*ret = tret;
315 
316 	return 0;
317 
318 eval_direct:
319 	cmd[0].type = AML_OBJTYPE_INTEGER;
320 	cmd[0].v_integer = arg;
321 	cmd[0].length = 1;
322 
323 	if (aml_evalinteger(acpi_softc, sc->sc_devnode, dsm_func, 1, cmd,
324 	    &tret) != 0) {
325 		printf("%s: exec of %s failed\n", sc->sc_dev.dv_xname,
326 		    dsm_func);
327 		return 1;
328 	}
329 
330 	if (ret != NULL)
331 		*ret = tret;
332 
333 	return 0;
334 }
335 
336 int
337 acpihid_button_array_enable(struct acpihid_softc *sc, int enable)
338 {
339 	int64_t cap;
340 
341 	if (aml_evalinteger(acpi_softc, sc->sc_devnode, "BTNC", 0, NULL,
342 	    &cap) != 0) {
343 		printf("%s: failed getting button array capability\n",
344 		    sc->sc_dev.dv_xname);
345 		return 1;
346 	}
347 
348 	if (acpihid_eval(sc, ACPIHID_FUNC_BTNE, enable ? cap : 1, NULL) != 0) {
349 		printf("%s: failed enabling button array\n",
350 		    sc->sc_dev.dv_xname);
351 		return 1;
352 	}
353 
354 	return 0;
355 }
356 
357 int
358 acpihid_notify(struct aml_node *node, int notify_type, void *arg)
359 {
360 #ifdef ACPIHID_DEBUG
361 	struct acpihid_softc *sc = arg;
362 
363 	DPRINTF(("%s: %s: %.2x\n", sc->sc_dev.dv_xname, __func__,
364 	    notify_type));
365 #endif
366 
367 	switch (notify_type) {
368 	case 0xc2: /* left meta press */
369 		break;
370 	case 0xc3: /* left meta release */
371 		break;
372 	case 0xc4: /* volume up press */
373 #if NAUDIO > 0 && NWSKBD > 0
374 		wskbd_set_mixervolume(1, 1);
375 #endif
376 		break;
377 	case 0xc5: /* volume up release */
378 		break;
379 	case 0xc6: /* volume down press */
380 #if NAUDIO > 0 && NWSKBD > 0
381 		wskbd_set_mixervolume(-1, 1);
382 #endif
383 		break;
384 	case 0xc7: /* volume down release */
385 		break;
386 	case 0xc8: /* rotate lock toggle press */
387 		break;
388 	case 0xc9: /* rotate lock toggle release */
389 		break;
390 	case 0xce: /* power button press */
391 		break;
392 	case 0xcf: /* power button release */
393 		break;
394 	default:
395 		DPRINTF(("%s: unhandled button 0x%x\n", sc->sc_dev.dv_xname,
396 		    notify_type));
397 	}
398 
399 	return 0;
400 }
401