acpi_asus.c revision 180075
1128561Sphilip/*-
2146024Sphilip * Copyright (c) 2004, 2005 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#include <sys/cdefs.h>
28128561Sphilip__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_asus.c 180075 2008-06-28 08:36:47Z remko $");
29128561Sphilip
30128561Sphilip/*
31128561Sphilip * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
32133095Sphilip * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
33128561Sphilip * implements these features in the Linux kernel.
34128561Sphilip *
35128561Sphilip *   <http://sourceforge.net/projects/acpi4asus/>
36128561Sphilip *
37128561Sphilip * Currently should support most features, but could use some more testing.
38128561Sphilip * Particularly the display-switching stuff is a bit hairy.  If you have an
39128561Sphilip * Asus laptop which doesn't appear to be supported, or strange things happen
40128561Sphilip * when using this driver, please report to <acpi@FreeBSD.org>.
41128561Sphilip */
42128561Sphilip
43128561Sphilip#include "opt_acpi.h"
44128561Sphilip#include <sys/param.h>
45128561Sphilip#include <sys/kernel.h>
46129882Sphk#include <sys/module.h>
47128561Sphilip#include <sys/bus.h>
48128561Sphilip#include <sys/sbuf.h>
49128561Sphilip
50150003Sobrien#include <contrib/dev/acpica/acpi.h>
51128561Sphilip#include <dev/acpica/acpivar.h>
52128561Sphilip#include <dev/led/led.h>
53128561Sphilip
54143894Sphilip/* Methods */
55143894Sphilip#define ACPI_ASUS_METHOD_BRN	1
56143894Sphilip#define ACPI_ASUS_METHOD_DISP	2
57143894Sphilip#define ACPI_ASUS_METHOD_LCD	3
58180062Srpaulo#define ACPI_ASUS_METHOD_CAMERA	4
59180075Sremko#define ACPI_ASUS_METHOD_CARDRD	5
60143894Sphilip
61138825Snjl#define _COMPONENT	ACPI_OEM
62128561SphilipACPI_MODULE_NAME("ASUS")
63128561Sphilip
64128561Sphilipstruct acpi_asus_model {
65128561Sphilip	char	*name;
66128561Sphilip
67146022Sphilip	char	*bled_set;
68178069Sjkim	char	*dled_set;
69178069Sjkim	char	*gled_set;
70128561Sphilip	char	*mled_set;
71128561Sphilip	char	*tled_set;
72128561Sphilip	char	*wled_set;
73128561Sphilip
74128561Sphilip	char	*brn_get;
75128561Sphilip	char	*brn_set;
76128561Sphilip	char	*brn_up;
77128561Sphilip	char	*brn_dn;
78128561Sphilip
79128561Sphilip	char	*lcd_get;
80128561Sphilip	char	*lcd_set;
81128561Sphilip
82128561Sphilip	char	*disp_get;
83128561Sphilip	char	*disp_set;
84180062Srpaulo
85180062Srpaulo	char	*cam_get;
86180062Srpaulo	char	*cam_set;
87180062Srpaulo
88180062Srpaulo	char	*crd_get;
89180062Srpaulo	char	*crd_set;
90180062Srpaulo
91180062Srpaulo	void	(*n_func)(ACPI_HANDLE, UINT32, void *);
92128561Sphilip};
93128561Sphilip
94133095Sphilipstruct acpi_asus_led {
95144339Sphilip	struct acpi_asus_softc *sc;
96133095Sphilip	struct cdev	*cdev;
97144339Sphilip	int		busy;
98144339Sphilip	int		state;
99133095Sphilip	enum {
100146022Sphilip		ACPI_ASUS_LED_BLED,
101178069Sjkim		ACPI_ASUS_LED_DLED,
102178069Sjkim		ACPI_ASUS_LED_GLED,
103133095Sphilip		ACPI_ASUS_LED_MLED,
104133095Sphilip		ACPI_ASUS_LED_TLED,
105133095Sphilip		ACPI_ASUS_LED_WLED,
106133095Sphilip	} type;
107133095Sphilip};
108133095Sphilip
109128561Sphilipstruct acpi_asus_softc {
110128561Sphilip	device_t		dev;
111128561Sphilip	ACPI_HANDLE		handle;
112128561Sphilip
113128561Sphilip	struct acpi_asus_model	*model;
114128561Sphilip	struct sysctl_ctx_list	sysctl_ctx;
115128561Sphilip	struct sysctl_oid	*sysctl_tree;
116128561Sphilip
117146022Sphilip	struct acpi_asus_led	s_bled;
118178069Sjkim	struct acpi_asus_led	s_dled;
119178069Sjkim	struct acpi_asus_led	s_gled;
120133095Sphilip	struct acpi_asus_led	s_mled;
121133095Sphilip	struct acpi_asus_led	s_tled;
122133095Sphilip	struct acpi_asus_led	s_wled;
123128561Sphilip
124128561Sphilip	int			s_brn;
125128561Sphilip	int			s_disp;
126128561Sphilip	int			s_lcd;
127180062Srpaulo	int			s_cam;
128180062Srpaulo	int			s_crd;
129128561Sphilip};
130128561Sphilip
131137245Sphilip/*
132137245Sphilip * We can identify Asus laptops from the string they return
133137245Sphilip * as a result of calling the ATK0100 'INIT' method.
134137245Sphilip */
135128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = {
136128561Sphilip	{
137146024Sphilip		.name		= "xxN",
138146024Sphilip		.mled_set	= "MLED",
139146024Sphilip		.wled_set	= "WLED",
140146024Sphilip		.lcd_get	= "\\BKLT",
141146024Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
142146024Sphilip		.brn_get	= "GPLV",
143146024Sphilip		.brn_set	= "SPLV",
144146024Sphilip		.disp_get	= "\\ADVG",
145146024Sphilip		.disp_set	= "SDSP"
146146024Sphilip	},
147146024Sphilip	{
148146024Sphilip		.name		= "A1x",
149146024Sphilip		.mled_set	= "MLED",
150146024Sphilip		.lcd_get	= "\\BKLI",
151146024Sphilip		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
152146024Sphilip		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0E",
153146024Sphilip		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0F"
154146024Sphilip	},
155146024Sphilip	{
156146024Sphilip		.name		= "A2x",
157146024Sphilip		.mled_set	= "MLED",
158146024Sphilip		.wled_set	= "WLED",
159146024Sphilip		.lcd_get	= "\\BAOF",
160146024Sphilip		.lcd_set	= "\\Q10",
161146024Sphilip		.brn_get	= "GPLV",
162146024Sphilip		.brn_set	= "SPLV",
163146024Sphilip		.disp_get	= "\\INFB",
164146024Sphilip		.disp_set	= "SDSP"
165146024Sphilip	},
166146024Sphilip	{
167170216Sphilip		.name           = "A3N",
168170216Sphilip		.mled_set       = "MLED",
169170216Sphilip		.bled_set       = "BLED",
170170216Sphilip		.wled_set       = "WLED",
171170216Sphilip		.lcd_get        = NULL,
172170216Sphilip		.lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
173170216Sphilip		.brn_set        = "SPLV",
174170216Sphilip		.brn_get        = "SDSP",
175170216Sphilip		.disp_set       = "SDSP",
176170216Sphilip		.disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD"
177170216Sphilip	},
178170216Sphilip	{
179155022Sphilip		.name		= "A4D",
180155022Sphilip		.mled_set	= "MLED",
181155022Sphilip		.brn_up		= "\\_SB_.PCI0.SBRG.EC0._Q0E",
182155022Sphilip		.brn_dn		= "\\_SB_.PCI0.SBRG.EC0._Q0F",
183155022Sphilip		.brn_get	= "GPLV",
184155022Sphilip		.brn_set	= "SPLV",
185155022Sphilip#ifdef notyet
186155022Sphilip		.disp_get	= "\\_SB_.PCI0.SBRG.EC0._Q10",
187155022Sphilip		.disp_set	= "\\_SB_.PCI0.SBRG.EC0._Q11"
188155022Sphilip#endif
189155022Sphilip	},
190155022Sphilip	{
191155021Sphilip		.name		= "A6V",
192155021Sphilip		.bled_set	= "BLED",
193155021Sphilip		.mled_set	= "MLED",
194155021Sphilip		.wled_set	= "WLED",
195155021Sphilip		.lcd_get	= NULL,
196155021Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
197155021Sphilip		.brn_get	= "GPLV",
198155021Sphilip		.brn_set	= "SPLV",
199155021Sphilip		.disp_get	= "\\_SB.PCI0.P0P3.VGA.GETD",
200155021Sphilip		.disp_set	= "SDSP"
201155021Sphilip	},
202155021Sphilip	{
203146024Sphilip		.name		= "D1x",
204146024Sphilip		.mled_set	= "MLED",
205146024Sphilip		.lcd_get	= "\\GP11",
206146024Sphilip		.lcd_set	= "\\Q0D",
207146024Sphilip		.brn_up		= "\\Q0C",
208146024Sphilip		.brn_dn		= "\\Q0B",
209146024Sphilip		.disp_get	= "\\INFB",
210146024Sphilip		.disp_set	= "SDSP"
211146024Sphilip	},
212146024Sphilip	{
213178069Sjkim		.name		= "G2K",
214178069Sjkim		.bled_set	= "BLED",
215178069Sjkim		.dled_set	= "DLED",
216178069Sjkim		.gled_set	= "GLED",
217178069Sjkim		.mled_set	= "MLED",
218178069Sjkim		.tled_set	= "TLED",
219178069Sjkim		.wled_set	= "WLED",
220178069Sjkim		.brn_get	= "GPLV",
221178069Sjkim		.brn_set	= "SPLV",
222178069Sjkim		.lcd_get	= "\\_SB.PCI0.SBRG.EC0.RPIN",
223178069Sjkim		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
224178069Sjkim		.disp_get	= "\\_SB.PCI0.PCE2.VGA.GETD",
225178069Sjkim		.disp_set	= "SDSP",
226178069Sjkim	},
227178069Sjkim	{
228128561Sphilip		.name		= "L2D",
229128561Sphilip		.mled_set	= "MLED",
230128561Sphilip		.wled_set	= "WLED",
231128561Sphilip		.brn_up		= "\\Q0E",
232128561Sphilip		.brn_dn		= "\\Q0F",
233128561Sphilip		.lcd_get	= "\\SGP0",
234128561Sphilip		.lcd_set	= "\\Q10"
235128561Sphilip	},
236128561Sphilip	{
237128561Sphilip		.name		= "L3C",
238128561Sphilip		.mled_set	= "MLED",
239128561Sphilip		.wled_set	= "WLED",
240128561Sphilip		.brn_get	= "GPLV",
241128561Sphilip		.brn_set	= "SPLV",
242128561Sphilip		.lcd_get	= "\\GL32",
243128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.ECD0._Q10"
244128561Sphilip	},
245128561Sphilip	{
246128561Sphilip		.name		= "L3D",
247128561Sphilip		.mled_set	= "MLED",
248128561Sphilip		.wled_set	= "WLED",
249128561Sphilip		.brn_get	= "GPLV",
250128561Sphilip		.brn_set	= "SPLV",
251128561Sphilip		.lcd_get	= "\\BKLG",
252128561Sphilip		.lcd_set	= "\\Q10"
253128561Sphilip	},
254128561Sphilip	{
255128561Sphilip		.name		= "L3H",
256128561Sphilip		.mled_set	= "MLED",
257128561Sphilip		.wled_set	= "WLED",
258128561Sphilip		.brn_get	= "GPLV",
259128561Sphilip		.brn_set	= "SPLV",
260128561Sphilip		.lcd_get	= "\\_SB.PCI0.PM.PBC",
261128561Sphilip		.lcd_set	= "EHK",
262128561Sphilip		.disp_get	= "\\_SB.INFB",
263128561Sphilip		.disp_set	= "SDSP"
264128561Sphilip	},
265128561Sphilip	{
266137388Sphilip		.name		= "L4R",
267137388Sphilip		.mled_set	= "MLED",
268137388Sphilip		.wled_set	= "WLED",
269137388Sphilip		.brn_get	= "GPLV",
270137388Sphilip		.brn_set	= "SPLV",
271137388Sphilip		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
272137388Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
273137388Sphilip		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
274137388Sphilip		.disp_set	= "SDSP"
275137388Sphilip	},
276137388Sphilip	{
277146024Sphilip		.name		= "L5x",
278146024Sphilip		.mled_set	= "MLED",
279146024Sphilip		.tled_set	= "TLED",
280146024Sphilip		.lcd_get	= "\\BAOF",
281146024Sphilip		.lcd_set	= "\\Q0D",
282146024Sphilip		.brn_get	= "GPLV",
283146024Sphilip		.brn_set	= "SPLV",
284146024Sphilip		.disp_get	= "\\INFB",
285146024Sphilip		.disp_set	= "SDSP"
286146024Sphilip	},
287146024Sphilip	{
288128561Sphilip		.name		= "L8L"
289128561Sphilip		/* Only has hotkeys, apparantly */
290128561Sphilip	},
291128561Sphilip	{
292128561Sphilip		.name		= "M1A",
293128561Sphilip		.mled_set	= "MLED",
294128561Sphilip		.brn_up		= "\\_SB.PCI0.PX40.EC0.Q0E",
295128561Sphilip		.brn_dn		= "\\_SB.PCI0.PX40.EC0.Q0F",
296128561Sphilip		.lcd_get	= "\\PNOF",
297128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.EC0.Q10"
298128561Sphilip	},
299128561Sphilip	{
300128561Sphilip		.name		= "M2E",
301128561Sphilip		.mled_set	= "MLED",
302128561Sphilip		.wled_set	= "WLED",
303128561Sphilip		.brn_get	= "GPLV",
304128561Sphilip		.brn_set	= "SPLV",
305128561Sphilip		.lcd_get	= "\\GP06",
306128561Sphilip		.lcd_set	= "\\Q10"
307128561Sphilip	},
308128561Sphilip	{
309137127Sphilip		.name		= "M6N",
310137127Sphilip		.mled_set	= "MLED",
311137127Sphilip		.wled_set	= "WLED",
312137127Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
313137127Sphilip		.lcd_get	= "\\_SB.BKLT",
314137127Sphilip		.brn_set	= "SPLV",
315137127Sphilip		.brn_get	= "GPLV",
316137127Sphilip		.disp_set	= "SDSP",
317137127Sphilip		.disp_get	= "\\SSTE"
318137127Sphilip	},
319137388Sphilip	{
320137388Sphilip		.name		= "M6R",
321137388Sphilip		.mled_set	= "MLED",
322137388Sphilip		.wled_set	= "WLED",
323137388Sphilip		.brn_get	= "GPLV",
324137388Sphilip		.brn_set	= "SPLV",
325137388Sphilip		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
326137388Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
327137388Sphilip		.disp_get	= "\\SSTE",
328137388Sphilip		.disp_set	= "SDSP"
329137388Sphilip	},
330146022Sphilip	{
331146024Sphilip		.name		= "S1x",
332146024Sphilip		.mled_set	= "MLED",
333146022Sphilip		.wled_set	= "WLED",
334146024Sphilip		.lcd_get	= "\\PNOF",
335146024Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.Q10",
336146022Sphilip		.brn_get	= "GPLV",
337146024Sphilip		.brn_set	= "SPLV"
338146022Sphilip	},
339146022Sphilip	{
340146024Sphilip		.name		= "S2x",
341146022Sphilip		.mled_set	= "MLED",
342146024Sphilip		.lcd_get	= "\\BKLI",
343146024Sphilip		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
344146024Sphilip		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0B",
345146024Sphilip		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0A"
346146024Sphilip	},
347146024Sphilip	{
348146024Sphilip		.name		= "V6V",
349146024Sphilip		.bled_set	= "BLED",
350146024Sphilip		.tled_set	= "TLED",
351146022Sphilip		.wled_set	= "WLED",
352146022Sphilip		.lcd_get	= "\\BKLT",
353146022Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
354146022Sphilip		.brn_get	= "GPLV",
355146022Sphilip		.brn_set	= "SPLV",
356146024Sphilip		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
357146022Sphilip		.disp_set	= "SDSP"
358146022Sphilip	},
359157605Sphilip	{
360157605Sphilip		.name		= "W5A",
361157605Sphilip		.bled_set	= "BLED",
362157605Sphilip		.lcd_get	= "\\BKLT",
363157605Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
364157605Sphilip		.brn_get	= "GPLV",
365157605Sphilip		.brn_set	= "SPLV",
366157605Sphilip		.disp_get	= "\\_SB.PCI0.P0P2.VGA.GETD",
367157605Sphilip		.disp_set	= "SDSP"
368157605Sphilip	},
369137245Sphilip
370137245Sphilip	{ .name = NULL }
371137245Sphilip};
372137245Sphilip
373137245Sphilip/*
374137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
375137245Sphilip * but they can't be probed quite the same way as Asus laptops.
376137245Sphilip */
377137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = {
378137127Sphilip	{
379128561Sphilip		.name		= "P30",
380128561Sphilip		.wled_set	= "WLED",
381128561Sphilip		.brn_up		= "\\_SB.PCI0.LPCB.EC0._Q68",
382128561Sphilip		.brn_dn		= "\\_SB.PCI0.LPCB.EC0._Q69",
383128561Sphilip		.lcd_get	= "\\BKLT",
384128561Sphilip		.lcd_set	= "\\_SB.PCI0.LPCB.EC0._Q0E"
385128561Sphilip	},
386128561Sphilip
387128561Sphilip	{ .name = NULL }
388128561Sphilip};
389128561Sphilip
390180062Srpaulostatic void	acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
391180062Srpaulo
392178178Srpaulo/*
393178178Srpaulo * EeePC have an Asus ASUS010 gadget interface,
394178178Srpaulo * but they can't be probed quite the same way as Asus laptops.
395178178Srpaulo */
396178178Srpaulostatic struct acpi_asus_model acpi_eeepc_models[] = {
397178178Srpaulo	{
398178178Srpaulo		.name		= "EEE",
399178178Srpaulo		.brn_get	= "\\_SB.ATKD.PBLG",
400180062Srpaulo		.brn_set	= "\\_SB.ATKD.PBLS",
401180062Srpaulo		.cam_get	= "\\_SB.ATKD.CAMG",
402180062Srpaulo		.cam_set	= "\\_SB.ATKD.CAMS",
403180062Srpaulo		.crd_set	= "\\_SB.ATKD.CRDS",
404180062Srpaulo		.crd_get	= "\\_SB.ATKD.CRDG",
405180062Srpaulo		.n_func		= acpi_asus_eeepc_notify
406178178Srpaulo	},
407178178Srpaulo
408178178Srpaulo	{ .name = NULL }
409178178Srpaulo};
410178178Srpaulo
411143894Sphilipstatic struct {
412143894Sphilip	char	*name;
413143894Sphilip	char	*description;
414143894Sphilip	int	method;
415180062Srpaulo	int	flags;
416143894Sphilip} acpi_asus_sysctls[] = {
417143894Sphilip	{
418143894Sphilip		.name		= "lcd_backlight",
419143894Sphilip		.method		= ACPI_ASUS_METHOD_LCD,
420180062Srpaulo		.description	= "state of the lcd backlight",
421180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
422143894Sphilip	},
423143894Sphilip	{
424143894Sphilip		.name		= "lcd_brightness",
425143894Sphilip		.method		= ACPI_ASUS_METHOD_BRN,
426180062Srpaulo		.description	= "brightness of the lcd panel",
427180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
428143894Sphilip	},
429143894Sphilip	{
430143894Sphilip		.name		= "video_output",
431143894Sphilip		.method		= ACPI_ASUS_METHOD_DISP,
432180062Srpaulo		.description	= "display output state",
433180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
434143894Sphilip	},
435180062Srpaulo	{
436180062Srpaulo		.name		= "camera",
437180062Srpaulo		.method		= ACPI_ASUS_METHOD_CAMERA,
438180062Srpaulo		.description	= "internal camera state",
439180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
440180062Srpaulo	},
441180062Srpaulo	{
442180062Srpaulo		.name		= "cardreader",
443180062Srpaulo		.method		= ACPI_ASUS_METHOD_CARDRD,
444180062Srpaulo		.description	= "internal card reader state",
445180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
446180062Srpaulo	},
447143894Sphilip
448143894Sphilip	{ .name = NULL }
449143894Sphilip};
450143894Sphilip
451133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
452133628Snjl
453128561Sphilip/* Function prototypes */
454128561Sphilipstatic int	acpi_asus_probe(device_t dev);
455128561Sphilipstatic int	acpi_asus_attach(device_t dev);
456128561Sphilipstatic int	acpi_asus_detach(device_t dev);
457128561Sphilip
458133095Sphilipstatic void	acpi_asus_led(struct acpi_asus_led *led, int state);
459144339Sphilipstatic void	acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
460128561Sphilip
461143894Sphilipstatic int	acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
462143894Sphilipstatic int	acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
463143894Sphilipstatic int	acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
464143894Sphilipstatic int	acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
465128561Sphilip
466128561Sphilipstatic void	acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
467128561Sphilip
468128561Sphilipstatic device_method_t acpi_asus_methods[] = {
469137631Sphilip	DEVMETHOD(device_probe,  acpi_asus_probe),
470128561Sphilip	DEVMETHOD(device_attach, acpi_asus_attach),
471128561Sphilip	DEVMETHOD(device_detach, acpi_asus_detach),
472128561Sphilip
473128561Sphilip	{ 0, 0 }
474128561Sphilip};
475128561Sphilip
476128561Sphilipstatic driver_t acpi_asus_driver = {
477128561Sphilip	"acpi_asus",
478128561Sphilip	acpi_asus_methods,
479128561Sphilip	sizeof(struct acpi_asus_softc)
480128561Sphilip};
481128561Sphilip
482128561Sphilipstatic devclass_t acpi_asus_devclass;
483128561Sphilip
484128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
485128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
486128561Sphilip
487128561Sphilipstatic int
488128561Sphilipacpi_asus_probe(device_t dev)
489128561Sphilip{
490128561Sphilip	struct acpi_asus_model	*model;
491128561Sphilip	struct acpi_asus_softc	*sc;
492128561Sphilip	struct sbuf		*sb;
493128561Sphilip	ACPI_BUFFER		Buf;
494128561Sphilip	ACPI_OBJECT		Arg, *Obj;
495128561Sphilip	ACPI_OBJECT_LIST	Args;
496178178Srpaulo	static char		*asus_ids[] = { "ATK0100", "ASUS010", NULL };
497178178Srpaulo	char *rstr;
498128561Sphilip
499128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
500128561Sphilip
501178178Srpaulo	if (acpi_disabled("asus"))
502137632Sphilip		return (ENXIO);
503178178Srpaulo	rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
504178178Srpaulo	if (rstr == NULL) {
505178178Srpaulo		return (ENXIO);
506178178Srpaulo	}
507137631Sphilip
508137632Sphilip	sc = device_get_softc(dev);
509137632Sphilip	sc->dev = dev;
510137632Sphilip	sc->handle = acpi_get_handle(dev);
511128561Sphilip
512137632Sphilip	Arg.Type = ACPI_TYPE_INTEGER;
513137632Sphilip	Arg.Integer.Value = 0;
514128561Sphilip
515137632Sphilip	Args.Count = 1;
516137632Sphilip	Args.Pointer = &Arg;
517137631Sphilip
518137632Sphilip	Buf.Pointer = NULL;
519137632Sphilip	Buf.Length = ACPI_ALLOCATE_BUFFER;
520128561Sphilip
521137632Sphilip	AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
522137632Sphilip	Obj = Buf.Pointer;
523137245Sphilip
524137632Sphilip	/*
525137632Sphilip	 * The Samsung P30 returns a null-pointer from INIT, we
526137632Sphilip	 * can identify it from the 'ODEM' string in the DSDT.
527137632Sphilip	 */
528137632Sphilip	if (Obj->String.Pointer == NULL) {
529137632Sphilip		ACPI_STATUS		status;
530137632Sphilip		ACPI_TABLE_HEADER	th;
531137245Sphilip
532167814Sjkim		status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
533137632Sphilip		if (ACPI_FAILURE(status)) {
534137632Sphilip			device_printf(dev, "Unsupported (Samsung?) laptop\n");
535137632Sphilip			AcpiOsFree(Buf.Pointer);
536137632Sphilip			return (ENXIO);
537137245Sphilip		}
538137245Sphilip
539137632Sphilip		if (strncmp("ODEM", th.OemTableId, 4) == 0) {
540137632Sphilip			sc->model = &acpi_samsung_models[0];
541137632Sphilip			device_set_desc(dev, "Samsung P30 Laptop Extras");
542137632Sphilip			AcpiOsFree(Buf.Pointer);
543137632Sphilip			return (0);
544137632Sphilip		}
545178178Srpaulo
546178178Srpaulo		/* if EeePC */
547178231Srpaulo		if (strncmp("ASUS010", rstr, 7) == 0) {
548178178Srpaulo			sc->model = &acpi_eeepc_models[0];
549178178Srpaulo			device_set_desc(dev, "ASUS EeePC");
550178178Srpaulo			AcpiOsFree(Buf.Pointer);
551178178Srpaulo			return (0);
552178178Srpaulo		}
553137632Sphilip	}
554137245Sphilip
555137632Sphilip	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
556137632Sphilip	if (sb == NULL)
557137632Sphilip		return (ENOMEM);
558128561Sphilip
559137632Sphilip	/*
560137632Sphilip	 * Asus laptops are simply identified by name, easy!
561137632Sphilip	 */
562146024Sphilip	for (model = acpi_asus_models; model->name != NULL; model++) {
563137632Sphilip		if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
564146024Sphilip
565146024Sphilipgood:
566146024Sphilip			sbuf_printf(sb, "Asus %s Laptop Extras",
567146024Sphilip			    Obj->String.Pointer);
568137632Sphilip			sbuf_finish(sb);
569128561Sphilip
570137632Sphilip			sc->model = model;
571144076Spjd			device_set_desc_copy(dev, sbuf_data(sb));
572128561Sphilip
573137632Sphilip			sbuf_delete(sb);
574137632Sphilip			AcpiOsFree(Buf.Pointer);
575137632Sphilip			return (0);
576137632Sphilip		}
577146024Sphilip
578146024Sphilip		/*
579146024Sphilip		 * Some models look exactly the same as other models, but have
580146024Sphilip		 * their own ids.  If we spot these, set them up with the same
581146024Sphilip		 * details as the models they're like, possibly dealing with
582146024Sphilip		 * small differences.
583146024Sphilip		 *
584146024Sphilip		 * XXX: there must be a prettier way to do this!
585146024Sphilip		 */
586146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
587146024Sphilip		    (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
588146024Sphilip		     strncmp(Obj->String.Pointer, "S1N", 3) == 0))
589146024Sphilip			goto good;
590146024Sphilip		else if (strncmp(model->name, "A1x", 3) == 0 &&
591146024Sphilip		    strncmp(Obj->String.Pointer, "A1", 2) == 0)
592146024Sphilip			goto good;
593146024Sphilip		else if (strncmp(model->name, "A2x", 3) == 0 &&
594146024Sphilip		    strncmp(Obj->String.Pointer, "A2", 2) == 0)
595146024Sphilip			goto good;
596146024Sphilip		else if (strncmp(model->name, "D1x", 3) == 0 &&
597146024Sphilip		    strncmp(Obj->String.Pointer, "D1", 2) == 0)
598146024Sphilip			goto good;
599146024Sphilip		else if (strncmp(model->name, "L3H", 3) == 0 &&
600146024Sphilip		    strncmp(Obj->String.Pointer, "L2E", 3) == 0)
601146024Sphilip			goto good;
602146024Sphilip		else if (strncmp(model->name, "L5x", 3) == 0 &&
603146024Sphilip		    strncmp(Obj->String.Pointer, "L5", 2) == 0)
604146024Sphilip			goto good;
605146024Sphilip		else if (strncmp(model->name, "M2E", 3) == 0 &&
606146024Sphilip		    (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
607146024Sphilip		     strncmp(Obj->String.Pointer, "L4E", 3) == 0))
608146024Sphilip			goto good;
609146024Sphilip		else if (strncmp(model->name, "S1x", 3) == 0 &&
610146024Sphilip		    (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
611146024Sphilip		     strncmp(Obj->String.Pointer, "S1", 2) == 0))
612146024Sphilip			goto good;
613146024Sphilip		else if (strncmp(model->name, "S2x", 3) == 0 &&
614146024Sphilip		    (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
615146024Sphilip		     strncmp(Obj->String.Pointer, "S2", 2) == 0))
616146024Sphilip			goto good;
617128561Sphilip
618146024Sphilip		/* L2B is like L3C but has no lcd_get method */
619146024Sphilip		else if (strncmp(model->name, "L3C", 3) == 0 &&
620146024Sphilip		    strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
621146024Sphilip			model->lcd_get = NULL;
622146024Sphilip			goto good;
623146024Sphilip		}
624146024Sphilip
625146024Sphilip		/* A3G is like M6R but with a different lcd_get method */
626146024Sphilip		else if (strncmp(model->name, "M6R", 3) == 0 &&
627146024Sphilip		    strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
628146024Sphilip			model->lcd_get = "\\BLFG";
629146024Sphilip			goto good;
630146024Sphilip		}
631146024Sphilip
632146024Sphilip		/* M2N and W1N are like xxN with added WLED */
633146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
634146024Sphilip		    (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
635146024Sphilip		     strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
636146024Sphilip			model->wled_set = "WLED";
637146024Sphilip			goto good;
638146024Sphilip		}
639146024Sphilip
640146024Sphilip		/* M5N and S5N are like xxN without MLED */
641146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
642146024Sphilip		    (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
643146024Sphilip		     strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
644146024Sphilip			model->mled_set = NULL;
645146024Sphilip			goto good;
646146024Sphilip		}
647146024Sphilip	}
648146024Sphilip
649137632Sphilip	sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
650137632Sphilip	sbuf_finish(sb);
651128561Sphilip
652137632Sphilip	device_printf(dev, sbuf_data(sb));
653137632Sphilip
654137632Sphilip	sbuf_delete(sb);
655137632Sphilip	AcpiOsFree(Buf.Pointer);
656137632Sphilip
657128561Sphilip	return (ENXIO);
658128561Sphilip}
659128561Sphilip
660128561Sphilipstatic int
661128561Sphilipacpi_asus_attach(device_t dev)
662128561Sphilip{
663128561Sphilip	struct acpi_asus_softc	*sc;
664128561Sphilip	struct acpi_softc	*acpi_sc;
665128561Sphilip
666128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
667128561Sphilip
668128561Sphilip	sc = device_get_softc(dev);
669128561Sphilip	acpi_sc = acpi_device_get_parent_softc(dev);
670128561Sphilip
671128561Sphilip	/* Build sysctl tree */
672128561Sphilip	sysctl_ctx_init(&sc->sysctl_ctx);
673128561Sphilip	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
674128561Sphilip	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
675128561Sphilip	    OID_AUTO, "asus", CTLFLAG_RD, 0, "");
676128561Sphilip
677143894Sphilip	/* Hook up nodes */
678143894Sphilip	for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
679143894Sphilip		if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
680143894Sphilip			continue;
681143894Sphilip
682143894Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
683143894Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
684143894Sphilip		    acpi_asus_sysctls[i].name,
685180062Srpaulo		    acpi_asus_sysctls[i].flags,
686143894Sphilip		    sc, i, acpi_asus_sysctl, "I",
687143894Sphilip		    acpi_asus_sysctls[i].description);
688143894Sphilip	}
689143894Sphilip
690128561Sphilip	/* Attach leds */
691146022Sphilip	if (sc->model->bled_set) {
692146022Sphilip		sc->s_bled.busy = 0;
693146022Sphilip		sc->s_bled.sc = sc;
694146022Sphilip		sc->s_bled.type = ACPI_ASUS_LED_BLED;
695146022Sphilip		sc->s_bled.cdev =
696178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
697178069Sjkim			"bled", 1);
698146022Sphilip	}
699146022Sphilip
700178069Sjkim	if (sc->model->dled_set) {
701178069Sjkim		sc->s_dled.busy = 0;
702178069Sjkim		sc->s_dled.sc = sc;
703178069Sjkim		sc->s_dled.type = ACPI_ASUS_LED_DLED;
704178069Sjkim		sc->s_dled.cdev =
705178069Sjkim		    led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
706178069Sjkim	}
707178069Sjkim
708178069Sjkim	if (sc->model->gled_set) {
709178069Sjkim		sc->s_gled.busy = 0;
710178069Sjkim		sc->s_gled.sc = sc;
711178069Sjkim		sc->s_gled.type = ACPI_ASUS_LED_GLED;
712178069Sjkim		sc->s_gled.cdev =
713178069Sjkim		    led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
714178069Sjkim	}
715178069Sjkim
716133095Sphilip	if (sc->model->mled_set) {
717144339Sphilip		sc->s_mled.busy = 0;
718144339Sphilip		sc->s_mled.sc = sc;
719133095Sphilip		sc->s_mled.type = ACPI_ASUS_LED_MLED;
720133095Sphilip		sc->s_mled.cdev =
721133095Sphilip		    led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
722133095Sphilip	}
723128561Sphilip
724133095Sphilip	if (sc->model->tled_set) {
725144339Sphilip		sc->s_tled.busy = 0;
726144339Sphilip		sc->s_tled.sc = sc;
727133095Sphilip		sc->s_tled.type = ACPI_ASUS_LED_TLED;
728133095Sphilip		sc->s_tled.cdev =
729178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
730178069Sjkim			"tled", 1);
731133095Sphilip	}
732128561Sphilip
733133095Sphilip	if (sc->model->wled_set) {
734144339Sphilip		sc->s_wled.busy = 0;
735144339Sphilip		sc->s_wled.sc = sc;
736133095Sphilip		sc->s_wled.type = ACPI_ASUS_LED_WLED;
737133095Sphilip		sc->s_wled.cdev =
738178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
739178069Sjkim			"wled", 1);
740133095Sphilip	}
741128561Sphilip
742128561Sphilip	/* Activate hotkeys */
743128561Sphilip	AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
744128561Sphilip
745128561Sphilip	/* Handle notifies */
746180062Srpaulo	if (sc->model->n_func == NULL)
747180062Srpaulo		sc->model->n_func = acpi_asus_notify;
748180062Srpaulo
749132610Snjl	AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
750180062Srpaulo	    sc->model->n_func, dev);
751137631Sphilip
752128561Sphilip	return (0);
753128561Sphilip}
754128561Sphilip
755128561Sphilipstatic int
756128561Sphilipacpi_asus_detach(device_t dev)
757128561Sphilip{
758128561Sphilip	struct acpi_asus_softc	*sc;
759137631Sphilip
760128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
761128561Sphilip
762128561Sphilip	sc = device_get_softc(dev);
763128561Sphilip
764128561Sphilip	/* Turn the lights off */
765146022Sphilip	if (sc->model->bled_set)
766146022Sphilip		led_destroy(sc->s_bled.cdev);
767146022Sphilip
768178069Sjkim	if (sc->model->dled_set)
769178069Sjkim		led_destroy(sc->s_dled.cdev);
770178069Sjkim
771178069Sjkim	if (sc->model->gled_set)
772178069Sjkim		led_destroy(sc->s_gled.cdev);
773178069Sjkim
774128561Sphilip	if (sc->model->mled_set)
775133095Sphilip		led_destroy(sc->s_mled.cdev);
776128561Sphilip
777128561Sphilip	if (sc->model->tled_set)
778133095Sphilip		led_destroy(sc->s_tled.cdev);
779128561Sphilip
780128561Sphilip	if (sc->model->wled_set)
781133095Sphilip		led_destroy(sc->s_wled.cdev);
782128561Sphilip
783128561Sphilip	/* Remove notify handler */
784132610Snjl	AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
785132610Snjl	    acpi_asus_notify);
786128561Sphilip
787128561Sphilip	/* Free sysctl tree */
788128561Sphilip	sysctl_ctx_free(&sc->sysctl_ctx);
789128561Sphilip
790128561Sphilip	return (0);
791128561Sphilip}
792128561Sphilip
793128561Sphilipstatic void
794144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
795128561Sphilip{
796128561Sphilip	struct acpi_asus_softc	*sc;
797133095Sphilip	char			*method;
798144339Sphilip	int			state;
799144339Sphilip
800128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
801128561Sphilip
802144339Sphilip	sc = led->sc;
803128561Sphilip
804133095Sphilip	switch (led->type) {
805146022Sphilip	case ACPI_ASUS_LED_BLED:
806146022Sphilip		method = sc->model->bled_set;
807146022Sphilip		state = led->state;
808146022Sphilip		break;
809178069Sjkim	case ACPI_ASUS_LED_DLED:
810178069Sjkim		method = sc->model->dled_set;
811178069Sjkim		state = led->state;
812178069Sjkim		break;
813178069Sjkim	case ACPI_ASUS_LED_GLED:
814178069Sjkim		method = sc->model->gled_set;
815178069Sjkim		state = led->state + 1;	/* 1: off, 2: on */
816178069Sjkim		break;
817143894Sphilip	case ACPI_ASUS_LED_MLED:
818143894Sphilip		method = sc->model->mled_set;
819178069Sjkim		state = !led->state;	/* inverted */
820143894Sphilip		break;
821143894Sphilip	case ACPI_ASUS_LED_TLED:
822143894Sphilip		method = sc->model->tled_set;
823144339Sphilip		state = led->state;
824143894Sphilip		break;
825143894Sphilip	case ACPI_ASUS_LED_WLED:
826143894Sphilip		method = sc->model->wled_set;
827144339Sphilip		state = led->state;
828143894Sphilip		break;
829143894Sphilip	default:
830143894Sphilip		printf("acpi_asus_led: invalid LED type %d\n",
831143894Sphilip		    (int)led->type);
832143894Sphilip		return;
833133095Sphilip	}
834128561Sphilip
835133095Sphilip	acpi_SetInteger(sc->handle, method, state);
836144339Sphilip	led->busy = 0;
837128561Sphilip}
838144339Sphilip
839144339Sphilipstatic void
840144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state)
841144339Sphilip{
842128561Sphilip
843144339Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
844144339Sphilip
845144339Sphilip	if (led->busy)
846144339Sphilip		return;
847144339Sphilip
848144339Sphilip	led->busy = 1;
849144339Sphilip	led->state = state;
850144339Sphilip
851167814Sjkim	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
852144339Sphilip}
853144339Sphilip
854128561Sphilipstatic int
855143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
856128561Sphilip{
857128561Sphilip	struct acpi_asus_softc	*sc;
858143894Sphilip	int			arg;
859143894Sphilip	int			error = 0;
860143894Sphilip	int			function;
861143894Sphilip	int			method;
862143894Sphilip
863128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
864128561Sphilip
865128561Sphilip	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
866143894Sphilip	function = oidp->oid_arg2;
867143894Sphilip	method = acpi_asus_sysctls[function].method;
868143894Sphilip
869133628Snjl	ACPI_SERIAL_BEGIN(asus);
870143894Sphilip	arg = acpi_asus_sysctl_get(sc, method);
871143894Sphilip	error = sysctl_handle_int(oidp, &arg, 0, req);
872128561Sphilip
873128561Sphilip	/* Sanity check */
874143894Sphilip	if (error != 0 || req->newptr == NULL)
875133092Snjl		goto out;
876128561Sphilip
877143894Sphilip	/* Update */
878143894Sphilip	error = acpi_asus_sysctl_set(sc, method, arg);
879128561Sphilip
880143894Sphilipout:
881143894Sphilip	ACPI_SERIAL_END(asus);
882143894Sphilip	return (error);
883143894Sphilip}
884128561Sphilip
885143894Sphilipstatic int
886143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
887143894Sphilip{
888143894Sphilip	int val = 0;
889128561Sphilip
890143894Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
891143894Sphilip	ACPI_SERIAL_ASSERT(asus);
892143894Sphilip
893143894Sphilip	switch (method) {
894143894Sphilip	case ACPI_ASUS_METHOD_BRN:
895143894Sphilip		val = sc->s_brn;
896143894Sphilip		break;
897143894Sphilip	case ACPI_ASUS_METHOD_DISP:
898143894Sphilip		val = sc->s_disp;
899143894Sphilip		break;
900143894Sphilip	case ACPI_ASUS_METHOD_LCD:
901143894Sphilip		val = sc->s_lcd;
902143894Sphilip		break;
903180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
904180062Srpaulo		val = sc->s_cam;
905180062Srpaulo		break;
906180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
907180062Srpaulo		val = sc->s_crd;
908180062Srpaulo		break;
909128561Sphilip	}
910128561Sphilip
911143894Sphilip	return (val);
912128561Sphilip}
913128561Sphilip
914128561Sphilipstatic int
915143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
916128561Sphilip{
917143937Sphilip	ACPI_STATUS	status = AE_OK;
918128561Sphilip
919128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
920143894Sphilip	ACPI_SERIAL_ASSERT(asus);
921128561Sphilip
922143894Sphilip	switch (method) {
923143894Sphilip	case ACPI_ASUS_METHOD_BRN:
924143894Sphilip		if (arg < 0 || arg > 15)
925143894Sphilip			return (EINVAL);
926128561Sphilip
927143894Sphilip		if (sc->model->brn_set)
928143894Sphilip			status = acpi_SetInteger(sc->handle,
929143894Sphilip			    sc->model->brn_set, arg);
930143894Sphilip		else {
931143894Sphilip			while (arg != 0) {
932143894Sphilip				status = AcpiEvaluateObject(sc->handle,
933143894Sphilip				    (arg > 0) ?  sc->model->brn_up :
934143894Sphilip				    sc->model->brn_dn, NULL, NULL);
935143894Sphilip				(arg > 0) ? arg-- : arg++;
936143894Sphilip			}
937143894Sphilip		}
938128561Sphilip
939143894Sphilip		if (ACPI_SUCCESS(status))
940143894Sphilip			sc->s_brn = arg;
941128561Sphilip
942143894Sphilip		break;
943143894Sphilip	case ACPI_ASUS_METHOD_DISP:
944143894Sphilip		if (arg < 0 || arg > 7)
945143894Sphilip			return (EINVAL);
946128561Sphilip
947143894Sphilip		status = acpi_SetInteger(sc->handle,
948143894Sphilip		    sc->model->disp_set, arg);
949128561Sphilip
950143894Sphilip		if (ACPI_SUCCESS(status))
951143894Sphilip			sc->s_disp = arg;
952128561Sphilip
953143894Sphilip		break;
954143894Sphilip	case ACPI_ASUS_METHOD_LCD:
955143894Sphilip		if (arg < 0 || arg > 1)
956143894Sphilip			return (EINVAL);
957143894Sphilip
958143894Sphilip		if (strncmp(sc->model->name, "L3H", 3) != 0)
959143894Sphilip			status = AcpiEvaluateObject(sc->handle,
960143894Sphilip			    sc->model->lcd_set, NULL, NULL);
961143894Sphilip		else
962143894Sphilip			status = acpi_SetInteger(sc->handle,
963143894Sphilip			    sc->model->lcd_set, 0x7);
964143894Sphilip
965143894Sphilip		if (ACPI_SUCCESS(status))
966143894Sphilip			sc->s_lcd = arg;
967143894Sphilip
968143894Sphilip		break;
969180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
970180062Srpaulo		if (arg < 0 || arg > 1)
971180062Srpaulo			return (EINVAL);
972180062Srpaulo
973180062Srpaulo		status = AcpiEvaluateObject(sc->handle,
974180062Srpaulo		    sc->model->cam_set, NULL, NULL);
975180062Srpaulo
976180062Srpaulo		if (ACPI_SUCCESS(status))
977180062Srpaulo			sc->s_cam = arg;
978180062Srpaulo		break;
979180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
980180062Srpaulo		if (arg < 0 || arg > 1)
981180062Srpaulo			return (EINVAL);
982180062Srpaulo
983180062Srpaulo		status = AcpiEvaluateObject(sc->handle,
984180062Srpaulo		    sc->model->crd_set, NULL, NULL);
985180062Srpaulo
986180062Srpaulo		if (ACPI_SUCCESS(status))
987180062Srpaulo			sc->s_crd = arg;
988180062Srpaulo		break;
989143894Sphilip	}
990143894Sphilip
991143894Sphilip	return (0);
992128561Sphilip}
993128561Sphilip
994128561Sphilipstatic int
995143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
996128561Sphilip{
997143894Sphilip	ACPI_STATUS	status;
998128561Sphilip
999143894Sphilip	switch (method) {
1000143894Sphilip	case ACPI_ASUS_METHOD_BRN:
1001143894Sphilip		if (sc->model->brn_get) {
1002143894Sphilip			/* GPLV/SPLV models */
1003143894Sphilip			status = acpi_GetInteger(sc->handle,
1004143894Sphilip			    sc->model->brn_get, &sc->s_brn);
1005143894Sphilip			if (ACPI_SUCCESS(status))
1006143894Sphilip				return (TRUE);
1007143894Sphilip		} else if (sc->model->brn_up) {
1008143894Sphilip			/* Relative models */
1009143894Sphilip			status = AcpiEvaluateObject(sc->handle,
1010143894Sphilip			    sc->model->brn_up, NULL, NULL);
1011143894Sphilip			if (ACPI_FAILURE(status))
1012143894Sphilip				return (FALSE);
1013128561Sphilip
1014143894Sphilip			status = AcpiEvaluateObject(sc->handle,
1015143894Sphilip			    sc->model->brn_dn, NULL, NULL);
1016143894Sphilip			if (ACPI_FAILURE(status))
1017143894Sphilip				return (FALSE);
1018128561Sphilip
1019143894Sphilip			return (TRUE);
1020143894Sphilip		}
1021143894Sphilip		return (FALSE);
1022143894Sphilip	case ACPI_ASUS_METHOD_DISP:
1023143894Sphilip		if (sc->model->disp_get) {
1024143894Sphilip			status = acpi_GetInteger(sc->handle,
1025143894Sphilip			    sc->model->disp_get, &sc->s_disp);
1026143894Sphilip			if (ACPI_SUCCESS(status))
1027143894Sphilip				return (TRUE);
1028143894Sphilip		}
1029143894Sphilip		return (FALSE);
1030143894Sphilip	case ACPI_ASUS_METHOD_LCD:
1031178069Sjkim		if (sc->model->lcd_get) {
1032178069Sjkim			if (strncmp(sc->model->name, "G2K", 3) == 0) {
1033178069Sjkim				ACPI_BUFFER		Buf;
1034178069Sjkim				ACPI_OBJECT		Arg, Obj;
1035178069Sjkim				ACPI_OBJECT_LIST	Args;
1036128561Sphilip
1037178069Sjkim				Arg.Type = ACPI_TYPE_INTEGER;
1038178069Sjkim				Arg.Integer.Value = 0x11;
1039178069Sjkim				Args.Count = 1;
1040178069Sjkim				Args.Pointer = &Arg;
1041178069Sjkim				Buf.Length = sizeof(Obj);
1042178069Sjkim				Buf.Pointer = &Obj;
1043128561Sphilip
1044178069Sjkim				status = AcpiEvaluateObject(sc->handle,
1045178069Sjkim				    sc->model->lcd_get, &Args, &Buf);
1046178069Sjkim				if (ACPI_SUCCESS(status) &&
1047178069Sjkim				    Obj.Type == ACPI_TYPE_INTEGER) {
1048178069Sjkim					sc->s_lcd = Obj.Integer.Value;
1049178069Sjkim					return (TRUE);
1050178069Sjkim				}
1051178069Sjkim			} else if (strncmp(sc->model->name, "L3H", 3) == 0) {
1052178069Sjkim				ACPI_BUFFER		Buf;
1053178069Sjkim				ACPI_OBJECT		Arg[2], Obj;
1054178069Sjkim				ACPI_OBJECT_LIST	Args;
1055128561Sphilip
1056178069Sjkim				/* L3H is a bit special */
1057178069Sjkim				Arg[0].Type = ACPI_TYPE_INTEGER;
1058178069Sjkim				Arg[0].Integer.Value = 0x02;
1059178069Sjkim				Arg[1].Type = ACPI_TYPE_INTEGER;
1060178069Sjkim				Arg[1].Integer.Value = 0x03;
1061128561Sphilip
1062178069Sjkim				Args.Count = 2;
1063178069Sjkim				Args.Pointer = Arg;
1064178069Sjkim
1065178069Sjkim				Buf.Length = sizeof(Obj);
1066178069Sjkim				Buf.Pointer = &Obj;
1067178069Sjkim
1068178069Sjkim				status = AcpiEvaluateObject(sc->handle,
1069178069Sjkim				    sc->model->lcd_get, &Args, &Buf);
1070178069Sjkim				if (ACPI_SUCCESS(status) &&
1071178069Sjkim				    Obj.Type == ACPI_TYPE_INTEGER) {
1072178069Sjkim					sc->s_lcd = Obj.Integer.Value >> 8;
1073178069Sjkim					return (TRUE);
1074178069Sjkim				}
1075178069Sjkim			} else {
1076178069Sjkim				status = acpi_GetInteger(sc->handle,
1077178069Sjkim				    sc->model->lcd_get, &sc->s_lcd);
1078178069Sjkim				if (ACPI_SUCCESS(status))
1079178069Sjkim					return (TRUE);
1080143894Sphilip			}
1081143894Sphilip		}
1082143894Sphilip		return (FALSE);
1083180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
1084180062Srpaulo		if (sc->model->cam_get) {
1085180062Srpaulo			status = acpi_GetInteger(sc->handle,
1086180062Srpaulo			    sc->model->cam_get, &sc->s_cam);
1087180062Srpaulo			if (ACPI_SUCCESS(status))
1088180062Srpaulo				return (TRUE);
1089180062Srpaulo		}
1090180062Srpaulo		return (FALSE);
1091180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
1092180062Srpaulo		if (sc->model->crd_get) {
1093180062Srpaulo			status = acpi_GetInteger(sc->handle,
1094180062Srpaulo			    sc->model->crd_get, &sc->s_crd);
1095180062Srpaulo			if (ACPI_SUCCESS(status))
1096180062Srpaulo				return (TRUE);
1097180062Srpaulo		}
1098180062Srpaulo		return (FALSE);
1099143894Sphilip	}
1100143894Sphilip	return (FALSE);
1101128561Sphilip}
1102128561Sphilip
1103128561Sphilipstatic void
1104128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1105128561Sphilip{
1106128561Sphilip	struct acpi_asus_softc	*sc;
1107128561Sphilip	struct acpi_softc	*acpi_sc;
1108128561Sphilip
1109128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1110128561Sphilip
1111128561Sphilip	sc = device_get_softc((device_t)context);
1112128561Sphilip	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1113128561Sphilip
1114133628Snjl	ACPI_SERIAL_BEGIN(asus);
1115128561Sphilip	if ((notify & ~0x10) <= 15) {
1116132610Snjl		sc->s_brn = notify & ~0x10;
1117128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1118128561Sphilip	} else if ((notify & ~0x20) <= 15) {
1119132610Snjl		sc->s_brn = notify & ~0x20;
1120128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1121128561Sphilip	} else if (notify == 0x33) {
1122128561Sphilip		sc->s_lcd = 1;
1123128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
1124128561Sphilip	} else if (notify == 0x34) {
1125128561Sphilip		sc->s_lcd = 0;
1126128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
1127128561Sphilip	} else {
1128128561Sphilip		/* Notify devd(8) */
1129128561Sphilip		acpi_UserNotify("ASUS", h, notify);
1130128561Sphilip	}
1131133628Snjl	ACPI_SERIAL_END(asus);
1132128561Sphilip}
1133180062Srpaulo
1134180062Srpaulostatic void
1135180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1136180062Srpaulo{
1137180062Srpaulo	struct acpi_asus_softc	*sc;
1138180062Srpaulo	struct acpi_softc	*acpi_sc;
1139180062Srpaulo
1140180062Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1141180062Srpaulo
1142180062Srpaulo	sc = device_get_softc((device_t)context);
1143180062Srpaulo	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1144180062Srpaulo
1145180062Srpaulo	ACPI_SERIAL_BEGIN(asus);
1146180062Srpaulo	if ((notify & ~0x20) <= 15) {
1147180062Srpaulo		sc->s_brn = notify & ~0x20;
1148180062Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc,
1149180062Srpaulo		    "Brightness increased/decreased\n");
1150180062Srpaulo	} else {
1151180062Srpaulo		/* Notify devd(8) */
1152180062Srpaulo		acpi_UserNotify("ASUS-Eee", h, notify);
1153180062Srpaulo	}
1154180062Srpaulo	ACPI_SERIAL_END(asus);
1155180062Srpaulo}
1156