xref: /freebsd-src/sys/dev/acpi_support/acpi_asus.c (revision bad36a49985c3cd7bfcb1b35ce3ae37f007843ce)
19a1fc77eSPhilip Paeps /*-
218d92cd8SPhilip Paeps  * Copyright (c) 2004, 2005 Philip Paeps <philip@FreeBSD.org>
39a1fc77eSPhilip Paeps  * All rights reserved.
49a1fc77eSPhilip Paeps  *
59a1fc77eSPhilip Paeps  * Redistribution and use in source and binary forms, with or without
69a1fc77eSPhilip Paeps  * modification, are permitted provided that the following conditions
79a1fc77eSPhilip Paeps  * are met:
89a1fc77eSPhilip Paeps  * 1. Redistributions of source code must retain the above copyright
99a1fc77eSPhilip Paeps  *    notice, this list of conditions and the following disclaimer.
109a1fc77eSPhilip Paeps  * 2. Redistributions in binary form must reproduce the above copyright
119a1fc77eSPhilip Paeps  *    notice, this list of conditions and the following disclaimer in the
129a1fc77eSPhilip Paeps  *    documentation and/or other materials provided with the distribution.
139a1fc77eSPhilip Paeps  *
149a1fc77eSPhilip Paeps  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159a1fc77eSPhilip Paeps  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169a1fc77eSPhilip Paeps  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179a1fc77eSPhilip Paeps  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189a1fc77eSPhilip Paeps  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199a1fc77eSPhilip Paeps  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209a1fc77eSPhilip Paeps  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219a1fc77eSPhilip Paeps  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229a1fc77eSPhilip Paeps  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239a1fc77eSPhilip Paeps  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249a1fc77eSPhilip Paeps  * SUCH DAMAGE.
259a1fc77eSPhilip Paeps  */
269a1fc77eSPhilip Paeps 
279a1fc77eSPhilip Paeps #include <sys/cdefs.h>
289a1fc77eSPhilip Paeps /*
299a1fc77eSPhilip Paeps  * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
30f5296c93SPhilip Paeps  * recent Asus (and Medion) laptops.  Inspired by the acpi4asus project which
319a1fc77eSPhilip Paeps  * implements these features in the Linux kernel.
329a1fc77eSPhilip Paeps  *
339a1fc77eSPhilip Paeps  *   <http://sourceforge.net/projects/acpi4asus/>
349a1fc77eSPhilip Paeps  *
359a1fc77eSPhilip Paeps  * Currently should support most features, but could use some more testing.
369a1fc77eSPhilip Paeps  * Particularly the display-switching stuff is a bit hairy.  If you have an
379a1fc77eSPhilip Paeps  * Asus laptop which doesn't appear to be supported, or strange things happen
389a1fc77eSPhilip Paeps  * when using this driver, please report to <acpi@FreeBSD.org>.
399a1fc77eSPhilip Paeps  */
409a1fc77eSPhilip Paeps 
419a1fc77eSPhilip Paeps #include "opt_acpi.h"
429a1fc77eSPhilip Paeps #include <sys/param.h>
439a1fc77eSPhilip Paeps #include <sys/kernel.h>
4477409fe1SPoul-Henning Kamp #include <sys/module.h>
459a1fc77eSPhilip Paeps #include <sys/bus.h>
469a1fc77eSPhilip Paeps 
47129d3046SJung-uk Kim #include <contrib/dev/acpica/include/acpi.h>
48129d3046SJung-uk Kim #include <contrib/dev/acpica/include/accommon.h>
49129d3046SJung-uk Kim 
509a1fc77eSPhilip Paeps #include <dev/acpica/acpivar.h>
519a1fc77eSPhilip Paeps #include <dev/led/led.h>
529a1fc77eSPhilip Paeps 
53d8e0b9e1SPhilip Paeps /* Methods */
54d8e0b9e1SPhilip Paeps #define ACPI_ASUS_METHOD_BRN	1
55d8e0b9e1SPhilip Paeps #define ACPI_ASUS_METHOD_DISP	2
56d8e0b9e1SPhilip Paeps #define ACPI_ASUS_METHOD_LCD	3
57aea61887SRui Paulo #define ACPI_ASUS_METHOD_CAMERA	4
58aea61887SRui Paulo #define ACPI_ASUS_METHOD_CARDRD	5
590fc34be4SRui Paulo #define ACPI_ASUS_METHOD_WLAN	6
60d8e0b9e1SPhilip Paeps 
61276cd921SNate Lawson #define _COMPONENT	ACPI_OEM
629a1fc77eSPhilip Paeps ACPI_MODULE_NAME("ASUS")
639a1fc77eSPhilip Paeps 
649a1fc77eSPhilip Paeps struct acpi_asus_model {
659a1fc77eSPhilip Paeps 	char	*name;
669a1fc77eSPhilip Paeps 
67caac4996SPhilip Paeps 	char	*bled_set;
68824d24a8SJung-uk Kim 	char	*dled_set;
69824d24a8SJung-uk Kim 	char	*gled_set;
709a1fc77eSPhilip Paeps 	char	*mled_set;
719a1fc77eSPhilip Paeps 	char	*tled_set;
729a1fc77eSPhilip Paeps 	char	*wled_set;
739a1fc77eSPhilip Paeps 
749a1fc77eSPhilip Paeps 	char	*brn_get;
759a1fc77eSPhilip Paeps 	char	*brn_set;
769a1fc77eSPhilip Paeps 	char	*brn_up;
779a1fc77eSPhilip Paeps 	char	*brn_dn;
789a1fc77eSPhilip Paeps 
799a1fc77eSPhilip Paeps 	char	*lcd_get;
809a1fc77eSPhilip Paeps 	char	*lcd_set;
819a1fc77eSPhilip Paeps 
829a1fc77eSPhilip Paeps 	char	*disp_get;
839a1fc77eSPhilip Paeps 	char	*disp_set;
84aea61887SRui Paulo 
85aea61887SRui Paulo 	char	*cam_get;
86aea61887SRui Paulo 	char	*cam_set;
87aea61887SRui Paulo 
88aea61887SRui Paulo 	char	*crd_get;
89aea61887SRui Paulo 	char	*crd_set;
90aea61887SRui Paulo 
910fc34be4SRui Paulo 	char	*wlan_get;
920fc34be4SRui Paulo 	char	*wlan_set;
930fc34be4SRui Paulo 
94aea61887SRui Paulo 	void	(*n_func)(ACPI_HANDLE, UINT32, void *);
9531fb9906SRui Paulo 
9631fb9906SRui Paulo 	char	*lcdd;
9731fb9906SRui Paulo 	void	(*lcdd_n_func)(ACPI_HANDLE, UINT32, void *);
989a1fc77eSPhilip Paeps };
999a1fc77eSPhilip Paeps 
100f5296c93SPhilip Paeps struct acpi_asus_led {
1014d2743aeSPhilip Paeps 	struct acpi_asus_softc *sc;
102f5296c93SPhilip Paeps 	struct cdev	*cdev;
1034d2743aeSPhilip Paeps 	int		busy;
1044d2743aeSPhilip Paeps 	int		state;
105f5296c93SPhilip Paeps 	enum {
106caac4996SPhilip Paeps 		ACPI_ASUS_LED_BLED,
107824d24a8SJung-uk Kim 		ACPI_ASUS_LED_DLED,
108824d24a8SJung-uk Kim 		ACPI_ASUS_LED_GLED,
109f5296c93SPhilip Paeps 		ACPI_ASUS_LED_MLED,
110f5296c93SPhilip Paeps 		ACPI_ASUS_LED_TLED,
111f5296c93SPhilip Paeps 		ACPI_ASUS_LED_WLED,
112f5296c93SPhilip Paeps 	} type;
113f5296c93SPhilip Paeps };
114f5296c93SPhilip Paeps 
1159a1fc77eSPhilip Paeps struct acpi_asus_softc {
1169a1fc77eSPhilip Paeps 	device_t		dev;
1179a1fc77eSPhilip Paeps 	ACPI_HANDLE		handle;
11831fb9906SRui Paulo 	ACPI_HANDLE		lcdd_handle;
1199a1fc77eSPhilip Paeps 
1209a1fc77eSPhilip Paeps 	struct acpi_asus_model	*model;
1219a1fc77eSPhilip Paeps 	struct sysctl_ctx_list	sysctl_ctx;
1229a1fc77eSPhilip Paeps 	struct sysctl_oid	*sysctl_tree;
1239a1fc77eSPhilip Paeps 
124caac4996SPhilip Paeps 	struct acpi_asus_led	s_bled;
125824d24a8SJung-uk Kim 	struct acpi_asus_led	s_dled;
126824d24a8SJung-uk Kim 	struct acpi_asus_led	s_gled;
127f5296c93SPhilip Paeps 	struct acpi_asus_led	s_mled;
128f5296c93SPhilip Paeps 	struct acpi_asus_led	s_tled;
129f5296c93SPhilip Paeps 	struct acpi_asus_led	s_wled;
1309a1fc77eSPhilip Paeps 
1319a1fc77eSPhilip Paeps 	int			s_brn;
1329a1fc77eSPhilip Paeps 	int			s_disp;
1339a1fc77eSPhilip Paeps 	int			s_lcd;
134aea61887SRui Paulo 	int			s_cam;
135aea61887SRui Paulo 	int			s_crd;
1360fc34be4SRui Paulo 	int			s_wlan;
1379a1fc77eSPhilip Paeps };
1389a1fc77eSPhilip Paeps 
13931fb9906SRui Paulo static void	acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
14031fb9906SRui Paulo     void *context);
14131fb9906SRui Paulo 
142706bd68cSPhilip Paeps /*
143706bd68cSPhilip Paeps  * We can identify Asus laptops from the string they return
144706bd68cSPhilip Paeps  * as a result of calling the ATK0100 'INIT' method.
145706bd68cSPhilip Paeps  */
1469a1fc77eSPhilip Paeps static struct acpi_asus_model acpi_asus_models[] = {
1479a1fc77eSPhilip Paeps 	{
14818d92cd8SPhilip Paeps 		.name		= "xxN",
14918d92cd8SPhilip Paeps 		.mled_set	= "MLED",
15018d92cd8SPhilip Paeps 		.wled_set	= "WLED",
15118d92cd8SPhilip Paeps 		.lcd_get	= "\\BKLT",
15218d92cd8SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
15318d92cd8SPhilip Paeps 		.brn_get	= "GPLV",
15418d92cd8SPhilip Paeps 		.brn_set	= "SPLV",
15518d92cd8SPhilip Paeps 		.disp_get	= "\\ADVG",
15618d92cd8SPhilip Paeps 		.disp_set	= "SDSP"
15718d92cd8SPhilip Paeps 	},
15818d92cd8SPhilip Paeps 	{
15918d92cd8SPhilip Paeps 		.name		= "A1x",
16018d92cd8SPhilip Paeps 		.mled_set	= "MLED",
16118d92cd8SPhilip Paeps 		.lcd_get	= "\\BKLI",
16218d92cd8SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
16318d92cd8SPhilip Paeps 		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0E",
16418d92cd8SPhilip Paeps 		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0F"
16518d92cd8SPhilip Paeps 	},
16618d92cd8SPhilip Paeps 	{
16718d92cd8SPhilip Paeps 		.name		= "A2x",
16818d92cd8SPhilip Paeps 		.mled_set	= "MLED",
16918d92cd8SPhilip Paeps 		.wled_set	= "WLED",
17018d92cd8SPhilip Paeps 		.lcd_get	= "\\BAOF",
17118d92cd8SPhilip Paeps 		.lcd_set	= "\\Q10",
17218d92cd8SPhilip Paeps 		.brn_get	= "GPLV",
17318d92cd8SPhilip Paeps 		.brn_set	= "SPLV",
17418d92cd8SPhilip Paeps 		.disp_get	= "\\INFB",
17518d92cd8SPhilip Paeps 		.disp_set	= "SDSP"
17618d92cd8SPhilip Paeps 	},
17718d92cd8SPhilip Paeps 	{
1784060f1bbSAttilio Rao 		.name           = "A3E",
1794060f1bbSAttilio Rao 		.mled_set       = "MLED",
1804060f1bbSAttilio Rao 		.wled_set       = "WLED",
1814060f1bbSAttilio Rao 		.lcd_get        = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)",
1824060f1bbSAttilio Rao 		.lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
1834060f1bbSAttilio Rao 		.brn_get        = "GPLV",
1844060f1bbSAttilio Rao 		.brn_set        = "SPLV",
1854060f1bbSAttilio Rao 		.disp_get       = "\\_SB.PCI0.P0P2.VGA.GETD",
1864060f1bbSAttilio Rao 		.disp_set       = "SDSP"
1874060f1bbSAttilio Rao 	},
1884060f1bbSAttilio Rao 	{
1894060f1bbSAttilio Rao 		.name           = "A3F",
1904060f1bbSAttilio Rao 		.mled_set       = "MLED",
1914060f1bbSAttilio Rao 		.wled_set       = "WLED",
1924060f1bbSAttilio Rao 		.bled_set       = "BLED",
1934060f1bbSAttilio Rao 		.lcd_get        = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)",
1944060f1bbSAttilio Rao 		.lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
1954060f1bbSAttilio Rao 		.brn_get        = "GPLV",
1964060f1bbSAttilio Rao 		.brn_set        = "SPLV",
1974060f1bbSAttilio Rao 		.disp_get       = "\\SSTE",
1984060f1bbSAttilio Rao 		.disp_set       = "SDSP"
1994060f1bbSAttilio Rao 	},
2004060f1bbSAttilio Rao 	{
20118d725a0SPhilip Paeps 		.name           = "A3N",
20218d725a0SPhilip Paeps 		.mled_set       = "MLED",
20318d725a0SPhilip Paeps 		.bled_set       = "BLED",
20418d725a0SPhilip Paeps 		.wled_set       = "WLED",
2054060f1bbSAttilio Rao 		.lcd_get        = "\\BKLT",
20618d725a0SPhilip Paeps 		.lcd_set        = "\\_SB.PCI0.SBRG.EC0._Q10",
2074060f1bbSAttilio Rao 		.brn_get        = "GPLV",
20818d725a0SPhilip Paeps 		.brn_set        = "SPLV",
2094060f1bbSAttilio Rao 		.disp_get       = "\\_SB.PCI0.P0P3.VGA.GETD",
2104060f1bbSAttilio Rao 		.disp_set       = "SDSP"
21118d725a0SPhilip Paeps 	},
21218d725a0SPhilip Paeps 	{
213f50bf401SPhilip Paeps 		.name		= "A4D",
214f50bf401SPhilip Paeps 		.mled_set	= "MLED",
215f50bf401SPhilip Paeps 		.brn_up		= "\\_SB_.PCI0.SBRG.EC0._Q0E",
216f50bf401SPhilip Paeps 		.brn_dn		= "\\_SB_.PCI0.SBRG.EC0._Q0F",
217f50bf401SPhilip Paeps 		.brn_get	= "GPLV",
218f50bf401SPhilip Paeps 		.brn_set	= "SPLV",
219f50bf401SPhilip Paeps #ifdef notyet
220f50bf401SPhilip Paeps 		.disp_get	= "\\_SB_.PCI0.SBRG.EC0._Q10",
221f50bf401SPhilip Paeps 		.disp_set	= "\\_SB_.PCI0.SBRG.EC0._Q11"
222f50bf401SPhilip Paeps #endif
223f50bf401SPhilip Paeps 	},
224f50bf401SPhilip Paeps 	{
225e691fe45SPhilip Paeps 		.name		= "A6V",
226e691fe45SPhilip Paeps 		.bled_set	= "BLED",
227e691fe45SPhilip Paeps 		.mled_set	= "MLED",
228e691fe45SPhilip Paeps 		.wled_set	= "WLED",
229e691fe45SPhilip Paeps 		.lcd_get	= NULL,
230e691fe45SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
231e691fe45SPhilip Paeps 		.brn_get	= "GPLV",
232e691fe45SPhilip Paeps 		.brn_set	= "SPLV",
233e691fe45SPhilip Paeps 		.disp_get	= "\\_SB.PCI0.P0P3.VGA.GETD",
234e691fe45SPhilip Paeps 		.disp_set	= "SDSP"
235e691fe45SPhilip Paeps 	},
236e691fe45SPhilip Paeps 	{
23731fb9906SRui Paulo 		.name		= "A8SR",
23831fb9906SRui Paulo 		.bled_set	= "BLED",
23931fb9906SRui Paulo 		.mled_set	= "MLED",
24031fb9906SRui Paulo 		.wled_set	= "WLED",
24131fb9906SRui Paulo 		.lcd_get	= NULL,
24231fb9906SRui Paulo 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
24331fb9906SRui Paulo 		.brn_get	= "GPLV",
24431fb9906SRui Paulo 		.brn_set	= "SPLV",
24531fb9906SRui Paulo 		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
24631fb9906SRui Paulo 		.disp_set	= "SDSP",
24731fb9906SRui Paulo 		.lcdd		= "\\_SB.PCI0.P0P1.VGA.LCDD",
24831fb9906SRui Paulo 		.lcdd_n_func	= acpi_asus_lcdd_notify
24931fb9906SRui Paulo 	},
25031fb9906SRui Paulo 	{
25118d92cd8SPhilip Paeps 		.name		= "D1x",
25218d92cd8SPhilip Paeps 		.mled_set	= "MLED",
25318d92cd8SPhilip Paeps 		.lcd_get	= "\\GP11",
25418d92cd8SPhilip Paeps 		.lcd_set	= "\\Q0D",
25518d92cd8SPhilip Paeps 		.brn_up		= "\\Q0C",
25618d92cd8SPhilip Paeps 		.brn_dn		= "\\Q0B",
25718d92cd8SPhilip Paeps 		.disp_get	= "\\INFB",
25818d92cd8SPhilip Paeps 		.disp_set	= "SDSP"
25918d92cd8SPhilip Paeps 	},
26018d92cd8SPhilip Paeps 	{
261824d24a8SJung-uk Kim 		.name		= "G2K",
262824d24a8SJung-uk Kim 		.bled_set	= "BLED",
263824d24a8SJung-uk Kim 		.dled_set	= "DLED",
264824d24a8SJung-uk Kim 		.gled_set	= "GLED",
265824d24a8SJung-uk Kim 		.mled_set	= "MLED",
266824d24a8SJung-uk Kim 		.tled_set	= "TLED",
267824d24a8SJung-uk Kim 		.wled_set	= "WLED",
268824d24a8SJung-uk Kim 		.brn_get	= "GPLV",
269824d24a8SJung-uk Kim 		.brn_set	= "SPLV",
270f67f8ffdSJung-uk Kim 		.lcd_get	= "GBTL",
271f67f8ffdSJung-uk Kim 		.lcd_set	= "SBTL",
272824d24a8SJung-uk Kim 		.disp_get	= "\\_SB.PCI0.PCE2.VGA.GETD",
273824d24a8SJung-uk Kim 		.disp_set	= "SDSP",
274824d24a8SJung-uk Kim 	},
275824d24a8SJung-uk Kim 	{
2769a1fc77eSPhilip Paeps 		.name		= "L2D",
2779a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
2789a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
2799a1fc77eSPhilip Paeps 		.brn_up		= "\\Q0E",
2809a1fc77eSPhilip Paeps 		.brn_dn		= "\\Q0F",
2819a1fc77eSPhilip Paeps 		.lcd_get	= "\\SGP0",
2829a1fc77eSPhilip Paeps 		.lcd_set	= "\\Q10"
2839a1fc77eSPhilip Paeps 	},
2849a1fc77eSPhilip Paeps 	{
2859a1fc77eSPhilip Paeps 		.name		= "L3C",
2869a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
2879a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
2889a1fc77eSPhilip Paeps 		.brn_get	= "GPLV",
2899a1fc77eSPhilip Paeps 		.brn_set	= "SPLV",
2909a1fc77eSPhilip Paeps 		.lcd_get	= "\\GL32",
2919a1fc77eSPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.PX40.ECD0._Q10"
2929a1fc77eSPhilip Paeps 	},
2939a1fc77eSPhilip Paeps 	{
2949a1fc77eSPhilip Paeps 		.name		= "L3D",
2959a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
2969a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
2979a1fc77eSPhilip Paeps 		.brn_get	= "GPLV",
2989a1fc77eSPhilip Paeps 		.brn_set	= "SPLV",
2999a1fc77eSPhilip Paeps 		.lcd_get	= "\\BKLG",
3009a1fc77eSPhilip Paeps 		.lcd_set	= "\\Q10"
3019a1fc77eSPhilip Paeps 	},
3029a1fc77eSPhilip Paeps 	{
3039a1fc77eSPhilip Paeps 		.name		= "L3H",
3049a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
3059a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
3069a1fc77eSPhilip Paeps 		.brn_get	= "GPLV",
3079a1fc77eSPhilip Paeps 		.brn_set	= "SPLV",
3089a1fc77eSPhilip Paeps 		.lcd_get	= "\\_SB.PCI0.PM.PBC",
3099a1fc77eSPhilip Paeps 		.lcd_set	= "EHK",
3109a1fc77eSPhilip Paeps 		.disp_get	= "\\_SB.INFB",
3119a1fc77eSPhilip Paeps 		.disp_set	= "SDSP"
3129a1fc77eSPhilip Paeps 	},
3139a1fc77eSPhilip Paeps 	{
3149923cf22SPhilip Paeps 		.name		= "L4R",
3159923cf22SPhilip Paeps 		.mled_set	= "MLED",
3169923cf22SPhilip Paeps 		.wled_set	= "WLED",
3179923cf22SPhilip Paeps 		.brn_get	= "GPLV",
3189923cf22SPhilip Paeps 		.brn_set	= "SPLV",
3199923cf22SPhilip Paeps 		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
3209923cf22SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
3219923cf22SPhilip Paeps 		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
3229923cf22SPhilip Paeps 		.disp_set	= "SDSP"
3239923cf22SPhilip Paeps 	},
3249923cf22SPhilip Paeps 	{
32518d92cd8SPhilip Paeps 		.name		= "L5x",
32618d92cd8SPhilip Paeps 		.mled_set	= "MLED",
32718d92cd8SPhilip Paeps 		.tled_set	= "TLED",
32818d92cd8SPhilip Paeps 		.lcd_get	= "\\BAOF",
32918d92cd8SPhilip Paeps 		.lcd_set	= "\\Q0D",
33018d92cd8SPhilip Paeps 		.brn_get	= "GPLV",
33118d92cd8SPhilip Paeps 		.brn_set	= "SPLV",
33218d92cd8SPhilip Paeps 		.disp_get	= "\\INFB",
33318d92cd8SPhilip Paeps 		.disp_set	= "SDSP"
33418d92cd8SPhilip Paeps 	},
33518d92cd8SPhilip Paeps 	{
3369a1fc77eSPhilip Paeps 		.name		= "L8L"
337de326158SRui Paulo 		/* Only has hotkeys, apparently */
3389a1fc77eSPhilip Paeps 	},
3399a1fc77eSPhilip Paeps 	{
3409a1fc77eSPhilip Paeps 		.name		= "M1A",
3419a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
3429a1fc77eSPhilip Paeps 		.brn_up		= "\\_SB.PCI0.PX40.EC0.Q0E",
3439a1fc77eSPhilip Paeps 		.brn_dn		= "\\_SB.PCI0.PX40.EC0.Q0F",
3449a1fc77eSPhilip Paeps 		.lcd_get	= "\\PNOF",
3459a1fc77eSPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.PX40.EC0.Q10"
3469a1fc77eSPhilip Paeps 	},
3479a1fc77eSPhilip Paeps 	{
3489a1fc77eSPhilip Paeps 		.name		= "M2E",
3499a1fc77eSPhilip Paeps 		.mled_set	= "MLED",
3509a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
3519a1fc77eSPhilip Paeps 		.brn_get	= "GPLV",
3529a1fc77eSPhilip Paeps 		.brn_set	= "SPLV",
3539a1fc77eSPhilip Paeps 		.lcd_get	= "\\GP06",
3549a1fc77eSPhilip Paeps 		.lcd_set	= "\\Q10"
3559a1fc77eSPhilip Paeps 	},
3569a1fc77eSPhilip Paeps 	{
35717520557SPhilip Paeps 		.name		= "M6N",
35817520557SPhilip Paeps 		.mled_set	= "MLED",
35917520557SPhilip Paeps 		.wled_set	= "WLED",
36017520557SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
36117520557SPhilip Paeps 		.lcd_get	= "\\_SB.BKLT",
36217520557SPhilip Paeps 		.brn_set	= "SPLV",
36317520557SPhilip Paeps 		.brn_get	= "GPLV",
36417520557SPhilip Paeps 		.disp_set	= "SDSP",
36517520557SPhilip Paeps 		.disp_get	= "\\SSTE"
36617520557SPhilip Paeps 	},
3679923cf22SPhilip Paeps 	{
3689923cf22SPhilip Paeps 		.name		= "M6R",
3699923cf22SPhilip Paeps 		.mled_set	= "MLED",
3709923cf22SPhilip Paeps 		.wled_set	= "WLED",
3719923cf22SPhilip Paeps 		.brn_get	= "GPLV",
3729923cf22SPhilip Paeps 		.brn_set	= "SPLV",
3739923cf22SPhilip Paeps 		.lcd_get	= "\\_SB.PCI0.SBSM.SEO4",
3749923cf22SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
3759923cf22SPhilip Paeps 		.disp_get	= "\\SSTE",
3769923cf22SPhilip Paeps 		.disp_set	= "SDSP"
3779923cf22SPhilip Paeps 	},
378caac4996SPhilip Paeps 	{
37918d92cd8SPhilip Paeps 		.name		= "S1x",
38018d92cd8SPhilip Paeps 		.mled_set	= "MLED",
38118d92cd8SPhilip Paeps 		.wled_set	= "WLED",
38218d92cd8SPhilip Paeps 		.lcd_get	= "\\PNOF",
38318d92cd8SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.PX40.Q10",
38418d92cd8SPhilip Paeps 		.brn_get	= "GPLV",
38518d92cd8SPhilip Paeps 		.brn_set	= "SPLV"
38618d92cd8SPhilip Paeps 	},
38718d92cd8SPhilip Paeps 	{
38818d92cd8SPhilip Paeps 		.name		= "S2x",
38918d92cd8SPhilip Paeps 		.mled_set	= "MLED",
39018d92cd8SPhilip Paeps 		.lcd_get	= "\\BKLI",
39118d92cd8SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.ISA.EC0._Q10",
39218d92cd8SPhilip Paeps 		.brn_up		= "\\_SB.PCI0.ISA.EC0._Q0B",
39318d92cd8SPhilip Paeps 		.brn_dn		= "\\_SB.PCI0.ISA.EC0._Q0A"
39418d92cd8SPhilip Paeps 	},
39518d92cd8SPhilip Paeps 	{
396caac4996SPhilip Paeps 		.name		= "V6V",
397caac4996SPhilip Paeps 		.bled_set	= "BLED",
398caac4996SPhilip Paeps 		.tled_set	= "TLED",
399caac4996SPhilip Paeps 		.wled_set	= "WLED",
400caac4996SPhilip Paeps 		.lcd_get	= "\\BKLT",
401caac4996SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
402caac4996SPhilip Paeps 		.brn_get	= "GPLV",
403caac4996SPhilip Paeps 		.brn_set	= "SPLV",
404caac4996SPhilip Paeps 		.disp_get	= "\\_SB.PCI0.P0P1.VGA.GETD",
405caac4996SPhilip Paeps 		.disp_set	= "SDSP"
406caac4996SPhilip Paeps 	},
407e13d4201SPhilip Paeps 	{
408e13d4201SPhilip Paeps 		.name		= "W5A",
409e13d4201SPhilip Paeps 		.bled_set	= "BLED",
410e13d4201SPhilip Paeps 		.lcd_get	= "\\BKLT",
411e13d4201SPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.SBRG.EC0._Q10",
412e13d4201SPhilip Paeps 		.brn_get	= "GPLV",
413e13d4201SPhilip Paeps 		.brn_set	= "SPLV",
414e13d4201SPhilip Paeps 		.disp_get	= "\\_SB.PCI0.P0P2.VGA.GETD",
415e13d4201SPhilip Paeps 		.disp_set	= "SDSP"
416e13d4201SPhilip Paeps 	},
417706bd68cSPhilip Paeps 	{ .name = NULL }
418706bd68cSPhilip Paeps };
419706bd68cSPhilip Paeps 
420706bd68cSPhilip Paeps /*
421706bd68cSPhilip Paeps  * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface,
422706bd68cSPhilip Paeps  * but they can't be probed quite the same way as Asus laptops.
423706bd68cSPhilip Paeps  */
424706bd68cSPhilip Paeps static struct acpi_asus_model acpi_samsung_models[] = {
42517520557SPhilip Paeps 	{
4269a1fc77eSPhilip Paeps 		.name		= "P30",
4279a1fc77eSPhilip Paeps 		.wled_set	= "WLED",
4289a1fc77eSPhilip Paeps 		.brn_up		= "\\_SB.PCI0.LPCB.EC0._Q68",
4299a1fc77eSPhilip Paeps 		.brn_dn		= "\\_SB.PCI0.LPCB.EC0._Q69",
4309a1fc77eSPhilip Paeps 		.lcd_get	= "\\BKLT",
4319a1fc77eSPhilip Paeps 		.lcd_set	= "\\_SB.PCI0.LPCB.EC0._Q0E"
4329a1fc77eSPhilip Paeps 	},
4339a1fc77eSPhilip Paeps 	{ .name = NULL }
4349a1fc77eSPhilip Paeps };
4359a1fc77eSPhilip Paeps 
436aea61887SRui Paulo static void	acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
437aea61887SRui Paulo 
438fea0f7ccSRui Paulo /*
439fea0f7ccSRui Paulo  * EeePC have an Asus ASUS010 gadget interface,
440fea0f7ccSRui Paulo  * but they can't be probed quite the same way as Asus laptops.
441fea0f7ccSRui Paulo  */
442fea0f7ccSRui Paulo static struct acpi_asus_model acpi_eeepc_models[] = {
443fea0f7ccSRui Paulo 	{
444fea0f7ccSRui Paulo 		.name		= "EEE",
445fea0f7ccSRui Paulo 		.brn_get	= "\\_SB.ATKD.PBLG",
446aea61887SRui Paulo 		.brn_set	= "\\_SB.ATKD.PBLS",
447aea61887SRui Paulo 		.cam_get	= "\\_SB.ATKD.CAMG",
448aea61887SRui Paulo 		.cam_set	= "\\_SB.ATKD.CAMS",
449aea61887SRui Paulo 		.crd_set	= "\\_SB.ATKD.CRDS",
450aea61887SRui Paulo 		.crd_get	= "\\_SB.ATKD.CRDG",
4510fc34be4SRui Paulo 		.wlan_get	= "\\_SB.ATKD.WLDG",
4520fc34be4SRui Paulo 		.wlan_set	= "\\_SB.ATKD.WLDS",
453aea61887SRui Paulo 		.n_func		= acpi_asus_eeepc_notify
454fea0f7ccSRui Paulo 	},
455fea0f7ccSRui Paulo 	{ .name = NULL }
456fea0f7ccSRui Paulo };
457fea0f7ccSRui Paulo 
458d8e0b9e1SPhilip Paeps static struct {
459d8e0b9e1SPhilip Paeps 	char	*name;
460d8e0b9e1SPhilip Paeps 	char	*description;
461d8e0b9e1SPhilip Paeps 	int	method;
462f0188618SHans Petter Selasky 	int	flag_anybody;
463d8e0b9e1SPhilip Paeps } acpi_asus_sysctls[] = {
464d8e0b9e1SPhilip Paeps 	{
465d8e0b9e1SPhilip Paeps 		.name		= "lcd_backlight",
466d8e0b9e1SPhilip Paeps 		.method		= ACPI_ASUS_METHOD_LCD,
467aea61887SRui Paulo 		.description	= "state of the lcd backlight",
468f0188618SHans Petter Selasky 		.flag_anybody	= 1
469d8e0b9e1SPhilip Paeps 	},
470d8e0b9e1SPhilip Paeps 	{
471d8e0b9e1SPhilip Paeps 		.name		= "lcd_brightness",
472d8e0b9e1SPhilip Paeps 		.method		= ACPI_ASUS_METHOD_BRN,
473aea61887SRui Paulo 		.description	= "brightness of the lcd panel",
474f0188618SHans Petter Selasky 		.flag_anybody	= 1
475d8e0b9e1SPhilip Paeps 	},
476d8e0b9e1SPhilip Paeps 	{
477d8e0b9e1SPhilip Paeps 		.name		= "video_output",
478d8e0b9e1SPhilip Paeps 		.method		= ACPI_ASUS_METHOD_DISP,
479aea61887SRui Paulo 		.description	= "display output state",
480aea61887SRui Paulo 	},
481aea61887SRui Paulo 	{
482aea61887SRui Paulo 		.name		= "camera",
483aea61887SRui Paulo 		.method		= ACPI_ASUS_METHOD_CAMERA,
484aea61887SRui Paulo 		.description	= "internal camera state",
485aea61887SRui Paulo 	},
486aea61887SRui Paulo 	{
487aea61887SRui Paulo 		.name		= "cardreader",
488aea61887SRui Paulo 		.method		= ACPI_ASUS_METHOD_CARDRD,
489aea61887SRui Paulo 		.description	= "internal card reader state",
490d8e0b9e1SPhilip Paeps 	},
4910fc34be4SRui Paulo 	{
4920fc34be4SRui Paulo 		.name		= "wlan",
4930fc34be4SRui Paulo 		.method		= ACPI_ASUS_METHOD_WLAN,
4940fc34be4SRui Paulo 		.description	= "wireless lan state",
4950fc34be4SRui Paulo 	},
496d8e0b9e1SPhilip Paeps 	{ .name = NULL }
497d8e0b9e1SPhilip Paeps };
498d8e0b9e1SPhilip Paeps 
4991051a7c2SNate Lawson ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
5001051a7c2SNate Lawson 
5019a1fc77eSPhilip Paeps /* Function prototypes */
5029a1fc77eSPhilip Paeps static int	acpi_asus_probe(device_t dev);
5039a1fc77eSPhilip Paeps static int	acpi_asus_attach(device_t dev);
5049a1fc77eSPhilip Paeps static int	acpi_asus_detach(device_t dev);
5059a1fc77eSPhilip Paeps 
506f5296c93SPhilip Paeps static void	acpi_asus_led(struct acpi_asus_led *led, int state);
5074d2743aeSPhilip Paeps static void	acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
5089a1fc77eSPhilip Paeps 
509d8e0b9e1SPhilip Paeps static int	acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
510d8e0b9e1SPhilip Paeps static int	acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
511d8e0b9e1SPhilip Paeps static int	acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
512d8e0b9e1SPhilip Paeps static int	acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
5139a1fc77eSPhilip Paeps 
5149a1fc77eSPhilip Paeps static void	acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
5159a1fc77eSPhilip Paeps 
5169a1fc77eSPhilip Paeps static device_method_t acpi_asus_methods[] = {
5179a1fc77eSPhilip Paeps 	DEVMETHOD(device_probe,  acpi_asus_probe),
5189a1fc77eSPhilip Paeps 	DEVMETHOD(device_attach, acpi_asus_attach),
5199a1fc77eSPhilip Paeps 	DEVMETHOD(device_detach, acpi_asus_detach),
5209a1fc77eSPhilip Paeps 	{ 0, 0 }
5219a1fc77eSPhilip Paeps };
5229a1fc77eSPhilip Paeps 
5239a1fc77eSPhilip Paeps static driver_t acpi_asus_driver = {
5249a1fc77eSPhilip Paeps 	"acpi_asus",
5259a1fc77eSPhilip Paeps 	acpi_asus_methods,
5269a1fc77eSPhilip Paeps 	sizeof(struct acpi_asus_softc)
5279a1fc77eSPhilip Paeps };
5289a1fc77eSPhilip Paeps 
52990161e72SJohn Baldwin DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, 0, 0);
5309a1fc77eSPhilip Paeps MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
5319a1fc77eSPhilip Paeps 
5329a1fc77eSPhilip Paeps static int
acpi_asus_probe(device_t dev)5339a1fc77eSPhilip Paeps acpi_asus_probe(device_t dev)
5349a1fc77eSPhilip Paeps {
5359a1fc77eSPhilip Paeps 	struct acpi_asus_model	*model;
5369a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
5379a1fc77eSPhilip Paeps 	ACPI_BUFFER		Buf;
5389a1fc77eSPhilip Paeps 	ACPI_OBJECT		Arg, *Obj;
5399a1fc77eSPhilip Paeps 	ACPI_OBJECT_LIST	Args;
540fea0f7ccSRui Paulo 	static char		*asus_ids[] = { "ATK0100", "ASUS010", NULL };
5415efca36fSTakanori Watanabe 	int rv;
542fea0f7ccSRui Paulo 	char *rstr;
5439a1fc77eSPhilip Paeps 
5449a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
5459a1fc77eSPhilip Paeps 
546fea0f7ccSRui Paulo 	if (acpi_disabled("asus"))
547078080c9SPhilip Paeps 		return (ENXIO);
5485efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids, &rstr);
5495efca36fSTakanori Watanabe 	if (rv > 0) {
5505efca36fSTakanori Watanabe 		return (rv);
551fea0f7ccSRui Paulo 	}
552078080c9SPhilip Paeps 
5539a1fc77eSPhilip Paeps 	sc = device_get_softc(dev);
5549a1fc77eSPhilip Paeps 	sc->dev = dev;
5559a1fc77eSPhilip Paeps 	sc->handle = acpi_get_handle(dev);
5569a1fc77eSPhilip Paeps 
5579a1fc77eSPhilip Paeps 	Arg.Type = ACPI_TYPE_INTEGER;
5589a1fc77eSPhilip Paeps 	Arg.Integer.Value = 0;
5599a1fc77eSPhilip Paeps 
5609a1fc77eSPhilip Paeps 	Args.Count = 1;
5619a1fc77eSPhilip Paeps 	Args.Pointer = &Arg;
5629a1fc77eSPhilip Paeps 
5639a1fc77eSPhilip Paeps 	Buf.Pointer = NULL;
5649a1fc77eSPhilip Paeps 	Buf.Length = ACPI_ALLOCATE_BUFFER;
5659a1fc77eSPhilip Paeps 
5669a1fc77eSPhilip Paeps 	AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
5679a1fc77eSPhilip Paeps 	Obj = Buf.Pointer;
5689a1fc77eSPhilip Paeps 
569706bd68cSPhilip Paeps 	/*
570706bd68cSPhilip Paeps 	 * The Samsung P30 returns a null-pointer from INIT, we
571706bd68cSPhilip Paeps 	 * can identify it from the 'ODEM' string in the DSDT.
572706bd68cSPhilip Paeps 	 */
573706bd68cSPhilip Paeps 	if (Obj->String.Pointer == NULL) {
574706bd68cSPhilip Paeps 		ACPI_STATUS		status;
575706bd68cSPhilip Paeps 		ACPI_TABLE_HEADER	th;
576706bd68cSPhilip Paeps 
5772be4e471SJung-uk Kim 		status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
578706bd68cSPhilip Paeps 		if (ACPI_FAILURE(status)) {
579078080c9SPhilip Paeps 			device_printf(dev, "Unsupported (Samsung?) laptop\n");
580706bd68cSPhilip Paeps 			AcpiOsFree(Buf.Pointer);
581706bd68cSPhilip Paeps 			return (ENXIO);
582706bd68cSPhilip Paeps 		}
583706bd68cSPhilip Paeps 
584706bd68cSPhilip Paeps 		if (strncmp("ODEM", th.OemTableId, 4) == 0) {
585706bd68cSPhilip Paeps 			sc->model = &acpi_samsung_models[0];
586078080c9SPhilip Paeps 			device_set_desc(dev, "Samsung P30 Laptop Extras");
587706bd68cSPhilip Paeps 			AcpiOsFree(Buf.Pointer);
5885efca36fSTakanori Watanabe 			return (rv);
589706bd68cSPhilip Paeps 		}
590fea0f7ccSRui Paulo 
5914060f1bbSAttilio Rao 		/* EeePC */
592fea0f7ccSRui Paulo 		if (strncmp("ASUS010", rstr, 7) == 0) {
593fea0f7ccSRui Paulo 			sc->model = &acpi_eeepc_models[0];
594fea0f7ccSRui Paulo 			device_set_desc(dev, "ASUS EeePC");
595fea0f7ccSRui Paulo 			AcpiOsFree(Buf.Pointer);
5965efca36fSTakanori Watanabe 			return (rv);
597fea0f7ccSRui Paulo 		}
598706bd68cSPhilip Paeps 	}
599706bd68cSPhilip Paeps 
600706bd68cSPhilip Paeps 	/*
601706bd68cSPhilip Paeps 	 * Asus laptops are simply identified by name, easy!
602706bd68cSPhilip Paeps 	 */
60318d92cd8SPhilip Paeps 	for (model = acpi_asus_models; model->name != NULL; model++) {
604078080c9SPhilip Paeps 		if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
60518d92cd8SPhilip Paeps good:
6069a1fc77eSPhilip Paeps 			sc->model = model;
6079a1fc77eSPhilip Paeps 
608*bad36a49SMark Johnston 			device_set_descf(dev, "Asus %s Laptop Extras",
609*bad36a49SMark Johnston 			    Obj->String.Pointer);
610*bad36a49SMark Johnston 
6119a1fc77eSPhilip Paeps 			AcpiOsFree(Buf.Pointer);
6125efca36fSTakanori Watanabe 			return (rv);
6139a1fc77eSPhilip Paeps 		}
6149a1fc77eSPhilip Paeps 
61518d92cd8SPhilip Paeps 		/*
61618d92cd8SPhilip Paeps 		 * Some models look exactly the same as other models, but have
61718d92cd8SPhilip Paeps 		 * their own ids.  If we spot these, set them up with the same
61818d92cd8SPhilip Paeps 		 * details as the models they're like, possibly dealing with
61918d92cd8SPhilip Paeps 		 * small differences.
62018d92cd8SPhilip Paeps 		 *
62118d92cd8SPhilip Paeps 		 * XXX: there must be a prettier way to do this!
62218d92cd8SPhilip Paeps 		 */
62318d92cd8SPhilip Paeps 		else if (strncmp(model->name, "xxN", 3) == 0 &&
62418d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
62518d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "S1N", 3) == 0))
62618d92cd8SPhilip Paeps 			goto good;
62718d92cd8SPhilip Paeps 		else if (strncmp(model->name, "A1x", 3) == 0 &&
62818d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "A1", 2) == 0)
62918d92cd8SPhilip Paeps 			goto good;
63018d92cd8SPhilip Paeps 		else if (strncmp(model->name, "A2x", 3) == 0 &&
63118d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "A2", 2) == 0)
63218d92cd8SPhilip Paeps 			goto good;
6334060f1bbSAttilio Rao 		else if (strncmp(model->name, "A3F", 3) == 0 &&
6344060f1bbSAttilio Rao 		    strncmp(Obj->String.Pointer, "A6F", 3) == 0)
6354060f1bbSAttilio Rao 			goto good;
63618d92cd8SPhilip Paeps 		else if (strncmp(model->name, "D1x", 3) == 0 &&
63718d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "D1", 2) == 0)
63818d92cd8SPhilip Paeps 			goto good;
63918d92cd8SPhilip Paeps 		else if (strncmp(model->name, "L3H", 3) == 0 &&
64018d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "L2E", 3) == 0)
64118d92cd8SPhilip Paeps 			goto good;
64218d92cd8SPhilip Paeps 		else if (strncmp(model->name, "L5x", 3) == 0 &&
64318d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "L5", 2) == 0)
64418d92cd8SPhilip Paeps 			goto good;
64518d92cd8SPhilip Paeps 		else if (strncmp(model->name, "M2E", 3) == 0 &&
64618d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
64718d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "L4E", 3) == 0))
64818d92cd8SPhilip Paeps 			goto good;
64918d92cd8SPhilip Paeps 		else if (strncmp(model->name, "S1x", 3) == 0 &&
65018d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
65118d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "S1", 2) == 0))
65218d92cd8SPhilip Paeps 			goto good;
65318d92cd8SPhilip Paeps 		else if (strncmp(model->name, "S2x", 3) == 0 &&
65418d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
65518d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "S2", 2) == 0))
65618d92cd8SPhilip Paeps 			goto good;
65718d92cd8SPhilip Paeps 
65818d92cd8SPhilip Paeps 		/* L2B is like L3C but has no lcd_get method */
65918d92cd8SPhilip Paeps 		else if (strncmp(model->name, "L3C", 3) == 0 &&
66018d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
66118d92cd8SPhilip Paeps 			model->lcd_get = NULL;
66218d92cd8SPhilip Paeps 			goto good;
66318d92cd8SPhilip Paeps 		}
66418d92cd8SPhilip Paeps 
66518d92cd8SPhilip Paeps 		/* A3G is like M6R but with a different lcd_get method */
66618d92cd8SPhilip Paeps 		else if (strncmp(model->name, "M6R", 3) == 0 &&
66718d92cd8SPhilip Paeps 		    strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
66818d92cd8SPhilip Paeps 			model->lcd_get = "\\BLFG";
66918d92cd8SPhilip Paeps 			goto good;
67018d92cd8SPhilip Paeps 		}
67118d92cd8SPhilip Paeps 
67218d92cd8SPhilip Paeps 		/* M2N and W1N are like xxN with added WLED */
67318d92cd8SPhilip Paeps 		else if (strncmp(model->name, "xxN", 3) == 0 &&
67418d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
67518d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
67618d92cd8SPhilip Paeps 			model->wled_set = "WLED";
67718d92cd8SPhilip Paeps 			goto good;
67818d92cd8SPhilip Paeps 		}
67918d92cd8SPhilip Paeps 
68018d92cd8SPhilip Paeps 		/* M5N and S5N are like xxN without MLED */
68118d92cd8SPhilip Paeps 		else if (strncmp(model->name, "xxN", 3) == 0 &&
68218d92cd8SPhilip Paeps 		    (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
68318d92cd8SPhilip Paeps 		     strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
68418d92cd8SPhilip Paeps 			model->mled_set = NULL;
68518d92cd8SPhilip Paeps 			goto good;
68618d92cd8SPhilip Paeps 		}
68718d92cd8SPhilip Paeps 	}
68818d92cd8SPhilip Paeps 
689*bad36a49SMark Johnston 	device_printf(dev, "Unsupported Asus laptop: %s\n",
690*bad36a49SMark Johnston 	    Obj->String.Pointer);
6919a1fc77eSPhilip Paeps 
6929a1fc77eSPhilip Paeps 	AcpiOsFree(Buf.Pointer);
6939a1fc77eSPhilip Paeps 
6949a1fc77eSPhilip Paeps 	return (ENXIO);
6959a1fc77eSPhilip Paeps }
6969a1fc77eSPhilip Paeps 
6979a1fc77eSPhilip Paeps static int
acpi_asus_attach(device_t dev)6989a1fc77eSPhilip Paeps acpi_asus_attach(device_t dev)
6999a1fc77eSPhilip Paeps {
7009a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
7019a1fc77eSPhilip Paeps 	struct acpi_softc	*acpi_sc;
7029a1fc77eSPhilip Paeps 
7039a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
7049a1fc77eSPhilip Paeps 
7059a1fc77eSPhilip Paeps 	sc = device_get_softc(dev);
7069a1fc77eSPhilip Paeps 	acpi_sc = acpi_device_get_parent_softc(dev);
7079a1fc77eSPhilip Paeps 
7089a1fc77eSPhilip Paeps 	/* Build sysctl tree */
7099a1fc77eSPhilip Paeps 	sysctl_ctx_init(&sc->sysctl_ctx);
7109a1fc77eSPhilip Paeps 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
7119a1fc77eSPhilip Paeps 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
7127029da5cSPawel Biernacki 	    OID_AUTO, "asus", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
7139a1fc77eSPhilip Paeps 
714d8e0b9e1SPhilip Paeps 	/* Hook up nodes */
715d8e0b9e1SPhilip Paeps 	for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
716d8e0b9e1SPhilip Paeps 		if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
717d8e0b9e1SPhilip Paeps 			continue;
718d8e0b9e1SPhilip Paeps 
719f0188618SHans Petter Selasky 		if (acpi_asus_sysctls[i].flag_anybody != 0) {
720d8e0b9e1SPhilip Paeps 			SYSCTL_ADD_PROC(&sc->sysctl_ctx,
721d8e0b9e1SPhilip Paeps 			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
722d8e0b9e1SPhilip Paeps 			    acpi_asus_sysctls[i].name,
7237029da5cSPawel Biernacki 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |
7246237a1ccSAlexander Motin 			    CTLFLAG_MPSAFE, sc, i, acpi_asus_sysctl, "I",
725d8e0b9e1SPhilip Paeps 			    acpi_asus_sysctls[i].description);
726f0188618SHans Petter Selasky 		} else {
727f0188618SHans Petter Selasky 			SYSCTL_ADD_PROC(&sc->sysctl_ctx,
728f0188618SHans Petter Selasky 			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
729f0188618SHans Petter Selasky 			    acpi_asus_sysctls[i].name,
7306237a1ccSAlexander Motin 			    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
731f0188618SHans Petter Selasky 			    sc, i, acpi_asus_sysctl, "I",
732f0188618SHans Petter Selasky 			    acpi_asus_sysctls[i].description);
733f0188618SHans Petter Selasky 		}
734d8e0b9e1SPhilip Paeps 	}
735d8e0b9e1SPhilip Paeps 
7369a1fc77eSPhilip Paeps 	/* Attach leds */
737caac4996SPhilip Paeps 	if (sc->model->bled_set) {
738caac4996SPhilip Paeps 		sc->s_bled.busy = 0;
739caac4996SPhilip Paeps 		sc->s_bled.sc = sc;
740caac4996SPhilip Paeps 		sc->s_bled.type = ACPI_ASUS_LED_BLED;
741caac4996SPhilip Paeps 		sc->s_bled.cdev =
742824d24a8SJung-uk Kim 		    led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
743824d24a8SJung-uk Kim 			"bled", 1);
744824d24a8SJung-uk Kim 	}
745824d24a8SJung-uk Kim 
746824d24a8SJung-uk Kim 	if (sc->model->dled_set) {
747824d24a8SJung-uk Kim 		sc->s_dled.busy = 0;
748824d24a8SJung-uk Kim 		sc->s_dled.sc = sc;
749824d24a8SJung-uk Kim 		sc->s_dled.type = ACPI_ASUS_LED_DLED;
750824d24a8SJung-uk Kim 		sc->s_dled.cdev =
751824d24a8SJung-uk Kim 		    led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
752824d24a8SJung-uk Kim 	}
753824d24a8SJung-uk Kim 
754824d24a8SJung-uk Kim 	if (sc->model->gled_set) {
755824d24a8SJung-uk Kim 		sc->s_gled.busy = 0;
756824d24a8SJung-uk Kim 		sc->s_gled.sc = sc;
757824d24a8SJung-uk Kim 		sc->s_gled.type = ACPI_ASUS_LED_GLED;
758824d24a8SJung-uk Kim 		sc->s_gled.cdev =
759824d24a8SJung-uk Kim 		    led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
760caac4996SPhilip Paeps 	}
761caac4996SPhilip Paeps 
762f5296c93SPhilip Paeps 	if (sc->model->mled_set) {
7634d2743aeSPhilip Paeps 		sc->s_mled.busy = 0;
7644d2743aeSPhilip Paeps 		sc->s_mled.sc = sc;
765f5296c93SPhilip Paeps 		sc->s_mled.type = ACPI_ASUS_LED_MLED;
766f5296c93SPhilip Paeps 		sc->s_mled.cdev =
767f5296c93SPhilip Paeps 		    led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
768f5296c93SPhilip Paeps 	}
7699a1fc77eSPhilip Paeps 
770f5296c93SPhilip Paeps 	if (sc->model->tled_set) {
7714d2743aeSPhilip Paeps 		sc->s_tled.busy = 0;
7724d2743aeSPhilip Paeps 		sc->s_tled.sc = sc;
773f5296c93SPhilip Paeps 		sc->s_tled.type = ACPI_ASUS_LED_TLED;
774f5296c93SPhilip Paeps 		sc->s_tled.cdev =
775824d24a8SJung-uk Kim 		    led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
776824d24a8SJung-uk Kim 			"tled", 1);
777f5296c93SPhilip Paeps 	}
7789a1fc77eSPhilip Paeps 
779f5296c93SPhilip Paeps 	if (sc->model->wled_set) {
7804d2743aeSPhilip Paeps 		sc->s_wled.busy = 0;
7814d2743aeSPhilip Paeps 		sc->s_wled.sc = sc;
782f5296c93SPhilip Paeps 		sc->s_wled.type = ACPI_ASUS_LED_WLED;
783f5296c93SPhilip Paeps 		sc->s_wled.cdev =
784824d24a8SJung-uk Kim 		    led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
785824d24a8SJung-uk Kim 			"wled", 1);
786f5296c93SPhilip Paeps 	}
7879a1fc77eSPhilip Paeps 
7889a1fc77eSPhilip Paeps 	/* Activate hotkeys */
7899a1fc77eSPhilip Paeps 	AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
7909a1fc77eSPhilip Paeps 
7919a1fc77eSPhilip Paeps 	/* Handle notifies */
792aea61887SRui Paulo 	if (sc->model->n_func == NULL)
793aea61887SRui Paulo 		sc->model->n_func = acpi_asus_notify;
794aea61887SRui Paulo 
795b4cb1402SNate Lawson 	AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
796aea61887SRui Paulo 	    sc->model->n_func, dev);
7979a1fc77eSPhilip Paeps 
79831fb9906SRui Paulo 	/* Find and hook the 'LCDD' object */
79931fb9906SRui Paulo 	if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
80031fb9906SRui Paulo 		ACPI_STATUS res;
80131fb9906SRui Paulo 
80231fb9906SRui Paulo 		sc->lcdd_handle = NULL;
80331fb9906SRui Paulo 		res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
80431fb9906SRui Paulo 		    NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
80531fb9906SRui Paulo 		if (ACPI_SUCCESS(res)) {
80631fb9906SRui Paulo 			AcpiInstallNotifyHandler((sc->lcdd_handle),
80731fb9906SRui Paulo 			    ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
80831fb9906SRui Paulo 	    	} else {
80931fb9906SRui Paulo 	    		printf("%s: unable to find LCD device '%s'\n",
81031fb9906SRui Paulo 	    		    __func__, sc->model->lcdd);
81131fb9906SRui Paulo 	    	}
81231fb9906SRui Paulo 	}
81331fb9906SRui Paulo 
8149a1fc77eSPhilip Paeps 	return (0);
8159a1fc77eSPhilip Paeps }
8169a1fc77eSPhilip Paeps 
8179a1fc77eSPhilip Paeps static int
acpi_asus_detach(device_t dev)8189a1fc77eSPhilip Paeps acpi_asus_detach(device_t dev)
8199a1fc77eSPhilip Paeps {
8209a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
8219a1fc77eSPhilip Paeps 
8229a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
8239a1fc77eSPhilip Paeps 
8249a1fc77eSPhilip Paeps 	sc = device_get_softc(dev);
8259a1fc77eSPhilip Paeps 
8269a1fc77eSPhilip Paeps 	/* Turn the lights off */
827caac4996SPhilip Paeps 	if (sc->model->bled_set)
828caac4996SPhilip Paeps 		led_destroy(sc->s_bled.cdev);
829caac4996SPhilip Paeps 
830824d24a8SJung-uk Kim 	if (sc->model->dled_set)
831824d24a8SJung-uk Kim 		led_destroy(sc->s_dled.cdev);
832824d24a8SJung-uk Kim 
833824d24a8SJung-uk Kim 	if (sc->model->gled_set)
834824d24a8SJung-uk Kim 		led_destroy(sc->s_gled.cdev);
835824d24a8SJung-uk Kim 
8369a1fc77eSPhilip Paeps 	if (sc->model->mled_set)
837f5296c93SPhilip Paeps 		led_destroy(sc->s_mled.cdev);
8389a1fc77eSPhilip Paeps 
8399a1fc77eSPhilip Paeps 	if (sc->model->tled_set)
840f5296c93SPhilip Paeps 		led_destroy(sc->s_tled.cdev);
8419a1fc77eSPhilip Paeps 
8429a1fc77eSPhilip Paeps 	if (sc->model->wled_set)
843f5296c93SPhilip Paeps 		led_destroy(sc->s_wled.cdev);
8449a1fc77eSPhilip Paeps 
8459a1fc77eSPhilip Paeps 	/* Remove notify handler */
846b4cb1402SNate Lawson 	AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
847b4cb1402SNate Lawson 	    acpi_asus_notify);
8489a1fc77eSPhilip Paeps 
84931fb9906SRui Paulo 	if (sc->lcdd_handle) {
85031fb9906SRui Paulo 		KASSERT(sc->model->lcdd_n_func != NULL,
85131fb9906SRui Paulo 		    ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
85231fb9906SRui Paulo 		AcpiRemoveNotifyHandler((sc->lcdd_handle),
85331fb9906SRui Paulo 		    ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
85431fb9906SRui Paulo 	}
85531fb9906SRui Paulo 
8569a1fc77eSPhilip Paeps 	/* Free sysctl tree */
8579a1fc77eSPhilip Paeps 	sysctl_ctx_free(&sc->sysctl_ctx);
8589a1fc77eSPhilip Paeps 
8599a1fc77eSPhilip Paeps 	return (0);
8609a1fc77eSPhilip Paeps }
8619a1fc77eSPhilip Paeps 
8629a1fc77eSPhilip Paeps static void
acpi_asus_led_task(struct acpi_asus_led * led,int pending __unused)8634d2743aeSPhilip Paeps acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
8649a1fc77eSPhilip Paeps {
8659a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
866f5296c93SPhilip Paeps 	char			*method;
8674d2743aeSPhilip Paeps 	int			state;
8689a1fc77eSPhilip Paeps 
8699a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
8709a1fc77eSPhilip Paeps 
8714d2743aeSPhilip Paeps 	sc = led->sc;
872f5296c93SPhilip Paeps 
873f5296c93SPhilip Paeps 	switch (led->type) {
874caac4996SPhilip Paeps 	case ACPI_ASUS_LED_BLED:
875caac4996SPhilip Paeps 		method = sc->model->bled_set;
876caac4996SPhilip Paeps 		state = led->state;
877caac4996SPhilip Paeps 		break;
878824d24a8SJung-uk Kim 	case ACPI_ASUS_LED_DLED:
879824d24a8SJung-uk Kim 		method = sc->model->dled_set;
880824d24a8SJung-uk Kim 		state = led->state;
881824d24a8SJung-uk Kim 		break;
882824d24a8SJung-uk Kim 	case ACPI_ASUS_LED_GLED:
883824d24a8SJung-uk Kim 		method = sc->model->gled_set;
884824d24a8SJung-uk Kim 		state = led->state + 1;	/* 1: off, 2: on */
885824d24a8SJung-uk Kim 		break;
886f5296c93SPhilip Paeps 	case ACPI_ASUS_LED_MLED:
887f5296c93SPhilip Paeps 		method = sc->model->mled_set;
888824d24a8SJung-uk Kim 		state = !led->state;	/* inverted */
889f5296c93SPhilip Paeps 		break;
890f5296c93SPhilip Paeps 	case ACPI_ASUS_LED_TLED:
891f5296c93SPhilip Paeps 		method = sc->model->tled_set;
8924d2743aeSPhilip Paeps 		state = led->state;
893f5296c93SPhilip Paeps 		break;
894f5296c93SPhilip Paeps 	case ACPI_ASUS_LED_WLED:
895f5296c93SPhilip Paeps 		method = sc->model->wled_set;
8964d2743aeSPhilip Paeps 		state = led->state;
897f5296c93SPhilip Paeps 		break;
89809003ac3SPhilip Paeps 	default:
89909003ac3SPhilip Paeps 		printf("acpi_asus_led: invalid LED type %d\n",
90009003ac3SPhilip Paeps 		    (int)led->type);
90109003ac3SPhilip Paeps 		return;
9029a1fc77eSPhilip Paeps 	}
9039a1fc77eSPhilip Paeps 
904f5296c93SPhilip Paeps 	acpi_SetInteger(sc->handle, method, state);
9054d2743aeSPhilip Paeps 	led->busy = 0;
9064d2743aeSPhilip Paeps }
9074d2743aeSPhilip Paeps 
9084d2743aeSPhilip Paeps static void
acpi_asus_led(struct acpi_asus_led * led,int state)9094d2743aeSPhilip Paeps acpi_asus_led(struct acpi_asus_led *led, int state)
9104d2743aeSPhilip Paeps {
9114d2743aeSPhilip Paeps 
9124d2743aeSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
9134d2743aeSPhilip Paeps 
9144d2743aeSPhilip Paeps 	if (led->busy)
9154d2743aeSPhilip Paeps 		return;
9164d2743aeSPhilip Paeps 
9174d2743aeSPhilip Paeps 	led->busy = 1;
9184d2743aeSPhilip Paeps 	led->state = state;
9194d2743aeSPhilip Paeps 
9202be4e471SJung-uk Kim 	AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
9219a1fc77eSPhilip Paeps }
9229a1fc77eSPhilip Paeps 
9239a1fc77eSPhilip Paeps static int
acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)924d8e0b9e1SPhilip Paeps acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
9259a1fc77eSPhilip Paeps {
9269a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
927d8e0b9e1SPhilip Paeps 	int			arg;
928d8e0b9e1SPhilip Paeps 	int			error = 0;
929d8e0b9e1SPhilip Paeps 	int			function;
930d8e0b9e1SPhilip Paeps 	int			method;
9319a1fc77eSPhilip Paeps 
9329a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
9339a1fc77eSPhilip Paeps 
9349a1fc77eSPhilip Paeps 	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
935d8e0b9e1SPhilip Paeps 	function = oidp->oid_arg2;
936d8e0b9e1SPhilip Paeps 	method = acpi_asus_sysctls[function].method;
937d8e0b9e1SPhilip Paeps 
9381051a7c2SNate Lawson 	ACPI_SERIAL_BEGIN(asus);
939d8e0b9e1SPhilip Paeps 	arg = acpi_asus_sysctl_get(sc, method);
940d8e0b9e1SPhilip Paeps 	error = sysctl_handle_int(oidp, &arg, 0, req);
9419a1fc77eSPhilip Paeps 
9429a1fc77eSPhilip Paeps 	/* Sanity check */
943d8e0b9e1SPhilip Paeps 	if (error != 0 || req->newptr == NULL)
9448390cfe8SNate Lawson 		goto out;
9459a1fc77eSPhilip Paeps 
946d8e0b9e1SPhilip Paeps 	/* Update */
947d8e0b9e1SPhilip Paeps 	error = acpi_asus_sysctl_set(sc, method, arg);
948d8e0b9e1SPhilip Paeps 
949d8e0b9e1SPhilip Paeps out:
950d8e0b9e1SPhilip Paeps 	ACPI_SERIAL_END(asus);
951d8e0b9e1SPhilip Paeps 	return (error);
9528390cfe8SNate Lawson }
9539a1fc77eSPhilip Paeps 
954d8e0b9e1SPhilip Paeps static int
acpi_asus_sysctl_get(struct acpi_asus_softc * sc,int method)955d8e0b9e1SPhilip Paeps acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
956d8e0b9e1SPhilip Paeps {
957d8e0b9e1SPhilip Paeps 	int val = 0;
958d8e0b9e1SPhilip Paeps 
959d8e0b9e1SPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
960d8e0b9e1SPhilip Paeps 	ACPI_SERIAL_ASSERT(asus);
961d8e0b9e1SPhilip Paeps 
962d8e0b9e1SPhilip Paeps 	switch (method) {
963d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_BRN:
964d8e0b9e1SPhilip Paeps 		val = sc->s_brn;
965d8e0b9e1SPhilip Paeps 		break;
966d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_DISP:
967d8e0b9e1SPhilip Paeps 		val = sc->s_disp;
968d8e0b9e1SPhilip Paeps 		break;
969d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_LCD:
970d8e0b9e1SPhilip Paeps 		val = sc->s_lcd;
971d8e0b9e1SPhilip Paeps 		break;
972aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CAMERA:
973aea61887SRui Paulo 		val = sc->s_cam;
974aea61887SRui Paulo 		break;
975aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CARDRD:
976aea61887SRui Paulo 		val = sc->s_crd;
977aea61887SRui Paulo 		break;
9780fc34be4SRui Paulo 	case ACPI_ASUS_METHOD_WLAN:
9790fc34be4SRui Paulo 		val = sc->s_wlan;
9800fc34be4SRui Paulo 		break;
981d8e0b9e1SPhilip Paeps 	}
982d8e0b9e1SPhilip Paeps 
983d8e0b9e1SPhilip Paeps 	return (val);
984d8e0b9e1SPhilip Paeps }
985d8e0b9e1SPhilip Paeps 
986d8e0b9e1SPhilip Paeps static int
acpi_asus_sysctl_set(struct acpi_asus_softc * sc,int method,int arg)987d8e0b9e1SPhilip Paeps acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
988d8e0b9e1SPhilip Paeps {
9894b0eb6a7SPhilip Paeps 	ACPI_STATUS		status = AE_OK;
9900fc34be4SRui Paulo 	ACPI_OBJECT_LIST 	acpiargs;
991d85f3935SStanislav Sedov 	ACPI_OBJECT		acpiarg[1];
992d8e0b9e1SPhilip Paeps 
993d8e0b9e1SPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
994d8e0b9e1SPhilip Paeps 	ACPI_SERIAL_ASSERT(asus);
995d8e0b9e1SPhilip Paeps 
9960fc34be4SRui Paulo 	acpiargs.Count = 1;
9970fc34be4SRui Paulo 	acpiargs.Pointer = acpiarg;
9980fc34be4SRui Paulo 	acpiarg[0].Type = ACPI_TYPE_INTEGER;
9990fc34be4SRui Paulo 	acpiarg[0].Integer.Value = arg;
10000fc34be4SRui Paulo 
1001d8e0b9e1SPhilip Paeps 	switch (method) {
1002d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_BRN:
1003d8e0b9e1SPhilip Paeps 		if (arg < 0 || arg > 15)
1004d8e0b9e1SPhilip Paeps 			return (EINVAL);
10059a1fc77eSPhilip Paeps 
10069a1fc77eSPhilip Paeps 		if (sc->model->brn_set)
1007d8e0b9e1SPhilip Paeps 			status = acpi_SetInteger(sc->handle,
1008d8e0b9e1SPhilip Paeps 			    sc->model->brn_set, arg);
10099a1fc77eSPhilip Paeps 		else {
1010d8e0b9e1SPhilip Paeps 			while (arg != 0) {
1011d8e0b9e1SPhilip Paeps 				status = AcpiEvaluateObject(sc->handle,
1012d8e0b9e1SPhilip Paeps 				    (arg > 0) ?  sc->model->brn_up :
1013d8e0b9e1SPhilip Paeps 				    sc->model->brn_dn, NULL, NULL);
1014d8e0b9e1SPhilip Paeps 				(arg > 0) ? arg-- : arg++;
10159a1fc77eSPhilip Paeps 			}
10169a1fc77eSPhilip Paeps 		}
10179a1fc77eSPhilip Paeps 
1018d8e0b9e1SPhilip Paeps 		if (ACPI_SUCCESS(status))
1019d8e0b9e1SPhilip Paeps 			sc->s_brn = arg;
10209a1fc77eSPhilip Paeps 
1021d8e0b9e1SPhilip Paeps 		break;
1022d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_DISP:
1023d8e0b9e1SPhilip Paeps 		if (arg < 0 || arg > 7)
1024d8e0b9e1SPhilip Paeps 			return (EINVAL);
10259a1fc77eSPhilip Paeps 
1026d8e0b9e1SPhilip Paeps 		status = acpi_SetInteger(sc->handle,
1027d8e0b9e1SPhilip Paeps 		    sc->model->disp_set, arg);
10289a1fc77eSPhilip Paeps 
1029d8e0b9e1SPhilip Paeps 		if (ACPI_SUCCESS(status))
1030d8e0b9e1SPhilip Paeps 			sc->s_disp = arg;
10319a1fc77eSPhilip Paeps 
1032d8e0b9e1SPhilip Paeps 		break;
1033d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_LCD:
1034d8e0b9e1SPhilip Paeps 		if (arg < 0 || arg > 1)
1035d8e0b9e1SPhilip Paeps 			return (EINVAL);
10369a1fc77eSPhilip Paeps 
10379a1fc77eSPhilip Paeps 		if (strncmp(sc->model->name, "L3H", 3) != 0)
1038d8e0b9e1SPhilip Paeps 			status = AcpiEvaluateObject(sc->handle,
1039d8e0b9e1SPhilip Paeps 			    sc->model->lcd_set, NULL, NULL);
10408390cfe8SNate Lawson 		else
1041d8e0b9e1SPhilip Paeps 			status = acpi_SetInteger(sc->handle,
1042d8e0b9e1SPhilip Paeps 			    sc->model->lcd_set, 0x7);
10439a1fc77eSPhilip Paeps 
1044d8e0b9e1SPhilip Paeps 		if (ACPI_SUCCESS(status))
1045d8e0b9e1SPhilip Paeps 			sc->s_lcd = arg;
1046d8e0b9e1SPhilip Paeps 
1047d8e0b9e1SPhilip Paeps 		break;
1048aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CAMERA:
1049aea61887SRui Paulo 		if (arg < 0 || arg > 1)
1050aea61887SRui Paulo 			return (EINVAL);
1051aea61887SRui Paulo 
1052aea61887SRui Paulo 		status = AcpiEvaluateObject(sc->handle,
10530fc34be4SRui Paulo 		    sc->model->cam_set, &acpiargs, NULL);
1054aea61887SRui Paulo 
1055aea61887SRui Paulo 		if (ACPI_SUCCESS(status))
1056aea61887SRui Paulo 			sc->s_cam = arg;
1057aea61887SRui Paulo 		break;
1058aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CARDRD:
1059aea61887SRui Paulo 		if (arg < 0 || arg > 1)
1060aea61887SRui Paulo 			return (EINVAL);
1061aea61887SRui Paulo 
1062aea61887SRui Paulo 		status = AcpiEvaluateObject(sc->handle,
10630fc34be4SRui Paulo 		    sc->model->crd_set, &acpiargs, NULL);
1064aea61887SRui Paulo 
1065aea61887SRui Paulo 		if (ACPI_SUCCESS(status))
1066aea61887SRui Paulo 			sc->s_crd = arg;
1067aea61887SRui Paulo 		break;
10680fc34be4SRui Paulo 	case ACPI_ASUS_METHOD_WLAN:
10690fc34be4SRui Paulo 		if (arg < 0 || arg > 1)
10700fc34be4SRui Paulo 			return (EINVAL);
10710fc34be4SRui Paulo 
10720fc34be4SRui Paulo 		status = AcpiEvaluateObject(sc->handle,
10730fc34be4SRui Paulo 		    sc->model->wlan_set, &acpiargs, NULL);
10740fc34be4SRui Paulo 
10750fc34be4SRui Paulo 		if (ACPI_SUCCESS(status))
10760fc34be4SRui Paulo 			sc->s_wlan = arg;
10770fc34be4SRui Paulo 		break;
1078d8e0b9e1SPhilip Paeps 	}
1079d8e0b9e1SPhilip Paeps 
1080d8e0b9e1SPhilip Paeps 	return (0);
10819a1fc77eSPhilip Paeps }
10829a1fc77eSPhilip Paeps 
10839a1fc77eSPhilip Paeps static int
acpi_asus_sysctl_init(struct acpi_asus_softc * sc,int method)1084d8e0b9e1SPhilip Paeps acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
10859a1fc77eSPhilip Paeps {
1086d8e0b9e1SPhilip Paeps 	ACPI_STATUS	status;
10879a1fc77eSPhilip Paeps 
1088d8e0b9e1SPhilip Paeps 	switch (method) {
1089d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_BRN:
1090d8e0b9e1SPhilip Paeps 		if (sc->model->brn_get) {
1091d8e0b9e1SPhilip Paeps 			/* GPLV/SPLV models */
1092d8e0b9e1SPhilip Paeps 			status = acpi_GetInteger(sc->handle,
1093d8e0b9e1SPhilip Paeps 			    sc->model->brn_get, &sc->s_brn);
1094d8e0b9e1SPhilip Paeps 			if (ACPI_SUCCESS(status))
1095d8e0b9e1SPhilip Paeps 				return (TRUE);
1096d8e0b9e1SPhilip Paeps 		} else if (sc->model->brn_up) {
1097d8e0b9e1SPhilip Paeps 			/* Relative models */
1098d8e0b9e1SPhilip Paeps 			status = AcpiEvaluateObject(sc->handle,
1099d8e0b9e1SPhilip Paeps 			    sc->model->brn_up, NULL, NULL);
1100d8e0b9e1SPhilip Paeps 			if (ACPI_FAILURE(status))
1101d8e0b9e1SPhilip Paeps 				return (FALSE);
11029a1fc77eSPhilip Paeps 
1103d8e0b9e1SPhilip Paeps 			status = AcpiEvaluateObject(sc->handle,
1104d8e0b9e1SPhilip Paeps 			    sc->model->brn_dn, NULL, NULL);
1105d8e0b9e1SPhilip Paeps 			if (ACPI_FAILURE(status))
1106d8e0b9e1SPhilip Paeps 				return (FALSE);
11079a1fc77eSPhilip Paeps 
1108d8e0b9e1SPhilip Paeps 			return (TRUE);
11098390cfe8SNate Lawson 		}
1110d8e0b9e1SPhilip Paeps 		return (FALSE);
1111d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_DISP:
1112d8e0b9e1SPhilip Paeps 		if (sc->model->disp_get) {
1113d8e0b9e1SPhilip Paeps 			status = acpi_GetInteger(sc->handle,
1114d8e0b9e1SPhilip Paeps 			    sc->model->disp_get, &sc->s_disp);
1115d8e0b9e1SPhilip Paeps 			if (ACPI_SUCCESS(status))
1116d8e0b9e1SPhilip Paeps 				return (TRUE);
1117d8e0b9e1SPhilip Paeps 		}
1118d8e0b9e1SPhilip Paeps 		return (FALSE);
1119d8e0b9e1SPhilip Paeps 	case ACPI_ASUS_METHOD_LCD:
1120824d24a8SJung-uk Kim 		if (sc->model->lcd_get) {
1121f67f8ffdSJung-uk Kim 			if (strncmp(sc->model->name, "L3H", 3) == 0) {
1122d8e0b9e1SPhilip Paeps 				ACPI_BUFFER		Buf;
1123d8e0b9e1SPhilip Paeps 				ACPI_OBJECT		Arg[2], Obj;
1124d8e0b9e1SPhilip Paeps 				ACPI_OBJECT_LIST	Args;
11259a1fc77eSPhilip Paeps 
1126d8e0b9e1SPhilip Paeps 				/* L3H is a bit special */
1127d8e0b9e1SPhilip Paeps 				Arg[0].Type = ACPI_TYPE_INTEGER;
1128d8e0b9e1SPhilip Paeps 				Arg[0].Integer.Value = 0x02;
1129d8e0b9e1SPhilip Paeps 				Arg[1].Type = ACPI_TYPE_INTEGER;
1130d8e0b9e1SPhilip Paeps 				Arg[1].Integer.Value = 0x03;
11319a1fc77eSPhilip Paeps 
1132d8e0b9e1SPhilip Paeps 				Args.Count = 2;
1133d8e0b9e1SPhilip Paeps 				Args.Pointer = Arg;
1134d8e0b9e1SPhilip Paeps 
1135d8e0b9e1SPhilip Paeps 				Buf.Length = sizeof(Obj);
1136d8e0b9e1SPhilip Paeps 				Buf.Pointer = &Obj;
1137d8e0b9e1SPhilip Paeps 
1138d8e0b9e1SPhilip Paeps 				status = AcpiEvaluateObject(sc->handle,
1139d8e0b9e1SPhilip Paeps 				    sc->model->lcd_get, &Args, &Buf);
1140d8e0b9e1SPhilip Paeps 				if (ACPI_SUCCESS(status) &&
1141d8e0b9e1SPhilip Paeps 				    Obj.Type == ACPI_TYPE_INTEGER) {
1142d8e0b9e1SPhilip Paeps 					sc->s_lcd = Obj.Integer.Value >> 8;
1143d8e0b9e1SPhilip Paeps 					return (TRUE);
1144d8e0b9e1SPhilip Paeps 				}
1145824d24a8SJung-uk Kim 			} else {
1146824d24a8SJung-uk Kim 				status = acpi_GetInteger(sc->handle,
1147824d24a8SJung-uk Kim 				    sc->model->lcd_get, &sc->s_lcd);
1148824d24a8SJung-uk Kim 				if (ACPI_SUCCESS(status))
1149824d24a8SJung-uk Kim 					return (TRUE);
1150824d24a8SJung-uk Kim 			}
1151d8e0b9e1SPhilip Paeps 		}
1152d8e0b9e1SPhilip Paeps 		return (FALSE);
1153aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CAMERA:
1154aea61887SRui Paulo 		if (sc->model->cam_get) {
1155aea61887SRui Paulo 			status = acpi_GetInteger(sc->handle,
1156aea61887SRui Paulo 			    sc->model->cam_get, &sc->s_cam);
1157aea61887SRui Paulo 			if (ACPI_SUCCESS(status))
1158aea61887SRui Paulo 				return (TRUE);
1159aea61887SRui Paulo 		}
1160aea61887SRui Paulo 		return (FALSE);
1161aea61887SRui Paulo 	case ACPI_ASUS_METHOD_CARDRD:
1162aea61887SRui Paulo 		if (sc->model->crd_get) {
1163aea61887SRui Paulo 			status = acpi_GetInteger(sc->handle,
1164aea61887SRui Paulo 			    sc->model->crd_get, &sc->s_crd);
1165aea61887SRui Paulo 			if (ACPI_SUCCESS(status))
1166aea61887SRui Paulo 				return (TRUE);
1167aea61887SRui Paulo 		}
1168aea61887SRui Paulo 		return (FALSE);
11690fc34be4SRui Paulo 	case ACPI_ASUS_METHOD_WLAN:
11700fc34be4SRui Paulo 		if (sc->model->wlan_get) {
11710fc34be4SRui Paulo 			status = acpi_GetInteger(sc->handle,
11720fc34be4SRui Paulo 			    sc->model->wlan_get, &sc->s_wlan);
11730fc34be4SRui Paulo 			if (ACPI_SUCCESS(status))
11740fc34be4SRui Paulo 				return (TRUE);
11750fc34be4SRui Paulo 		}
11760fc34be4SRui Paulo 		return (FALSE);
1177d8e0b9e1SPhilip Paeps 	}
1178d8e0b9e1SPhilip Paeps 	return (FALSE);
11799a1fc77eSPhilip Paeps }
11809a1fc77eSPhilip Paeps 
11819a1fc77eSPhilip Paeps static void
acpi_asus_notify(ACPI_HANDLE h,UINT32 notify,void * context)11829a1fc77eSPhilip Paeps acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
11839a1fc77eSPhilip Paeps {
11849a1fc77eSPhilip Paeps 	struct acpi_asus_softc	*sc;
11859a1fc77eSPhilip Paeps 	struct acpi_softc	*acpi_sc;
11869a1fc77eSPhilip Paeps 
11879a1fc77eSPhilip Paeps 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
11889a1fc77eSPhilip Paeps 
11899a1fc77eSPhilip Paeps 	sc = device_get_softc((device_t)context);
11909a1fc77eSPhilip Paeps 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
11919a1fc77eSPhilip Paeps 
11921051a7c2SNate Lawson 	ACPI_SERIAL_BEGIN(asus);
11939a1fc77eSPhilip Paeps 	if ((notify & ~0x10) <= 15) {
1194b4cb1402SNate Lawson 		sc->s_brn = notify & ~0x10;
11959a1fc77eSPhilip Paeps 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
11969a1fc77eSPhilip Paeps 	} else if ((notify & ~0x20) <= 15) {
1197b4cb1402SNate Lawson 		sc->s_brn = notify & ~0x20;
11989a1fc77eSPhilip Paeps 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
11999a1fc77eSPhilip Paeps 	} else if (notify == 0x33) {
12009a1fc77eSPhilip Paeps 		sc->s_lcd = 1;
12019a1fc77eSPhilip Paeps 		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
12029a1fc77eSPhilip Paeps 	} else if (notify == 0x34) {
12039a1fc77eSPhilip Paeps 		sc->s_lcd = 0;
12049a1fc77eSPhilip Paeps 		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
120531fb9906SRui Paulo 	} else if (notify == 0x86) {
120631fb9906SRui Paulo 		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
120731fb9906SRui Paulo 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
120831fb9906SRui Paulo 	} else if (notify == 0x87) {
120931fb9906SRui Paulo 		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
121031fb9906SRui Paulo 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
12119a1fc77eSPhilip Paeps 	} else {
12129a1fc77eSPhilip Paeps 		/* Notify devd(8) */
12139a1fc77eSPhilip Paeps 		acpi_UserNotify("ASUS", h, notify);
12149a1fc77eSPhilip Paeps 	}
12151051a7c2SNate Lawson 	ACPI_SERIAL_END(asus);
12169a1fc77eSPhilip Paeps }
1217aea61887SRui Paulo 
1218aea61887SRui Paulo static void
acpi_asus_lcdd_notify(ACPI_HANDLE h,UINT32 notify,void * context)121931fb9906SRui Paulo acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
122031fb9906SRui Paulo {
122131fb9906SRui Paulo 	struct acpi_asus_softc	*sc;
122231fb9906SRui Paulo 	struct acpi_softc	*acpi_sc;
122331fb9906SRui Paulo 
122431fb9906SRui Paulo 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
122531fb9906SRui Paulo 
122631fb9906SRui Paulo 	sc = device_get_softc((device_t)context);
122731fb9906SRui Paulo 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
122831fb9906SRui Paulo 
122931fb9906SRui Paulo 	ACPI_SERIAL_BEGIN(asus);
123031fb9906SRui Paulo 	switch (notify) {
123131fb9906SRui Paulo 	case 0x87:
123231fb9906SRui Paulo 		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
123331fb9906SRui Paulo 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
123431fb9906SRui Paulo 		break;
123531fb9906SRui Paulo 	case 0x86:
123631fb9906SRui Paulo 		acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
123731fb9906SRui Paulo 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
123831fb9906SRui Paulo 		break;
123931fb9906SRui Paulo 	}
124031fb9906SRui Paulo 	ACPI_SERIAL_END(asus);
124131fb9906SRui Paulo }
124231fb9906SRui Paulo 
124331fb9906SRui Paulo static void
acpi_asus_eeepc_notify(ACPI_HANDLE h,UINT32 notify,void * context)1244aea61887SRui Paulo acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1245aea61887SRui Paulo {
1246aea61887SRui Paulo 	struct acpi_asus_softc	*sc;
1247aea61887SRui Paulo 	struct acpi_softc	*acpi_sc;
1248aea61887SRui Paulo 
1249aea61887SRui Paulo 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1250aea61887SRui Paulo 
1251aea61887SRui Paulo 	sc = device_get_softc((device_t)context);
1252aea61887SRui Paulo 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
1253aea61887SRui Paulo 
1254aea61887SRui Paulo 	ACPI_SERIAL_BEGIN(asus);
1255aea61887SRui Paulo 	if ((notify & ~0x20) <= 15) {
1256aea61887SRui Paulo 		sc->s_brn = notify & ~0x20;
1257aea61887SRui Paulo 		ACPI_VPRINT(sc->dev, acpi_sc,
1258aea61887SRui Paulo 		    "Brightness increased/decreased\n");
1259aea61887SRui Paulo 	} else {
1260aea61887SRui Paulo 		/* Notify devd(8) */
1261aea61887SRui Paulo 		acpi_UserNotify("ASUS-Eee", h, notify);
1262aea61887SRui Paulo 	}
1263aea61887SRui Paulo 	ACPI_SERIAL_END(asus);
1264aea61887SRui Paulo }
1265