xref: /openbsd/sys/dev/acpi/acpithinkpad.c (revision d415bd75)
1 /*	$OpenBSD: acpithinkpad.c,v 1.74 2023/07/07 07:37:59 claudio 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_SENSOR_FANRPM		0
116 #define THINKPAD_SENSOR_PORTREPL	1
117 #define THINKPAD_SENSOR_TMP0		2
118 #define THINKPAD_NSENSORS		10
119 
120 #define THINKPAD_ECOFFSET_VOLUME	0x30
121 #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
122 #define THINKPAD_ECOFFSET_FANLO		0x84
123 #define THINKPAD_ECOFFSET_FANHI		0x85
124 
125 #define	THINKPAD_ADAPTIVE_MODE_HOME	1
126 #define	THINKPAD_ADAPTIVE_MODE_FUNCTION	3
127 
128 #define THINKPAD_MASK_MIC_MUTE		(1 << 14)
129 #define THINKPAD_MASK_BRIGHTNESS_UP	(1 << 15)
130 #define THINKPAD_MASK_BRIGHTNESS_DOWN	(1 << 16)
131 #define THINKPAD_MASK_KBD_BACKLIGHT	(1 << 17)
132 
133 #define THINKPAD_BATTERY_ERROR		0x80000000
134 #define THINKPAD_BATTERY_SUPPORT	0x00000100
135 #define THINKPAD_BATTERY_SUPPORT_BICG	0x00000020
136 #define THINKPAD_BATTERY_SHIFT		8
137 
138 struct acpithinkpad_softc {
139 	struct device		 sc_dev;
140 
141 	struct acpiec_softc     *sc_ec;
142 	struct acpi_softc	*sc_acpi;
143 	struct aml_node		*sc_devnode;
144 
145 	struct ksensor		 sc_sens[THINKPAD_NSENSORS];
146 	struct ksensordev	 sc_sensdev;
147 	int			 sc_ntempsens;
148 
149 	uint64_t		 sc_hkey_version;
150 
151 	uint64_t		 sc_thinklight;
152 	const char		*sc_thinklight_get;
153 	const char		*sc_thinklight_set;
154 
155 	uint64_t		 sc_brightness;
156 };
157 
158 extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
159 
160 int	thinkpad_match(struct device *, void *, void *);
161 void	thinkpad_attach(struct device *, struct device *, void *);
162 int	thinkpad_hotkey(struct aml_node *, int, void *);
163 int	thinkpad_enable_events(struct acpithinkpad_softc *);
164 int	thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
165 int	thinkpad_toggle_wan(struct acpithinkpad_softc *);
166 int	thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
167 int	thinkpad_volume_down(struct acpithinkpad_softc *);
168 int	thinkpad_volume_up(struct acpithinkpad_softc *);
169 int	thinkpad_volume_mute(struct acpithinkpad_softc *);
170 int	thinkpad_brightness_up(struct acpithinkpad_softc *);
171 int	thinkpad_brightness_down(struct acpithinkpad_softc *);
172 int	thinkpad_adaptive_change(struct acpithinkpad_softc *);
173 int	thinkpad_activate(struct device *, int);
174 
175 /* wscons hook functions */
176 void	thinkpad_get_thinklight(struct acpithinkpad_softc *);
177 void	thinkpad_set_thinklight(void *, int);
178 int	thinkpad_get_kbd_backlight(struct wskbd_backlight *);
179 int	thinkpad_set_kbd_backlight(struct wskbd_backlight *);
180 extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
181 extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
182 int	thinkpad_get_brightness(struct acpithinkpad_softc *);
183 int	thinkpad_set_brightness(void *, int);
184 int	thinkpad_get_param(struct wsdisplay_param *);
185 int	thinkpad_set_param(struct wsdisplay_param *);
186 int	thinkpad_get_temp(struct acpithinkpad_softc *, int, int64_t *);
187 
188 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
189 void    thinkpad_sensor_refresh(void *);
190 
191 #if NAUDIO > 0 && NWSKBD > 0
192 void thinkpad_attach_deferred(void *);
193 int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
194 extern int wskbd_set_mixermute(long, long);
195 extern int wskbd_set_mixervolume(long, long);
196 #endif
197 
198 int	thinkpad_battery_setchargemode(int);
199 int	thinkpad_battery_setchargestart(int);
200 int	thinkpad_battery_setchargestop(int);
201 
202 extern int (*hw_battery_setchargemode)(int);
203 extern int (*hw_battery_setchargestart)(int);
204 extern int (*hw_battery_setchargestop)(int);
205 extern int hw_battery_chargemode;
206 extern int hw_battery_chargestart;
207 extern int hw_battery_chargestop;
208 
209 const struct cfattach acpithinkpad_ca = {
210 	sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
211 	NULL, thinkpad_activate
212 };
213 
214 struct cfdriver acpithinkpad_cd = {
215 	NULL, "acpithinkpad", DV_DULL
216 };
217 
218 const char *acpithinkpad_hids[] = {
219 	"IBM0068",
220 	"LEN0068",
221 	"LEN0268",
222 	NULL
223 };
224 
225 int
226 thinkpad_match(struct device *parent, void *match, void *aux)
227 {
228 	struct acpi_attach_args	*aa = aux;
229 	struct cfdata *cf = match;
230 	int64_t	res;
231 
232 	if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
233 		return (0);
234 
235 	if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
236 	    "MHKV", 0, NULL, &res))
237 		return (0);
238 
239 	if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
240 		return (0);
241 
242 	return (1);
243 }
244 
245 void
246 thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
247 {
248 	int64_t tmp;
249 	int i;
250 
251 	if (sc->sc_acpi->sc_ec == NULL)
252 		return;
253 	sc->sc_ec = sc->sc_acpi->sc_ec;
254 
255 	/* Add temperature probes */
256 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
257 	    sizeof(sc->sc_sensdev.xname));
258 	sc->sc_ntempsens = 0;
259 	for (i = 0; i < THINKPAD_NSENSORS - THINKPAD_SENSOR_TMP0; i++) {
260 		if (thinkpad_get_temp(sc, i, &tmp) != 0)
261 			break;
262 
263 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].type = SENSOR_TEMP;
264 		sensor_attach(&sc->sc_sensdev,
265 		    &sc->sc_sens[THINKPAD_SENSOR_TMP0 + i]);
266 		sc->sc_ntempsens++;
267  	}
268 
269 	/* Add fan probe */
270 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
271 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
272 
273 	/* Add port replicator indicator */
274 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
275 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
276 	strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
277 	        sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
278 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
279 
280 	sensordev_install(&sc->sc_sensdev);
281 }
282 
283 void
284 thinkpad_sensor_refresh(void *arg)
285 {
286 	struct acpithinkpad_softc *sc = arg;
287 	uint8_t lo, hi, i;
288 	int64_t tmp;
289 
290 	/* Refresh sensor readings */
291 	for (i = 0; i < sc->sc_ntempsens; i++) {
292 		if (thinkpad_get_temp(sc, i, &tmp) != 0) {
293  			sc->sc_sens[i].flags = SENSOR_FINVALID;
294 			continue;
295 		}
296 
297 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].value =
298 		    (tmp * 1000000) + 273150000;
299 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].flags =
300 		    (tmp > 127 || tmp < -127) ? SENSOR_FINVALID : 0;
301  	}
302 
303 	/* Read fan RPM */
304 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
305 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
306 	if (hi == 0xff && lo == 0xff) {
307  		sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = SENSOR_FINVALID;
308 	} else {
309 		sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
310  		sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = 0;
311 	}
312 }
313 
314 void
315 thinkpad_attach(struct device *parent, struct device *self, void *aux)
316 {
317 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
318 	struct acpi_attach_args	*aa = aux;
319 	struct aml_value arg;
320 	uint64_t ret;
321 
322 	sc->sc_acpi = (struct acpi_softc *)parent;
323 	sc->sc_devnode = aa->aaa_node;
324 
325 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
326 	    &sc->sc_hkey_version))
327 		sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
328 
329 	printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8,
330 	    sc->sc_hkey_version & 0xff);
331 
332 #if NAUDIO > 0 && NWSKBD > 0
333 	/* Defer speaker mute */
334 	if (thinkpad_get_volume_mute(sc) == 1)
335 		startuphook_establish(thinkpad_attach_deferred, sc);
336 #endif
337 
338 	/* Set event mask to receive everything */
339 	thinkpad_enable_events(sc);
340 	thinkpad_sensor_attach(sc);
341 
342 	/* Check for ThinkLight or keyboard backlight */
343 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
344 	    0, NULL, &sc->sc_thinklight) == 0) {
345 		sc->sc_thinklight_get = "KLCG";
346 		sc->sc_thinklight_set = "KLCS";
347 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
348 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
349 	} else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
350 	    0, NULL, &sc->sc_thinklight) == 0) {
351 		sc->sc_thinklight_get = "MLCG";
352 		sc->sc_thinklight_set = "MLCS";
353 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
354 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
355 	}
356 
357 	/* On version 2 and newer, let *drm or acpivout control brightness */
358 	if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 &&
359 	    (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
360 	    0, NULL, &sc->sc_brightness) == 0)) {
361 		ws_get_param = thinkpad_get_param;
362 		ws_set_param = thinkpad_set_param;
363 	}
364 
365 	memset(&arg, 0, sizeof(arg));
366 	arg.type = AML_OBJTYPE_INTEGER;
367 	arg.v_integer = 1;
368 
369 	hw_battery_chargemode = 1;
370 	hw_battery_chargestart = 0;
371 	hw_battery_chargestop = 100;
372 
373 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCTG",
374 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
375 		if (ret & THINKPAD_BATTERY_SUPPORT) {
376 			hw_battery_chargestart = ret & 0xff;
377 			hw_battery_setchargestart =
378 				thinkpad_battery_setchargestart;
379 		}
380 	}
381 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSG",
382 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
383 		if (ret & THINKPAD_BATTERY_SUPPORT) {
384 			if ((ret & 0xff) == 0)
385 				hw_battery_chargestop = 100;
386 			else
387 				hw_battery_chargestop = ret & 0xff;
388 			hw_battery_setchargestop =
389 				thinkpad_battery_setchargestop;
390 		}
391 	}
392 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSG",
393 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
394 		if (ret & THINKPAD_BATTERY_SUPPORT) {
395 			if (ret & 0x1)
396 				hw_battery_chargemode = -1;
397 			hw_battery_setchargemode =
398 				thinkpad_battery_setchargemode;
399 		}
400 	}
401 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICG",
402 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
403 		if (ret & THINKPAD_BATTERY_SUPPORT_BICG) {
404 			if (ret & 0x1)
405 				hw_battery_chargemode = 0;
406 			hw_battery_setchargemode =
407 				thinkpad_battery_setchargemode;
408 		}
409 	}
410 
411 	/* Run thinkpad_hotkey on button presses */
412 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
413 	    thinkpad_hotkey, sc, ACPIDEV_POLL);
414 }
415 
416 int
417 thinkpad_enable_events(struct acpithinkpad_softc *sc)
418 {
419 	struct aml_value arg, args[2];
420 	int64_t	mask;
421 	int i;
422 
423 	/* Get the default event mask */
424 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
425 	    0, NULL, &mask)) {
426 		printf("%s: no MHKA\n", DEVNAME(sc));
427 		return (1);
428 	}
429 
430 	/* Enable events we need to know about */
431 	mask |= (THINKPAD_MASK_MIC_MUTE	|
432 	    THINKPAD_MASK_BRIGHTNESS_UP |
433 	    THINKPAD_MASK_BRIGHTNESS_DOWN |
434 	    THINKPAD_MASK_KBD_BACKLIGHT);
435 
436 	DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
437 
438 	/* Update hotkey mask */
439 	bzero(args, sizeof(args));
440 	args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
441 	for (i = 0; i < 32; i++) {
442 		args[0].v_integer = i + 1;
443 		args[1].v_integer = (((1 << i) & mask) != 0);
444 
445 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
446 		    2, args, NULL)) {
447 			printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
448 			return (1);
449 		}
450 	}
451 
452 	/* Enable hotkeys */
453 	bzero(&arg, sizeof(arg));
454 	arg.type = AML_OBJTYPE_INTEGER;
455 	arg.v_integer = 1;
456 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
457 	    1, &arg, NULL)) {
458 		printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
459 		return (1);
460 	}
461 
462 	return (0);
463 }
464 
465 int
466 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
467 {
468 	struct acpithinkpad_softc *sc = arg;
469 	int64_t	event;
470 
471 	if (notify_type == 0x00) {
472 		/* Poll sensors */
473 		thinkpad_sensor_refresh(sc);
474 		return (0);
475 	}
476 
477 	if (notify_type != 0x80)
478 		return (1);
479 
480 	for (;;) {
481 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
482 		    0, NULL, &event))
483 			break;
484 
485 		DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
486 		if (event == 0)
487 			break;
488 
489 		switch (event) {
490 		case THINKPAD_BUTTON_BRIGHTNESS_UP:
491 			thinkpad_brightness_up(sc);
492 			break;
493 		case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
494 			thinkpad_brightness_down(sc);
495 			break;
496 		case THINKPAD_BUTTON_WIRELESS:
497 			thinkpad_toggle_bluetooth(sc);
498 			break;
499 		case THINKPAD_BUTTON_SUSPEND:
500 #ifndef SMALL_KERNEL
501 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
502 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
503 				    sc->sc_acpi, SLEEP_SUSPEND);
504 #endif
505 			break;
506 		case THINKPAD_BUTTON_VOLUME_MUTE:
507 			thinkpad_volume_mute(sc);
508 			break;
509 		case THINKPAD_BUTTON_VOLUME_DOWN:
510 			thinkpad_volume_down(sc);
511 			break;
512 		case THINKPAD_BUTTON_VOLUME_UP:
513 			thinkpad_volume_up(sc);
514 			break;
515 		case THINKPAD_BUTTON_MICROPHONE_MUTE:
516 #if NAUDIO > 0 && NWSKBD > 0
517 			wskbd_set_mixervolume(0, 0);
518 #endif
519 			break;
520 		case THINKPAD_BUTTON_HIBERNATE:
521 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
522 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
523 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
524 				    sc->sc_acpi, SLEEP_HIBERNATE);
525 #endif
526 			break;
527 		case THINKPAD_BUTTON_THINKLIGHT:
528 			thinkpad_get_thinklight(sc);
529 			break;
530 		case THINKPAD_ADAPTIVE_NEXT:
531 		case THINKPAD_ADAPTIVE_QUICK:
532 			thinkpad_adaptive_change(sc);
533 			break;
534 		case THINKPAD_BACKLIGHT_CHANGED:
535 			thinkpad_get_brightness(sc);
536 			break;
537 		case THINKPAD_PORT_REPL_DOCKED:
538 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
539 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
540 			    SENSOR_S_OK;
541 			break;
542 		case THINKPAD_PORT_REPL_UNDOCKED:
543 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
544 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
545 			    SENSOR_S_OK;
546 			break;
547 		default:
548 			/* unknown or boring event */
549 			DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
550 			    event));
551 			break;
552 		}
553 	}
554 
555 	return (0);
556 }
557 
558 int
559 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
560 {
561 	struct aml_value arg;
562 	int64_t	bluetooth;
563 
564 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
565 	    0, NULL, &bluetooth))
566 		return (1);
567 
568 	if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
569 		return (1);
570 
571 	bzero(&arg, sizeof(arg));
572 	arg.type = AML_OBJTYPE_INTEGER;
573 	arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
574 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
575 	    1, &arg, NULL)) {
576 		printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
577 		return (1);
578 	}
579 
580 	return (0);
581 }
582 
583 int
584 thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
585 {
586 	struct aml_value arg;
587 	int64_t wan;
588 
589 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
590 	    0, NULL, &wan))
591 		return (1);
592 
593 	if (!(wan & THINKPAD_WAN_PRESENT))
594 		return (1);
595 
596 	bzero(&arg, sizeof(arg));
597 	arg.type = AML_OBJTYPE_INTEGER;
598 	arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
599 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
600 	    1, &arg, NULL)) {
601 		printf("%s: couldn't toggle wan\n", DEVNAME(sc));
602 		return (1);
603 	}
604 
605 	return (0);
606 }
607 
608 int
609 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
610 {
611 	struct aml_value arg;
612 
613 	bzero(&arg, sizeof(arg));
614 	arg.type = AML_OBJTYPE_INTEGER;
615 	arg.v_integer = cmd;
616 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
617 	return (0);
618 }
619 
620 int
621 thinkpad_volume_down(struct acpithinkpad_softc *sc)
622 {
623 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
624 }
625 
626 int
627 thinkpad_volume_up(struct acpithinkpad_softc *sc)
628 {
629 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
630 }
631 
632 int
633 thinkpad_volume_mute(struct acpithinkpad_softc *sc)
634 {
635 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
636 }
637 
638 int
639 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
640 {
641 	int b;
642 
643 	if (thinkpad_get_brightness(sc) == 0) {
644 		b = sc->sc_brightness & 0xff;
645 		if (b < ((sc->sc_brightness >> 8) & 0xff)) {
646 			sc->sc_brightness = b + 1;
647 			thinkpad_set_brightness(sc, 0);
648 		}
649 
650 		return (0);
651 	} else
652 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
653 }
654 
655 int
656 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
657 {
658 	int b;
659 
660 	if (thinkpad_get_brightness(sc) == 0) {
661 		b = sc->sc_brightness & 0xff;
662 		if (b > 0) {
663 			sc->sc_brightness = b - 1;
664 			thinkpad_set_brightness(sc, 0);
665 		}
666 
667 		return (0);
668 	} else
669 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
670 }
671 
672 int
673 thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
674 {
675 	struct aml_value arg;
676 	int64_t	mode;
677 
678 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
679 	    0, NULL, &mode)) {
680 		printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
681 		return (1);
682 	}
683 
684 	bzero(&arg, sizeof(arg));
685 	arg.type = AML_OBJTYPE_INTEGER;
686 
687 	if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
688 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
689 	else
690 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
691 
692 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
693 	    1, &arg, NULL)) {
694 		printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
695 		return (1);
696 	}
697 
698 	return (0);
699 }
700 
701 int
702 thinkpad_activate(struct device *self, int act)
703 {
704 
705 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
706 	int64_t res;
707 #if NAUDIO > 0 && NWSKBD > 0
708 	int mute;
709 #endif
710 
711 	switch (act) {
712 	case DVACT_WAKEUP:
713 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
714 		    0, NULL, &res) == 0)
715 			thinkpad_adaptive_change(sc);
716 #if NAUDIO > 0 && NWSKBD > 0
717 		mute = thinkpad_get_volume_mute(sc);
718 		if (mute != -1)
719 			wskbd_set_mixermute(mute, 1);
720 #endif
721 		break;
722 	}
723 	return (0);
724 }
725 
726 void
727 thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
728 {
729 	if (sc->sc_thinklight_get)
730 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
731 		    sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
732 }
733 
734 void
735 thinkpad_set_thinklight(void *arg0, int arg1)
736 {
737 	struct acpithinkpad_softc *sc = arg0;
738 	struct aml_value arg;
739 
740 	memset(&arg, 0, sizeof(arg));
741 	arg.type = AML_OBJTYPE_INTEGER;
742 	arg.v_integer = sc->sc_thinklight & 0x0f;
743 	aml_evalname(sc->sc_acpi, sc->sc_devnode,
744 	    sc->sc_thinklight_set, 1, &arg, NULL);
745 }
746 
747 int
748 thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
749 {
750 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
751 
752 	KASSERT(sc != NULL);
753 
754 	kbl->min = 0;
755 	kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
756 	kbl->curval = sc->sc_thinklight & 0x0f;
757 
758 	if (kbl->max == 0)
759 		return (ENOTTY);
760 
761 	return 0;
762 }
763 
764 int
765 thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
766 {
767 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
768 	int maxval;
769 
770 	KASSERT(sc != NULL);
771 
772 	maxval = (sc->sc_thinklight >> 8) & 0x0f;
773 
774 	if (maxval == 0)
775 		return (ENOTTY);
776 
777 	if (kbl->curval > maxval)
778 		return EINVAL;
779 
780 	sc->sc_thinklight &= ~0xff;
781 	sc->sc_thinklight |= kbl->curval;
782 	acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
783 	acpi_wakeup(sc->sc_acpi);
784 	return 0;
785 }
786 
787 int
788 thinkpad_get_brightness(struct acpithinkpad_softc *sc)
789 {
790 	int ret;
791 
792 	ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
793 	    &sc->sc_brightness);
794 
795 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
796 
797 	return ret;
798 }
799 
800 int
801 thinkpad_set_brightness(void *arg0, int arg1)
802 {
803 	struct acpithinkpad_softc *sc = arg0;
804 	struct aml_value arg;
805 	int ret;
806 
807 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
808 
809 	memset(&arg, 0, sizeof(arg));
810 	arg.type = AML_OBJTYPE_INTEGER;
811 	arg.v_integer = sc->sc_brightness & 0xff;
812 	ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
813 
814 	if (ret)
815 		return ret;
816 
817 	thinkpad_get_brightness(sc);
818 
819 	return 0;
820 }
821 
822 int
823 thinkpad_get_param(struct wsdisplay_param *dp)
824 {
825 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
826 
827 	if (sc == NULL)
828 		return -1;
829 
830 	switch (dp->param) {
831 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
832 		dp->min = 0;
833 		dp->max = (sc->sc_brightness >> 8) & 0xff;
834 		dp->curval = sc->sc_brightness & 0xff;
835 		return 0;
836 	default:
837 		return -1;
838 	}
839 }
840 
841 int
842 thinkpad_set_param(struct wsdisplay_param *dp)
843 {
844 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
845 	int maxval;
846 
847 	if (sc == NULL)
848 		return -1;
849 
850 	maxval = (sc->sc_brightness >> 8) & 0xff;
851 
852 	switch (dp->param) {
853 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
854 		if (dp->curval < 0)
855 			dp->curval = 0;
856 		if (dp->curval > maxval)
857 			dp->curval = maxval;
858 		sc->sc_brightness &= ~0xff;
859 		sc->sc_brightness |= dp->curval;
860 		acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
861 		    0);
862 		acpi_wakeup(sc->sc_acpi);
863 		return 0;
864 	default:
865 		return -1;
866 	}
867 }
868 
869 int
870 thinkpad_get_temp(struct acpithinkpad_softc *sc, int idx, int64_t *temp)
871 {
872 	char sname[5];
873 
874 	snprintf(sname, sizeof(sname), "TMP%d", idx);
875 
876 	if (aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, sname, 0, 0,
877 	    temp) != 0)
878 		return (1);
879 
880 	return (0);
881 }
882 
883 #if NAUDIO > 0 && NWSKBD > 0
884 void
885 thinkpad_attach_deferred(void *v __unused)
886 {
887 	wskbd_set_mixermute(1, 1);
888 }
889 
890 int
891 thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
892 {
893 	uint8_t vol = 0;
894 
895 	if (sc->sc_acpi->sc_ec == NULL)
896 		return (-1);
897 
898 	acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
899 	return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
900 	    THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
901 }
902 #endif
903 
904 int
905 thinkpad_battery_inhibit_charge(int state)
906 {
907 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
908 	struct aml_value arg;
909 	int battery, count;
910 	uint64_t ret;
911 
912 	count = acpi_batcount(sc->sc_acpi);
913 	for (battery = 1; battery <= count; battery++) {
914 		memset(&arg, 0, sizeof(arg));
915 		arg.type = AML_OBJTYPE_INTEGER;
916 		arg.v_integer = (0xffff << 8) | (battery << 4) | state;
917 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICS",
918 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
919 			return EIO;
920 	}
921 	return 0;
922 }
923 
924 int
925 thinkpad_battery_force_discharge(int state)
926 {
927 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
928 	struct aml_value arg;
929 	int battery, count;
930 	uint64_t ret;
931 
932 	count = acpi_batcount(sc->sc_acpi);
933 	for (battery = 1; battery <= count; battery++) {
934 		memset(&arg, 0, sizeof(arg));
935 		arg.type = AML_OBJTYPE_INTEGER;
936 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | state;
937 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSS",
938 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
939 			return EIO;
940 	}
941 	return 0;
942 }
943 
944 int
945 thinkpad_battery_setchargemode(int mode)
946 {
947 	int error;
948 
949 	switch (mode) {
950 	case -1:
951 		error = thinkpad_battery_inhibit_charge(1);
952 		if (error)
953 			return error;
954 		error = thinkpad_battery_force_discharge(1);
955 		if (error)
956 			return error;
957 		break;
958 	case 0:
959 		error = thinkpad_battery_force_discharge(0);
960 		if (error)
961 			return error;
962 		error = thinkpad_battery_inhibit_charge(1);
963 		if (error)
964 			return error;
965 		break;
966 	case 1:
967 		error = thinkpad_battery_force_discharge(0);
968 		if (error)
969 			return error;
970 		error = thinkpad_battery_inhibit_charge(0);
971 		if (error)
972 			return error;
973 		break;
974 	default:
975 		return EOPNOTSUPP;
976 	}
977 
978 	hw_battery_chargemode = mode;
979 	return 0;
980 }
981 
982 int
983 thinkpad_battery_setchargestart(int start)
984 {
985 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
986 	struct aml_value arg;
987 	int battery, count;
988 	uint64_t ret;
989 
990 	if (start >= hw_battery_chargestop)
991 		return EINVAL;
992 
993 	count = acpi_batcount(sc->sc_acpi);
994 	for (battery = 1; battery <= count; battery++) {
995 		memset(&arg, 0, sizeof(arg));
996 		arg.type = AML_OBJTYPE_INTEGER;
997 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | start;
998 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCCS",
999 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1000 			return EIO;
1001 	}
1002 	hw_battery_chargestart = start;
1003 	return 0;
1004 }
1005 
1006 int
1007 thinkpad_battery_setchargestop(int stop)
1008 {
1009 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
1010 	struct aml_value arg;
1011 	int battery, count;
1012 	uint64_t ret;
1013 
1014 	if (stop <= hw_battery_chargestart)
1015 		return EINVAL;
1016 
1017 	if (stop == 100)
1018 		stop = 0;
1019 
1020 	count = acpi_batcount(sc->sc_acpi);
1021 	for (battery = 1; battery <= count; battery++) {
1022 		memset(&arg, 0, sizeof(arg));
1023 		arg.type = AML_OBJTYPE_INTEGER;
1024 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | stop;
1025 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSS",
1026 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1027 			return EIO;
1028 	}
1029 	hw_battery_chargestop = (stop == 0) ? 100 : stop;
1030 	return 0;
1031 }
1032