acpi_asus.c revision 133628
1128561Sphilip/*-
2128561Sphilip * Copyright (c) 2004 Philip Paeps <philip@FreeBSD.org>
3128561Sphilip * All rights reserved.
4128561Sphilip *
5128561Sphilip * Redistribution and use in source and binary forms, with or without
6128561Sphilip * modification, are permitted provided that the following conditions
7128561Sphilip * are met:
8128561Sphilip * 1. Redistributions of source code must retain the above copyright
9128561Sphilip *    notice, this list of conditions and the following disclaimer.
10128561Sphilip * 2. Redistributions in binary form must reproduce the above copyright
11128561Sphilip *    notice, this list of conditions and the following disclaimer in the
12128561Sphilip *    documentation and/or other materials provided with the distribution.
13128561Sphilip *
14128561Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15128561Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16128561Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17128561Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18128561Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19128561Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20128561Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21128561Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22128561Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23128561Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24128561Sphilip * SUCH DAMAGE.
25128561Sphilip *
26128561Sphilip */
27128561Sphilip
28128561Sphilip#include <sys/cdefs.h>
29128561Sphilip__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_asus.c 133628 2004-08-13 06:22:29Z njl $");
30128561Sphilip
31128561Sphilip/*
32128561Sphilip * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
33133095Sphilip * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
34128561Sphilip * implements these features in the Linux kernel.
35128561Sphilip *
36128561Sphilip *   <http://sourceforge.net/projects/acpi4asus/>
37128561Sphilip *
38128561Sphilip * Currently should support most features, but could use some more testing.
39128561Sphilip * Particularly the display-switching stuff is a bit hairy.  If you have an
40128561Sphilip * Asus laptop which doesn't appear to be supported, or strange things happen
41128561Sphilip * when using this driver, please report to <acpi@FreeBSD.org>.
42128561Sphilip *
43128561Sphilip */
44128561Sphilip
45128561Sphilip#include "opt_acpi.h"
46128561Sphilip#include <sys/param.h>
47128561Sphilip#include <sys/kernel.h>
48129882Sphk#include <sys/module.h>
49128561Sphilip#include <sys/bus.h>
50128561Sphilip#include <sys/sbuf.h>
51128561Sphilip
52128561Sphilip#include "acpi.h"
53128561Sphilip#include <dev/acpica/acpivar.h>
54128561Sphilip#include <dev/led/led.h>
55128561Sphilip
56128561Sphilip#define _COMPONENT	ACPI_ASUS
57128561SphilipACPI_MODULE_NAME("ASUS")
58128561Sphilip
59128561Sphilipstruct acpi_asus_model {
60128561Sphilip	char	*name;
61128561Sphilip
62128561Sphilip	char	*mled_set;
63128561Sphilip	char	*tled_set;
64128561Sphilip	char	*wled_set;
65128561Sphilip
66128561Sphilip	char	*brn_get;
67128561Sphilip	char	*brn_set;
68128561Sphilip	char	*brn_up;
69128561Sphilip	char	*brn_dn;
70128561Sphilip
71128561Sphilip	char	*lcd_get;
72128561Sphilip	char	*lcd_set;
73128561Sphilip
74128561Sphilip	char	*disp_get;
75128561Sphilip	char	*disp_set;
76128561Sphilip};
77128561Sphilip
78133095Sphilipstruct acpi_asus_led {
79133095Sphilip	struct cdev	*cdev;
80133095Sphilip	device_t	dev;
81133095Sphilip	enum {
82133095Sphilip		ACPI_ASUS_LED_MLED,
83133095Sphilip		ACPI_ASUS_LED_TLED,
84133095Sphilip		ACPI_ASUS_LED_WLED,
85133095Sphilip	} type;
86133095Sphilip};
87133095Sphilip
88128561Sphilipstruct acpi_asus_softc {
89128561Sphilip	device_t		dev;
90128561Sphilip	ACPI_HANDLE		handle;
91128561Sphilip
92128561Sphilip	struct acpi_asus_model	*model;
93128561Sphilip	struct sysctl_ctx_list	sysctl_ctx;
94128561Sphilip	struct sysctl_oid	*sysctl_tree;
95128561Sphilip
96133095Sphilip	struct acpi_asus_led	s_mled;
97133095Sphilip	struct acpi_asus_led	s_tled;
98133095Sphilip	struct acpi_asus_led	s_wled;
99128561Sphilip
100128561Sphilip	int			s_brn;
101128561Sphilip	int			s_disp;
102128561Sphilip	int			s_lcd;
103128561Sphilip};
104128561Sphilip
105128561Sphilip/* Models we know about */
106128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = {
107128561Sphilip	{
108128561Sphilip		.name		= "L2D",
109128561Sphilip		.mled_set	= "MLED",
110128561Sphilip		.wled_set	= "WLED",
111128561Sphilip		.brn_up		= "\\Q0E",
112128561Sphilip		.brn_dn		= "\\Q0F",
113128561Sphilip		.lcd_get	= "\\SGP0",
114128561Sphilip		.lcd_set	= "\\Q10"
115128561Sphilip	},
116128561Sphilip	{
117128561Sphilip		.name		= "L3C",
118128561Sphilip		.mled_set	= "MLED",
119128561Sphilip		.wled_set	= "WLED",
120128561Sphilip		.brn_get	= "GPLV",
121128561Sphilip		.brn_set	= "SPLV",
122128561Sphilip		.lcd_get	= "\\GL32",
123128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.ECD0._Q10"
124128561Sphilip	},
125128561Sphilip	{
126128561Sphilip		.name		= "L3D",
127128561Sphilip		.mled_set	= "MLED",
128128561Sphilip		.wled_set	= "WLED",
129128561Sphilip		.brn_get	= "GPLV",
130128561Sphilip		.brn_set	= "SPLV",
131128561Sphilip		.lcd_get	= "\\BKLG",
132128561Sphilip		.lcd_set	= "\\Q10"
133128561Sphilip	},
134128561Sphilip	{
135128561Sphilip		.name		= "L3H",
136128561Sphilip		.mled_set	= "MLED",
137128561Sphilip		.wled_set	= "WLED",
138128561Sphilip		.brn_get	= "GPLV",
139128561Sphilip		.brn_set	= "SPLV",
140128561Sphilip		.lcd_get	= "\\_SB.PCI0.PM.PBC",
141128561Sphilip		.lcd_set	= "EHK",
142128561Sphilip		.disp_get	= "\\_SB.INFB",
143128561Sphilip		.disp_set	= "SDSP"
144128561Sphilip	},
145128561Sphilip	{
146128561Sphilip		.name		= "L8L"
147128561Sphilip		/* Only has hotkeys, apparantly */
148128561Sphilip	},
149128561Sphilip	{
150128561Sphilip		.name		= "M1A",
151128561Sphilip		.mled_set	= "MLED",
152128561Sphilip		.brn_up		= "\\_SB.PCI0.PX40.EC0.Q0E",
153128561Sphilip		.brn_dn		= "\\_SB.PCI0.PX40.EC0.Q0F",
154128561Sphilip		.lcd_get	= "\\PNOF",
155128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.EC0.Q10"
156128561Sphilip	},
157128561Sphilip	{
158128561Sphilip		.name		= "M2E",
159128561Sphilip		.mled_set	= "MLED",
160128561Sphilip		.wled_set	= "WLED",
161128561Sphilip		.brn_get	= "GPLV",
162128561Sphilip		.brn_set	= "SPLV",
163128561Sphilip		.lcd_get	= "\\GP06",
164128561Sphilip		.lcd_set	= "\\Q10"
165128561Sphilip	},
166128561Sphilip	{
167128561Sphilip		.name		= "P30",
168128561Sphilip		.wled_set	= "WLED",
169128561Sphilip		.brn_up		= "\\_SB.PCI0.LPCB.EC0._Q68",
170128561Sphilip		.brn_dn		= "\\_SB.PCI0.LPCB.EC0._Q69",
171128561Sphilip		.lcd_get	= "\\BKLT",
172128561Sphilip		.lcd_set	= "\\_SB.PCI0.LPCB.EC0._Q0E"
173128561Sphilip	},
174128561Sphilip
175128561Sphilip	{ .name = NULL }
176128561Sphilip};
177128561Sphilip
178133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
179133628Snjl
180128561Sphilip/* Function prototypes */
181128561Sphilipstatic int	acpi_asus_probe(device_t dev);
182128561Sphilipstatic int	acpi_asus_attach(device_t dev);
183128561Sphilipstatic int	acpi_asus_detach(device_t dev);
184128561Sphilip
185133095Sphilipstatic void	acpi_asus_led(struct acpi_asus_led *led, int state);
186128561Sphilip
187128561Sphilipstatic int	acpi_asus_sysctl_brn(SYSCTL_HANDLER_ARGS);
188128561Sphilipstatic int	acpi_asus_sysctl_lcd(SYSCTL_HANDLER_ARGS);
189128561Sphilipstatic int	acpi_asus_sysctl_disp(SYSCTL_HANDLER_ARGS);
190128561Sphilip
191128561Sphilipstatic void	acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
192128561Sphilip
193128561Sphilipstatic device_method_t acpi_asus_methods[] = {
194128561Sphilip	DEVMETHOD(device_probe,	 acpi_asus_probe),
195128561Sphilip	DEVMETHOD(device_attach, acpi_asus_attach),
196128561Sphilip	DEVMETHOD(device_detach, acpi_asus_detach),
197128561Sphilip
198128561Sphilip	{ 0, 0 }
199128561Sphilip};
200128561Sphilip
201128561Sphilipstatic driver_t acpi_asus_driver = {
202128561Sphilip	"acpi_asus",
203128561Sphilip	acpi_asus_methods,
204128561Sphilip	sizeof(struct acpi_asus_softc)
205128561Sphilip};
206128561Sphilip
207128561Sphilipstatic devclass_t acpi_asus_devclass;
208128561Sphilip
209128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
210128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
211128561Sphilip
212128561Sphilipstatic int
213128561Sphilipacpi_asus_probe(device_t dev)
214128561Sphilip{
215128561Sphilip	struct acpi_asus_model	*model;
216128561Sphilip	struct acpi_asus_softc	*sc;
217128561Sphilip	struct sbuf		*sb;
218128561Sphilip	ACPI_BUFFER		Buf;
219128561Sphilip	ACPI_OBJECT		Arg, *Obj;
220128561Sphilip	ACPI_OBJECT_LIST	Args;
221131284Snjl	static char 		*asus_ids[] = { "ATK0100", NULL };
222128561Sphilip
223128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
224128561Sphilip
225128561Sphilip	if (!acpi_disabled("asus") &&
226131284Snjl	    ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids)) {
227128561Sphilip		sc = device_get_softc(dev);
228128561Sphilip		sc->dev = dev;
229128561Sphilip		sc->handle = acpi_get_handle(dev);
230128561Sphilip
231128561Sphilip		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
232128561Sphilip
233128561Sphilip		if (sb == NULL)
234128561Sphilip			return (ENOMEM);
235128561Sphilip
236128561Sphilip		Arg.Type = ACPI_TYPE_INTEGER;
237128561Sphilip		Arg.Integer.Value = 0;
238128561Sphilip
239128561Sphilip		Args.Count = 1;
240128561Sphilip		Args.Pointer = &Arg;
241128561Sphilip
242128561Sphilip		Buf.Pointer = NULL;
243128561Sphilip		Buf.Length = ACPI_ALLOCATE_BUFFER;
244128561Sphilip
245128561Sphilip		AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
246128561Sphilip
247128561Sphilip		Obj = Buf.Pointer;
248128561Sphilip
249128561Sphilip		for (model = acpi_asus_models; model->name != NULL; model++)
250128561Sphilip			if (strcmp(Obj->String.Pointer, model->name) == 0) {
251128561Sphilip				sbuf_printf(sb, "Asus %s Laptop Extras",
252128561Sphilip						Obj->String.Pointer);
253128561Sphilip				sbuf_finish(sb);
254128561Sphilip
255128561Sphilip				sc->model = model;
256128561Sphilip				device_set_desc(dev, sbuf_data(sb));
257128561Sphilip
258128561Sphilip				sbuf_delete(sb);
259128561Sphilip				AcpiOsFree(Buf.Pointer);
260128561Sphilip				return (0);
261128561Sphilip			}
262128561Sphilip
263128561Sphilip		sbuf_printf(sb, "Unsupported Asus laptop detected: %s\n",
264128561Sphilip				Obj->String.Pointer);
265128561Sphilip		sbuf_finish(sb);
266128561Sphilip
267128561Sphilip		device_printf(dev, sbuf_data(sb));
268128561Sphilip
269128561Sphilip		sbuf_delete(sb);
270128561Sphilip		AcpiOsFree(Buf.Pointer);
271128561Sphilip	}
272128561Sphilip
273128561Sphilip	return (ENXIO);
274128561Sphilip}
275128561Sphilip
276128561Sphilipstatic int
277128561Sphilipacpi_asus_attach(device_t dev)
278128561Sphilip{
279128561Sphilip	struct acpi_asus_softc	*sc;
280128561Sphilip	struct acpi_softc	*acpi_sc;
281128561Sphilip
282128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
283128561Sphilip
284128561Sphilip	sc = device_get_softc(dev);
285128561Sphilip	acpi_sc = acpi_device_get_parent_softc(dev);
286128561Sphilip
287128561Sphilip	/* Build sysctl tree */
288128561Sphilip	sysctl_ctx_init(&sc->sysctl_ctx);
289128561Sphilip	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
290128561Sphilip	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
291128561Sphilip	    OID_AUTO, "asus", CTLFLAG_RD, 0, "");
292128561Sphilip
293128561Sphilip	/* Attach leds */
294133095Sphilip	if (sc->model->mled_set) {
295133095Sphilip		sc->s_mled.dev = dev;
296133095Sphilip		sc->s_mled.type = ACPI_ASUS_LED_MLED;
297133095Sphilip		sc->s_mled.cdev =
298133095Sphilip		    led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
299133095Sphilip	}
300128561Sphilip
301133095Sphilip	if (sc->model->tled_set) {
302133095Sphilip		sc->s_tled.dev = dev;
303133095Sphilip		sc->s_tled.type = ACPI_ASUS_LED_TLED;
304133095Sphilip		sc->s_tled.cdev =
305133095Sphilip		    led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled");
306133095Sphilip	}
307128561Sphilip
308133095Sphilip	if (sc->model->wled_set) {
309133095Sphilip		sc->s_wled.dev = dev;
310133095Sphilip		sc->s_wled.type = ACPI_ASUS_LED_WLED;
311133095Sphilip		sc->s_wled.cdev =
312133095Sphilip		    led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled");
313133095Sphilip	}
314128561Sphilip
315128561Sphilip	/* Attach brightness for GPLV/SPLV models */
316132610Snjl	if (sc->model->brn_get && ACPI_SUCCESS(acpi_GetInteger(sc->handle,
317132610Snjl	    sc->model->brn_get, &sc->s_brn)))
318128561Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
319128561Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
320128561Sphilip		    "lcd_brightness", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
321128561Sphilip		    acpi_asus_sysctl_brn, "I", "brightness of the lcd panel");
322128561Sphilip
323128561Sphilip	/* Attach brightness for other models */
324128561Sphilip	if (sc->model->brn_up &&
325132610Snjl	    ACPI_SUCCESS(AcpiEvaluateObject(sc->handle, sc->model->brn_up,
326132610Snjl	    NULL, NULL)) &&
327132610Snjl	    ACPI_SUCCESS(AcpiEvaluateObject(sc->handle, sc->model->brn_dn,
328132610Snjl	    NULL, NULL)))
329128561Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
330128561Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
331128561Sphilip		    "lcd_brightness", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
332128561Sphilip		    acpi_asus_sysctl_brn, "I", "brightness of the lcd panel");
333128561Sphilip
334128561Sphilip	/* Attach display switching */
335132610Snjl	if (sc->model->disp_get && ACPI_SUCCESS(acpi_GetInteger(sc->handle,
336132610Snjl	    sc->model->disp_get, &sc->s_disp)))
337128561Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
338128561Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
339128561Sphilip		    "video_output", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
340128561Sphilip		    acpi_asus_sysctl_disp, "I", "display output state");
341128561Sphilip
342128561Sphilip	/* Attach LCD state, easy for most models... */
343132610Snjl	if (sc->model->lcd_get && strncmp(sc->model->name, "L3H", 3) != 0 &&
344132610Snjl	    ACPI_SUCCESS(acpi_GetInteger(sc->handle, sc->model->lcd_get,
345133092Snjl	    &sc->s_lcd))) {
346128561Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
347128561Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
348128561Sphilip		    "lcd_backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
349128561Sphilip		    acpi_asus_sysctl_lcd, "I", "state of the lcd backlight");
350133092Snjl	} else if (sc->model->lcd_get) {
351128561Sphilip		ACPI_BUFFER		Buf;
352128561Sphilip		ACPI_OBJECT		Arg[2], Obj;
353128561Sphilip		ACPI_OBJECT_LIST	Args;
354128561Sphilip
355133092Snjl		/* ...a nightmare for the L3H */
356128561Sphilip		Arg[0].Type = ACPI_TYPE_INTEGER;
357128561Sphilip		Arg[0].Integer.Value = 0x02;
358128561Sphilip		Arg[1].Type = ACPI_TYPE_INTEGER;
359128561Sphilip		Arg[1].Integer.Value = 0x03;
360128561Sphilip
361128561Sphilip		Args.Count = 2;
362128561Sphilip		Args.Pointer = Arg;
363128561Sphilip
364128561Sphilip		Buf.Length = sizeof(Obj);
365128561Sphilip		Buf.Pointer = &Obj;
366128561Sphilip
367128561Sphilip		if (ACPI_SUCCESS(AcpiEvaluateObject(sc->handle,
368132610Snjl		    sc->model->lcd_get, &Args, &Buf)) &&
369128561Sphilip		    Obj.Type == ACPI_TYPE_INTEGER) {
370128561Sphilip			sc->s_lcd = Obj.Integer.Value >> 8;
371128561Sphilip
372128561Sphilip			SYSCTL_ADD_PROC(&sc->sysctl_ctx,
373128561Sphilip			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
374128561Sphilip			    "lcd_backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
375128561Sphilip			    acpi_asus_sysctl_lcd, "I",
376128561Sphilip			    "state of the lcd backlight");
377128561Sphilip		}
378128561Sphilip	}
379128561Sphilip
380128561Sphilip	/* Activate hotkeys */
381128561Sphilip	AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
382128561Sphilip
383128561Sphilip	/* Handle notifies */
384132610Snjl	AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
385132610Snjl	    acpi_asus_notify, dev);
386128561Sphilip
387128561Sphilip	return (0);
388128561Sphilip}
389128561Sphilip
390128561Sphilipstatic int
391128561Sphilipacpi_asus_detach(device_t dev)
392128561Sphilip{
393128561Sphilip	struct acpi_asus_softc	*sc;
394128561Sphilip
395128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
396128561Sphilip
397128561Sphilip	sc = device_get_softc(dev);
398128561Sphilip
399128561Sphilip	/* Turn the lights off */
400128561Sphilip	if (sc->model->mled_set)
401133095Sphilip		led_destroy(sc->s_mled.cdev);
402128561Sphilip
403128561Sphilip	if (sc->model->tled_set)
404133095Sphilip		led_destroy(sc->s_tled.cdev);
405128561Sphilip
406128561Sphilip	if (sc->model->wled_set)
407133095Sphilip		led_destroy(sc->s_wled.cdev);
408128561Sphilip
409128561Sphilip	/* Remove notify handler */
410132610Snjl	AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
411132610Snjl	    acpi_asus_notify);
412128561Sphilip
413128561Sphilip	/* Free sysctl tree */
414128561Sphilip	sysctl_ctx_free(&sc->sysctl_ctx);
415128561Sphilip
416128561Sphilip	return (0);
417128561Sphilip}
418128561Sphilip
419128561Sphilipstatic void
420133095Sphilipacpi_asus_led(struct acpi_asus_led *led, int state)
421128561Sphilip{
422128561Sphilip	struct acpi_asus_softc	*sc;
423133095Sphilip	char			*method;
424128561Sphilip
425128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
426128561Sphilip
427133095Sphilip	sc = device_get_softc(led->dev);
428128561Sphilip
429133095Sphilip	switch (led->type) {
430133095Sphilip		case ACPI_ASUS_LED_MLED:
431133095Sphilip			method = sc->model->mled_set;
432128561Sphilip
433133095Sphilip			/* Note: inverted */
434133095Sphilip			state = !state;
435133095Sphilip			break;
436133095Sphilip		case ACPI_ASUS_LED_TLED:
437133095Sphilip			method = sc->model->tled_set;
438133095Sphilip			break;
439133095Sphilip		case ACPI_ASUS_LED_WLED:
440133095Sphilip			method = sc->model->wled_set;
441133095Sphilip			break;
442133118Sphilip		default:
443133118Sphilip			printf("acpi_asus_led: invalid LED type %d\n",
444133118Sphilip			    (int)led->type);
445133118Sphilip			return;
446133095Sphilip	}
447128561Sphilip
448133095Sphilip	acpi_SetInteger(sc->handle, method, state);
449128561Sphilip}
450128561Sphilip
451128561Sphilipstatic int
452128561Sphilipacpi_asus_sysctl_brn(SYSCTL_HANDLER_ARGS)
453128561Sphilip{
454128561Sphilip	struct acpi_asus_softc	*sc;
455128561Sphilip	int			brn, err;
456128561Sphilip
457128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
458128561Sphilip
459128561Sphilip	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
460133628Snjl	ACPI_SERIAL_BEGIN(asus);
461128561Sphilip
462128561Sphilip	/* Sanity check */
463128561Sphilip	brn = sc->s_brn;
464128561Sphilip	err = sysctl_handle_int(oidp, &brn, 0, req);
465128561Sphilip
466132610Snjl	if (err != 0 || req->newptr == NULL)
467133092Snjl		goto out;
468128561Sphilip
469133092Snjl	if (brn < 0 || brn > 15) {
470133092Snjl		err = EINVAL;
471133092Snjl		goto out;
472133092Snjl	}
473128561Sphilip
474128561Sphilip	/* Keep track and update */
475128561Sphilip	sc->s_brn = brn;
476128561Sphilip
477128561Sphilip	if (sc->model->brn_set)
478133092Snjl		acpi_SetInteger(sc->handle, sc->model->brn_set, brn);
479128561Sphilip	else {
480128561Sphilip		brn -= sc->s_brn;
481128561Sphilip
482128561Sphilip		while (brn != 0) {
483132610Snjl			AcpiEvaluateObject(sc->handle, (brn > 0) ?
484128561Sphilip			    sc->model->brn_up : sc->model->brn_dn,
485128561Sphilip			    NULL, NULL);
486128561Sphilip			(brn > 0) ? brn-- : brn++;
487128561Sphilip		}
488128561Sphilip	}
489128561Sphilip
490133092Snjlout:
491133628Snjl	ACPI_SERIAL_END(asus);
492133092Snjl	return (err);
493128561Sphilip}
494128561Sphilip
495128561Sphilipstatic int
496128561Sphilipacpi_asus_sysctl_lcd(SYSCTL_HANDLER_ARGS)
497128561Sphilip{
498128561Sphilip	struct acpi_asus_softc	*sc;
499128561Sphilip	int			lcd, err;
500128561Sphilip
501128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
502128561Sphilip
503128561Sphilip	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
504133628Snjl	ACPI_SERIAL_BEGIN(asus);
505128561Sphilip
506128561Sphilip	/* Sanity check */
507128561Sphilip	lcd = sc->s_lcd;
508128561Sphilip	err = sysctl_handle_int(oidp, &lcd, 0, req);
509128561Sphilip
510132610Snjl	if (err != 0 || req->newptr == NULL)
511133092Snjl		goto out;
512128561Sphilip
513133092Snjl	if (lcd < 0 || lcd > 1) {
514133092Snjl		err = EINVAL;
515133092Snjl		goto out;
516133092Snjl	}
517128561Sphilip
518128561Sphilip	/* Keep track and update */
519128561Sphilip	sc->s_lcd = lcd;
520128561Sphilip
521128561Sphilip	/* Most models just need a lcd_set evaluated, the L3H is trickier */
522128561Sphilip	if (strncmp(sc->model->name, "L3H", 3) != 0)
523132610Snjl		AcpiEvaluateObject(sc->handle, sc->model->lcd_set, NULL, NULL);
524133092Snjl	else
525133092Snjl		acpi_SetInteger(sc->handle, sc->model->lcd_set, 0x7);
526128561Sphilip
527133092Snjlout:
528133628Snjl	ACPI_SERIAL_END(asus);
529133092Snjl	return (err);
530128561Sphilip}
531128561Sphilip
532128561Sphilipstatic int
533128561Sphilipacpi_asus_sysctl_disp(SYSCTL_HANDLER_ARGS)
534128561Sphilip{
535128561Sphilip	struct acpi_asus_softc	*sc;
536128561Sphilip	int			disp, err;
537128561Sphilip
538128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
539128561Sphilip
540128561Sphilip	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
541128561Sphilip
542128561Sphilip	/* Sanity check */
543133628Snjl	ACPI_SERIAL_BEGIN(asus);
544128561Sphilip	disp = sc->s_disp;
545128561Sphilip	err = sysctl_handle_int(oidp, &disp, 0, req);
546128561Sphilip
547132610Snjl	if (err != 0 || req->newptr == NULL)
548133092Snjl		goto out;
549128561Sphilip
550133092Snjl	if (disp < 0 || disp > 7) {
551133092Snjl		err = EINVAL;
552133092Snjl		goto out;
553133092Snjl	}
554128561Sphilip
555128561Sphilip	/* Keep track and update */
556128561Sphilip	sc->s_disp = disp;
557133092Snjl	acpi_SetInteger(sc->handle, sc->model->disp_set, disp);
558128561Sphilip
559133092Snjlout:
560133628Snjl	ACPI_SERIAL_END(asus);
561133092Snjl	return (err);
562128561Sphilip}
563128561Sphilip
564128561Sphilipstatic void
565128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
566128561Sphilip{
567128561Sphilip	struct acpi_asus_softc	*sc;
568128561Sphilip	struct acpi_softc	*acpi_sc;
569128561Sphilip
570128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
571128561Sphilip
572128561Sphilip	sc = device_get_softc((device_t)context);
573128561Sphilip	acpi_sc = acpi_device_get_parent_softc(sc->dev);
574128561Sphilip
575133628Snjl	ACPI_SERIAL_BEGIN(asus);
576128561Sphilip	if ((notify & ~0x10) <= 15) {
577132610Snjl		sc->s_brn = notify & ~0x10;
578128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
579128561Sphilip	} else if ((notify & ~0x20) <= 15) {
580132610Snjl		sc->s_brn = notify & ~0x20;
581128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
582128561Sphilip	} else if (notify == 0x33) {
583128561Sphilip		sc->s_lcd = 1;
584128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
585128561Sphilip	} else if (notify == 0x34) {
586128561Sphilip		sc->s_lcd = 0;
587128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
588128561Sphilip	} else {
589128561Sphilip		/* Notify devd(8) */
590128561Sphilip		acpi_UserNotify("ASUS", h, notify);
591128561Sphilip	}
592133628Snjl	ACPI_SERIAL_END(asus);
593128561Sphilip}
594