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