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
107
108struct 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
118static 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
283ACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");
284
285static void	acpi_asus_wmi_identify(driver_t *driver, device_t parent);
286static int	acpi_asus_wmi_probe(device_t dev);
287static int	acpi_asus_wmi_attach(device_t dev);
288static int	acpi_asus_wmi_detach(device_t dev);
289
290static int	acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
291static int	acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
292		    int arg, int oldarg);
293static int	acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);
294static int	acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
295		    UINT32 arg0, UINT32 arg1, UINT32 *retval);
296static int	acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
297		    UINT32 dev_id, UINT32 *retval);
298static int	acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
299		    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
300static void	acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
301
302static 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
311static driver_t	acpi_asus_wmi_driver = {
312	"acpi_asus_wmi",
313	acpi_asus_wmi_methods,
314	sizeof(struct acpi_asus_wmi_softc),
315};
316
317static devclass_t acpi_asus_wmi_devclass;
318
319DRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver,
320    acpi_asus_wmi_devclass, 0, 0);
321MODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
322MODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
323
324static void
325acpi_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
345static int
346acpi_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
356static int
357acpi_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;
380next:
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
464static int
465acpi_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
477static int
478acpi_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
504static int
505acpi_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
538static int
539acpi_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
557static __inline void
558acpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
559	if (buf && buf->Pointer) {
560		AcpiOsFree(buf->Pointer);
561	}
562}
563
564static void
565acpi_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
610static int
611acpi_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
636static int
637acpi_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
645static int
646acpi_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