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