xref: /freebsd/sys/dev/acpi_support/acpi_asus_wmi.c (revision d2ce15bd43b3a1dcce08eecbff8d5d359946d972)
1 /*-
2  * Copyright (c) 2012 Alexander Motin <mav@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/conf.h>
33 #include <sys/uio.h>
34 #include <sys/proc.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/sbuf.h>
38 #include <sys/module.h>
39 #include <sys/sysctl.h>
40 
41 #include <contrib/dev/acpica/include/acpi.h>
42 #include <contrib/dev/acpica/include/accommon.h>
43 #include <dev/acpica/acpivar.h>
44 #include "acpi_wmi_if.h"
45 
46 #define _COMPONENT	ACPI_OEM
47 ACPI_MODULE_NAME("ASUS-WMI")
48 
49 #define ACPI_ASUS_WMI_MGMT_GUID 	"97845ED0-4E6D-11DE-8A39-0800200C9A66"
50 #define ACPI_ASUS_WMI_EVENT_GUID	"0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
51 #define ACPI_EEEPC_WMI_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
52 
53 /* WMI Methods */
54 #define ASUS_WMI_METHODID_SPEC          0x43455053
55 #define ASUS_WMI_METHODID_SFUN          0x4E554653
56 #define ASUS_WMI_METHODID_DSTS          0x53544344
57 #define ASUS_WMI_METHODID_DSTS2         0x53545344
58 #define ASUS_WMI_METHODID_DEVS          0x53564544
59 #define ASUS_WMI_METHODID_INIT          0x54494E49
60 #define ASUS_WMI_METHODID_HKEY          0x59454B48
61 
62 #define ASUS_WMI_UNSUPPORTED_METHOD     0xFFFFFFFE
63 
64 /* Wireless */
65 #define ASUS_WMI_DEVID_HW_SWITCH        0x00010001
66 #define ASUS_WMI_DEVID_WIRELESS_LED     0x00010002
67 #define ASUS_WMI_DEVID_CWAP             0x00010003
68 #define ASUS_WMI_DEVID_WLAN             0x00010011
69 #define ASUS_WMI_DEVID_BLUETOOTH        0x00010013
70 #define ASUS_WMI_DEVID_GPS              0x00010015
71 #define ASUS_WMI_DEVID_WIMAX            0x00010017
72 #define ASUS_WMI_DEVID_WWAN3G           0x00010019
73 #define ASUS_WMI_DEVID_UWB              0x00010021
74 
75 /* LEDs */
76 #define ASUS_WMI_DEVID_LED1             0x00020011
77 #define ASUS_WMI_DEVID_LED2             0x00020012
78 #define ASUS_WMI_DEVID_LED3             0x00020013
79 #define ASUS_WMI_DEVID_LED4             0x00020014
80 #define ASUS_WMI_DEVID_LED5             0x00020015
81 #define ASUS_WMI_DEVID_LED6             0x00020016
82 
83 /* Backlight and Brightness */
84 #define ASUS_WMI_DEVID_BACKLIGHT        0x00050011
85 #define ASUS_WMI_DEVID_BRIGHTNESS       0x00050012
86 #define ASUS_WMI_DEVID_KBD_BACKLIGHT    0x00050021
87 #define ASUS_WMI_DEVID_LIGHT_SENSOR     0x00050022
88 
89 /* Misc */
90 #define ASUS_WMI_DEVID_CAMERA           0x00060013
91 #define ASUS_WMI_DEVID_CARDREADER       0x00080013
92 #define ASUS_WMI_DEVID_TOUCHPAD         0x00100011
93 #define ASUS_WMI_DEVID_TOUCHPAD_LED     0x00100012
94 #define ASUS_WMI_DEVID_THERMAL_CTRL     0x00110011
95 #define ASUS_WMI_DEVID_FAN_CTRL         0x00110012
96 #define ASUS_WMI_DEVID_PROCESSOR_STATE  0x00120012
97 
98 /* DSTS masks */
99 #define ASUS_WMI_DSTS_STATUS_BIT        0x00000001
100 #define ASUS_WMI_DSTS_UNKNOWN_BIT       0x00000002
101 #define ASUS_WMI_DSTS_PRESENCE_BIT      0x00010000
102 #define ASUS_WMI_DSTS_USER_BIT          0x00020000
103 #define ASUS_WMI_DSTS_BIOS_BIT          0x00040000
104 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK   0x000000FF
105 #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK   0x0000FF00
106 
107 
108 struct acpi_asus_wmi_softc {
109 	device_t	dev;
110 	device_t	wmi_dev;
111 	const char	*notify_guid;
112 	struct sysctl_ctx_list	*sysctl_ctx;
113 	struct sysctl_oid	*sysctl_tree;
114 	int		dsts_id;
115 	int		handle_keys;
116 };
117 
118 static struct {
119 	char	*name;
120 	int	dev_id;
121 	char	*description;
122 	int	access;
123 } acpi_asus_wmi_sysctls[] = {
124 	{
125 		.name		= "hw_switch",
126 		.dev_id		= ASUS_WMI_DEVID_HW_SWITCH,
127 		.description	= "hw_switch",
128 		.access		= CTLTYPE_INT | CTLFLAG_RW
129 	},
130 	{
131 		.name		= "wireless_led",
132 		.dev_id		= ASUS_WMI_DEVID_WIRELESS_LED,
133 		.description	= "Wireless LED control",
134 		.access		= CTLTYPE_INT | CTLFLAG_RW
135 	},
136 	{
137 		.name		= "cwap",
138 		.dev_id		= ASUS_WMI_DEVID_CWAP,
139 		.description	= "Alt+F2 function",
140 		.access		= CTLTYPE_INT | CTLFLAG_RW
141 	},
142 	{
143 		.name		= "wlan",
144 		.dev_id		= ASUS_WMI_DEVID_WLAN,
145 		.description	= "WLAN power control",
146 		.access		= CTLTYPE_INT | CTLFLAG_RW
147 	},
148 	{
149 		.name		= "bluetooth",
150 		.dev_id		= ASUS_WMI_DEVID_BLUETOOTH,
151 		.description	= "Bluetooth power control",
152 		.access		= CTLTYPE_INT | CTLFLAG_RW
153 	},
154 	{
155 		.name		= "gps",
156 		.dev_id		= ASUS_WMI_DEVID_GPS,
157 		.description	= "GPS power control",
158 		.access		= CTLTYPE_INT | CTLFLAG_RW
159 	},
160 	{
161 		.name		= "wimax",
162 		.dev_id		= ASUS_WMI_DEVID_WIMAX,
163 		.description	= "WiMAX power control",
164 		.access		= CTLTYPE_INT | CTLFLAG_RW
165 	},
166 	{
167 		.name		= "wwan3g",
168 		.dev_id		= ASUS_WMI_DEVID_WWAN3G,
169 		.description	= "WWAN-3G power control",
170 		.access		= CTLTYPE_INT | CTLFLAG_RW
171 	},
172 	{
173 		.name		= "uwb",
174 		.dev_id		= ASUS_WMI_DEVID_UWB,
175 		.description	= "UWB power control",
176 		.access		= CTLTYPE_INT | CTLFLAG_RW
177 	},
178 	{
179 		.name		= "led1",
180 		.dev_id		= ASUS_WMI_DEVID_LED1,
181 		.description	= "LED1 control",
182 		.access		= CTLTYPE_INT | CTLFLAG_RW
183 	},
184 	{
185 		.name		= "led2",
186 		.dev_id		= ASUS_WMI_DEVID_LED2,
187 		.description	= "LED2 control",
188 		.access		= CTLTYPE_INT | CTLFLAG_RW
189 	},
190 	{
191 		.name		= "led3",
192 		.dev_id		= ASUS_WMI_DEVID_LED3,
193 		.description	= "LED3 control",
194 		.access		= CTLTYPE_INT | CTLFLAG_RW
195 	},
196 	{
197 		.name		= "led4",
198 		.dev_id		= ASUS_WMI_DEVID_LED4,
199 		.description	= "LED4 control",
200 		.access		= CTLTYPE_INT | CTLFLAG_RW
201 	},
202 	{
203 		.name		= "led5",
204 		.dev_id		= ASUS_WMI_DEVID_LED5,
205 		.description	= "LED5 control",
206 		.access		= CTLTYPE_INT | CTLFLAG_RW
207 	},
208 	{
209 		.name		= "led6",
210 		.dev_id		= ASUS_WMI_DEVID_LED6,
211 		.description	= "LED6 control",
212 		.access		= CTLTYPE_INT | CTLFLAG_RW
213 	},
214 	{
215 		.name		= "backlight",
216 		.dev_id		= ASUS_WMI_DEVID_BACKLIGHT,
217 		.description	= "LCD backlight on/off control",
218 		.access		= CTLTYPE_INT | CTLFLAG_RW
219 	},
220 	{
221 		.name		= "brightness",
222 		.dev_id		= ASUS_WMI_DEVID_BRIGHTNESS,
223 		.description	= "LCD backlight brightness control",
224 		.access		= CTLTYPE_INT | CTLFLAG_RW
225 	},
226 	{
227 		.name		= "kbd_backlight",
228 		.dev_id		= ASUS_WMI_DEVID_KBD_BACKLIGHT,
229 		.description	= "Keyboard backlight brightness control",
230 		.access		= CTLTYPE_INT | CTLFLAG_RW
231 	},
232 	{
233 		.name		= "light_sensor",
234 		.dev_id		= ASUS_WMI_DEVID_LIGHT_SENSOR,
235 		.description	= "Ambient light sensor",
236 		.access		= CTLTYPE_INT | CTLFLAG_RW
237 	},
238 	{
239 		.name		= "camera",
240 		.dev_id		= ASUS_WMI_DEVID_CAMERA,
241 		.description	= "Camera power control",
242 		.access		= CTLTYPE_INT | CTLFLAG_RW
243 	},
244 	{
245 		.name		= "cardreader",
246 		.dev_id		= ASUS_WMI_DEVID_CARDREADER,
247 		.description	= "Cardreader power control",
248 		.access		= CTLTYPE_INT | CTLFLAG_RW
249 	},
250 	{
251 		.name		= "touchpad",
252 		.dev_id		= ASUS_WMI_DEVID_TOUCHPAD,
253 		.description	= "Touchpad control",
254 		.access		= CTLTYPE_INT | CTLFLAG_RW
255 	},
256 	{
257 		.name		= "touchpad_led",
258 		.dev_id		= ASUS_WMI_DEVID_TOUCHPAD_LED,
259 		.description	= "Touchpad LED control",
260 		.access		= CTLTYPE_INT | CTLFLAG_RW
261 	},
262 	{
263 		.name		= "themperature",
264 		.dev_id		= ASUS_WMI_DEVID_THERMAL_CTRL,
265 		.description	= "Temperature (C)",
266 		.access		= CTLTYPE_INT | CTLFLAG_RD
267 	},
268 	{
269 		.name		= "fan_speed",
270 		.dev_id		= ASUS_WMI_DEVID_FAN_CTRL,
271 		.description	= "Fan speed (0-3)",
272 		.access		= CTLTYPE_INT | CTLFLAG_RD
273 	},
274 	{
275 		.name		= "processor_state",
276 		.dev_id		= ASUS_WMI_DEVID_PROCESSOR_STATE,
277 		.description	= "Processor state",
278 		.access		= CTLTYPE_INT | CTLFLAG_RW
279 	},
280 	{ NULL, 0, NULL, 0 }
281 };
282 
283 ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");
284 
285 static void	acpi_asus_wmi_identify(driver_t *driver, device_t parent);
286 static int	acpi_asus_wmi_probe(device_t dev);
287 static int	acpi_asus_wmi_attach(device_t dev);
288 static int	acpi_asus_wmi_detach(device_t dev);
289 
290 static int	acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
291 static int	acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
292 		    int arg, int oldarg);
293 static int	acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);
294 static int	acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
295 		    UINT32 arg0, UINT32 arg1, UINT32 *retval);
296 static int	acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
297 		    UINT32 dev_id, UINT32 *retval);
298 static int	acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
299 		    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
300 static void	acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
301 
302 static device_method_t acpi_asus_wmi_methods[] = {
303 	DEVMETHOD(device_identify, acpi_asus_wmi_identify),
304 	DEVMETHOD(device_probe, acpi_asus_wmi_probe),
305 	DEVMETHOD(device_attach, acpi_asus_wmi_attach),
306 	DEVMETHOD(device_detach, acpi_asus_wmi_detach),
307 
308 	DEVMETHOD_END
309 };
310 
311 static driver_t	acpi_asus_wmi_driver = {
312 	"acpi_asus_wmi",
313 	acpi_asus_wmi_methods,
314 	sizeof(struct acpi_asus_wmi_softc),
315 };
316 
317 static devclass_t acpi_asus_wmi_devclass;
318 
319 DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver,
320     acpi_asus_wmi_devclass, 0, 0);
321 MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
322 MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
323 
324 static void
325 acpi_asus_wmi_identify(driver_t *driver, device_t parent)
326 {
327 
328 	/* Don't do anything if driver is disabled. */
329 	if (acpi_disabled("asus_wmi"))
330 		return;
331 
332 	/* Add only a single device instance. */
333 	if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL)
334 		return;
335 
336 	/* Check management GUID to see whether system is compatible. */
337 	if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,
338 	    ACPI_ASUS_WMI_MGMT_GUID))
339 		return;
340 
341 	if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL)
342 		device_printf(parent, "add acpi_asus_wmi child failed\n");
343 }
344 
345 static int
346 acpi_asus_wmi_probe(device_t dev)
347 {
348 
349 	if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev),
350 	    ACPI_ASUS_WMI_MGMT_GUID))
351 		return (EINVAL);
352 	device_set_desc(dev, "ASUS WMI device");
353 	return (0);
354 }
355 
356 static int
357 acpi_asus_wmi_attach(device_t dev)
358 {
359 	struct acpi_asus_wmi_softc	*sc;
360 	UINT32			val;
361 	int			dev_id, i;
362 
363 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
364 
365 	sc = device_get_softc(dev);
366 	sc->dev = dev;
367 	sc->wmi_dev = device_get_parent(dev);
368 	sc->handle_keys = 1;
369 
370 	/* Check management GUID. */
371 	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
372 	    ACPI_ASUS_WMI_MGMT_GUID)) {
373 		device_printf(dev,
374 		    "WMI device does not provide the ASUS management GUID\n");
375 		return (EINVAL);
376 	}
377 
378 	/* Find proper DSTS method. */
379 	sc->dsts_id = ASUS_WMI_METHODID_DSTS;
380 next:
381 	for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
382 		dev_id = acpi_asus_wmi_sysctls[i].dev_id;
383 		if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
384 			continue;
385 		break;
386 	}
387 	if (acpi_asus_wmi_sysctls[i].name == NULL) {
388 		if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) {
389 			sc->dsts_id = ASUS_WMI_METHODID_DSTS2;
390 			goto next;
391 		} else {
392 			device_printf(dev, "Can not detect DSTS method ID\n");
393 			return (EINVAL);
394 		}
395 	}
396 
397 	/* Find proper and attach to notufy GUID. */
398 	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
399 	    ACPI_ASUS_WMI_EVENT_GUID))
400 		sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID;
401 	else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
402 	    ACPI_EEEPC_WMI_EVENT_GUID))
403 		sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID;
404 	else
405 		sc->notify_guid = NULL;
406 	if (sc->notify_guid != NULL) {
407 		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
408 		    sc->notify_guid, acpi_asus_wmi_notify, dev))
409 			sc->notify_guid = NULL;
410 	}
411 	if (sc->notify_guid == NULL)
412 		device_printf(dev, "Could not install event handler!\n");
413 
414 	/* Initialize. */
415 	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
416 	    ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose)
417 		device_printf(dev, "Initialization: %#x\n", val);
418 	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
419 	    ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose)
420 		device_printf(dev, "WMI BIOS version: %d.%d\n",
421 		    val >> 16, val & 0xFF);
422 	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
423 	    ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose)
424 		device_printf(dev, "SFUN value: %#x\n", val);
425 
426 	ACPI_SERIAL_BEGIN(asus_wmi);
427 
428 	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
429 	sc->sysctl_tree = device_get_sysctl_tree(dev);
430 	SYSCTL_ADD_INT(sc->sysctl_ctx,
431 	    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
432 	    "handle_keys", CTLFLAG_RW, &sc->handle_keys,
433 	    0, "Handle some hardware keys inside the driver");
434 	for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
435 		dev_id = acpi_asus_wmi_sysctls[i].dev_id;
436 		if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
437 			continue;
438 		switch (dev_id) {
439 		case ASUS_WMI_DEVID_THERMAL_CTRL:
440 		case ASUS_WMI_DEVID_PROCESSOR_STATE:
441 		case ASUS_WMI_DEVID_FAN_CTRL:
442 		case ASUS_WMI_DEVID_BRIGHTNESS:
443 			if (val == 0)
444 				continue;
445 			break;
446 		default:
447 			if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0)
448 				continue;
449 			break;
450 		}
451 
452 		SYSCTL_ADD_PROC(sc->sysctl_ctx,
453 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
454 		    acpi_asus_wmi_sysctls[i].name,
455 		    acpi_asus_wmi_sysctls[i].access,
456 		    sc, i, acpi_asus_wmi_sysctl, "I",
457 		    acpi_asus_wmi_sysctls[i].description);
458 	}
459 	ACPI_SERIAL_END(asus_wmi);
460 
461 	return (0);
462 }
463 
464 static int
465 acpi_asus_wmi_detach(device_t dev)
466 {
467 	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
468 
469 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
470 
471 	if (sc->notify_guid)
472 		ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid);
473 
474 	return (0);
475 }
476 
477 static int
478 acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS)
479 {
480 	struct acpi_asus_wmi_softc	*sc;
481 	int			arg;
482 	int			oldarg;
483 	int			error = 0;
484 	int			function;
485 	int			dev_id;
486 
487 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
488 
489 	sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1;
490 	function = oidp->oid_arg2;
491 	dev_id = acpi_asus_wmi_sysctls[function].dev_id;
492 
493 	ACPI_SERIAL_BEGIN(asus_wmi);
494 	arg = acpi_asus_wmi_sysctl_get(sc, dev_id);
495 	oldarg = arg;
496 	error = sysctl_handle_int(oidp, &arg, 0, req);
497 	if (!error && req->newptr != NULL)
498 		error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg);
499 	ACPI_SERIAL_END(asus_wmi);
500 
501 	return (error);
502 }
503 
504 static int
505 acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id)
506 {
507 	UINT32	val = 0;
508 
509 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
510 	ACPI_SERIAL_ASSERT(asus_wmi);
511 
512 	acpi_wpi_asus_get_devstate(sc, dev_id, &val);
513 
514 	switch(dev_id) {
515 	case ASUS_WMI_DEVID_THERMAL_CTRL:
516 		val = (val - 2732 + 5) / 10;
517 		break;
518 	case ASUS_WMI_DEVID_PROCESSOR_STATE:
519 	case ASUS_WMI_DEVID_FAN_CTRL:
520 		break;
521 	case ASUS_WMI_DEVID_BRIGHTNESS:
522 		val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;
523 		break;
524 	case ASUS_WMI_DEVID_KBD_BACKLIGHT:
525 		val &= 0x7;
526 		break;
527 	default:
528 		if (val & ASUS_WMI_DSTS_UNKNOWN_BIT)
529 			val = -1;
530 		else
531 			val = !!(val & ASUS_WMI_DSTS_STATUS_BIT);
532 		break;
533 	}
534 
535 	return (val);
536 }
537 
538 static int
539 acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg)
540 {
541 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
542 	ACPI_SERIAL_ASSERT(asus_wmi);
543 
544 	switch(dev_id) {
545 	case ASUS_WMI_DEVID_KBD_BACKLIGHT:
546 		arg = min(0x7, arg);
547 		if (arg != 0)
548 			arg |= 0x80;
549 		break;
550 	}
551 
552 	acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL);
553 
554 	return (0);
555 }
556 
557 static __inline void
558 acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
559 	if (buf && buf->Pointer) {
560 		AcpiOsFree(buf->Pointer);
561 	}
562 }
563 
564 static void
565 acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
566 {
567 	device_t dev = context;
568 	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
569 	UINT32 val;
570 	int code = 0;
571 
572 	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
573 	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
574 	ACPI_OBJECT *obj;
575 	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
576 	obj = (ACPI_OBJECT*) response.Pointer;
577 	if (obj && obj->Type == ACPI_TYPE_INTEGER) {
578 		code = obj->Integer.Value;
579 		acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT,
580 		    code);
581 	}
582 	if (code && sc->handle_keys) {
583 		/* Keyboard backlight control. */
584 		if (code == 0xc4 || code == 0xc5) {
585 			acpi_wpi_asus_get_devstate(sc,
586 			    ASUS_WMI_DEVID_KBD_BACKLIGHT, &val);
587 			val &= 0x7;
588 			if (code == 0xc4) {
589 				if (val < 0x7)
590 					val++;
591 			} else if (val > 0)
592 				val--;
593 			if (val != 0)
594 				val |= 0x80;
595 			acpi_wpi_asus_set_devstate(sc,
596 			    ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL);
597 		}
598 		/* Touchpad control. */
599 		if (code == 0x6b) {
600 			acpi_wpi_asus_get_devstate(sc,
601 			    ASUS_WMI_DEVID_TOUCHPAD, &val);
602 			val = !(val & 1);
603 			acpi_wpi_asus_set_devstate(sc,
604 			    ASUS_WMI_DEVID_TOUCHPAD, val, NULL);
605 		}
606 	}
607 	acpi_asus_wmi_free_buffer(&response);
608 }
609 
610 static int
611 acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
612     UINT32 arg0, UINT32 arg1, UINT32 *retval)
613 {
614 	UINT32		params[2] = { arg0, arg1 };
615 	UINT32		result;
616 	ACPI_OBJECT	*obj;
617 	ACPI_BUFFER	in = { sizeof(params), &params };
618 	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
619 
620 	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev,
621 	    ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) {
622 		acpi_asus_wmi_free_buffer(&out);
623 		return (-EINVAL);
624 	}
625 	obj = out.Pointer;
626 	if (obj && obj->Type == ACPI_TYPE_INTEGER)
627 		result = (UINT32) obj->Integer.Value;
628 	else
629 		result = 0;
630 	acpi_asus_wmi_free_buffer(&out);
631 	if (retval)
632 		*retval = result;
633 	return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0);
634 }
635 
636 static int
637 acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
638     UINT32 dev_id, UINT32 *retval)
639 {
640 
641 	return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
642 	    sc->dsts_id, dev_id, 0, retval));
643 }
644 
645 static int
646 acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
647     UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval)
648 {
649 
650 	return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
651 	    ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval));
652 }
653