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
45ACPI_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
105struct 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
115static 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
256ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");
257
258static void	acpi_asus_wmi_identify(driver_t *driver, device_t parent);
259static int	acpi_asus_wmi_probe(device_t dev);
260static int	acpi_asus_wmi_attach(device_t dev);
261static int	acpi_asus_wmi_detach(device_t dev);
262
263static int	acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
264static int	acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
265		    int arg, int oldarg);
266static int	acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);
267static int	acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
268		    UINT32 arg0, UINT32 arg1, UINT32 *retval);
269static int	acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
270		    UINT32 dev_id, UINT32 *retval);
271static int	acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
272		    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
273static void	acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
274
275static 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
284static driver_t	acpi_asus_wmi_driver = {
285	"acpi_asus_wmi",
286	acpi_asus_wmi_methods,
287	sizeof(struct acpi_asus_wmi_softc),
288};
289
290DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver, 0, 0);
291MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
292MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
293
294static void
295acpi_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
315static int
316acpi_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
326static int
327acpi_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;
350next:
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
443static int
444acpi_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
456static int
457acpi_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
483static int
484acpi_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
517static int
518acpi_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
536static __inline void
537acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
538	if (buf && buf->Pointer) {
539		AcpiOsFree(buf->Pointer);
540	}
541}
542
543static void
544acpi_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
589static int
590acpi_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
615static int
616acpi_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
624static int
625acpi_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