xref: /openbsd/sys/dev/acpi/acpihid.c (revision 2bcc3feb)
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