xref: /openbsd-src/sys/dev/acpi/acpithinkpad.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: acpithinkpad.c,v 1.68 2019/12/31 01:38:33 jsg Exp $	*/
2 /*
3  * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/sensors.h>
21 
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.h>
27 #include <dev/wscons/wsconsio.h>
28 #include <dev/wscons/wsdisplayvar.h>
29 
30 #include <machine/apmvar.h>
31 
32 #include "audio.h"
33 #include "wskbd.h"
34 
35 /* #define ACPITHINKPAD_DEBUG */
36 
37 #ifdef ACPITHINKPAD_DEBUG
38 #define DPRINTF(x) printf x
39 #else
40 #define DPRINTF(x)
41 #endif
42 
43 #define	THINKPAD_HKEY_VERSION1		0x0100
44 #define	THINKPAD_HKEY_VERSION2		0x0200
45 
46 #define	THINKPAD_CMOS_VOLUME_DOWN	0x00
47 #define	THINKPAD_CMOS_VOLUME_UP		0x01
48 #define	THINKPAD_CMOS_VOLUME_MUTE	0x02
49 #define	THINKPAD_CMOS_BRIGHTNESS_UP	0x04
50 #define	THINKPAD_CMOS_BRIGHTNESS_DOWN	0x05
51 
52 #define	THINKPAD_BLUETOOTH_PRESENT	0x01
53 #define	THINKPAD_BLUETOOTH_ENABLED	0x02
54 
55 /* wan (not wifi) card */
56 #define	THINKPAD_WAN_PRESENT		0x01
57 #define	THINKPAD_WAN_ENABLED		0x02
58 
59 #define	THINKPAD_BUTTON_FN_F1		0x1001
60 #define	THINKPAD_BUTTON_LOCK_SCREEN	0x1002
61 #define	THINKPAD_BUTTON_BATTERY_INFO	0x1003
62 #define	THINKPAD_BUTTON_SUSPEND		0x1004
63 #define	THINKPAD_BUTTON_WIRELESS	0x1005
64 #define	THINKPAD_BUTTON_FN_F6		0x1006
65 #define	THINKPAD_BUTTON_EXTERNAL_SCREEN	0x1007
66 #define	THINKPAD_BUTTON_POINTER_SWITCH	0x1008
67 #define	THINKPAD_BUTTON_EJECT		0x1009
68 #define	THINKPAD_BUTTON_FN_F11		0x100b
69 #define	THINKPAD_BUTTON_HIBERNATE	0x100c
70 #define	THINKPAD_BUTTON_BRIGHTNESS_UP	0x1010
71 #define	THINKPAD_BUTTON_BRIGHTNESS_DOWN	0x1011
72 #define	THINKPAD_BUTTON_THINKLIGHT	0x1012
73 #define	THINKPAD_BUTTON_FN_SPACE	0x1014
74 #define	THINKPAD_BUTTON_VOLUME_UP	0x1015
75 #define	THINKPAD_BUTTON_VOLUME_DOWN	0x1016
76 #define	THINKPAD_BUTTON_VOLUME_MUTE	0x1017
77 #define	THINKPAD_BUTTON_THINKVANTAGE	0x1018
78 #define	THINKPAD_BUTTON_BLACK		0x101a
79 #define	THINKPAD_BUTTON_MICROPHONE_MUTE	0x101b
80 #define	THINKPAD_KEYLIGHT_CHANGED	0x101c
81 #define	THINKPAD_BUTTON_CONFIG		0x101d
82 #define	THINKPAD_BUTTON_FIND		0x101e
83 #define	THINKPAD_BUTTON_ALL_ACTIVEPROGS	0x101f
84 #define	THINKPAD_BUTTON_ALL_PROGS	0x1020
85 
86 #define	THINKPAD_ADAPTIVE_NEXT		0x1101
87 #define	THINKPAD_ADAPTIVE_QUICK		0x1102
88 #define	THINKPAD_ADAPTIVE_SNIP		0x1105
89 #define	THINKPAD_ADAPTIVE_VOICE		0x1108
90 #define	THINKPAD_ADAPTIVE_GESTURES	0x110a
91 #define	THINKPAD_ADAPTIVE_SETTINGS	0x110e
92 #define	THINKPAD_ADAPTIVE_TAB		0x110f
93 #define	THINKPAD_ADAPTIVE_REFRESH	0x1110
94 #define	THINKPAD_ADAPTIVE_BACK		0x1111
95 #define THINKPAD_PORT_REPL_DOCKED	0x4010
96 #define THINKPAD_PORT_REPL_UNDOCKED	0x4011
97 #define	THINKPAD_TABLET_DOCKED		0x4012
98 #define	THINKPAD_TABLET_UNDOCKED	0x4013
99 #define	THINKPAD_LID_OPEN		0x5001
100 #define	THINKPAD_LID_CLOSED		0x5002
101 #define	THINKPAD_TABLET_SCREEN_NORMAL	0x500a
102 #define	THINKPAD_TABLET_SCREEN_ROTATED	0x5009
103 #define	THINKPAD_BRIGHTNESS_CHANGED	0x5010
104 #define	THINKPAD_TABLET_PEN_INSERTED	0x500b
105 #define	THINKPAD_TABLET_PEN_REMOVED	0x500c
106 #define	THINKPAD_SWITCH_NUMLOCK		0x6000
107 #define	THINKPAD_BUTTON_ROTATION_LOCK	0x6020
108 #define	THINKPAD_THERMAL_TABLE_CHANGED	0x6030
109 #define	THINKPAD_POWER_CHANGED		0x6040
110 #define	THINKPAD_BACKLIGHT_CHANGED	0x6050
111 #define	THINKPAD_BUTTON_FN_TOGGLE       0x6060
112 #define	THINKPAD_TABLET_SCREEN_CHANGED	0x60c0
113 #define	THINKPAD_SWITCH_WIRELESS	0x7000
114 
115 #define THINKPAD_NSENSORS 10
116 #define THINKPAD_NTEMPSENSORS 8
117 
118 #define THINKPAD_SENSOR_FANRPM		THINKPAD_NTEMPSENSORS
119 #define THINKPAD_SENSOR_PORTREPL	THINKPAD_NTEMPSENSORS + 1
120 
121 #define THINKPAD_ECOFFSET_VOLUME	0x30
122 #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
123 #define THINKPAD_ECOFFSET_FANLO		0x84
124 #define THINKPAD_ECOFFSET_FANHI		0x85
125 
126 #define	THINKPAD_ADAPTIVE_MODE_HOME	1
127 #define	THINKPAD_ADAPTIVE_MODE_FUNCTION	3
128 
129 #define THINKPAD_MASK_MIC_MUTE		(1 << 14)
130 #define THINKPAD_MASK_BRIGHTNESS_UP	(1 << 15)
131 #define THINKPAD_MASK_BRIGHTNESS_DOWN	(1 << 16)
132 #define THINKPAD_MASK_KBD_BACKLIGHT	(1 << 17)
133 
134 struct acpithinkpad_softc {
135 	struct device		 sc_dev;
136 
137 	struct acpiec_softc     *sc_ec;
138 	struct acpi_softc	*sc_acpi;
139 	struct aml_node		*sc_devnode;
140 
141 	struct ksensor		 sc_sens[THINKPAD_NSENSORS];
142 	struct ksensordev	 sc_sensdev;
143 
144 	uint64_t		 sc_hkey_version;
145 
146 	uint64_t		 sc_thinklight;
147 	const char		*sc_thinklight_get;
148 	const char		*sc_thinklight_set;
149 
150 	uint64_t		 sc_brightness;
151 };
152 
153 extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
154 
155 int	thinkpad_match(struct device *, void *, void *);
156 void	thinkpad_attach(struct device *, struct device *, void *);
157 int	thinkpad_hotkey(struct aml_node *, int, void *);
158 int	thinkpad_enable_events(struct acpithinkpad_softc *);
159 int	thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
160 int	thinkpad_toggle_wan(struct acpithinkpad_softc *);
161 int	thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
162 int	thinkpad_volume_down(struct acpithinkpad_softc *);
163 int	thinkpad_volume_up(struct acpithinkpad_softc *);
164 int	thinkpad_volume_mute(struct acpithinkpad_softc *);
165 int	thinkpad_brightness_up(struct acpithinkpad_softc *);
166 int	thinkpad_brightness_down(struct acpithinkpad_softc *);
167 int	thinkpad_adaptive_change(struct acpithinkpad_softc *);
168 int	thinkpad_activate(struct device *, int);
169 
170 /* wscons hook functions */
171 void	thinkpad_get_thinklight(struct acpithinkpad_softc *);
172 void	thinkpad_set_thinklight(void *, int);
173 int	thinkpad_get_kbd_backlight(struct wskbd_backlight *);
174 int	thinkpad_set_kbd_backlight(struct wskbd_backlight *);
175 extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
176 extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
177 int	thinkpad_get_brightness(struct acpithinkpad_softc *);
178 int	thinkpad_set_brightness(void *, int);
179 int	thinkpad_get_param(struct wsdisplay_param *);
180 int	thinkpad_set_param(struct wsdisplay_param *);
181 
182 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
183 void    thinkpad_sensor_refresh(void *);
184 
185 #if NAUDIO > 0 && NWSKBD > 0
186 void thinkpad_attach_deferred(void *);
187 int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
188 extern int wskbd_set_mixermute(long, long);
189 extern int wskbd_set_mixervolume(long, long);
190 #endif
191 
192 struct cfattach acpithinkpad_ca = {
193 	sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
194 	NULL, thinkpad_activate
195 };
196 
197 struct cfdriver acpithinkpad_cd = {
198 	NULL, "acpithinkpad", DV_DULL
199 };
200 
201 const char *acpithinkpad_hids[] = {
202 	"IBM0068",
203 	"LEN0068",
204 	"LEN0268",
205 	NULL
206 };
207 
208 int
209 thinkpad_match(struct device *parent, void *match, void *aux)
210 {
211 	struct acpi_attach_args	*aa = aux;
212 	struct cfdata *cf = match;
213 	int64_t	res;
214 
215 	if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
216 		return (0);
217 
218 	if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
219 	    "MHKV", 0, NULL, &res))
220 		return (0);
221 
222 	if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
223 		return (0);
224 
225 	return (1);
226 }
227 
228 void
229 thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
230 {
231 	int i;
232 
233 	if (sc->sc_acpi->sc_ec == NULL)
234 		return;
235 	sc->sc_ec = sc->sc_acpi->sc_ec;
236 
237 	/* Add temperature probes */
238 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
239 	    sizeof(sc->sc_sensdev.xname));
240 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
241 		sc->sc_sens[i].type = SENSOR_TEMP;
242 		sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
243 	}
244 
245 	/* Add fan probe */
246 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
247 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
248 
249 	/* Add port replicator indicator */
250 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
251 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
252 	strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
253 	        sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
254 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
255 
256 	sensordev_install(&sc->sc_sensdev);
257 }
258 
259 void
260 thinkpad_sensor_refresh(void *arg)
261 {
262 	struct acpithinkpad_softc *sc = arg;
263 	uint8_t lo, hi, i;
264 	int64_t tmp;
265 	char sname[5];
266 
267 	/* Refresh sensor readings */
268 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
269 		snprintf(sname, sizeof(sname), "TMP%d", i);
270 		aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode,
271 		    sname, 0, 0, &tmp);
272 		sc->sc_sens[i].value = (tmp * 1000000) + 273150000;
273 		if (tmp > 127 || tmp < -127)
274 			sc->sc_sens[i].flags = SENSOR_FINVALID;
275 	}
276 
277 	/* Read fan RPM */
278 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
279 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
280 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
281 }
282 
283 void
284 thinkpad_attach(struct device *parent, struct device *self, void *aux)
285 {
286 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
287 	struct acpi_attach_args	*aa = aux;
288 
289 	sc->sc_acpi = (struct acpi_softc *)parent;
290 	sc->sc_devnode = aa->aaa_node;
291 
292 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
293 	    &sc->sc_hkey_version))
294 		sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
295 
296 	printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8,
297 	    sc->sc_hkey_version & 0xff);
298 
299 #if NAUDIO > 0 && NWSKBD > 0
300 	/* Defer speaker mute */
301 	if (thinkpad_get_volume_mute(sc) == 1)
302 		startuphook_establish(thinkpad_attach_deferred, sc);
303 #endif
304 
305 	/* Set event mask to receive everything */
306 	thinkpad_enable_events(sc);
307 	thinkpad_sensor_attach(sc);
308 
309 	/* Check for ThinkLight or keyboard backlight */
310 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
311 	    0, NULL, &sc->sc_thinklight) == 0) {
312 		sc->sc_thinklight_get = "KLCG";
313 		sc->sc_thinklight_set = "KLCS";
314 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
315 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
316 	} else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
317 	    0, NULL, &sc->sc_thinklight) == 0) {
318 		sc->sc_thinklight_get = "MLCG";
319 		sc->sc_thinklight_set = "MLCS";
320 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
321 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
322 	}
323 
324 	/* On version 2 and newer, let *drm or acpivout control brightness */
325 	if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 &&
326 	    (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
327 	    0, NULL, &sc->sc_brightness) == 0)) {
328 		ws_get_param = thinkpad_get_param;
329 		ws_set_param = thinkpad_set_param;
330 	}
331 
332 	/* Run thinkpad_hotkey on button presses */
333 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
334 	    thinkpad_hotkey, sc, ACPIDEV_POLL);
335 }
336 
337 int
338 thinkpad_enable_events(struct acpithinkpad_softc *sc)
339 {
340 	struct aml_value arg, args[2];
341 	int64_t	mask;
342 	int i;
343 
344 	/* Get the default event mask */
345 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
346 	    0, NULL, &mask)) {
347 		printf("%s: no MHKA\n", DEVNAME(sc));
348 		return (1);
349 	}
350 
351 	/* Enable events we need to know about */
352 	mask |= (THINKPAD_MASK_MIC_MUTE	|
353 	    THINKPAD_MASK_BRIGHTNESS_UP |
354 	    THINKPAD_MASK_BRIGHTNESS_DOWN |
355 	    THINKPAD_MASK_KBD_BACKLIGHT);
356 
357 	DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
358 
359 	/* Update hotkey mask */
360 	bzero(args, sizeof(args));
361 	args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
362 	for (i = 0; i < 32; i++) {
363 		args[0].v_integer = i + 1;
364 		args[1].v_integer = (((1 << i) & mask) != 0);
365 
366 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
367 		    2, args, NULL)) {
368 			printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
369 			return (1);
370 		}
371 	}
372 
373 	/* Enable hotkeys */
374 	bzero(&arg, sizeof(arg));
375 	arg.type = AML_OBJTYPE_INTEGER;
376 	arg.v_integer = 1;
377 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
378 	    1, &arg, NULL)) {
379 		printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
380 		return (1);
381 	}
382 
383 	return (0);
384 }
385 
386 int
387 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
388 {
389 	struct acpithinkpad_softc *sc = arg;
390 	int64_t	event;
391 
392 	if (notify_type == 0x00) {
393 		/* Poll sensors */
394 		thinkpad_sensor_refresh(sc);
395 		return (0);
396 	}
397 
398 	if (notify_type != 0x80)
399 		return (1);
400 
401 	for (;;) {
402 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
403 		    0, NULL, &event))
404 			break;
405 
406 		DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
407 		if (event == 0)
408 			break;
409 
410 		switch (event) {
411 		case THINKPAD_BUTTON_BRIGHTNESS_UP:
412 			thinkpad_brightness_up(sc);
413 			break;
414 		case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
415 			thinkpad_brightness_down(sc);
416 			break;
417 		case THINKPAD_BUTTON_WIRELESS:
418 			thinkpad_toggle_bluetooth(sc);
419 			break;
420 		case THINKPAD_BUTTON_SUSPEND:
421 #ifndef SMALL_KERNEL
422 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
423 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
424 				    sc->sc_acpi, ACPI_SLEEP_SUSPEND);
425 #endif
426 			break;
427 		case THINKPAD_BUTTON_VOLUME_MUTE:
428 			thinkpad_volume_mute(sc);
429 			break;
430 		case THINKPAD_BUTTON_VOLUME_DOWN:
431 			thinkpad_volume_down(sc);
432 			break;
433 		case THINKPAD_BUTTON_VOLUME_UP:
434 			thinkpad_volume_up(sc);
435 			break;
436 		case THINKPAD_BUTTON_MICROPHONE_MUTE:
437 #if NAUDIO > 0 && NWSKBD > 0
438 			wskbd_set_mixervolume(0, 0);
439 #endif
440 			break;
441 		case THINKPAD_BUTTON_HIBERNATE:
442 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
443 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
444 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
445 				    sc->sc_acpi, ACPI_SLEEP_HIBERNATE);
446 #endif
447 			break;
448 		case THINKPAD_BUTTON_THINKLIGHT:
449 			thinkpad_get_thinklight(sc);
450 			break;
451 		case THINKPAD_ADAPTIVE_NEXT:
452 		case THINKPAD_ADAPTIVE_QUICK:
453 			thinkpad_adaptive_change(sc);
454 			break;
455 		case THINKPAD_BACKLIGHT_CHANGED:
456 			thinkpad_get_brightness(sc);
457 			break;
458 		case THINKPAD_PORT_REPL_DOCKED:
459 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
460 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
461 			    SENSOR_S_OK;
462 			break;
463 		case THINKPAD_PORT_REPL_UNDOCKED:
464 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
465 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
466 			    SENSOR_S_OK;
467 			break;
468 		default:
469 			/* unknown or boring event */
470 			DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
471 			    event));
472 			break;
473 		}
474 	}
475 
476 	return (0);
477 }
478 
479 int
480 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
481 {
482 	struct aml_value arg;
483 	int64_t	bluetooth;
484 
485 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
486 	    0, NULL, &bluetooth))
487 		return (1);
488 
489 	if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
490 		return (1);
491 
492 	bzero(&arg, sizeof(arg));
493 	arg.type = AML_OBJTYPE_INTEGER;
494 	arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
495 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
496 	    1, &arg, NULL)) {
497 		printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
498 		return (1);
499 	}
500 
501 	return (0);
502 }
503 
504 int
505 thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
506 {
507 	struct aml_value arg;
508 	int64_t wan;
509 
510 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
511 	    0, NULL, &wan))
512 		return (1);
513 
514 	if (!(wan & THINKPAD_WAN_PRESENT))
515 		return (1);
516 
517 	bzero(&arg, sizeof(arg));
518 	arg.type = AML_OBJTYPE_INTEGER;
519 	arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
520 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
521 	    1, &arg, NULL)) {
522 		printf("%s: couldn't toggle wan\n", DEVNAME(sc));
523 		return (1);
524 	}
525 
526 	return (0);
527 }
528 
529 int
530 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
531 {
532 	struct aml_value arg;
533 
534 	bzero(&arg, sizeof(arg));
535 	arg.type = AML_OBJTYPE_INTEGER;
536 	arg.v_integer = cmd;
537 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
538 	return (0);
539 }
540 
541 int
542 thinkpad_volume_down(struct acpithinkpad_softc *sc)
543 {
544 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
545 }
546 
547 int
548 thinkpad_volume_up(struct acpithinkpad_softc *sc)
549 {
550 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
551 }
552 
553 int
554 thinkpad_volume_mute(struct acpithinkpad_softc *sc)
555 {
556 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
557 }
558 
559 int
560 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
561 {
562 	int b;
563 
564 	if (thinkpad_get_brightness(sc) == 0) {
565 		b = sc->sc_brightness & 0xff;
566 		if (b < ((sc->sc_brightness >> 8) & 0xff)) {
567 			sc->sc_brightness = b + 1;
568 			thinkpad_set_brightness(sc, 0);
569 		}
570 
571 		return (0);
572 	} else
573 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
574 }
575 
576 int
577 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
578 {
579 	int b;
580 
581 	if (thinkpad_get_brightness(sc) == 0) {
582 		b = sc->sc_brightness & 0xff;
583 		if (b > 0) {
584 			sc->sc_brightness = b - 1;
585 			thinkpad_set_brightness(sc, 0);
586 		}
587 
588 		return (0);
589 	} else
590 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
591 }
592 
593 int
594 thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
595 {
596 	struct aml_value arg;
597 	int64_t	mode;
598 
599 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
600 	    0, NULL, &mode)) {
601 		printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
602 		return (1);
603 	}
604 
605 	bzero(&arg, sizeof(arg));
606 	arg.type = AML_OBJTYPE_INTEGER;
607 
608 	if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
609 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
610 	else
611 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
612 
613 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
614 	    1, &arg, NULL)) {
615 		printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
616 		return (1);
617 	}
618 
619 	return (0);
620 }
621 
622 int
623 thinkpad_activate(struct device *self, int act)
624 {
625 
626 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
627 	int64_t res;
628 #if NAUDIO > 0 && NWSKBD > 0
629 	int mute;
630 #endif
631 
632 	switch (act) {
633 	case DVACT_WAKEUP:
634 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
635 		    0, NULL, &res) == 0)
636 			thinkpad_adaptive_change(sc);
637 #if NAUDIO > 0 && NWSKBD > 0
638 		mute = thinkpad_get_volume_mute(sc);
639 		if (mute != -1)
640 			wskbd_set_mixermute(mute, 1);
641 #endif
642 		break;
643 	}
644 	return (0);
645 }
646 
647 void
648 thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
649 {
650 	if (sc->sc_thinklight_get)
651 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
652 		    sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
653 }
654 
655 void
656 thinkpad_set_thinklight(void *arg0, int arg1)
657 {
658 	struct acpithinkpad_softc *sc = arg0;
659 	struct aml_value arg;
660 
661 	memset(&arg, 0, sizeof(arg));
662 	arg.type = AML_OBJTYPE_INTEGER;
663 	arg.v_integer = sc->sc_thinklight & 0x0f;
664 	aml_evalname(sc->sc_acpi, sc->sc_devnode,
665 	    sc->sc_thinklight_set, 1, &arg, NULL);
666 }
667 
668 int
669 thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
670 {
671 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
672 
673 	KASSERT(sc != NULL);
674 
675 	kbl->min = 0;
676 	kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
677 	kbl->curval = sc->sc_thinklight & 0x0f;
678 
679 	if (kbl->max == 0)
680 		return (ENOTTY);
681 
682 	return 0;
683 }
684 
685 int
686 thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
687 {
688 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
689 	int maxval;
690 
691 	KASSERT(sc != NULL);
692 
693 	maxval = (sc->sc_thinklight >> 8) & 0x0f;
694 
695 	if (maxval == 0)
696 		return (ENOTTY);
697 
698 	if (kbl->curval > maxval)
699 		return EINVAL;
700 
701 	sc->sc_thinklight &= ~0xff;
702 	sc->sc_thinklight |= kbl->curval;
703 	acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
704 	acpi_wakeup(sc->sc_acpi);
705 	return 0;
706 }
707 
708 int
709 thinkpad_get_brightness(struct acpithinkpad_softc *sc)
710 {
711 	int ret;
712 
713 	ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
714 	    &sc->sc_brightness);
715 
716 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
717 
718 	return ret;
719 }
720 
721 int
722 thinkpad_set_brightness(void *arg0, int arg1)
723 {
724 	struct acpithinkpad_softc *sc = arg0;
725 	struct aml_value arg;
726 	int ret;
727 
728 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
729 
730 	memset(&arg, 0, sizeof(arg));
731 	arg.type = AML_OBJTYPE_INTEGER;
732 	arg.v_integer = sc->sc_brightness & 0xff;
733 	ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
734 
735 	if (ret)
736 		return ret;
737 
738 	thinkpad_get_brightness(sc);
739 
740 	return 0;
741 }
742 
743 int
744 thinkpad_get_param(struct wsdisplay_param *dp)
745 {
746 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
747 
748 	if (sc == NULL)
749 		return -1;
750 
751 	switch (dp->param) {
752 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
753 		dp->min = 0;
754 		dp->max = (sc->sc_brightness >> 8) & 0xff;
755 		dp->curval = sc->sc_brightness & 0xff;
756 		return 0;
757 	default:
758 		return -1;
759 	}
760 }
761 
762 int
763 thinkpad_set_param(struct wsdisplay_param *dp)
764 {
765 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
766 	int maxval;
767 
768 	if (sc == NULL)
769 		return -1;
770 
771 	maxval = (sc->sc_brightness >> 8) & 0xff;
772 
773 	switch (dp->param) {
774 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
775 		if (dp->curval < 0)
776 			dp->curval = 0;
777 		if (dp->curval > maxval)
778 			dp->curval = maxval;
779 		sc->sc_brightness &= ~0xff;
780 		sc->sc_brightness |= dp->curval;
781 		acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
782 		    0);
783 		acpi_wakeup(sc->sc_acpi);
784 		return 0;
785 	default:
786 		return -1;
787 	}
788 }
789 
790 #if NAUDIO > 0 && NWSKBD > 0
791 void
792 thinkpad_attach_deferred(void *v __unused)
793 {
794 	wskbd_set_mixermute(1, 1);
795 }
796 
797 int
798 thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
799 {
800 	uint8_t vol = 0;
801 
802 	if (sc->sc_acpi->sc_ec == NULL)
803 		return (-1);
804 
805 	acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
806 	return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
807 	    THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
808 }
809 #endif
810