aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/laptops/asus-laptop.txt2
-rw-r--r--drivers/acpi/video_detect.c2
-rw-r--r--drivers/platform/x86/Kconfig43
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/asus_acpi.c1513
5 files changed, 6 insertions, 1555 deletions
diff --git a/Documentation/laptops/asus-laptop.txt b/Documentation/laptops/asus-laptop.txt
index 803e51f6768b..a1e04d679289 100644
--- a/Documentation/laptops/asus-laptop.txt
+++ b/Documentation/laptops/asus-laptop.txt
@@ -45,7 +45,7 @@ Status
45Usage 45Usage
46----- 46-----
47 47
48 Try "modprobe asus_acpi". Check your dmesg (simply type dmesg). You should 48 Try "modprobe asus-laptop". Check your dmesg (simply type dmesg). You should
49 see some lines like this : 49 see some lines like this :
50 50
51 Asus Laptop Extras version 0.42 51 Asus Laptop Extras version 0.42
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index f3f0fe7e255a..45d8097ef4cf 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -23,7 +23,7 @@
23 * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) 23 * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B)
24 * are available, video.ko should be used to handle the device. 24 * are available, video.ko should be used to handle the device.
25 * 25 *
26 * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi, 26 * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop,
27 * sony_acpi,... can take care about backlight brightness. 27 * sony_acpi,... can take care about backlight brightness.
28 * 28 *
29 * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) 29 * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m)
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 912ffef0f148..0b5519cda194 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -54,7 +54,6 @@ config ACERHDF
54config ASUS_LAPTOP 54config ASUS_LAPTOP
55 tristate "Asus Laptop Extras" 55 tristate "Asus Laptop Extras"
56 depends on ACPI 56 depends on ACPI
57 depends on !ACPI_ASUS
58 select LEDS_CLASS 57 select LEDS_CLASS
59 select NEW_LEDS 58 select NEW_LEDS
60 select BACKLIGHT_CLASS_DEVICE 59 select BACKLIGHT_CLASS_DEVICE
@@ -460,10 +459,9 @@ config INTEL_MENLOW
460 If unsure, say N. 459 If unsure, say N.
461 460
462config EEEPC_LAPTOP 461config EEEPC_LAPTOP
463 tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" 462 tristate "Eee PC Hotkey Driver"
464 depends on ACPI 463 depends on ACPI
465 depends on INPUT 464 depends on INPUT
466 depends on EXPERIMENTAL
467 depends on RFKILL || RFKILL = n 465 depends on RFKILL || RFKILL = n
468 depends on HOTPLUG_PCI 466 depends on HOTPLUG_PCI
469 select BACKLIGHT_CLASS_DEVICE 467 select BACKLIGHT_CLASS_DEVICE
@@ -482,11 +480,10 @@ config EEEPC_LAPTOP
482 doesn't work on your Eee PC, try eeepc-wmi instead. 480 doesn't work on your Eee PC, try eeepc-wmi instead.
483 481
484config ASUS_WMI 482config ASUS_WMI
485 tristate "ASUS WMI Driver (EXPERIMENTAL)" 483 tristate "ASUS WMI Driver"
486 depends on ACPI_WMI 484 depends on ACPI_WMI
487 depends on INPUT 485 depends on INPUT
488 depends on HWMON 486 depends on HWMON
489 depends on EXPERIMENTAL
490 depends on BACKLIGHT_CLASS_DEVICE 487 depends on BACKLIGHT_CLASS_DEVICE
491 depends on RFKILL || RFKILL = n 488 depends on RFKILL || RFKILL = n
492 depends on HOTPLUG_PCI 489 depends on HOTPLUG_PCI
@@ -501,7 +498,7 @@ config ASUS_WMI
501 be called asus-wmi. 498 be called asus-wmi.
502 499
503config ASUS_NB_WMI 500config ASUS_NB_WMI
504 tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" 501 tristate "Asus Notebook WMI Driver"
505 depends on ASUS_WMI 502 depends on ASUS_WMI
506 ---help--- 503 ---help---
507 This is a driver for newer Asus notebooks. It adds extra features 504 This is a driver for newer Asus notebooks. It adds extra features
@@ -514,7 +511,7 @@ config ASUS_NB_WMI
514 here. 511 here.
515 512
516config EEEPC_WMI 513config EEEPC_WMI
517 tristate "Eee PC WMI Driver (EXPERIMENTAL)" 514 tristate "Eee PC WMI Driver"
518 depends on ASUS_WMI 515 depends on ASUS_WMI
519 ---help--- 516 ---help---
520 This is a driver for newer Eee PC laptops. It adds extra features 517 This is a driver for newer Eee PC laptops. It adds extra features
@@ -559,38 +556,6 @@ config MSI_WMI
559 To compile this driver as a module, choose M here: the module will 556 To compile this driver as a module, choose M here: the module will
560 be called msi-wmi. 557 be called msi-wmi.
561 558
562config ACPI_ASUS
563 tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
564 depends on ACPI
565 select BACKLIGHT_CLASS_DEVICE
566 ---help---
567 This driver provides support for extra features of ACPI-compatible
568 ASUS laptops. As some of Medion laptops are made by ASUS, it may also
569 support some Medion laptops (such as 9675 for example). It makes all
570 the extra buttons generate standard ACPI events that go through
571 /proc/acpi/events, and (on some models) adds support for changing the
572 display brightness and output, switching the LCD backlight on and off,
573 and most importantly, allows you to blink those fancy LEDs intended
574 for reporting mail and wireless status.
575
576 Note: display switching code is currently considered EXPERIMENTAL,
577 toying with these values may even lock your machine.
578
579 All settings are changed via /proc/acpi/asus directory entries. Owner
580 and group for these entries can be set with asus_uid and asus_gid
581 parameters.
582
583 More information and a userspace daemon for handling the extra buttons
584 at <http://acpi4asus.sf.net>.
585
586 If you have an ACPI-compatible ASUS laptop, say Y or M here. This
587 driver is still under development, so if your laptop is unsupported or
588 something works not quite as expected, please use the mailing list
589 available on the above page (acpi4asus-user@lists.sourceforge.net).
590
591 NOTE: This driver is deprecated and will probably be removed soon,
592 use asus-laptop instead.
593
594config TOPSTAR_LAPTOP 559config TOPSTAR_LAPTOP
595 tristate "Topstar Laptop Extras" 560 tristate "Topstar Laptop Extras"
596 depends on ACPI 561 depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index d328f21e9fdd..3c07f8bfd42c 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -29,7 +29,6 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
29obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o 29obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
30obj-$(CONFIG_ACPI_WMI) += wmi.o 30obj-$(CONFIG_ACPI_WMI) += wmi.o
31obj-$(CONFIG_MSI_WMI) += msi-wmi.o 31obj-$(CONFIG_MSI_WMI) += msi-wmi.o
32obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
33obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o 32obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
34obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 33obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
35obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o 34obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
deleted file mode 100644
index 6f966d6c062b..000000000000
--- a/drivers/platform/x86/asus_acpi.c
+++ /dev/null
@@ -1,1513 +0,0 @@
1/*
2 * asus_acpi.c - Asus Laptop ACPI Extras
3 *
4 *
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 *
22 * The development page for this driver is located at
23 * http://sourceforge.net/projects/acpi4asus/
24 *
25 * Credits:
26 * Pontus Fuchs - Helper functions, cleanup
27 * Johann Wiesner - Small compile fixes
28 * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
29 * �ic Burghard - LED display support for W1N
30 *
31 */
32
33#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
34
35#include <linux/kernel.h>
36#include <linux/module.h>
37#include <linux/slab.h>
38#include <linux/init.h>
39#include <linux/types.h>
40#include <linux/proc_fs.h>
41#include <linux/seq_file.h>
42#include <linux/backlight.h>
43#include <acpi/acpi_drivers.h>
44#include <acpi/acpi_bus.h>
45#include <asm/uaccess.h>
46
47#define ASUS_ACPI_VERSION "0.30"
48
49#define PROC_ASUS "asus" /* The directory */
50#define PROC_MLED "mled"
51#define PROC_WLED "wled"
52#define PROC_TLED "tled"
53#define PROC_BT "bluetooth"
54#define PROC_LEDD "ledd"
55#define PROC_INFO "info"
56#define PROC_LCD "lcd"
57#define PROC_BRN "brn"
58#define PROC_DISP "disp"
59
60#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
61#define ACPI_HOTK_CLASS "hotkey"
62#define ACPI_HOTK_DEVICE_NAME "Hotkey"
63
64/*
65 * Some events we use, same for all Asus
66 */
67#define BR_UP 0x10
68#define BR_DOWN 0x20
69
70/*
71 * Flags for hotk status
72 */
73#define MLED_ON 0x01 /* Mail LED */
74#define WLED_ON 0x02 /* Wireless LED */
75#define TLED_ON 0x04 /* Touchpad LED */
76#define BT_ON 0x08 /* Internal Bluetooth */
77
78MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
79MODULE_DESCRIPTION(ACPI_HOTK_NAME);
80MODULE_LICENSE("GPL");
81
82static uid_t asus_uid;
83static gid_t asus_gid;
84module_param(asus_uid, uint, 0);
85MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
86module_param(asus_gid, uint, 0);
87MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
88
89/* For each model, all features implemented,
90 * those marked with R are relative to HOTK, A for absolute */
91struct model_data {
92 char *name; /* name of the laptop________________A */
93 char *mt_mled; /* method to handle mled_____________R */
94 char *mled_status; /* node to handle mled reading_______A */
95 char *mt_wled; /* method to handle wled_____________R */
96 char *wled_status; /* node to handle wled reading_______A */
97 char *mt_tled; /* method to handle tled_____________R */
98 char *tled_status; /* node to handle tled reading_______A */
99 char *mt_ledd; /* method to handle LED display______R */
100 char *mt_bt_switch; /* method to switch Bluetooth on/off_R */
101 char *bt_status; /* no model currently supports this__? */
102 char *mt_lcd_switch; /* method to turn LCD on/off_________A */
103 char *lcd_status; /* node to read LCD panel state______A */
104 char *brightness_up; /* method to set brightness up_______A */
105 char *brightness_down; /* method to set brightness down ____A */
106 char *brightness_set; /* method to set absolute brightness_R */
107 char *brightness_get; /* method to get absolute brightness_R */
108 char *brightness_status;/* node to get brightness____________A */
109 char *display_set; /* method to set video output________R */
110 char *display_get; /* method to get video output________R */
111};
112
113/*
114 * This is the main structure, we can use it to store anything interesting
115 * about the hotk device
116 */
117struct asus_hotk {
118 struct acpi_device *device; /* the device we are in */
119 acpi_handle handle; /* the handle of the hotk device */
120 char status; /* status of the hotk, for LEDs */
121 u32 ledd_status; /* status of the LED display */
122 struct model_data *methods; /* methods available on the laptop */
123 u8 brightness; /* brightness level */
124 enum {
125 A1x = 0, /* A1340D, A1300F */
126 A2x, /* A2500H */
127 A4G, /* A4700G */
128 D1x, /* D1 */
129 L2D, /* L2000D */
130 L3C, /* L3800C */
131 L3D, /* L3400D */
132 L3H, /* L3H, L2000E, L5D */
133 L4R, /* L4500R */
134 L5x, /* L5800C */
135 L8L, /* L8400L */
136 M1A, /* M1300A */
137 M2E, /* M2400E, L4400L */
138 M6N, /* M6800N, W3400N */
139 M6R, /* M6700R, A3000G */
140 P30, /* Samsung P30 */
141 S1x, /* S1300A, but also L1400B and M2400A (L84F) */
142 S2x, /* S200 (J1 reported), Victor MP-XP7210 */
143 W1N, /* W1000N */
144 W5A, /* W5A */
145 W3V, /* W3030V */
146 xxN, /* M2400N, M3700N, M5200N, M6800N,
147 S1300N, S5200N*/
148 A4S, /* Z81sp */
149 F3Sa, /* (Centrino) */
150 R1F,
151 END_MODEL
152 } model; /* Models currently supported */
153 u16 event_count[128]; /* Count for each event TODO make this better */
154};
155
156/* Here we go */
157#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
158#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
159#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
160#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
161#define S1x_PREFIX "\\_SB.PCI0.PX40."
162#define S2x_PREFIX A1x_PREFIX
163#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
164
165static struct model_data model_conf[END_MODEL] = {
166 /*
167 * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
168 * it seems to be a kind of switch, but what for ?
169 */
170
171 {
172 .name = "A1x",
173 .mt_mled = "MLED",
174 .mled_status = "\\MAIL",
175 .mt_lcd_switch = A1x_PREFIX "_Q10",
176 .lcd_status = "\\BKLI",
177 .brightness_up = A1x_PREFIX "_Q0E",
178 .brightness_down = A1x_PREFIX "_Q0F"},
179
180 {
181 .name = "A2x",
182 .mt_mled = "MLED",
183 .mt_wled = "WLED",
184 .wled_status = "\\SG66",
185 .mt_lcd_switch = "\\Q10",
186 .lcd_status = "\\BAOF",
187 .brightness_set = "SPLV",
188 .brightness_get = "GPLV",
189 .display_set = "SDSP",
190 .display_get = "\\INFB"},
191
192 {
193 .name = "A4G",
194 .mt_mled = "MLED",
195/* WLED present, but not controlled by ACPI */
196 .mt_lcd_switch = xxN_PREFIX "_Q10",
197 .brightness_set = "SPLV",
198 .brightness_get = "GPLV",
199 .display_set = "SDSP",
200 .display_get = "\\ADVG"},
201
202 {
203 .name = "D1x",
204 .mt_mled = "MLED",
205 .mt_lcd_switch = "\\Q0D",
206 .lcd_status = "\\GP11",
207 .brightness_up = "\\Q0C",
208 .brightness_down = "\\Q0B",
209 .brightness_status = "\\BLVL",
210 .display_set = "SDSP",
211 .display_get = "\\INFB"},
212
213 {
214 .name = "L2D",
215 .mt_mled = "MLED",
216 .mled_status = "\\SGP6",
217 .mt_wled = "WLED",
218 .wled_status = "\\RCP3",
219 .mt_lcd_switch = "\\Q10",
220 .lcd_status = "\\SGP0",
221 .brightness_up = "\\Q0E",
222 .brightness_down = "\\Q0F",
223 .display_set = "SDSP",
224 .display_get = "\\INFB"},
225
226 {
227 .name = "L3C",
228 .mt_mled = "MLED",
229 .mt_wled = "WLED",
230 .mt_lcd_switch = L3C_PREFIX "_Q10",
231 .lcd_status = "\\GL32",
232 .brightness_set = "SPLV",
233 .brightness_get = "GPLV",
234 .display_set = "SDSP",
235 .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
236
237 {
238 .name = "L3D",
239 .mt_mled = "MLED",
240 .mled_status = "\\MALD",
241 .mt_wled = "WLED",
242 .mt_lcd_switch = "\\Q10",
243 .lcd_status = "\\BKLG",
244 .brightness_set = "SPLV",
245 .brightness_get = "GPLV",
246 .display_set = "SDSP",
247 .display_get = "\\INFB"},
248
249 {
250 .name = "L3H",
251 .mt_mled = "MLED",
252 .mt_wled = "WLED",
253 .mt_lcd_switch = "EHK",
254 .lcd_status = "\\_SB.PCI0.PM.PBC",
255 .brightness_set = "SPLV",
256 .brightness_get = "GPLV",
257 .display_set = "SDSP",
258 .display_get = "\\INFB"},
259
260 {
261 .name = "L4R",
262 .mt_mled = "MLED",
263 .mt_wled = "WLED",
264 .wled_status = "\\_SB.PCI0.SBRG.SG13",
265 .mt_lcd_switch = xxN_PREFIX "_Q10",
266 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
267 .brightness_set = "SPLV",
268 .brightness_get = "GPLV",
269 .display_set = "SDSP",
270 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
271
272 {
273 .name = "L5x",
274 .mt_mled = "MLED",
275/* WLED present, but not controlled by ACPI */
276 .mt_tled = "TLED",
277 .mt_lcd_switch = "\\Q0D",
278 .lcd_status = "\\BAOF",
279 .brightness_set = "SPLV",
280 .brightness_get = "GPLV",
281 .display_set = "SDSP",
282 .display_get = "\\INFB"},
283
284 {
285 .name = "L8L"
286/* No features, but at least support the hotkeys */
287 },
288
289 {
290 .name = "M1A",
291 .mt_mled = "MLED",
292 .mt_lcd_switch = M1A_PREFIX "Q10",
293 .lcd_status = "\\PNOF",
294 .brightness_up = M1A_PREFIX "Q0E",
295 .brightness_down = M1A_PREFIX "Q0F",
296 .brightness_status = "\\BRIT",
297 .display_set = "SDSP",
298 .display_get = "\\INFB"},
299
300 {
301 .name = "M2E",
302 .mt_mled = "MLED",
303 .mt_wled = "WLED",
304 .mt_lcd_switch = "\\Q10",
305 .lcd_status = "\\GP06",
306 .brightness_set = "SPLV",
307 .brightness_get = "GPLV",
308 .display_set = "SDSP",
309 .display_get = "\\INFB"},
310
311 {
312 .name = "M6N",
313 .mt_mled = "MLED",
314 .mt_wled = "WLED",
315 .wled_status = "\\_SB.PCI0.SBRG.SG13",
316 .mt_lcd_switch = xxN_PREFIX "_Q10",
317 .lcd_status = "\\_SB.BKLT",
318 .brightness_set = "SPLV",
319 .brightness_get = "GPLV",
320 .display_set = "SDSP",
321 .display_get = "\\SSTE"},
322
323 {
324 .name = "M6R",
325 .mt_mled = "MLED",
326 .mt_wled = "WLED",
327 .mt_lcd_switch = xxN_PREFIX "_Q10",
328 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
329 .brightness_set = "SPLV",
330 .brightness_get = "GPLV",
331 .display_set = "SDSP",
332 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
333
334 {
335 .name = "P30",
336 .mt_wled = "WLED",
337 .mt_lcd_switch = P30_PREFIX "_Q0E",
338 .lcd_status = "\\BKLT",
339 .brightness_up = P30_PREFIX "_Q68",
340 .brightness_down = P30_PREFIX "_Q69",
341 .brightness_get = "GPLV",
342 .display_set = "SDSP",
343 .display_get = "\\DNXT"},
344
345 {
346 .name = "S1x",
347 .mt_mled = "MLED",
348 .mled_status = "\\EMLE",
349 .mt_wled = "WLED",
350 .mt_lcd_switch = S1x_PREFIX "Q10",
351 .lcd_status = "\\PNOF",
352 .brightness_set = "SPLV",
353 .brightness_get = "GPLV"},
354
355 {
356 .name = "S2x",
357 .mt_mled = "MLED",
358 .mled_status = "\\MAIL",
359 .mt_lcd_switch = S2x_PREFIX "_Q10",
360 .lcd_status = "\\BKLI",
361 .brightness_up = S2x_PREFIX "_Q0B",
362 .brightness_down = S2x_PREFIX "_Q0A"},
363
364 {
365 .name = "W1N",
366 .mt_mled = "MLED",
367 .mt_wled = "WLED",
368 .mt_ledd = "SLCM",
369 .mt_lcd_switch = xxN_PREFIX "_Q10",
370 .lcd_status = "\\BKLT",
371 .brightness_set = "SPLV",
372 .brightness_get = "GPLV",
373 .display_set = "SDSP",
374 .display_get = "\\ADVG"},
375
376 {
377 .name = "W5A",
378 .mt_bt_switch = "BLED",
379 .mt_wled = "WLED",
380 .mt_lcd_switch = xxN_PREFIX "_Q10",
381 .brightness_set = "SPLV",
382 .brightness_get = "GPLV",
383 .display_set = "SDSP",
384 .display_get = "\\ADVG"},
385
386 {
387 .name = "W3V",
388 .mt_mled = "MLED",
389 .mt_wled = "WLED",
390 .mt_lcd_switch = xxN_PREFIX "_Q10",
391 .lcd_status = "\\BKLT",
392 .brightness_set = "SPLV",
393 .brightness_get = "GPLV",
394 .display_set = "SDSP",
395 .display_get = "\\INFB"},
396
397 {
398 .name = "xxN",
399 .mt_mled = "MLED",
400/* WLED present, but not controlled by ACPI */
401 .mt_lcd_switch = xxN_PREFIX "_Q10",
402 .lcd_status = "\\BKLT",
403 .brightness_set = "SPLV",
404 .brightness_get = "GPLV",
405 .display_set = "SDSP",
406 .display_get = "\\ADVG"},
407
408 {
409 .name = "A4S",
410 .brightness_set = "SPLV",
411 .brightness_get = "GPLV",
412 .mt_bt_switch = "BLED",
413 .mt_wled = "WLED"
414 },
415
416 {
417 .name = "F3Sa",
418 .mt_bt_switch = "BLED",
419 .mt_wled = "WLED",
420 .mt_mled = "MLED",
421 .brightness_get = "GPLV",
422 .brightness_set = "SPLV",
423 .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10",
424 .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN",
425 .display_get = "\\ADVG",
426 .display_set = "SDSP",
427 },
428 {
429 .name = "R1F",
430 .mt_bt_switch = "BLED",
431 .mt_mled = "MLED",
432 .mt_wled = "WLED",
433 .mt_lcd_switch = "\\Q10",
434 .lcd_status = "\\GP06",
435 .brightness_set = "SPLV",
436 .brightness_get = "GPLV",
437 .display_set = "SDSP",
438 .display_get = "\\INFB"
439 }
440};
441
442/* procdir we use */
443static struct proc_dir_entry *asus_proc_dir;
444
445static struct backlight_device *asus_backlight_device;
446
447/*
448 * This header is made available to allow proper configuration given model,
449 * revision number , ... this info cannot go in struct asus_hotk because it is
450 * available before the hotk
451 */
452static struct acpi_table_header *asus_info;
453
454/* The actual device the driver binds to */
455static struct asus_hotk *hotk;
456
457/*
458 * The hotkey driver and autoloading declaration
459 */
460static int asus_hotk_add(struct acpi_device *device);
461static int asus_hotk_remove(struct acpi_device *device, int type);
462static void asus_hotk_notify(struct acpi_device *device, u32 event);
463
464static const struct acpi_device_id asus_device_ids[] = {
465 {"ATK0100", 0},
466 {"", 0},
467};
468MODULE_DEVICE_TABLE(acpi, asus_device_ids);
469
470static struct acpi_driver asus_hotk_driver = {
471 .name = "asus_acpi",
472 .class = ACPI_HOTK_CLASS,
473 .owner = THIS_MODULE,
474 .ids = asus_device_ids,
475 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
476 .ops = {
477 .add = asus_hotk_add,
478 .remove = asus_hotk_remove,
479 .notify = asus_hotk_notify,
480 },
481};
482
483/*
484 * This function evaluates an ACPI method, given an int as parameter, the
485 * method is searched within the scope of the handle, can be NULL. The output
486 * of the method is written is output, which can also be NULL
487 *
488 * returns 1 if write is successful, 0 else.
489 */
490static int write_acpi_int(acpi_handle handle, const char *method, int val,
491 struct acpi_buffer *output)
492{
493 struct acpi_object_list params; /* list of input parameters (int) */
494 union acpi_object in_obj; /* the only param we use */
495 acpi_status status;
496
497 params.count = 1;
498 params.pointer = &in_obj;
499 in_obj.type = ACPI_TYPE_INTEGER;
500 in_obj.integer.value = val;
501
502 status = acpi_evaluate_object(handle, (char *)method, &params, output);
503 return (status == AE_OK);
504}
505
506static int read_acpi_int(acpi_handle handle, const char *method, int *val)
507{
508 struct acpi_buffer output;
509 union acpi_object out_obj;
510 acpi_status status;
511
512 output.length = sizeof(out_obj);
513 output.pointer = &out_obj;
514
515 status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
516 *val = out_obj.integer.value;
517 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
518}
519
520static int asus_info_proc_show(struct seq_file *m, void *v)
521{
522 int temp;
523
524 seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
525 seq_printf(m, "Model reference : %s\n", hotk->methods->name);
526 /*
527 * The SFUN method probably allows the original driver to get the list
528 * of features supported by a given model. For now, 0x0100 or 0x0800
529 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
530 * The significance of others is yet to be found.
531 */
532 if (read_acpi_int(hotk->handle, "SFUN", &temp))
533 seq_printf(m, "SFUN value : 0x%04x\n", temp);
534 /*
535 * Another value for userspace: the ASYM method returns 0x02 for
536 * battery low and 0x04 for battery critical, its readings tend to be
537 * more accurate than those provided by _BST.
538 * Note: since not all the laptops provide this method, errors are
539 * silently ignored.
540 */
541 if (read_acpi_int(hotk->handle, "ASYM", &temp))
542 seq_printf(m, "ASYM value : 0x%04x\n", temp);
543 if (asus_info) {
544 seq_printf(m, "DSDT length : %d\n", asus_info->length);
545 seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum);
546 seq_printf(m, "DSDT revision : %d\n", asus_info->revision);
547 seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id);
548 seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id);
549 seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision);
550 seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id);
551 seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision);
552 }
553
554 return 0;
555}
556
557static int asus_info_proc_open(struct inode *inode, struct file *file)
558{
559 return single_open(file, asus_info_proc_show, NULL);
560}
561
562static const struct file_operations asus_info_proc_fops = {
563 .owner = THIS_MODULE,
564 .open = asus_info_proc_open,
565 .read = seq_read,
566 .llseek = seq_lseek,
567 .release = single_release,
568};
569
570/*
571 * /proc handlers
572 * We write our info in page, we begin at offset off and cannot write more
573 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
574 * number of bytes written in page
575 */
576
577/* Generic LED functions */
578static int read_led(const char *ledname, int ledmask)
579{
580 if (ledname) {
581 int led_status;
582
583 if (read_acpi_int(NULL, ledname, &led_status))
584 return led_status;
585 else
586 pr_warn("Error reading LED status\n");
587 }
588 return (hotk->status & ledmask) ? 1 : 0;
589}
590
591static int parse_arg(const char __user *buf, unsigned long count, int *val)
592{
593 char s[32];
594 if (!count)
595 return 0;
596 if (count > 31)
597 return -EINVAL;
598 if (copy_from_user(s, buf, count))
599 return -EFAULT;
600 s[count] = 0;
601 if (sscanf(s, "%i", val) != 1)
602 return -EINVAL;
603 return count;
604}
605
606/* FIXME: kill extraneous args so it can be called independently */
607static int
608write_led(const char __user *buffer, unsigned long count,
609 char *ledname, int ledmask, int invert)
610{
611 int rv, value;
612 int led_out = 0;
613
614 rv = parse_arg(buffer, count, &value);
615 if (rv > 0)
616 led_out = value ? 1 : 0;
617
618 hotk->status =
619 (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
620
621 if (invert) /* invert target value */
622 led_out = !led_out;
623
624 if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
625 pr_warn("LED (%s) write failed\n", ledname);
626
627 return rv;
628}
629
630/*
631 * Proc handlers for MLED
632 */
633static int mled_proc_show(struct seq_file *m, void *v)
634{
635 seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON));
636 return 0;
637}
638
639static int mled_proc_open(struct inode *inode, struct file *file)
640{
641 return single_open(file, mled_proc_show, NULL);
642}
643
644static ssize_t mled_proc_write(struct file *file, const char __user *buffer,
645 size_t count, loff_t *pos)
646{
647 return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
648}
649
650static const struct file_operations mled_proc_fops = {
651 .owner = THIS_MODULE,
652 .open = mled_proc_open,
653 .read = seq_read,
654 .llseek = seq_lseek,
655 .release = single_release,
656 .write = mled_proc_write,
657};
658
659/*
660 * Proc handlers for LED display
661 */
662static int ledd_proc_show(struct seq_file *m, void *v)
663{
664 seq_printf(m, "0x%08x\n", hotk->ledd_status);
665 return 0;
666}
667
668static int ledd_proc_open(struct inode *inode, struct file *file)
669{
670 return single_open(file, ledd_proc_show, NULL);
671}
672
673static ssize_t ledd_proc_write(struct file *file, const char __user *buffer,
674 size_t count, loff_t *pos)
675{
676 int rv, value;
677
678 rv = parse_arg(buffer, count, &value);
679 if (rv > 0) {
680 if (!write_acpi_int
681 (hotk->handle, hotk->methods->mt_ledd, value, NULL))
682 pr_warn("LED display write failed\n");
683 else
684 hotk->ledd_status = (u32) value;
685 }
686 return rv;
687}
688
689static const struct file_operations ledd_proc_fops = {
690 .owner = THIS_MODULE,
691 .open = ledd_proc_open,
692 .read = seq_read,
693 .llseek = seq_lseek,
694 .release = single_release,
695 .write = ledd_proc_write,
696};
697
698/*
699 * Proc handlers for WLED
700 */
701static int wled_proc_show(struct seq_file *m, void *v)
702{
703 seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON));
704 return 0;
705}
706
707static int wled_proc_open(struct inode *inode, struct file *file)
708{
709 return single_open(file, wled_proc_show, NULL);
710}
711
712static ssize_t wled_proc_write(struct file *file, const char __user *buffer,
713 size_t count, loff_t *pos)
714{
715 return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
716}
717
718static const struct file_operations wled_proc_fops = {
719 .owner = THIS_MODULE,
720 .open = wled_proc_open,
721 .read = seq_read,
722 .llseek = seq_lseek,
723 .release = single_release,
724 .write = wled_proc_write,
725};
726
727/*
728 * Proc handlers for Bluetooth
729 */
730static int bluetooth_proc_show(struct seq_file *m, void *v)
731{
732 seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
733 return 0;
734}
735
736static int bluetooth_proc_open(struct inode *inode, struct file *file)
737{
738 return single_open(file, bluetooth_proc_show, NULL);
739}
740
741static ssize_t bluetooth_proc_write(struct file *file,
742 const char __user *buffer, size_t count, loff_t *pos)
743{
744 /* Note: mt_bt_switch controls both internal Bluetooth adapter's
745 presence and its LED */
746 return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
747}
748
749static const struct file_operations bluetooth_proc_fops = {
750 .owner = THIS_MODULE,
751 .open = bluetooth_proc_open,
752 .read = seq_read,
753 .llseek = seq_lseek,
754 .release = single_release,
755 .write = bluetooth_proc_write,
756};
757
758/*
759 * Proc handlers for TLED
760 */
761static int tled_proc_show(struct seq_file *m, void *v)
762{
763 seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON));
764 return 0;
765}
766
767static int tled_proc_open(struct inode *inode, struct file *file)
768{
769 return single_open(file, tled_proc_show, NULL);
770}
771
772static ssize_t tled_proc_write(struct file *file, const char __user *buffer,
773 size_t count, loff_t *pos)
774{
775 return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
776}
777
778static const struct file_operations tled_proc_fops = {
779 .owner = THIS_MODULE,
780 .open = tled_proc_open,
781 .read = seq_read,
782 .llseek = seq_lseek,
783 .release = single_release,
784 .write = tled_proc_write,
785};
786
787static int get_lcd_state(void)
788{
789 int lcd = 0;
790
791 if (hotk->model == L3H) {
792 /* L3H and the like have to be handled differently */
793 acpi_status status = 0;
794 struct acpi_object_list input;
795 union acpi_object mt_params[2];
796 struct acpi_buffer output;
797 union acpi_object out_obj;
798
799 input.count = 2;
800 input.pointer = mt_params;
801 /* Note: the following values are partly guessed up, but
802 otherwise they seem to work */
803 mt_params[0].type = ACPI_TYPE_INTEGER;
804 mt_params[0].integer.value = 0x02;
805 mt_params[1].type = ACPI_TYPE_INTEGER;
806 mt_params[1].integer.value = 0x02;
807
808 output.length = sizeof(out_obj);
809 output.pointer = &out_obj;
810
811 status =
812 acpi_evaluate_object(NULL, hotk->methods->lcd_status,
813 &input, &output);
814 if (status != AE_OK)
815 return -1;
816 if (out_obj.type == ACPI_TYPE_INTEGER)
817 /* That's what the AML code does */
818 lcd = out_obj.integer.value >> 8;
819 } else if (hotk->model == F3Sa) {
820 unsigned long long tmp;
821 union acpi_object param;
822 struct acpi_object_list input;
823 acpi_status status;
824
825 /* Read pin 11 */
826 param.type = ACPI_TYPE_INTEGER;
827 param.integer.value = 0x11;
828 input.count = 1;
829 input.pointer = &param;
830
831 status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
832 &input, &tmp);
833 if (status != AE_OK)
834 return -1;
835
836 lcd = tmp;
837 } else {
838 /* We don't have to check anything if we are here */
839 if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
840 pr_warn("Error reading LCD status\n");
841
842 if (hotk->model == L2D)
843 lcd = ~lcd;
844 }
845
846 return (lcd & 1);
847}
848
849static int set_lcd_state(int value)
850{
851 int lcd = 0;
852 acpi_status status = 0;
853
854 lcd = value ? 1 : 0;
855 if (lcd != get_lcd_state()) {
856 /* switch */
857 if (hotk->model != L3H) {
858 status =
859 acpi_evaluate_object(NULL,
860 hotk->methods->mt_lcd_switch,
861 NULL, NULL);
862 } else {
863 /* L3H and the like must be handled differently */
864 if (!write_acpi_int
865 (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
866 NULL))
867 status = AE_ERROR;
868 /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
869 the exact behaviour is simulated here */
870 }
871 if (ACPI_FAILURE(status))
872 pr_warn("Error switching LCD\n");
873 }
874 return 0;
875
876}
877
878static int lcd_proc_show(struct seq_file *m, void *v)
879{
880 seq_printf(m, "%d\n", get_lcd_state());
881 return 0;
882}
883
884static int lcd_proc_open(struct inode *inode, struct file *file)
885{
886 return single_open(file, lcd_proc_show, NULL);
887}
888
889static ssize_t lcd_proc_write(struct file *file, const char __user *buffer,
890 size_t count, loff_t *pos)
891{
892 int rv, value;
893
894 rv = parse_arg(buffer, count, &value);
895 if (rv > 0)
896 set_lcd_state(value);
897 return rv;
898}
899
900static const struct file_operations lcd_proc_fops = {
901 .owner = THIS_MODULE,
902 .open = lcd_proc_open,
903 .read = seq_read,
904 .llseek = seq_lseek,
905 .release = single_release,
906 .write = lcd_proc_write,
907};
908
909static int read_brightness(struct backlight_device *bd)
910{
911 int value;
912
913 if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
914 if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
915 &value))
916 pr_warn("Error reading brightness\n");
917 } else if (hotk->methods->brightness_status) { /* For D1 for example */
918 if (!read_acpi_int(NULL, hotk->methods->brightness_status,
919 &value))
920 pr_warn("Error reading brightness\n");
921 } else /* No GPLV method */
922 value = hotk->brightness;
923 return value;
924}
925
926/*
927 * Change the brightness level
928 */
929static int set_brightness(int value)
930{
931 acpi_status status = 0;
932 int ret = 0;
933
934 /* SPLV laptop */
935 if (hotk->methods->brightness_set) {
936 if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
937 value, NULL)) {
938 pr_warn("Error changing brightness\n");
939 ret = -EIO;
940 }
941 goto out;
942 }
943
944 /* No SPLV method if we are here, act as appropriate */
945 value -= read_brightness(NULL);
946 while (value != 0) {
947 status = acpi_evaluate_object(NULL, (value > 0) ?
948 hotk->methods->brightness_up :
949 hotk->methods->brightness_down,
950 NULL, NULL);
951 (value > 0) ? value-- : value++;
952 if (ACPI_FAILURE(status)) {
953 pr_warn("Error changing brightness\n");
954 ret = -EIO;
955 }
956 }
957out:
958 return ret;
959}
960
961static int set_brightness_status(struct backlight_device *bd)
962{
963 return set_brightness(bd->props.brightness);
964}
965
966static int brn_proc_show(struct seq_file *m, void *v)
967{
968 seq_printf(m, "%d\n", read_brightness(NULL));
969 return 0;
970}
971
972static int brn_proc_open(struct inode *inode, struct file *file)
973{
974 return single_open(file, brn_proc_show, NULL);
975}
976
977static ssize_t brn_proc_write(struct file *file, const char __user *buffer,
978 size_t count, loff_t *pos)
979{
980 int rv, value;
981
982 rv = parse_arg(buffer, count, &value);
983 if (rv > 0) {
984 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
985 /* 0 <= value <= 15 */
986 set_brightness(value);
987 }
988 return rv;
989}
990
991static const struct file_operations brn_proc_fops = {
992 .owner = THIS_MODULE,
993 .open = brn_proc_open,
994 .read = seq_read,
995 .llseek = seq_lseek,
996 .release = single_release,
997 .write = brn_proc_write,
998};
999
1000static void set_display(int value)
1001{
1002 /* no sanity check needed for now */
1003 if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
1004 value, NULL))
1005 pr_warn("Error setting display\n");
1006 return;
1007}
1008
1009/*
1010 * Now, *this* one could be more user-friendly, but so far, no-one has
1011 * complained. The significance of bits is the same as in proc_write_disp()
1012 */
1013static int disp_proc_show(struct seq_file *m, void *v)
1014{
1015 int value = 0;
1016
1017 if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
1018 pr_warn("Error reading display status\n");
1019 value &= 0x07; /* needed for some models, shouldn't hurt others */
1020 seq_printf(m, "%d\n", value);
1021 return 0;
1022}
1023
1024static int disp_proc_open(struct inode *inode, struct file *file)
1025{
1026 return single_open(file, disp_proc_show, NULL);
1027}
1028
1029/*
1030 * Experimental support for display switching. As of now: 1 should activate
1031 * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
1032 * (bitwise) of these will suffice. I never actually tested 3 displays hooked
1033 * up simultaneously, so be warned. See the acpi4asus README for more info.
1034 */
1035static ssize_t disp_proc_write(struct file *file, const char __user *buffer,
1036 size_t count, loff_t *pos)
1037{
1038 int rv, value;
1039
1040 rv = parse_arg(buffer, count, &value);
1041 if (rv > 0)
1042 set_display(value);
1043 return rv;
1044}
1045
1046static const struct file_operations disp_proc_fops = {
1047 .owner = THIS_MODULE,
1048 .open = disp_proc_open,
1049 .read = seq_read,
1050 .llseek = seq_lseek,
1051 .release = single_release,
1052 .write = disp_proc_write,
1053};
1054
1055static int
1056asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode,
1057 struct acpi_device *device)
1058{
1059 struct proc_dir_entry *proc;
1060
1061 proc = proc_create_data(name, mode, acpi_device_dir(device),
1062 proc_fops, acpi_driver_data(device));
1063 if (!proc) {
1064 pr_warn(" Unable to create %s fs entry\n", name);
1065 return -1;
1066 }
1067 proc->uid = asus_uid;
1068 proc->gid = asus_gid;
1069 return 0;
1070}
1071
1072static int asus_hotk_add_fs(struct acpi_device *device)
1073{
1074 struct proc_dir_entry *proc;
1075 umode_t mode;
1076
1077 if ((asus_uid == 0) && (asus_gid == 0)) {
1078 mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP;
1079 } else {
1080 mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
1081 pr_warn(" asus_uid and asus_gid parameters are "
1082 "deprecated, use chown and chmod instead!\n");
1083 }
1084
1085 acpi_device_dir(device) = asus_proc_dir;
1086 if (!acpi_device_dir(device))
1087 return -ENODEV;
1088
1089 proc = proc_create(PROC_INFO, mode, acpi_device_dir(device),
1090 &asus_info_proc_fops);
1091 if (proc) {
1092 proc->uid = asus_uid;
1093 proc->gid = asus_gid;
1094 } else {
1095 pr_warn(" Unable to create " PROC_INFO " fs entry\n");
1096 }
1097
1098 if (hotk->methods->mt_wled) {
1099 asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device);
1100 }
1101
1102 if (hotk->methods->mt_ledd) {
1103 asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device);
1104 }
1105
1106 if (hotk->methods->mt_mled) {
1107 asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device);
1108 }
1109
1110 if (hotk->methods->mt_tled) {
1111 asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device);
1112 }
1113
1114 if (hotk->methods->mt_bt_switch) {
1115 asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device);
1116 }
1117
1118 /*
1119 * We need both read node and write method as LCD switch is also
1120 * accessible from the keyboard
1121 */
1122 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
1123 asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device);
1124 }
1125
1126 if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
1127 (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
1128 asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device);
1129 }
1130
1131 if (hotk->methods->display_set) {
1132 asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device);
1133 }
1134
1135 return 0;
1136}
1137
1138static int asus_hotk_remove_fs(struct acpi_device *device)
1139{
1140 if (acpi_device_dir(device)) {
1141 remove_proc_entry(PROC_INFO, acpi_device_dir(device));
1142 if (hotk->methods->mt_wled)
1143 remove_proc_entry(PROC_WLED, acpi_device_dir(device));
1144 if (hotk->methods->mt_mled)
1145 remove_proc_entry(PROC_MLED, acpi_device_dir(device));
1146 if (hotk->methods->mt_tled)
1147 remove_proc_entry(PROC_TLED, acpi_device_dir(device));
1148 if (hotk->methods->mt_ledd)
1149 remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
1150 if (hotk->methods->mt_bt_switch)
1151 remove_proc_entry(PROC_BT, acpi_device_dir(device));
1152 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
1153 remove_proc_entry(PROC_LCD, acpi_device_dir(device));
1154 if ((hotk->methods->brightness_up
1155 && hotk->methods->brightness_down)
1156 || (hotk->methods->brightness_get
1157 && hotk->methods->brightness_set))
1158 remove_proc_entry(PROC_BRN, acpi_device_dir(device));
1159 if (hotk->methods->display_set)
1160 remove_proc_entry(PROC_DISP, acpi_device_dir(device));
1161 }
1162 return 0;
1163}
1164
1165static void asus_hotk_notify(struct acpi_device *device, u32 event)
1166{
1167 /* TODO Find a better way to handle events count. */
1168 if (!hotk)
1169 return;
1170
1171 /*
1172 * The BIOS *should* be sending us device events, but apparently
1173 * Asus uses system events instead, so just ignore any device
1174 * events we get.
1175 */
1176 if (event > ACPI_MAX_SYS_NOTIFY)
1177 return;
1178
1179 if ((event & ~((u32) BR_UP)) < 16)
1180 hotk->brightness = (event & ~((u32) BR_UP));
1181 else if ((event & ~((u32) BR_DOWN)) < 16)
1182 hotk->brightness = (event & ~((u32) BR_DOWN));
1183
1184 acpi_bus_generate_proc_event(hotk->device, event,
1185 hotk->event_count[event % 128]++);
1186
1187 return;
1188}
1189
1190/*
1191 * Match the model string to the list of supported models. Return END_MODEL if
1192 * no match or model is NULL.
1193 */
1194static int asus_model_match(char *model)
1195{
1196 if (model == NULL)
1197 return END_MODEL;
1198
1199 if (strncmp(model, "L3D", 3) == 0)
1200 return L3D;
1201 else if (strncmp(model, "L2E", 3) == 0 ||
1202 strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
1203 return L3H;
1204 else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
1205 return L3C;
1206 else if (strncmp(model, "L8L", 3) == 0)
1207 return L8L;
1208 else if (strncmp(model, "L4R", 3) == 0)
1209 return L4R;
1210 else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
1211 return M6N;
1212 else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
1213 return M6R;
1214 else if (strncmp(model, "M2N", 3) == 0 ||
1215 strncmp(model, "M3N", 3) == 0 ||
1216 strncmp(model, "M5N", 3) == 0 ||
1217 strncmp(model, "S1N", 3) == 0 ||
1218 strncmp(model, "S5N", 3) == 0)
1219 return xxN;
1220 else if (strncmp(model, "M1", 2) == 0)
1221 return M1A;
1222 else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
1223 return M2E;
1224 else if (strncmp(model, "L2", 2) == 0)
1225 return L2D;
1226 else if (strncmp(model, "L8", 2) == 0)
1227 return S1x;
1228 else if (strncmp(model, "D1", 2) == 0)
1229 return D1x;
1230 else if (strncmp(model, "A1", 2) == 0)
1231 return A1x;
1232 else if (strncmp(model, "A2", 2) == 0)
1233 return A2x;
1234 else if (strncmp(model, "J1", 2) == 0)
1235 return S2x;
1236 else if (strncmp(model, "L5", 2) == 0)
1237 return L5x;
1238 else if (strncmp(model, "A4G", 3) == 0)
1239 return A4G;
1240 else if (strncmp(model, "W1N", 3) == 0)
1241 return W1N;
1242 else if (strncmp(model, "W3V", 3) == 0)
1243 return W3V;
1244 else if (strncmp(model, "W5A", 3) == 0)
1245 return W5A;
1246 else if (strncmp(model, "R1F", 3) == 0)
1247 return R1F;
1248 else if (strncmp(model, "A4S", 3) == 0)
1249 return A4S;
1250 else if (strncmp(model, "F3Sa", 4) == 0)
1251 return F3Sa;
1252 else
1253 return END_MODEL;
1254}
1255
1256/*
1257 * This function is used to initialize the hotk with right values. In this
1258 * method, we can make all the detection we want, and modify the hotk struct
1259 */
1260static int asus_hotk_get_info(void)
1261{
1262 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1263 union acpi_object *model = NULL;
1264 int bsts_result;
1265 char *string = NULL;
1266 acpi_status status;
1267
1268 /*
1269 * Get DSDT headers early enough to allow for differentiating between
1270 * models, but late enough to allow acpi_bus_register_driver() to fail
1271 * before doing anything ACPI-specific. Should we encounter a machine,
1272 * which needs special handling (i.e. its hotkey device has a different
1273 * HID), this bit will be moved. A global variable asus_info contains
1274 * the DSDT header.
1275 */
1276 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
1277 if (ACPI_FAILURE(status))
1278 pr_warn(" Couldn't get the DSDT table header\n");
1279
1280 /* We have to write 0 on init this far for all ASUS models */
1281 if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
1282 pr_err(" Hotkey initialization failed\n");
1283 return -ENODEV;
1284 }
1285
1286 /* This needs to be called for some laptops to init properly */
1287 if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
1288 pr_warn(" Error calling BSTS\n");
1289 else if (bsts_result)
1290 pr_notice(" BSTS called, 0x%02x returned\n", bsts_result);
1291
1292 /*
1293 * Try to match the object returned by INIT to the specific model.
1294 * Handle every possible object (or the lack of thereof) the DSDT
1295 * writers might throw at us. When in trouble, we pass NULL to
1296 * asus_model_match() and try something completely different.
1297 */
1298 if (buffer.pointer) {
1299 model = buffer.pointer;
1300 switch (model->type) {
1301 case ACPI_TYPE_STRING:
1302 string = model->string.pointer;
1303 break;
1304 case ACPI_TYPE_BUFFER:
1305 string = model->buffer.pointer;
1306 break;
1307 default:
1308 kfree(model);
1309 model = NULL;
1310 break;
1311 }
1312 }
1313 hotk->model = asus_model_match(string);
1314 if (hotk->model == END_MODEL) { /* match failed */
1315 if (asus_info &&
1316 strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
1317 hotk->model = P30;
1318 pr_notice(" Samsung P30 detected, supported\n");
1319 hotk->methods = &model_conf[hotk->model];
1320 kfree(model);
1321 return 0;
1322 } else {
1323 hotk->model = M2E;
1324 pr_notice(" unsupported model %s, trying default values\n",
1325 string);
1326 pr_notice(" send /proc/acpi/dsdt to the developers\n");
1327 kfree(model);
1328 return -ENODEV;
1329 }
1330 }
1331 hotk->methods = &model_conf[hotk->model];
1332 pr_notice(" %s model detected, supported\n", string);
1333
1334 /* Sort of per-model blacklist */
1335 if (strncmp(string, "L2B", 3) == 0)
1336 hotk->methods->lcd_status = NULL;
1337 /* L2B is similar enough to L3C to use its settings, with this only
1338 exception */
1339 else if (strncmp(string, "A3G", 3) == 0)
1340 hotk->methods->lcd_status = "\\BLFG";
1341 /* A3G is like M6R */
1342 else if (strncmp(string, "S5N", 3) == 0 ||
1343 strncmp(string, "M5N", 3) == 0 ||
1344 strncmp(string, "W3N", 3) == 0)
1345 hotk->methods->mt_mled = NULL;
1346 /* S5N, M5N and W3N have no MLED */
1347 else if (strncmp(string, "L5D", 3) == 0)
1348 hotk->methods->mt_wled = NULL;
1349 /* L5D's WLED is not controlled by ACPI */
1350 else if (strncmp(string, "M2N", 3) == 0 ||
1351 strncmp(string, "W3V", 3) == 0 ||
1352 strncmp(string, "S1N", 3) == 0)
1353 hotk->methods->mt_wled = "WLED";
1354 /* M2N, S1N and W3V have a usable WLED */
1355 else if (asus_info) {
1356 if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
1357 hotk->methods->mled_status = NULL;
1358 /* S1300A reports L84F, but L1400B too, account for that */
1359 }
1360
1361 kfree(model);
1362
1363 return 0;
1364}
1365
1366static int asus_hotk_check(void)
1367{
1368 int result = 0;
1369
1370 result = acpi_bus_get_status(hotk->device);
1371 if (result)
1372 return result;
1373
1374 if (hotk->device->status.present) {
1375 result = asus_hotk_get_info();
1376 } else {
1377 pr_err(" Hotkey device not present, aborting\n");
1378 return -EINVAL;
1379 }
1380
1381 return result;
1382}
1383
1384static int asus_hotk_found;
1385
1386static int asus_hotk_add(struct acpi_device *device)
1387{
1388 acpi_status status = AE_OK;
1389 int result;
1390
1391 pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION);
1392
1393 hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1394 if (!hotk)
1395 return -ENOMEM;
1396
1397 hotk->handle = device->handle;
1398 strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
1399 strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
1400 device->driver_data = hotk;
1401 hotk->device = device;
1402
1403 result = asus_hotk_check();
1404 if (result)
1405 goto end;
1406
1407 result = asus_hotk_add_fs(device);
1408 if (result)
1409 goto end;
1410
1411 /* For laptops without GPLV: init the hotk->brightness value */
1412 if ((!hotk->methods->brightness_get)
1413 && (!hotk->methods->brightness_status)
1414 && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
1415 status =
1416 acpi_evaluate_object(NULL, hotk->methods->brightness_down,
1417 NULL, NULL);
1418 if (ACPI_FAILURE(status))
1419 pr_warn(" Error changing brightness\n");
1420 else {
1421 status =
1422 acpi_evaluate_object(NULL,
1423 hotk->methods->brightness_up,
1424 NULL, NULL);
1425 if (ACPI_FAILURE(status))
1426 pr_warn(" Strange, error changing brightness\n");
1427 }
1428 }
1429
1430 asus_hotk_found = 1;
1431
1432 /* LED display is off by default */
1433 hotk->ledd_status = 0xFFF;
1434
1435end:
1436 if (result)
1437 kfree(hotk);
1438
1439 return result;
1440}
1441
1442static int asus_hotk_remove(struct acpi_device *device, int type)
1443{
1444 asus_hotk_remove_fs(device);
1445
1446 kfree(hotk);
1447
1448 return 0;
1449}
1450
1451static const struct backlight_ops asus_backlight_data = {
1452 .get_brightness = read_brightness,
1453 .update_status = set_brightness_status,
1454};
1455
1456static void asus_acpi_exit(void)
1457{
1458 if (asus_backlight_device)
1459 backlight_device_unregister(asus_backlight_device);
1460
1461 acpi_bus_unregister_driver(&asus_hotk_driver);
1462 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1463
1464 return;
1465}
1466
1467static int __init asus_acpi_init(void)
1468{
1469 struct backlight_properties props;
1470 int result;
1471
1472 result = acpi_bus_register_driver(&asus_hotk_driver);
1473 if (result < 0)
1474 return result;
1475
1476 asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
1477 if (!asus_proc_dir) {
1478 pr_err("Unable to create /proc entry\n");
1479 acpi_bus_unregister_driver(&asus_hotk_driver);
1480 return -ENODEV;
1481 }
1482
1483 /*
1484 * This is a bit of a kludge. We only want this module loaded
1485 * for ASUS systems, but there's currently no way to probe the
1486 * ACPI namespace for ASUS HIDs. So we just return failure if
1487 * we didn't find one, which will cause the module to be
1488 * unloaded.
1489 */
1490 if (!asus_hotk_found) {
1491 acpi_bus_unregister_driver(&asus_hotk_driver);
1492 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1493 return -ENODEV;
1494 }
1495
1496 memset(&props, 0, sizeof(struct backlight_properties));
1497 props.type = BACKLIGHT_PLATFORM;
1498 props.max_brightness = 15;
1499 asus_backlight_device = backlight_device_register("asus", NULL, NULL,
1500 &asus_backlight_data,
1501 &props);
1502 if (IS_ERR(asus_backlight_device)) {
1503 pr_err("Could not register asus backlight device\n");
1504 asus_backlight_device = NULL;
1505 asus_acpi_exit();
1506 return -ENODEV;
1507 }
1508
1509 return 0;
1510}
1511
1512module_init(asus_acpi_init);
1513module_exit(asus_acpi_exit);