acpi_asus.c revision 186529
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 186529 2008-12-27 20:48:11Z stas $");
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
60180268Srpaulo#define ACPI_ASUS_METHOD_WLAN	6
61143894Sphilip
62138825Snjl#define _COMPONENT	ACPI_OEM
63128561SphilipACPI_MODULE_NAME("ASUS")
64128561Sphilip
65128561Sphilipstruct acpi_asus_model {
66128561Sphilip	char	*name;
67128561Sphilip
68146022Sphilip	char	*bled_set;
69178069Sjkim	char	*dled_set;
70178069Sjkim	char	*gled_set;
71128561Sphilip	char	*mled_set;
72128561Sphilip	char	*tled_set;
73128561Sphilip	char	*wled_set;
74128561Sphilip
75128561Sphilip	char	*brn_get;
76128561Sphilip	char	*brn_set;
77128561Sphilip	char	*brn_up;
78128561Sphilip	char	*brn_dn;
79128561Sphilip
80128561Sphilip	char	*lcd_get;
81128561Sphilip	char	*lcd_set;
82128561Sphilip
83128561Sphilip	char	*disp_get;
84128561Sphilip	char	*disp_set;
85180062Srpaulo
86180062Srpaulo	char	*cam_get;
87180062Srpaulo	char	*cam_set;
88180062Srpaulo
89180062Srpaulo	char	*crd_get;
90180062Srpaulo	char	*crd_set;
91180062Srpaulo
92180268Srpaulo	char	*wlan_get;
93180268Srpaulo	char	*wlan_set;
94180268Srpaulo
95180062Srpaulo	void	(*n_func)(ACPI_HANDLE, UINT32, void *);
96184625Srpaulo
97184625Srpaulo	char	*lcdd;
98184625Srpaulo	void	(*lcdd_n_func)(ACPI_HANDLE, UINT32, void *);
99128561Sphilip};
100128561Sphilip
101133095Sphilipstruct acpi_asus_led {
102144339Sphilip	struct acpi_asus_softc *sc;
103133095Sphilip	struct cdev	*cdev;
104144339Sphilip	int		busy;
105144339Sphilip	int		state;
106133095Sphilip	enum {
107146022Sphilip		ACPI_ASUS_LED_BLED,
108178069Sjkim		ACPI_ASUS_LED_DLED,
109178069Sjkim		ACPI_ASUS_LED_GLED,
110133095Sphilip		ACPI_ASUS_LED_MLED,
111133095Sphilip		ACPI_ASUS_LED_TLED,
112133095Sphilip		ACPI_ASUS_LED_WLED,
113133095Sphilip	} type;
114133095Sphilip};
115133095Sphilip
116128561Sphilipstruct acpi_asus_softc {
117128561Sphilip	device_t		dev;
118128561Sphilip	ACPI_HANDLE		handle;
119184625Srpaulo	ACPI_HANDLE		lcdd_handle;
120128561Sphilip
121128561Sphilip	struct acpi_asus_model	*model;
122128561Sphilip	struct sysctl_ctx_list	sysctl_ctx;
123128561Sphilip	struct sysctl_oid	*sysctl_tree;
124128561Sphilip
125146022Sphilip	struct acpi_asus_led	s_bled;
126178069Sjkim	struct acpi_asus_led	s_dled;
127178069Sjkim	struct acpi_asus_led	s_gled;
128133095Sphilip	struct acpi_asus_led	s_mled;
129133095Sphilip	struct acpi_asus_led	s_tled;
130133095Sphilip	struct acpi_asus_led	s_wled;
131128561Sphilip
132128561Sphilip	int			s_brn;
133128561Sphilip	int			s_disp;
134128561Sphilip	int			s_lcd;
135180062Srpaulo	int			s_cam;
136180062Srpaulo	int			s_crd;
137180268Srpaulo	int			s_wlan;
138128561Sphilip};
139128561Sphilip
140184625Srpaulostatic void	acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
141184625Srpaulo    void *context);
142184625Srpaulo
143137245Sphilip/*
144137245Sphilip * We can identify Asus laptops from the string they return
145137245Sphilip * as a result of calling the ATK0100 'INIT' method.
146137245Sphilip */
147128561Sphilipstatic struct acpi_asus_model acpi_asus_models[] = {
148128561Sphilip	{
149146024Sphilip		.name		= "xxN",
150146024Sphilip		.mled_set	= "MLED",
151146024Sphilip		.wled_set	= "WLED",
152146024Sphilip		.lcd_get	= "\\BKLT",
153146024Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
154146024Sphilip		.brn_get	= "GPLV",
155146024Sphilip		.brn_set	= "SPLV",
156146024Sphilip		.disp_get	= "\\ADVG",
157146024Sphilip		.disp_set	= "SDSP"
158146024Sphilip	},
159146024Sphilip	{
160146024Sphilip		.name		= "A1x",
161146024Sphilip		.mled_set	= "MLED",
162146024Sphilip		.lcd_get	= "\\BKLI",
163146024Sphilip		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
164146024Sphilip		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0E",
165146024Sphilip		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0F"
166146024Sphilip	},
167146024Sphilip	{
168146024Sphilip		.name		= "A2x",
169146024Sphilip		.mled_set	= "MLED",
170146024Sphilip		.wled_set	= "WLED",
171146024Sphilip		.lcd_get	= "\\BAOF",
172146024Sphilip		.lcd_set	= "\\Q10",
173146024Sphilip		.brn_get	= "GPLV",
174146024Sphilip		.brn_set	= "SPLV",
175146024Sphilip		.disp_get	= "\\INFB",
176146024Sphilip		.disp_set	= "SDSP"
177146024Sphilip	},
178146024Sphilip	{
179170216Sphilip		.name           = "A3N",
180170216Sphilip		.mled_set       = "MLED",
181170216Sphilip		.bled_set       = "BLED",
182170216Sphilip		.wled_set       = "WLED",
183170216Sphilip		.lcd_get        = NULL,
184170216Sphilip		.lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
185170216Sphilip		.brn_set        = "SPLV",
186170216Sphilip		.brn_get        = "SDSP",
187170216Sphilip		.disp_set       = "SDSP",
188170216Sphilip		.disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD"
189170216Sphilip	},
190170216Sphilip	{
191155022Sphilip		.name		= "A4D",
192155022Sphilip		.mled_set	= "MLED",
193155022Sphilip		.brn_up		= "\\_SB_.PCI0.SBRG.EC0._Q0E",
194155022Sphilip		.brn_dn		= "\\_SB_.PCI0.SBRG.EC0._Q0F",
195155022Sphilip		.brn_get	= "GPLV",
196155022Sphilip		.brn_set	= "SPLV",
197155022Sphilip#ifdef notyet
198155022Sphilip		.disp_get	= "\\_SB_.PCI0.SBRG.EC0._Q10",
199155022Sphilip		.disp_set	= "\\_SB_.PCI0.SBRG.EC0._Q11"
200155022Sphilip#endif
201155022Sphilip	},
202155022Sphilip	{
203155021Sphilip		.name		= "A6V",
204155021Sphilip		.bled_set	= "BLED",
205155021Sphilip		.mled_set	= "MLED",
206155021Sphilip		.wled_set	= "WLED",
207155021Sphilip		.lcd_get	= NULL,
208155021Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
209155021Sphilip		.brn_get	= "GPLV",
210155021Sphilip		.brn_set	= "SPLV",
211155021Sphilip		.disp_get	= "\\_SB.PCI0.P0P3.VGA.GETD",
212155021Sphilip		.disp_set	= "SDSP"
213155021Sphilip	},
214155021Sphilip	{
215184625Srpaulo		.name		= "A8SR",
216184625Srpaulo		.bled_set	= "BLED",
217184625Srpaulo		.mled_set	= "MLED",
218184625Srpaulo		.wled_set	= "WLED",
219184625Srpaulo		.lcd_get	= NULL,
220184625Srpaulo		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
221184625Srpaulo		.brn_get	= "GPLV",
222184625Srpaulo		.brn_set	= "SPLV",
223184625Srpaulo		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
224184625Srpaulo		.disp_set	= "SDSP",
225184625Srpaulo		.lcdd		= "\\_SB.PCI0.P0P1.VGA.LCDD",
226184625Srpaulo		.lcdd_n_func	= acpi_asus_lcdd_notify
227184625Srpaulo	},
228184625Srpaulo	{
229146024Sphilip		.name		= "D1x",
230146024Sphilip		.mled_set	= "MLED",
231146024Sphilip		.lcd_get	= "\\GP11",
232146024Sphilip		.lcd_set	= "\\Q0D",
233146024Sphilip		.brn_up		= "\\Q0C",
234146024Sphilip		.brn_dn		= "\\Q0B",
235146024Sphilip		.disp_get	= "\\INFB",
236146024Sphilip		.disp_set	= "SDSP"
237146024Sphilip	},
238146024Sphilip	{
239178069Sjkim		.name		= "G2K",
240178069Sjkim		.bled_set	= "BLED",
241178069Sjkim		.dled_set	= "DLED",
242178069Sjkim		.gled_set	= "GLED",
243178069Sjkim		.mled_set	= "MLED",
244178069Sjkim		.tled_set	= "TLED",
245178069Sjkim		.wled_set	= "WLED",
246178069Sjkim		.brn_get	= "GPLV",
247178069Sjkim		.brn_set	= "SPLV",
248178069Sjkim		.lcd_get	= "\\_SB.PCI0.SBRG.EC0.RPIN",
249178069Sjkim		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
250178069Sjkim		.disp_get	= "\\_SB.PCI0.PCE2.VGA.GETD",
251178069Sjkim		.disp_set	= "SDSP",
252178069Sjkim	},
253178069Sjkim	{
254128561Sphilip		.name		= "L2D",
255128561Sphilip		.mled_set	= "MLED",
256128561Sphilip		.wled_set	= "WLED",
257128561Sphilip		.brn_up		= "\\Q0E",
258128561Sphilip		.brn_dn		= "\\Q0F",
259128561Sphilip		.lcd_get	= "\\SGP0",
260128561Sphilip		.lcd_set	= "\\Q10"
261128561Sphilip	},
262128561Sphilip	{
263128561Sphilip		.name		= "L3C",
264128561Sphilip		.mled_set	= "MLED",
265128561Sphilip		.wled_set	= "WLED",
266128561Sphilip		.brn_get	= "GPLV",
267128561Sphilip		.brn_set	= "SPLV",
268128561Sphilip		.lcd_get	= "\\GL32",
269128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.ECD0._Q10"
270128561Sphilip	},
271128561Sphilip	{
272128561Sphilip		.name		= "L3D",
273128561Sphilip		.mled_set	= "MLED",
274128561Sphilip		.wled_set	= "WLED",
275128561Sphilip		.brn_get	= "GPLV",
276128561Sphilip		.brn_set	= "SPLV",
277128561Sphilip		.lcd_get	= "\\BKLG",
278128561Sphilip		.lcd_set	= "\\Q10"
279128561Sphilip	},
280128561Sphilip	{
281128561Sphilip		.name		= "L3H",
282128561Sphilip		.mled_set	= "MLED",
283128561Sphilip		.wled_set	= "WLED",
284128561Sphilip		.brn_get	= "GPLV",
285128561Sphilip		.brn_set	= "SPLV",
286128561Sphilip		.lcd_get	= "\\_SB.PCI0.PM.PBC",
287128561Sphilip		.lcd_set	= "EHK",
288128561Sphilip		.disp_get	= "\\_SB.INFB",
289128561Sphilip		.disp_set	= "SDSP"
290128561Sphilip	},
291128561Sphilip	{
292137388Sphilip		.name		= "L4R",
293137388Sphilip		.mled_set	= "MLED",
294137388Sphilip		.wled_set	= "WLED",
295137388Sphilip		.brn_get	= "GPLV",
296137388Sphilip		.brn_set	= "SPLV",
297137388Sphilip		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
298137388Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
299137388Sphilip		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
300137388Sphilip		.disp_set	= "SDSP"
301137388Sphilip	},
302137388Sphilip	{
303146024Sphilip		.name		= "L5x",
304146024Sphilip		.mled_set	= "MLED",
305146024Sphilip		.tled_set	= "TLED",
306146024Sphilip		.lcd_get	= "\\BAOF",
307146024Sphilip		.lcd_set	= "\\Q0D",
308146024Sphilip		.brn_get	= "GPLV",
309146024Sphilip		.brn_set	= "SPLV",
310146024Sphilip		.disp_get	= "\\INFB",
311146024Sphilip		.disp_set	= "SDSP"
312146024Sphilip	},
313146024Sphilip	{
314128561Sphilip		.name		= "L8L"
315181885Srpaulo		/* Only has hotkeys, apparently */
316128561Sphilip	},
317128561Sphilip	{
318128561Sphilip		.name		= "M1A",
319128561Sphilip		.mled_set	= "MLED",
320128561Sphilip		.brn_up		= "\\_SB.PCI0.PX40.EC0.Q0E",
321128561Sphilip		.brn_dn		= "\\_SB.PCI0.PX40.EC0.Q0F",
322128561Sphilip		.lcd_get	= "\\PNOF",
323128561Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.EC0.Q10"
324128561Sphilip	},
325128561Sphilip	{
326128561Sphilip		.name		= "M2E",
327128561Sphilip		.mled_set	= "MLED",
328128561Sphilip		.wled_set	= "WLED",
329128561Sphilip		.brn_get	= "GPLV",
330128561Sphilip		.brn_set	= "SPLV",
331128561Sphilip		.lcd_get	= "\\GP06",
332128561Sphilip		.lcd_set	= "\\Q10"
333128561Sphilip	},
334128561Sphilip	{
335137127Sphilip		.name		= "M6N",
336137127Sphilip		.mled_set	= "MLED",
337137127Sphilip		.wled_set	= "WLED",
338137127Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
339137127Sphilip		.lcd_get	= "\\_SB.BKLT",
340137127Sphilip		.brn_set	= "SPLV",
341137127Sphilip		.brn_get	= "GPLV",
342137127Sphilip		.disp_set	= "SDSP",
343137127Sphilip		.disp_get	= "\\SSTE"
344137127Sphilip	},
345137388Sphilip	{
346137388Sphilip		.name		= "M6R",
347137388Sphilip		.mled_set	= "MLED",
348137388Sphilip		.wled_set	= "WLED",
349137388Sphilip		.brn_get	= "GPLV",
350137388Sphilip		.brn_set	= "SPLV",
351137388Sphilip		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
352137388Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
353137388Sphilip		.disp_get	= "\\SSTE",
354137388Sphilip		.disp_set	= "SDSP"
355137388Sphilip	},
356146022Sphilip	{
357146024Sphilip		.name		= "S1x",
358146024Sphilip		.mled_set	= "MLED",
359146022Sphilip		.wled_set	= "WLED",
360146024Sphilip		.lcd_get	= "\\PNOF",
361146024Sphilip		.lcd_set	= "\\_SB.PCI0.PX40.Q10",
362146022Sphilip		.brn_get	= "GPLV",
363146024Sphilip		.brn_set	= "SPLV"
364146022Sphilip	},
365146022Sphilip	{
366146024Sphilip		.name		= "S2x",
367146022Sphilip		.mled_set	= "MLED",
368146024Sphilip		.lcd_get	= "\\BKLI",
369146024Sphilip		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
370146024Sphilip		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0B",
371146024Sphilip		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0A"
372146024Sphilip	},
373146024Sphilip	{
374146024Sphilip		.name		= "V6V",
375146024Sphilip		.bled_set	= "BLED",
376146024Sphilip		.tled_set	= "TLED",
377146022Sphilip		.wled_set	= "WLED",
378146022Sphilip		.lcd_get	= "\\BKLT",
379146022Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
380146022Sphilip		.brn_get	= "GPLV",
381146022Sphilip		.brn_set	= "SPLV",
382146024Sphilip		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
383146022Sphilip		.disp_set	= "SDSP"
384146022Sphilip	},
385157605Sphilip	{
386157605Sphilip		.name		= "W5A",
387157605Sphilip		.bled_set	= "BLED",
388157605Sphilip		.lcd_get	= "\\BKLT",
389157605Sphilip		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
390157605Sphilip		.brn_get	= "GPLV",
391157605Sphilip		.brn_set	= "SPLV",
392157605Sphilip		.disp_get	= "\\_SB.PCI0.P0P2.VGA.GETD",
393157605Sphilip		.disp_set	= "SDSP"
394157605Sphilip	},
395137245Sphilip
396137245Sphilip	{ .name = NULL }
397137245Sphilip};
398137245Sphilip
399137245Sphilip/*
400137245Sphilip * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
401137245Sphilip * but they can't be probed quite the same way as Asus laptops.
402137245Sphilip */
403137245Sphilipstatic struct acpi_asus_model acpi_samsung_models[] = {
404137127Sphilip	{
405128561Sphilip		.name		= "P30",
406128561Sphilip		.wled_set	= "WLED",
407128561Sphilip		.brn_up		= "\\_SB.PCI0.LPCB.EC0._Q68",
408128561Sphilip		.brn_dn		= "\\_SB.PCI0.LPCB.EC0._Q69",
409128561Sphilip		.lcd_get	= "\\BKLT",
410128561Sphilip		.lcd_set	= "\\_SB.PCI0.LPCB.EC0._Q0E"
411128561Sphilip	},
412128561Sphilip
413128561Sphilip	{ .name = NULL }
414128561Sphilip};
415128561Sphilip
416180062Srpaulostatic void	acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
417180062Srpaulo
418178178Srpaulo/*
419178178Srpaulo * EeePC have an Asus ASUS010 gadget interface,
420178178Srpaulo * but they can't be probed quite the same way as Asus laptops.
421178178Srpaulo */
422178178Srpaulostatic struct acpi_asus_model acpi_eeepc_models[] = {
423178178Srpaulo	{
424178178Srpaulo		.name		= "EEE",
425178178Srpaulo		.brn_get	= "\\_SB.ATKD.PBLG",
426180062Srpaulo		.brn_set	= "\\_SB.ATKD.PBLS",
427180062Srpaulo		.cam_get	= "\\_SB.ATKD.CAMG",
428180062Srpaulo		.cam_set	= "\\_SB.ATKD.CAMS",
429180062Srpaulo		.crd_set	= "\\_SB.ATKD.CRDS",
430180062Srpaulo		.crd_get	= "\\_SB.ATKD.CRDG",
431180268Srpaulo		.wlan_get	= "\\_SB.ATKD.WLDG",
432180268Srpaulo		.wlan_set	= "\\_SB.ATKD.WLDS",
433180062Srpaulo		.n_func		= acpi_asus_eeepc_notify
434178178Srpaulo	},
435178178Srpaulo
436178178Srpaulo	{ .name = NULL }
437178178Srpaulo};
438178178Srpaulo
439143894Sphilipstatic struct {
440143894Sphilip	char	*name;
441143894Sphilip	char	*description;
442143894Sphilip	int	method;
443180062Srpaulo	int	flags;
444143894Sphilip} acpi_asus_sysctls[] = {
445143894Sphilip	{
446143894Sphilip		.name		= "lcd_backlight",
447143894Sphilip		.method		= ACPI_ASUS_METHOD_LCD,
448180062Srpaulo		.description	= "state of the lcd backlight",
449180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
450143894Sphilip	},
451143894Sphilip	{
452143894Sphilip		.name		= "lcd_brightness",
453143894Sphilip		.method		= ACPI_ASUS_METHOD_BRN,
454180062Srpaulo		.description	= "brightness of the lcd panel",
455180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
456143894Sphilip	},
457143894Sphilip	{
458143894Sphilip		.name		= "video_output",
459143894Sphilip		.method		= ACPI_ASUS_METHOD_DISP,
460180062Srpaulo		.description	= "display output state",
461180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
462143894Sphilip	},
463180062Srpaulo	{
464180062Srpaulo		.name		= "camera",
465180062Srpaulo		.method		= ACPI_ASUS_METHOD_CAMERA,
466180062Srpaulo		.description	= "internal camera state",
467180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
468180062Srpaulo	},
469180062Srpaulo	{
470180062Srpaulo		.name		= "cardreader",
471180062Srpaulo		.method		= ACPI_ASUS_METHOD_CARDRD,
472180062Srpaulo		.description	= "internal card reader state",
473180062Srpaulo		.flags 		= CTLTYPE_INT | CTLFLAG_RW
474180062Srpaulo	},
475180268Srpaulo	{
476180268Srpaulo		.name		= "wlan",
477180268Srpaulo		.method		= ACPI_ASUS_METHOD_WLAN,
478180268Srpaulo		.description	= "wireless lan state",
479180268Srpaulo		.flags		= CTLTYPE_INT | CTLFLAG_RW
480180268Srpaulo	},
481143894Sphilip
482143894Sphilip	{ .name = NULL }
483143894Sphilip};
484143894Sphilip
485133628SnjlACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
486133628Snjl
487128561Sphilip/* Function prototypes */
488128561Sphilipstatic int	acpi_asus_probe(device_t dev);
489128561Sphilipstatic int	acpi_asus_attach(device_t dev);
490128561Sphilipstatic int	acpi_asus_detach(device_t dev);
491128561Sphilip
492133095Sphilipstatic void	acpi_asus_led(struct acpi_asus_led *led, int state);
493144339Sphilipstatic void	acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
494128561Sphilip
495143894Sphilipstatic int	acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
496143894Sphilipstatic int	acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
497143894Sphilipstatic int	acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
498143894Sphilipstatic int	acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
499128561Sphilip
500128561Sphilipstatic void	acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
501128561Sphilip
502128561Sphilipstatic device_method_t acpi_asus_methods[] = {
503137631Sphilip	DEVMETHOD(device_probe,  acpi_asus_probe),
504128561Sphilip	DEVMETHOD(device_attach, acpi_asus_attach),
505128561Sphilip	DEVMETHOD(device_detach, acpi_asus_detach),
506128561Sphilip
507128561Sphilip	{ 0, 0 }
508128561Sphilip};
509128561Sphilip
510128561Sphilipstatic driver_t acpi_asus_driver = {
511128561Sphilip	"acpi_asus",
512128561Sphilip	acpi_asus_methods,
513128561Sphilip	sizeof(struct acpi_asus_softc)
514128561Sphilip};
515128561Sphilip
516128561Sphilipstatic devclass_t acpi_asus_devclass;
517128561Sphilip
518128561SphilipDRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
519128561SphilipMODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
520128561Sphilip
521128561Sphilipstatic int
522128561Sphilipacpi_asus_probe(device_t dev)
523128561Sphilip{
524128561Sphilip	struct acpi_asus_model	*model;
525128561Sphilip	struct acpi_asus_softc	*sc;
526128561Sphilip	struct sbuf		*sb;
527128561Sphilip	ACPI_BUFFER		Buf;
528128561Sphilip	ACPI_OBJECT		Arg, *Obj;
529128561Sphilip	ACPI_OBJECT_LIST	Args;
530178178Srpaulo	static char		*asus_ids[] = { "ATK0100", "ASUS010", NULL };
531178178Srpaulo	char *rstr;
532128561Sphilip
533128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
534128561Sphilip
535178178Srpaulo	if (acpi_disabled("asus"))
536137632Sphilip		return (ENXIO);
537178178Srpaulo	rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
538178178Srpaulo	if (rstr == NULL) {
539178178Srpaulo		return (ENXIO);
540178178Srpaulo	}
541137631Sphilip
542137632Sphilip	sc = device_get_softc(dev);
543137632Sphilip	sc->dev = dev;
544137632Sphilip	sc->handle = acpi_get_handle(dev);
545128561Sphilip
546137632Sphilip	Arg.Type = ACPI_TYPE_INTEGER;
547137632Sphilip	Arg.Integer.Value = 0;
548128561Sphilip
549137632Sphilip	Args.Count = 1;
550137632Sphilip	Args.Pointer = &Arg;
551137631Sphilip
552137632Sphilip	Buf.Pointer = NULL;
553137632Sphilip	Buf.Length = ACPI_ALLOCATE_BUFFER;
554128561Sphilip
555137632Sphilip	AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
556137632Sphilip	Obj = Buf.Pointer;
557137245Sphilip
558137632Sphilip	/*
559137632Sphilip	 * The Samsung P30 returns a null-pointer from INIT, we
560137632Sphilip	 * can identify it from the 'ODEM' string in the DSDT.
561137632Sphilip	 */
562137632Sphilip	if (Obj->String.Pointer == NULL) {
563137632Sphilip		ACPI_STATUS		status;
564137632Sphilip		ACPI_TABLE_HEADER	th;
565137245Sphilip
566167814Sjkim		status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
567137632Sphilip		if (ACPI_FAILURE(status)) {
568137632Sphilip			device_printf(dev, "Unsupported (Samsung?) laptop\n");
569137632Sphilip			AcpiOsFree(Buf.Pointer);
570137632Sphilip			return (ENXIO);
571137245Sphilip		}
572137245Sphilip
573137632Sphilip		if (strncmp("ODEM", th.OemTableId, 4) == 0) {
574137632Sphilip			sc->model = &acpi_samsung_models[0];
575137632Sphilip			device_set_desc(dev, "Samsung P30 Laptop Extras");
576137632Sphilip			AcpiOsFree(Buf.Pointer);
577137632Sphilip			return (0);
578137632Sphilip		}
579178178Srpaulo
580178178Srpaulo		/* if EeePC */
581178231Srpaulo		if (strncmp("ASUS010", rstr, 7) == 0) {
582178178Srpaulo			sc->model = &acpi_eeepc_models[0];
583178178Srpaulo			device_set_desc(dev, "ASUS EeePC");
584178178Srpaulo			AcpiOsFree(Buf.Pointer);
585178178Srpaulo			return (0);
586178178Srpaulo		}
587137632Sphilip	}
588137245Sphilip
589181463Sdes	sb = sbuf_new_auto();
590137632Sphilip	if (sb == NULL)
591137632Sphilip		return (ENOMEM);
592128561Sphilip
593137632Sphilip	/*
594137632Sphilip	 * Asus laptops are simply identified by name, easy!
595137632Sphilip	 */
596146024Sphilip	for (model = acpi_asus_models; model->name != NULL; model++) {
597137632Sphilip		if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
598146024Sphilip
599146024Sphilipgood:
600146024Sphilip			sbuf_printf(sb, "Asus %s Laptop Extras",
601146024Sphilip			    Obj->String.Pointer);
602137632Sphilip			sbuf_finish(sb);
603128561Sphilip
604137632Sphilip			sc->model = model;
605144076Spjd			device_set_desc_copy(dev, sbuf_data(sb));
606128561Sphilip
607137632Sphilip			sbuf_delete(sb);
608137632Sphilip			AcpiOsFree(Buf.Pointer);
609137632Sphilip			return (0);
610137632Sphilip		}
611146024Sphilip
612146024Sphilip		/*
613146024Sphilip		 * Some models look exactly the same as other models, but have
614146024Sphilip		 * their own ids.  If we spot these, set them up with the same
615146024Sphilip		 * details as the models they're like, possibly dealing with
616146024Sphilip		 * small differences.
617146024Sphilip		 *
618146024Sphilip		 * XXX: there must be a prettier way to do this!
619146024Sphilip		 */
620146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
621146024Sphilip		    (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
622146024Sphilip		     strncmp(Obj->String.Pointer, "S1N", 3) == 0))
623146024Sphilip			goto good;
624146024Sphilip		else if (strncmp(model->name, "A1x", 3) == 0 &&
625146024Sphilip		    strncmp(Obj->String.Pointer, "A1", 2) == 0)
626146024Sphilip			goto good;
627146024Sphilip		else if (strncmp(model->name, "A2x", 3) == 0 &&
628146024Sphilip		    strncmp(Obj->String.Pointer, "A2", 2) == 0)
629146024Sphilip			goto good;
630146024Sphilip		else if (strncmp(model->name, "D1x", 3) == 0 &&
631146024Sphilip		    strncmp(Obj->String.Pointer, "D1", 2) == 0)
632146024Sphilip			goto good;
633146024Sphilip		else if (strncmp(model->name, "L3H", 3) == 0 &&
634146024Sphilip		    strncmp(Obj->String.Pointer, "L2E", 3) == 0)
635146024Sphilip			goto good;
636146024Sphilip		else if (strncmp(model->name, "L5x", 3) == 0 &&
637146024Sphilip		    strncmp(Obj->String.Pointer, "L5", 2) == 0)
638146024Sphilip			goto good;
639146024Sphilip		else if (strncmp(model->name, "M2E", 3) == 0 &&
640146024Sphilip		    (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
641146024Sphilip		     strncmp(Obj->String.Pointer, "L4E", 3) == 0))
642146024Sphilip			goto good;
643146024Sphilip		else if (strncmp(model->name, "S1x", 3) == 0 &&
644146024Sphilip		    (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
645146024Sphilip		     strncmp(Obj->String.Pointer, "S1", 2) == 0))
646146024Sphilip			goto good;
647146024Sphilip		else if (strncmp(model->name, "S2x", 3) == 0 &&
648146024Sphilip		    (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
649146024Sphilip		     strncmp(Obj->String.Pointer, "S2", 2) == 0))
650146024Sphilip			goto good;
651128561Sphilip
652146024Sphilip		/* L2B is like L3C but has no lcd_get method */
653146024Sphilip		else if (strncmp(model->name, "L3C", 3) == 0 &&
654146024Sphilip		    strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
655146024Sphilip			model->lcd_get = NULL;
656146024Sphilip			goto good;
657146024Sphilip		}
658146024Sphilip
659146024Sphilip		/* A3G is like M6R but with a different lcd_get method */
660146024Sphilip		else if (strncmp(model->name, "M6R", 3) == 0 &&
661146024Sphilip		    strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
662146024Sphilip			model->lcd_get = "\\BLFG";
663146024Sphilip			goto good;
664146024Sphilip		}
665146024Sphilip
666146024Sphilip		/* M2N and W1N are like xxN with added WLED */
667146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
668146024Sphilip		    (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
669146024Sphilip		     strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
670146024Sphilip			model->wled_set = "WLED";
671146024Sphilip			goto good;
672146024Sphilip		}
673146024Sphilip
674146024Sphilip		/* M5N and S5N are like xxN without MLED */
675146024Sphilip		else if (strncmp(model->name, "xxN", 3) == 0 &&
676146024Sphilip		    (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
677146024Sphilip		     strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
678146024Sphilip			model->mled_set = NULL;
679146024Sphilip			goto good;
680146024Sphilip		}
681146024Sphilip	}
682146024Sphilip
683137632Sphilip	sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
684137632Sphilip	sbuf_finish(sb);
685128561Sphilip
686137632Sphilip	device_printf(dev, sbuf_data(sb));
687137632Sphilip
688137632Sphilip	sbuf_delete(sb);
689137632Sphilip	AcpiOsFree(Buf.Pointer);
690137632Sphilip
691128561Sphilip	return (ENXIO);
692128561Sphilip}
693128561Sphilip
694128561Sphilipstatic int
695128561Sphilipacpi_asus_attach(device_t dev)
696128561Sphilip{
697128561Sphilip	struct acpi_asus_softc	*sc;
698128561Sphilip	struct acpi_softc	*acpi_sc;
699128561Sphilip
700128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
701128561Sphilip
702128561Sphilip	sc = device_get_softc(dev);
703128561Sphilip	acpi_sc = acpi_device_get_parent_softc(dev);
704128561Sphilip
705128561Sphilip	/* Build sysctl tree */
706128561Sphilip	sysctl_ctx_init(&sc->sysctl_ctx);
707128561Sphilip	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
708128561Sphilip	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
709128561Sphilip	    OID_AUTO, "asus", CTLFLAG_RD, 0, "");
710128561Sphilip
711143894Sphilip	/* Hook up nodes */
712143894Sphilip	for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
713143894Sphilip		if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
714143894Sphilip			continue;
715143894Sphilip
716143894Sphilip		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
717143894Sphilip		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
718143894Sphilip		    acpi_asus_sysctls[i].name,
719180062Srpaulo		    acpi_asus_sysctls[i].flags,
720143894Sphilip		    sc, i, acpi_asus_sysctl, "I",
721143894Sphilip		    acpi_asus_sysctls[i].description);
722143894Sphilip	}
723143894Sphilip
724128561Sphilip	/* Attach leds */
725146022Sphilip	if (sc->model->bled_set) {
726146022Sphilip		sc->s_bled.busy = 0;
727146022Sphilip		sc->s_bled.sc = sc;
728146022Sphilip		sc->s_bled.type = ACPI_ASUS_LED_BLED;
729146022Sphilip		sc->s_bled.cdev =
730178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
731178069Sjkim			"bled", 1);
732146022Sphilip	}
733146022Sphilip
734178069Sjkim	if (sc->model->dled_set) {
735178069Sjkim		sc->s_dled.busy = 0;
736178069Sjkim		sc->s_dled.sc = sc;
737178069Sjkim		sc->s_dled.type = ACPI_ASUS_LED_DLED;
738178069Sjkim		sc->s_dled.cdev =
739178069Sjkim		    led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
740178069Sjkim	}
741178069Sjkim
742178069Sjkim	if (sc->model->gled_set) {
743178069Sjkim		sc->s_gled.busy = 0;
744178069Sjkim		sc->s_gled.sc = sc;
745178069Sjkim		sc->s_gled.type = ACPI_ASUS_LED_GLED;
746178069Sjkim		sc->s_gled.cdev =
747178069Sjkim		    led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
748178069Sjkim	}
749178069Sjkim
750133095Sphilip	if (sc->model->mled_set) {
751144339Sphilip		sc->s_mled.busy = 0;
752144339Sphilip		sc->s_mled.sc = sc;
753133095Sphilip		sc->s_mled.type = ACPI_ASUS_LED_MLED;
754133095Sphilip		sc->s_mled.cdev =
755133095Sphilip		    led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
756133095Sphilip	}
757128561Sphilip
758133095Sphilip	if (sc->model->tled_set) {
759144339Sphilip		sc->s_tled.busy = 0;
760144339Sphilip		sc->s_tled.sc = sc;
761133095Sphilip		sc->s_tled.type = ACPI_ASUS_LED_TLED;
762133095Sphilip		sc->s_tled.cdev =
763178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
764178069Sjkim			"tled", 1);
765133095Sphilip	}
766128561Sphilip
767133095Sphilip	if (sc->model->wled_set) {
768144339Sphilip		sc->s_wled.busy = 0;
769144339Sphilip		sc->s_wled.sc = sc;
770133095Sphilip		sc->s_wled.type = ACPI_ASUS_LED_WLED;
771133095Sphilip		sc->s_wled.cdev =
772178069Sjkim		    led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
773178069Sjkim			"wled", 1);
774133095Sphilip	}
775128561Sphilip
776128561Sphilip	/* Activate hotkeys */
777128561Sphilip	AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
778128561Sphilip
779128561Sphilip	/* Handle notifies */
780180062Srpaulo	if (sc->model->n_func == NULL)
781180062Srpaulo		sc->model->n_func = acpi_asus_notify;
782180062Srpaulo
783132610Snjl	AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
784180062Srpaulo	    sc->model->n_func, dev);
785137631Sphilip
786184625Srpaulo	/* Find and hook the 'LCDD' object */
787184625Srpaulo	if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
788184625Srpaulo		ACPI_STATUS res;
789184625Srpaulo
790184625Srpaulo		sc->lcdd_handle = NULL;
791184625Srpaulo		res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
792184625Srpaulo		    NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
793184625Srpaulo		if (ACPI_SUCCESS(res)) {
794184625Srpaulo			AcpiInstallNotifyHandler((sc->lcdd_handle),
795184625Srpaulo			    ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
796184625Srpaulo	    	} else {
797184625Srpaulo	    		printf("%s: unable to find LCD device '%s'\n",
798184625Srpaulo	    		    __func__, sc->model->lcdd);
799184625Srpaulo	    	}
800184625Srpaulo	}
801184625Srpaulo
802128561Sphilip	return (0);
803128561Sphilip}
804128561Sphilip
805128561Sphilipstatic int
806128561Sphilipacpi_asus_detach(device_t dev)
807128561Sphilip{
808128561Sphilip	struct acpi_asus_softc	*sc;
809137631Sphilip
810128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
811128561Sphilip
812128561Sphilip	sc = device_get_softc(dev);
813128561Sphilip
814128561Sphilip	/* Turn the lights off */
815146022Sphilip	if (sc->model->bled_set)
816146022Sphilip		led_destroy(sc->s_bled.cdev);
817146022Sphilip
818178069Sjkim	if (sc->model->dled_set)
819178069Sjkim		led_destroy(sc->s_dled.cdev);
820178069Sjkim
821178069Sjkim	if (sc->model->gled_set)
822178069Sjkim		led_destroy(sc->s_gled.cdev);
823178069Sjkim
824128561Sphilip	if (sc->model->mled_set)
825133095Sphilip		led_destroy(sc->s_mled.cdev);
826128561Sphilip
827128561Sphilip	if (sc->model->tled_set)
828133095Sphilip		led_destroy(sc->s_tled.cdev);
829128561Sphilip
830128561Sphilip	if (sc->model->wled_set)
831133095Sphilip		led_destroy(sc->s_wled.cdev);
832128561Sphilip
833128561Sphilip	/* Remove notify handler */
834132610Snjl	AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
835132610Snjl	    acpi_asus_notify);
836184625Srpaulo
837184625Srpaulo	if (sc->lcdd_handle) {
838184625Srpaulo		KASSERT(sc->model->lcdd_n_func != NULL,
839184625Srpaulo		    ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
840184625Srpaulo		AcpiRemoveNotifyHandler((sc->lcdd_handle),
841184625Srpaulo		    ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
842184625Srpaulo	}
843128561Sphilip
844128561Sphilip	/* Free sysctl tree */
845128561Sphilip	sysctl_ctx_free(&sc->sysctl_ctx);
846128561Sphilip
847128561Sphilip	return (0);
848128561Sphilip}
849128561Sphilip
850128561Sphilipstatic void
851144339Sphilipacpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
852128561Sphilip{
853128561Sphilip	struct acpi_asus_softc	*sc;
854133095Sphilip	char			*method;
855144339Sphilip	int			state;
856144339Sphilip
857128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
858128561Sphilip
859144339Sphilip	sc = led->sc;
860128561Sphilip
861133095Sphilip	switch (led->type) {
862146022Sphilip	case ACPI_ASUS_LED_BLED:
863146022Sphilip		method = sc->model->bled_set;
864146022Sphilip		state = led->state;
865146022Sphilip		break;
866178069Sjkim	case ACPI_ASUS_LED_DLED:
867178069Sjkim		method = sc->model->dled_set;
868178069Sjkim		state = led->state;
869178069Sjkim		break;
870178069Sjkim	case ACPI_ASUS_LED_GLED:
871178069Sjkim		method = sc->model->gled_set;
872178069Sjkim		state = led->state + 1;	/* 1: off, 2: on */
873178069Sjkim		break;
874143894Sphilip	case ACPI_ASUS_LED_MLED:
875143894Sphilip		method = sc->model->mled_set;
876178069Sjkim		state = !led->state;	/* inverted */
877143894Sphilip		break;
878143894Sphilip	case ACPI_ASUS_LED_TLED:
879143894Sphilip		method = sc->model->tled_set;
880144339Sphilip		state = led->state;
881143894Sphilip		break;
882143894Sphilip	case ACPI_ASUS_LED_WLED:
883143894Sphilip		method = sc->model->wled_set;
884144339Sphilip		state = led->state;
885143894Sphilip		break;
886143894Sphilip	default:
887143894Sphilip		printf("acpi_asus_led: invalid LED type %d\n",
888143894Sphilip		    (int)led->type);
889143894Sphilip		return;
890133095Sphilip	}
891128561Sphilip
892133095Sphilip	acpi_SetInteger(sc->handle, method, state);
893144339Sphilip	led->busy = 0;
894128561Sphilip}
895144339Sphilip
896144339Sphilipstatic void
897144339Sphilipacpi_asus_led(struct acpi_asus_led *led, int state)
898144339Sphilip{
899128561Sphilip
900144339Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
901144339Sphilip
902144339Sphilip	if (led->busy)
903144339Sphilip		return;
904144339Sphilip
905144339Sphilip	led->busy = 1;
906144339Sphilip	led->state = state;
907144339Sphilip
908167814Sjkim	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
909144339Sphilip}
910144339Sphilip
911128561Sphilipstatic int
912143894Sphilipacpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
913128561Sphilip{
914128561Sphilip	struct acpi_asus_softc	*sc;
915143894Sphilip	int			arg;
916143894Sphilip	int			error = 0;
917143894Sphilip	int			function;
918143894Sphilip	int			method;
919143894Sphilip
920128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
921128561Sphilip
922128561Sphilip	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
923143894Sphilip	function = oidp->oid_arg2;
924143894Sphilip	method = acpi_asus_sysctls[function].method;
925143894Sphilip
926133628Snjl	ACPI_SERIAL_BEGIN(asus);
927143894Sphilip	arg = acpi_asus_sysctl_get(sc, method);
928143894Sphilip	error = sysctl_handle_int(oidp, &arg, 0, req);
929128561Sphilip
930128561Sphilip	/* Sanity check */
931143894Sphilip	if (error != 0 || req->newptr == NULL)
932133092Snjl		goto out;
933128561Sphilip
934143894Sphilip	/* Update */
935143894Sphilip	error = acpi_asus_sysctl_set(sc, method, arg);
936128561Sphilip
937143894Sphilipout:
938143894Sphilip	ACPI_SERIAL_END(asus);
939143894Sphilip	return (error);
940143894Sphilip}
941128561Sphilip
942143894Sphilipstatic int
943143894Sphilipacpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
944143894Sphilip{
945143894Sphilip	int val = 0;
946128561Sphilip
947143894Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
948143894Sphilip	ACPI_SERIAL_ASSERT(asus);
949143894Sphilip
950143894Sphilip	switch (method) {
951143894Sphilip	case ACPI_ASUS_METHOD_BRN:
952143894Sphilip		val = sc->s_brn;
953143894Sphilip		break;
954143894Sphilip	case ACPI_ASUS_METHOD_DISP:
955143894Sphilip		val = sc->s_disp;
956143894Sphilip		break;
957143894Sphilip	case ACPI_ASUS_METHOD_LCD:
958143894Sphilip		val = sc->s_lcd;
959143894Sphilip		break;
960180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
961180062Srpaulo		val = sc->s_cam;
962180062Srpaulo		break;
963180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
964180062Srpaulo		val = sc->s_crd;
965180062Srpaulo		break;
966180268Srpaulo	case ACPI_ASUS_METHOD_WLAN:
967180268Srpaulo		val = sc->s_wlan;
968180268Srpaulo		break;
969128561Sphilip	}
970128561Sphilip
971143894Sphilip	return (val);
972128561Sphilip}
973128561Sphilip
974128561Sphilipstatic int
975143894Sphilipacpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
976128561Sphilip{
977180268Srpaulo	ACPI_STATUS		status = AE_OK;
978180268Srpaulo	ACPI_OBJECT_LIST 	acpiargs;
979186529Sstas	ACPI_OBJECT		acpiarg[1];
980128561Sphilip
981128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
982143894Sphilip	ACPI_SERIAL_ASSERT(asus);
983128561Sphilip
984180268Srpaulo	acpiargs.Count = 1;
985180268Srpaulo	acpiargs.Pointer = acpiarg;
986180268Srpaulo	acpiarg[0].Type = ACPI_TYPE_INTEGER;
987180268Srpaulo	acpiarg[0].Integer.Value = arg;
988180268Srpaulo
989143894Sphilip	switch (method) {
990143894Sphilip	case ACPI_ASUS_METHOD_BRN:
991143894Sphilip		if (arg < 0 || arg > 15)
992143894Sphilip			return (EINVAL);
993128561Sphilip
994143894Sphilip		if (sc->model->brn_set)
995143894Sphilip			status = acpi_SetInteger(sc->handle,
996143894Sphilip			    sc->model->brn_set, arg);
997143894Sphilip		else {
998143894Sphilip			while (arg != 0) {
999143894Sphilip				status = AcpiEvaluateObject(sc->handle,
1000143894Sphilip				    (arg > 0) ?  sc->model->brn_up :
1001143894Sphilip				    sc->model->brn_dn, NULL, NULL);
1002143894Sphilip				(arg > 0) ? arg-- : arg++;
1003143894Sphilip			}
1004143894Sphilip		}
1005128561Sphilip
1006143894Sphilip		if (ACPI_SUCCESS(status))
1007143894Sphilip			sc->s_brn = arg;
1008128561Sphilip
1009143894Sphilip		break;
1010143894Sphilip	case ACPI_ASUS_METHOD_DISP:
1011143894Sphilip		if (arg < 0 || arg > 7)
1012143894Sphilip			return (EINVAL);
1013128561Sphilip
1014143894Sphilip		status = acpi_SetInteger(sc->handle,
1015143894Sphilip		    sc->model->disp_set, arg);
1016128561Sphilip
1017143894Sphilip		if (ACPI_SUCCESS(status))
1018143894Sphilip			sc->s_disp = arg;
1019128561Sphilip
1020143894Sphilip		break;
1021143894Sphilip	case ACPI_ASUS_METHOD_LCD:
1022143894Sphilip		if (arg < 0 || arg > 1)
1023143894Sphilip			return (EINVAL);
1024143894Sphilip
1025143894Sphilip		if (strncmp(sc->model->name, "L3H", 3) != 0)
1026143894Sphilip			status = AcpiEvaluateObject(sc->handle,
1027143894Sphilip			    sc->model->lcd_set, NULL, NULL);
1028143894Sphilip		else
1029143894Sphilip			status = acpi_SetInteger(sc->handle,
1030143894Sphilip			    sc->model->lcd_set, 0x7);
1031143894Sphilip
1032143894Sphilip		if (ACPI_SUCCESS(status))
1033143894Sphilip			sc->s_lcd = arg;
1034143894Sphilip
1035143894Sphilip		break;
1036180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
1037180062Srpaulo		if (arg < 0 || arg > 1)
1038180062Srpaulo			return (EINVAL);
1039180062Srpaulo
1040180062Srpaulo		status = AcpiEvaluateObject(sc->handle,
1041180268Srpaulo		    sc->model->cam_set, &acpiargs, NULL);
1042180062Srpaulo
1043180062Srpaulo		if (ACPI_SUCCESS(status))
1044180062Srpaulo			sc->s_cam = arg;
1045180062Srpaulo		break;
1046180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
1047180062Srpaulo		if (arg < 0 || arg > 1)
1048180062Srpaulo			return (EINVAL);
1049180062Srpaulo
1050180062Srpaulo		status = AcpiEvaluateObject(sc->handle,
1051180268Srpaulo		    sc->model->crd_set, &acpiargs, NULL);
1052180062Srpaulo
1053180062Srpaulo		if (ACPI_SUCCESS(status))
1054180062Srpaulo			sc->s_crd = arg;
1055180062Srpaulo		break;
1056180268Srpaulo	case ACPI_ASUS_METHOD_WLAN:
1057180268Srpaulo		if (arg < 0 || arg > 1)
1058180268Srpaulo			return (EINVAL);
1059180268Srpaulo
1060180268Srpaulo		status = AcpiEvaluateObject(sc->handle,
1061180268Srpaulo		    sc->model->wlan_set, &acpiargs, NULL);
1062180268Srpaulo
1063180268Srpaulo		if (ACPI_SUCCESS(status))
1064180268Srpaulo			sc->s_wlan = arg;
1065180268Srpaulo		break;
1066143894Sphilip	}
1067143894Sphilip
1068143894Sphilip	return (0);
1069128561Sphilip}
1070128561Sphilip
1071128561Sphilipstatic int
1072143894Sphilipacpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
1073128561Sphilip{
1074143894Sphilip	ACPI_STATUS	status;
1075128561Sphilip
1076143894Sphilip	switch (method) {
1077143894Sphilip	case ACPI_ASUS_METHOD_BRN:
1078143894Sphilip		if (sc->model->brn_get) {
1079143894Sphilip			/* GPLV/SPLV models */
1080143894Sphilip			status = acpi_GetInteger(sc->handle,
1081143894Sphilip			    sc->model->brn_get, &sc->s_brn);
1082143894Sphilip			if (ACPI_SUCCESS(status))
1083143894Sphilip				return (TRUE);
1084143894Sphilip		} else if (sc->model->brn_up) {
1085143894Sphilip			/* Relative models */
1086143894Sphilip			status = AcpiEvaluateObject(sc->handle,
1087143894Sphilip			    sc->model->brn_up, NULL, NULL);
1088143894Sphilip			if (ACPI_FAILURE(status))
1089143894Sphilip				return (FALSE);
1090128561Sphilip
1091143894Sphilip			status = AcpiEvaluateObject(sc->handle,
1092143894Sphilip			    sc->model->brn_dn, NULL, NULL);
1093143894Sphilip			if (ACPI_FAILURE(status))
1094143894Sphilip				return (FALSE);
1095128561Sphilip
1096143894Sphilip			return (TRUE);
1097143894Sphilip		}
1098143894Sphilip		return (FALSE);
1099143894Sphilip	case ACPI_ASUS_METHOD_DISP:
1100143894Sphilip		if (sc->model->disp_get) {
1101143894Sphilip			status = acpi_GetInteger(sc->handle,
1102143894Sphilip			    sc->model->disp_get, &sc->s_disp);
1103143894Sphilip			if (ACPI_SUCCESS(status))
1104143894Sphilip				return (TRUE);
1105143894Sphilip		}
1106143894Sphilip		return (FALSE);
1107143894Sphilip	case ACPI_ASUS_METHOD_LCD:
1108178069Sjkim		if (sc->model->lcd_get) {
1109178069Sjkim			if (strncmp(sc->model->name, "G2K", 3) == 0) {
1110178069Sjkim				ACPI_BUFFER		Buf;
1111178069Sjkim				ACPI_OBJECT		Arg, Obj;
1112178069Sjkim				ACPI_OBJECT_LIST	Args;
1113128561Sphilip
1114178069Sjkim				Arg.Type = ACPI_TYPE_INTEGER;
1115178069Sjkim				Arg.Integer.Value = 0x11;
1116178069Sjkim				Args.Count = 1;
1117178069Sjkim				Args.Pointer = &Arg;
1118178069Sjkim				Buf.Length = sizeof(Obj);
1119178069Sjkim				Buf.Pointer = &Obj;
1120128561Sphilip
1121178069Sjkim				status = AcpiEvaluateObject(sc->handle,
1122178069Sjkim				    sc->model->lcd_get, &Args, &Buf);
1123178069Sjkim				if (ACPI_SUCCESS(status) &&
1124178069Sjkim				    Obj.Type == ACPI_TYPE_INTEGER) {
1125178069Sjkim					sc->s_lcd = Obj.Integer.Value;
1126178069Sjkim					return (TRUE);
1127178069Sjkim				}
1128178069Sjkim			} else if (strncmp(sc->model->name, "L3H", 3) == 0) {
1129178069Sjkim				ACPI_BUFFER		Buf;
1130178069Sjkim				ACPI_OBJECT		Arg[2], Obj;
1131178069Sjkim				ACPI_OBJECT_LIST	Args;
1132128561Sphilip
1133178069Sjkim				/* L3H is a bit special */
1134178069Sjkim				Arg[0].Type = ACPI_TYPE_INTEGER;
1135178069Sjkim				Arg[0].Integer.Value = 0x02;
1136178069Sjkim				Arg[1].Type = ACPI_TYPE_INTEGER;
1137178069Sjkim				Arg[1].Integer.Value = 0x03;
1138128561Sphilip
1139178069Sjkim				Args.Count = 2;
1140178069Sjkim				Args.Pointer = Arg;
1141178069Sjkim
1142178069Sjkim				Buf.Length = sizeof(Obj);
1143178069Sjkim				Buf.Pointer = &Obj;
1144178069Sjkim
1145178069Sjkim				status = AcpiEvaluateObject(sc->handle,
1146178069Sjkim				    sc->model->lcd_get, &Args, &Buf);
1147178069Sjkim				if (ACPI_SUCCESS(status) &&
1148178069Sjkim				    Obj.Type == ACPI_TYPE_INTEGER) {
1149178069Sjkim					sc->s_lcd = Obj.Integer.Value >> 8;
1150178069Sjkim					return (TRUE);
1151178069Sjkim				}
1152178069Sjkim			} else {
1153178069Sjkim				status = acpi_GetInteger(sc->handle,
1154178069Sjkim				    sc->model->lcd_get, &sc->s_lcd);
1155178069Sjkim				if (ACPI_SUCCESS(status))
1156178069Sjkim					return (TRUE);
1157143894Sphilip			}
1158143894Sphilip		}
1159143894Sphilip		return (FALSE);
1160180062Srpaulo	case ACPI_ASUS_METHOD_CAMERA:
1161180062Srpaulo		if (sc->model->cam_get) {
1162180062Srpaulo			status = acpi_GetInteger(sc->handle,
1163180062Srpaulo			    sc->model->cam_get, &sc->s_cam);
1164180062Srpaulo			if (ACPI_SUCCESS(status))
1165180062Srpaulo				return (TRUE);
1166180062Srpaulo		}
1167180062Srpaulo		return (FALSE);
1168180062Srpaulo	case ACPI_ASUS_METHOD_CARDRD:
1169180062Srpaulo		if (sc->model->crd_get) {
1170180062Srpaulo			status = acpi_GetInteger(sc->handle,
1171180062Srpaulo			    sc->model->crd_get, &sc->s_crd);
1172180062Srpaulo			if (ACPI_SUCCESS(status))
1173180062Srpaulo				return (TRUE);
1174180062Srpaulo		}
1175180062Srpaulo		return (FALSE);
1176180268Srpaulo	case ACPI_ASUS_METHOD_WLAN:
1177180268Srpaulo		if (sc->model->wlan_get) {
1178180268Srpaulo			status = acpi_GetInteger(sc->handle,
1179180268Srpaulo			    sc->model->wlan_get, &sc->s_wlan);
1180180268Srpaulo			if (ACPI_SUCCESS(status))
1181180268Srpaulo				return (TRUE);
1182180268Srpaulo		}
1183180268Srpaulo		return (FALSE);
1184143894Sphilip	}
1185143894Sphilip	return (FALSE);
1186128561Sphilip}
1187128561Sphilip
1188128561Sphilipstatic void
1189128561Sphilipacpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1190128561Sphilip{
1191128561Sphilip	struct acpi_asus_softc	*sc;
1192128561Sphilip	struct acpi_softc	*acpi_sc;
1193128561Sphilip
1194128561Sphilip	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1195128561Sphilip
1196128561Sphilip	sc = device_get_softc((device_t)context);
1197128561Sphilip	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1198128561Sphilip
1199133628Snjl	ACPI_SERIAL_BEGIN(asus);
1200128561Sphilip	if ((notify & ~0x10) <= 15) {
1201132610Snjl		sc->s_brn = notify & ~0x10;
1202128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1203128561Sphilip	} else if ((notify & ~0x20) <= 15) {
1204132610Snjl		sc->s_brn = notify & ~0x20;
1205128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1206128561Sphilip	} else if (notify == 0x33) {
1207128561Sphilip		sc->s_lcd = 1;
1208128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
1209128561Sphilip	} else if (notify == 0x34) {
1210128561Sphilip		sc->s_lcd = 0;
1211128561Sphilip		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
1212184625Srpaulo	} else if (notify == 0x86) {
1213184625Srpaulo		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1214184625Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1215184625Srpaulo	} else if (notify == 0x87) {
1216184625Srpaulo		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1217184625Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1218128561Sphilip	} else {
1219128561Sphilip		/* Notify devd(8) */
1220128561Sphilip		acpi_UserNotify("ASUS", h, notify);
1221128561Sphilip	}
1222133628Snjl	ACPI_SERIAL_END(asus);
1223128561Sphilip}
1224180062Srpaulo
1225180062Srpaulostatic void
1226184625Srpauloacpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1227184625Srpaulo{
1228184625Srpaulo	struct acpi_asus_softc	*sc;
1229184625Srpaulo	struct acpi_softc	*acpi_sc;
1230184625Srpaulo
1231184625Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1232184625Srpaulo
1233184625Srpaulo	sc = device_get_softc((device_t)context);
1234184625Srpaulo	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1235184625Srpaulo
1236184625Srpaulo	ACPI_SERIAL_BEGIN(asus);
1237184625Srpaulo	switch (notify) {
1238184625Srpaulo	case 0x87:
1239184625Srpaulo		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1240184625Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1241184625Srpaulo		break;
1242184625Srpaulo	case 0x86:
1243184625Srpaulo		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1244184625Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1245184625Srpaulo		break;
1246184625Srpaulo	}
1247184625Srpaulo	ACPI_SERIAL_END(asus);
1248184625Srpaulo}
1249184625Srpaulo
1250184625Srpaulostatic void
1251180062Srpauloacpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1252180062Srpaulo{
1253180062Srpaulo	struct acpi_asus_softc	*sc;
1254180062Srpaulo	struct acpi_softc	*acpi_sc;
1255180062Srpaulo
1256180062Srpaulo	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1257180062Srpaulo
1258180062Srpaulo	sc = device_get_softc((device_t)context);
1259180062Srpaulo	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1260180062Srpaulo
1261180062Srpaulo	ACPI_SERIAL_BEGIN(asus);
1262180062Srpaulo	if ((notify & ~0x20) <= 15) {
1263180062Srpaulo		sc->s_brn = notify & ~0x20;
1264180062Srpaulo		ACPI_VPRINT(sc->dev, acpi_sc,
1265180062Srpaulo		    "Brightness increased/decreased\n");
1266180062Srpaulo	} else {
1267180062Srpaulo		/* Notify devd(8) */
1268180062Srpaulo		acpi_UserNotify("ASUS-Eee", h, notify);
1269180062Srpaulo	}
1270180062Srpaulo	ACPI_SERIAL_END(asus);
1271180062Srpaulo}
1272