diff options
-rw-r--r-- | Documentation/ABI/stable/sysfs-class-backlight | 36 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-lcd | 23 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-led | 28 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-platform-asus-laptop | 52 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-platform-eeepc-laptop | 50 | ||||
-rw-r--r-- | Documentation/laptops/asus-laptop.txt | 258 | ||||
-rw-r--r-- | Documentation/leds-class.txt | 9 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 227 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 340 |
9 files changed, 830 insertions, 193 deletions
diff --git a/Documentation/ABI/stable/sysfs-class-backlight b/Documentation/ABI/stable/sysfs-class-backlight new file mode 100644 index 000000000000..4d637e1c4ff7 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-class-backlight | |||
@@ -0,0 +1,36 @@ | |||
1 | What: /sys/class/backlight/<backlight>/bl_power | ||
2 | Date: April 2005 | ||
3 | KernelVersion: 2.6.12 | ||
4 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
5 | Description: | ||
6 | Control BACKLIGHT power, values are FB_BLANK_* from fb.h | ||
7 | - FB_BLANK_UNBLANK (0) : power on. | ||
8 | - FB_BLANK_POWERDOWN (4) : power off | ||
9 | Users: HAL | ||
10 | |||
11 | What: /sys/class/backlight/<backlight>/brightness | ||
12 | Date: April 2005 | ||
13 | KernelVersion: 2.6.12 | ||
14 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
15 | Description: | ||
16 | Control the brightness for this <backlight>. Values | ||
17 | are between 0 and max_brightness. This file will also | ||
18 | show the brightness level stored in the driver, which | ||
19 | may not be the actual brightness (see actual_brightness). | ||
20 | Users: HAL | ||
21 | |||
22 | What: /sys/class/backlight/<backlight>/actual_brightness | ||
23 | Date: March 2006 | ||
24 | KernelVersion: 2.6.17 | ||
25 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
26 | Description: | ||
27 | Show the actual brightness by querying the hardware. | ||
28 | Users: HAL | ||
29 | |||
30 | What: /sys/class/backlight/<backlight>/max_brightness | ||
31 | Date: April 2005 | ||
32 | KernelVersion: 2.6.12 | ||
33 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
34 | Description: | ||
35 | Maximum brightness for <backlight>. | ||
36 | Users: HAL | ||
diff --git a/Documentation/ABI/testing/sysfs-class-lcd b/Documentation/ABI/testing/sysfs-class-lcd new file mode 100644 index 000000000000..35906bf7aa70 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-lcd | |||
@@ -0,0 +1,23 @@ | |||
1 | What: /sys/class/lcd/<lcd>/lcd_power | ||
2 | Date: April 2005 | ||
3 | KernelVersion: 2.6.12 | ||
4 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
5 | Description: | ||
6 | Control LCD power, values are FB_BLANK_* from fb.h | ||
7 | - FB_BLANK_UNBLANK (0) : power on. | ||
8 | - FB_BLANK_POWERDOWN (4) : power off | ||
9 | |||
10 | What: /sys/class/lcd/<lcd>/contrast | ||
11 | Date: April 2005 | ||
12 | KernelVersion: 2.6.12 | ||
13 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
14 | Description: | ||
15 | Current contrast of this LCD device. Value is between 0 and | ||
16 | /sys/class/lcd/<lcd>/max_contrast. | ||
17 | |||
18 | What: /sys/class/lcd/<lcd>/max_contrast | ||
19 | Date: April 2005 | ||
20 | KernelVersion: 2.6.12 | ||
21 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
22 | Description: | ||
23 | Maximum contrast for this LCD device. | ||
diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led new file mode 100644 index 000000000000..9e4541d71cb6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led | |||
@@ -0,0 +1,28 @@ | |||
1 | What: /sys/class/leds/<led>/brightness | ||
2 | Date: March 2006 | ||
3 | KernelVersion: 2.6.17 | ||
4 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
5 | Description: | ||
6 | Set the brightness of the LED. Most LEDs don't | ||
7 | have hardware brightness support so will just be turned on for | ||
8 | non-zero brightness settings. The value is between 0 and | ||
9 | /sys/class/leds/<led>/max_brightness. | ||
10 | |||
11 | What: /sys/class/leds/<led>/max_brightness | ||
12 | Date: March 2006 | ||
13 | KernelVersion: 2.6.17 | ||
14 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
15 | Description: | ||
16 | Maximum brightness level for this led, default is 255 (LED_FULL). | ||
17 | |||
18 | What: /sys/class/leds/<led>/trigger | ||
19 | Date: March 2006 | ||
20 | KernelVersion: 2.6.17 | ||
21 | Contact: Richard Purdie <rpurdie@rpsys.net> | ||
22 | Description: | ||
23 | Set the trigger for this LED. A trigger is a kernel based source | ||
24 | of led events. | ||
25 | You can change triggers in a similar manner to the way an IO | ||
26 | scheduler is chosen. Trigger specific parameters can appear in | ||
27 | /sys/class/leds/<led> once a given trigger is selected. | ||
28 | |||
diff --git a/Documentation/ABI/testing/sysfs-platform-asus-laptop b/Documentation/ABI/testing/sysfs-platform-asus-laptop new file mode 100644 index 000000000000..a1cb660c50cf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-asus-laptop | |||
@@ -0,0 +1,52 @@ | |||
1 | What: /sys/devices/platform/asus-laptop/display | ||
2 | Date: January 2007 | ||
3 | KernelVersion: 2.6.20 | ||
4 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
5 | Description: | ||
6 | This file allows display switching. The value | ||
7 | is composed by 4 bits and defined as follow: | ||
8 | 4321 | ||
9 | |||`- LCD | ||
10 | ||`-- CRT | ||
11 | |`--- TV | ||
12 | `---- DVI | ||
13 | Ex: - 0 (0000b) means no display | ||
14 | - 3 (0011b) CRT+LCD. | ||
15 | |||
16 | What: /sys/devices/platform/asus-laptop/gps | ||
17 | Date: January 2007 | ||
18 | KernelVersion: 2.6.20 | ||
19 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
20 | Description: | ||
21 | Control the gps device. 1 means on, 0 means off. | ||
22 | Users: Lapsus | ||
23 | |||
24 | What: /sys/devices/platform/asus-laptop/ledd | ||
25 | Date: January 2007 | ||
26 | KernelVersion: 2.6.20 | ||
27 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
28 | Description: | ||
29 | Some models like the W1N have a LED display that can be | ||
30 | used to display several informations. | ||
31 | To control the LED display, use the following : | ||
32 | echo 0x0T000DDD > /sys/devices/platform/asus-laptop/ | ||
33 | where T control the 3 letters display, and DDD the 3 digits display. | ||
34 | The DDD table can be found in Documentation/laptops/asus-laptop.txt | ||
35 | |||
36 | What: /sys/devices/platform/asus-laptop/bluetooth | ||
37 | Date: January 2007 | ||
38 | KernelVersion: 2.6.20 | ||
39 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
40 | Description: | ||
41 | Control the bluetooth device. 1 means on, 0 means off. | ||
42 | This may control the led, the device or both. | ||
43 | Users: Lapsus | ||
44 | |||
45 | What: /sys/devices/platform/asus-laptop/wlan | ||
46 | Date: January 2007 | ||
47 | KernelVersion: 2.6.20 | ||
48 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
49 | Description: | ||
50 | Control the bluetooth device. 1 means on, 0 means off. | ||
51 | This may control the led, the device or both. | ||
52 | Users: Lapsus | ||
diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-laptop b/Documentation/ABI/testing/sysfs-platform-eeepc-laptop new file mode 100644 index 000000000000..7445dfb321b5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-laptop | |||
@@ -0,0 +1,50 @@ | |||
1 | What: /sys/devices/platform/eeepc-laptop/disp | ||
2 | Date: May 2008 | ||
3 | KernelVersion: 2.6.26 | ||
4 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
5 | Description: | ||
6 | This file allows display switching. | ||
7 | - 1 = LCD | ||
8 | - 2 = CRT | ||
9 | - 3 = LCD+CRT | ||
10 | If you run X11, you should use xrandr instead. | ||
11 | |||
12 | What: /sys/devices/platform/eeepc-laptop/camera | ||
13 | Date: May 2008 | ||
14 | KernelVersion: 2.6.26 | ||
15 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
16 | Description: | ||
17 | Control the camera. 1 means on, 0 means off. | ||
18 | |||
19 | What: /sys/devices/platform/eeepc-laptop/cardr | ||
20 | Date: May 2008 | ||
21 | KernelVersion: 2.6.26 | ||
22 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
23 | Description: | ||
24 | Control the card reader. 1 means on, 0 means off. | ||
25 | |||
26 | What: /sys/devices/platform/eeepc-laptop/cpufv | ||
27 | Date: Jun 2009 | ||
28 | KernelVersion: 2.6.31 | ||
29 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
30 | Description: | ||
31 | Change CPU clock configuration. | ||
32 | On the Eee PC 1000H there are three available clock configuration: | ||
33 | * 0 -> Super Performance Mode | ||
34 | * 1 -> High Performance Mode | ||
35 | * 2 -> Power Saving Mode | ||
36 | On Eee PC 701 there is only 2 available clock configurations. | ||
37 | Available configuration are listed in available_cpufv file. | ||
38 | Reading this file will show the raw hexadecimal value which | ||
39 | is defined as follow: | ||
40 | | 8 bit | 8 bit | | ||
41 | | `---- Current mode | ||
42 | `------------ Availables modes | ||
43 | For example, 0x301 means: mode 1 selected, 3 available modes. | ||
44 | |||
45 | What: /sys/devices/platform/eeepc-laptop/available_cpufv | ||
46 | Date: Jun 2009 | ||
47 | KernelVersion: 2.6.31 | ||
48 | Contact: "Corentin Chary" <corentincj@iksaif.net> | ||
49 | Description: | ||
50 | List available cpufv modes. | ||
diff --git a/Documentation/laptops/asus-laptop.txt b/Documentation/laptops/asus-laptop.txt new file mode 100644 index 000000000000..c1c5be84e4b1 --- /dev/null +++ b/Documentation/laptops/asus-laptop.txt | |||
@@ -0,0 +1,258 @@ | |||
1 | Asus Laptop Extras | ||
2 | |||
3 | Version 0.1 | ||
4 | August 6, 2009 | ||
5 | |||
6 | Corentin Chary <corentincj@iksaif.net> | ||
7 | http://acpi4asus.sf.net/ | ||
8 | |||
9 | This driver provides support for extra features of ACPI-compatible ASUS laptops. | ||
10 | It may also support some MEDION, JVC or VICTOR laptops (such as MEDION 9675 or | ||
11 | VICTOR XP7210 for example). It makes all the extra buttons generate standard | ||
12 | ACPI events that go through /proc/acpi/events and input events (like keyboards). | ||
13 | On some models adds support for changing the display brightness and output, | ||
14 | switching the LCD backlight on and off, and most importantly, allows you to | ||
15 | blink those fancy LEDs intended for reporting mail and wireless status. | ||
16 | |||
17 | This driver supercedes the old asus_acpi driver. | ||
18 | |||
19 | Requirements | ||
20 | ------------ | ||
21 | |||
22 | Kernel 2.6.X sources, configured for your computer, with ACPI support. | ||
23 | You also need CONFIG_INPUT and CONFIG_ACPI. | ||
24 | |||
25 | Status | ||
26 | ------ | ||
27 | |||
28 | The features currently supported are the following (see below for | ||
29 | detailed description): | ||
30 | |||
31 | - Fn key combinations | ||
32 | - Bluetooth enable and disable | ||
33 | - Wlan enable and disable | ||
34 | - GPS enable and disable | ||
35 | - Video output switching | ||
36 | - Ambient Light Sensor on and off | ||
37 | - LED control | ||
38 | - LED Display control | ||
39 | - LCD brightness control | ||
40 | - LCD on and off | ||
41 | |||
42 | A compatibility table by model and feature is maintained on the web | ||
43 | site, http://acpi4asus.sf.net/. | ||
44 | |||
45 | Usage | ||
46 | ----- | ||
47 | |||
48 | Try "modprobe asus_acpi". Check your dmesg (simply type dmesg). You should | ||
49 | see some lines like this : | ||
50 | |||
51 | Asus Laptop Extras version 0.42 | ||
52 | L2D model detected. | ||
53 | |||
54 | If it is not the output you have on your laptop, send it (and the laptop's | ||
55 | DSDT) to me. | ||
56 | |||
57 | That's all, now, all the events generated by the hotkeys of your laptop | ||
58 | should be reported in your /proc/acpi/event entry. You can check with | ||
59 | "acpi_listen". | ||
60 | |||
61 | Hotkeys are also reported as input keys (like keyboards) you can check | ||
62 | which key are supported using "xev" under X11. | ||
63 | |||
64 | You can get informations on the version of your DSDT table by reading the | ||
65 | /sys/devices/platform/asus-laptop/infos entry. If you have a question or a | ||
66 | bug report to do, please include the output of this entry. | ||
67 | |||
68 | LEDs | ||
69 | ---- | ||
70 | |||
71 | You can modify LEDs be echoing values to /sys/class/leds/asus::*/brightness : | ||
72 | echo 1 > /sys/class/leds/asus::mail/brightness | ||
73 | will switch the mail LED on. | ||
74 | You can also know if they are on/off by reading their content and use | ||
75 | kernel triggers like ide-disk or heartbeat. | ||
76 | |||
77 | Backlight | ||
78 | --------- | ||
79 | |||
80 | You can control lcd backlight power and brightness with | ||
81 | /sys/class/backlight/asus-laptop/. Brightness Values are between 0 and 15. | ||
82 | |||
83 | Wireless devices | ||
84 | --------------- | ||
85 | |||
86 | You can turn the internal Bluetooth adapter on/off with the bluetooth entry | ||
87 | (only on models with Bluetooth). This usually controls the associated LED. | ||
88 | Same for Wlan adapter. | ||
89 | |||
90 | Display switching | ||
91 | ----------------- | ||
92 | |||
93 | Note: the display switching code is currently considered EXPERIMENTAL. | ||
94 | |||
95 | Switching works for the following models: | ||
96 | L3800C | ||
97 | A2500H | ||
98 | L5800C | ||
99 | M5200N | ||
100 | W1000N (albeit with some glitches) | ||
101 | M6700R | ||
102 | A6JC | ||
103 | F3J | ||
104 | |||
105 | Switching doesn't work for the following: | ||
106 | M3700N | ||
107 | L2X00D (locks the laptop under certain conditions) | ||
108 | |||
109 | To switch the displays, echo values from 0 to 15 to | ||
110 | /sys/devices/platform/asus-laptop/display. The significance of those values | ||
111 | is as follows: | ||
112 | |||
113 | +-------+-----+-----+-----+-----+-----+ | ||
114 | | Bin | Val | DVI | TV | CRT | LCD | | ||
115 | +-------+-----+-----+-----+-----+-----+ | ||
116 | + 0000 + 0 + + + + + | ||
117 | +-------+-----+-----+-----+-----+-----+ | ||
118 | + 0001 + 1 + + + + X + | ||
119 | +-------+-----+-----+-----+-----+-----+ | ||
120 | + 0010 + 2 + + + X + + | ||
121 | +-------+-----+-----+-----+-----+-----+ | ||
122 | + 0011 + 3 + + + X + X + | ||
123 | +-------+-----+-----+-----+-----+-----+ | ||
124 | + 0100 + 4 + + X + + + | ||
125 | +-------+-----+-----+-----+-----+-----+ | ||
126 | + 0101 + 5 + + X + + X + | ||
127 | +-------+-----+-----+-----+-----+-----+ | ||
128 | + 0110 + 6 + + X + X + + | ||
129 | +-------+-----+-----+-----+-----+-----+ | ||
130 | + 0111 + 7 + + X + X + X + | ||
131 | +-------+-----+-----+-----+-----+-----+ | ||
132 | + 1000 + 8 + X + + + + | ||
133 | +-------+-----+-----+-----+-----+-----+ | ||
134 | + 1001 + 9 + X + + + X + | ||
135 | +-------+-----+-----+-----+-----+-----+ | ||
136 | + 1010 + 10 + X + + X + + | ||
137 | +-------+-----+-----+-----+-----+-----+ | ||
138 | + 1011 + 11 + X + + X + X + | ||
139 | +-------+-----+-----+-----+-----+-----+ | ||
140 | + 1100 + 12 + X + X + + + | ||
141 | +-------+-----+-----+-----+-----+-----+ | ||
142 | + 1101 + 13 + X + X + + X + | ||
143 | +-------+-----+-----+-----+-----+-----+ | ||
144 | + 1110 + 14 + X + X + X + + | ||
145 | +-------+-----+-----+-----+-----+-----+ | ||
146 | + 1111 + 15 + X + X + X + X + | ||
147 | +-------+-----+-----+-----+-----+-----+ | ||
148 | |||
149 | In most cases, the appropriate displays must be plugged in for the above | ||
150 | combinations to work. TV-Out may need to be initialized at boot time. | ||
151 | |||
152 | Debugging: | ||
153 | 1) Check whether the Fn+F8 key: | ||
154 | a) does not lock the laptop (try disabling CONFIG_X86_UP_APIC or boot with | ||
155 | noapic / nolapic if it does) | ||
156 | b) generates events (0x6n, where n is the value corresponding to the | ||
157 | configuration above) | ||
158 | c) actually works | ||
159 | Record the disp value at every configuration. | ||
160 | 2) Echo values from 0 to 15 to /sys/devices/platform/asus-laptop/display. | ||
161 | Record its value, note any change. If nothing changes, try a broader range, | ||
162 | up to 65535. | ||
163 | 3) Send ANY output (both positive and negative reports are needed, unless your | ||
164 | machine is already listed above) to the acpi4asus-user mailing list. | ||
165 | |||
166 | Note: on some machines (e.g. L3C), after the module has been loaded, only 0x6n | ||
167 | events are generated and no actual switching occurs. In such a case, a line | ||
168 | like: | ||
169 | |||
170 | echo $((10#$arg-60)) > /sys/devices/platform/asus-laptop/display | ||
171 | |||
172 | will usually do the trick ($arg is the 0000006n-like event passed to acpid). | ||
173 | |||
174 | Note: there is currently no reliable way to read display status on xxN | ||
175 | (Centrino) models. | ||
176 | |||
177 | LED display | ||
178 | ----------- | ||
179 | |||
180 | Some models like the W1N have a LED display that can be used to display | ||
181 | several informations. | ||
182 | |||
183 | LED display works for the following models: | ||
184 | W1000N | ||
185 | W1J | ||
186 | |||
187 | To control the LED display, use the following : | ||
188 | |||
189 | echo 0x0T000DDD > /sys/devices/platform/asus-laptop/ | ||
190 | |||
191 | where T control the 3 letters display, and DDD the 3 digits display, | ||
192 | according to the tables below. | ||
193 | |||
194 | DDD (digits) | ||
195 | 000 to 999 = display digits | ||
196 | AAA = --- | ||
197 | BBB to FFF = turn-off | ||
198 | |||
199 | T (type) | ||
200 | 0 = off | ||
201 | 1 = dvd | ||
202 | 2 = vcd | ||
203 | 3 = mp3 | ||
204 | 4 = cd | ||
205 | 5 = tv | ||
206 | 6 = cpu | ||
207 | 7 = vol | ||
208 | |||
209 | For example "echo 0x01000001 >/sys/devices/platform/asus-laptop/ledd" | ||
210 | would display "DVD001". | ||
211 | |||
212 | Driver options: | ||
213 | --------------- | ||
214 | |||
215 | Options can be passed to the asus-laptop driver using the standard | ||
216 | module argument syntax (<param>=<value> when passing the option to the | ||
217 | module or asus-laptop.<param>=<value> on the kernel boot line when | ||
218 | asus-laptop is statically linked into the kernel). | ||
219 | |||
220 | wapf: WAPF defines the behavior of the Fn+Fx wlan key | ||
221 | The significance of values is yet to be found, but | ||
222 | most of the time: | ||
223 | - 0x0 should do nothing | ||
224 | - 0x1 should allow to control the device with Fn+Fx key. | ||
225 | - 0x4 should send an ACPI event (0x88) while pressing the Fn+Fx key | ||
226 | - 0x5 like 0x1 or 0x4 | ||
227 | |||
228 | The default value is 0x1. | ||
229 | |||
230 | Unsupported models | ||
231 | ------------------ | ||
232 | |||
233 | These models will never be supported by this module, as they use a completely | ||
234 | different mechanism to handle LEDs and extra stuff (meaning we have no clue | ||
235 | how it works): | ||
236 | |||
237 | - ASUS A1300 (A1B), A1370D | ||
238 | - ASUS L7300G | ||
239 | - ASUS L8400 | ||
240 | |||
241 | Patches, Errors, Questions: | ||
242 | -------------------------- | ||
243 | |||
244 | I appreciate any success or failure | ||
245 | reports, especially if they add to or correct the compatibility table. | ||
246 | Please include the following information in your report: | ||
247 | |||
248 | - Asus model name | ||
249 | - a copy of your ACPI tables, using the "acpidump" utility | ||
250 | - a copy of /sys/devices/platform/asus-laptop/infos | ||
251 | - which driver features work and which don't | ||
252 | - the observed behavior of non-working features | ||
253 | |||
254 | Any other comments or patches are also more than welcome. | ||
255 | |||
256 | acpi4asus-user@lists.sourceforge.net | ||
257 | http://sourceforge.net/projects/acpi4asus | ||
258 | |||
diff --git a/Documentation/leds-class.txt b/Documentation/leds-class.txt index 6399557cdab3..8fd5ca2ae32d 100644 --- a/Documentation/leds-class.txt +++ b/Documentation/leds-class.txt | |||
@@ -1,3 +1,4 @@ | |||
1 | |||
1 | LED handling under Linux | 2 | LED handling under Linux |
2 | ======================== | 3 | ======================== |
3 | 4 | ||
@@ -5,10 +6,10 @@ If you're reading this and thinking about keyboard leds, these are | |||
5 | handled by the input subsystem and the led class is *not* needed. | 6 | handled by the input subsystem and the led class is *not* needed. |
6 | 7 | ||
7 | In its simplest form, the LED class just allows control of LEDs from | 8 | In its simplest form, the LED class just allows control of LEDs from |
8 | userspace. LEDs appear in /sys/class/leds/. The brightness file will | 9 | userspace. LEDs appear in /sys/class/leds/. The maximum brightness of the |
9 | set the brightness of the LED (taking a value 0-255). Most LEDs don't | 10 | LED is defined in max_brightness file. The brightness file will set the brightness |
10 | have hardware brightness support so will just be turned on for non-zero | 11 | of the LED (taking a value 0-max_brightness). Most LEDs don't have hardware |
11 | brightness settings. | 12 | brightness support so will just be turned on for non-zero brightness settings. |
12 | 13 | ||
13 | The class also introduces the optional concept of an LED trigger. A trigger | 14 | The class also introduces the optional concept of an LED trigger. A trigger |
14 | is a kernel based source of led events. Triggers can either be simple or | 15 | is a kernel based source of led events. Triggers can either be simple or |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index db657bbeec90..b39d2bb3e75b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -77,15 +77,16 @@ | |||
77 | * Flags for hotk status | 77 | * Flags for hotk status |
78 | * WL_ON and BT_ON are also used for wireless_status() | 78 | * WL_ON and BT_ON are also used for wireless_status() |
79 | */ | 79 | */ |
80 | #define WL_ON 0x01 //internal Wifi | 80 | #define WL_ON 0x01 /* internal Wifi */ |
81 | #define BT_ON 0x02 //internal Bluetooth | 81 | #define BT_ON 0x02 /* internal Bluetooth */ |
82 | #define MLED_ON 0x04 //mail LED | 82 | #define MLED_ON 0x04 /* mail LED */ |
83 | #define TLED_ON 0x08 //touchpad LED | 83 | #define TLED_ON 0x08 /* touchpad LED */ |
84 | #define RLED_ON 0x10 //Record LED | 84 | #define RLED_ON 0x10 /* Record LED */ |
85 | #define PLED_ON 0x20 //Phone LED | 85 | #define PLED_ON 0x20 /* Phone LED */ |
86 | #define GLED_ON 0x40 //Gaming LED | 86 | #define GLED_ON 0x40 /* Gaming LED */ |
87 | #define LCD_ON 0x80 //LCD backlight | 87 | #define LCD_ON 0x80 /* LCD backlight */ |
88 | #define GPS_ON 0x100 //GPS | 88 | #define GPS_ON 0x100 /* GPS */ |
89 | #define KEY_ON 0x200 /* Keyboard backlight */ | ||
89 | 90 | ||
90 | #define ASUS_LOG ASUS_HOTK_FILE ": " | 91 | #define ASUS_LOG ASUS_HOTK_FILE ": " |
91 | #define ASUS_ERR KERN_ERR ASUS_LOG | 92 | #define ASUS_ERR KERN_ERR ASUS_LOG |
@@ -98,7 +99,8 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); | |||
98 | MODULE_DESCRIPTION(ASUS_HOTK_NAME); | 99 | MODULE_DESCRIPTION(ASUS_HOTK_NAME); |
99 | MODULE_LICENSE("GPL"); | 100 | MODULE_LICENSE("GPL"); |
100 | 101 | ||
101 | /* WAPF defines the behavior of the Fn+Fx wlan key | 102 | /* |
103 | * WAPF defines the behavior of the Fn+Fx wlan key | ||
102 | * The significance of values is yet to be found, but | 104 | * The significance of values is yet to be found, but |
103 | * most of the time: | 105 | * most of the time: |
104 | * 0x0 will do nothing | 106 | * 0x0 will do nothing |
@@ -125,7 +127,8 @@ ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ | |||
125 | /* LEDD */ | 127 | /* LEDD */ |
126 | ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); | 128 | ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); |
127 | 129 | ||
128 | /* Bluetooth and WLAN | 130 | /* |
131 | * Bluetooth and WLAN | ||
129 | * WLED and BLED are not handled like other XLED, because in some dsdt | 132 | * WLED and BLED are not handled like other XLED, because in some dsdt |
130 | * they also control the WLAN/Bluetooth device. | 133 | * they also control the WLAN/Bluetooth device. |
131 | */ | 134 | */ |
@@ -149,22 +152,32 @@ ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ | |||
149 | 152 | ||
150 | /* Display */ | 153 | /* Display */ |
151 | ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); | 154 | ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP"); |
152 | ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G | 155 | ASUS_HANDLE(display_get, |
153 | M6A M6V VX-1 V6J V6V W3Z */ | 156 | /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ |
154 | "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V | 157 | "\\_SB.PCI0.P0P1.VGA.GETD", |
155 | S5A M5A z33A W1Jc W2V G1 */ | 158 | /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ |
156 | "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */ | 159 | "\\_SB.PCI0.P0P2.VGA.GETD", |
157 | "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */ | 160 | /* A6V A6Q */ |
158 | "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */ | 161 | "\\_SB.PCI0.P0P3.VGA.GETD", |
159 | "\\_SB.PCI0.VGA.GETD", /* Z96F */ | 162 | /* A6T, A6M */ |
160 | "\\ACTD", /* A2D */ | 163 | "\\_SB.PCI0.P0PA.VGA.GETD", |
161 | "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ | 164 | /* L3C */ |
162 | "\\DNXT", /* P30 */ | 165 | "\\_SB.PCI0.PCI1.VGAC.NMAP", |
163 | "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | 166 | /* Z96F */ |
164 | "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */ | 167 | "\\_SB.PCI0.VGA.GETD", |
165 | 168 | /* A2D */ | |
166 | ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ | 169 | "\\ACTD", |
167 | ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ | 170 | /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ |
171 | "\\ADVG", | ||
172 | /* P30 */ | ||
173 | "\\DNXT", | ||
174 | /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ | ||
175 | "\\INFB", | ||
176 | /* A3F A6F A3N A3L M6N W3N W6A */ | ||
177 | "\\SSTE"); | ||
178 | |||
179 | ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */ | ||
180 | ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */ | ||
168 | 181 | ||
169 | /* GPS */ | 182 | /* GPS */ |
170 | /* R2H use different handle for GPS on/off */ | 183 | /* R2H use different handle for GPS on/off */ |
@@ -172,19 +185,23 @@ ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */ | |||
172 | ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ | 185 | ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */ |
173 | ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); | 186 | ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST"); |
174 | 187 | ||
188 | /* Keyboard light */ | ||
189 | ASUS_HANDLE(kled_set, ASUS_HOTK_PREFIX "SLKB"); | ||
190 | ASUS_HANDLE(kled_get, ASUS_HOTK_PREFIX "GLKB"); | ||
191 | |||
175 | /* | 192 | /* |
176 | * This is the main structure, we can use it to store anything interesting | 193 | * This is the main structure, we can use it to store anything interesting |
177 | * about the hotk device | 194 | * about the hotk device |
178 | */ | 195 | */ |
179 | struct asus_hotk { | 196 | struct asus_hotk { |
180 | char *name; //laptop name | 197 | char *name; /* laptop name */ |
181 | struct acpi_device *device; //the device we are in | 198 | struct acpi_device *device; /* the device we are in */ |
182 | acpi_handle handle; //the handle of the hotk device | 199 | acpi_handle handle; /* the handle of the hotk device */ |
183 | char status; //status of the hotk, for LEDs, ... | 200 | char status; /* status of the hotk, for LEDs, ... */ |
184 | u32 ledd_status; //status of the LED display | 201 | u32 ledd_status; /* status of the LED display */ |
185 | u8 light_level; //light sensor level | 202 | u8 light_level; /* light sensor level */ |
186 | u8 light_switch; //light sensor switch value | 203 | u8 light_switch; /* light sensor switch value */ |
187 | u16 event_count[128]; //count for each event TODO make this better | 204 | u16 event_count[128]; /* count for each event TODO make this better */ |
188 | struct input_dev *inputdev; | 205 | struct input_dev *inputdev; |
189 | u16 *keycode_map; | 206 | u16 *keycode_map; |
190 | }; | 207 | }; |
@@ -237,28 +254,35 @@ static struct backlight_ops asusbl_ops = { | |||
237 | .update_status = update_bl_status, | 254 | .update_status = update_bl_status, |
238 | }; | 255 | }; |
239 | 256 | ||
240 | /* These functions actually update the LED's, and are called from a | 257 | /* |
258 | * These functions actually update the LED's, and are called from a | ||
241 | * workqueue. By doing this as separate work rather than when the LED | 259 | * workqueue. By doing this as separate work rather than when the LED |
242 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | 260 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a |
243 | * potentially bad time, such as a timer interrupt. */ | 261 | * potentially bad time, such as a timer interrupt. |
262 | */ | ||
244 | static struct workqueue_struct *led_workqueue; | 263 | static struct workqueue_struct *led_workqueue; |
245 | 264 | ||
246 | #define ASUS_LED(object, ledname) \ | 265 | #define ASUS_LED(object, ledname, max) \ |
247 | static void object##_led_set(struct led_classdev *led_cdev, \ | 266 | static void object##_led_set(struct led_classdev *led_cdev, \ |
248 | enum led_brightness value); \ | 267 | enum led_brightness value); \ |
268 | static enum led_brightness object##_led_get( \ | ||
269 | struct led_classdev *led_cdev); \ | ||
249 | static void object##_led_update(struct work_struct *ignored); \ | 270 | static void object##_led_update(struct work_struct *ignored); \ |
250 | static int object##_led_wk; \ | 271 | static int object##_led_wk; \ |
251 | static DECLARE_WORK(object##_led_work, object##_led_update); \ | 272 | static DECLARE_WORK(object##_led_work, object##_led_update); \ |
252 | static struct led_classdev object##_led = { \ | 273 | static struct led_classdev object##_led = { \ |
253 | .name = "asus::" ledname, \ | 274 | .name = "asus::" ledname, \ |
254 | .brightness_set = object##_led_set, \ | 275 | .brightness_set = object##_led_set, \ |
276 | .brightness_get = object##_led_get, \ | ||
277 | .max_brightness = max \ | ||
255 | } | 278 | } |
256 | 279 | ||
257 | ASUS_LED(mled, "mail"); | 280 | ASUS_LED(mled, "mail", 1); |
258 | ASUS_LED(tled, "touchpad"); | 281 | ASUS_LED(tled, "touchpad", 1); |
259 | ASUS_LED(rled, "record"); | 282 | ASUS_LED(rled, "record", 1); |
260 | ASUS_LED(pled, "phone"); | 283 | ASUS_LED(pled, "phone", 1); |
261 | ASUS_LED(gled, "gaming"); | 284 | ASUS_LED(gled, "gaming", 1); |
285 | ASUS_LED(kled, "kbd_backlight", 3); | ||
262 | 286 | ||
263 | struct key_entry { | 287 | struct key_entry { |
264 | char type; | 288 | char type; |
@@ -278,16 +302,23 @@ static struct key_entry asus_keymap[] = { | |||
278 | {KE_KEY, 0x41, KEY_NEXTSONG}, | 302 | {KE_KEY, 0x41, KEY_NEXTSONG}, |
279 | {KE_KEY, 0x43, KEY_STOPCD}, | 303 | {KE_KEY, 0x43, KEY_STOPCD}, |
280 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, | 304 | {KE_KEY, 0x45, KEY_PLAYPAUSE}, |
305 | {KE_KEY, 0x4c, KEY_MEDIA}, | ||
281 | {KE_KEY, 0x50, KEY_EMAIL}, | 306 | {KE_KEY, 0x50, KEY_EMAIL}, |
282 | {KE_KEY, 0x51, KEY_WWW}, | 307 | {KE_KEY, 0x51, KEY_WWW}, |
308 | {KE_KEY, 0x55, KEY_CALC}, | ||
283 | {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ | 309 | {KE_KEY, 0x5C, KEY_SCREENLOCK}, /* Screenlock */ |
284 | {KE_KEY, 0x5D, KEY_WLAN}, | 310 | {KE_KEY, 0x5D, KEY_WLAN}, |
311 | {KE_KEY, 0x5E, KEY_WLAN}, | ||
312 | {KE_KEY, 0x5F, KEY_WLAN}, | ||
313 | {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, | ||
285 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, | 314 | {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, |
286 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ | 315 | {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ |
287 | {KE_KEY, 0x82, KEY_CAMERA}, | 316 | {KE_KEY, 0x82, KEY_CAMERA}, |
288 | {KE_KEY, 0x8A, KEY_PROG1}, | 317 | {KE_KEY, 0x8A, KEY_PROG1}, |
289 | {KE_KEY, 0x95, KEY_MEDIA}, | 318 | {KE_KEY, 0x95, KEY_MEDIA}, |
290 | {KE_KEY, 0x99, KEY_PHONE}, | 319 | {KE_KEY, 0x99, KEY_PHONE}, |
320 | {KE_KEY, 0xc4, KEY_KBDILLUMUP}, | ||
321 | {KE_KEY, 0xc5, KEY_KBDILLUMDOWN}, | ||
291 | {KE_END, 0}, | 322 | {KE_END, 0}, |
292 | }; | 323 | }; |
293 | 324 | ||
@@ -301,8 +332,8 @@ static struct key_entry asus_keymap[] = { | |||
301 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | 332 | static int write_acpi_int(acpi_handle handle, const char *method, int val, |
302 | struct acpi_buffer *output) | 333 | struct acpi_buffer *output) |
303 | { | 334 | { |
304 | struct acpi_object_list params; //list of input parameters (an int here) | 335 | struct acpi_object_list params; /* list of input parameters (an int) */ |
305 | union acpi_object in_obj; //the only param we use | 336 | union acpi_object in_obj; /* the only param we use */ |
306 | acpi_status status; | 337 | acpi_status status; |
307 | 338 | ||
308 | if (!handle) | 339 | if (!handle) |
@@ -399,6 +430,11 @@ static void write_status(acpi_handle handle, int out, int mask) | |||
399 | { \ | 430 | { \ |
400 | int value = object##_led_wk; \ | 431 | int value = object##_led_wk; \ |
401 | write_status(object##_set_handle, value, (mask)); \ | 432 | write_status(object##_set_handle, value, (mask)); \ |
433 | } \ | ||
434 | static enum led_brightness object##_led_get( \ | ||
435 | struct led_classdev *led_cdev) \ | ||
436 | { \ | ||
437 | return led_cdev->brightness; \ | ||
402 | } | 438 | } |
403 | 439 | ||
404 | ASUS_LED_HANDLER(mled, MLED_ON); | 440 | ASUS_LED_HANDLER(mled, MLED_ON); |
@@ -407,6 +443,60 @@ ASUS_LED_HANDLER(rled, RLED_ON); | |||
407 | ASUS_LED_HANDLER(tled, TLED_ON); | 443 | ASUS_LED_HANDLER(tled, TLED_ON); |
408 | ASUS_LED_HANDLER(gled, GLED_ON); | 444 | ASUS_LED_HANDLER(gled, GLED_ON); |
409 | 445 | ||
446 | /* | ||
447 | * Keyboard backlight | ||
448 | */ | ||
449 | static int get_kled_lvl(void) | ||
450 | { | ||
451 | unsigned long long kblv; | ||
452 | struct acpi_object_list params; | ||
453 | union acpi_object in_obj; | ||
454 | acpi_status rv; | ||
455 | |||
456 | params.count = 1; | ||
457 | params.pointer = &in_obj; | ||
458 | in_obj.type = ACPI_TYPE_INTEGER; | ||
459 | in_obj.integer.value = 2; | ||
460 | |||
461 | rv = acpi_evaluate_integer(kled_get_handle, NULL, ¶ms, &kblv); | ||
462 | if (ACPI_FAILURE(rv)) { | ||
463 | pr_warning("Error reading kled level\n"); | ||
464 | return 0; | ||
465 | } | ||
466 | return kblv; | ||
467 | } | ||
468 | |||
469 | static int set_kled_lvl(int kblv) | ||
470 | { | ||
471 | if (kblv > 0) | ||
472 | kblv = (1 << 7) | (kblv & 0x7F); | ||
473 | else | ||
474 | kblv = 0; | ||
475 | |||
476 | if (write_acpi_int(kled_set_handle, NULL, kblv, NULL)) { | ||
477 | pr_warning("Keyboard LED display write failed\n"); | ||
478 | return -EINVAL; | ||
479 | } | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | static void kled_led_set(struct led_classdev *led_cdev, | ||
484 | enum led_brightness value) | ||
485 | { | ||
486 | kled_led_wk = value; | ||
487 | queue_work(led_workqueue, &kled_led_work); | ||
488 | } | ||
489 | |||
490 | static void kled_led_update(struct work_struct *ignored) | ||
491 | { | ||
492 | set_kled_lvl(kled_led_wk); | ||
493 | } | ||
494 | |||
495 | static enum led_brightness kled_led_get(struct led_classdev *led_cdev) | ||
496 | { | ||
497 | return get_kled_lvl(); | ||
498 | } | ||
499 | |||
410 | static int get_lcd_state(void) | 500 | static int get_lcd_state(void) |
411 | { | 501 | { |
412 | return read_status(LCD_ON); | 502 | return read_status(LCD_ON); |
@@ -498,7 +588,7 @@ static ssize_t show_infos(struct device *dev, | |||
498 | { | 588 | { |
499 | int len = 0; | 589 | int len = 0; |
500 | unsigned long long temp; | 590 | unsigned long long temp; |
501 | char buf[16]; //enough for all info | 591 | char buf[16]; /* enough for all info */ |
502 | acpi_status rv = AE_OK; | 592 | acpi_status rv = AE_OK; |
503 | 593 | ||
504 | /* | 594 | /* |
@@ -516,7 +606,17 @@ static ssize_t show_infos(struct device *dev, | |||
516 | */ | 606 | */ |
517 | rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); | 607 | rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); |
518 | if (!ACPI_FAILURE(rv)) | 608 | if (!ACPI_FAILURE(rv)) |
519 | len += sprintf(page + len, "SFUN value : 0x%04x\n", | 609 | len += sprintf(page + len, "SFUN value : %#x\n", |
610 | (uint) temp); | ||
611 | /* | ||
612 | * The HWRS method return informations about the hardware. | ||
613 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | ||
614 | * The significance of others is yet to be found. | ||
615 | * If we don't find the method, we assume the device are present. | ||
616 | */ | ||
617 | rv = acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &temp); | ||
618 | if (!ACPI_FAILURE(rv)) | ||
619 | len += sprintf(page + len, "HRWS value : %#x\n", | ||
520 | (uint) temp); | 620 | (uint) temp); |
521 | /* | 621 | /* |
522 | * Another value for userspace: the ASYM method returns 0x02 for | 622 | * Another value for userspace: the ASYM method returns 0x02 for |
@@ -527,7 +627,7 @@ static ssize_t show_infos(struct device *dev, | |||
527 | */ | 627 | */ |
528 | rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); | 628 | rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); |
529 | if (!ACPI_FAILURE(rv)) | 629 | if (!ACPI_FAILURE(rv)) |
530 | len += sprintf(page + len, "ASYM value : 0x%04x\n", | 630 | len += sprintf(page + len, "ASYM value : %#x\n", |
531 | (uint) temp); | 631 | (uint) temp); |
532 | if (asus_info) { | 632 | if (asus_info) { |
533 | snprintf(buf, 16, "%d", asus_info->length); | 633 | snprintf(buf, 16, "%d", asus_info->length); |
@@ -648,8 +748,10 @@ static int read_display(void) | |||
648 | unsigned long long value = 0; | 748 | unsigned long long value = 0; |
649 | acpi_status rv = AE_OK; | 749 | acpi_status rv = AE_OK; |
650 | 750 | ||
651 | /* In most of the case, we know how to set the display, but sometime | 751 | /* |
652 | we can't read it */ | 752 | * In most of the case, we know how to set the display, but sometime |
753 | * we can't read it | ||
754 | */ | ||
653 | if (display_get_handle) { | 755 | if (display_get_handle) { |
654 | rv = acpi_evaluate_integer(display_get_handle, NULL, | 756 | rv = acpi_evaluate_integer(display_get_handle, NULL, |
655 | NULL, &value); | 757 | NULL, &value); |
@@ -1037,6 +1139,9 @@ static int asus_hotk_get_info(void) | |||
1037 | 1139 | ||
1038 | ASUS_HANDLE_INIT(ledd_set); | 1140 | ASUS_HANDLE_INIT(ledd_set); |
1039 | 1141 | ||
1142 | ASUS_HANDLE_INIT(kled_set); | ||
1143 | ASUS_HANDLE_INIT(kled_get); | ||
1144 | |||
1040 | /* | 1145 | /* |
1041 | * The HWRS method return informations about the hardware. | 1146 | * The HWRS method return informations about the hardware. |
1042 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. | 1147 | * 0x80 bit is for WLAN, 0x100 for Bluetooth. |
@@ -1063,8 +1168,10 @@ static int asus_hotk_get_info(void) | |||
1063 | ASUS_HANDLE_INIT(display_set); | 1168 | ASUS_HANDLE_INIT(display_set); |
1064 | ASUS_HANDLE_INIT(display_get); | 1169 | ASUS_HANDLE_INIT(display_get); |
1065 | 1170 | ||
1066 | /* There is a lot of models with "ALSL", but a few get | 1171 | /* |
1067 | a real light sens, so we need to check it. */ | 1172 | * There is a lot of models with "ALSL", but a few get |
1173 | * a real light sens, so we need to check it. | ||
1174 | */ | ||
1068 | if (!ASUS_HANDLE_INIT(ls_switch)) | 1175 | if (!ASUS_HANDLE_INIT(ls_switch)) |
1069 | ASUS_HANDLE_INIT(ls_level); | 1176 | ASUS_HANDLE_INIT(ls_level); |
1070 | 1177 | ||
@@ -1168,6 +1275,10 @@ static int asus_hotk_add(struct acpi_device *device) | |||
1168 | /* LCD Backlight is on by default */ | 1275 | /* LCD Backlight is on by default */ |
1169 | write_status(NULL, 1, LCD_ON); | 1276 | write_status(NULL, 1, LCD_ON); |
1170 | 1277 | ||
1278 | /* Keyboard Backlight is on by default */ | ||
1279 | if (kled_set_handle) | ||
1280 | set_kled_lvl(1); | ||
1281 | |||
1171 | /* LED display is off by default */ | 1282 | /* LED display is off by default */ |
1172 | hotk->ledd_status = 0xFFF; | 1283 | hotk->ledd_status = 0xFFF; |
1173 | 1284 | ||
@@ -1222,6 +1333,7 @@ static void asus_led_exit(void) | |||
1222 | ASUS_LED_UNREGISTER(pled); | 1333 | ASUS_LED_UNREGISTER(pled); |
1223 | ASUS_LED_UNREGISTER(rled); | 1334 | ASUS_LED_UNREGISTER(rled); |
1224 | ASUS_LED_UNREGISTER(gled); | 1335 | ASUS_LED_UNREGISTER(gled); |
1336 | ASUS_LED_UNREGISTER(kled); | ||
1225 | } | 1337 | } |
1226 | 1338 | ||
1227 | static void asus_input_exit(void) | 1339 | static void asus_input_exit(void) |
@@ -1301,13 +1413,20 @@ static int asus_led_init(struct device *dev) | |||
1301 | if (rv) | 1413 | if (rv) |
1302 | goto out4; | 1414 | goto out4; |
1303 | 1415 | ||
1416 | if (kled_set_handle && kled_get_handle) | ||
1417 | rv = ASUS_LED_REGISTER(kled, dev); | ||
1418 | if (rv) | ||
1419 | goto out5; | ||
1420 | |||
1304 | led_workqueue = create_singlethread_workqueue("led_workqueue"); | 1421 | led_workqueue = create_singlethread_workqueue("led_workqueue"); |
1305 | if (!led_workqueue) | 1422 | if (!led_workqueue) |
1306 | goto out5; | 1423 | goto out6; |
1307 | 1424 | ||
1308 | return 0; | 1425 | return 0; |
1309 | out5: | 1426 | out6: |
1310 | rv = -ENOMEM; | 1427 | rv = -ENOMEM; |
1428 | ASUS_LED_UNREGISTER(kled); | ||
1429 | out5: | ||
1311 | ASUS_LED_UNREGISTER(gled); | 1430 | ASUS_LED_UNREGISTER(gled); |
1312 | out4: | 1431 | out4: |
1313 | ASUS_LED_UNREGISTER(pled); | 1432 | ASUS_LED_UNREGISTER(pled); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 222ffb892f22..da3c08b3dcc1 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -142,18 +142,28 @@ struct eeepc_hotk { | |||
142 | struct rfkill *wlan_rfkill; | 142 | struct rfkill *wlan_rfkill; |
143 | struct rfkill *bluetooth_rfkill; | 143 | struct rfkill *bluetooth_rfkill; |
144 | struct rfkill *wwan3g_rfkill; | 144 | struct rfkill *wwan3g_rfkill; |
145 | struct rfkill *wimax_rfkill; | ||
145 | struct hotplug_slot *hotplug_slot; | 146 | struct hotplug_slot *hotplug_slot; |
146 | struct work_struct hotplug_work; | 147 | struct mutex hotplug_lock; |
147 | }; | 148 | }; |
148 | 149 | ||
149 | /* The actual device the driver binds to */ | 150 | /* The actual device the driver binds to */ |
150 | static struct eeepc_hotk *ehotk; | 151 | static struct eeepc_hotk *ehotk; |
151 | 152 | ||
152 | /* Platform device/driver */ | 153 | /* Platform device/driver */ |
154 | static int eeepc_hotk_thaw(struct device *device); | ||
155 | static int eeepc_hotk_restore(struct device *device); | ||
156 | |||
157 | static struct dev_pm_ops eeepc_pm_ops = { | ||
158 | .thaw = eeepc_hotk_thaw, | ||
159 | .restore = eeepc_hotk_restore, | ||
160 | }; | ||
161 | |||
153 | static struct platform_driver platform_driver = { | 162 | static struct platform_driver platform_driver = { |
154 | .driver = { | 163 | .driver = { |
155 | .name = EEEPC_HOTK_FILE, | 164 | .name = EEEPC_HOTK_FILE, |
156 | .owner = THIS_MODULE, | 165 | .owner = THIS_MODULE, |
166 | .pm = &eeepc_pm_ops, | ||
157 | } | 167 | } |
158 | }; | 168 | }; |
159 | 169 | ||
@@ -192,7 +202,6 @@ static struct key_entry eeepc_keymap[] = { | |||
192 | */ | 202 | */ |
193 | static int eeepc_hotk_add(struct acpi_device *device); | 203 | static int eeepc_hotk_add(struct acpi_device *device); |
194 | static int eeepc_hotk_remove(struct acpi_device *device, int type); | 204 | static int eeepc_hotk_remove(struct acpi_device *device, int type); |
195 | static int eeepc_hotk_resume(struct acpi_device *device); | ||
196 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); | 205 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event); |
197 | 206 | ||
198 | static const struct acpi_device_id eeepc_device_ids[] = { | 207 | static const struct acpi_device_id eeepc_device_ids[] = { |
@@ -209,7 +218,6 @@ static struct acpi_driver eeepc_hotk_driver = { | |||
209 | .ops = { | 218 | .ops = { |
210 | .add = eeepc_hotk_add, | 219 | .add = eeepc_hotk_add, |
211 | .remove = eeepc_hotk_remove, | 220 | .remove = eeepc_hotk_remove, |
212 | .resume = eeepc_hotk_resume, | ||
213 | .notify = eeepc_hotk_notify, | 221 | .notify = eeepc_hotk_notify, |
214 | }, | 222 | }, |
215 | }; | 223 | }; |
@@ -579,7 +587,6 @@ static void cmsg_quirks(void) | |||
579 | 587 | ||
580 | static int eeepc_hotk_check(void) | 588 | static int eeepc_hotk_check(void) |
581 | { | 589 | { |
582 | const struct key_entry *key; | ||
583 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | 590 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
584 | int result; | 591 | int result; |
585 | 592 | ||
@@ -604,31 +611,6 @@ static int eeepc_hotk_check(void) | |||
604 | pr_info("Get control methods supported: 0x%x\n", | 611 | pr_info("Get control methods supported: 0x%x\n", |
605 | ehotk->cm_supported); | 612 | ehotk->cm_supported); |
606 | } | 613 | } |
607 | ehotk->inputdev = input_allocate_device(); | ||
608 | if (!ehotk->inputdev) { | ||
609 | pr_info("Unable to allocate input device\n"); | ||
610 | return 0; | ||
611 | } | ||
612 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | ||
613 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | ||
614 | ehotk->inputdev->id.bustype = BUS_HOST; | ||
615 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | ||
616 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | ||
617 | |||
618 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
619 | switch (key->type) { | ||
620 | case KE_KEY: | ||
621 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
622 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
623 | break; | ||
624 | } | ||
625 | } | ||
626 | result = input_register_device(ehotk->inputdev); | ||
627 | if (result) { | ||
628 | pr_info("Unable to register input device\n"); | ||
629 | input_free_device(ehotk->inputdev); | ||
630 | return 0; | ||
631 | } | ||
632 | } else { | 614 | } else { |
633 | pr_err("Hotkey device not present, aborting\n"); | 615 | pr_err("Hotkey device not present, aborting\n"); |
634 | return -EINVAL; | 616 | return -EINVAL; |
@@ -661,40 +643,48 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | |||
661 | return 0; | 643 | return 0; |
662 | } | 644 | } |
663 | 645 | ||
664 | static void eeepc_hotplug_work(struct work_struct *work) | 646 | static void eeepc_rfkill_hotplug(void) |
665 | { | 647 | { |
666 | struct pci_dev *dev; | 648 | struct pci_dev *dev; |
667 | struct pci_bus *bus = pci_find_bus(0, 1); | 649 | struct pci_bus *bus; |
668 | bool blocked; | 650 | bool blocked = eeepc_wlan_rfkill_blocked(); |
669 | 651 | ||
670 | if (!bus) { | 652 | if (ehotk->wlan_rfkill) |
671 | pr_warning("Unable to find PCI bus 1?\n"); | 653 | rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); |
672 | return; | ||
673 | } | ||
674 | 654 | ||
675 | blocked = eeepc_wlan_rfkill_blocked(); | 655 | mutex_lock(&ehotk->hotplug_lock); |
676 | if (!blocked) { | 656 | |
677 | dev = pci_get_slot(bus, 0); | 657 | if (ehotk->hotplug_slot) { |
678 | if (dev) { | 658 | bus = pci_find_bus(0, 1); |
679 | /* Device already present */ | 659 | if (!bus) { |
680 | pci_dev_put(dev); | 660 | pr_warning("Unable to find PCI bus 1?\n"); |
681 | return; | 661 | goto out_unlock; |
682 | } | ||
683 | dev = pci_scan_single_device(bus, 0); | ||
684 | if (dev) { | ||
685 | pci_bus_assign_resources(bus); | ||
686 | if (pci_bus_add_device(dev)) | ||
687 | pr_err("Unable to hotplug wifi\n"); | ||
688 | } | 662 | } |
689 | } else { | 663 | |
690 | dev = pci_get_slot(bus, 0); | 664 | if (!blocked) { |
691 | if (dev) { | 665 | dev = pci_get_slot(bus, 0); |
692 | pci_remove_bus_device(dev); | 666 | if (dev) { |
693 | pci_dev_put(dev); | 667 | /* Device already present */ |
668 | pci_dev_put(dev); | ||
669 | goto out_unlock; | ||
670 | } | ||
671 | dev = pci_scan_single_device(bus, 0); | ||
672 | if (dev) { | ||
673 | pci_bus_assign_resources(bus); | ||
674 | if (pci_bus_add_device(dev)) | ||
675 | pr_err("Unable to hotplug wifi\n"); | ||
676 | } | ||
677 | } else { | ||
678 | dev = pci_get_slot(bus, 0); | ||
679 | if (dev) { | ||
680 | pci_remove_bus_device(dev); | ||
681 | pci_dev_put(dev); | ||
682 | } | ||
694 | } | 683 | } |
695 | } | 684 | } |
696 | 685 | ||
697 | rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); | 686 | out_unlock: |
687 | mutex_unlock(&ehotk->hotplug_lock); | ||
698 | } | 688 | } |
699 | 689 | ||
700 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | 690 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) |
@@ -702,7 +692,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |||
702 | if (event != ACPI_NOTIFY_BUS_CHECK) | 692 | if (event != ACPI_NOTIFY_BUS_CHECK) |
703 | return; | 693 | return; |
704 | 694 | ||
705 | schedule_work(&ehotk->hotplug_work); | 695 | eeepc_rfkill_hotplug(); |
706 | } | 696 | } |
707 | 697 | ||
708 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) | 698 | static void eeepc_hotk_notify(struct acpi_device *device, u32 event) |
@@ -839,66 +829,38 @@ error_slot: | |||
839 | return ret; | 829 | return ret; |
840 | } | 830 | } |
841 | 831 | ||
842 | static int eeepc_hotk_add(struct acpi_device *device) | 832 | static int eeepc_hotk_thaw(struct device *device) |
843 | { | ||
844 | int result; | ||
845 | |||
846 | if (!device) | ||
847 | return -EINVAL; | ||
848 | pr_notice(EEEPC_HOTK_NAME "\n"); | ||
849 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
850 | if (!ehotk) | ||
851 | return -ENOMEM; | ||
852 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | ||
853 | ehotk->handle = device->handle; | ||
854 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | ||
855 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | ||
856 | device->driver_data = ehotk; | ||
857 | ehotk->device = device; | ||
858 | result = eeepc_hotk_check(); | ||
859 | if (result) | ||
860 | goto ehotk_fail; | ||
861 | |||
862 | return 0; | ||
863 | |||
864 | ehotk_fail: | ||
865 | kfree(ehotk); | ||
866 | ehotk = NULL; | ||
867 | |||
868 | return result; | ||
869 | } | ||
870 | |||
871 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | ||
872 | { | ||
873 | if (!device || !acpi_driver_data(device)) | ||
874 | return -EINVAL; | ||
875 | |||
876 | kfree(ehotk); | ||
877 | return 0; | ||
878 | } | ||
879 | |||
880 | static int eeepc_hotk_resume(struct acpi_device *device) | ||
881 | { | 833 | { |
882 | if (ehotk->wlan_rfkill) { | 834 | if (ehotk->wlan_rfkill) { |
883 | bool wlan; | 835 | bool wlan; |
884 | 836 | ||
885 | /* Workaround - it seems that _PTS disables the wireless | 837 | /* |
886 | without notification or changing the value read by WLAN. | 838 | * Work around bios bug - acpi _PTS turns off the wireless led |
887 | Normally this is fine because the correct value is restored | 839 | * during suspend. Normally it restores it on resume, but |
888 | from the non-volatile storage on resume, but we need to do | 840 | * we should kick it ourselves in case hibernation is aborted. |
889 | it ourself if case suspend is aborted, or we lose wireless. | ||
890 | */ | 841 | */ |
891 | wlan = get_acpi(CM_ASL_WLAN); | 842 | wlan = get_acpi(CM_ASL_WLAN); |
892 | set_acpi(CM_ASL_WLAN, wlan); | 843 | set_acpi(CM_ASL_WLAN, wlan); |
844 | } | ||
893 | 845 | ||
894 | rfkill_set_sw_state(ehotk->wlan_rfkill, wlan != 1); | 846 | return 0; |
847 | } | ||
895 | 848 | ||
896 | schedule_work(&ehotk->hotplug_work); | 849 | static int eeepc_hotk_restore(struct device *device) |
897 | } | 850 | { |
851 | /* Refresh both wlan rfkill state and pci hotplug */ | ||
852 | if (ehotk->wlan_rfkill) | ||
853 | eeepc_rfkill_hotplug(); | ||
898 | 854 | ||
899 | if (ehotk->bluetooth_rfkill) | 855 | if (ehotk->bluetooth_rfkill) |
900 | rfkill_set_sw_state(ehotk->bluetooth_rfkill, | 856 | rfkill_set_sw_state(ehotk->bluetooth_rfkill, |
901 | get_acpi(CM_ASL_BLUETOOTH) != 1); | 857 | get_acpi(CM_ASL_BLUETOOTH) != 1); |
858 | if (ehotk->wwan3g_rfkill) | ||
859 | rfkill_set_sw_state(ehotk->wwan3g_rfkill, | ||
860 | get_acpi(CM_ASL_3G) != 1); | ||
861 | if (ehotk->wimax_rfkill) | ||
862 | rfkill_set_sw_state(ehotk->wimax_rfkill, | ||
863 | get_acpi(CM_ASL_WIMAX) != 1); | ||
902 | 864 | ||
903 | return 0; | 865 | return 0; |
904 | } | 866 | } |
@@ -1019,16 +981,37 @@ static void eeepc_backlight_exit(void) | |||
1019 | 981 | ||
1020 | static void eeepc_rfkill_exit(void) | 982 | static void eeepc_rfkill_exit(void) |
1021 | { | 983 | { |
984 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5"); | ||
1022 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | 985 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); |
1023 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | 986 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); |
1024 | if (ehotk->wlan_rfkill) | 987 | if (ehotk->wlan_rfkill) { |
1025 | rfkill_unregister(ehotk->wlan_rfkill); | 988 | rfkill_unregister(ehotk->wlan_rfkill); |
1026 | if (ehotk->bluetooth_rfkill) | 989 | rfkill_destroy(ehotk->wlan_rfkill); |
1027 | rfkill_unregister(ehotk->bluetooth_rfkill); | 990 | ehotk->wlan_rfkill = NULL; |
1028 | if (ehotk->wwan3g_rfkill) | 991 | } |
1029 | rfkill_unregister(ehotk->wwan3g_rfkill); | 992 | /* |
993 | * Refresh pci hotplug in case the rfkill state was changed after | ||
994 | * eeepc_unregister_rfkill_notifier() | ||
995 | */ | ||
996 | eeepc_rfkill_hotplug(); | ||
1030 | if (ehotk->hotplug_slot) | 997 | if (ehotk->hotplug_slot) |
1031 | pci_hp_deregister(ehotk->hotplug_slot); | 998 | pci_hp_deregister(ehotk->hotplug_slot); |
999 | |||
1000 | if (ehotk->bluetooth_rfkill) { | ||
1001 | rfkill_unregister(ehotk->bluetooth_rfkill); | ||
1002 | rfkill_destroy(ehotk->bluetooth_rfkill); | ||
1003 | ehotk->bluetooth_rfkill = NULL; | ||
1004 | } | ||
1005 | if (ehotk->wwan3g_rfkill) { | ||
1006 | rfkill_unregister(ehotk->wwan3g_rfkill); | ||
1007 | rfkill_destroy(ehotk->wwan3g_rfkill); | ||
1008 | ehotk->wwan3g_rfkill = NULL; | ||
1009 | } | ||
1010 | if (ehotk->wimax_rfkill) { | ||
1011 | rfkill_unregister(ehotk->wimax_rfkill); | ||
1012 | rfkill_destroy(ehotk->wimax_rfkill); | ||
1013 | ehotk->wimax_rfkill = NULL; | ||
1014 | } | ||
1032 | } | 1015 | } |
1033 | 1016 | ||
1034 | static void eeepc_input_exit(void) | 1017 | static void eeepc_input_exit(void) |
@@ -1050,19 +1033,6 @@ static void eeepc_hwmon_exit(void) | |||
1050 | eeepc_hwmon_device = NULL; | 1033 | eeepc_hwmon_device = NULL; |
1051 | } | 1034 | } |
1052 | 1035 | ||
1053 | static void __exit eeepc_laptop_exit(void) | ||
1054 | { | ||
1055 | eeepc_backlight_exit(); | ||
1056 | eeepc_rfkill_exit(); | ||
1057 | eeepc_input_exit(); | ||
1058 | eeepc_hwmon_exit(); | ||
1059 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1060 | sysfs_remove_group(&platform_device->dev.kobj, | ||
1061 | &platform_attribute_group); | ||
1062 | platform_device_unregister(platform_device); | ||
1063 | platform_driver_unregister(&platform_driver); | ||
1064 | } | ||
1065 | |||
1066 | static int eeepc_new_rfkill(struct rfkill **rfkill, | 1036 | static int eeepc_new_rfkill(struct rfkill **rfkill, |
1067 | const char *name, struct device *dev, | 1037 | const char *name, struct device *dev, |
1068 | enum rfkill_type type, int cm) | 1038 | enum rfkill_type type, int cm) |
@@ -1094,10 +1064,7 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1094 | { | 1064 | { |
1095 | int result = 0; | 1065 | int result = 0; |
1096 | 1066 | ||
1097 | INIT_WORK(&ehotk->hotplug_work, eeepc_hotplug_work); | 1067 | mutex_init(&ehotk->hotplug_lock); |
1098 | |||
1099 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
1100 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
1101 | 1068 | ||
1102 | result = eeepc_new_rfkill(&ehotk->wlan_rfkill, | 1069 | result = eeepc_new_rfkill(&ehotk->wlan_rfkill, |
1103 | "eeepc-wlan", dev, | 1070 | "eeepc-wlan", dev, |
@@ -1120,6 +1087,13 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1120 | if (result && result != -ENODEV) | 1087 | if (result && result != -ENODEV) |
1121 | goto exit; | 1088 | goto exit; |
1122 | 1089 | ||
1090 | result = eeepc_new_rfkill(&ehotk->wimax_rfkill, | ||
1091 | "eeepc-wimax", dev, | ||
1092 | RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); | ||
1093 | |||
1094 | if (result && result != -ENODEV) | ||
1095 | goto exit; | ||
1096 | |||
1123 | result = eeepc_setup_pci_hotplug(); | 1097 | result = eeepc_setup_pci_hotplug(); |
1124 | /* | 1098 | /* |
1125 | * If we get -EBUSY then something else is handling the PCI hotplug - | 1099 | * If we get -EBUSY then something else is handling the PCI hotplug - |
@@ -1128,6 +1102,15 @@ static int eeepc_rfkill_init(struct device *dev) | |||
1128 | if (result == -EBUSY) | 1102 | if (result == -EBUSY) |
1129 | result = 0; | 1103 | result = 0; |
1130 | 1104 | ||
1105 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); | ||
1106 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
1107 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
1108 | /* | ||
1109 | * Refresh pci hotplug in case the rfkill state was changed during | ||
1110 | * setup. | ||
1111 | */ | ||
1112 | eeepc_rfkill_hotplug(); | ||
1113 | |||
1131 | exit: | 1114 | exit: |
1132 | if (result && result != -ENODEV) | 1115 | if (result && result != -ENODEV) |
1133 | eeepc_rfkill_exit(); | 1116 | eeepc_rfkill_exit(); |
@@ -1172,21 +1155,61 @@ static int eeepc_hwmon_init(struct device *dev) | |||
1172 | return result; | 1155 | return result; |
1173 | } | 1156 | } |
1174 | 1157 | ||
1175 | static int __init eeepc_laptop_init(void) | 1158 | static int eeepc_input_init(struct device *dev) |
1176 | { | 1159 | { |
1177 | struct device *dev; | 1160 | const struct key_entry *key; |
1178 | int result; | 1161 | int result; |
1179 | 1162 | ||
1180 | if (acpi_disabled) | 1163 | ehotk->inputdev = input_allocate_device(); |
1181 | return -ENODEV; | 1164 | if (!ehotk->inputdev) { |
1182 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | 1165 | pr_info("Unable to allocate input device\n"); |
1183 | if (result < 0) | 1166 | return -ENOMEM; |
1167 | } | ||
1168 | ehotk->inputdev->name = "Asus EeePC extra buttons"; | ||
1169 | ehotk->inputdev->dev.parent = dev; | ||
1170 | ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; | ||
1171 | ehotk->inputdev->id.bustype = BUS_HOST; | ||
1172 | ehotk->inputdev->getkeycode = eeepc_getkeycode; | ||
1173 | ehotk->inputdev->setkeycode = eeepc_setkeycode; | ||
1174 | |||
1175 | for (key = eeepc_keymap; key->type != KE_END; key++) { | ||
1176 | switch (key->type) { | ||
1177 | case KE_KEY: | ||
1178 | set_bit(EV_KEY, ehotk->inputdev->evbit); | ||
1179 | set_bit(key->keycode, ehotk->inputdev->keybit); | ||
1180 | break; | ||
1181 | } | ||
1182 | } | ||
1183 | result = input_register_device(ehotk->inputdev); | ||
1184 | if (result) { | ||
1185 | pr_info("Unable to register input device\n"); | ||
1186 | input_free_device(ehotk->inputdev); | ||
1184 | return result; | 1187 | return result; |
1185 | if (!ehotk) { | ||
1186 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1187 | return -ENODEV; | ||
1188 | } | 1188 | } |
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static int eeepc_hotk_add(struct acpi_device *device) | ||
1193 | { | ||
1194 | struct device *dev; | ||
1195 | int result; | ||
1189 | 1196 | ||
1197 | if (!device) | ||
1198 | return -EINVAL; | ||
1199 | pr_notice(EEEPC_HOTK_NAME "\n"); | ||
1200 | ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); | ||
1201 | if (!ehotk) | ||
1202 | return -ENOMEM; | ||
1203 | ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; | ||
1204 | ehotk->handle = device->handle; | ||
1205 | strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); | ||
1206 | strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); | ||
1207 | device->driver_data = ehotk; | ||
1208 | ehotk->device = device; | ||
1209 | |||
1210 | result = eeepc_hotk_check(); | ||
1211 | if (result) | ||
1212 | goto fail_platform_driver; | ||
1190 | eeepc_enable_camera(); | 1213 | eeepc_enable_camera(); |
1191 | 1214 | ||
1192 | /* Register platform stuff */ | 1215 | /* Register platform stuff */ |
@@ -1216,6 +1239,10 @@ static int __init eeepc_laptop_init(void) | |||
1216 | pr_info("Backlight controlled by ACPI video " | 1239 | pr_info("Backlight controlled by ACPI video " |
1217 | "driver\n"); | 1240 | "driver\n"); |
1218 | 1241 | ||
1242 | result = eeepc_input_init(dev); | ||
1243 | if (result) | ||
1244 | goto fail_input; | ||
1245 | |||
1219 | result = eeepc_hwmon_init(dev); | 1246 | result = eeepc_hwmon_init(dev); |
1220 | if (result) | 1247 | if (result) |
1221 | goto fail_hwmon; | 1248 | goto fail_hwmon; |
@@ -1225,9 +1252,12 @@ static int __init eeepc_laptop_init(void) | |||
1225 | goto fail_rfkill; | 1252 | goto fail_rfkill; |
1226 | 1253 | ||
1227 | return 0; | 1254 | return 0; |
1255 | |||
1228 | fail_rfkill: | 1256 | fail_rfkill: |
1229 | eeepc_hwmon_exit(); | 1257 | eeepc_hwmon_exit(); |
1230 | fail_hwmon: | 1258 | fail_hwmon: |
1259 | eeepc_input_exit(); | ||
1260 | fail_input: | ||
1231 | eeepc_backlight_exit(); | 1261 | eeepc_backlight_exit(); |
1232 | fail_backlight: | 1262 | fail_backlight: |
1233 | sysfs_remove_group(&platform_device->dev.kobj, | 1263 | sysfs_remove_group(&platform_device->dev.kobj, |
@@ -1239,9 +1269,49 @@ fail_platform_device2: | |||
1239 | fail_platform_device1: | 1269 | fail_platform_device1: |
1240 | platform_driver_unregister(&platform_driver); | 1270 | platform_driver_unregister(&platform_driver); |
1241 | fail_platform_driver: | 1271 | fail_platform_driver: |
1242 | eeepc_input_exit(); | 1272 | kfree(ehotk); |
1273 | |||
1243 | return result; | 1274 | return result; |
1244 | } | 1275 | } |
1245 | 1276 | ||
1277 | static int eeepc_hotk_remove(struct acpi_device *device, int type) | ||
1278 | { | ||
1279 | if (!device || !acpi_driver_data(device)) | ||
1280 | return -EINVAL; | ||
1281 | |||
1282 | eeepc_backlight_exit(); | ||
1283 | eeepc_rfkill_exit(); | ||
1284 | eeepc_input_exit(); | ||
1285 | eeepc_hwmon_exit(); | ||
1286 | sysfs_remove_group(&platform_device->dev.kobj, | ||
1287 | &platform_attribute_group); | ||
1288 | platform_device_unregister(platform_device); | ||
1289 | platform_driver_unregister(&platform_driver); | ||
1290 | |||
1291 | kfree(ehotk); | ||
1292 | return 0; | ||
1293 | } | ||
1294 | |||
1295 | static int __init eeepc_laptop_init(void) | ||
1296 | { | ||
1297 | int result; | ||
1298 | |||
1299 | if (acpi_disabled) | ||
1300 | return -ENODEV; | ||
1301 | result = acpi_bus_register_driver(&eeepc_hotk_driver); | ||
1302 | if (result < 0) | ||
1303 | return result; | ||
1304 | if (!ehotk) { | ||
1305 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1306 | return -ENODEV; | ||
1307 | } | ||
1308 | return 0; | ||
1309 | } | ||
1310 | |||
1311 | static void __exit eeepc_laptop_exit(void) | ||
1312 | { | ||
1313 | acpi_bus_unregister_driver(&eeepc_hotk_driver); | ||
1314 | } | ||
1315 | |||
1246 | module_init(eeepc_laptop_init); | 1316 | module_init(eeepc_laptop_init); |
1247 | module_exit(eeepc_laptop_exit); | 1317 | module_exit(eeepc_laptop_exit); |