1237981Smav/*-
2237981Smav * Copyright (c) 2012 Alexander Motin <mav@FreeBSD.org>
3237981Smav * All rights reserved.
4237981Smav *
5237981Smav * Redistribution and use in source and binary forms, with or without
6237981Smav * modification, are permitted provided that the following conditions
7237981Smav * are met:
8237981Smav * 1. Redistributions of source code must retain the above copyright
9237981Smav *    notice, this list of conditions and the following disclaimer.
10237981Smav * 2. Redistributions in binary form must reproduce the above copyright
11237981Smav *    notice, this list of conditions and the following disclaimer in the
12237981Smav *    documentation and/or other materials provided with the distribution.
13237981Smav *
14237981Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15237981Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16237981Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17237981Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18237981Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19237981Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20237981Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21237981Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22237981Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23237981Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24237981Smav * SUCH DAMAGE.
25237981Smav */
26237981Smav
27237981Smav#include <sys/cdefs.h>
28237981Smav__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpi_support/acpi_asus_wmi.c 273736 2014-10-27 14:38:00Z hselasky $");
29237981Smav
30237981Smav#include "opt_acpi.h"
31237981Smav#include <sys/param.h>
32237981Smav#include <sys/conf.h>
33237981Smav#include <sys/uio.h>
34237981Smav#include <sys/proc.h>
35237981Smav#include <sys/kernel.h>
36237981Smav#include <sys/bus.h>
37237981Smav#include <sys/sbuf.h>
38237981Smav#include <sys/module.h>
39237981Smav#include <sys/sysctl.h>
40237981Smav
41237981Smav#include <contrib/dev/acpica/include/acpi.h>
42237981Smav#include <contrib/dev/acpica/include/accommon.h>
43237981Smav#include <dev/acpica/acpivar.h>
44237981Smav#include "acpi_wmi_if.h"
45237981Smav
46237981Smav#define _COMPONENT	ACPI_OEM
47237981SmavACPI_MODULE_NAME("ASUS-WMI")
48237981Smav
49237981Smav#define ACPI_ASUS_WMI_MGMT_GUID 	"97845ED0-4E6D-11DE-8A39-0800200C9A66"
50237981Smav#define ACPI_ASUS_WMI_EVENT_GUID	"0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
51237981Smav#define ACPI_EEEPC_WMI_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
52237981Smav
53237981Smav/* WMI Methods */
54237981Smav#define ASUS_WMI_METHODID_SPEC          0x43455053
55237981Smav#define ASUS_WMI_METHODID_SFUN          0x4E554653
56237981Smav#define ASUS_WMI_METHODID_DSTS          0x53544344
57237981Smav#define ASUS_WMI_METHODID_DSTS2         0x53545344
58237981Smav#define ASUS_WMI_METHODID_DEVS          0x53564544
59237981Smav#define ASUS_WMI_METHODID_INIT          0x54494E49
60237981Smav#define ASUS_WMI_METHODID_HKEY          0x59454B48
61237981Smav
62237981Smav#define ASUS_WMI_UNSUPPORTED_METHOD     0xFFFFFFFE
63237981Smav
64237981Smav/* Wireless */
65237981Smav#define ASUS_WMI_DEVID_HW_SWITCH        0x00010001
66237981Smav#define ASUS_WMI_DEVID_WIRELESS_LED     0x00010002
67237981Smav#define ASUS_WMI_DEVID_CWAP             0x00010003
68237981Smav#define ASUS_WMI_DEVID_WLAN             0x00010011
69237981Smav#define ASUS_WMI_DEVID_BLUETOOTH        0x00010013
70237981Smav#define ASUS_WMI_DEVID_GPS              0x00010015
71237981Smav#define ASUS_WMI_DEVID_WIMAX            0x00010017
72237981Smav#define ASUS_WMI_DEVID_WWAN3G           0x00010019
73237981Smav#define ASUS_WMI_DEVID_UWB              0x00010021
74237981Smav
75237981Smav/* LEDs */
76237981Smav#define ASUS_WMI_DEVID_LED1             0x00020011
77237981Smav#define ASUS_WMI_DEVID_LED2             0x00020012
78237981Smav#define ASUS_WMI_DEVID_LED3             0x00020013
79237981Smav#define ASUS_WMI_DEVID_LED4             0x00020014
80237981Smav#define ASUS_WMI_DEVID_LED5             0x00020015
81237981Smav#define ASUS_WMI_DEVID_LED6             0x00020016
82237981Smav
83237981Smav/* Backlight and Brightness */
84237981Smav#define ASUS_WMI_DEVID_BACKLIGHT        0x00050011
85237981Smav#define ASUS_WMI_DEVID_BRIGHTNESS       0x00050012
86237981Smav#define ASUS_WMI_DEVID_KBD_BACKLIGHT    0x00050021
87237981Smav#define ASUS_WMI_DEVID_LIGHT_SENSOR     0x00050022
88237981Smav
89237981Smav/* Misc */
90237981Smav#define ASUS_WMI_DEVID_CAMERA           0x00060013
91237981Smav#define ASUS_WMI_DEVID_CARDREADER       0x00080013
92237981Smav#define ASUS_WMI_DEVID_TOUCHPAD         0x00100011
93237981Smav#define ASUS_WMI_DEVID_TOUCHPAD_LED     0x00100012
94237981Smav#define ASUS_WMI_DEVID_THERMAL_CTRL     0x00110011
95237981Smav#define ASUS_WMI_DEVID_FAN_CTRL         0x00110012
96237981Smav#define ASUS_WMI_DEVID_PROCESSOR_STATE  0x00120012
97237981Smav
98237981Smav/* DSTS masks */
99237981Smav#define ASUS_WMI_DSTS_STATUS_BIT        0x00000001
100237981Smav#define ASUS_WMI_DSTS_UNKNOWN_BIT       0x00000002
101237981Smav#define ASUS_WMI_DSTS_PRESENCE_BIT      0x00010000
102237981Smav#define ASUS_WMI_DSTS_USER_BIT          0x00020000
103237981Smav#define ASUS_WMI_DSTS_BIOS_BIT          0x00040000
104237981Smav#define ASUS_WMI_DSTS_BRIGHTNESS_MASK   0x000000FF
105237981Smav#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK   0x0000FF00
106237981Smav
107237981Smav
108237981Smavstruct acpi_asus_wmi_softc {
109237981Smav	device_t	dev;
110237981Smav	device_t	wmi_dev;
111237981Smav	const char	*notify_guid;
112237981Smav	struct sysctl_ctx_list	*sysctl_ctx;
113237981Smav	struct sysctl_oid	*sysctl_tree;
114237981Smav	int		dsts_id;
115237981Smav	int		handle_keys;
116237981Smav};
117237981Smav
118237981Smavstatic struct {
119237981Smav	char	*name;
120237981Smav	int	dev_id;
121237981Smav	char	*description;
122273736Shselasky	int	flag_rdonly;
123237981Smav} acpi_asus_wmi_sysctls[] = {
124237981Smav	{
125237981Smav		.name		= "hw_switch",
126237981Smav		.dev_id		= ASUS_WMI_DEVID_HW_SWITCH,
127237981Smav		.description	= "hw_switch",
128237981Smav	},
129237981Smav	{
130237981Smav		.name		= "wireless_led",
131237981Smav		.dev_id		= ASUS_WMI_DEVID_WIRELESS_LED,
132237981Smav		.description	= "Wireless LED control",
133237981Smav	},
134237981Smav	{
135237981Smav		.name		= "cwap",
136237981Smav		.dev_id		= ASUS_WMI_DEVID_CWAP,
137237981Smav		.description	= "Alt+F2 function",
138237981Smav	},
139237981Smav	{
140237981Smav		.name		= "wlan",
141237981Smav		.dev_id		= ASUS_WMI_DEVID_WLAN,
142237981Smav		.description	= "WLAN power control",
143237981Smav	},
144237981Smav	{
145237981Smav		.name		= "bluetooth",
146237981Smav		.dev_id		= ASUS_WMI_DEVID_BLUETOOTH,
147237981Smav		.description	= "Bluetooth power control",
148237981Smav	},
149237981Smav	{
150237981Smav		.name		= "gps",
151237981Smav		.dev_id		= ASUS_WMI_DEVID_GPS,
152237981Smav		.description	= "GPS power control",
153237981Smav	},
154237981Smav	{
155237981Smav		.name		= "wimax",
156237981Smav		.dev_id		= ASUS_WMI_DEVID_WIMAX,
157237981Smav		.description	= "WiMAX power control",
158237981Smav	},
159237981Smav	{
160237981Smav		.name		= "wwan3g",
161237981Smav		.dev_id		= ASUS_WMI_DEVID_WWAN3G,
162237981Smav		.description	= "WWAN-3G power control",
163237981Smav	},
164237981Smav	{
165237981Smav		.name		= "uwb",
166237981Smav		.dev_id		= ASUS_WMI_DEVID_UWB,
167237981Smav		.description	= "UWB power control",
168237981Smav	},
169237981Smav	{
170237981Smav		.name		= "led1",
171237981Smav		.dev_id		= ASUS_WMI_DEVID_LED1,
172237981Smav		.description	= "LED1 control",
173237981Smav	},
174237981Smav	{
175237981Smav		.name		= "led2",
176237981Smav		.dev_id		= ASUS_WMI_DEVID_LED2,
177237981Smav		.description	= "LED2 control",
178237981Smav	},
179237981Smav	{
180237981Smav		.name		= "led3",
181237981Smav		.dev_id		= ASUS_WMI_DEVID_LED3,
182237981Smav		.description	= "LED3 control",
183237981Smav	},
184237981Smav	{
185237981Smav		.name		= "led4",
186237981Smav		.dev_id		= ASUS_WMI_DEVID_LED4,
187237981Smav		.description	= "LED4 control",
188237981Smav	},
189237981Smav	{
190237981Smav		.name		= "led5",
191237981Smav		.dev_id		= ASUS_WMI_DEVID_LED5,
192237981Smav		.description	= "LED5 control",
193237981Smav	},
194237981Smav	{
195237981Smav		.name		= "led6",
196237981Smav		.dev_id		= ASUS_WMI_DEVID_LED6,
197237981Smav		.description	= "LED6 control",
198237981Smav	},
199237981Smav	{
200237981Smav		.name		= "backlight",
201237981Smav		.dev_id		= ASUS_WMI_DEVID_BACKLIGHT,
202237981Smav		.description	= "LCD backlight on/off control",
203237981Smav	},
204237981Smav	{
205237981Smav		.name		= "brightness",
206237981Smav		.dev_id		= ASUS_WMI_DEVID_BRIGHTNESS,
207237981Smav		.description	= "LCD backlight brightness control",
208237981Smav	},
209237981Smav	{
210237981Smav		.name		= "kbd_backlight",
211237981Smav		.dev_id		= ASUS_WMI_DEVID_KBD_BACKLIGHT,
212237981Smav		.description	= "Keyboard backlight brightness control",
213237981Smav	},
214237981Smav	{
215237981Smav		.name		= "light_sensor",
216237981Smav		.dev_id		= ASUS_WMI_DEVID_LIGHT_SENSOR,
217237981Smav		.description	= "Ambient light sensor",
218237981Smav	},
219237981Smav	{
220237981Smav		.name		= "camera",
221237981Smav		.dev_id		= ASUS_WMI_DEVID_CAMERA,
222237981Smav		.description	= "Camera power control",
223237981Smav	},
224237981Smav	{
225237981Smav		.name		= "cardreader",
226237981Smav		.dev_id		= ASUS_WMI_DEVID_CARDREADER,
227237981Smav		.description	= "Cardreader power control",
228237981Smav	},
229237981Smav	{
230237981Smav		.name		= "touchpad",
231237981Smav		.dev_id		= ASUS_WMI_DEVID_TOUCHPAD,
232237981Smav		.description	= "Touchpad control",
233237981Smav	},
234237981Smav	{
235237981Smav		.name		= "touchpad_led",
236237981Smav		.dev_id		= ASUS_WMI_DEVID_TOUCHPAD_LED,
237237981Smav		.description	= "Touchpad LED control",
238237981Smav	},
239237981Smav	{
240237981Smav		.name		= "themperature",
241237981Smav		.dev_id		= ASUS_WMI_DEVID_THERMAL_CTRL,
242237981Smav		.description	= "Temperature (C)",
243273736Shselasky		.flag_rdonly	= 1
244237981Smav	},
245237981Smav	{
246237981Smav		.name		= "fan_speed",
247237981Smav		.dev_id		= ASUS_WMI_DEVID_FAN_CTRL,
248237981Smav		.description	= "Fan speed (0-3)",
249273736Shselasky		.flag_rdonly	= 1
250237981Smav	},
251237981Smav	{
252237981Smav		.name		= "processor_state",
253237981Smav		.dev_id		= ASUS_WMI_DEVID_PROCESSOR_STATE,
254273736Shselasky		.flag_rdonly	= 1
255237981Smav	},
256237981Smav	{ NULL, 0, NULL, 0 }
257237981Smav};
258237981Smav
259237981SmavACPI_SERIAL_DECL(asus_wmi, "ASUS WMI device");
260237981Smav
261237981Smavstatic void	acpi_asus_wmi_identify(driver_t *driver, device_t parent);
262237981Smavstatic int	acpi_asus_wmi_probe(device_t dev);
263237981Smavstatic int	acpi_asus_wmi_attach(device_t dev);
264237981Smavstatic int	acpi_asus_wmi_detach(device_t dev);
265237981Smav
266237981Smavstatic int	acpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS);
267237981Smavstatic int	acpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id,
268237981Smav		    int arg, int oldarg);
269237981Smavstatic int	acpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id);
270237981Smavstatic int	acpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
271237981Smav		    UINT32 arg0, UINT32 arg1, UINT32 *retval);
272237981Smavstatic int	acpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
273237981Smav		    UINT32 dev_id, UINT32 *retval);
274237981Smavstatic int	acpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
275237981Smav		    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval);
276237981Smavstatic void	acpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context);
277237981Smav
278237981Smavstatic device_method_t acpi_asus_wmi_methods[] = {
279237981Smav	DEVMETHOD(device_identify, acpi_asus_wmi_identify),
280237981Smav	DEVMETHOD(device_probe, acpi_asus_wmi_probe),
281237981Smav	DEVMETHOD(device_attach, acpi_asus_wmi_attach),
282237981Smav	DEVMETHOD(device_detach, acpi_asus_wmi_detach),
283246128Ssbz
284246128Ssbz	DEVMETHOD_END
285237981Smav};
286237981Smav
287237981Smavstatic driver_t	acpi_asus_wmi_driver = {
288237981Smav	"acpi_asus_wmi",
289237981Smav	acpi_asus_wmi_methods,
290237981Smav	sizeof(struct acpi_asus_wmi_softc),
291237981Smav};
292237981Smav
293237981Smavstatic devclass_t acpi_asus_wmi_devclass;
294237981Smav
295237981SmavDRIVER_MODULE(acpi_asus_wmi, acpi_wmi, acpi_asus_wmi_driver,
296237981Smav    acpi_asus_wmi_devclass, 0, 0);
297237981SmavMODULE_DEPEND(acpi_asus_wmi, acpi_wmi, 1, 1, 1);
298237981SmavMODULE_DEPEND(acpi_asus_wmi, acpi, 1, 1, 1);
299237981Smav
300237981Smavstatic void
301237981Smavacpi_asus_wmi_identify(driver_t *driver, device_t parent)
302237981Smav{
303237981Smav
304237981Smav	/* Don't do anything if driver is disabled. */
305237981Smav	if (acpi_disabled("asus_wmi"))
306237981Smav		return;
307237981Smav
308237981Smav	/* Add only a single device instance. */
309237981Smav	if (device_find_child(parent, "acpi_asus_wmi", -1) != NULL)
310237981Smav		return;
311237981Smav
312237981Smav	/* Check management GUID to see whether system is compatible. */
313237981Smav	if (!ACPI_WMI_PROVIDES_GUID_STRING(parent,
314237981Smav	    ACPI_ASUS_WMI_MGMT_GUID))
315237981Smav		return;
316237981Smav
317237981Smav	if (BUS_ADD_CHILD(parent, 0, "acpi_asus_wmi", -1) == NULL)
318237981Smav		device_printf(parent, "add acpi_asus_wmi child failed\n");
319237981Smav}
320237981Smav
321237981Smavstatic int
322237981Smavacpi_asus_wmi_probe(device_t dev)
323237981Smav{
324237981Smav
325237981Smav	if (!ACPI_WMI_PROVIDES_GUID_STRING(device_get_parent(dev),
326237981Smav	    ACPI_ASUS_WMI_MGMT_GUID))
327237981Smav		return (EINVAL);
328237981Smav	device_set_desc(dev, "ASUS WMI device");
329237981Smav	return (0);
330237981Smav}
331237981Smav
332237981Smavstatic int
333237981Smavacpi_asus_wmi_attach(device_t dev)
334237981Smav{
335237981Smav	struct acpi_asus_wmi_softc	*sc;
336237981Smav	UINT32			val;
337237981Smav	int			dev_id, i;
338237981Smav
339237981Smav	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
340237981Smav
341237981Smav	sc = device_get_softc(dev);
342237981Smav	sc->dev = dev;
343237981Smav	sc->wmi_dev = device_get_parent(dev);
344237981Smav	sc->handle_keys = 1;
345237981Smav
346237981Smav	/* Check management GUID. */
347237981Smav	if (!ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
348237981Smav	    ACPI_ASUS_WMI_MGMT_GUID)) {
349237981Smav		device_printf(dev,
350237981Smav		    "WMI device does not provide the ASUS management GUID\n");
351237981Smav		return (EINVAL);
352237981Smav	}
353237981Smav
354237981Smav	/* Find proper DSTS method. */
355237981Smav	sc->dsts_id = ASUS_WMI_METHODID_DSTS;
356237981Smavnext:
357237981Smav	for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
358237981Smav		dev_id = acpi_asus_wmi_sysctls[i].dev_id;
359237981Smav		if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
360237981Smav			continue;
361237981Smav		break;
362237981Smav	}
363237981Smav	if (acpi_asus_wmi_sysctls[i].name == NULL) {
364237981Smav		if (sc->dsts_id == ASUS_WMI_METHODID_DSTS) {
365237981Smav			sc->dsts_id = ASUS_WMI_METHODID_DSTS2;
366237981Smav			goto next;
367237981Smav		} else {
368237981Smav			device_printf(dev, "Can not detect DSTS method ID\n");
369237981Smav			return (EINVAL);
370237981Smav		}
371237981Smav	}
372237981Smav
373237981Smav	/* Find proper and attach to notufy GUID. */
374237981Smav	if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
375237981Smav	    ACPI_ASUS_WMI_EVENT_GUID))
376237981Smav		sc->notify_guid = ACPI_ASUS_WMI_EVENT_GUID;
377237981Smav	else if (ACPI_WMI_PROVIDES_GUID_STRING(sc->wmi_dev,
378237981Smav	    ACPI_EEEPC_WMI_EVENT_GUID))
379237981Smav		sc->notify_guid = ACPI_EEEPC_WMI_EVENT_GUID;
380237981Smav	else
381237981Smav		sc->notify_guid = NULL;
382237981Smav	if (sc->notify_guid != NULL) {
383237981Smav		if (ACPI_WMI_INSTALL_EVENT_HANDLER(sc->wmi_dev,
384237981Smav		    sc->notify_guid, acpi_asus_wmi_notify, dev))
385237981Smav			sc->notify_guid = NULL;
386237981Smav	}
387237981Smav	if (sc->notify_guid == NULL)
388237981Smav		device_printf(dev, "Could not install event handler!\n");
389237981Smav
390237981Smav	/* Initialize. */
391237981Smav	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
392237981Smav	    ASUS_WMI_METHODID_INIT, 0, 0, &val) && bootverbose)
393237981Smav		device_printf(dev, "Initialization: %#x\n", val);
394237981Smav	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
395237981Smav	    ASUS_WMI_METHODID_SPEC, 0, 0x9, &val) && bootverbose)
396237981Smav		device_printf(dev, "WMI BIOS version: %d.%d\n",
397237981Smav		    val >> 16, val & 0xFF);
398237981Smav	if (!acpi_asus_wmi_evaluate_method(sc->wmi_dev,
399237981Smav	    ASUS_WMI_METHODID_SFUN, 0, 0, &val) && bootverbose)
400237981Smav		device_printf(dev, "SFUN value: %#x\n", val);
401237981Smav
402237981Smav	ACPI_SERIAL_BEGIN(asus_wmi);
403237981Smav
404237981Smav	sc->sysctl_ctx = device_get_sysctl_ctx(dev);
405237981Smav	sc->sysctl_tree = device_get_sysctl_tree(dev);
406237981Smav	SYSCTL_ADD_INT(sc->sysctl_ctx,
407237981Smav	    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
408237981Smav	    "handle_keys", CTLFLAG_RW, &sc->handle_keys,
409237981Smav	    0, "Handle some hardware keys inside the driver");
410237981Smav	for (i = 0; acpi_asus_wmi_sysctls[i].name != NULL; ++i) {
411237981Smav		dev_id = acpi_asus_wmi_sysctls[i].dev_id;
412237981Smav		if (acpi_wpi_asus_get_devstate(sc, dev_id, &val))
413237981Smav			continue;
414237981Smav		switch (dev_id) {
415237981Smav		case ASUS_WMI_DEVID_THERMAL_CTRL:
416237981Smav		case ASUS_WMI_DEVID_PROCESSOR_STATE:
417237981Smav		case ASUS_WMI_DEVID_FAN_CTRL:
418237981Smav		case ASUS_WMI_DEVID_BRIGHTNESS:
419237981Smav			if (val == 0)
420237981Smav				continue;
421237981Smav			break;
422237981Smav		default:
423237981Smav			if ((val & ASUS_WMI_DSTS_PRESENCE_BIT) == 0)
424237981Smav				continue;
425237981Smav			break;
426237981Smav		}
427237981Smav
428273736Shselasky		if (acpi_asus_wmi_sysctls[i].flag_rdonly != 0) {
429273736Shselasky			SYSCTL_ADD_PROC(sc->sysctl_ctx,
430273736Shselasky			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
431273736Shselasky			    acpi_asus_wmi_sysctls[i].name,
432273736Shselasky			    CTLTYPE_INT | CTLFLAG_RD,
433273736Shselasky			    sc, i, acpi_asus_wmi_sysctl, "I",
434273736Shselasky			    acpi_asus_wmi_sysctls[i].description);
435273736Shselasky		} else {
436273736Shselasky			SYSCTL_ADD_PROC(sc->sysctl_ctx,
437273736Shselasky			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
438273736Shselasky			    acpi_asus_wmi_sysctls[i].name,
439273736Shselasky			    CTLTYPE_INT | CTLFLAG_RW,
440273736Shselasky			    sc, i, acpi_asus_wmi_sysctl, "I",
441273736Shselasky			    acpi_asus_wmi_sysctls[i].description);
442273736Shselasky		}
443237981Smav	}
444237981Smav	ACPI_SERIAL_END(asus_wmi);
445237981Smav
446237981Smav	return (0);
447237981Smav}
448237981Smav
449237981Smavstatic int
450237981Smavacpi_asus_wmi_detach(device_t dev)
451237981Smav{
452237981Smav	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
453237981Smav
454237981Smav	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
455237981Smav
456237981Smav	if (sc->notify_guid)
457237981Smav		ACPI_WMI_REMOVE_EVENT_HANDLER(dev, sc->notify_guid);
458237981Smav
459237981Smav	return (0);
460237981Smav}
461237981Smav
462237981Smavstatic int
463237981Smavacpi_asus_wmi_sysctl(SYSCTL_HANDLER_ARGS)
464237981Smav{
465237981Smav	struct acpi_asus_wmi_softc	*sc;
466237981Smav	int			arg;
467237981Smav	int			oldarg;
468237981Smav	int			error = 0;
469237981Smav	int			function;
470237981Smav	int			dev_id;
471237981Smav
472237981Smav	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
473237981Smav
474237981Smav	sc = (struct acpi_asus_wmi_softc *)oidp->oid_arg1;
475237981Smav	function = oidp->oid_arg2;
476237981Smav	dev_id = acpi_asus_wmi_sysctls[function].dev_id;
477237981Smav
478237981Smav	ACPI_SERIAL_BEGIN(asus_wmi);
479237981Smav	arg = acpi_asus_wmi_sysctl_get(sc, dev_id);
480237981Smav	oldarg = arg;
481237981Smav	error = sysctl_handle_int(oidp, &arg, 0, req);
482237981Smav	if (!error && req->newptr != NULL)
483237981Smav		error = acpi_asus_wmi_sysctl_set(sc, dev_id, arg, oldarg);
484237981Smav	ACPI_SERIAL_END(asus_wmi);
485237981Smav
486237981Smav	return (error);
487237981Smav}
488237981Smav
489237981Smavstatic int
490237981Smavacpi_asus_wmi_sysctl_get(struct acpi_asus_wmi_softc *sc, int dev_id)
491237981Smav{
492237981Smav	UINT32	val = 0;
493237981Smav
494237981Smav	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
495237981Smav	ACPI_SERIAL_ASSERT(asus_wmi);
496237981Smav
497237981Smav	acpi_wpi_asus_get_devstate(sc, dev_id, &val);
498237981Smav
499237981Smav	switch(dev_id) {
500237981Smav	case ASUS_WMI_DEVID_THERMAL_CTRL:
501237981Smav		val = (val - 2732 + 5) / 10;
502237981Smav		break;
503237981Smav	case ASUS_WMI_DEVID_PROCESSOR_STATE:
504237981Smav	case ASUS_WMI_DEVID_FAN_CTRL:
505237981Smav		break;
506237981Smav	case ASUS_WMI_DEVID_BRIGHTNESS:
507237981Smav		val &= ASUS_WMI_DSTS_BRIGHTNESS_MASK;
508237981Smav		break;
509237981Smav	case ASUS_WMI_DEVID_KBD_BACKLIGHT:
510237981Smav		val &= 0x7;
511237981Smav		break;
512237981Smav	default:
513237981Smav		if (val & ASUS_WMI_DSTS_UNKNOWN_BIT)
514237981Smav			val = -1;
515237981Smav		else
516237981Smav			val = !!(val & ASUS_WMI_DSTS_STATUS_BIT);
517237981Smav		break;
518237981Smav	}
519237981Smav
520237981Smav	return (val);
521237981Smav}
522237981Smav
523237981Smavstatic int
524237981Smavacpi_asus_wmi_sysctl_set(struct acpi_asus_wmi_softc *sc, int dev_id, int arg, int oldarg)
525237981Smav{
526237981Smav	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
527237981Smav	ACPI_SERIAL_ASSERT(asus_wmi);
528237981Smav
529237981Smav	switch(dev_id) {
530237981Smav	case ASUS_WMI_DEVID_KBD_BACKLIGHT:
531237981Smav		arg = min(0x7, arg);
532237981Smav		if (arg != 0)
533237981Smav			arg |= 0x80;
534237981Smav		break;
535237981Smav	}
536237981Smav
537237981Smav	acpi_wpi_asus_set_devstate(sc, dev_id, arg, NULL);
538237981Smav
539237981Smav	return (0);
540237981Smav}
541237981Smav
542237981Smavstatic __inline void
543237981Smavacpi_asus_wmi_free_buffer(ACPI_BUFFER* buf) {
544237981Smav	if (buf && buf->Pointer) {
545237981Smav		AcpiOsFree(buf->Pointer);
546237981Smav	}
547237981Smav}
548237981Smav
549237981Smavstatic void
550237981Smavacpi_asus_wmi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
551237981Smav{
552237981Smav	device_t dev = context;
553237981Smav	ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
554237981Smav	UINT32 val;
555237981Smav	int code = 0;
556237981Smav
557237981Smav	struct acpi_asus_wmi_softc *sc = device_get_softc(dev);
558237981Smav	ACPI_BUFFER response = { ACPI_ALLOCATE_BUFFER, NULL };
559237981Smav	ACPI_OBJECT *obj;
560237981Smav	ACPI_WMI_GET_EVENT_DATA(sc->wmi_dev, notify, &response);
561237981Smav	obj = (ACPI_OBJECT*) response.Pointer;
562237981Smav	if (obj && obj->Type == ACPI_TYPE_INTEGER) {
563237981Smav		code = obj->Integer.Value;
564237981Smav		acpi_UserNotify("ASUS", ACPI_ROOT_OBJECT,
565237981Smav		    code);
566237981Smav	}
567237981Smav	if (code && sc->handle_keys) {
568237981Smav		/* Keyboard backlight control. */
569237981Smav		if (code == 0xc4 || code == 0xc5) {
570237981Smav			acpi_wpi_asus_get_devstate(sc,
571237981Smav			    ASUS_WMI_DEVID_KBD_BACKLIGHT, &val);
572237981Smav			val &= 0x7;
573237981Smav			if (code == 0xc4) {
574237981Smav				if (val < 0x7)
575237981Smav					val++;
576237981Smav			} else if (val > 0)
577237981Smav				val--;
578237981Smav			if (val != 0)
579237981Smav				val |= 0x80;
580237981Smav			acpi_wpi_asus_set_devstate(sc,
581237981Smav			    ASUS_WMI_DEVID_KBD_BACKLIGHT, val, NULL);
582237981Smav		}
583237981Smav		/* Touchpad control. */
584237981Smav		if (code == 0x6b) {
585237981Smav			acpi_wpi_asus_get_devstate(sc,
586237981Smav			    ASUS_WMI_DEVID_TOUCHPAD, &val);
587237981Smav			val = !(val & 1);
588237981Smav			acpi_wpi_asus_set_devstate(sc,
589237981Smav			    ASUS_WMI_DEVID_TOUCHPAD, val, NULL);
590237981Smav		}
591237981Smav	}
592237981Smav	acpi_asus_wmi_free_buffer(&response);
593237981Smav}
594237981Smav
595237981Smavstatic int
596237981Smavacpi_asus_wmi_evaluate_method(device_t wmi_dev, int method,
597237981Smav    UINT32 arg0, UINT32 arg1, UINT32 *retval)
598237981Smav{
599237981Smav	UINT32		params[2] = { arg0, arg1 };
600237981Smav	UINT32		result;
601237981Smav	ACPI_OBJECT	*obj;
602237981Smav	ACPI_BUFFER	in = { sizeof(params), &params };
603237981Smav	ACPI_BUFFER	out = { ACPI_ALLOCATE_BUFFER, NULL };
604237981Smav
605237981Smav	if (ACPI_FAILURE(ACPI_WMI_EVALUATE_CALL(wmi_dev,
606237981Smav	    ACPI_ASUS_WMI_MGMT_GUID, 1, method, &in, &out))) {
607237981Smav		acpi_asus_wmi_free_buffer(&out);
608237981Smav		return (-EINVAL);
609237981Smav	}
610237981Smav	obj = out.Pointer;
611237981Smav	if (obj && obj->Type == ACPI_TYPE_INTEGER)
612237981Smav		result = (UINT32) obj->Integer.Value;
613237981Smav	else
614237981Smav		result = 0;
615237981Smav	acpi_asus_wmi_free_buffer(&out);
616237981Smav	if (retval)
617237981Smav		*retval = result;
618237981Smav	return (result == ASUS_WMI_UNSUPPORTED_METHOD ? -ENODEV : 0);
619237981Smav}
620237981Smav
621237981Smavstatic int
622237981Smavacpi_wpi_asus_get_devstate(struct acpi_asus_wmi_softc *sc,
623237981Smav    UINT32 dev_id, UINT32 *retval)
624237981Smav{
625237981Smav
626237981Smav	return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
627237981Smav	    sc->dsts_id, dev_id, 0, retval));
628237981Smav}
629237981Smav
630237981Smavstatic int
631237981Smavacpi_wpi_asus_set_devstate(struct acpi_asus_wmi_softc *sc,
632237981Smav    UINT32 dev_id, UINT32 ctrl_param, UINT32 *retval)
633237981Smav{
634237981Smav
635237981Smav	return (acpi_asus_wmi_evaluate_method(sc->wmi_dev,
636237981Smav	    ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval));
637237981Smav}
638