aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2009-01-09 04:01:26 -0500
committerLen Brown <len.brown@intel.com>2009-01-09 04:56:56 -0500
commitd97c0defba25a959a990f6d4759f43075540832e (patch)
tree27dceaf310e01a6bbcceee3550112b19202f142f /drivers/platform
parentec9f168fcc344d2ffec1c8c822076bf22dab5c33 (diff)
parentb4f9fe12157a33351d0df78e925dcacd13252783 (diff)
Merge branch 'drivers-platform' into release
Conflicts: drivers/misc/Kconfig Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/Kconfig5
-rw-r--r--drivers/platform/Makefile5
-rw-r--r--drivers/platform/x86/Kconfig375
-rw-r--r--drivers/platform/x86/Makefile19
-rw-r--r--drivers/platform/x86/acer-wmi.c1345
-rw-r--r--drivers/platform/x86/asus-laptop.c1266
-rw-r--r--drivers/platform/x86/asus_acpi.c1460
-rw-r--r--drivers/platform/x86/compal-laptop.c406
-rw-r--r--drivers/platform/x86/eeepc-laptop.c872
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c1293
-rw-r--r--drivers/platform/x86/hp-wmi.c512
-rw-r--r--drivers/platform/x86/intel_menlow.c536
-rw-r--r--drivers/platform/x86/msi-laptop.c437
-rw-r--r--drivers/platform/x86/panasonic-laptop.c744
-rw-r--r--drivers/platform/x86/sony-laptop.c2784
-rw-r--r--drivers/platform/x86/tc1100-wmi.c289
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c6948
-rw-r--r--drivers/platform/x86/toshiba_acpi.c863
-rw-r--r--drivers/platform/x86/wmi.c747
19 files changed, 20906 insertions, 0 deletions
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
new file mode 100644
index 000000000000..9652c3fe7f5e
--- /dev/null
+++ b/drivers/platform/Kconfig
@@ -0,0 +1,5 @@
1# drivers/platform/Kconfig
2
3if X86
4source "drivers/platform/x86/Kconfig"
5endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
new file mode 100644
index 000000000000..782953ae4c03
--- /dev/null
+++ b/drivers/platform/Makefile
@@ -0,0 +1,5 @@
1#
2# Makefile for linux/drivers/platform
3#
4
5obj-$(CONFIG_X86) += x86/
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
new file mode 100644
index 000000000000..e65448e99b48
--- /dev/null
+++ b/drivers/platform/x86/Kconfig
@@ -0,0 +1,375 @@
1#
2# X86 Platform Specific Drivers
3#
4
5menuconfig X86_PLATFORM_DEVICES
6 bool "X86 Platform Specific Device Drivers"
7 default y
8 ---help---
9 Say Y here to get to see options for device drivers for various
10 x86 platforms, including vendor-specific laptop extension drivers.
11 This option alone does not add any kernel code.
12
13 If you say N, all options in this submenu will be skipped and disabled.
14
15if X86_PLATFORM_DEVICES
16
17config ACER_WMI
18 tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
19 depends on EXPERIMENTAL
20 depends on ACPI
21 depends on LEDS_CLASS
22 depends on NEW_LEDS
23 depends on BACKLIGHT_CLASS_DEVICE
24 depends on SERIO_I8042
25 depends on RFKILL
26 select ACPI_WMI
27 ---help---
28 This is a driver for newer Acer (and Wistron) laptops. It adds
29 wireless radio and bluetooth control, and on some laptops,
30 exposes the mail LED and LCD backlight.
31
32 For more information about this driver see
33 <file:Documentation/laptops/acer-wmi.txt>
34
35 If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
36 here.
37
38config ASUS_LAPTOP
39 tristate "Asus Laptop Extras (EXPERIMENTAL)"
40 depends on ACPI
41 depends on EXPERIMENTAL && !ACPI_ASUS
42 depends on LEDS_CLASS
43 depends on NEW_LEDS
44 depends on BACKLIGHT_CLASS_DEVICE
45 ---help---
46 This is the new Linux driver for Asus laptops. It may also support some
47 MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
48 standard ACPI events that go through /proc/acpi/events. It also adds
49 support for video output switching, LCD backlight control, Bluetooth and
50 Wlan control, and most importantly, allows you to blink those fancy LEDs.
51
52 For more information and a userspace daemon for handling the extra
53 buttons see <http://acpi4asus.sf.net/>.
54
55 If you have an ACPI-compatible ASUS laptop, say Y or M here.
56
57config FUJITSU_LAPTOP
58 tristate "Fujitsu Laptop Extras"
59 depends on ACPI
60 depends on INPUT
61 depends on BACKLIGHT_CLASS_DEVICE
62 ---help---
63 This is a driver for laptops built by Fujitsu:
64
65 * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks
66 * Possibly other Fujitsu laptop models
67 * Tested with S6410 and S7020
68
69 It adds support for LCD brightness control and some hotkeys.
70
71 If you have a Fujitsu laptop, say Y or M here.
72
73config FUJITSU_LAPTOP_DEBUG
74 bool "Verbose debug mode for Fujitsu Laptop Extras"
75 depends on FUJITSU_LAPTOP
76 default n
77 ---help---
78 Enables extra debug output from the fujitsu extras driver, at the
79 expense of a slight increase in driver size.
80
81 If you are not sure, say N here.
82
83config TC1100_WMI
84 tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
85 depends on !X86_64
86 depends on EXPERIMENTAL
87 depends on ACPI
88 select ACPI_WMI
89 ---help---
90 This is a driver for the WMI extensions (wireless and bluetooth power
91 control) of the HP Compaq TC1100 tablet.
92
93config HP_WMI
94 tristate "HP WMI extras"
95 depends on ACPI_WMI
96 depends on INPUT
97 depends on RFKILL
98 help
99 Say Y here if you want to support WMI-based hotkeys on HP laptops and
100 to read data from WMI such as docking or ambient light sensor state.
101
102 To compile this driver as a module, choose M here: the module will
103 be called hp-wmi.
104
105config MSI_LAPTOP
106 tristate "MSI Laptop Extras"
107 depends on ACPI
108 depends on BACKLIGHT_CLASS_DEVICE
109 ---help---
110 This is a driver for laptops built by MSI (MICRO-STAR
111 INTERNATIONAL):
112
113 MSI MegaBook S270 (MS-1013)
114 Cytron/TCM/Medion/Tchibo MD96100/SAM2000
115
116 It adds support for Bluetooth, WLAN and LCD brightness control.
117
118 More information about this driver is available at
119 <http://0pointer.de/lennart/tchibo.html>.
120
121 If you have an MSI S270 laptop, say Y or M here.
122
123config PANASONIC_LAPTOP
124 tristate "Panasonic Laptop Extras"
125 depends on INPUT && ACPI
126 depends on BACKLIGHT_CLASS_DEVICE
127 ---help---
128 This driver adds support for access to backlight control and hotkeys
129 on Panasonic Let's Note laptops.
130
131 If you have a Panasonic Let's note laptop (such as the R1(N variant),
132 R2, R3, R5, T2, W2 and Y2 series), say Y.
133
134config COMPAL_LAPTOP
135 tristate "Compal Laptop Extras"
136 depends on ACPI
137 depends on BACKLIGHT_CLASS_DEVICE
138 ---help---
139 This is a driver for laptops built by Compal:
140
141 Compal FL90/IFL90
142 Compal FL91/IFL91
143 Compal FL92/JFL92
144 Compal FT00/IFT00
145
146 It adds support for Bluetooth, WLAN and LCD brightness control.
147
148 If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
149
150config SONY_LAPTOP
151 tristate "Sony Laptop Extras"
152 depends on ACPI
153 select BACKLIGHT_CLASS_DEVICE
154 depends on INPUT
155 ---help---
156 This mini-driver drives the SNC and SPIC devices present in the ACPI
157 BIOS of the Sony Vaio laptops.
158
159 It gives access to some extra laptop functionalities like Bluetooth,
160 screen brightness control, Fn keys and allows powering on/off some
161 devices.
162
163 Read <file:Documentation/laptops/sony-laptop.txt> for more information.
164
165config SONYPI_COMPAT
166 bool "Sonypi compatibility"
167 depends on SONY_LAPTOP
168 ---help---
169 Build the sonypi driver compatibility code into the sony-laptop driver.
170
171config THINKPAD_ACPI
172 tristate "ThinkPad ACPI Laptop Extras"
173 depends on ACPI
174 select BACKLIGHT_LCD_SUPPORT
175 select BACKLIGHT_CLASS_DEVICE
176 select HWMON
177 select NVRAM
178 select INPUT
179 select NEW_LEDS
180 select LEDS_CLASS
181 select NET
182 select RFKILL
183 ---help---
184 This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
185 support for Fn-Fx key combinations, Bluetooth control, video
186 output switching, ThinkLight control, UltraBay eject and more.
187 For more information about this driver see
188 <file:Documentation/laptops/thinkpad-acpi.txt> and
189 <http://ibm-acpi.sf.net/> .
190
191 This driver was formerly known as ibm-acpi.
192
193 If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
194
195config THINKPAD_ACPI_DEBUG
196 bool "Verbose debug mode"
197 depends on THINKPAD_ACPI
198 default n
199 ---help---
200 Enables extra debugging information, at the expense of a slightly
201 increase in driver size.
202
203 If you are not sure, say N here.
204
205config THINKPAD_ACPI_DOCK
206 bool "Legacy Docking Station Support"
207 depends on THINKPAD_ACPI
208 depends on ACPI_DOCK=n
209 default n
210 ---help---
211 Allows the thinkpad_acpi driver to handle docking station events.
212 This support was made obsolete by the generic ACPI docking station
213 support (CONFIG_ACPI_DOCK). It will allow locking and removing the
214 laptop from the docking station, but will not properly connect PCI
215 devices.
216
217 If you are not sure, say N here.
218
219config THINKPAD_ACPI_BAY
220 bool "Legacy Removable Bay Support"
221 depends on THINKPAD_ACPI
222 default y
223 ---help---
224 Allows the thinkpad_acpi driver to handle removable bays. It will
225 electrically disable the device in the bay, and also generate
226 notifications when the bay lever is ejected or inserted.
227
228 If you are not sure, say Y here.
229
230config THINKPAD_ACPI_VIDEO
231 bool "Video output control support"
232 depends on THINKPAD_ACPI
233 default y
234 ---help---
235 Allows the thinkpad_acpi driver to provide an interface to control
236 the various video output ports.
237
238 This feature often won't work well, depending on ThinkPad model,
239 display state, video output devices in use, whether there is a X
240 server running, phase of the moon, and the current mood of
241 Schroedinger's cat. If you can use X.org's RandR to control
242 your ThinkPad's video output ports instead of this feature,
243 don't think twice: do it and say N here to save some memory.
244
245 If you are not sure, say Y here.
246
247config THINKPAD_ACPI_HOTKEY_POLL
248 bool "Support NVRAM polling for hot keys"
249 depends on THINKPAD_ACPI
250 default y
251 ---help---
252 Some thinkpad models benefit from NVRAM polling to detect a few of
253 the hot key press events. If you know your ThinkPad model does not
254 need to do NVRAM polling to support any of the hot keys you use,
255 unselecting this option will save about 1kB of memory.
256
257 ThinkPads T40 and newer, R52 and newer, and X31 and newer are
258 unlikely to need NVRAM polling in their latest BIOS versions.
259
260 NVRAM polling can detect at most the following keys: ThinkPad/Access
261 IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
262 Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
263
264 If you are not sure, say Y here. The driver enables polling only if
265 it is strictly necessary to do so.
266
267config INTEL_MENLOW
268 tristate "Thermal Management driver for Intel menlow platform"
269 depends on ACPI_THERMAL
270 select THERMAL
271 ---help---
272 ACPI thermal management enhancement driver on
273 Intel Menlow platform.
274
275 If unsure, say N.
276
277config EEEPC_LAPTOP
278 tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
279 depends on ACPI
280 depends on EXPERIMENTAL
281 select BACKLIGHT_CLASS_DEVICE
282 select HWMON
283 select RFKILL
284 ---help---
285 This driver supports the Fn-Fx keys on Eee PC laptops.
286 It also adds the ability to switch camera/wlan on/off.
287
288 If you have an Eee PC laptop, say Y or M here.
289
290
291config ACPI_WMI
292 tristate "WMI (EXPERIMENTAL)"
293 depends on ACPI
294 depends on EXPERIMENTAL
295 help
296 This driver adds support for the ACPI-WMI (Windows Management
297 Instrumentation) mapper device (PNP0C14) found on some systems.
298
299 ACPI-WMI is a proprietary extension to ACPI to expose parts of the
300 ACPI firmware to userspace - this is done through various vendor
301 defined methods and data blocks in a PNP0C14 device, which are then
302 made available for userspace to call.
303
304 The implementation of this in Linux currently only exposes this to
305 other kernel space drivers.
306
307 This driver is a required dependency to build the firmware specific
308 drivers needed on many machines, including Acer and HP laptops.
309
310 It is safe to enable this driver even if your DSDT doesn't define
311 any ACPI-WMI devices.
312
313config ACPI_ASUS
314 tristate "ASUS/Medion Laptop Extras"
315 depends on ACPI
316 select BACKLIGHT_CLASS_DEVICE
317 ---help---
318 This driver provides support for extra features of ACPI-compatible
319 ASUS laptops. As some of Medion laptops are made by ASUS, it may also
320 support some Medion laptops (such as 9675 for example). It makes all
321 the extra buttons generate standard ACPI events that go through
322 /proc/acpi/events, and (on some models) adds support for changing the
323 display brightness and output, switching the LCD backlight on and off,
324 and most importantly, allows you to blink those fancy LEDs intended
325 for reporting mail and wireless status.
326
327 Note: display switching code is currently considered EXPERIMENTAL,
328 toying with these values may even lock your machine.
329
330 All settings are changed via /proc/acpi/asus directory entries. Owner
331 and group for these entries can be set with asus_uid and asus_gid
332 parameters.
333
334 More information and a userspace daemon for handling the extra buttons
335 at <http://sourceforge.net/projects/acpi4asus/>.
336
337 If you have an ACPI-compatible ASUS laptop, say Y or M here. This
338 driver is still under development, so if your laptop is unsupported or
339 something works not quite as expected, please use the mailing list
340 available on the above page (acpi4asus-user@lists.sourceforge.net).
341
342 NOTE: This driver is deprecated and will probably be removed soon,
343 use asus-laptop instead.
344
345config ACPI_TOSHIBA
346 tristate "Toshiba Laptop Extras"
347 depends on ACPI
348 depends on INPUT
349 select INPUT_POLLDEV
350 select NET
351 select RFKILL
352 select BACKLIGHT_CLASS_DEVICE
353 ---help---
354 This driver adds support for access to certain system settings
355 on "legacy free" Toshiba laptops. These laptops can be recognized by
356 their lack of a BIOS setup menu and APM support.
357
358 On these machines, all system configuration is handled through the
359 ACPI. This driver is required for access to controls not covered
360 by the general ACPI drivers, such as LCD brightness, video output,
361 etc.
362
363 This driver differs from the non-ACPI Toshiba laptop driver (located
364 under "Processor type and features") in several aspects.
365 Configuration is accessed by reading and writing text files in the
366 /proc tree instead of by program interface to /dev. Furthermore, no
367 power management functions are exposed, as those are handled by the
368 general ACPI drivers.
369
370 More information about this driver is available at
371 <http://memebeam.org/toys/ToshibaAcpiDriver>.
372
373 If you have a legacy free Toshiba laptop (such as the Libretto L1
374 series), say Y.
375endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
new file mode 100644
index 000000000000..1e9de2ae0de5
--- /dev/null
+++ b/drivers/platform/x86/Makefile
@@ -0,0 +1,19 @@
1#
2# Makefile for linux/drivers/platform/x86
3# x86 Platform-Specific Drivers
4#
5obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
6obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
7obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
8obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
9obj-$(CONFIG_ACER_WMI) += acer-wmi.o
10obj-$(CONFIG_HP_WMI) += hp-wmi.o
11obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
12obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
13obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
14obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
15obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
16obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
17obj-$(CONFIG_ACPI_WMI) += wmi.o
18obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
19obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
new file mode 100644
index 000000000000..94c9f911824e
--- /dev/null
+++ b/drivers/platform/x86/acer-wmi.c
@@ -0,0 +1,1345 @@
1/*
2 * Acer WMI Laptop Extras
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * Based on acer_acpi:
7 * Copyright (C) 2005-2007 E.M. Smith
8 * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/types.h>
29#include <linux/dmi.h>
30#include <linux/fb.h>
31#include <linux/backlight.h>
32#include <linux/leds.h>
33#include <linux/platform_device.h>
34#include <linux/acpi.h>
35#include <linux/i8042.h>
36#include <linux/rfkill.h>
37#include <linux/workqueue.h>
38#include <linux/debugfs.h>
39
40#include <acpi/acpi_drivers.h>
41
42MODULE_AUTHOR("Carlos Corbacho");
43MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
44MODULE_LICENSE("GPL");
45
46#define ACER_LOGPREFIX "acer-wmi: "
47#define ACER_ERR KERN_ERR ACER_LOGPREFIX
48#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
49#define ACER_INFO KERN_INFO ACER_LOGPREFIX
50
51/*
52 * The following defines quirks to get some specific functions to work
53 * which are known to not be supported over ACPI-WMI (such as the mail LED
54 * on WMID based Acer's)
55 */
56struct acer_quirks {
57 const char *vendor;
58 const char *model;
59 u16 quirks;
60};
61
62/*
63 * Magic Number
64 * Meaning is unknown - this number is required for writing to ACPI for AMW0
65 * (it's also used in acerhk when directly accessing the BIOS)
66 */
67#define ACER_AMW0_WRITE 0x9610
68
69/*
70 * Bit masks for the AMW0 interface
71 */
72#define ACER_AMW0_WIRELESS_MASK 0x35
73#define ACER_AMW0_BLUETOOTH_MASK 0x34
74#define ACER_AMW0_MAILLED_MASK 0x31
75
76/*
77 * Method IDs for WMID interface
78 */
79#define ACER_WMID_GET_WIRELESS_METHODID 1
80#define ACER_WMID_GET_BLUETOOTH_METHODID 2
81#define ACER_WMID_GET_BRIGHTNESS_METHODID 3
82#define ACER_WMID_SET_WIRELESS_METHODID 4
83#define ACER_WMID_SET_BLUETOOTH_METHODID 5
84#define ACER_WMID_SET_BRIGHTNESS_METHODID 6
85#define ACER_WMID_GET_THREEG_METHODID 10
86#define ACER_WMID_SET_THREEG_METHODID 11
87
88/*
89 * Acer ACPI method GUIDs
90 */
91#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
92#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
93#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
94#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
95
96MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
97MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
98
99/* Temporary workaround until the WMI sysfs interface goes in */
100MODULE_ALIAS("dmi:*:*Acer*:*:");
101
102/*
103 * Interface capability flags
104 */
105#define ACER_CAP_MAILLED (1<<0)
106#define ACER_CAP_WIRELESS (1<<1)
107#define ACER_CAP_BLUETOOTH (1<<2)
108#define ACER_CAP_BRIGHTNESS (1<<3)
109#define ACER_CAP_THREEG (1<<4)
110#define ACER_CAP_ANY (0xFFFFFFFF)
111
112/*
113 * Interface type flags
114 */
115enum interface_flags {
116 ACER_AMW0,
117 ACER_AMW0_V2,
118 ACER_WMID,
119};
120
121#define ACER_DEFAULT_WIRELESS 0
122#define ACER_DEFAULT_BLUETOOTH 0
123#define ACER_DEFAULT_MAILLED 0
124#define ACER_DEFAULT_THREEG 0
125
126static int max_brightness = 0xF;
127
128static int mailled = -1;
129static int brightness = -1;
130static int threeg = -1;
131static int force_series;
132
133module_param(mailled, int, 0444);
134module_param(brightness, int, 0444);
135module_param(threeg, int, 0444);
136module_param(force_series, int, 0444);
137MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
138MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
139MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
140MODULE_PARM_DESC(force_series, "Force a different laptop series");
141
142struct acer_data {
143 int mailled;
144 int threeg;
145 int brightness;
146};
147
148struct acer_debug {
149 struct dentry *root;
150 struct dentry *devices;
151 u32 wmid_devices;
152};
153
154static struct rfkill *wireless_rfkill;
155static struct rfkill *bluetooth_rfkill;
156
157/* Each low-level interface must define at least some of the following */
158struct wmi_interface {
159 /* The WMI device type */
160 u32 type;
161
162 /* The capabilities this interface provides */
163 u32 capability;
164
165 /* Private data for the current interface */
166 struct acer_data data;
167
168 /* debugfs entries associated with this interface */
169 struct acer_debug debug;
170};
171
172/* The static interface pointer, points to the currently detected interface */
173static struct wmi_interface *interface;
174
175/*
176 * Embedded Controller quirks
177 * Some laptops require us to directly access the EC to either enable or query
178 * features that are not available through WMI.
179 */
180
181struct quirk_entry {
182 u8 wireless;
183 u8 mailled;
184 s8 brightness;
185 u8 bluetooth;
186};
187
188static struct quirk_entry *quirks;
189
190static void set_quirks(void)
191{
192 if (!interface)
193 return;
194
195 if (quirks->mailled)
196 interface->capability |= ACER_CAP_MAILLED;
197
198 if (quirks->brightness)
199 interface->capability |= ACER_CAP_BRIGHTNESS;
200}
201
202static int dmi_matched(const struct dmi_system_id *dmi)
203{
204 quirks = dmi->driver_data;
205 return 0;
206}
207
208static struct quirk_entry quirk_unknown = {
209};
210
211static struct quirk_entry quirk_acer_aspire_1520 = {
212 .brightness = -1,
213};
214
215static struct quirk_entry quirk_acer_travelmate_2490 = {
216 .mailled = 1,
217};
218
219/* This AMW0 laptop has no bluetooth */
220static struct quirk_entry quirk_medion_md_98300 = {
221 .wireless = 1,
222};
223
224static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
225 .wireless = 2,
226};
227
228static struct dmi_system_id acer_quirks[] = {
229 {
230 .callback = dmi_matched,
231 .ident = "Acer Aspire 1360",
232 .matches = {
233 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
234 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
235 },
236 .driver_data = &quirk_acer_aspire_1520,
237 },
238 {
239 .callback = dmi_matched,
240 .ident = "Acer Aspire 1520",
241 .matches = {
242 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
243 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
244 },
245 .driver_data = &quirk_acer_aspire_1520,
246 },
247 {
248 .callback = dmi_matched,
249 .ident = "Acer Aspire 3100",
250 .matches = {
251 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
252 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
253 },
254 .driver_data = &quirk_acer_travelmate_2490,
255 },
256 {
257 .callback = dmi_matched,
258 .ident = "Acer Aspire 3610",
259 .matches = {
260 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
261 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
262 },
263 .driver_data = &quirk_acer_travelmate_2490,
264 },
265 {
266 .callback = dmi_matched,
267 .ident = "Acer Aspire 5100",
268 .matches = {
269 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
270 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
271 },
272 .driver_data = &quirk_acer_travelmate_2490,
273 },
274 {
275 .callback = dmi_matched,
276 .ident = "Acer Aspire 5610",
277 .matches = {
278 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
279 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
280 },
281 .driver_data = &quirk_acer_travelmate_2490,
282 },
283 {
284 .callback = dmi_matched,
285 .ident = "Acer Aspire 5630",
286 .matches = {
287 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
288 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
289 },
290 .driver_data = &quirk_acer_travelmate_2490,
291 },
292 {
293 .callback = dmi_matched,
294 .ident = "Acer Aspire 5650",
295 .matches = {
296 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
297 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
298 },
299 .driver_data = &quirk_acer_travelmate_2490,
300 },
301 {
302 .callback = dmi_matched,
303 .ident = "Acer Aspire 5680",
304 .matches = {
305 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
306 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
307 },
308 .driver_data = &quirk_acer_travelmate_2490,
309 },
310 {
311 .callback = dmi_matched,
312 .ident = "Acer Aspire 9110",
313 .matches = {
314 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
315 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
316 },
317 .driver_data = &quirk_acer_travelmate_2490,
318 },
319 {
320 .callback = dmi_matched,
321 .ident = "Acer TravelMate 2490",
322 .matches = {
323 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
324 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
325 },
326 .driver_data = &quirk_acer_travelmate_2490,
327 },
328 {
329 .callback = dmi_matched,
330 .ident = "Acer TravelMate 4200",
331 .matches = {
332 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
333 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
334 },
335 .driver_data = &quirk_acer_travelmate_2490,
336 },
337 {
338 .callback = dmi_matched,
339 .ident = "Fujitsu Siemens Amilo Li 1718",
340 .matches = {
341 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
342 DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
343 },
344 .driver_data = &quirk_fujitsu_amilo_li_1718,
345 },
346 {
347 .callback = dmi_matched,
348 .ident = "Medion MD 98300",
349 .matches = {
350 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
351 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
352 },
353 .driver_data = &quirk_medion_md_98300,
354 },
355 {}
356};
357
358/* Find which quirks are needed for a particular vendor/ model pair */
359static void find_quirks(void)
360{
361 if (!force_series) {
362 dmi_check_system(acer_quirks);
363 } else if (force_series == 2490) {
364 quirks = &quirk_acer_travelmate_2490;
365 }
366
367 if (quirks == NULL)
368 quirks = &quirk_unknown;
369
370 set_quirks();
371}
372
373/*
374 * General interface convenience methods
375 */
376
377static bool has_cap(u32 cap)
378{
379 if ((interface->capability & cap) != 0)
380 return 1;
381
382 return 0;
383}
384
385/*
386 * AMW0 (V1) interface
387 */
388struct wmab_args {
389 u32 eax;
390 u32 ebx;
391 u32 ecx;
392 u32 edx;
393};
394
395struct wmab_ret {
396 u32 eax;
397 u32 ebx;
398 u32 ecx;
399 u32 edx;
400 u32 eex;
401};
402
403static acpi_status wmab_execute(struct wmab_args *regbuf,
404struct acpi_buffer *result)
405{
406 struct acpi_buffer input;
407 acpi_status status;
408 input.length = sizeof(struct wmab_args);
409 input.pointer = (u8 *)regbuf;
410
411 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
412
413 return status;
414}
415
416static acpi_status AMW0_get_u32(u32 *value, u32 cap,
417struct wmi_interface *iface)
418{
419 int err;
420 u8 result;
421
422 switch (cap) {
423 case ACER_CAP_MAILLED:
424 switch (quirks->mailled) {
425 default:
426 err = ec_read(0xA, &result);
427 if (err)
428 return AE_ERROR;
429 *value = (result >> 7) & 0x1;
430 return AE_OK;
431 }
432 break;
433 case ACER_CAP_WIRELESS:
434 switch (quirks->wireless) {
435 case 1:
436 err = ec_read(0x7B, &result);
437 if (err)
438 return AE_ERROR;
439 *value = result & 0x1;
440 return AE_OK;
441 case 2:
442 err = ec_read(0x71, &result);
443 if (err)
444 return AE_ERROR;
445 *value = result & 0x1;
446 return AE_OK;
447 default:
448 err = ec_read(0xA, &result);
449 if (err)
450 return AE_ERROR;
451 *value = (result >> 2) & 0x1;
452 return AE_OK;
453 }
454 break;
455 case ACER_CAP_BLUETOOTH:
456 switch (quirks->bluetooth) {
457 default:
458 err = ec_read(0xA, &result);
459 if (err)
460 return AE_ERROR;
461 *value = (result >> 4) & 0x1;
462 return AE_OK;
463 }
464 break;
465 case ACER_CAP_BRIGHTNESS:
466 switch (quirks->brightness) {
467 default:
468 err = ec_read(0x83, &result);
469 if (err)
470 return AE_ERROR;
471 *value = result;
472 return AE_OK;
473 }
474 break;
475 default:
476 return AE_ERROR;
477 }
478 return AE_OK;
479}
480
481static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
482{
483 struct wmab_args args;
484
485 args.eax = ACER_AMW0_WRITE;
486 args.ebx = value ? (1<<8) : 0;
487 args.ecx = args.edx = 0;
488
489 switch (cap) {
490 case ACER_CAP_MAILLED:
491 if (value > 1)
492 return AE_BAD_PARAMETER;
493 args.ebx |= ACER_AMW0_MAILLED_MASK;
494 break;
495 case ACER_CAP_WIRELESS:
496 if (value > 1)
497 return AE_BAD_PARAMETER;
498 args.ebx |= ACER_AMW0_WIRELESS_MASK;
499 break;
500 case ACER_CAP_BLUETOOTH:
501 if (value > 1)
502 return AE_BAD_PARAMETER;
503 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
504 break;
505 case ACER_CAP_BRIGHTNESS:
506 if (value > max_brightness)
507 return AE_BAD_PARAMETER;
508 switch (quirks->brightness) {
509 default:
510 return ec_write(0x83, value);
511 break;
512 }
513 default:
514 return AE_ERROR;
515 }
516
517 /* Actually do the set */
518 return wmab_execute(&args, NULL);
519}
520
521static acpi_status AMW0_find_mailled(void)
522{
523 struct wmab_args args;
524 struct wmab_ret ret;
525 acpi_status status = AE_OK;
526 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
527 union acpi_object *obj;
528
529 args.eax = 0x86;
530 args.ebx = args.ecx = args.edx = 0;
531
532 status = wmab_execute(&args, &out);
533 if (ACPI_FAILURE(status))
534 return status;
535
536 obj = (union acpi_object *) out.pointer;
537 if (obj && obj->type == ACPI_TYPE_BUFFER &&
538 obj->buffer.length == sizeof(struct wmab_ret)) {
539 ret = *((struct wmab_ret *) obj->buffer.pointer);
540 } else {
541 return AE_ERROR;
542 }
543
544 if (ret.eex & 0x1)
545 interface->capability |= ACER_CAP_MAILLED;
546
547 kfree(out.pointer);
548
549 return AE_OK;
550}
551
552static acpi_status AMW0_set_capabilities(void)
553{
554 struct wmab_args args;
555 struct wmab_ret ret;
556 acpi_status status = AE_OK;
557 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
558 union acpi_object *obj;
559
560 /*
561 * On laptops with this strange GUID (non Acer), normal probing doesn't
562 * work.
563 */
564 if (wmi_has_guid(AMW0_GUID2)) {
565 interface->capability |= ACER_CAP_WIRELESS;
566 return AE_OK;
567 }
568
569 args.eax = ACER_AMW0_WRITE;
570 args.ecx = args.edx = 0;
571
572 args.ebx = 0xa2 << 8;
573 args.ebx |= ACER_AMW0_WIRELESS_MASK;
574
575 status = wmab_execute(&args, &out);
576 if (ACPI_FAILURE(status))
577 return status;
578
579 obj = (union acpi_object *) out.pointer;
580 if (obj && obj->type == ACPI_TYPE_BUFFER &&
581 obj->buffer.length == sizeof(struct wmab_ret)) {
582 ret = *((struct wmab_ret *) obj->buffer.pointer);
583 } else {
584 return AE_ERROR;
585 }
586
587 if (ret.eax & 0x1)
588 interface->capability |= ACER_CAP_WIRELESS;
589
590 args.ebx = 2 << 8;
591 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
592
593 status = wmab_execute(&args, &out);
594 if (ACPI_FAILURE(status))
595 return status;
596
597 obj = (union acpi_object *) out.pointer;
598 if (obj && obj->type == ACPI_TYPE_BUFFER
599 && obj->buffer.length == sizeof(struct wmab_ret)) {
600 ret = *((struct wmab_ret *) obj->buffer.pointer);
601 } else {
602 return AE_ERROR;
603 }
604
605 if (ret.eax & 0x1)
606 interface->capability |= ACER_CAP_BLUETOOTH;
607
608 kfree(out.pointer);
609
610 /*
611 * This appears to be safe to enable, since all Wistron based laptops
612 * appear to use the same EC register for brightness, even if they
613 * differ for wireless, etc
614 */
615 if (quirks->brightness >= 0)
616 interface->capability |= ACER_CAP_BRIGHTNESS;
617
618 return AE_OK;
619}
620
621static struct wmi_interface AMW0_interface = {
622 .type = ACER_AMW0,
623};
624
625static struct wmi_interface AMW0_V2_interface = {
626 .type = ACER_AMW0_V2,
627};
628
629/*
630 * New interface (The WMID interface)
631 */
632static acpi_status
633WMI_execute_u32(u32 method_id, u32 in, u32 *out)
634{
635 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
636 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
637 union acpi_object *obj;
638 u32 tmp;
639 acpi_status status;
640
641 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
642
643 if (ACPI_FAILURE(status))
644 return status;
645
646 obj = (union acpi_object *) result.pointer;
647 if (obj && obj->type == ACPI_TYPE_BUFFER &&
648 obj->buffer.length == sizeof(u32)) {
649 tmp = *((u32 *) obj->buffer.pointer);
650 } else {
651 tmp = 0;
652 }
653
654 if (out)
655 *out = tmp;
656
657 kfree(result.pointer);
658
659 return status;
660}
661
662static acpi_status WMID_get_u32(u32 *value, u32 cap,
663struct wmi_interface *iface)
664{
665 acpi_status status;
666 u8 tmp;
667 u32 result, method_id = 0;
668
669 switch (cap) {
670 case ACER_CAP_WIRELESS:
671 method_id = ACER_WMID_GET_WIRELESS_METHODID;
672 break;
673 case ACER_CAP_BLUETOOTH:
674 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
675 break;
676 case ACER_CAP_BRIGHTNESS:
677 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
678 break;
679 case ACER_CAP_THREEG:
680 method_id = ACER_WMID_GET_THREEG_METHODID;
681 break;
682 case ACER_CAP_MAILLED:
683 if (quirks->mailled == 1) {
684 ec_read(0x9f, &tmp);
685 *value = tmp & 0x1;
686 return 0;
687 }
688 default:
689 return AE_ERROR;
690 }
691 status = WMI_execute_u32(method_id, 0, &result);
692
693 if (ACPI_SUCCESS(status))
694 *value = (u8)result;
695
696 return status;
697}
698
699static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
700{
701 u32 method_id = 0;
702 char param;
703
704 switch (cap) {
705 case ACER_CAP_BRIGHTNESS:
706 if (value > max_brightness)
707 return AE_BAD_PARAMETER;
708 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
709 break;
710 case ACER_CAP_WIRELESS:
711 if (value > 1)
712 return AE_BAD_PARAMETER;
713 method_id = ACER_WMID_SET_WIRELESS_METHODID;
714 break;
715 case ACER_CAP_BLUETOOTH:
716 if (value > 1)
717 return AE_BAD_PARAMETER;
718 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
719 break;
720 case ACER_CAP_THREEG:
721 if (value > 1)
722 return AE_BAD_PARAMETER;
723 method_id = ACER_WMID_SET_THREEG_METHODID;
724 break;
725 case ACER_CAP_MAILLED:
726 if (value > 1)
727 return AE_BAD_PARAMETER;
728 if (quirks->mailled == 1) {
729 param = value ? 0x92 : 0x93;
730 i8042_command(&param, 0x1059);
731 return 0;
732 }
733 break;
734 default:
735 return AE_ERROR;
736 }
737 return WMI_execute_u32(method_id, (u32)value, NULL);
738}
739
740static acpi_status WMID_set_capabilities(void)
741{
742 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
743 union acpi_object *obj;
744 acpi_status status;
745 u32 devices;
746
747 status = wmi_query_block(WMID_GUID2, 1, &out);
748 if (ACPI_FAILURE(status))
749 return status;
750
751 obj = (union acpi_object *) out.pointer;
752 if (obj && obj->type == ACPI_TYPE_BUFFER &&
753 obj->buffer.length == sizeof(u32)) {
754 devices = *((u32 *) obj->buffer.pointer);
755 } else {
756 return AE_ERROR;
757 }
758
759 /* Not sure on the meaning of the relevant bits yet to detect these */
760 interface->capability |= ACER_CAP_WIRELESS;
761 interface->capability |= ACER_CAP_THREEG;
762
763 /* WMID always provides brightness methods */
764 interface->capability |= ACER_CAP_BRIGHTNESS;
765
766 if (devices & 0x10)
767 interface->capability |= ACER_CAP_BLUETOOTH;
768
769 if (!(devices & 0x20))
770 max_brightness = 0x9;
771
772 return status;
773}
774
775static struct wmi_interface wmid_interface = {
776 .type = ACER_WMID,
777};
778
779/*
780 * Generic Device (interface-independent)
781 */
782
783static acpi_status get_u32(u32 *value, u32 cap)
784{
785 acpi_status status = AE_ERROR;
786
787 switch (interface->type) {
788 case ACER_AMW0:
789 status = AMW0_get_u32(value, cap, interface);
790 break;
791 case ACER_AMW0_V2:
792 if (cap == ACER_CAP_MAILLED) {
793 status = AMW0_get_u32(value, cap, interface);
794 break;
795 }
796 case ACER_WMID:
797 status = WMID_get_u32(value, cap, interface);
798 break;
799 }
800
801 return status;
802}
803
804static acpi_status set_u32(u32 value, u32 cap)
805{
806 acpi_status status;
807
808 if (interface->capability & cap) {
809 switch (interface->type) {
810 case ACER_AMW0:
811 return AMW0_set_u32(value, cap, interface);
812 case ACER_AMW0_V2:
813 if (cap == ACER_CAP_MAILLED)
814 return AMW0_set_u32(value, cap, interface);
815
816 /*
817 * On some models, some WMID methods don't toggle
818 * properly. For those cases, we want to run the AMW0
819 * method afterwards to be certain we've really toggled
820 * the device state.
821 */
822 if (cap == ACER_CAP_WIRELESS ||
823 cap == ACER_CAP_BLUETOOTH) {
824 status = WMID_set_u32(value, cap, interface);
825 if (ACPI_FAILURE(status))
826 return status;
827
828 return AMW0_set_u32(value, cap, interface);
829 }
830 case ACER_WMID:
831 return WMID_set_u32(value, cap, interface);
832 default:
833 return AE_BAD_PARAMETER;
834 }
835 }
836 return AE_BAD_PARAMETER;
837}
838
839static void __init acer_commandline_init(void)
840{
841 /*
842 * These will all fail silently if the value given is invalid, or the
843 * capability isn't available on the given interface
844 */
845 set_u32(mailled, ACER_CAP_MAILLED);
846 set_u32(threeg, ACER_CAP_THREEG);
847 set_u32(brightness, ACER_CAP_BRIGHTNESS);
848}
849
850/*
851 * LED device (Mail LED only, no other LEDs known yet)
852 */
853static void mail_led_set(struct led_classdev *led_cdev,
854enum led_brightness value)
855{
856 set_u32(value, ACER_CAP_MAILLED);
857}
858
859static struct led_classdev mail_led = {
860 .name = "acer-wmi::mail",
861 .brightness_set = mail_led_set,
862};
863
864static int __devinit acer_led_init(struct device *dev)
865{
866 return led_classdev_register(dev, &mail_led);
867}
868
869static void acer_led_exit(void)
870{
871 led_classdev_unregister(&mail_led);
872}
873
874/*
875 * Backlight device
876 */
877static struct backlight_device *acer_backlight_device;
878
879static int read_brightness(struct backlight_device *bd)
880{
881 u32 value;
882 get_u32(&value, ACER_CAP_BRIGHTNESS);
883 return value;
884}
885
886static int update_bl_status(struct backlight_device *bd)
887{
888 int intensity = bd->props.brightness;
889
890 if (bd->props.power != FB_BLANK_UNBLANK)
891 intensity = 0;
892 if (bd->props.fb_blank != FB_BLANK_UNBLANK)
893 intensity = 0;
894
895 set_u32(intensity, ACER_CAP_BRIGHTNESS);
896
897 return 0;
898}
899
900static struct backlight_ops acer_bl_ops = {
901 .get_brightness = read_brightness,
902 .update_status = update_bl_status,
903};
904
905static int __devinit acer_backlight_init(struct device *dev)
906{
907 struct backlight_device *bd;
908
909 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
910 if (IS_ERR(bd)) {
911 printk(ACER_ERR "Could not register Acer backlight device\n");
912 acer_backlight_device = NULL;
913 return PTR_ERR(bd);
914 }
915
916 acer_backlight_device = bd;
917
918 bd->props.power = FB_BLANK_UNBLANK;
919 bd->props.brightness = max_brightness;
920 bd->props.max_brightness = max_brightness;
921 backlight_update_status(bd);
922 return 0;
923}
924
925static void acer_backlight_exit(void)
926{
927 backlight_device_unregister(acer_backlight_device);
928}
929
930/*
931 * Rfkill devices
932 */
933static void acer_rfkill_update(struct work_struct *ignored);
934static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
935static void acer_rfkill_update(struct work_struct *ignored)
936{
937 u32 state;
938 acpi_status status;
939
940 status = get_u32(&state, ACER_CAP_WIRELESS);
941 if (ACPI_SUCCESS(status))
942 rfkill_force_state(wireless_rfkill, state ?
943 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
944
945 if (has_cap(ACER_CAP_BLUETOOTH)) {
946 status = get_u32(&state, ACER_CAP_BLUETOOTH);
947 if (ACPI_SUCCESS(status))
948 rfkill_force_state(bluetooth_rfkill, state ?
949 RFKILL_STATE_UNBLOCKED :
950 RFKILL_STATE_SOFT_BLOCKED);
951 }
952
953 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
954}
955
956static int acer_rfkill_set(void *data, enum rfkill_state state)
957{
958 acpi_status status;
959 u32 *cap = data;
960 status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
961 if (ACPI_FAILURE(status))
962 return -ENODEV;
963 return 0;
964}
965
966static struct rfkill * acer_rfkill_register(struct device *dev,
967enum rfkill_type type, char *name, u32 cap)
968{
969 int err;
970 u32 state;
971 u32 *data;
972 struct rfkill *rfkill_dev;
973
974 rfkill_dev = rfkill_allocate(dev, type);
975 if (!rfkill_dev)
976 return ERR_PTR(-ENOMEM);
977 rfkill_dev->name = name;
978 get_u32(&state, cap);
979 rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
980 RFKILL_STATE_SOFT_BLOCKED;
981 data = kzalloc(sizeof(u32), GFP_KERNEL);
982 if (!data) {
983 rfkill_free(rfkill_dev);
984 return ERR_PTR(-ENOMEM);
985 }
986 *data = cap;
987 rfkill_dev->data = data;
988 rfkill_dev->toggle_radio = acer_rfkill_set;
989 rfkill_dev->user_claim_unsupported = 1;
990
991 err = rfkill_register(rfkill_dev);
992 if (err) {
993 kfree(rfkill_dev->data);
994 rfkill_free(rfkill_dev);
995 return ERR_PTR(err);
996 }
997 return rfkill_dev;
998}
999
1000static int acer_rfkill_init(struct device *dev)
1001{
1002 wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
1003 "acer-wireless", ACER_CAP_WIRELESS);
1004 if (IS_ERR(wireless_rfkill))
1005 return PTR_ERR(wireless_rfkill);
1006
1007 if (has_cap(ACER_CAP_BLUETOOTH)) {
1008 bluetooth_rfkill = acer_rfkill_register(dev,
1009 RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
1010 ACER_CAP_BLUETOOTH);
1011 if (IS_ERR(bluetooth_rfkill)) {
1012 kfree(wireless_rfkill->data);
1013 rfkill_unregister(wireless_rfkill);
1014 return PTR_ERR(bluetooth_rfkill);
1015 }
1016 }
1017
1018 schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
1019
1020 return 0;
1021}
1022
1023static void acer_rfkill_exit(void)
1024{
1025 cancel_delayed_work_sync(&acer_rfkill_work);
1026 kfree(wireless_rfkill->data);
1027 rfkill_unregister(wireless_rfkill);
1028 if (has_cap(ACER_CAP_BLUETOOTH)) {
1029 kfree(wireless_rfkill->data);
1030 rfkill_unregister(bluetooth_rfkill);
1031 }
1032 return;
1033}
1034
1035/*
1036 * sysfs interface
1037 */
1038static ssize_t show_bool_threeg(struct device *dev,
1039 struct device_attribute *attr, char *buf)
1040{
1041 u32 result; \
1042 acpi_status status = get_u32(&result, ACER_CAP_THREEG);
1043 if (ACPI_SUCCESS(status))
1044 return sprintf(buf, "%u\n", result);
1045 return sprintf(buf, "Read error\n");
1046}
1047
1048static ssize_t set_bool_threeg(struct device *dev,
1049 struct device_attribute *attr, const char *buf, size_t count)
1050{
1051 u32 tmp = simple_strtoul(buf, NULL, 10);
1052 acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
1053 if (ACPI_FAILURE(status))
1054 return -EINVAL;
1055 return count;
1056}
1057static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
1058 set_bool_threeg);
1059
1060static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
1061 char *buf)
1062{
1063 switch (interface->type) {
1064 case ACER_AMW0:
1065 return sprintf(buf, "AMW0\n");
1066 case ACER_AMW0_V2:
1067 return sprintf(buf, "AMW0 v2\n");
1068 case ACER_WMID:
1069 return sprintf(buf, "WMID\n");
1070 default:
1071 return sprintf(buf, "Error!\n");
1072 }
1073}
1074
1075static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
1076 show_interface, NULL);
1077
1078/*
1079 * debugfs functions
1080 */
1081static u32 get_wmid_devices(void)
1082{
1083 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
1084 union acpi_object *obj;
1085 acpi_status status;
1086
1087 status = wmi_query_block(WMID_GUID2, 1, &out);
1088 if (ACPI_FAILURE(status))
1089 return 0;
1090
1091 obj = (union acpi_object *) out.pointer;
1092 if (obj && obj->type == ACPI_TYPE_BUFFER &&
1093 obj->buffer.length == sizeof(u32)) {
1094 return *((u32 *) obj->buffer.pointer);
1095 } else {
1096 return 0;
1097 }
1098}
1099
1100/*
1101 * Platform device
1102 */
1103static int __devinit acer_platform_probe(struct platform_device *device)
1104{
1105 int err;
1106
1107 if (has_cap(ACER_CAP_MAILLED)) {
1108 err = acer_led_init(&device->dev);
1109 if (err)
1110 goto error_mailled;
1111 }
1112
1113 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1114 err = acer_backlight_init(&device->dev);
1115 if (err)
1116 goto error_brightness;
1117 }
1118
1119 err = acer_rfkill_init(&device->dev);
1120
1121 return err;
1122
1123error_brightness:
1124 acer_led_exit();
1125error_mailled:
1126 return err;
1127}
1128
1129static int acer_platform_remove(struct platform_device *device)
1130{
1131 if (has_cap(ACER_CAP_MAILLED))
1132 acer_led_exit();
1133 if (has_cap(ACER_CAP_BRIGHTNESS))
1134 acer_backlight_exit();
1135
1136 acer_rfkill_exit();
1137 return 0;
1138}
1139
1140static int acer_platform_suspend(struct platform_device *dev,
1141pm_message_t state)
1142{
1143 u32 value;
1144 struct acer_data *data = &interface->data;
1145
1146 if (!data)
1147 return -ENOMEM;
1148
1149 if (has_cap(ACER_CAP_MAILLED)) {
1150 get_u32(&value, ACER_CAP_MAILLED);
1151 data->mailled = value;
1152 }
1153
1154 if (has_cap(ACER_CAP_BRIGHTNESS)) {
1155 get_u32(&value, ACER_CAP_BRIGHTNESS);
1156 data->brightness = value;
1157 }
1158
1159 return 0;
1160}
1161
1162static int acer_platform_resume(struct platform_device *device)
1163{
1164 struct acer_data *data = &interface->data;
1165
1166 if (!data)
1167 return -ENOMEM;
1168
1169 if (has_cap(ACER_CAP_MAILLED))
1170 set_u32(data->mailled, ACER_CAP_MAILLED);
1171
1172 if (has_cap(ACER_CAP_BRIGHTNESS))
1173 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1174
1175 return 0;
1176}
1177
1178static struct platform_driver acer_platform_driver = {
1179 .driver = {
1180 .name = "acer-wmi",
1181 .owner = THIS_MODULE,
1182 },
1183 .probe = acer_platform_probe,
1184 .remove = acer_platform_remove,
1185 .suspend = acer_platform_suspend,
1186 .resume = acer_platform_resume,
1187};
1188
1189static struct platform_device *acer_platform_device;
1190
1191static int remove_sysfs(struct platform_device *device)
1192{
1193 if (has_cap(ACER_CAP_THREEG))
1194 device_remove_file(&device->dev, &dev_attr_threeg);
1195
1196 device_remove_file(&device->dev, &dev_attr_interface);
1197
1198 return 0;
1199}
1200
1201static int create_sysfs(void)
1202{
1203 int retval = -ENOMEM;
1204
1205 if (has_cap(ACER_CAP_THREEG)) {
1206 retval = device_create_file(&acer_platform_device->dev,
1207 &dev_attr_threeg);
1208 if (retval)
1209 goto error_sysfs;
1210 }
1211
1212 retval = device_create_file(&acer_platform_device->dev,
1213 &dev_attr_interface);
1214 if (retval)
1215 goto error_sysfs;
1216
1217 return 0;
1218
1219error_sysfs:
1220 remove_sysfs(acer_platform_device);
1221 return retval;
1222}
1223
1224static void remove_debugfs(void)
1225{
1226 debugfs_remove(interface->debug.devices);
1227 debugfs_remove(interface->debug.root);
1228}
1229
1230static int create_debugfs(void)
1231{
1232 interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
1233 if (!interface->debug.root) {
1234 printk(ACER_ERR "Failed to create debugfs directory");
1235 return -ENOMEM;
1236 }
1237
1238 interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
1239 interface->debug.root,
1240 &interface->debug.wmid_devices);
1241 if (!interface->debug.devices)
1242 goto error_debugfs;
1243
1244 return 0;
1245
1246error_debugfs:
1247 remove_debugfs();
1248 return -ENOMEM;
1249}
1250
1251static int __init acer_wmi_init(void)
1252{
1253 int err;
1254
1255 printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
1256
1257 find_quirks();
1258
1259 /*
1260 * Detect which ACPI-WMI interface we're using.
1261 */
1262 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1263 interface = &AMW0_V2_interface;
1264
1265 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1266 interface = &wmid_interface;
1267
1268 if (wmi_has_guid(WMID_GUID2) && interface) {
1269 if (ACPI_FAILURE(WMID_set_capabilities())) {
1270 printk(ACER_ERR "Unable to detect available WMID "
1271 "devices\n");
1272 return -ENODEV;
1273 }
1274 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
1275 printk(ACER_ERR "No WMID device detection method found\n");
1276 return -ENODEV;
1277 }
1278
1279 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1280 interface = &AMW0_interface;
1281
1282 if (ACPI_FAILURE(AMW0_set_capabilities())) {
1283 printk(ACER_ERR "Unable to detect available AMW0 "
1284 "devices\n");
1285 return -ENODEV;
1286 }
1287 }
1288
1289 if (wmi_has_guid(AMW0_GUID1))
1290 AMW0_find_mailled();
1291
1292 if (!interface) {
1293 printk(ACER_ERR "No or unsupported WMI interface, unable to "
1294 "load\n");
1295 return -ENODEV;
1296 }
1297
1298 set_quirks();
1299
1300 if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
1301 interface->capability &= ~ACER_CAP_BRIGHTNESS;
1302 printk(ACER_INFO "Brightness must be controlled by "
1303 "generic video driver\n");
1304 }
1305
1306 if (platform_driver_register(&acer_platform_driver)) {
1307 printk(ACER_ERR "Unable to register platform driver.\n");
1308 goto error_platform_register;
1309 }
1310 acer_platform_device = platform_device_alloc("acer-wmi", -1);
1311 platform_device_add(acer_platform_device);
1312
1313 err = create_sysfs();
1314 if (err)
1315 return err;
1316
1317 if (wmi_has_guid(WMID_GUID2)) {
1318 interface->debug.wmid_devices = get_wmid_devices();
1319 err = create_debugfs();
1320 if (err)
1321 return err;
1322 }
1323
1324 /* Override any initial settings with values from the commandline */
1325 acer_commandline_init();
1326
1327 return 0;
1328
1329error_platform_register:
1330 return -ENODEV;
1331}
1332
1333static void __exit acer_wmi_exit(void)
1334{
1335 remove_sysfs(acer_platform_device);
1336 remove_debugfs();
1337 platform_device_del(acer_platform_device);
1338 platform_driver_unregister(&acer_platform_driver);
1339
1340 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1341 return;
1342}
1343
1344module_init(acer_wmi_init);
1345module_exit(acer_wmi_exit);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
new file mode 100644
index 000000000000..8fb8b3591048
--- /dev/null
+++ b/drivers/platform/x86/asus-laptop.c
@@ -0,0 +1,1266 @@
1/*
2 * asus-laptop.c - Asus Laptop Support
3 *
4 *
5 * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6 * Copyright (C) 2006-2007 Corentin Chary
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * The development page for this driver is located at
24 * http://sourceforge.net/projects/acpi4asus/
25 *
26 * Credits:
27 * Pontus Fuchs - Helper functions, cleanup
28 * Johann Wiesner - Small compile fixes
29 * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
30 * Eric Burghard - LED display support for W1N
31 * Josh Green - Light Sens support
32 * Thomas Tuttle - His first patch for led support was very helpfull
33 * Sam Lin - GPS support
34 */
35
36#include <linux/kernel.h>
37#include <linux/module.h>
38#include <linux/init.h>
39#include <linux/types.h>
40#include <linux/err.h>
41#include <linux/proc_fs.h>
42#include <linux/backlight.h>
43#include <linux/fb.h>
44#include <linux/leds.h>
45#include <linux/platform_device.h>
46#include <acpi/acpi_drivers.h>
47#include <acpi/acpi_bus.h>
48#include <asm/uaccess.h>
49
50#define ASUS_LAPTOP_VERSION "0.42"
51
52#define ASUS_HOTK_NAME "Asus Laptop Support"
53#define ASUS_HOTK_CLASS "hotkey"
54#define ASUS_HOTK_DEVICE_NAME "Hotkey"
55#define ASUS_HOTK_FILE "asus-laptop"
56#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
57
58/*
59 * Some events we use, same for all Asus
60 */
61#define ATKD_BR_UP 0x10
62#define ATKD_BR_DOWN 0x20
63#define ATKD_LCD_ON 0x33
64#define ATKD_LCD_OFF 0x34
65
66/*
67 * Known bits returned by \_SB.ATKD.HWRS
68 */
69#define WL_HWRS 0x80
70#define BT_HWRS 0x100
71
72/*
73 * Flags for hotk status
74 * WL_ON and BT_ON are also used for wireless_status()
75 */
76#define WL_ON 0x01 //internal Wifi
77#define BT_ON 0x02 //internal Bluetooth
78#define MLED_ON 0x04 //mail LED
79#define TLED_ON 0x08 //touchpad LED
80#define RLED_ON 0x10 //Record LED
81#define PLED_ON 0x20 //Phone LED
82#define GLED_ON 0x40 //Gaming LED
83#define LCD_ON 0x80 //LCD backlight
84#define GPS_ON 0x100 //GPS
85
86#define ASUS_LOG ASUS_HOTK_FILE ": "
87#define ASUS_ERR KERN_ERR ASUS_LOG
88#define ASUS_WARNING KERN_WARNING ASUS_LOG
89#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
90#define ASUS_INFO KERN_INFO ASUS_LOG
91#define ASUS_DEBUG KERN_DEBUG ASUS_LOG
92
93MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
94MODULE_DESCRIPTION(ASUS_HOTK_NAME);
95MODULE_LICENSE("GPL");
96
97/* WAPF defines the behavior of the Fn+Fx wlan key
98 * The significance of values is yet to be found, but
99 * most of the time:
100 * 0x0 will do nothing
101 * 0x1 will allow to control the device with Fn+Fx key.
102 * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
103 * 0x5 like 0x1 or 0x4
104 * So, if something doesn't work as you want, just try other values =)
105 */
106static uint wapf = 1;
107module_param(wapf, uint, 0644);
108MODULE_PARM_DESC(wapf, "WAPF value");
109
110#define ASUS_HANDLE(object, paths...) \
111 static acpi_handle object##_handle = NULL; \
112 static char *object##_paths[] = { paths }
113
114/* LED */
115ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
116ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
117ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */
118ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */
119ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */
120
121/* LEDD */
122ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
123
124/* Bluetooth and WLAN
125 * WLED and BLED are not handled like other XLED, because in some dsdt
126 * they also control the WLAN/Bluetooth device.
127 */
128ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
129ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
130ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */
131
132/* Brightness */
133ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
134ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
135
136/* Backlight */
137ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
138 "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */
139 "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */
140 "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */
141 "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
142 "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
143 "\\_SB.PCI0.PX40.Q10", /* S1x */
144 "\\Q10"); /* A2x, L2D, L3D, M2E */
145
146/* Display */
147ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
148ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD", /* A6B, A6K A6R A7D F3JM L4R M6R A3G
149 M6A M6V VX-1 V6J V6V W3Z */
150 "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
151 S5A M5A z33A W1Jc W2V G1 */
152 "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
153 "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
154 "\\_SB.PCI0.PCI1.VGAC.NMAP", /* L3C */
155 "\\_SB.PCI0.VGA.GETD", /* Z96F */
156 "\\ACTD", /* A2D */
157 "\\ADVG", /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
158 "\\DNXT", /* P30 */
159 "\\INFB", /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
160 "\\SSTE"); /* A3F A6F A3N A3L M6N W3N W6A */
161
162ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC"); /* Z71A Z71V */
163ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL"); /* Z71A Z71V */
164
165/* GPS */
166/* R2H use different handle for GPS on/off */
167ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON"); /* R2H */
168ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
169ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
170
171/*
172 * This is the main structure, we can use it to store anything interesting
173 * about the hotk device
174 */
175struct asus_hotk {
176 char *name; //laptop name
177 struct acpi_device *device; //the device we are in
178 acpi_handle handle; //the handle of the hotk device
179 char status; //status of the hotk, for LEDs, ...
180 u32 ledd_status; //status of the LED display
181 u8 light_level; //light sensor level
182 u8 light_switch; //light sensor switch value
183 u16 event_count[128]; //count for each event TODO make this better
184};
185
186/*
187 * This header is made available to allow proper configuration given model,
188 * revision number , ... this info cannot go in struct asus_hotk because it is
189 * available before the hotk
190 */
191static struct acpi_table_header *asus_info;
192
193/* The actual device the driver binds to */
194static struct asus_hotk *hotk;
195
196/*
197 * The hotkey driver declaration
198 */
199static const struct acpi_device_id asus_device_ids[] = {
200 {"ATK0100", 0},
201 {"", 0},
202};
203MODULE_DEVICE_TABLE(acpi, asus_device_ids);
204
205static int asus_hotk_add(struct acpi_device *device);
206static int asus_hotk_remove(struct acpi_device *device, int type);
207static struct acpi_driver asus_hotk_driver = {
208 .name = ASUS_HOTK_NAME,
209 .class = ASUS_HOTK_CLASS,
210 .ids = asus_device_ids,
211 .ops = {
212 .add = asus_hotk_add,
213 .remove = asus_hotk_remove,
214 },
215};
216
217/* The backlight device /sys/class/backlight */
218static struct backlight_device *asus_backlight_device;
219
220/*
221 * The backlight class declaration
222 */
223static int read_brightness(struct backlight_device *bd);
224static int update_bl_status(struct backlight_device *bd);
225static struct backlight_ops asusbl_ops = {
226 .get_brightness = read_brightness,
227 .update_status = update_bl_status,
228};
229
230/* These functions actually update the LED's, and are called from a
231 * workqueue. By doing this as separate work rather than when the LED
232 * subsystem asks, we avoid messing with the Asus ACPI stuff during a
233 * potentially bad time, such as a timer interrupt. */
234static struct workqueue_struct *led_workqueue;
235
236#define ASUS_LED(object, ledname) \
237 static void object##_led_set(struct led_classdev *led_cdev, \
238 enum led_brightness value); \
239 static void object##_led_update(struct work_struct *ignored); \
240 static int object##_led_wk; \
241 static DECLARE_WORK(object##_led_work, object##_led_update); \
242 static struct led_classdev object##_led = { \
243 .name = "asus::" ledname, \
244 .brightness_set = object##_led_set, \
245 }
246
247ASUS_LED(mled, "mail");
248ASUS_LED(tled, "touchpad");
249ASUS_LED(rled, "record");
250ASUS_LED(pled, "phone");
251ASUS_LED(gled, "gaming");
252
253/*
254 * This function evaluates an ACPI method, given an int as parameter, the
255 * method is searched within the scope of the handle, can be NULL. The output
256 * of the method is written is output, which can also be NULL
257 *
258 * returns 0 if write is successful, -1 else.
259 */
260static int write_acpi_int(acpi_handle handle, const char *method, int val,
261 struct acpi_buffer *output)
262{
263 struct acpi_object_list params; //list of input parameters (an int here)
264 union acpi_object in_obj; //the only param we use
265 acpi_status status;
266
267 if (!handle)
268 return 0;
269
270 params.count = 1;
271 params.pointer = &in_obj;
272 in_obj.type = ACPI_TYPE_INTEGER;
273 in_obj.integer.value = val;
274
275 status = acpi_evaluate_object(handle, (char *)method, &params, output);
276 if (status == AE_OK)
277 return 0;
278 else
279 return -1;
280}
281
282static int read_wireless_status(int mask)
283{
284 unsigned long long status;
285 acpi_status rv = AE_OK;
286
287 if (!wireless_status_handle)
288 return (hotk->status & mask) ? 1 : 0;
289
290 rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
291 if (ACPI_FAILURE(rv))
292 printk(ASUS_WARNING "Error reading Wireless status\n");
293 else
294 return (status & mask) ? 1 : 0;
295
296 return (hotk->status & mask) ? 1 : 0;
297}
298
299static int read_gps_status(void)
300{
301 unsigned long long status;
302 acpi_status rv = AE_OK;
303
304 rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
305 if (ACPI_FAILURE(rv))
306 printk(ASUS_WARNING "Error reading GPS status\n");
307 else
308 return status ? 1 : 0;
309
310 return (hotk->status & GPS_ON) ? 1 : 0;
311}
312
313/* Generic LED functions */
314static int read_status(int mask)
315{
316 /* There is a special method for both wireless devices */
317 if (mask == BT_ON || mask == WL_ON)
318 return read_wireless_status(mask);
319 else if (mask == GPS_ON)
320 return read_gps_status();
321
322 return (hotk->status & mask) ? 1 : 0;
323}
324
325static void write_status(acpi_handle handle, int out, int mask)
326{
327 hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
328
329 switch (mask) {
330 case MLED_ON:
331 out = !(out & 0x1);
332 break;
333 case GLED_ON:
334 out = (out & 0x1) + 1;
335 break;
336 case GPS_ON:
337 handle = (out) ? gps_on_handle : gps_off_handle;
338 out = 0x02;
339 break;
340 default:
341 out &= 0x1;
342 break;
343 }
344
345 if (write_acpi_int(handle, NULL, out, NULL))
346 printk(ASUS_WARNING " write failed %x\n", mask);
347}
348
349/* /sys/class/led handlers */
350#define ASUS_LED_HANDLER(object, mask) \
351 static void object##_led_set(struct led_classdev *led_cdev, \
352 enum led_brightness value) \
353 { \
354 object##_led_wk = (value > 0) ? 1 : 0; \
355 queue_work(led_workqueue, &object##_led_work); \
356 } \
357 static void object##_led_update(struct work_struct *ignored) \
358 { \
359 int value = object##_led_wk; \
360 write_status(object##_set_handle, value, (mask)); \
361 }
362
363ASUS_LED_HANDLER(mled, MLED_ON);
364ASUS_LED_HANDLER(pled, PLED_ON);
365ASUS_LED_HANDLER(rled, RLED_ON);
366ASUS_LED_HANDLER(tled, TLED_ON);
367ASUS_LED_HANDLER(gled, GLED_ON);
368
369static int get_lcd_state(void)
370{
371 return read_status(LCD_ON);
372}
373
374static int set_lcd_state(int value)
375{
376 int lcd = 0;
377 acpi_status status = 0;
378
379 lcd = value ? 1 : 0;
380
381 if (lcd == get_lcd_state())
382 return 0;
383
384 if (lcd_switch_handle) {
385 status = acpi_evaluate_object(lcd_switch_handle,
386 NULL, NULL, NULL);
387
388 if (ACPI_FAILURE(status))
389 printk(ASUS_WARNING "Error switching LCD\n");
390 }
391
392 write_status(NULL, lcd, LCD_ON);
393 return 0;
394}
395
396static void lcd_blank(int blank)
397{
398 struct backlight_device *bd = asus_backlight_device;
399
400 if (bd) {
401 bd->props.power = blank;
402 backlight_update_status(bd);
403 }
404}
405
406static int read_brightness(struct backlight_device *bd)
407{
408 unsigned long long value;
409 acpi_status rv = AE_OK;
410
411 rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
412 if (ACPI_FAILURE(rv))
413 printk(ASUS_WARNING "Error reading brightness\n");
414
415 return value;
416}
417
418static int set_brightness(struct backlight_device *bd, int value)
419{
420 int ret = 0;
421
422 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
423 /* 0 <= value <= 15 */
424
425 if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
426 printk(ASUS_WARNING "Error changing brightness\n");
427 ret = -EIO;
428 }
429
430 return ret;
431}
432
433static int update_bl_status(struct backlight_device *bd)
434{
435 int rv;
436 int value = bd->props.brightness;
437
438 rv = set_brightness(bd, value);
439 if (rv)
440 return rv;
441
442 value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
443 return set_lcd_state(value);
444}
445
446/*
447 * Platform device handlers
448 */
449
450/*
451 * We write our info in page, we begin at offset off and cannot write more
452 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
453 * number of bytes written in page
454 */
455static ssize_t show_infos(struct device *dev,
456 struct device_attribute *attr, char *page)
457{
458 int len = 0;
459 unsigned long long temp;
460 char buf[16]; //enough for all info
461 acpi_status rv = AE_OK;
462
463 /*
464 * We use the easy way, we don't care of off and count, so we don't set eof
465 * to 1
466 */
467
468 len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
469 len += sprintf(page + len, "Model reference : %s\n", hotk->name);
470 /*
471 * The SFUN method probably allows the original driver to get the list
472 * of features supported by a given model. For now, 0x0100 or 0x0800
473 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
474 * The significance of others is yet to be found.
475 */
476 rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
477 if (!ACPI_FAILURE(rv))
478 len += sprintf(page + len, "SFUN value : 0x%04x\n",
479 (uint) temp);
480 /*
481 * Another value for userspace: the ASYM method returns 0x02 for
482 * battery low and 0x04 for battery critical, its readings tend to be
483 * more accurate than those provided by _BST.
484 * Note: since not all the laptops provide this method, errors are
485 * silently ignored.
486 */
487 rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
488 if (!ACPI_FAILURE(rv))
489 len += sprintf(page + len, "ASYM value : 0x%04x\n",
490 (uint) temp);
491 if (asus_info) {
492 snprintf(buf, 16, "%d", asus_info->length);
493 len += sprintf(page + len, "DSDT length : %s\n", buf);
494 snprintf(buf, 16, "%d", asus_info->checksum);
495 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
496 snprintf(buf, 16, "%d", asus_info->revision);
497 len += sprintf(page + len, "DSDT revision : %s\n", buf);
498 snprintf(buf, 7, "%s", asus_info->oem_id);
499 len += sprintf(page + len, "OEM id : %s\n", buf);
500 snprintf(buf, 9, "%s", asus_info->oem_table_id);
501 len += sprintf(page + len, "OEM table id : %s\n", buf);
502 snprintf(buf, 16, "%x", asus_info->oem_revision);
503 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
504 snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
505 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
506 snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
507 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
508 }
509
510 return len;
511}
512
513static int parse_arg(const char *buf, unsigned long count, int *val)
514{
515 if (!count)
516 return 0;
517 if (count > 31)
518 return -EINVAL;
519 if (sscanf(buf, "%i", val) != 1)
520 return -EINVAL;
521 return count;
522}
523
524static ssize_t store_status(const char *buf, size_t count,
525 acpi_handle handle, int mask)
526{
527 int rv, value;
528 int out = 0;
529
530 rv = parse_arg(buf, count, &value);
531 if (rv > 0)
532 out = value ? 1 : 0;
533
534 write_status(handle, out, mask);
535
536 return rv;
537}
538
539/*
540 * LEDD display
541 */
542static ssize_t show_ledd(struct device *dev,
543 struct device_attribute *attr, char *buf)
544{
545 return sprintf(buf, "0x%08x\n", hotk->ledd_status);
546}
547
548static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
549 const char *buf, size_t count)
550{
551 int rv, value;
552
553 rv = parse_arg(buf, count, &value);
554 if (rv > 0) {
555 if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
556 printk(ASUS_WARNING "LED display write failed\n");
557 else
558 hotk->ledd_status = (u32) value;
559 }
560 return rv;
561}
562
563/*
564 * WLAN
565 */
566static ssize_t show_wlan(struct device *dev,
567 struct device_attribute *attr, char *buf)
568{
569 return sprintf(buf, "%d\n", read_status(WL_ON));
570}
571
572static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
573 const char *buf, size_t count)
574{
575 return store_status(buf, count, wl_switch_handle, WL_ON);
576}
577
578/*
579 * Bluetooth
580 */
581static ssize_t show_bluetooth(struct device *dev,
582 struct device_attribute *attr, char *buf)
583{
584 return sprintf(buf, "%d\n", read_status(BT_ON));
585}
586
587static ssize_t store_bluetooth(struct device *dev,
588 struct device_attribute *attr, const char *buf,
589 size_t count)
590{
591 return store_status(buf, count, bt_switch_handle, BT_ON);
592}
593
594/*
595 * Display
596 */
597static void set_display(int value)
598{
599 /* no sanity check needed for now */
600 if (write_acpi_int(display_set_handle, NULL, value, NULL))
601 printk(ASUS_WARNING "Error setting display\n");
602 return;
603}
604
605static int read_display(void)
606{
607 unsigned long long value = 0;
608 acpi_status rv = AE_OK;
609
610 /* In most of the case, we know how to set the display, but sometime
611 we can't read it */
612 if (display_get_handle) {
613 rv = acpi_evaluate_integer(display_get_handle, NULL,
614 NULL, &value);
615 if (ACPI_FAILURE(rv))
616 printk(ASUS_WARNING "Error reading display status\n");
617 }
618
619 value &= 0x0F; /* needed for some models, shouldn't hurt others */
620
621 return value;
622}
623
624/*
625 * Now, *this* one could be more user-friendly, but so far, no-one has
626 * complained. The significance of bits is the same as in store_disp()
627 */
628static ssize_t show_disp(struct device *dev,
629 struct device_attribute *attr, char *buf)
630{
631 return sprintf(buf, "%d\n", read_display());
632}
633
634/*
635 * Experimental support for display switching. As of now: 1 should activate
636 * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
637 * Any combination (bitwise) of these will suffice. I never actually tested 4
638 * displays hooked up simultaneously, so be warned. See the acpi4asus README
639 * for more info.
640 */
641static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
642 const char *buf, size_t count)
643{
644 int rv, value;
645
646 rv = parse_arg(buf, count, &value);
647 if (rv > 0)
648 set_display(value);
649 return rv;
650}
651
652/*
653 * Light Sens
654 */
655static void set_light_sens_switch(int value)
656{
657 if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
658 printk(ASUS_WARNING "Error setting light sensor switch\n");
659 hotk->light_switch = value;
660}
661
662static ssize_t show_lssw(struct device *dev,
663 struct device_attribute *attr, char *buf)
664{
665 return sprintf(buf, "%d\n", hotk->light_switch);
666}
667
668static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
669 const char *buf, size_t count)
670{
671 int rv, value;
672
673 rv = parse_arg(buf, count, &value);
674 if (rv > 0)
675 set_light_sens_switch(value ? 1 : 0);
676
677 return rv;
678}
679
680static void set_light_sens_level(int value)
681{
682 if (write_acpi_int(ls_level_handle, NULL, value, NULL))
683 printk(ASUS_WARNING "Error setting light sensor level\n");
684 hotk->light_level = value;
685}
686
687static ssize_t show_lslvl(struct device *dev,
688 struct device_attribute *attr, char *buf)
689{
690 return sprintf(buf, "%d\n", hotk->light_level);
691}
692
693static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
694 const char *buf, size_t count)
695{
696 int rv, value;
697
698 rv = parse_arg(buf, count, &value);
699 if (rv > 0) {
700 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
701 /* 0 <= value <= 15 */
702 set_light_sens_level(value);
703 }
704
705 return rv;
706}
707
708/*
709 * GPS
710 */
711static ssize_t show_gps(struct device *dev,
712 struct device_attribute *attr, char *buf)
713{
714 return sprintf(buf, "%d\n", read_status(GPS_ON));
715}
716
717static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
718 const char *buf, size_t count)
719{
720 return store_status(buf, count, NULL, GPS_ON);
721}
722
723static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
724{
725 /* TODO Find a better way to handle events count. */
726 if (!hotk)
727 return;
728
729 /*
730 * We need to tell the backlight device when the backlight power is
731 * switched
732 */
733 if (event == ATKD_LCD_ON) {
734 write_status(NULL, 1, LCD_ON);
735 lcd_blank(FB_BLANK_UNBLANK);
736 } else if (event == ATKD_LCD_OFF) {
737 write_status(NULL, 0, LCD_ON);
738 lcd_blank(FB_BLANK_POWERDOWN);
739 }
740
741 acpi_bus_generate_proc_event(hotk->device, event,
742 hotk->event_count[event % 128]++);
743
744 return;
745}
746
747#define ASUS_CREATE_DEVICE_ATTR(_name) \
748 struct device_attribute dev_attr_##_name = { \
749 .attr = { \
750 .name = __stringify(_name), \
751 .mode = 0 }, \
752 .show = NULL, \
753 .store = NULL, \
754 }
755
756#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store) \
757 do { \
758 dev_attr_##_name.attr.mode = _mode; \
759 dev_attr_##_name.show = _show; \
760 dev_attr_##_name.store = _store; \
761 } while(0)
762
763static ASUS_CREATE_DEVICE_ATTR(infos);
764static ASUS_CREATE_DEVICE_ATTR(wlan);
765static ASUS_CREATE_DEVICE_ATTR(bluetooth);
766static ASUS_CREATE_DEVICE_ATTR(display);
767static ASUS_CREATE_DEVICE_ATTR(ledd);
768static ASUS_CREATE_DEVICE_ATTR(ls_switch);
769static ASUS_CREATE_DEVICE_ATTR(ls_level);
770static ASUS_CREATE_DEVICE_ATTR(gps);
771
772static struct attribute *asuspf_attributes[] = {
773 &dev_attr_infos.attr,
774 &dev_attr_wlan.attr,
775 &dev_attr_bluetooth.attr,
776 &dev_attr_display.attr,
777 &dev_attr_ledd.attr,
778 &dev_attr_ls_switch.attr,
779 &dev_attr_ls_level.attr,
780 &dev_attr_gps.attr,
781 NULL
782};
783
784static struct attribute_group asuspf_attribute_group = {
785 .attrs = asuspf_attributes
786};
787
788static struct platform_driver asuspf_driver = {
789 .driver = {
790 .name = ASUS_HOTK_FILE,
791 .owner = THIS_MODULE,
792 }
793};
794
795static struct platform_device *asuspf_device;
796
797static void asus_hotk_add_fs(void)
798{
799 ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
800
801 if (wl_switch_handle)
802 ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
803
804 if (bt_switch_handle)
805 ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
806 show_bluetooth, store_bluetooth);
807
808 if (display_set_handle && display_get_handle)
809 ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
810 else if (display_set_handle)
811 ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
812
813 if (ledd_set_handle)
814 ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
815
816 if (ls_switch_handle && ls_level_handle) {
817 ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
818 ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
819 }
820
821 if (gps_status_handle && gps_on_handle && gps_off_handle)
822 ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
823}
824
825static int asus_handle_init(char *name, acpi_handle * handle,
826 char **paths, int num_paths)
827{
828 int i;
829 acpi_status status;
830
831 for (i = 0; i < num_paths; i++) {
832 status = acpi_get_handle(NULL, paths[i], handle);
833 if (ACPI_SUCCESS(status))
834 return 0;
835 }
836
837 *handle = NULL;
838 return -ENODEV;
839}
840
841#define ASUS_HANDLE_INIT(object) \
842 asus_handle_init(#object, &object##_handle, object##_paths, \
843 ARRAY_SIZE(object##_paths))
844
845/*
846 * This function is used to initialize the hotk with right values. In this
847 * method, we can make all the detection we want, and modify the hotk struct
848 */
849static int asus_hotk_get_info(void)
850{
851 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
852 union acpi_object *model = NULL;
853 unsigned long long bsts_result, hwrs_result;
854 char *string = NULL;
855 acpi_status status;
856
857 /*
858 * Get DSDT headers early enough to allow for differentiating between
859 * models, but late enough to allow acpi_bus_register_driver() to fail
860 * before doing anything ACPI-specific. Should we encounter a machine,
861 * which needs special handling (i.e. its hotkey device has a different
862 * HID), this bit will be moved. A global variable asus_info contains
863 * the DSDT header.
864 */
865 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
866 if (ACPI_FAILURE(status))
867 printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
868
869 /* We have to write 0 on init this far for all ASUS models */
870 if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
871 printk(ASUS_ERR "Hotkey initialization failed\n");
872 return -ENODEV;
873 }
874
875 /* This needs to be called for some laptops to init properly */
876 status =
877 acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
878 if (ACPI_FAILURE(status))
879 printk(ASUS_WARNING "Error calling BSTS\n");
880 else if (bsts_result)
881 printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
882 (uint) bsts_result);
883
884 /* This too ... */
885 write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
886
887 /*
888 * Try to match the object returned by INIT to the specific model.
889 * Handle every possible object (or the lack of thereof) the DSDT
890 * writers might throw at us. When in trouble, we pass NULL to
891 * asus_model_match() and try something completely different.
892 */
893 if (buffer.pointer) {
894 model = buffer.pointer;
895 switch (model->type) {
896 case ACPI_TYPE_STRING:
897 string = model->string.pointer;
898 break;
899 case ACPI_TYPE_BUFFER:
900 string = model->buffer.pointer;
901 break;
902 default:
903 string = "";
904 break;
905 }
906 }
907 hotk->name = kstrdup(string, GFP_KERNEL);
908 if (!hotk->name)
909 return -ENOMEM;
910
911 if (*string)
912 printk(ASUS_NOTICE " %s model detected\n", string);
913
914 ASUS_HANDLE_INIT(mled_set);
915 ASUS_HANDLE_INIT(tled_set);
916 ASUS_HANDLE_INIT(rled_set);
917 ASUS_HANDLE_INIT(pled_set);
918 ASUS_HANDLE_INIT(gled_set);
919
920 ASUS_HANDLE_INIT(ledd_set);
921
922 /*
923 * The HWRS method return informations about the hardware.
924 * 0x80 bit is for WLAN, 0x100 for Bluetooth.
925 * The significance of others is yet to be found.
926 * If we don't find the method, we assume the device are present.
927 */
928 status =
929 acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
930 if (ACPI_FAILURE(status))
931 hwrs_result = WL_HWRS | BT_HWRS;
932
933 if (hwrs_result & WL_HWRS)
934 ASUS_HANDLE_INIT(wl_switch);
935 if (hwrs_result & BT_HWRS)
936 ASUS_HANDLE_INIT(bt_switch);
937
938 ASUS_HANDLE_INIT(wireless_status);
939
940 ASUS_HANDLE_INIT(brightness_set);
941 ASUS_HANDLE_INIT(brightness_get);
942
943 ASUS_HANDLE_INIT(lcd_switch);
944
945 ASUS_HANDLE_INIT(display_set);
946 ASUS_HANDLE_INIT(display_get);
947
948 /* There is a lot of models with "ALSL", but a few get
949 a real light sens, so we need to check it. */
950 if (!ASUS_HANDLE_INIT(ls_switch))
951 ASUS_HANDLE_INIT(ls_level);
952
953 ASUS_HANDLE_INIT(gps_on);
954 ASUS_HANDLE_INIT(gps_off);
955 ASUS_HANDLE_INIT(gps_status);
956
957 kfree(model);
958
959 return AE_OK;
960}
961
962static int asus_hotk_check(void)
963{
964 int result = 0;
965
966 result = acpi_bus_get_status(hotk->device);
967 if (result)
968 return result;
969
970 if (hotk->device->status.present) {
971 result = asus_hotk_get_info();
972 } else {
973 printk(ASUS_ERR "Hotkey device not present, aborting\n");
974 return -EINVAL;
975 }
976
977 return result;
978}
979
980static int asus_hotk_found;
981
982static int asus_hotk_add(struct acpi_device *device)
983{
984 acpi_status status = AE_OK;
985 int result;
986
987 if (!device)
988 return -EINVAL;
989
990 printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
991 ASUS_LAPTOP_VERSION);
992
993 hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
994 if (!hotk)
995 return -ENOMEM;
996
997 hotk->handle = device->handle;
998 strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
999 strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
1000 device->driver_data = hotk;
1001 hotk->device = device;
1002
1003 result = asus_hotk_check();
1004 if (result)
1005 goto end;
1006
1007 asus_hotk_add_fs();
1008
1009 /*
1010 * We install the handler, it will receive the hotk in parameter, so, we
1011 * could add other data to the hotk struct
1012 */
1013 status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1014 asus_hotk_notify, hotk);
1015 if (ACPI_FAILURE(status))
1016 printk(ASUS_ERR "Error installing notify handler\n");
1017
1018 asus_hotk_found = 1;
1019
1020 /* WLED and BLED are on by default */
1021 write_status(bt_switch_handle, 1, BT_ON);
1022 write_status(wl_switch_handle, 1, WL_ON);
1023
1024 /* If the h/w switch is off, we need to check the real status */
1025 write_status(NULL, read_status(BT_ON), BT_ON);
1026 write_status(NULL, read_status(WL_ON), WL_ON);
1027
1028 /* LCD Backlight is on by default */
1029 write_status(NULL, 1, LCD_ON);
1030
1031 /* LED display is off by default */
1032 hotk->ledd_status = 0xFFF;
1033
1034 /* Set initial values of light sensor and level */
1035 hotk->light_switch = 1; /* Default to light sensor disabled */
1036 hotk->light_level = 0; /* level 5 for sensor sensitivity */
1037
1038 if (ls_switch_handle)
1039 set_light_sens_switch(hotk->light_switch);
1040
1041 if (ls_level_handle)
1042 set_light_sens_level(hotk->light_level);
1043
1044 /* GPS is on by default */
1045 write_status(NULL, 1, GPS_ON);
1046
1047 end:
1048 if (result) {
1049 kfree(hotk->name);
1050 kfree(hotk);
1051 }
1052
1053 return result;
1054}
1055
1056static int asus_hotk_remove(struct acpi_device *device, int type)
1057{
1058 acpi_status status = 0;
1059
1060 if (!device || !acpi_driver_data(device))
1061 return -EINVAL;
1062
1063 status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
1064 asus_hotk_notify);
1065 if (ACPI_FAILURE(status))
1066 printk(ASUS_ERR "Error removing notify handler\n");
1067
1068 kfree(hotk->name);
1069 kfree(hotk);
1070
1071 return 0;
1072}
1073
1074static void asus_backlight_exit(void)
1075{
1076 if (asus_backlight_device)
1077 backlight_device_unregister(asus_backlight_device);
1078}
1079
1080#define ASUS_LED_UNREGISTER(object) \
1081 if (object##_led.dev) \
1082 led_classdev_unregister(&object##_led)
1083
1084static void asus_led_exit(void)
1085{
1086 destroy_workqueue(led_workqueue);
1087 ASUS_LED_UNREGISTER(mled);
1088 ASUS_LED_UNREGISTER(tled);
1089 ASUS_LED_UNREGISTER(pled);
1090 ASUS_LED_UNREGISTER(rled);
1091 ASUS_LED_UNREGISTER(gled);
1092}
1093
1094static void __exit asus_laptop_exit(void)
1095{
1096 asus_backlight_exit();
1097 asus_led_exit();
1098
1099 acpi_bus_unregister_driver(&asus_hotk_driver);
1100 sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
1101 platform_device_unregister(asuspf_device);
1102 platform_driver_unregister(&asuspf_driver);
1103}
1104
1105static int asus_backlight_init(struct device *dev)
1106{
1107 struct backlight_device *bd;
1108
1109 if (brightness_set_handle && lcd_switch_handle) {
1110 bd = backlight_device_register(ASUS_HOTK_FILE, dev,
1111 NULL, &asusbl_ops);
1112 if (IS_ERR(bd)) {
1113 printk(ASUS_ERR
1114 "Could not register asus backlight device\n");
1115 asus_backlight_device = NULL;
1116 return PTR_ERR(bd);
1117 }
1118
1119 asus_backlight_device = bd;
1120
1121 bd->props.max_brightness = 15;
1122 bd->props.brightness = read_brightness(NULL);
1123 bd->props.power = FB_BLANK_UNBLANK;
1124 backlight_update_status(bd);
1125 }
1126 return 0;
1127}
1128
1129static int asus_led_register(acpi_handle handle,
1130 struct led_classdev *ldev, struct device *dev)
1131{
1132 if (!handle)
1133 return 0;
1134
1135 return led_classdev_register(dev, ldev);
1136}
1137
1138#define ASUS_LED_REGISTER(object, device) \
1139 asus_led_register(object##_set_handle, &object##_led, device)
1140
1141static int asus_led_init(struct device *dev)
1142{
1143 int rv;
1144
1145 rv = ASUS_LED_REGISTER(mled, dev);
1146 if (rv)
1147 goto out;
1148
1149 rv = ASUS_LED_REGISTER(tled, dev);
1150 if (rv)
1151 goto out1;
1152
1153 rv = ASUS_LED_REGISTER(rled, dev);
1154 if (rv)
1155 goto out2;
1156
1157 rv = ASUS_LED_REGISTER(pled, dev);
1158 if (rv)
1159 goto out3;
1160
1161 rv = ASUS_LED_REGISTER(gled, dev);
1162 if (rv)
1163 goto out4;
1164
1165 led_workqueue = create_singlethread_workqueue("led_workqueue");
1166 if (!led_workqueue)
1167 goto out5;
1168
1169 return 0;
1170out5:
1171 rv = -ENOMEM;
1172 ASUS_LED_UNREGISTER(gled);
1173out4:
1174 ASUS_LED_UNREGISTER(pled);
1175out3:
1176 ASUS_LED_UNREGISTER(rled);
1177out2:
1178 ASUS_LED_UNREGISTER(tled);
1179out1:
1180 ASUS_LED_UNREGISTER(mled);
1181out:
1182 return rv;
1183}
1184
1185static int __init asus_laptop_init(void)
1186{
1187 struct device *dev;
1188 int result;
1189
1190 if (acpi_disabled)
1191 return -ENODEV;
1192
1193 result = acpi_bus_register_driver(&asus_hotk_driver);
1194 if (result < 0)
1195 return result;
1196
1197 /*
1198 * This is a bit of a kludge. We only want this module loaded
1199 * for ASUS systems, but there's currently no way to probe the
1200 * ACPI namespace for ASUS HIDs. So we just return failure if
1201 * we didn't find one, which will cause the module to be
1202 * unloaded.
1203 */
1204 if (!asus_hotk_found) {
1205 acpi_bus_unregister_driver(&asus_hotk_driver);
1206 return -ENODEV;
1207 }
1208
1209 dev = acpi_get_physical_device(hotk->device->handle);
1210
1211 if (!acpi_video_backlight_support()) {
1212 result = asus_backlight_init(dev);
1213 if (result)
1214 goto fail_backlight;
1215 } else
1216 printk(ASUS_INFO "Brightness ignored, must be controlled by "
1217 "ACPI video driver\n");
1218
1219 result = asus_led_init(dev);
1220 if (result)
1221 goto fail_led;
1222
1223 /* Register platform stuff */
1224 result = platform_driver_register(&asuspf_driver);
1225 if (result)
1226 goto fail_platform_driver;
1227
1228 asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
1229 if (!asuspf_device) {
1230 result = -ENOMEM;
1231 goto fail_platform_device1;
1232 }
1233
1234 result = platform_device_add(asuspf_device);
1235 if (result)
1236 goto fail_platform_device2;
1237
1238 result = sysfs_create_group(&asuspf_device->dev.kobj,
1239 &asuspf_attribute_group);
1240 if (result)
1241 goto fail_sysfs;
1242
1243 return 0;
1244
1245 fail_sysfs:
1246 platform_device_del(asuspf_device);
1247
1248 fail_platform_device2:
1249 platform_device_put(asuspf_device);
1250
1251 fail_platform_device1:
1252 platform_driver_unregister(&asuspf_driver);
1253
1254 fail_platform_driver:
1255 asus_led_exit();
1256
1257 fail_led:
1258 asus_backlight_exit();
1259
1260 fail_backlight:
1261
1262 return result;
1263}
1264
1265module_init(asus_laptop_init);
1266module_exit(asus_laptop_exit);
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
new file mode 100644
index 000000000000..1e74988c7b2d
--- /dev/null
+++ b/drivers/platform/x86/asus_acpi.c
@@ -0,0 +1,1460 @@
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#include <linux/kernel.h>
34#include <linux/module.h>
35#include <linux/init.h>
36#include <linux/types.h>
37#include <linux/proc_fs.h>
38#include <linux/backlight.h>
39#include <acpi/acpi_drivers.h>
40#include <acpi/acpi_bus.h>
41#include <asm/uaccess.h>
42
43#define ASUS_ACPI_VERSION "0.30"
44
45#define PROC_ASUS "asus" /* The directory */
46#define PROC_MLED "mled"
47#define PROC_WLED "wled"
48#define PROC_TLED "tled"
49#define PROC_BT "bluetooth"
50#define PROC_LEDD "ledd"
51#define PROC_INFO "info"
52#define PROC_LCD "lcd"
53#define PROC_BRN "brn"
54#define PROC_DISP "disp"
55
56#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
57#define ACPI_HOTK_CLASS "hotkey"
58#define ACPI_HOTK_DEVICE_NAME "Hotkey"
59
60/*
61 * Some events we use, same for all Asus
62 */
63#define BR_UP 0x10
64#define BR_DOWN 0x20
65
66/*
67 * Flags for hotk status
68 */
69#define MLED_ON 0x01 /* Mail LED */
70#define WLED_ON 0x02 /* Wireless LED */
71#define TLED_ON 0x04 /* Touchpad LED */
72#define BT_ON 0x08 /* Internal Bluetooth */
73
74MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
75MODULE_DESCRIPTION(ACPI_HOTK_NAME);
76MODULE_LICENSE("GPL");
77
78static uid_t asus_uid;
79static gid_t asus_gid;
80module_param(asus_uid, uint, 0);
81MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
82module_param(asus_gid, uint, 0);
83MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
84
85/* For each model, all features implemented,
86 * those marked with R are relative to HOTK, A for absolute */
87struct model_data {
88 char *name; /* name of the laptop________________A */
89 char *mt_mled; /* method to handle mled_____________R */
90 char *mled_status; /* node to handle mled reading_______A */
91 char *mt_wled; /* method to handle wled_____________R */
92 char *wled_status; /* node to handle wled reading_______A */
93 char *mt_tled; /* method to handle tled_____________R */
94 char *tled_status; /* node to handle tled reading_______A */
95 char *mt_ledd; /* method to handle LED display______R */
96 char *mt_bt_switch; /* method to switch Bluetooth on/off_R */
97 char *bt_status; /* no model currently supports this__? */
98 char *mt_lcd_switch; /* method to turn LCD on/off_________A */
99 char *lcd_status; /* node to read LCD panel state______A */
100 char *brightness_up; /* method to set brightness up_______A */
101 char *brightness_down; /* method to set brightness down ____A */
102 char *brightness_set; /* method to set absolute brightness_R */
103 char *brightness_get; /* method to get absolute brightness_R */
104 char *brightness_status;/* node to get brightness____________A */
105 char *display_set; /* method to set video output________R */
106 char *display_get; /* method to get video output________R */
107};
108
109/*
110 * This is the main structure, we can use it to store anything interesting
111 * about the hotk device
112 */
113struct asus_hotk {
114 struct acpi_device *device; /* the device we are in */
115 acpi_handle handle; /* the handle of the hotk device */
116 char status; /* status of the hotk, for LEDs */
117 u32 ledd_status; /* status of the LED display */
118 struct model_data *methods; /* methods available on the laptop */
119 u8 brightness; /* brightness level */
120 enum {
121 A1x = 0, /* A1340D, A1300F */
122 A2x, /* A2500H */
123 A4G, /* A4700G */
124 D1x, /* D1 */
125 L2D, /* L2000D */
126 L3C, /* L3800C */
127 L3D, /* L3400D */
128 L3H, /* L3H, L2000E, L5D */
129 L4R, /* L4500R */
130 L5x, /* L5800C */
131 L8L, /* L8400L */
132 M1A, /* M1300A */
133 M2E, /* M2400E, L4400L */
134 M6N, /* M6800N, W3400N */
135 M6R, /* M6700R, A3000G */
136 P30, /* Samsung P30 */
137 S1x, /* S1300A, but also L1400B and M2400A (L84F) */
138 S2x, /* S200 (J1 reported), Victor MP-XP7210 */
139 W1N, /* W1000N */
140 W5A, /* W5A */
141 W3V, /* W3030V */
142 xxN, /* M2400N, M3700N, M5200N, M6800N,
143 S1300N, S5200N*/
144 A4S, /* Z81sp */
145 F3Sa, /* (Centrino) */
146 END_MODEL
147 } model; /* Models currently supported */
148 u16 event_count[128]; /* Count for each event TODO make this better */
149};
150
151/* Here we go */
152#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
153#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
154#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
155#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
156#define S1x_PREFIX "\\_SB.PCI0.PX40."
157#define S2x_PREFIX A1x_PREFIX
158#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
159
160static struct model_data model_conf[END_MODEL] = {
161 /*
162 * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
163 * it seems to be a kind of switch, but what for ?
164 */
165
166 {
167 .name = "A1x",
168 .mt_mled = "MLED",
169 .mled_status = "\\MAIL",
170 .mt_lcd_switch = A1x_PREFIX "_Q10",
171 .lcd_status = "\\BKLI",
172 .brightness_up = A1x_PREFIX "_Q0E",
173 .brightness_down = A1x_PREFIX "_Q0F"},
174
175 {
176 .name = "A2x",
177 .mt_mled = "MLED",
178 .mt_wled = "WLED",
179 .wled_status = "\\SG66",
180 .mt_lcd_switch = "\\Q10",
181 .lcd_status = "\\BAOF",
182 .brightness_set = "SPLV",
183 .brightness_get = "GPLV",
184 .display_set = "SDSP",
185 .display_get = "\\INFB"},
186
187 {
188 .name = "A4G",
189 .mt_mled = "MLED",
190/* WLED present, but not controlled by ACPI */
191 .mt_lcd_switch = xxN_PREFIX "_Q10",
192 .brightness_set = "SPLV",
193 .brightness_get = "GPLV",
194 .display_set = "SDSP",
195 .display_get = "\\ADVG"},
196
197 {
198 .name = "D1x",
199 .mt_mled = "MLED",
200 .mt_lcd_switch = "\\Q0D",
201 .lcd_status = "\\GP11",
202 .brightness_up = "\\Q0C",
203 .brightness_down = "\\Q0B",
204 .brightness_status = "\\BLVL",
205 .display_set = "SDSP",
206 .display_get = "\\INFB"},
207
208 {
209 .name = "L2D",
210 .mt_mled = "MLED",
211 .mled_status = "\\SGP6",
212 .mt_wled = "WLED",
213 .wled_status = "\\RCP3",
214 .mt_lcd_switch = "\\Q10",
215 .lcd_status = "\\SGP0",
216 .brightness_up = "\\Q0E",
217 .brightness_down = "\\Q0F",
218 .display_set = "SDSP",
219 .display_get = "\\INFB"},
220
221 {
222 .name = "L3C",
223 .mt_mled = "MLED",
224 .mt_wled = "WLED",
225 .mt_lcd_switch = L3C_PREFIX "_Q10",
226 .lcd_status = "\\GL32",
227 .brightness_set = "SPLV",
228 .brightness_get = "GPLV",
229 .display_set = "SDSP",
230 .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
231
232 {
233 .name = "L3D",
234 .mt_mled = "MLED",
235 .mled_status = "\\MALD",
236 .mt_wled = "WLED",
237 .mt_lcd_switch = "\\Q10",
238 .lcd_status = "\\BKLG",
239 .brightness_set = "SPLV",
240 .brightness_get = "GPLV",
241 .display_set = "SDSP",
242 .display_get = "\\INFB"},
243
244 {
245 .name = "L3H",
246 .mt_mled = "MLED",
247 .mt_wled = "WLED",
248 .mt_lcd_switch = "EHK",
249 .lcd_status = "\\_SB.PCI0.PM.PBC",
250 .brightness_set = "SPLV",
251 .brightness_get = "GPLV",
252 .display_set = "SDSP",
253 .display_get = "\\INFB"},
254
255 {
256 .name = "L4R",
257 .mt_mled = "MLED",
258 .mt_wled = "WLED",
259 .wled_status = "\\_SB.PCI0.SBRG.SG13",
260 .mt_lcd_switch = xxN_PREFIX "_Q10",
261 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
262 .brightness_set = "SPLV",
263 .brightness_get = "GPLV",
264 .display_set = "SDSP",
265 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
266
267 {
268 .name = "L5x",
269 .mt_mled = "MLED",
270/* WLED present, but not controlled by ACPI */
271 .mt_tled = "TLED",
272 .mt_lcd_switch = "\\Q0D",
273 .lcd_status = "\\BAOF",
274 .brightness_set = "SPLV",
275 .brightness_get = "GPLV",
276 .display_set = "SDSP",
277 .display_get = "\\INFB"},
278
279 {
280 .name = "L8L"
281/* No features, but at least support the hotkeys */
282 },
283
284 {
285 .name = "M1A",
286 .mt_mled = "MLED",
287 .mt_lcd_switch = M1A_PREFIX "Q10",
288 .lcd_status = "\\PNOF",
289 .brightness_up = M1A_PREFIX "Q0E",
290 .brightness_down = M1A_PREFIX "Q0F",
291 .brightness_status = "\\BRIT",
292 .display_set = "SDSP",
293 .display_get = "\\INFB"},
294
295 {
296 .name = "M2E",
297 .mt_mled = "MLED",
298 .mt_wled = "WLED",
299 .mt_lcd_switch = "\\Q10",
300 .lcd_status = "\\GP06",
301 .brightness_set = "SPLV",
302 .brightness_get = "GPLV",
303 .display_set = "SDSP",
304 .display_get = "\\INFB"},
305
306 {
307 .name = "M6N",
308 .mt_mled = "MLED",
309 .mt_wled = "WLED",
310 .wled_status = "\\_SB.PCI0.SBRG.SG13",
311 .mt_lcd_switch = xxN_PREFIX "_Q10",
312 .lcd_status = "\\_SB.BKLT",
313 .brightness_set = "SPLV",
314 .brightness_get = "GPLV",
315 .display_set = "SDSP",
316 .display_get = "\\SSTE"},
317
318 {
319 .name = "M6R",
320 .mt_mled = "MLED",
321 .mt_wled = "WLED",
322 .mt_lcd_switch = xxN_PREFIX "_Q10",
323 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
324 .brightness_set = "SPLV",
325 .brightness_get = "GPLV",
326 .display_set = "SDSP",
327 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
328
329 {
330 .name = "P30",
331 .mt_wled = "WLED",
332 .mt_lcd_switch = P30_PREFIX "_Q0E",
333 .lcd_status = "\\BKLT",
334 .brightness_up = P30_PREFIX "_Q68",
335 .brightness_down = P30_PREFIX "_Q69",
336 .brightness_get = "GPLV",
337 .display_set = "SDSP",
338 .display_get = "\\DNXT"},
339
340 {
341 .name = "S1x",
342 .mt_mled = "MLED",
343 .mled_status = "\\EMLE",
344 .mt_wled = "WLED",
345 .mt_lcd_switch = S1x_PREFIX "Q10",
346 .lcd_status = "\\PNOF",
347 .brightness_set = "SPLV",
348 .brightness_get = "GPLV"},
349
350 {
351 .name = "S2x",
352 .mt_mled = "MLED",
353 .mled_status = "\\MAIL",
354 .mt_lcd_switch = S2x_PREFIX "_Q10",
355 .lcd_status = "\\BKLI",
356 .brightness_up = S2x_PREFIX "_Q0B",
357 .brightness_down = S2x_PREFIX "_Q0A"},
358
359 {
360 .name = "W1N",
361 .mt_mled = "MLED",
362 .mt_wled = "WLED",
363 .mt_ledd = "SLCM",
364 .mt_lcd_switch = xxN_PREFIX "_Q10",
365 .lcd_status = "\\BKLT",
366 .brightness_set = "SPLV",
367 .brightness_get = "GPLV",
368 .display_set = "SDSP",
369 .display_get = "\\ADVG"},
370
371 {
372 .name = "W5A",
373 .mt_bt_switch = "BLED",
374 .mt_wled = "WLED",
375 .mt_lcd_switch = xxN_PREFIX "_Q10",
376 .brightness_set = "SPLV",
377 .brightness_get = "GPLV",
378 .display_set = "SDSP",
379 .display_get = "\\ADVG"},
380
381 {
382 .name = "W3V",
383 .mt_mled = "MLED",
384 .mt_wled = "WLED",
385 .mt_lcd_switch = xxN_PREFIX "_Q10",
386 .lcd_status = "\\BKLT",
387 .brightness_set = "SPLV",
388 .brightness_get = "GPLV",
389 .display_set = "SDSP",
390 .display_get = "\\INFB"},
391
392 {
393 .name = "xxN",
394 .mt_mled = "MLED",
395/* WLED present, but not controlled by ACPI */
396 .mt_lcd_switch = xxN_PREFIX "_Q10",
397 .lcd_status = "\\BKLT",
398 .brightness_set = "SPLV",
399 .brightness_get = "GPLV",
400 .display_set = "SDSP",
401 .display_get = "\\ADVG"},
402
403 {
404 .name = "A4S",
405 .brightness_set = "SPLV",
406 .brightness_get = "GPLV",
407 .mt_bt_switch = "BLED",
408 .mt_wled = "WLED"
409 },
410
411 {
412 .name = "F3Sa",
413 .mt_bt_switch = "BLED",
414 .mt_wled = "WLED",
415 .mt_mled = "MLED",
416 .brightness_get = "GPLV",
417 .brightness_set = "SPLV",
418 .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10",
419 .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN",
420 .display_get = "\\ADVG",
421 .display_set = "SDSP",
422 },
423
424};
425
426/* procdir we use */
427static struct proc_dir_entry *asus_proc_dir;
428
429static struct backlight_device *asus_backlight_device;
430
431/*
432 * This header is made available to allow proper configuration given model,
433 * revision number , ... this info cannot go in struct asus_hotk because it is
434 * available before the hotk
435 */
436static struct acpi_table_header *asus_info;
437
438/* The actual device the driver binds to */
439static struct asus_hotk *hotk;
440
441/*
442 * The hotkey driver and autoloading declaration
443 */
444static int asus_hotk_add(struct acpi_device *device);
445static int asus_hotk_remove(struct acpi_device *device, int type);
446static const struct acpi_device_id asus_device_ids[] = {
447 {"ATK0100", 0},
448 {"", 0},
449};
450MODULE_DEVICE_TABLE(acpi, asus_device_ids);
451
452static struct acpi_driver asus_hotk_driver = {
453 .name = "asus_acpi",
454 .class = ACPI_HOTK_CLASS,
455 .ids = asus_device_ids,
456 .ops = {
457 .add = asus_hotk_add,
458 .remove = asus_hotk_remove,
459 },
460};
461
462/*
463 * This function evaluates an ACPI method, given an int as parameter, the
464 * method is searched within the scope of the handle, can be NULL. The output
465 * of the method is written is output, which can also be NULL
466 *
467 * returns 1 if write is successful, 0 else.
468 */
469static int write_acpi_int(acpi_handle handle, const char *method, int val,
470 struct acpi_buffer *output)
471{
472 struct acpi_object_list params; /* list of input parameters (int) */
473 union acpi_object in_obj; /* the only param we use */
474 acpi_status status;
475
476 params.count = 1;
477 params.pointer = &in_obj;
478 in_obj.type = ACPI_TYPE_INTEGER;
479 in_obj.integer.value = val;
480
481 status = acpi_evaluate_object(handle, (char *)method, &params, output);
482 return (status == AE_OK);
483}
484
485static int read_acpi_int(acpi_handle handle, const char *method, int *val)
486{
487 struct acpi_buffer output;
488 union acpi_object out_obj;
489 acpi_status status;
490
491 output.length = sizeof(out_obj);
492 output.pointer = &out_obj;
493
494 status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
495 *val = out_obj.integer.value;
496 return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
497}
498
499/*
500 * We write our info in page, we begin at offset off and cannot write more
501 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
502 * number of bytes written in page
503 */
504static int
505proc_read_info(char *page, char **start, off_t off, int count, int *eof,
506 void *data)
507{
508 int len = 0;
509 int temp;
510 char buf[16]; /* enough for all info */
511 /*
512 * We use the easy way, we don't care of off and count,
513 * so we don't set eof to 1
514 */
515
516 len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
517 len += sprintf(page + len, "Model reference : %s\n",
518 hotk->methods->name);
519 /*
520 * The SFUN method probably allows the original driver to get the list
521 * of features supported by a given model. For now, 0x0100 or 0x0800
522 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
523 * The significance of others is yet to be found.
524 */
525 if (read_acpi_int(hotk->handle, "SFUN", &temp))
526 len +=
527 sprintf(page + len, "SFUN value : 0x%04x\n", temp);
528 /*
529 * Another value for userspace: the ASYM method returns 0x02 for
530 * battery low and 0x04 for battery critical, its readings tend to be
531 * more accurate than those provided by _BST.
532 * Note: since not all the laptops provide this method, errors are
533 * silently ignored.
534 */
535 if (read_acpi_int(hotk->handle, "ASYM", &temp))
536 len +=
537 sprintf(page + len, "ASYM value : 0x%04x\n", temp);
538 if (asus_info) {
539 snprintf(buf, 16, "%d", asus_info->length);
540 len += sprintf(page + len, "DSDT length : %s\n", buf);
541 snprintf(buf, 16, "%d", asus_info->checksum);
542 len += sprintf(page + len, "DSDT checksum : %s\n", buf);
543 snprintf(buf, 16, "%d", asus_info->revision);
544 len += sprintf(page + len, "DSDT revision : %s\n", buf);
545 snprintf(buf, 7, "%s", asus_info->oem_id);
546 len += sprintf(page + len, "OEM id : %s\n", buf);
547 snprintf(buf, 9, "%s", asus_info->oem_table_id);
548 len += sprintf(page + len, "OEM table id : %s\n", buf);
549 snprintf(buf, 16, "%x", asus_info->oem_revision);
550 len += sprintf(page + len, "OEM revision : 0x%s\n", buf);
551 snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
552 len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
553 snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
554 len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf);
555 }
556
557 return len;
558}
559
560/*
561 * /proc handlers
562 * We write our info in page, we begin at offset off and cannot write more
563 * than count bytes. We set eof to 1 if we handle those 2 values. We return the
564 * number of bytes written in page
565 */
566
567/* Generic LED functions */
568static int read_led(const char *ledname, int ledmask)
569{
570 if (ledname) {
571 int led_status;
572
573 if (read_acpi_int(NULL, ledname, &led_status))
574 return led_status;
575 else
576 printk(KERN_WARNING "Asus ACPI: Error reading LED "
577 "status\n");
578 }
579 return (hotk->status & ledmask) ? 1 : 0;
580}
581
582static int parse_arg(const char __user *buf, unsigned long count, int *val)
583{
584 char s[32];
585 if (!count)
586 return 0;
587 if (count > 31)
588 return -EINVAL;
589 if (copy_from_user(s, buf, count))
590 return -EFAULT;
591 s[count] = 0;
592 if (sscanf(s, "%i", val) != 1)
593 return -EINVAL;
594 return count;
595}
596
597/* FIXME: kill extraneous args so it can be called independently */
598static int
599write_led(const char __user *buffer, unsigned long count,
600 char *ledname, int ledmask, int invert)
601{
602 int rv, value;
603 int led_out = 0;
604
605 rv = parse_arg(buffer, count, &value);
606 if (rv > 0)
607 led_out = value ? 1 : 0;
608
609 hotk->status =
610 (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
611
612 if (invert) /* invert target value */
613 led_out = !led_out;
614
615 if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
616 printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
617 ledname);
618
619 return rv;
620}
621
622/*
623 * Proc handlers for MLED
624 */
625static int
626proc_read_mled(char *page, char **start, off_t off, int count, int *eof,
627 void *data)
628{
629 return sprintf(page, "%d\n",
630 read_led(hotk->methods->mled_status, MLED_ON));
631}
632
633static int
634proc_write_mled(struct file *file, const char __user *buffer,
635 unsigned long count, void *data)
636{
637 return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
638}
639
640/*
641 * Proc handlers for LED display
642 */
643static int
644proc_read_ledd(char *page, char **start, off_t off, int count, int *eof,
645 void *data)
646{
647 return sprintf(page, "0x%08x\n", hotk->ledd_status);
648}
649
650static int
651proc_write_ledd(struct file *file, const char __user *buffer,
652 unsigned long count, void *data)
653{
654 int rv, value;
655
656 rv = parse_arg(buffer, count, &value);
657 if (rv > 0) {
658 if (!write_acpi_int
659 (hotk->handle, hotk->methods->mt_ledd, value, NULL))
660 printk(KERN_WARNING
661 "Asus ACPI: LED display write failed\n");
662 else
663 hotk->ledd_status = (u32) value;
664 }
665 return rv;
666}
667
668/*
669 * Proc handlers for WLED
670 */
671static int
672proc_read_wled(char *page, char **start, off_t off, int count, int *eof,
673 void *data)
674{
675 return sprintf(page, "%d\n",
676 read_led(hotk->methods->wled_status, WLED_ON));
677}
678
679static int
680proc_write_wled(struct file *file, const char __user *buffer,
681 unsigned long count, void *data)
682{
683 return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
684}
685
686/*
687 * Proc handlers for Bluetooth
688 */
689static int
690proc_read_bluetooth(char *page, char **start, off_t off, int count, int *eof,
691 void *data)
692{
693 return sprintf(page, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
694}
695
696static int
697proc_write_bluetooth(struct file *file, const char __user *buffer,
698 unsigned long count, void *data)
699{
700 /* Note: mt_bt_switch controls both internal Bluetooth adapter's
701 presence and its LED */
702 return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
703}
704
705/*
706 * Proc handlers for TLED
707 */
708static int
709proc_read_tled(char *page, char **start, off_t off, int count, int *eof,
710 void *data)
711{
712 return sprintf(page, "%d\n",
713 read_led(hotk->methods->tled_status, TLED_ON));
714}
715
716static int
717proc_write_tled(struct file *file, const char __user *buffer,
718 unsigned long count, void *data)
719{
720 return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
721}
722
723static int get_lcd_state(void)
724{
725 int lcd = 0;
726
727 if (hotk->model == L3H) {
728 /* L3H and the like have to be handled differently */
729 acpi_status status = 0;
730 struct acpi_object_list input;
731 union acpi_object mt_params[2];
732 struct acpi_buffer output;
733 union acpi_object out_obj;
734
735 input.count = 2;
736 input.pointer = mt_params;
737 /* Note: the following values are partly guessed up, but
738 otherwise they seem to work */
739 mt_params[0].type = ACPI_TYPE_INTEGER;
740 mt_params[0].integer.value = 0x02;
741 mt_params[1].type = ACPI_TYPE_INTEGER;
742 mt_params[1].integer.value = 0x02;
743
744 output.length = sizeof(out_obj);
745 output.pointer = &out_obj;
746
747 status =
748 acpi_evaluate_object(NULL, hotk->methods->lcd_status,
749 &input, &output);
750 if (status != AE_OK)
751 return -1;
752 if (out_obj.type == ACPI_TYPE_INTEGER)
753 /* That's what the AML code does */
754 lcd = out_obj.integer.value >> 8;
755 } else if (hotk->model == F3Sa) {
756 unsigned long long tmp;
757 union acpi_object param;
758 struct acpi_object_list input;
759 acpi_status status;
760
761 /* Read pin 11 */
762 param.type = ACPI_TYPE_INTEGER;
763 param.integer.value = 0x11;
764 input.count = 1;
765 input.pointer = &param;
766
767 status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
768 &input, &tmp);
769 if (status != AE_OK)
770 return -1;
771
772 lcd = tmp;
773 } else {
774 /* We don't have to check anything if we are here */
775 if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
776 printk(KERN_WARNING
777 "Asus ACPI: Error reading LCD status\n");
778
779 if (hotk->model == L2D)
780 lcd = ~lcd;
781 }
782
783 return (lcd & 1);
784}
785
786static int set_lcd_state(int value)
787{
788 int lcd = 0;
789 acpi_status status = 0;
790
791 lcd = value ? 1 : 0;
792 if (lcd != get_lcd_state()) {
793 /* switch */
794 if (hotk->model != L3H) {
795 status =
796 acpi_evaluate_object(NULL,
797 hotk->methods->mt_lcd_switch,
798 NULL, NULL);
799 } else {
800 /* L3H and the like must be handled differently */
801 if (!write_acpi_int
802 (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
803 NULL))
804 status = AE_ERROR;
805 /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
806 the exact behaviour is simulated here */
807 }
808 if (ACPI_FAILURE(status))
809 printk(KERN_WARNING "Asus ACPI: Error switching LCD\n");
810 }
811 return 0;
812
813}
814
815static int
816proc_read_lcd(char *page, char **start, off_t off, int count, int *eof,
817 void *data)
818{
819 return sprintf(page, "%d\n", get_lcd_state());
820}
821
822static int
823proc_write_lcd(struct file *file, const char __user *buffer,
824 unsigned long count, void *data)
825{
826 int rv, value;
827
828 rv = parse_arg(buffer, count, &value);
829 if (rv > 0)
830 set_lcd_state(value);
831 return rv;
832}
833
834static int read_brightness(struct backlight_device *bd)
835{
836 int value;
837
838 if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
839 if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
840 &value))
841 printk(KERN_WARNING
842 "Asus ACPI: Error reading brightness\n");
843 } else if (hotk->methods->brightness_status) { /* For D1 for example */
844 if (!read_acpi_int(NULL, hotk->methods->brightness_status,
845 &value))
846 printk(KERN_WARNING
847 "Asus ACPI: Error reading brightness\n");
848 } else /* No GPLV method */
849 value = hotk->brightness;
850 return value;
851}
852
853/*
854 * Change the brightness level
855 */
856static int set_brightness(int value)
857{
858 acpi_status status = 0;
859 int ret = 0;
860
861 /* SPLV laptop */
862 if (hotk->methods->brightness_set) {
863 if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
864 value, NULL))
865 printk(KERN_WARNING
866 "Asus ACPI: Error changing brightness\n");
867 ret = -EIO;
868 goto out;
869 }
870
871 /* No SPLV method if we are here, act as appropriate */
872 value -= read_brightness(NULL);
873 while (value != 0) {
874 status = acpi_evaluate_object(NULL, (value > 0) ?
875 hotk->methods->brightness_up :
876 hotk->methods->brightness_down,
877 NULL, NULL);
878 (value > 0) ? value-- : value++;
879 if (ACPI_FAILURE(status))
880 printk(KERN_WARNING
881 "Asus ACPI: Error changing brightness\n");
882 ret = -EIO;
883 }
884out:
885 return ret;
886}
887
888static int set_brightness_status(struct backlight_device *bd)
889{
890 return set_brightness(bd->props.brightness);
891}
892
893static int
894proc_read_brn(char *page, char **start, off_t off, int count, int *eof,
895 void *data)
896{
897 return sprintf(page, "%d\n", read_brightness(NULL));
898}
899
900static int
901proc_write_brn(struct file *file, const char __user *buffer,
902 unsigned long count, void *data)
903{
904 int rv, value;
905
906 rv = parse_arg(buffer, count, &value);
907 if (rv > 0) {
908 value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
909 /* 0 <= value <= 15 */
910 set_brightness(value);
911 }
912 return rv;
913}
914
915static void set_display(int value)
916{
917 /* no sanity check needed for now */
918 if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
919 value, NULL))
920 printk(KERN_WARNING "Asus ACPI: Error setting display\n");
921 return;
922}
923
924/*
925 * Now, *this* one could be more user-friendly, but so far, no-one has
926 * complained. The significance of bits is the same as in proc_write_disp()
927 */
928static int
929proc_read_disp(char *page, char **start, off_t off, int count, int *eof,
930 void *data)
931{
932 int value = 0;
933
934 if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
935 printk(KERN_WARNING
936 "Asus ACPI: Error reading display status\n");
937 value &= 0x07; /* needed for some models, shouldn't hurt others */
938 return sprintf(page, "%d\n", value);
939}
940
941/*
942 * Experimental support for display switching. As of now: 1 should activate
943 * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
944 * (bitwise) of these will suffice. I never actually tested 3 displays hooked
945 * up simultaneously, so be warned. See the acpi4asus README for more info.
946 */
947static int
948proc_write_disp(struct file *file, const char __user *buffer,
949 unsigned long count, void *data)
950{
951 int rv, value;
952
953 rv = parse_arg(buffer, count, &value);
954 if (rv > 0)
955 set_display(value);
956 return rv;
957}
958
959typedef int (proc_readfunc) (char *page, char **start, off_t off, int count,
960 int *eof, void *data);
961typedef int (proc_writefunc) (struct file *file, const char __user *buffer,
962 unsigned long count, void *data);
963
964static int
965asus_proc_add(char *name, proc_writefunc *writefunc,
966 proc_readfunc *readfunc, mode_t mode,
967 struct acpi_device *device)
968{
969 struct proc_dir_entry *proc =
970 create_proc_entry(name, mode, acpi_device_dir(device));
971 if (!proc) {
972 printk(KERN_WARNING " Unable to create %s fs entry\n", name);
973 return -1;
974 }
975 proc->write_proc = writefunc;
976 proc->read_proc = readfunc;
977 proc->data = acpi_driver_data(device);
978 proc->owner = THIS_MODULE;
979 proc->uid = asus_uid;
980 proc->gid = asus_gid;
981 return 0;
982}
983
984static int asus_hotk_add_fs(struct acpi_device *device)
985{
986 struct proc_dir_entry *proc;
987 mode_t mode;
988
989 /*
990 * If parameter uid or gid is not changed, keep the default setting for
991 * our proc entries (-rw-rw-rw-) else, it means we care about security,
992 * and then set to -rw-rw----
993 */
994
995 if ((asus_uid == 0) && (asus_gid == 0)) {
996 mode = S_IFREG | S_IRUGO | S_IWUGO;
997 } else {
998 mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
999 printk(KERN_WARNING " asus_uid and asus_gid parameters are "
1000 "deprecated, use chown and chmod instead!\n");
1001 }
1002
1003 acpi_device_dir(device) = asus_proc_dir;
1004 if (!acpi_device_dir(device))
1005 return -ENODEV;
1006
1007 proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device));
1008 if (proc) {
1009 proc->read_proc = proc_read_info;
1010 proc->data = acpi_driver_data(device);
1011 proc->owner = THIS_MODULE;
1012 proc->uid = asus_uid;
1013 proc->gid = asus_gid;
1014 } else {
1015 printk(KERN_WARNING " Unable to create " PROC_INFO
1016 " fs entry\n");
1017 }
1018
1019 if (hotk->methods->mt_wled) {
1020 asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled,
1021 mode, device);
1022 }
1023
1024 if (hotk->methods->mt_ledd) {
1025 asus_proc_add(PROC_LEDD, &proc_write_ledd, &proc_read_ledd,
1026 mode, device);
1027 }
1028
1029 if (hotk->methods->mt_mled) {
1030 asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled,
1031 mode, device);
1032 }
1033
1034 if (hotk->methods->mt_tled) {
1035 asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled,
1036 mode, device);
1037 }
1038
1039 if (hotk->methods->mt_bt_switch) {
1040 asus_proc_add(PROC_BT, &proc_write_bluetooth,
1041 &proc_read_bluetooth, mode, device);
1042 }
1043
1044 /*
1045 * We need both read node and write method as LCD switch is also
1046 * accessible from the keyboard
1047 */
1048 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
1049 asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode,
1050 device);
1051 }
1052
1053 if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
1054 (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
1055 asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode,
1056 device);
1057 }
1058
1059 if (hotk->methods->display_set) {
1060 asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp,
1061 mode, device);
1062 }
1063
1064 return 0;
1065}
1066
1067static int asus_hotk_remove_fs(struct acpi_device *device)
1068{
1069 if (acpi_device_dir(device)) {
1070 remove_proc_entry(PROC_INFO, acpi_device_dir(device));
1071 if (hotk->methods->mt_wled)
1072 remove_proc_entry(PROC_WLED, acpi_device_dir(device));
1073 if (hotk->methods->mt_mled)
1074 remove_proc_entry(PROC_MLED, acpi_device_dir(device));
1075 if (hotk->methods->mt_tled)
1076 remove_proc_entry(PROC_TLED, acpi_device_dir(device));
1077 if (hotk->methods->mt_ledd)
1078 remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
1079 if (hotk->methods->mt_bt_switch)
1080 remove_proc_entry(PROC_BT, acpi_device_dir(device));
1081 if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
1082 remove_proc_entry(PROC_LCD, acpi_device_dir(device));
1083 if ((hotk->methods->brightness_up
1084 && hotk->methods->brightness_down)
1085 || (hotk->methods->brightness_get
1086 && hotk->methods->brightness_set))
1087 remove_proc_entry(PROC_BRN, acpi_device_dir(device));
1088 if (hotk->methods->display_set)
1089 remove_proc_entry(PROC_DISP, acpi_device_dir(device));
1090 }
1091 return 0;
1092}
1093
1094static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
1095{
1096 /* TODO Find a better way to handle events count. */
1097 if (!hotk)
1098 return;
1099
1100 if ((event & ~((u32) BR_UP)) < 16)
1101 hotk->brightness = (event & ~((u32) BR_UP));
1102 else if ((event & ~((u32) BR_DOWN)) < 16)
1103 hotk->brightness = (event & ~((u32) BR_DOWN));
1104
1105 acpi_bus_generate_proc_event(hotk->device, event,
1106 hotk->event_count[event % 128]++);
1107
1108 return;
1109}
1110
1111/*
1112 * Match the model string to the list of supported models. Return END_MODEL if
1113 * no match or model is NULL.
1114 */
1115static int asus_model_match(char *model)
1116{
1117 if (model == NULL)
1118 return END_MODEL;
1119
1120 if (strncmp(model, "L3D", 3) == 0)
1121 return L3D;
1122 else if (strncmp(model, "L2E", 3) == 0 ||
1123 strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
1124 return L3H;
1125 else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
1126 return L3C;
1127 else if (strncmp(model, "L8L", 3) == 0)
1128 return L8L;
1129 else if (strncmp(model, "L4R", 3) == 0)
1130 return L4R;
1131 else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
1132 return M6N;
1133 else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
1134 return M6R;
1135 else if (strncmp(model, "M2N", 3) == 0 ||
1136 strncmp(model, "M3N", 3) == 0 ||
1137 strncmp(model, "M5N", 3) == 0 ||
1138 strncmp(model, "M6N", 3) == 0 ||
1139 strncmp(model, "S1N", 3) == 0 ||
1140 strncmp(model, "S5N", 3) == 0 || strncmp(model, "W1N", 3) == 0)
1141 return xxN;
1142 else if (strncmp(model, "M1", 2) == 0)
1143 return M1A;
1144 else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
1145 return M2E;
1146 else if (strncmp(model, "L2", 2) == 0)
1147 return L2D;
1148 else if (strncmp(model, "L8", 2) == 0)
1149 return S1x;
1150 else if (strncmp(model, "D1", 2) == 0)
1151 return D1x;
1152 else if (strncmp(model, "A1", 2) == 0)
1153 return A1x;
1154 else if (strncmp(model, "A2", 2) == 0)
1155 return A2x;
1156 else if (strncmp(model, "J1", 2) == 0)
1157 return S2x;
1158 else if (strncmp(model, "L5", 2) == 0)
1159 return L5x;
1160 else if (strncmp(model, "A4G", 3) == 0)
1161 return A4G;
1162 else if (strncmp(model, "W1N", 3) == 0)
1163 return W1N;
1164 else if (strncmp(model, "W3V", 3) == 0)
1165 return W3V;
1166 else if (strncmp(model, "W5A", 3) == 0)
1167 return W5A;
1168 else if (strncmp(model, "A4S", 3) == 0)
1169 return A4S;
1170 else if (strncmp(model, "F3Sa", 4) == 0)
1171 return F3Sa;
1172 else
1173 return END_MODEL;
1174}
1175
1176/*
1177 * This function is used to initialize the hotk with right values. In this
1178 * method, we can make all the detection we want, and modify the hotk struct
1179 */
1180static int asus_hotk_get_info(void)
1181{
1182 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1183 union acpi_object *model = NULL;
1184 int bsts_result;
1185 char *string = NULL;
1186 acpi_status status;
1187
1188 /*
1189 * Get DSDT headers early enough to allow for differentiating between
1190 * models, but late enough to allow acpi_bus_register_driver() to fail
1191 * before doing anything ACPI-specific. Should we encounter a machine,
1192 * which needs special handling (i.e. its hotkey device has a different
1193 * HID), this bit will be moved. A global variable asus_info contains
1194 * the DSDT header.
1195 */
1196 status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
1197 if (ACPI_FAILURE(status))
1198 printk(KERN_WARNING " Couldn't get the DSDT table header\n");
1199
1200 /* We have to write 0 on init this far for all ASUS models */
1201 if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
1202 printk(KERN_ERR " Hotkey initialization failed\n");
1203 return -ENODEV;
1204 }
1205
1206 /* This needs to be called for some laptops to init properly */
1207 if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
1208 printk(KERN_WARNING " Error calling BSTS\n");
1209 else if (bsts_result)
1210 printk(KERN_NOTICE " BSTS called, 0x%02x returned\n",
1211 bsts_result);
1212
1213 /*
1214 * Try to match the object returned by INIT to the specific model.
1215 * Handle every possible object (or the lack of thereof) the DSDT
1216 * writers might throw at us. When in trouble, we pass NULL to
1217 * asus_model_match() and try something completely different.
1218 */
1219 if (buffer.pointer) {
1220 model = buffer.pointer;
1221 switch (model->type) {
1222 case ACPI_TYPE_STRING:
1223 string = model->string.pointer;
1224 break;
1225 case ACPI_TYPE_BUFFER:
1226 string = model->buffer.pointer;
1227 break;
1228 default:
1229 kfree(model);
1230 model = NULL;
1231 break;
1232 }
1233 }
1234 hotk->model = asus_model_match(string);
1235 if (hotk->model == END_MODEL) { /* match failed */
1236 if (asus_info &&
1237 strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
1238 hotk->model = P30;
1239 printk(KERN_NOTICE
1240 " Samsung P30 detected, supported\n");
1241 } else {
1242 hotk->model = M2E;
1243 printk(KERN_NOTICE " unsupported model %s, trying "
1244 "default values\n", string);
1245 printk(KERN_NOTICE
1246 " send /proc/acpi/dsdt to the developers\n");
1247 kfree(model);
1248 return -ENODEV;
1249 }
1250 hotk->methods = &model_conf[hotk->model];
1251 return AE_OK;
1252 }
1253 hotk->methods = &model_conf[hotk->model];
1254 printk(KERN_NOTICE " %s model detected, supported\n", string);
1255
1256 /* Sort of per-model blacklist */
1257 if (strncmp(string, "L2B", 3) == 0)
1258 hotk->methods->lcd_status = NULL;
1259 /* L2B is similar enough to L3C to use its settings, with this only
1260 exception */
1261 else if (strncmp(string, "A3G", 3) == 0)
1262 hotk->methods->lcd_status = "\\BLFG";
1263 /* A3G is like M6R */
1264 else if (strncmp(string, "S5N", 3) == 0 ||
1265 strncmp(string, "M5N", 3) == 0 ||
1266 strncmp(string, "W3N", 3) == 0)
1267 hotk->methods->mt_mled = NULL;
1268 /* S5N, M5N and W3N have no MLED */
1269 else if (strncmp(string, "L5D", 3) == 0)
1270 hotk->methods->mt_wled = NULL;
1271 /* L5D's WLED is not controlled by ACPI */
1272 else if (strncmp(string, "M2N", 3) == 0 ||
1273 strncmp(string, "W3V", 3) == 0 ||
1274 strncmp(string, "S1N", 3) == 0)
1275 hotk->methods->mt_wled = "WLED";
1276 /* M2N, S1N and W3V have a usable WLED */
1277 else if (asus_info) {
1278 if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
1279 hotk->methods->mled_status = NULL;
1280 /* S1300A reports L84F, but L1400B too, account for that */
1281 }
1282
1283 kfree(model);
1284
1285 return AE_OK;
1286}
1287
1288static int asus_hotk_check(void)
1289{
1290 int result = 0;
1291
1292 result = acpi_bus_get_status(hotk->device);
1293 if (result)
1294 return result;
1295
1296 if (hotk->device->status.present) {
1297 result = asus_hotk_get_info();
1298 } else {
1299 printk(KERN_ERR " Hotkey device not present, aborting\n");
1300 return -EINVAL;
1301 }
1302
1303 return result;
1304}
1305
1306static int asus_hotk_found;
1307
1308static int asus_hotk_add(struct acpi_device *device)
1309{
1310 acpi_status status = AE_OK;
1311 int result;
1312
1313 if (!device)
1314 return -EINVAL;
1315
1316 printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
1317 ASUS_ACPI_VERSION);
1318
1319 hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1320 if (!hotk)
1321 return -ENOMEM;
1322
1323 hotk->handle = device->handle;
1324 strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
1325 strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
1326 device->driver_data = hotk;
1327 hotk->device = device;
1328
1329 result = asus_hotk_check();
1330 if (result)
1331 goto end;
1332
1333 result = asus_hotk_add_fs(device);
1334 if (result)
1335 goto end;
1336
1337 /*
1338 * We install the handler, it will receive the hotk in parameter, so, we
1339 * could add other data to the hotk struct
1340 */
1341 status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1342 asus_hotk_notify, hotk);
1343 if (ACPI_FAILURE(status))
1344 printk(KERN_ERR " Error installing notify handler\n");
1345
1346 /* For laptops without GPLV: init the hotk->brightness value */
1347 if ((!hotk->methods->brightness_get)
1348 && (!hotk->methods->brightness_status)
1349 && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
1350 status =
1351 acpi_evaluate_object(NULL, hotk->methods->brightness_down,
1352 NULL, NULL);
1353 if (ACPI_FAILURE(status))
1354 printk(KERN_WARNING " Error changing brightness\n");
1355 else {
1356 status =
1357 acpi_evaluate_object(NULL,
1358 hotk->methods->brightness_up,
1359 NULL, NULL);
1360 if (ACPI_FAILURE(status))
1361 printk(KERN_WARNING " Strange, error changing"
1362 " brightness\n");
1363 }
1364 }
1365
1366 asus_hotk_found = 1;
1367
1368 /* LED display is off by default */
1369 hotk->ledd_status = 0xFFF;
1370
1371end:
1372 if (result)
1373 kfree(hotk);
1374
1375 return result;
1376}
1377
1378static int asus_hotk_remove(struct acpi_device *device, int type)
1379{
1380 acpi_status status = 0;
1381
1382 if (!device || !acpi_driver_data(device))
1383 return -EINVAL;
1384
1385 status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
1386 asus_hotk_notify);
1387 if (ACPI_FAILURE(status))
1388 printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
1389
1390 asus_hotk_remove_fs(device);
1391
1392 kfree(hotk);
1393
1394 return 0;
1395}
1396
1397static struct backlight_ops asus_backlight_data = {
1398 .get_brightness = read_brightness,
1399 .update_status = set_brightness_status,
1400};
1401
1402static void asus_acpi_exit(void)
1403{
1404 if (asus_backlight_device)
1405 backlight_device_unregister(asus_backlight_device);
1406
1407 acpi_bus_unregister_driver(&asus_hotk_driver);
1408 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1409
1410 return;
1411}
1412
1413static int __init asus_acpi_init(void)
1414{
1415 int result;
1416
1417 if (acpi_disabled)
1418 return -ENODEV;
1419
1420 asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
1421 if (!asus_proc_dir) {
1422 printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
1423 return -ENODEV;
1424 }
1425 asus_proc_dir->owner = THIS_MODULE;
1426
1427 result = acpi_bus_register_driver(&asus_hotk_driver);
1428 if (result < 0) {
1429 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1430 return result;
1431 }
1432
1433 /*
1434 * This is a bit of a kludge. We only want this module loaded
1435 * for ASUS systems, but there's currently no way to probe the
1436 * ACPI namespace for ASUS HIDs. So we just return failure if
1437 * we didn't find one, which will cause the module to be
1438 * unloaded.
1439 */
1440 if (!asus_hotk_found) {
1441 acpi_bus_unregister_driver(&asus_hotk_driver);
1442 remove_proc_entry(PROC_ASUS, acpi_root_dir);
1443 return -ENODEV;
1444 }
1445
1446 asus_backlight_device = backlight_device_register("asus", NULL, NULL,
1447 &asus_backlight_data);
1448 if (IS_ERR(asus_backlight_device)) {
1449 printk(KERN_ERR "Could not register asus backlight device\n");
1450 asus_backlight_device = NULL;
1451 asus_acpi_exit();
1452 return -ENODEV;
1453 }
1454 asus_backlight_device->props.max_brightness = 15;
1455
1456 return 0;
1457}
1458
1459module_init(asus_acpi_init);
1460module_exit(asus_acpi_exit);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
new file mode 100644
index 000000000000..11003bba10d3
--- /dev/null
+++ b/drivers/platform/x86/compal-laptop.c
@@ -0,0 +1,406 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
5
6 based on MSI driver
7
8 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301, USA.
24 */
25
26/*
27 * comapl-laptop.c - Compal laptop support.
28 *
29 * This driver exports a few files in /sys/devices/platform/compal-laptop/:
30 *
31 * wlan - wlan subsystem state: contains 0 or 1 (rw)
32 *
33 * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
34 *
35 * raw - raw value taken from embedded controller register (ro)
36 *
37 * In addition to these platform device attributes the driver
38 * registers itself in the Linux backlight control subsystem and is
39 * available to userspace under /sys/class/backlight/compal-laptop/.
40 *
41 * This driver might work on other laptops produced by Compal. If you
42 * want to try it you can pass force=1 as argument to the module which
43 * will force it to load even when the DMI data doesn't identify the
44 * laptop as FL9x.
45 */
46
47#include <linux/module.h>
48#include <linux/kernel.h>
49#include <linux/init.h>
50#include <linux/acpi.h>
51#include <linux/dmi.h>
52#include <linux/backlight.h>
53#include <linux/platform_device.h>
54#include <linux/autoconf.h>
55
56#define COMPAL_DRIVER_VERSION "0.2.6"
57
58#define COMPAL_LCD_LEVEL_MAX 8
59
60#define COMPAL_EC_COMMAND_WIRELESS 0xBB
61#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
62
63#define KILLSWITCH_MASK 0x10
64#define WLAN_MASK 0x01
65#define BT_MASK 0x02
66
67static int force;
68module_param(force, bool, 0);
69MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
70
71/* Hardware access */
72
73static int set_lcd_level(int level)
74{
75 if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
76 return -EINVAL;
77
78 ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
79
80 return 0;
81}
82
83static int get_lcd_level(void)
84{
85 u8 result;
86
87 ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
88
89 return (int) result;
90}
91
92static int set_wlan_state(int state)
93{
94 u8 result, value;
95
96 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
97
98 if ((result & KILLSWITCH_MASK) == 0)
99 return -EINVAL;
100 else {
101 if (state)
102 value = (u8) (result | WLAN_MASK);
103 else
104 value = (u8) (result & ~WLAN_MASK);
105 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
106 }
107
108 return 0;
109}
110
111static int set_bluetooth_state(int state)
112{
113 u8 result, value;
114
115 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
116
117 if ((result & KILLSWITCH_MASK) == 0)
118 return -EINVAL;
119 else {
120 if (state)
121 value = (u8) (result | BT_MASK);
122 else
123 value = (u8) (result & ~BT_MASK);
124 ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
125 }
126
127 return 0;
128}
129
130static int get_wireless_state(int *wlan, int *bluetooth)
131{
132 u8 result;
133
134 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
135
136 if (wlan) {
137 if ((result & KILLSWITCH_MASK) == 0)
138 *wlan = 0;
139 else
140 *wlan = result & WLAN_MASK;
141 }
142
143 if (bluetooth) {
144 if ((result & KILLSWITCH_MASK) == 0)
145 *bluetooth = 0;
146 else
147 *bluetooth = (result & BT_MASK) >> 1;
148 }
149
150 return 0;
151}
152
153/* Backlight device stuff */
154
155static int bl_get_brightness(struct backlight_device *b)
156{
157 return get_lcd_level();
158}
159
160
161static int bl_update_status(struct backlight_device *b)
162{
163 return set_lcd_level(b->props.brightness);
164}
165
166static struct backlight_ops compalbl_ops = {
167 .get_brightness = bl_get_brightness,
168 .update_status = bl_update_status,
169};
170
171static struct backlight_device *compalbl_device;
172
173/* Platform device */
174
175static ssize_t show_wlan(struct device *dev,
176 struct device_attribute *attr, char *buf)
177{
178 int ret, enabled;
179
180 ret = get_wireless_state(&enabled, NULL);
181 if (ret < 0)
182 return ret;
183
184 return sprintf(buf, "%i\n", enabled);
185}
186
187static ssize_t show_raw(struct device *dev,
188 struct device_attribute *attr, char *buf)
189{
190 u8 result;
191
192 ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
193
194 return sprintf(buf, "%i\n", result);
195}
196
197static ssize_t show_bluetooth(struct device *dev,
198 struct device_attribute *attr, char *buf)
199{
200 int ret, enabled;
201
202 ret = get_wireless_state(NULL, &enabled);
203 if (ret < 0)
204 return ret;
205
206 return sprintf(buf, "%i\n", enabled);
207}
208
209static ssize_t store_wlan_state(struct device *dev,
210 struct device_attribute *attr, const char *buf, size_t count)
211{
212 int state, ret;
213
214 if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
215 return -EINVAL;
216
217 ret = set_wlan_state(state);
218 if (ret < 0)
219 return ret;
220
221 return count;
222}
223
224static ssize_t store_bluetooth_state(struct device *dev,
225 struct device_attribute *attr, const char *buf, size_t count)
226{
227 int state, ret;
228
229 if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
230 return -EINVAL;
231
232 ret = set_bluetooth_state(state);
233 if (ret < 0)
234 return ret;
235
236 return count;
237}
238
239static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
240static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
241static DEVICE_ATTR(raw, 0444, show_raw, NULL);
242
243static struct attribute *compal_attributes[] = {
244 &dev_attr_bluetooth.attr,
245 &dev_attr_wlan.attr,
246 &dev_attr_raw.attr,
247 NULL
248};
249
250static struct attribute_group compal_attribute_group = {
251 .attrs = compal_attributes
252};
253
254static struct platform_driver compal_driver = {
255 .driver = {
256 .name = "compal-laptop",
257 .owner = THIS_MODULE,
258 }
259};
260
261static struct platform_device *compal_device;
262
263/* Initialization */
264
265static int dmi_check_cb(const struct dmi_system_id *id)
266{
267 printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
268 id->ident);
269
270 return 0;
271}
272
273static struct dmi_system_id __initdata compal_dmi_table[] = {
274 {
275 .ident = "FL90/IFL90",
276 .matches = {
277 DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
278 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
279 },
280 .callback = dmi_check_cb
281 },
282 {
283 .ident = "FL90/IFL90",
284 .matches = {
285 DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
286 DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
287 },
288 .callback = dmi_check_cb
289 },
290 {
291 .ident = "FL91/IFL91",
292 .matches = {
293 DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
294 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
295 },
296 .callback = dmi_check_cb
297 },
298 {
299 .ident = "FL92/JFL92",
300 .matches = {
301 DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
302 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
303 },
304 .callback = dmi_check_cb
305 },
306 {
307 .ident = "FT00/IFT00",
308 .matches = {
309 DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
310 DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
311 },
312 .callback = dmi_check_cb
313 },
314 { }
315};
316
317static int __init compal_init(void)
318{
319 int ret;
320
321 if (acpi_disabled)
322 return -ENODEV;
323
324 if (!force && !dmi_check_system(compal_dmi_table))
325 return -ENODEV;
326
327 /* Register backlight stuff */
328
329 if (!acpi_video_backlight_support()) {
330 compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
331 &compalbl_ops);
332 if (IS_ERR(compalbl_device))
333 return PTR_ERR(compalbl_device);
334
335 compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
336 }
337
338 ret = platform_driver_register(&compal_driver);
339 if (ret)
340 goto fail_backlight;
341
342 /* Register platform stuff */
343
344 compal_device = platform_device_alloc("compal-laptop", -1);
345 if (!compal_device) {
346 ret = -ENOMEM;
347 goto fail_platform_driver;
348 }
349
350 ret = platform_device_add(compal_device);
351 if (ret)
352 goto fail_platform_device1;
353
354 ret = sysfs_create_group(&compal_device->dev.kobj,
355 &compal_attribute_group);
356 if (ret)
357 goto fail_platform_device2;
358
359 printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
360 " successfully loaded.\n");
361
362 return 0;
363
364fail_platform_device2:
365
366 platform_device_del(compal_device);
367
368fail_platform_device1:
369
370 platform_device_put(compal_device);
371
372fail_platform_driver:
373
374 platform_driver_unregister(&compal_driver);
375
376fail_backlight:
377
378 backlight_device_unregister(compalbl_device);
379
380 return ret;
381}
382
383static void __exit compal_cleanup(void)
384{
385
386 sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
387 platform_device_unregister(compal_device);
388 platform_driver_unregister(&compal_driver);
389 backlight_device_unregister(compalbl_device);
390
391 printk(KERN_INFO "compal-laptop: driver unloaded.\n");
392}
393
394module_init(compal_init);
395module_exit(compal_cleanup);
396
397MODULE_AUTHOR("Cezary Jackiewicz");
398MODULE_DESCRIPTION("Compal Laptop Support");
399MODULE_VERSION(COMPAL_DRIVER_VERSION);
400MODULE_LICENSE("GPL");
401
402MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
403MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
404MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
405MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
406MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
new file mode 100644
index 000000000000..02fe2b8b8939
--- /dev/null
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -0,0 +1,872 @@
1/*
2 * eepc-laptop.c - Asus Eee PC extras
3 *
4 * Based on asus_acpi.c as patched for the Eee PC by Asus:
5 * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
6 * Based on eee.c from eeepc-linux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/types.h>
23#include <linux/platform_device.h>
24#include <linux/backlight.h>
25#include <linux/fb.h>
26#include <linux/hwmon.h>
27#include <linux/hwmon-sysfs.h>
28#include <acpi/acpi_drivers.h>
29#include <acpi/acpi_bus.h>
30#include <linux/uaccess.h>
31#include <linux/input.h>
32#include <linux/rfkill.h>
33
34#define EEEPC_LAPTOP_VERSION "0.1"
35
36#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
37#define EEEPC_HOTK_FILE "eeepc"
38#define EEEPC_HOTK_CLASS "hotkey"
39#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
40#define EEEPC_HOTK_HID "ASUS010"
41
42#define EEEPC_LOG EEEPC_HOTK_FILE ": "
43#define EEEPC_ERR KERN_ERR EEEPC_LOG
44#define EEEPC_WARNING KERN_WARNING EEEPC_LOG
45#define EEEPC_NOTICE KERN_NOTICE EEEPC_LOG
46#define EEEPC_INFO KERN_INFO EEEPC_LOG
47
48/*
49 * Definitions for Asus EeePC
50 */
51#define NOTIFY_WLAN_ON 0x10
52#define NOTIFY_BRN_MIN 0x20
53#define NOTIFY_BRN_MAX 0x2f
54
55enum {
56 DISABLE_ASL_WLAN = 0x0001,
57 DISABLE_ASL_BLUETOOTH = 0x0002,
58 DISABLE_ASL_IRDA = 0x0004,
59 DISABLE_ASL_CAMERA = 0x0008,
60 DISABLE_ASL_TV = 0x0010,
61 DISABLE_ASL_GPS = 0x0020,
62 DISABLE_ASL_DISPLAYSWITCH = 0x0040,
63 DISABLE_ASL_MODEM = 0x0080,
64 DISABLE_ASL_CARDREADER = 0x0100
65};
66
67enum {
68 CM_ASL_WLAN = 0,
69 CM_ASL_BLUETOOTH,
70 CM_ASL_IRDA,
71 CM_ASL_1394,
72 CM_ASL_CAMERA,
73 CM_ASL_TV,
74 CM_ASL_GPS,
75 CM_ASL_DVDROM,
76 CM_ASL_DISPLAYSWITCH,
77 CM_ASL_PANELBRIGHT,
78 CM_ASL_BIOSFLASH,
79 CM_ASL_ACPIFLASH,
80 CM_ASL_CPUFV,
81 CM_ASL_CPUTEMPERATURE,
82 CM_ASL_FANCPU,
83 CM_ASL_FANCHASSIS,
84 CM_ASL_USBPORT1,
85 CM_ASL_USBPORT2,
86 CM_ASL_USBPORT3,
87 CM_ASL_MODEM,
88 CM_ASL_CARDREADER,
89 CM_ASL_LID
90};
91
92static const char *cm_getv[] = {
93 "WLDG", NULL, NULL, NULL,
94 "CAMG", NULL, NULL, NULL,
95 NULL, "PBLG", NULL, NULL,
96 "CFVG", NULL, NULL, NULL,
97 "USBG", NULL, NULL, "MODG",
98 "CRDG", "LIDG"
99};
100
101static const char *cm_setv[] = {
102 "WLDS", NULL, NULL, NULL,
103 "CAMS", NULL, NULL, NULL,
104 "SDSP", "PBLS", "HDPS", NULL,
105 "CFVS", NULL, NULL, NULL,
106 "USBG", NULL, NULL, "MODS",
107 "CRDS", NULL
108};
109
110#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
111
112#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
113#define EEEPC_EC_SC02 0x63
114#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
115#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
116#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
117#define EEEPC_EC_SFB3 0xD3
118
119/*
120 * This is the main structure, we can use it to store useful information
121 * about the hotk device
122 */
123struct eeepc_hotk {
124 struct acpi_device *device; /* the device we are in */
125 acpi_handle handle; /* the handle of the hotk device */
126 u32 cm_supported; /* the control methods supported
127 by this BIOS */
128 uint init_flag; /* Init flags */
129 u16 event_count[128]; /* count for each event */
130 struct input_dev *inputdev;
131 u16 *keycode_map;
132 struct rfkill *eeepc_wlan_rfkill;
133 struct rfkill *eeepc_bluetooth_rfkill;
134};
135
136/* The actual device the driver binds to */
137static struct eeepc_hotk *ehotk;
138
139/* Platform device/driver */
140static struct platform_driver platform_driver = {
141 .driver = {
142 .name = EEEPC_HOTK_FILE,
143 .owner = THIS_MODULE,
144 }
145};
146
147static struct platform_device *platform_device;
148
149struct key_entry {
150 char type;
151 u8 code;
152 u16 keycode;
153};
154
155enum { KE_KEY, KE_END };
156
157static struct key_entry eeepc_keymap[] = {
158 /* Sleep already handled via generic ACPI code */
159 {KE_KEY, 0x10, KEY_WLAN },
160 {KE_KEY, 0x12, KEY_PROG1 },
161 {KE_KEY, 0x13, KEY_MUTE },
162 {KE_KEY, 0x14, KEY_VOLUMEDOWN },
163 {KE_KEY, 0x15, KEY_VOLUMEUP },
164 {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
165 {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
166 {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
167 {KE_END, 0},
168};
169
170/*
171 * The hotkey driver declaration
172 */
173static int eeepc_hotk_add(struct acpi_device *device);
174static int eeepc_hotk_remove(struct acpi_device *device, int type);
175
176static const struct acpi_device_id eeepc_device_ids[] = {
177 {EEEPC_HOTK_HID, 0},
178 {"", 0},
179};
180MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
181
182static struct acpi_driver eeepc_hotk_driver = {
183 .name = EEEPC_HOTK_NAME,
184 .class = EEEPC_HOTK_CLASS,
185 .ids = eeepc_device_ids,
186 .ops = {
187 .add = eeepc_hotk_add,
188 .remove = eeepc_hotk_remove,
189 },
190};
191
192/* The backlight device /sys/class/backlight */
193static struct backlight_device *eeepc_backlight_device;
194
195/* The hwmon device */
196static struct device *eeepc_hwmon_device;
197
198/*
199 * The backlight class declaration
200 */
201static int read_brightness(struct backlight_device *bd);
202static int update_bl_status(struct backlight_device *bd);
203static struct backlight_ops eeepcbl_ops = {
204 .get_brightness = read_brightness,
205 .update_status = update_bl_status,
206};
207
208MODULE_AUTHOR("Corentin Chary, Eric Cooper");
209MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
210MODULE_LICENSE("GPL");
211
212/*
213 * ACPI Helpers
214 */
215static int write_acpi_int(acpi_handle handle, const char *method, int val,
216 struct acpi_buffer *output)
217{
218 struct acpi_object_list params;
219 union acpi_object in_obj;
220 acpi_status status;
221
222 params.count = 1;
223 params.pointer = &in_obj;
224 in_obj.type = ACPI_TYPE_INTEGER;
225 in_obj.integer.value = val;
226
227 status = acpi_evaluate_object(handle, (char *)method, &params, output);
228 return (status == AE_OK ? 0 : -1);
229}
230
231static int read_acpi_int(acpi_handle handle, const char *method, int *val)
232{
233 acpi_status status;
234 unsigned long long result;
235
236 status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
237 if (ACPI_FAILURE(status)) {
238 *val = -1;
239 return -1;
240 } else {
241 *val = result;
242 return 0;
243 }
244}
245
246static int set_acpi(int cm, int value)
247{
248 if (ehotk->cm_supported & (0x1 << cm)) {
249 const char *method = cm_setv[cm];
250 if (method == NULL)
251 return -ENODEV;
252 if (write_acpi_int(ehotk->handle, method, value, NULL))
253 printk(EEEPC_WARNING "Error writing %s\n", method);
254 }
255 return 0;
256}
257
258static int get_acpi(int cm)
259{
260 int value = -1;
261 if ((ehotk->cm_supported & (0x1 << cm))) {
262 const char *method = cm_getv[cm];
263 if (method == NULL)
264 return -ENODEV;
265 if (read_acpi_int(ehotk->handle, method, &value))
266 printk(EEEPC_WARNING "Error reading %s\n", method);
267 }
268 return value;
269}
270
271/*
272 * Backlight
273 */
274static int read_brightness(struct backlight_device *bd)
275{
276 return get_acpi(CM_ASL_PANELBRIGHT);
277}
278
279static int set_brightness(struct backlight_device *bd, int value)
280{
281 value = max(0, min(15, value));
282 return set_acpi(CM_ASL_PANELBRIGHT, value);
283}
284
285static int update_bl_status(struct backlight_device *bd)
286{
287 return set_brightness(bd, bd->props.brightness);
288}
289
290/*
291 * Rfkill helpers
292 */
293
294static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
295{
296 if (state == RFKILL_STATE_SOFT_BLOCKED)
297 return set_acpi(CM_ASL_WLAN, 0);
298 else
299 return set_acpi(CM_ASL_WLAN, 1);
300}
301
302static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
303{
304 if (get_acpi(CM_ASL_WLAN) == 1)
305 *state = RFKILL_STATE_UNBLOCKED;
306 else
307 *state = RFKILL_STATE_SOFT_BLOCKED;
308 return 0;
309}
310
311static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
312{
313 if (state == RFKILL_STATE_SOFT_BLOCKED)
314 return set_acpi(CM_ASL_BLUETOOTH, 0);
315 else
316 return set_acpi(CM_ASL_BLUETOOTH, 1);
317}
318
319static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
320{
321 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
322 *state = RFKILL_STATE_UNBLOCKED;
323 else
324 *state = RFKILL_STATE_SOFT_BLOCKED;
325 return 0;
326}
327
328/*
329 * Sys helpers
330 */
331static int parse_arg(const char *buf, unsigned long count, int *val)
332{
333 if (!count)
334 return 0;
335 if (sscanf(buf, "%i", val) != 1)
336 return -EINVAL;
337 return count;
338}
339
340static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
341{
342 int rv, value;
343
344 rv = parse_arg(buf, count, &value);
345 if (rv > 0)
346 set_acpi(cm, value);
347 return rv;
348}
349
350static ssize_t show_sys_acpi(int cm, char *buf)
351{
352 return sprintf(buf, "%d\n", get_acpi(cm));
353}
354
355#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
356 static ssize_t show_##_name(struct device *dev, \
357 struct device_attribute *attr, \
358 char *buf) \
359 { \
360 return show_sys_acpi(_cm, buf); \
361 } \
362 static ssize_t store_##_name(struct device *dev, \
363 struct device_attribute *attr, \
364 const char *buf, size_t count) \
365 { \
366 return store_sys_acpi(_cm, buf, count); \
367 } \
368 static struct device_attribute dev_attr_##_name = { \
369 .attr = { \
370 .name = __stringify(_name), \
371 .mode = 0644 }, \
372 .show = show_##_name, \
373 .store = store_##_name, \
374 }
375
376EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
377EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
378EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
379
380static struct attribute *platform_attributes[] = {
381 &dev_attr_camera.attr,
382 &dev_attr_cardr.attr,
383 &dev_attr_disp.attr,
384 NULL
385};
386
387static struct attribute_group platform_attribute_group = {
388 .attrs = platform_attributes
389};
390
391/*
392 * Hotkey functions
393 */
394static struct key_entry *eepc_get_entry_by_scancode(int code)
395{
396 struct key_entry *key;
397
398 for (key = eeepc_keymap; key->type != KE_END; key++)
399 if (code == key->code)
400 return key;
401
402 return NULL;
403}
404
405static struct key_entry *eepc_get_entry_by_keycode(int code)
406{
407 struct key_entry *key;
408
409 for (key = eeepc_keymap; key->type != KE_END; key++)
410 if (code == key->keycode && key->type == KE_KEY)
411 return key;
412
413 return NULL;
414}
415
416static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
417{
418 struct key_entry *key = eepc_get_entry_by_scancode(scancode);
419
420 if (key && key->type == KE_KEY) {
421 *keycode = key->keycode;
422 return 0;
423 }
424
425 return -EINVAL;
426}
427
428static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
429{
430 struct key_entry *key;
431 int old_keycode;
432
433 if (keycode < 0 || keycode > KEY_MAX)
434 return -EINVAL;
435
436 key = eepc_get_entry_by_scancode(scancode);
437 if (key && key->type == KE_KEY) {
438 old_keycode = key->keycode;
439 key->keycode = keycode;
440 set_bit(keycode, dev->keybit);
441 if (!eepc_get_entry_by_keycode(old_keycode))
442 clear_bit(old_keycode, dev->keybit);
443 return 0;
444 }
445
446 return -EINVAL;
447}
448
449static int eeepc_hotk_check(void)
450{
451 const struct key_entry *key;
452 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
453 int result;
454
455 result = acpi_bus_get_status(ehotk->device);
456 if (result)
457 return result;
458 if (ehotk->device->status.present) {
459 if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
460 &buffer)) {
461 printk(EEEPC_ERR "Hotkey initialization failed\n");
462 return -ENODEV;
463 } else {
464 printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
465 ehotk->init_flag);
466 }
467 /* get control methods supported */
468 if (read_acpi_int(ehotk->handle, "CMSG"
469 , &ehotk->cm_supported)) {
470 printk(EEEPC_ERR
471 "Get control methods supported failed\n");
472 return -ENODEV;
473 } else {
474 printk(EEEPC_INFO
475 "Get control methods supported: 0x%x\n",
476 ehotk->cm_supported);
477 }
478 ehotk->inputdev = input_allocate_device();
479 if (!ehotk->inputdev) {
480 printk(EEEPC_INFO "Unable to allocate input device\n");
481 return 0;
482 }
483 ehotk->inputdev->name = "Asus EeePC extra buttons";
484 ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
485 ehotk->inputdev->id.bustype = BUS_HOST;
486 ehotk->inputdev->getkeycode = eeepc_getkeycode;
487 ehotk->inputdev->setkeycode = eeepc_setkeycode;
488
489 for (key = eeepc_keymap; key->type != KE_END; key++) {
490 switch (key->type) {
491 case KE_KEY:
492 set_bit(EV_KEY, ehotk->inputdev->evbit);
493 set_bit(key->keycode, ehotk->inputdev->keybit);
494 break;
495 }
496 }
497 result = input_register_device(ehotk->inputdev);
498 if (result) {
499 printk(EEEPC_INFO "Unable to register input device\n");
500 input_free_device(ehotk->inputdev);
501 return 0;
502 }
503 } else {
504 printk(EEEPC_ERR "Hotkey device not present, aborting\n");
505 return -EINVAL;
506 }
507 return 0;
508}
509
510static void notify_brn(void)
511{
512 struct backlight_device *bd = eeepc_backlight_device;
513 bd->props.brightness = read_brightness(bd);
514}
515
516static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
517{
518 static struct key_entry *key;
519 if (!ehotk)
520 return;
521 if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
522 notify_brn();
523 acpi_bus_generate_proc_event(ehotk->device, event,
524 ehotk->event_count[event % 128]++);
525 if (ehotk->inputdev) {
526 key = eepc_get_entry_by_scancode(event);
527 if (key) {
528 switch (key->type) {
529 case KE_KEY:
530 input_report_key(ehotk->inputdev, key->keycode,
531 1);
532 input_sync(ehotk->inputdev);
533 input_report_key(ehotk->inputdev, key->keycode,
534 0);
535 input_sync(ehotk->inputdev);
536 break;
537 }
538 }
539 }
540}
541
542static int eeepc_hotk_add(struct acpi_device *device)
543{
544 acpi_status status = AE_OK;
545 int result;
546
547 if (!device)
548 return -EINVAL;
549 printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
550 ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
551 if (!ehotk)
552 return -ENOMEM;
553 ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
554 ehotk->handle = device->handle;
555 strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
556 strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
557 device->driver_data = ehotk;
558 ehotk->device = device;
559 result = eeepc_hotk_check();
560 if (result)
561 goto end;
562 status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
563 eeepc_hotk_notify, ehotk);
564 if (ACPI_FAILURE(status))
565 printk(EEEPC_ERR "Error installing notify handler\n");
566
567 if (get_acpi(CM_ASL_WLAN) != -1) {
568 ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
569 RFKILL_TYPE_WLAN);
570
571 if (!ehotk->eeepc_wlan_rfkill)
572 goto end;
573
574 ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
575 ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
576 ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
577 if (get_acpi(CM_ASL_WLAN) == 1)
578 ehotk->eeepc_wlan_rfkill->state =
579 RFKILL_STATE_UNBLOCKED;
580 else
581 ehotk->eeepc_wlan_rfkill->state =
582 RFKILL_STATE_SOFT_BLOCKED;
583 rfkill_register(ehotk->eeepc_wlan_rfkill);
584 }
585
586 if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
587 ehotk->eeepc_bluetooth_rfkill =
588 rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
589
590 if (!ehotk->eeepc_bluetooth_rfkill)
591 goto end;
592
593 ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
594 ehotk->eeepc_bluetooth_rfkill->toggle_radio =
595 eeepc_bluetooth_rfkill_set;
596 ehotk->eeepc_bluetooth_rfkill->get_state =
597 eeepc_bluetooth_rfkill_state;
598 if (get_acpi(CM_ASL_BLUETOOTH) == 1)
599 ehotk->eeepc_bluetooth_rfkill->state =
600 RFKILL_STATE_UNBLOCKED;
601 else
602 ehotk->eeepc_bluetooth_rfkill->state =
603 RFKILL_STATE_SOFT_BLOCKED;
604 rfkill_register(ehotk->eeepc_bluetooth_rfkill);
605 }
606
607 end:
608 if (result) {
609 kfree(ehotk);
610 ehotk = NULL;
611 }
612 return result;
613}
614
615static int eeepc_hotk_remove(struct acpi_device *device, int type)
616{
617 acpi_status status = 0;
618
619 if (!device || !acpi_driver_data(device))
620 return -EINVAL;
621 status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
622 eeepc_hotk_notify);
623 if (ACPI_FAILURE(status))
624 printk(EEEPC_ERR "Error removing notify handler\n");
625 kfree(ehotk);
626 return 0;
627}
628
629/*
630 * Hwmon
631 */
632static int eeepc_get_fan_pwm(void)
633{
634 int value = 0;
635
636 read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
637 value = value * 255 / 100;
638 return (value);
639}
640
641static void eeepc_set_fan_pwm(int value)
642{
643 value = SENSORS_LIMIT(value, 0, 255);
644 value = value * 100 / 255;
645 ec_write(EEEPC_EC_SC02, value);
646}
647
648static int eeepc_get_fan_rpm(void)
649{
650 int high = 0;
651 int low = 0;
652
653 read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
654 read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
655 return (high << 8 | low);
656}
657
658static int eeepc_get_fan_ctrl(void)
659{
660 int value = 0;
661
662 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
663 return ((value & 0x02 ? 1 : 0));
664}
665
666static void eeepc_set_fan_ctrl(int manual)
667{
668 int value = 0;
669
670 read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
671 if (manual)
672 value |= 0x02;
673 else
674 value &= ~0x02;
675 ec_write(EEEPC_EC_SFB3, value);
676}
677
678static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
679{
680 int rv, value;
681
682 rv = parse_arg(buf, count, &value);
683 if (rv > 0)
684 set(value);
685 return rv;
686}
687
688static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
689{
690 return sprintf(buf, "%d\n", get());
691}
692
693#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get) \
694 static ssize_t show_##_name(struct device *dev, \
695 struct device_attribute *attr, \
696 char *buf) \
697 { \
698 return show_sys_hwmon(_set, buf); \
699 } \
700 static ssize_t store_##_name(struct device *dev, \
701 struct device_attribute *attr, \
702 const char *buf, size_t count) \
703 { \
704 return store_sys_hwmon(_get, buf, count); \
705 } \
706 static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
707
708EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
709EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
710 eeepc_get_fan_pwm, eeepc_set_fan_pwm);
711EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
712 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
713
714static ssize_t
715show_name(struct device *dev, struct device_attribute *attr, char *buf)
716{
717 return sprintf(buf, "eeepc\n");
718}
719static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
720
721static struct attribute *hwmon_attributes[] = {
722 &sensor_dev_attr_pwm1.dev_attr.attr,
723 &sensor_dev_attr_fan1_input.dev_attr.attr,
724 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
725 &sensor_dev_attr_name.dev_attr.attr,
726 NULL
727};
728
729static struct attribute_group hwmon_attribute_group = {
730 .attrs = hwmon_attributes
731};
732
733/*
734 * exit/init
735 */
736static void eeepc_backlight_exit(void)
737{
738 if (eeepc_backlight_device)
739 backlight_device_unregister(eeepc_backlight_device);
740 if (ehotk->inputdev)
741 input_unregister_device(ehotk->inputdev);
742 if (ehotk->eeepc_wlan_rfkill)
743 rfkill_unregister(ehotk->eeepc_wlan_rfkill);
744 if (ehotk->eeepc_bluetooth_rfkill)
745 rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
746 eeepc_backlight_device = NULL;
747}
748
749static void eeepc_hwmon_exit(void)
750{
751 struct device *hwmon;
752
753 hwmon = eeepc_hwmon_device;
754 if (!hwmon)
755 return ;
756 sysfs_remove_group(&hwmon->kobj,
757 &hwmon_attribute_group);
758 hwmon_device_unregister(hwmon);
759 eeepc_hwmon_device = NULL;
760}
761
762static void __exit eeepc_laptop_exit(void)
763{
764 eeepc_backlight_exit();
765 eeepc_hwmon_exit();
766 acpi_bus_unregister_driver(&eeepc_hotk_driver);
767 sysfs_remove_group(&platform_device->dev.kobj,
768 &platform_attribute_group);
769 platform_device_unregister(platform_device);
770 platform_driver_unregister(&platform_driver);
771}
772
773static int eeepc_backlight_init(struct device *dev)
774{
775 struct backlight_device *bd;
776
777 bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
778 NULL, &eeepcbl_ops);
779 if (IS_ERR(bd)) {
780 printk(EEEPC_ERR
781 "Could not register eeepc backlight device\n");
782 eeepc_backlight_device = NULL;
783 return PTR_ERR(bd);
784 }
785 eeepc_backlight_device = bd;
786 bd->props.max_brightness = 15;
787 bd->props.brightness = read_brightness(NULL);
788 bd->props.power = FB_BLANK_UNBLANK;
789 backlight_update_status(bd);
790 return 0;
791}
792
793static int eeepc_hwmon_init(struct device *dev)
794{
795 struct device *hwmon;
796 int result;
797
798 hwmon = hwmon_device_register(dev);
799 if (IS_ERR(hwmon)) {
800 printk(EEEPC_ERR
801 "Could not register eeepc hwmon device\n");
802 eeepc_hwmon_device = NULL;
803 return PTR_ERR(hwmon);
804 }
805 eeepc_hwmon_device = hwmon;
806 result = sysfs_create_group(&hwmon->kobj,
807 &hwmon_attribute_group);
808 if (result)
809 eeepc_hwmon_exit();
810 return result;
811}
812
813static int __init eeepc_laptop_init(void)
814{
815 struct device *dev;
816 int result;
817
818 if (acpi_disabled)
819 return -ENODEV;
820 result = acpi_bus_register_driver(&eeepc_hotk_driver);
821 if (result < 0)
822 return result;
823 if (!ehotk) {
824 acpi_bus_unregister_driver(&eeepc_hotk_driver);
825 return -ENODEV;
826 }
827 dev = acpi_get_physical_device(ehotk->device->handle);
828
829 if (!acpi_video_backlight_support()) {
830 result = eeepc_backlight_init(dev);
831 if (result)
832 goto fail_backlight;
833 } else
834 printk(EEEPC_INFO "Backlight controlled by ACPI video "
835 "driver\n");
836
837 result = eeepc_hwmon_init(dev);
838 if (result)
839 goto fail_hwmon;
840 /* Register platform stuff */
841 result = platform_driver_register(&platform_driver);
842 if (result)
843 goto fail_platform_driver;
844 platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
845 if (!platform_device) {
846 result = -ENOMEM;
847 goto fail_platform_device1;
848 }
849 result = platform_device_add(platform_device);
850 if (result)
851 goto fail_platform_device2;
852 result = sysfs_create_group(&platform_device->dev.kobj,
853 &platform_attribute_group);
854 if (result)
855 goto fail_sysfs;
856 return 0;
857fail_sysfs:
858 platform_device_del(platform_device);
859fail_platform_device2:
860 platform_device_put(platform_device);
861fail_platform_device1:
862 platform_driver_unregister(&platform_driver);
863fail_platform_driver:
864 eeepc_hwmon_exit();
865fail_hwmon:
866 eeepc_backlight_exit();
867fail_backlight:
868 return result;
869}
870
871module_init(eeepc_laptop_init);
872module_exit(eeepc_laptop_exit);
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
new file mode 100644
index 000000000000..65dc41540c62
--- /dev/null
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -0,0 +1,1293 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
5 Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
6 Copyright (C) 2008 Tony Vroon <tony@linx.net>
7 Based on earlier work:
8 Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
9 Adrian Yee <brewt-fujitsu@brewt.org>
10
11 Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
12 by its respective authors.
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27 02110-1301, USA.
28 */
29
30/*
31 * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
32 * features made available on a range of Fujitsu laptops including the
33 * P2xxx/P5xxx/S6xxx/S7xxx series.
34 *
35 * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
36 * others may be added at a later date.
37 *
38 * lcd_level - Screen brightness: contains a single integer in the
39 * range 0..7. (rw)
40 *
41 * In addition to these platform device attributes the driver
42 * registers itself in the Linux backlight control subsystem and is
43 * available to userspace under /sys/class/backlight/fujitsu-laptop/.
44 *
45 * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
46 * also supported by this driver.
47 *
48 * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
49 * P8010. It should work on most P-series and S-series Lifebooks, but
50 * YMMV.
51 *
52 * The module parameter use_alt_lcd_levels switches between different ACPI
53 * brightness controls which are used by different Fujitsu laptops. In most
54 * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
55 * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
56 *
57 */
58
59#include <linux/module.h>
60#include <linux/kernel.h>
61#include <linux/init.h>
62#include <linux/acpi.h>
63#include <linux/dmi.h>
64#include <linux/backlight.h>
65#include <linux/input.h>
66#include <linux/kfifo.h>
67#include <linux/video_output.h>
68#include <linux/platform_device.h>
69#ifdef CONFIG_LEDS_CLASS
70#include <linux/leds.h>
71#endif
72
73#define FUJITSU_DRIVER_VERSION "0.5.0"
74
75#define FUJITSU_LCD_N_LEVELS 8
76
77#define ACPI_FUJITSU_CLASS "fujitsu"
78#define ACPI_FUJITSU_HID "FUJ02B1"
79#define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver"
80#define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1"
81#define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3"
82#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
83#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
84
85#define ACPI_FUJITSU_NOTIFY_CODE1 0x80
86
87#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86
88#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87
89
90/* FUNC interface - command values */
91#define FUNC_RFKILL 0x1000
92#define FUNC_LEDS 0x1001
93#define FUNC_BUTTONS 0x1002
94#define FUNC_BACKLIGHT 0x1004
95
96/* FUNC interface - responses */
97#define UNSUPPORTED_CMD 0x80000000
98
99#ifdef CONFIG_LEDS_CLASS
100/* FUNC interface - LED control */
101#define FUNC_LED_OFF 0x1
102#define FUNC_LED_ON 0x30001
103#define KEYBOARD_LAMPS 0x100
104#define LOGOLAMP_POWERON 0x2000
105#define LOGOLAMP_ALWAYS 0x4000
106#endif
107
108/* Hotkey details */
109#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */
110#define KEY2_CODE 0x411
111#define KEY3_CODE 0x412
112#define KEY4_CODE 0x413
113
114#define MAX_HOTKEY_RINGBUFFER_SIZE 100
115#define RINGBUFFERSIZE 40
116
117/* Debugging */
118#define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": "
119#define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG
120#define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG
121#define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG
122#define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG
123
124#define FUJLAPTOP_DBG_ALL 0xffff
125#define FUJLAPTOP_DBG_ERROR 0x0001
126#define FUJLAPTOP_DBG_WARN 0x0002
127#define FUJLAPTOP_DBG_INFO 0x0004
128#define FUJLAPTOP_DBG_TRACE 0x0008
129
130#define dbg_printk(a_dbg_level, format, arg...) \
131 do { if (dbg_level & a_dbg_level) \
132 printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
133 } while (0)
134#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
135#define vdbg_printk(a_dbg_level, format, arg...) \
136 dbg_printk(a_dbg_level, format, ## arg)
137#else
138#define vdbg_printk(a_dbg_level, format, arg...)
139#endif
140
141/* Device controlling the backlight and associated keys */
142struct fujitsu_t {
143 acpi_handle acpi_handle;
144 struct acpi_device *dev;
145 struct input_dev *input;
146 char phys[32];
147 struct backlight_device *bl_device;
148 struct platform_device *pf_device;
149 int keycode1, keycode2, keycode3, keycode4;
150
151 unsigned int max_brightness;
152 unsigned int brightness_changed;
153 unsigned int brightness_level;
154};
155
156static struct fujitsu_t *fujitsu;
157static int use_alt_lcd_levels = -1;
158static int disable_brightness_adjust = -1;
159
160/* Device used to access other hotkeys on the laptop */
161struct fujitsu_hotkey_t {
162 acpi_handle acpi_handle;
163 struct acpi_device *dev;
164 struct input_dev *input;
165 char phys[32];
166 struct platform_device *pf_device;
167 struct kfifo *fifo;
168 spinlock_t fifo_lock;
169 int rfkill_state;
170 int logolamp_registered;
171 int kblamps_registered;
172};
173
174static struct fujitsu_hotkey_t *fujitsu_hotkey;
175
176static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
177 void *data);
178
179#ifdef CONFIG_LEDS_CLASS
180static enum led_brightness logolamp_get(struct led_classdev *cdev);
181static void logolamp_set(struct led_classdev *cdev,
182 enum led_brightness brightness);
183
184struct led_classdev logolamp_led = {
185 .name = "fujitsu::logolamp",
186 .brightness_get = logolamp_get,
187 .brightness_set = logolamp_set
188};
189
190static enum led_brightness kblamps_get(struct led_classdev *cdev);
191static void kblamps_set(struct led_classdev *cdev,
192 enum led_brightness brightness);
193
194struct led_classdev kblamps_led = {
195 .name = "fujitsu::kblamps",
196 .brightness_get = kblamps_get,
197 .brightness_set = kblamps_set
198};
199#endif
200
201#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
202static u32 dbg_level = 0x03;
203#endif
204
205static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
206
207/* Fujitsu ACPI interface function */
208
209static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
210{
211 acpi_status status = AE_OK;
212 union acpi_object params[4] = {
213 { .type = ACPI_TYPE_INTEGER },
214 { .type = ACPI_TYPE_INTEGER },
215 { .type = ACPI_TYPE_INTEGER },
216 { .type = ACPI_TYPE_INTEGER }
217 };
218 struct acpi_object_list arg_list = { 4, &params[0] };
219 struct acpi_buffer output;
220 union acpi_object out_obj;
221 acpi_handle handle = NULL;
222
223 status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle);
224 if (ACPI_FAILURE(status)) {
225 vdbg_printk(FUJLAPTOP_DBG_ERROR,
226 "FUNC interface is not present\n");
227 return -ENODEV;
228 }
229
230 params[0].integer.value = cmd;
231 params[1].integer.value = arg0;
232 params[2].integer.value = arg1;
233 params[3].integer.value = arg2;
234
235 output.length = sizeof(out_obj);
236 output.pointer = &out_obj;
237
238 status = acpi_evaluate_object(handle, NULL, &arg_list, &output);
239 if (ACPI_FAILURE(status)) {
240 vdbg_printk(FUJLAPTOP_DBG_WARN,
241 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
242 cmd, arg0, arg1, arg2);
243 return -ENODEV;
244 }
245
246 if (out_obj.type != ACPI_TYPE_INTEGER) {
247 vdbg_printk(FUJLAPTOP_DBG_WARN,
248 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not "
249 "return an integer\n",
250 cmd, arg0, arg1, arg2);
251 return -ENODEV;
252 }
253
254 vdbg_printk(FUJLAPTOP_DBG_TRACE,
255 "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
256 cmd, arg0, arg1, arg2, (int)out_obj.integer.value);
257 return out_obj.integer.value;
258}
259
260#ifdef CONFIG_LEDS_CLASS
261/* LED class callbacks */
262
263static void logolamp_set(struct led_classdev *cdev,
264 enum led_brightness brightness)
265{
266 if (brightness >= LED_FULL) {
267 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
268 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON);
269 } else if (brightness >= LED_HALF) {
270 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON);
271 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF);
272 } else {
273 call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF);
274 }
275}
276
277static void kblamps_set(struct led_classdev *cdev,
278 enum led_brightness brightness)
279{
280 if (brightness >= LED_FULL)
281 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON);
282 else
283 call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF);
284}
285
286static enum led_brightness logolamp_get(struct led_classdev *cdev)
287{
288 enum led_brightness brightness = LED_OFF;
289 int poweron, always;
290
291 poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
292 if (poweron == FUNC_LED_ON) {
293 brightness = LED_HALF;
294 always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
295 if (always == FUNC_LED_ON)
296 brightness = LED_FULL;
297 }
298 return brightness;
299}
300
301static enum led_brightness kblamps_get(struct led_classdev *cdev)
302{
303 enum led_brightness brightness = LED_OFF;
304
305 if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
306 brightness = LED_FULL;
307
308 return brightness;
309}
310#endif
311
312/* Hardware access for LCD brightness control */
313
314static int set_lcd_level(int level)
315{
316 acpi_status status = AE_OK;
317 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
318 struct acpi_object_list arg_list = { 1, &arg0 };
319 acpi_handle handle = NULL;
320
321 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
322 level);
323
324 if (level < 0 || level >= fujitsu->max_brightness)
325 return -EINVAL;
326
327 if (!fujitsu)
328 return -EINVAL;
329
330 status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
331 if (ACPI_FAILURE(status)) {
332 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
333 return -ENODEV;
334 }
335
336 arg0.integer.value = level;
337
338 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
339 if (ACPI_FAILURE(status))
340 return -ENODEV;
341
342 return 0;
343}
344
345static int set_lcd_level_alt(int level)
346{
347 acpi_status status = AE_OK;
348 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
349 struct acpi_object_list arg_list = { 1, &arg0 };
350 acpi_handle handle = NULL;
351
352 vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
353 level);
354
355 if (level < 0 || level >= fujitsu->max_brightness)
356 return -EINVAL;
357
358 if (!fujitsu)
359 return -EINVAL;
360
361 status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
362 if (ACPI_FAILURE(status)) {
363 vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
364 return -ENODEV;
365 }
366
367 arg0.integer.value = level;
368
369 status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
370 if (ACPI_FAILURE(status))
371 return -ENODEV;
372
373 return 0;
374}
375
376static int get_lcd_level(void)
377{
378 unsigned long long state = 0;
379 acpi_status status = AE_OK;
380
381 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
382
383 status =
384 acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
385 if (status < 0)
386 return status;
387
388 fujitsu->brightness_level = state & 0x0fffffff;
389
390 if (state & 0x80000000)
391 fujitsu->brightness_changed = 1;
392 else
393 fujitsu->brightness_changed = 0;
394
395 return fujitsu->brightness_level;
396}
397
398static int get_max_brightness(void)
399{
400 unsigned long long state = 0;
401 acpi_status status = AE_OK;
402
403 vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
404
405 status =
406 acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
407 if (status < 0)
408 return status;
409
410 fujitsu->max_brightness = state;
411
412 return fujitsu->max_brightness;
413}
414
415/* Backlight device stuff */
416
417static int bl_get_brightness(struct backlight_device *b)
418{
419 return get_lcd_level();
420}
421
422static int bl_update_status(struct backlight_device *b)
423{
424 int ret;
425 if (b->props.power == 4)
426 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
427 else
428 ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
429 if (ret != 0)
430 vdbg_printk(FUJLAPTOP_DBG_ERROR,
431 "Unable to adjust backlight power, error code %i\n",
432 ret);
433
434 if (use_alt_lcd_levels)
435 ret = set_lcd_level_alt(b->props.brightness);
436 else
437 ret = set_lcd_level(b->props.brightness);
438 if (ret != 0)
439 vdbg_printk(FUJLAPTOP_DBG_ERROR,
440 "Unable to adjust LCD brightness, error code %i\n",
441 ret);
442 return ret;
443}
444
445static struct backlight_ops fujitsubl_ops = {
446 .get_brightness = bl_get_brightness,
447 .update_status = bl_update_status,
448};
449
450/* Platform LCD brightness device */
451
452static ssize_t
453show_max_brightness(struct device *dev,
454 struct device_attribute *attr, char *buf)
455{
456
457 int ret;
458
459 ret = get_max_brightness();
460 if (ret < 0)
461 return ret;
462
463 return sprintf(buf, "%i\n", ret);
464}
465
466static ssize_t
467show_brightness_changed(struct device *dev,
468 struct device_attribute *attr, char *buf)
469{
470
471 int ret;
472
473 ret = fujitsu->brightness_changed;
474 if (ret < 0)
475 return ret;
476
477 return sprintf(buf, "%i\n", ret);
478}
479
480static ssize_t show_lcd_level(struct device *dev,
481 struct device_attribute *attr, char *buf)
482{
483
484 int ret;
485
486 ret = get_lcd_level();
487 if (ret < 0)
488 return ret;
489
490 return sprintf(buf, "%i\n", ret);
491}
492
493static ssize_t store_lcd_level(struct device *dev,
494 struct device_attribute *attr, const char *buf,
495 size_t count)
496{
497
498 int level, ret;
499
500 if (sscanf(buf, "%i", &level) != 1
501 || (level < 0 || level >= fujitsu->max_brightness))
502 return -EINVAL;
503
504 if (use_alt_lcd_levels)
505 ret = set_lcd_level_alt(level);
506 else
507 ret = set_lcd_level(level);
508 if (ret < 0)
509 return ret;
510
511 ret = get_lcd_level();
512 if (ret < 0)
513 return ret;
514
515 return count;
516}
517
518static ssize_t
519ignore_store(struct device *dev,
520 struct device_attribute *attr, const char *buf, size_t count)
521{
522 return count;
523}
524
525static ssize_t
526show_lid_state(struct device *dev,
527 struct device_attribute *attr, char *buf)
528{
529 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
530 return sprintf(buf, "unknown\n");
531 if (fujitsu_hotkey->rfkill_state & 0x100)
532 return sprintf(buf, "open\n");
533 else
534 return sprintf(buf, "closed\n");
535}
536
537static ssize_t
538show_dock_state(struct device *dev,
539 struct device_attribute *attr, char *buf)
540{
541 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
542 return sprintf(buf, "unknown\n");
543 if (fujitsu_hotkey->rfkill_state & 0x200)
544 return sprintf(buf, "docked\n");
545 else
546 return sprintf(buf, "undocked\n");
547}
548
549static ssize_t
550show_radios_state(struct device *dev,
551 struct device_attribute *attr, char *buf)
552{
553 if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD)
554 return sprintf(buf, "unknown\n");
555 if (fujitsu_hotkey->rfkill_state & 0x20)
556 return sprintf(buf, "on\n");
557 else
558 return sprintf(buf, "killed\n");
559}
560
561static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
562static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
563 ignore_store);
564static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
565static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store);
566static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store);
567static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store);
568
569static struct attribute *fujitsupf_attributes[] = {
570 &dev_attr_brightness_changed.attr,
571 &dev_attr_max_brightness.attr,
572 &dev_attr_lcd_level.attr,
573 &dev_attr_lid.attr,
574 &dev_attr_dock.attr,
575 &dev_attr_radios.attr,
576 NULL
577};
578
579static struct attribute_group fujitsupf_attribute_group = {
580 .attrs = fujitsupf_attributes
581};
582
583static struct platform_driver fujitsupf_driver = {
584 .driver = {
585 .name = "fujitsu-laptop",
586 .owner = THIS_MODULE,
587 }
588};
589
590static void dmi_check_cb_common(const struct dmi_system_id *id)
591{
592 acpi_handle handle;
593 printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n",
594 id->ident);
595 if (use_alt_lcd_levels == -1) {
596 if (ACPI_SUCCESS(acpi_get_handle(NULL,
597 "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle)))
598 use_alt_lcd_levels = 1;
599 else
600 use_alt_lcd_levels = 0;
601 vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as "
602 "%i\n", use_alt_lcd_levels);
603 }
604}
605
606static int dmi_check_cb_s6410(const struct dmi_system_id *id)
607{
608 dmi_check_cb_common(id);
609 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */
610 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */
611 return 0;
612}
613
614static int dmi_check_cb_s6420(const struct dmi_system_id *id)
615{
616 dmi_check_cb_common(id);
617 fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */
618 fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */
619 return 0;
620}
621
622static int dmi_check_cb_p8010(const struct dmi_system_id *id)
623{
624 dmi_check_cb_common(id);
625 fujitsu->keycode1 = KEY_HELP; /* "Support" */
626 fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */
627 fujitsu->keycode4 = KEY_WWW; /* "Internet" */
628 return 0;
629}
630
631static struct dmi_system_id fujitsu_dmi_table[] = {
632 {
633 .ident = "Fujitsu Siemens S6410",
634 .matches = {
635 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
636 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
637 },
638 .callback = dmi_check_cb_s6410},
639 {
640 .ident = "Fujitsu Siemens S6420",
641 .matches = {
642 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
643 DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
644 },
645 .callback = dmi_check_cb_s6420},
646 {
647 .ident = "Fujitsu LifeBook P8010",
648 .matches = {
649 DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
650 DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
651 },
652 .callback = dmi_check_cb_p8010},
653 {}
654};
655
656/* ACPI device for LCD brightness control */
657
658static int acpi_fujitsu_add(struct acpi_device *device)
659{
660 acpi_status status;
661 acpi_handle handle;
662 int result = 0;
663 int state = 0;
664 struct input_dev *input;
665 int error;
666
667 if (!device)
668 return -EINVAL;
669
670 fujitsu->acpi_handle = device->handle;
671 sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
672 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
673 device->driver_data = fujitsu;
674
675 status = acpi_install_notify_handler(device->handle,
676 ACPI_DEVICE_NOTIFY,
677 acpi_fujitsu_notify, fujitsu);
678
679 if (ACPI_FAILURE(status)) {
680 printk(KERN_ERR "Error installing notify handler\n");
681 error = -ENODEV;
682 goto err_stop;
683 }
684
685 fujitsu->input = input = input_allocate_device();
686 if (!input) {
687 error = -ENOMEM;
688 goto err_uninstall_notify;
689 }
690
691 snprintf(fujitsu->phys, sizeof(fujitsu->phys),
692 "%s/video/input0", acpi_device_hid(device));
693
694 input->name = acpi_device_name(device);
695 input->phys = fujitsu->phys;
696 input->id.bustype = BUS_HOST;
697 input->id.product = 0x06;
698 input->dev.parent = &device->dev;
699 input->evbit[0] = BIT(EV_KEY);
700 set_bit(KEY_BRIGHTNESSUP, input->keybit);
701 set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
702 set_bit(KEY_UNKNOWN, input->keybit);
703
704 error = input_register_device(input);
705 if (error)
706 goto err_free_input_dev;
707
708 result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
709 if (result) {
710 printk(KERN_ERR "Error reading power state\n");
711 goto end;
712 }
713
714 printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
715 acpi_device_name(device), acpi_device_bid(device),
716 !device->power.state ? "on" : "off");
717
718 fujitsu->dev = device;
719
720 if (ACPI_SUCCESS
721 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
722 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
723 if (ACPI_FAILURE
724 (acpi_evaluate_object
725 (device->handle, METHOD_NAME__INI, NULL, NULL)))
726 printk(KERN_ERR "_INI Method failed\n");
727 }
728
729 /* do config (detect defaults) */
730 use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
731 disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
732 vdbg_printk(FUJLAPTOP_DBG_INFO,
733 "config: [alt interface: %d], [adjust disable: %d]\n",
734 use_alt_lcd_levels, disable_brightness_adjust);
735
736 if (get_max_brightness() <= 0)
737 fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
738 get_lcd_level();
739
740 return result;
741
742end:
743err_free_input_dev:
744 input_free_device(input);
745err_uninstall_notify:
746 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
747 acpi_fujitsu_notify);
748err_stop:
749
750 return result;
751}
752
753static int acpi_fujitsu_remove(struct acpi_device *device, int type)
754{
755 acpi_status status;
756 struct fujitsu_t *fujitsu = NULL;
757
758 if (!device || !acpi_driver_data(device))
759 return -EINVAL;
760
761 fujitsu = acpi_driver_data(device);
762
763 status = acpi_remove_notify_handler(fujitsu->acpi_handle,
764 ACPI_DEVICE_NOTIFY,
765 acpi_fujitsu_notify);
766
767 if (!device || !acpi_driver_data(device))
768 return -EINVAL;
769
770 fujitsu->acpi_handle = NULL;
771
772 return 0;
773}
774
775/* Brightness notify */
776
777static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
778{
779 struct input_dev *input;
780 int keycode;
781 int oldb, newb;
782
783 input = fujitsu->input;
784
785 switch (event) {
786 case ACPI_FUJITSU_NOTIFY_CODE1:
787 keycode = 0;
788 oldb = fujitsu->brightness_level;
789 get_lcd_level();
790 newb = fujitsu->brightness_level;
791
792 vdbg_printk(FUJLAPTOP_DBG_TRACE,
793 "brightness button event [%i -> %i (%i)]\n",
794 oldb, newb, fujitsu->brightness_changed);
795
796 if (oldb < newb) {
797 if (disable_brightness_adjust != 1) {
798 if (use_alt_lcd_levels)
799 set_lcd_level_alt(newb);
800 else
801 set_lcd_level(newb);
802 }
803 acpi_bus_generate_proc_event(fujitsu->dev,
804 ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
805 keycode = KEY_BRIGHTNESSUP;
806 } else if (oldb > newb) {
807 if (disable_brightness_adjust != 1) {
808 if (use_alt_lcd_levels)
809 set_lcd_level_alt(newb);
810 else
811 set_lcd_level(newb);
812 }
813 acpi_bus_generate_proc_event(fujitsu->dev,
814 ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
815 keycode = KEY_BRIGHTNESSDOWN;
816 }
817 break;
818 default:
819 keycode = KEY_UNKNOWN;
820 vdbg_printk(FUJLAPTOP_DBG_WARN,
821 "unsupported event [0x%x]\n", event);
822 break;
823 }
824
825 if (keycode != 0) {
826 input_report_key(input, keycode, 1);
827 input_sync(input);
828 input_report_key(input, keycode, 0);
829 input_sync(input);
830 }
831
832 return;
833}
834
835/* ACPI device for hotkey handling */
836
837static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
838{
839 acpi_status status;
840 acpi_handle handle;
841 int result = 0;
842 int state = 0;
843 struct input_dev *input;
844 int error;
845 int i;
846
847 if (!device)
848 return -EINVAL;
849
850 fujitsu_hotkey->acpi_handle = device->handle;
851 sprintf(acpi_device_name(device), "%s",
852 ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
853 sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
854 device->driver_data = fujitsu_hotkey;
855
856 status = acpi_install_notify_handler(device->handle,
857 ACPI_DEVICE_NOTIFY,
858 acpi_fujitsu_hotkey_notify,
859 fujitsu_hotkey);
860
861 if (ACPI_FAILURE(status)) {
862 printk(KERN_ERR "Error installing notify handler\n");
863 error = -ENODEV;
864 goto err_stop;
865 }
866
867 /* kfifo */
868 spin_lock_init(&fujitsu_hotkey->fifo_lock);
869 fujitsu_hotkey->fifo =
870 kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
871 &fujitsu_hotkey->fifo_lock);
872 if (IS_ERR(fujitsu_hotkey->fifo)) {
873 printk(KERN_ERR "kfifo_alloc failed\n");
874 error = PTR_ERR(fujitsu_hotkey->fifo);
875 goto err_stop;
876 }
877
878 fujitsu_hotkey->input = input = input_allocate_device();
879 if (!input) {
880 error = -ENOMEM;
881 goto err_uninstall_notify;
882 }
883
884 snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
885 "%s/video/input0", acpi_device_hid(device));
886
887 input->name = acpi_device_name(device);
888 input->phys = fujitsu_hotkey->phys;
889 input->id.bustype = BUS_HOST;
890 input->id.product = 0x06;
891 input->dev.parent = &device->dev;
892
893 set_bit(EV_KEY, input->evbit);
894 set_bit(fujitsu->keycode1, input->keybit);
895 set_bit(fujitsu->keycode2, input->keybit);
896 set_bit(fujitsu->keycode3, input->keybit);
897 set_bit(fujitsu->keycode4, input->keybit);
898 set_bit(KEY_UNKNOWN, input->keybit);
899
900 error = input_register_device(input);
901 if (error)
902 goto err_free_input_dev;
903
904 result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
905 if (result) {
906 printk(KERN_ERR "Error reading power state\n");
907 goto end;
908 }
909
910 printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
911 acpi_device_name(device), acpi_device_bid(device),
912 !device->power.state ? "on" : "off");
913
914 fujitsu_hotkey->dev = device;
915
916 if (ACPI_SUCCESS
917 (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
918 vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
919 if (ACPI_FAILURE
920 (acpi_evaluate_object
921 (device->handle, METHOD_NAME__INI, NULL, NULL)))
922 printk(KERN_ERR "_INI Method failed\n");
923 }
924
925 i = 0;
926 while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
927 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
928 ; /* No action, result is discarded */
929 vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
930
931 fujitsu_hotkey->rfkill_state =
932 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
933
934 /* Suspect this is a keymap of the application panel, print it */
935 printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n",
936 call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
937
938 #ifdef CONFIG_LEDS_CLASS
939 if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
940 result = led_classdev_register(&fujitsu->pf_device->dev,
941 &logolamp_led);
942 if (result == 0) {
943 fujitsu_hotkey->logolamp_registered = 1;
944 } else {
945 printk(KERN_ERR "fujitsu-laptop: Could not register "
946 "LED handler for logo lamp, error %i\n", result);
947 }
948 }
949
950 if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
951 (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
952 result = led_classdev_register(&fujitsu->pf_device->dev,
953 &kblamps_led);
954 if (result == 0) {
955 fujitsu_hotkey->kblamps_registered = 1;
956 } else {
957 printk(KERN_ERR "fujitsu-laptop: Could not register "
958 "LED handler for keyboard lamps, error %i\n", result);
959 }
960 }
961 #endif
962
963 return result;
964
965end:
966err_free_input_dev:
967 input_free_device(input);
968err_uninstall_notify:
969 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
970 acpi_fujitsu_hotkey_notify);
971 kfifo_free(fujitsu_hotkey->fifo);
972err_stop:
973
974 return result;
975}
976
977static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
978{
979 acpi_status status;
980 struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
981
982 if (!device || !acpi_driver_data(device))
983 return -EINVAL;
984
985 fujitsu_hotkey = acpi_driver_data(device);
986
987 status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
988 ACPI_DEVICE_NOTIFY,
989 acpi_fujitsu_hotkey_notify);
990
991 fujitsu_hotkey->acpi_handle = NULL;
992
993 kfifo_free(fujitsu_hotkey->fifo);
994
995 return 0;
996}
997
998static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
999 void *data)
1000{
1001 struct input_dev *input;
1002 int keycode, keycode_r;
1003 unsigned int irb = 1;
1004 int i, status;
1005
1006 input = fujitsu_hotkey->input;
1007
1008 fujitsu_hotkey->rfkill_state =
1009 call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0);
1010
1011 switch (event) {
1012 case ACPI_FUJITSU_NOTIFY_CODE1:
1013 i = 0;
1014 while ((irb =
1015 call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0
1016 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
1017 switch (irb & 0x4ff) {
1018 case KEY1_CODE:
1019 keycode = fujitsu->keycode1;
1020 break;
1021 case KEY2_CODE:
1022 keycode = fujitsu->keycode2;
1023 break;
1024 case KEY3_CODE:
1025 keycode = fujitsu->keycode3;
1026 break;
1027 case KEY4_CODE:
1028 keycode = fujitsu->keycode4;
1029 break;
1030 case 0:
1031 keycode = 0;
1032 break;
1033 default:
1034 vdbg_printk(FUJLAPTOP_DBG_WARN,
1035 "Unknown GIRB result [%x]\n", irb);
1036 keycode = -1;
1037 break;
1038 }
1039 if (keycode > 0) {
1040 vdbg_printk(FUJLAPTOP_DBG_TRACE,
1041 "Push keycode into ringbuffer [%d]\n",
1042 keycode);
1043 status = kfifo_put(fujitsu_hotkey->fifo,
1044 (unsigned char *)&keycode,
1045 sizeof(keycode));
1046 if (status != sizeof(keycode)) {
1047 vdbg_printk(FUJLAPTOP_DBG_WARN,
1048 "Could not push keycode [0x%x]\n",
1049 keycode);
1050 } else {
1051 input_report_key(input, keycode, 1);
1052 input_sync(input);
1053 }
1054 } else if (keycode == 0) {
1055 while ((status =
1056 kfifo_get
1057 (fujitsu_hotkey->fifo, (unsigned char *)
1058 &keycode_r,
1059 sizeof
1060 (keycode_r))) == sizeof(keycode_r)) {
1061 input_report_key(input, keycode_r, 0);
1062 input_sync(input);
1063 vdbg_printk(FUJLAPTOP_DBG_TRACE,
1064 "Pop keycode from ringbuffer [%d]\n",
1065 keycode_r);
1066 }
1067 }
1068 }
1069
1070 break;
1071 default:
1072 keycode = KEY_UNKNOWN;
1073 vdbg_printk(FUJLAPTOP_DBG_WARN,
1074 "Unsupported event [0x%x]\n", event);
1075 input_report_key(input, keycode, 1);
1076 input_sync(input);
1077 input_report_key(input, keycode, 0);
1078 input_sync(input);
1079 break;
1080 }
1081
1082 return;
1083}
1084
1085/* Initialization */
1086
1087static const struct acpi_device_id fujitsu_device_ids[] = {
1088 {ACPI_FUJITSU_HID, 0},
1089 {"", 0},
1090};
1091
1092static struct acpi_driver acpi_fujitsu_driver = {
1093 .name = ACPI_FUJITSU_DRIVER_NAME,
1094 .class = ACPI_FUJITSU_CLASS,
1095 .ids = fujitsu_device_ids,
1096 .ops = {
1097 .add = acpi_fujitsu_add,
1098 .remove = acpi_fujitsu_remove,
1099 },
1100};
1101
1102static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
1103 {ACPI_FUJITSU_HOTKEY_HID, 0},
1104 {"", 0},
1105};
1106
1107static struct acpi_driver acpi_fujitsu_hotkey_driver = {
1108 .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
1109 .class = ACPI_FUJITSU_CLASS,
1110 .ids = fujitsu_hotkey_device_ids,
1111 .ops = {
1112 .add = acpi_fujitsu_hotkey_add,
1113 .remove = acpi_fujitsu_hotkey_remove,
1114 },
1115};
1116
1117static int __init fujitsu_init(void)
1118{
1119 int ret, result, max_brightness;
1120
1121 if (acpi_disabled)
1122 return -ENODEV;
1123
1124 fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
1125 if (!fujitsu)
1126 return -ENOMEM;
1127 memset(fujitsu, 0, sizeof(struct fujitsu_t));
1128 fujitsu->keycode1 = KEY_PROG1;
1129 fujitsu->keycode2 = KEY_PROG2;
1130 fujitsu->keycode3 = KEY_PROG3;
1131 fujitsu->keycode4 = KEY_PROG4;
1132 dmi_check_system(fujitsu_dmi_table);
1133
1134 result = acpi_bus_register_driver(&acpi_fujitsu_driver);
1135 if (result < 0) {
1136 ret = -ENODEV;
1137 goto fail_acpi;
1138 }
1139
1140 /* Register platform stuff */
1141
1142 fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
1143 if (!fujitsu->pf_device) {
1144 ret = -ENOMEM;
1145 goto fail_platform_driver;
1146 }
1147
1148 ret = platform_device_add(fujitsu->pf_device);
1149 if (ret)
1150 goto fail_platform_device1;
1151
1152 ret =
1153 sysfs_create_group(&fujitsu->pf_device->dev.kobj,
1154 &fujitsupf_attribute_group);
1155 if (ret)
1156 goto fail_platform_device2;
1157
1158 /* Register backlight stuff */
1159
1160 if (!acpi_video_backlight_support()) {
1161 fujitsu->bl_device =
1162 backlight_device_register("fujitsu-laptop", NULL, NULL,
1163 &fujitsubl_ops);
1164 if (IS_ERR(fujitsu->bl_device))
1165 return PTR_ERR(fujitsu->bl_device);
1166 max_brightness = fujitsu->max_brightness;
1167 fujitsu->bl_device->props.max_brightness = max_brightness - 1;
1168 fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
1169 }
1170
1171 ret = platform_driver_register(&fujitsupf_driver);
1172 if (ret)
1173 goto fail_backlight;
1174
1175 /* Register hotkey driver */
1176
1177 fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
1178 if (!fujitsu_hotkey) {
1179 ret = -ENOMEM;
1180 goto fail_hotkey;
1181 }
1182 memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
1183
1184 result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
1185 if (result < 0) {
1186 ret = -ENODEV;
1187 goto fail_hotkey1;
1188 }
1189
1190 /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
1191
1192 if (!acpi_video_backlight_support()) {
1193 if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
1194 fujitsu->bl_device->props.power = 4;
1195 else
1196 fujitsu->bl_device->props.power = 0;
1197 }
1198
1199 printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
1200 " successfully loaded.\n");
1201
1202 return 0;
1203
1204fail_hotkey1:
1205
1206 kfree(fujitsu_hotkey);
1207
1208fail_hotkey:
1209
1210 platform_driver_unregister(&fujitsupf_driver);
1211
1212fail_backlight:
1213
1214 if (fujitsu->bl_device)
1215 backlight_device_unregister(fujitsu->bl_device);
1216
1217fail_platform_device2:
1218
1219 platform_device_del(fujitsu->pf_device);
1220
1221fail_platform_device1:
1222
1223 platform_device_put(fujitsu->pf_device);
1224
1225fail_platform_driver:
1226
1227 acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1228
1229fail_acpi:
1230
1231 kfree(fujitsu);
1232
1233 return ret;
1234}
1235
1236static void __exit fujitsu_cleanup(void)
1237{
1238 #ifdef CONFIG_LEDS_CLASS
1239 if (fujitsu_hotkey->logolamp_registered != 0)
1240 led_classdev_unregister(&logolamp_led);
1241
1242 if (fujitsu_hotkey->kblamps_registered != 0)
1243 led_classdev_unregister(&kblamps_led);
1244 #endif
1245
1246 sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
1247 &fujitsupf_attribute_group);
1248 platform_device_unregister(fujitsu->pf_device);
1249 platform_driver_unregister(&fujitsupf_driver);
1250 if (fujitsu->bl_device)
1251 backlight_device_unregister(fujitsu->bl_device);
1252
1253 acpi_bus_unregister_driver(&acpi_fujitsu_driver);
1254
1255 kfree(fujitsu);
1256
1257 acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
1258
1259 kfree(fujitsu_hotkey);
1260
1261 printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
1262}
1263
1264module_init(fujitsu_init);
1265module_exit(fujitsu_cleanup);
1266
1267module_param(use_alt_lcd_levels, uint, 0644);
1268MODULE_PARM_DESC(use_alt_lcd_levels,
1269 "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
1270module_param(disable_brightness_adjust, uint, 0644);
1271MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
1272#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
1273module_param_named(debug, dbg_level, uint, 0644);
1274MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
1275#endif
1276
1277MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
1278MODULE_DESCRIPTION("Fujitsu laptop extras support");
1279MODULE_VERSION(FUJITSU_DRIVER_VERSION);
1280MODULE_LICENSE("GPL");
1281
1282MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
1283MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*");
1284MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
1285
1286static struct pnp_device_id pnp_ids[] = {
1287 {.id = "FUJ02bf"},
1288 {.id = "FUJ02B1"},
1289 {.id = "FUJ02E3"},
1290 {.id = ""}
1291};
1292
1293MODULE_DEVICE_TABLE(pnp, pnp_ids);
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
new file mode 100644
index 000000000000..4b7c24c519c3
--- /dev/null
+++ b/drivers/platform/x86/hp-wmi.c
@@ -0,0 +1,512 @@
1/*
2 * HP WMI hotkeys
3 *
4 * Copyright (C) 2008 Red Hat <mjg@redhat.com>
5 *
6 * Portions based on wistron_btns.c:
7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/input.h>
31#include <acpi/acpi_drivers.h>
32#include <linux/platform_device.h>
33#include <linux/acpi.h>
34#include <linux/rfkill.h>
35#include <linux/string.h>
36
37MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
38MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
39MODULE_LICENSE("GPL");
40
41MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
42MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
43
44#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
45#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
46
47#define HPWMI_DISPLAY_QUERY 0x1
48#define HPWMI_HDDTEMP_QUERY 0x2
49#define HPWMI_ALS_QUERY 0x3
50#define HPWMI_DOCK_QUERY 0x4
51#define HPWMI_WIRELESS_QUERY 0x5
52#define HPWMI_HOTKEY_QUERY 0xc
53
54static int __init hp_wmi_bios_setup(struct platform_device *device);
55static int __exit hp_wmi_bios_remove(struct platform_device *device);
56
57struct bios_args {
58 u32 signature;
59 u32 command;
60 u32 commandtype;
61 u32 datasize;
62 u32 data;
63};
64
65struct bios_return {
66 u32 sigpass;
67 u32 return_code;
68 u32 value;
69};
70
71struct key_entry {
72 char type; /* See KE_* below */
73 u16 code;
74 u16 keycode;
75};
76
77enum { KE_KEY, KE_SW, KE_END };
78
79static struct key_entry hp_wmi_keymap[] = {
80 {KE_SW, 0x01, SW_DOCK},
81 {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
82 {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
83 {KE_KEY, 0x20e6, KEY_PROG1},
84 {KE_KEY, 0x2142, KEY_MEDIA},
85 {KE_KEY, 0x213b, KEY_INFO},
86 {KE_KEY, 0x231b, KEY_HELP},
87 {KE_END, 0}
88};
89
90static struct input_dev *hp_wmi_input_dev;
91static struct platform_device *hp_wmi_platform_dev;
92
93static struct rfkill *wifi_rfkill;
94static struct rfkill *bluetooth_rfkill;
95static struct rfkill *wwan_rfkill;
96
97static struct platform_driver hp_wmi_driver = {
98 .driver = {
99 .name = "hp-wmi",
100 .owner = THIS_MODULE,
101 },
102 .probe = hp_wmi_bios_setup,
103 .remove = hp_wmi_bios_remove,
104};
105
106static int hp_wmi_perform_query(int query, int write, int value)
107{
108 struct bios_return bios_return;
109 acpi_status status;
110 union acpi_object *obj;
111 struct bios_args args = {
112 .signature = 0x55434553,
113 .command = write ? 0x2 : 0x1,
114 .commandtype = query,
115 .datasize = write ? 0x4 : 0,
116 .data = value,
117 };
118 struct acpi_buffer input = { sizeof(struct bios_args), &args };
119 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
120
121 status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
122
123 obj = output.pointer;
124
125 if (!obj || obj->type != ACPI_TYPE_BUFFER)
126 return -EINVAL;
127
128 bios_return = *((struct bios_return *)obj->buffer.pointer);
129 if (bios_return.return_code > 0)
130 return bios_return.return_code * -1;
131 else
132 return bios_return.value;
133}
134
135static int hp_wmi_display_state(void)
136{
137 return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
138}
139
140static int hp_wmi_hddtemp_state(void)
141{
142 return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
143}
144
145static int hp_wmi_als_state(void)
146{
147 return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
148}
149
150static int hp_wmi_dock_state(void)
151{
152 return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
153}
154
155static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
156{
157 if (state)
158 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
159 else
160 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
161}
162
163static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
164{
165 if (state)
166 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
167 else
168 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
169}
170
171static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
172{
173 if (state)
174 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
175 else
176 return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
177}
178
179static int hp_wmi_wifi_state(void)
180{
181 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
182
183 if (wireless & 0x100)
184 return RFKILL_STATE_UNBLOCKED;
185 else
186 return RFKILL_STATE_SOFT_BLOCKED;
187}
188
189static int hp_wmi_bluetooth_state(void)
190{
191 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
192
193 if (wireless & 0x10000)
194 return RFKILL_STATE_UNBLOCKED;
195 else
196 return RFKILL_STATE_SOFT_BLOCKED;
197}
198
199static int hp_wmi_wwan_state(void)
200{
201 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
202
203 if (wireless & 0x1000000)
204 return RFKILL_STATE_UNBLOCKED;
205 else
206 return RFKILL_STATE_SOFT_BLOCKED;
207}
208
209static ssize_t show_display(struct device *dev, struct device_attribute *attr,
210 char *buf)
211{
212 int value = hp_wmi_display_state();
213 if (value < 0)
214 return -EINVAL;
215 return sprintf(buf, "%d\n", value);
216}
217
218static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
219 char *buf)
220{
221 int value = hp_wmi_hddtemp_state();
222 if (value < 0)
223 return -EINVAL;
224 return sprintf(buf, "%d\n", value);
225}
226
227static ssize_t show_als(struct device *dev, struct device_attribute *attr,
228 char *buf)
229{
230 int value = hp_wmi_als_state();
231 if (value < 0)
232 return -EINVAL;
233 return sprintf(buf, "%d\n", value);
234}
235
236static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
237 char *buf)
238{
239 int value = hp_wmi_dock_state();
240 if (value < 0)
241 return -EINVAL;
242 return sprintf(buf, "%d\n", value);
243}
244
245static ssize_t set_als(struct device *dev, struct device_attribute *attr,
246 const char *buf, size_t count)
247{
248 u32 tmp = simple_strtoul(buf, NULL, 10);
249 hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
250 return count;
251}
252
253static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
254static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
255static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
256static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
257
258static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
259{
260 struct key_entry *key;
261
262 for (key = hp_wmi_keymap; key->type != KE_END; key++)
263 if (code == key->code)
264 return key;
265
266 return NULL;
267}
268
269static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
270{
271 struct key_entry *key;
272
273 for (key = hp_wmi_keymap; key->type != KE_END; key++)
274 if (key->type == KE_KEY && keycode == key->keycode)
275 return key;
276
277 return NULL;
278}
279
280static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
281{
282 struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
283
284 if (key && key->type == KE_KEY) {
285 *keycode = key->keycode;
286 return 0;
287 }
288
289 return -EINVAL;
290}
291
292static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
293{
294 struct key_entry *key;
295 int old_keycode;
296
297 if (keycode < 0 || keycode > KEY_MAX)
298 return -EINVAL;
299
300 key = hp_wmi_get_entry_by_scancode(scancode);
301 if (key && key->type == KE_KEY) {
302 old_keycode = key->keycode;
303 key->keycode = keycode;
304 set_bit(keycode, dev->keybit);
305 if (!hp_wmi_get_entry_by_keycode(old_keycode))
306 clear_bit(old_keycode, dev->keybit);
307 return 0;
308 }
309
310 return -EINVAL;
311}
312
313static void hp_wmi_notify(u32 value, void *context)
314{
315 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
316 static struct key_entry *key;
317 union acpi_object *obj;
318
319 wmi_get_event_data(value, &response);
320
321 obj = (union acpi_object *)response.pointer;
322
323 if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
324 int eventcode = *((u8 *) obj->buffer.pointer);
325 if (eventcode == 0x4)
326 eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
327 0);
328 key = hp_wmi_get_entry_by_scancode(eventcode);
329 if (key) {
330 switch (key->type) {
331 case KE_KEY:
332 input_report_key(hp_wmi_input_dev,
333 key->keycode, 1);
334 input_sync(hp_wmi_input_dev);
335 input_report_key(hp_wmi_input_dev,
336 key->keycode, 0);
337 input_sync(hp_wmi_input_dev);
338 break;
339 case KE_SW:
340 input_report_switch(hp_wmi_input_dev,
341 key->keycode,
342 hp_wmi_dock_state());
343 input_sync(hp_wmi_input_dev);
344 break;
345 }
346 } else if (eventcode == 0x5) {
347 if (wifi_rfkill)
348 rfkill_force_state(wifi_rfkill,
349 hp_wmi_wifi_state());
350 if (bluetooth_rfkill)
351 rfkill_force_state(bluetooth_rfkill,
352 hp_wmi_bluetooth_state());
353 if (wwan_rfkill)
354 rfkill_force_state(wwan_rfkill,
355 hp_wmi_wwan_state());
356 } else
357 printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
358 eventcode);
359 } else
360 printk(KERN_INFO "HP WMI: Unknown response received\n");
361}
362
363static int __init hp_wmi_input_setup(void)
364{
365 struct key_entry *key;
366 int err;
367
368 hp_wmi_input_dev = input_allocate_device();
369
370 hp_wmi_input_dev->name = "HP WMI hotkeys";
371 hp_wmi_input_dev->phys = "wmi/input0";
372 hp_wmi_input_dev->id.bustype = BUS_HOST;
373 hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
374 hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
375
376 for (key = hp_wmi_keymap; key->type != KE_END; key++) {
377 switch (key->type) {
378 case KE_KEY:
379 set_bit(EV_KEY, hp_wmi_input_dev->evbit);
380 set_bit(key->keycode, hp_wmi_input_dev->keybit);
381 break;
382 case KE_SW:
383 set_bit(EV_SW, hp_wmi_input_dev->evbit);
384 set_bit(key->keycode, hp_wmi_input_dev->swbit);
385 break;
386 }
387 }
388
389 err = input_register_device(hp_wmi_input_dev);
390
391 if (err) {
392 input_free_device(hp_wmi_input_dev);
393 return err;
394 }
395
396 return 0;
397}
398
399static void cleanup_sysfs(struct platform_device *device)
400{
401 device_remove_file(&device->dev, &dev_attr_display);
402 device_remove_file(&device->dev, &dev_attr_hddtemp);
403 device_remove_file(&device->dev, &dev_attr_als);
404 device_remove_file(&device->dev, &dev_attr_dock);
405}
406
407static int __init hp_wmi_bios_setup(struct platform_device *device)
408{
409 int err;
410 int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
411
412 err = device_create_file(&device->dev, &dev_attr_display);
413 if (err)
414 goto add_sysfs_error;
415 err = device_create_file(&device->dev, &dev_attr_hddtemp);
416 if (err)
417 goto add_sysfs_error;
418 err = device_create_file(&device->dev, &dev_attr_als);
419 if (err)
420 goto add_sysfs_error;
421 err = device_create_file(&device->dev, &dev_attr_dock);
422 if (err)
423 goto add_sysfs_error;
424
425 if (wireless & 0x1) {
426 wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
427 wifi_rfkill->name = "hp-wifi";
428 wifi_rfkill->state = hp_wmi_wifi_state();
429 wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
430 wifi_rfkill->user_claim_unsupported = 1;
431 rfkill_register(wifi_rfkill);
432 }
433
434 if (wireless & 0x2) {
435 bluetooth_rfkill = rfkill_allocate(&device->dev,
436 RFKILL_TYPE_BLUETOOTH);
437 bluetooth_rfkill->name = "hp-bluetooth";
438 bluetooth_rfkill->state = hp_wmi_bluetooth_state();
439 bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
440 bluetooth_rfkill->user_claim_unsupported = 1;
441 rfkill_register(bluetooth_rfkill);
442 }
443
444 if (wireless & 0x4) {
445 wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
446 wwan_rfkill->name = "hp-wwan";
447 wwan_rfkill->state = hp_wmi_wwan_state();
448 wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
449 wwan_rfkill->user_claim_unsupported = 1;
450 rfkill_register(wwan_rfkill);
451 }
452
453 return 0;
454add_sysfs_error:
455 cleanup_sysfs(device);
456 return err;
457}
458
459static int __exit hp_wmi_bios_remove(struct platform_device *device)
460{
461 cleanup_sysfs(device);
462
463 if (wifi_rfkill)
464 rfkill_unregister(wifi_rfkill);
465 if (bluetooth_rfkill)
466 rfkill_unregister(bluetooth_rfkill);
467 if (wwan_rfkill)
468 rfkill_unregister(wwan_rfkill);
469
470 return 0;
471}
472
473static int __init hp_wmi_init(void)
474{
475 int err;
476
477 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
478 err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
479 hp_wmi_notify, NULL);
480 if (!err)
481 hp_wmi_input_setup();
482 }
483
484 if (wmi_has_guid(HPWMI_BIOS_GUID)) {
485 err = platform_driver_register(&hp_wmi_driver);
486 if (err)
487 return 0;
488 hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
489 if (!hp_wmi_platform_dev) {
490 platform_driver_unregister(&hp_wmi_driver);
491 return 0;
492 }
493 platform_device_add(hp_wmi_platform_dev);
494 }
495
496 return 0;
497}
498
499static void __exit hp_wmi_exit(void)
500{
501 if (wmi_has_guid(HPWMI_EVENT_GUID)) {
502 wmi_remove_notify_handler(HPWMI_EVENT_GUID);
503 input_unregister_device(hp_wmi_input_dev);
504 }
505 if (hp_wmi_platform_dev) {
506 platform_device_del(hp_wmi_platform_dev);
507 platform_driver_unregister(&hp_wmi_driver);
508 }
509}
510
511module_init(hp_wmi_init);
512module_exit(hp_wmi_exit);
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
new file mode 100644
index 000000000000..27b7662955bb
--- /dev/null
+++ b/drivers/platform/x86/intel_menlow.c
@@ -0,0 +1,536 @@
1/*
2 * intel_menlow.c - Intel menlow Driver for thermal management extension
3 *
4 * Copyright (C) 2008 Intel Corp
5 * Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
6 * Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 *
24 * This driver creates the sys I/F for programming the sensors.
25 * It also implements the driver for intel menlow memory controller (hardware
26 * id is INT0002) which makes use of the platform specific ACPI methods
27 * to get/set bandwidth.
28 */
29
30#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/types.h>
34#include <linux/pci.h>
35#include <linux/pm.h>
36
37#include <linux/thermal.h>
38#include <acpi/acpi_bus.h>
39#include <acpi/acpi_drivers.h>
40
41MODULE_AUTHOR("Thomas Sujith");
42MODULE_AUTHOR("Zhang Rui");
43MODULE_DESCRIPTION("Intel Menlow platform specific driver");
44MODULE_LICENSE("GPL");
45
46/*
47 * Memory controller device control
48 */
49
50#define MEMORY_GET_BANDWIDTH "GTHS"
51#define MEMORY_SET_BANDWIDTH "STHS"
52#define MEMORY_ARG_CUR_BANDWIDTH 1
53#define MEMORY_ARG_MAX_BANDWIDTH 0
54
55/*
56 * GTHS returning 'n' would mean that [0,n-1] states are supported
57 * In that case max_cstate would be n-1
58 * GTHS returning '0' would mean that no bandwidth control states are supported
59 */
60static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
61 unsigned long *max_state)
62{
63 struct acpi_device *device = cdev->devdata;
64 acpi_handle handle = device->handle;
65 unsigned long long value;
66 struct acpi_object_list arg_list;
67 union acpi_object arg;
68 acpi_status status = AE_OK;
69
70 arg_list.count = 1;
71 arg_list.pointer = &arg;
72 arg.type = ACPI_TYPE_INTEGER;
73 arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
74 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
75 &arg_list, &value);
76 if (ACPI_FAILURE(status))
77 return -EFAULT;
78
79 if (!value)
80 return -EINVAL;
81
82 *max_state = value - 1;
83 return 0;
84}
85
86static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
87 char *buf)
88{
89 unsigned long value;
90 if (memory_get_int_max_bandwidth(cdev, &value))
91 return -EINVAL;
92
93 return sprintf(buf, "%ld\n", value);
94}
95
96static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
97 char *buf)
98{
99 struct acpi_device *device = cdev->devdata;
100 acpi_handle handle = device->handle;
101 unsigned long long value;
102 struct acpi_object_list arg_list;
103 union acpi_object arg;
104 acpi_status status = AE_OK;
105
106 arg_list.count = 1;
107 arg_list.pointer = &arg;
108 arg.type = ACPI_TYPE_INTEGER;
109 arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
110 status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
111 &arg_list, &value);
112 if (ACPI_FAILURE(status))
113 return -EFAULT;
114
115 return sprintf(buf, "%llu\n", value);
116}
117
118static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
119 unsigned int state)
120{
121 struct acpi_device *device = cdev->devdata;
122 acpi_handle handle = device->handle;
123 struct acpi_object_list arg_list;
124 union acpi_object arg;
125 acpi_status status;
126 unsigned long long temp;
127 unsigned long max_state;
128
129 if (memory_get_int_max_bandwidth(cdev, &max_state))
130 return -EFAULT;
131
132 if (state > max_state)
133 return -EINVAL;
134
135 arg_list.count = 1;
136 arg_list.pointer = &arg;
137 arg.type = ACPI_TYPE_INTEGER;
138 arg.integer.value = state;
139
140 status =
141 acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
142 &temp);
143
144 printk(KERN_INFO
145 "Bandwidth value was %d: status is %d\n", state, status);
146 if (ACPI_FAILURE(status))
147 return -EFAULT;
148
149 return 0;
150}
151
152static struct thermal_cooling_device_ops memory_cooling_ops = {
153 .get_max_state = memory_get_max_bandwidth,
154 .get_cur_state = memory_get_cur_bandwidth,
155 .set_cur_state = memory_set_cur_bandwidth,
156};
157
158/*
159 * Memory Device Management
160 */
161static int intel_menlow_memory_add(struct acpi_device *device)
162{
163 int result = -ENODEV;
164 acpi_status status = AE_OK;
165 acpi_handle dummy;
166 struct thermal_cooling_device *cdev;
167
168 if (!device)
169 return -EINVAL;
170
171 status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
172 if (ACPI_FAILURE(status))
173 goto end;
174
175 status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
176 if (ACPI_FAILURE(status))
177 goto end;
178
179 cdev = thermal_cooling_device_register("Memory controller", device,
180 &memory_cooling_ops);
181 if (IS_ERR(cdev)) {
182 result = PTR_ERR(cdev);
183 goto end;
184 }
185
186 device->driver_data = cdev;
187 result = sysfs_create_link(&device->dev.kobj,
188 &cdev->device.kobj, "thermal_cooling");
189 if (result)
190 goto unregister;
191
192 result = sysfs_create_link(&cdev->device.kobj,
193 &device->dev.kobj, "device");
194 if (result) {
195 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
196 goto unregister;
197 }
198
199 end:
200 return result;
201
202 unregister:
203 thermal_cooling_device_unregister(cdev);
204 return result;
205
206}
207
208static int intel_menlow_memory_remove(struct acpi_device *device, int type)
209{
210 struct thermal_cooling_device *cdev = acpi_driver_data(device);
211
212 if (!device || !cdev)
213 return -EINVAL;
214
215 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
216 sysfs_remove_link(&cdev->device.kobj, "device");
217 thermal_cooling_device_unregister(cdev);
218
219 return 0;
220}
221
222static const struct acpi_device_id intel_menlow_memory_ids[] = {
223 {"INT0002", 0},
224 {"", 0},
225};
226
227static struct acpi_driver intel_menlow_memory_driver = {
228 .name = "intel_menlow_thermal_control",
229 .ids = intel_menlow_memory_ids,
230 .ops = {
231 .add = intel_menlow_memory_add,
232 .remove = intel_menlow_memory_remove,
233 },
234};
235
236/*
237 * Sensor control on menlow platform
238 */
239
240#define THERMAL_AUX0 0
241#define THERMAL_AUX1 1
242#define GET_AUX0 "GAX0"
243#define GET_AUX1 "GAX1"
244#define SET_AUX0 "SAX0"
245#define SET_AUX1 "SAX1"
246
247struct intel_menlow_attribute {
248 struct device_attribute attr;
249 struct device *device;
250 acpi_handle handle;
251 struct list_head node;
252};
253
254static LIST_HEAD(intel_menlow_attr_list);
255static DEFINE_MUTEX(intel_menlow_attr_lock);
256
257/*
258 * sensor_get_auxtrip - get the current auxtrip value from sensor
259 * @name: Thermalzone name
260 * @auxtype : AUX0/AUX1
261 * @buf: syfs buffer
262 */
263static int sensor_get_auxtrip(acpi_handle handle, int index,
264 unsigned long long *value)
265{
266 acpi_status status;
267
268 if ((index != 0 && index != 1) || !value)
269 return -EINVAL;
270
271 status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
272 NULL, value);
273 if (ACPI_FAILURE(status))
274 return -EIO;
275
276 return 0;
277}
278
279/*
280 * sensor_set_auxtrip - set the new auxtrip value to sensor
281 * @name: Thermalzone name
282 * @auxtype : AUX0/AUX1
283 * @buf: syfs buffer
284 */
285static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
286{
287 acpi_status status;
288 union acpi_object arg = {
289 ACPI_TYPE_INTEGER
290 };
291 struct acpi_object_list args = {
292 1, &arg
293 };
294 unsigned long long temp;
295
296 if (index != 0 && index != 1)
297 return -EINVAL;
298
299 status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
300 NULL, &temp);
301 if (ACPI_FAILURE(status))
302 return -EIO;
303 if ((index && value < temp) || (!index && value > temp))
304 return -EINVAL;
305
306 arg.integer.value = value;
307 status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
308 &args, &temp);
309 if (ACPI_FAILURE(status))
310 return -EIO;
311
312 /* do we need to check the return value of SAX0/SAX1 ? */
313
314 return 0;
315}
316
317#define to_intel_menlow_attr(_attr) \
318 container_of(_attr, struct intel_menlow_attribute, attr)
319
320static ssize_t aux0_show(struct device *dev,
321 struct device_attribute *dev_attr, char *buf)
322{
323 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
324 unsigned long long value;
325 int result;
326
327 result = sensor_get_auxtrip(attr->handle, 0, &value);
328
329 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
330}
331
332static ssize_t aux1_show(struct device *dev,
333 struct device_attribute *dev_attr, char *buf)
334{
335 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
336 unsigned long long value;
337 int result;
338
339 result = sensor_get_auxtrip(attr->handle, 1, &value);
340
341 return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
342}
343
344static ssize_t aux0_store(struct device *dev,
345 struct device_attribute *dev_attr,
346 const char *buf, size_t count)
347{
348 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
349 int value;
350 int result;
351
352 /*Sanity check; should be a positive integer */
353 if (!sscanf(buf, "%d", &value))
354 return -EINVAL;
355
356 if (value < 0)
357 return -EINVAL;
358
359 result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
360 return result ? result : count;
361}
362
363static ssize_t aux1_store(struct device *dev,
364 struct device_attribute *dev_attr,
365 const char *buf, size_t count)
366{
367 struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
368 int value;
369 int result;
370
371 /*Sanity check; should be a positive integer */
372 if (!sscanf(buf, "%d", &value))
373 return -EINVAL;
374
375 if (value < 0)
376 return -EINVAL;
377
378 result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
379 return result ? result : count;
380}
381
382/* BIOS can enable/disable the thermal user application in dabney platform */
383#define BIOS_ENABLED "\\_TZ.GSTS"
384static ssize_t bios_enabled_show(struct device *dev,
385 struct device_attribute *attr, char *buf)
386{
387 acpi_status status;
388 unsigned long long bios_enabled;
389
390 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
391 if (ACPI_FAILURE(status))
392 return -ENODEV;
393
394 return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
395}
396
397static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
398 void *store, struct device *dev,
399 acpi_handle handle)
400{
401 struct intel_menlow_attribute *attr;
402 int result;
403
404 attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
405 if (!attr)
406 return -ENOMEM;
407
408 attr->attr.attr.name = name;
409 attr->attr.attr.mode = mode;
410 attr->attr.show = show;
411 attr->attr.store = store;
412 attr->device = dev;
413 attr->handle = handle;
414
415 result = device_create_file(dev, &attr->attr);
416 if (result)
417 return result;
418
419 mutex_lock(&intel_menlow_attr_lock);
420 list_add_tail(&attr->node, &intel_menlow_attr_list);
421 mutex_unlock(&intel_menlow_attr_lock);
422
423 return 0;
424}
425
426static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
427 void *context, void **rv)
428{
429 acpi_status status;
430 acpi_handle dummy;
431 struct thermal_zone_device *thermal;
432 int result;
433
434 result = acpi_bus_get_private_data(handle, (void **)&thermal);
435 if (result)
436 return 0;
437
438 /* _TZ must have the AUX0/1 methods */
439 status = acpi_get_handle(handle, GET_AUX0, &dummy);
440 if (ACPI_FAILURE(status))
441 goto not_found;
442
443 status = acpi_get_handle(handle, SET_AUX0, &dummy);
444 if (ACPI_FAILURE(status))
445 goto not_found;
446
447 result = intel_menlow_add_one_attribute("aux0", 0644,
448 aux0_show, aux0_store,
449 &thermal->device, handle);
450 if (result)
451 return AE_ERROR;
452
453 status = acpi_get_handle(handle, GET_AUX1, &dummy);
454 if (ACPI_FAILURE(status))
455 goto not_found;
456
457 status = acpi_get_handle(handle, SET_AUX1, &dummy);
458 if (ACPI_FAILURE(status))
459 goto not_found;
460
461 result = intel_menlow_add_one_attribute("aux1", 0644,
462 aux1_show, aux1_store,
463 &thermal->device, handle);
464 if (result)
465 return AE_ERROR;
466
467 /*
468 * create the "dabney_enabled" attribute which means the user app
469 * should be loaded or not
470 */
471
472 result = intel_menlow_add_one_attribute("bios_enabled", 0444,
473 bios_enabled_show, NULL,
474 &thermal->device, handle);
475 if (result)
476 return AE_ERROR;
477
478 not_found:
479 if (status == AE_NOT_FOUND)
480 return AE_OK;
481 else
482 return status;
483}
484
485static void intel_menlow_unregister_sensor(void)
486{
487 struct intel_menlow_attribute *pos, *next;
488
489 mutex_lock(&intel_menlow_attr_lock);
490 list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
491 list_del(&pos->node);
492 device_remove_file(pos->device, &pos->attr);
493 kfree(pos);
494 }
495 mutex_unlock(&intel_menlow_attr_lock);
496
497 return;
498}
499
500static int __init intel_menlow_module_init(void)
501{
502 int result = -ENODEV;
503 acpi_status status;
504 unsigned long long enable;
505
506 if (acpi_disabled)
507 return result;
508
509 /* Looking for the \_TZ.GSTS method */
510 status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
511 if (ACPI_FAILURE(status) || !enable)
512 return -ENODEV;
513
514 /* Looking for ACPI device MEM0 with hardware id INT0002 */
515 result = acpi_bus_register_driver(&intel_menlow_memory_driver);
516 if (result)
517 return result;
518
519 /* Looking for sensors in each ACPI thermal zone */
520 status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
521 ACPI_UINT32_MAX,
522 intel_menlow_register_sensor, NULL, NULL);
523 if (ACPI_FAILURE(status))
524 return -ENODEV;
525
526 return 0;
527}
528
529static void __exit intel_menlow_module_exit(void)
530{
531 acpi_bus_unregister_driver(&intel_menlow_memory_driver);
532 intel_menlow_unregister_sensor();
533}
534
535module_init(intel_menlow_module_init);
536module_exit(intel_menlow_module_exit);
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
new file mode 100644
index 000000000000..759763d18e4c
--- /dev/null
+++ b/drivers/platform/x86/msi-laptop.c
@@ -0,0 +1,437 @@
1/*-*-linux-c-*-*/
2
3/*
4 Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20 */
21
22/*
23 * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
24 * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
25 *
26 * Driver also supports S271, S420 models.
27 *
28 * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
29 *
30 * lcd_level - Screen brightness: contains a single integer in the
31 * range 0..8. (rw)
32 *
33 * auto_brightness - Enable automatic brightness control: contains
34 * either 0 or 1. If set to 1 the hardware adjusts the screen
35 * brightness automatically when the power cord is
36 * plugged/unplugged. (rw)
37 *
38 * wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
39 *
40 * bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
41 * Please note that this file is constantly 0 if no Bluetooth
42 * hardware is available. (ro)
43 *
44 * In addition to these platform device attributes the driver
45 * registers itself in the Linux backlight control subsystem and is
46 * available to userspace under /sys/class/backlight/msi-laptop-bl/.
47 *
48 * This driver might work on other laptops produced by MSI. If you
49 * want to try it you can pass force=1 as argument to the module which
50 * will force it to load even when the DMI data doesn't identify the
51 * laptop as MSI S270. YMMV.
52 */
53
54#include <linux/module.h>
55#include <linux/kernel.h>
56#include <linux/init.h>
57#include <linux/acpi.h>
58#include <linux/dmi.h>
59#include <linux/backlight.h>
60#include <linux/platform_device.h>
61
62#define MSI_DRIVER_VERSION "0.5"
63
64#define MSI_LCD_LEVEL_MAX 9
65
66#define MSI_EC_COMMAND_WIRELESS 0x10
67#define MSI_EC_COMMAND_LCD_LEVEL 0x11
68
69static int force;
70module_param(force, bool, 0);
71MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
72
73static int auto_brightness;
74module_param(auto_brightness, int, 0);
75MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
76
77/* Hardware access */
78
79static int set_lcd_level(int level)
80{
81 u8 buf[2];
82
83 if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
84 return -EINVAL;
85
86 buf[0] = 0x80;
87 buf[1] = (u8) (level*31);
88
89 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
90}
91
92static int get_lcd_level(void)
93{
94 u8 wdata = 0, rdata;
95 int result;
96
97 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
98 if (result < 0)
99 return result;
100
101 return (int) rdata / 31;
102}
103
104static int get_auto_brightness(void)
105{
106 u8 wdata = 4, rdata;
107 int result;
108
109 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
110 if (result < 0)
111 return result;
112
113 return !!(rdata & 8);
114}
115
116static int set_auto_brightness(int enable)
117{
118 u8 wdata[2], rdata;
119 int result;
120
121 wdata[0] = 4;
122
123 result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
124 if (result < 0)
125 return result;
126
127 wdata[0] = 0x84;
128 wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
129
130 return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
131}
132
133static int get_wireless_state(int *wlan, int *bluetooth)
134{
135 u8 wdata = 0, rdata;
136 int result;
137
138 result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
139 if (result < 0)
140 return -1;
141
142 if (wlan)
143 *wlan = !!(rdata & 8);
144
145 if (bluetooth)
146 *bluetooth = !!(rdata & 128);
147
148 return 0;
149}
150
151/* Backlight device stuff */
152
153static int bl_get_brightness(struct backlight_device *b)
154{
155 return get_lcd_level();
156}
157
158
159static int bl_update_status(struct backlight_device *b)
160{
161 return set_lcd_level(b->props.brightness);
162}
163
164static struct backlight_ops msibl_ops = {
165 .get_brightness = bl_get_brightness,
166 .update_status = bl_update_status,
167};
168
169static struct backlight_device *msibl_device;
170
171/* Platform device */
172
173static ssize_t show_wlan(struct device *dev,
174 struct device_attribute *attr, char *buf)
175{
176
177 int ret, enabled;
178
179 ret = get_wireless_state(&enabled, NULL);
180 if (ret < 0)
181 return ret;
182
183 return sprintf(buf, "%i\n", enabled);
184}
185
186static ssize_t show_bluetooth(struct device *dev,
187 struct device_attribute *attr, char *buf)
188{
189
190 int ret, enabled;
191
192 ret = get_wireless_state(NULL, &enabled);
193 if (ret < 0)
194 return ret;
195
196 return sprintf(buf, "%i\n", enabled);
197}
198
199static ssize_t show_lcd_level(struct device *dev,
200 struct device_attribute *attr, char *buf)
201{
202
203 int ret;
204
205 ret = get_lcd_level();
206 if (ret < 0)
207 return ret;
208
209 return sprintf(buf, "%i\n", ret);
210}
211
212static ssize_t store_lcd_level(struct device *dev,
213 struct device_attribute *attr, const char *buf, size_t count)
214{
215
216 int level, ret;
217
218 if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
219 return -EINVAL;
220
221 ret = set_lcd_level(level);
222 if (ret < 0)
223 return ret;
224
225 return count;
226}
227
228static ssize_t show_auto_brightness(struct device *dev,
229 struct device_attribute *attr, char *buf)
230{
231
232 int ret;
233
234 ret = get_auto_brightness();
235 if (ret < 0)
236 return ret;
237
238 return sprintf(buf, "%i\n", ret);
239}
240
241static ssize_t store_auto_brightness(struct device *dev,
242 struct device_attribute *attr, const char *buf, size_t count)
243{
244
245 int enable, ret;
246
247 if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
248 return -EINVAL;
249
250 ret = set_auto_brightness(enable);
251 if (ret < 0)
252 return ret;
253
254 return count;
255}
256
257static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
258static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
259static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
260static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
261
262static struct attribute *msipf_attributes[] = {
263 &dev_attr_lcd_level.attr,
264 &dev_attr_auto_brightness.attr,
265 &dev_attr_bluetooth.attr,
266 &dev_attr_wlan.attr,
267 NULL
268};
269
270static struct attribute_group msipf_attribute_group = {
271 .attrs = msipf_attributes
272};
273
274static struct platform_driver msipf_driver = {
275 .driver = {
276 .name = "msi-laptop-pf",
277 .owner = THIS_MODULE,
278 }
279};
280
281static struct platform_device *msipf_device;
282
283/* Initialization */
284
285static int dmi_check_cb(const struct dmi_system_id *id)
286{
287 printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
288 return 0;
289}
290
291static struct dmi_system_id __initdata msi_dmi_table[] = {
292 {
293 .ident = "MSI S270",
294 .matches = {
295 DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
296 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
297 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
298 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
299 },
300 .callback = dmi_check_cb
301 },
302 {
303 .ident = "MSI S271",
304 .matches = {
305 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
306 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
307 DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
308 DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
309 },
310 .callback = dmi_check_cb
311 },
312 {
313 .ident = "MSI S420",
314 .matches = {
315 DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
316 DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
317 DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
318 DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
319 },
320 .callback = dmi_check_cb
321 },
322 {
323 .ident = "Medion MD96100",
324 .matches = {
325 DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
326 DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
327 DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
328 DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
329 },
330 .callback = dmi_check_cb
331 },
332 { }
333};
334
335static int __init msi_init(void)
336{
337 int ret;
338
339 if (acpi_disabled)
340 return -ENODEV;
341
342 if (!force && !dmi_check_system(msi_dmi_table))
343 return -ENODEV;
344
345 if (auto_brightness < 0 || auto_brightness > 2)
346 return -EINVAL;
347
348 /* Register backlight stuff */
349
350 if (acpi_video_backlight_support()) {
351 printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
352 "by ACPI video driver\n");
353 } else {
354 msibl_device = backlight_device_register("msi-laptop-bl", NULL,
355 NULL, &msibl_ops);
356 if (IS_ERR(msibl_device))
357 return PTR_ERR(msibl_device);
358 msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
359 }
360
361 ret = platform_driver_register(&msipf_driver);
362 if (ret)
363 goto fail_backlight;
364
365 /* Register platform stuff */
366
367 msipf_device = platform_device_alloc("msi-laptop-pf", -1);
368 if (!msipf_device) {
369 ret = -ENOMEM;
370 goto fail_platform_driver;
371 }
372
373 ret = platform_device_add(msipf_device);
374 if (ret)
375 goto fail_platform_device1;
376
377 ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
378 if (ret)
379 goto fail_platform_device2;
380
381 /* Disable automatic brightness control by default because
382 * this module was probably loaded to do brightness control in
383 * software. */
384
385 if (auto_brightness != 2)
386 set_auto_brightness(auto_brightness);
387
388 printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
389
390 return 0;
391
392fail_platform_device2:
393
394 platform_device_del(msipf_device);
395
396fail_platform_device1:
397
398 platform_device_put(msipf_device);
399
400fail_platform_driver:
401
402 platform_driver_unregister(&msipf_driver);
403
404fail_backlight:
405
406 backlight_device_unregister(msibl_device);
407
408 return ret;
409}
410
411static void __exit msi_cleanup(void)
412{
413
414 sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
415 platform_device_unregister(msipf_device);
416 platform_driver_unregister(&msipf_driver);
417 backlight_device_unregister(msibl_device);
418
419 /* Enable automatic brightness control again */
420 if (auto_brightness != 2)
421 set_auto_brightness(1);
422
423 printk(KERN_INFO "msi-laptop: driver unloaded.\n");
424}
425
426module_init(msi_init);
427module_exit(msi_cleanup);
428
429MODULE_AUTHOR("Lennart Poettering");
430MODULE_DESCRIPTION("MSI Laptop Support");
431MODULE_VERSION(MSI_DRIVER_VERSION);
432MODULE_LICENSE("GPL");
433
434MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
435MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
436MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
437MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
new file mode 100644
index 000000000000..f30db367c82e
--- /dev/null
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -0,0 +1,744 @@
1/*
2 * Panasonic HotKey and LCD brightness control driver
3 * (C) 2004 Hiroshi Miura <miura@da-cha.org>
4 * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
5 * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
6 * (C) 2004 David Bronaugh <dbronaugh>
7 * (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
8 *
9 * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * publicshed by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 *---------------------------------------------------------------------------
25 *
26 * ChangeLog:
27 * Sep.23, 2008 Harald Welte <laforge@gnumonks.org>
28 * -v0.95 rename driver from drivers/acpi/pcc_acpi.c to
29 * drivers/misc/panasonic-laptop.c
30 *
31 * Jul.04, 2008 Harald Welte <laforge@gnumonks.org>
32 * -v0.94 replace /proc interface with device attributes
33 * support {set,get}keycode on th input device
34 *
35 * Jun.27, 2008 Harald Welte <laforge@gnumonks.org>
36 * -v0.92 merge with 2.6.26-rc6 input API changes
37 * remove broken <= 2.6.15 kernel support
38 * resolve all compiler warnings
39 * various coding style fixes (checkpatch.pl)
40 * add support for backlight api
41 * major code restructuring
42 *
43 * Dac.28, 2007 Harald Welte <laforge@gnumonks.org>
44 * -v0.91 merge with 2.6.24-rc6 ACPI changes
45 *
46 * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org>
47 * -v0.9 remove warning about section reference.
48 * remove acpi_os_free
49 * add /proc/acpi/pcc/brightness interface for HAL access
50 * merge dbronaugh's enhancement
51 * Aug.17, 2004 David Bronaugh (dbronaugh)
52 * - Added screen brightness setting interface
53 * Thanks to FreeBSD crew (acpi_panasonic.c)
54 * for the ideas I needed to accomplish it
55 *
56 * May.29, 2006 Hiroshi Miura <miura@da-cha.org>
57 * -v0.8.4 follow to change keyinput structure
58 * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
59 * Jacob Bower <jacob.bower@ic.ac.uk> and
60 * Hiroshi Yokota for providing solutions.
61 *
62 * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org>
63 * -v0.8.2 merge code of YOKOTA Hiroshi
64 * <yokota@netlab.is.tsukuba.ac.jp>.
65 * Add sticky key mode interface.
66 * Refactoring acpi_pcc_generate_keyinput().
67 *
68 * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org>
69 * -v0.8 Generate key input event on input subsystem.
70 * This is based on yet another driver written by
71 * Ryuta Nakanishi.
72 *
73 * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org>
74 * -v0.7 Change proc interface functions using seq_file
75 * facility as same as other ACPI drivers.
76 *
77 * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org>
78 * -v0.6.4 Fix a silly error with status checking
79 *
80 * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org>
81 * -v0.6.3 replace read_acpi_int by standard function
82 * acpi_evaluate_integer
83 * some clean up and make smart copyright notice.
84 * fix return value of pcc_acpi_get_key()
85 * fix checking return value of acpi_bus_register_driver()
86 *
87 * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
88 * -v0.6.2 Add check on ACPI data (num_sifr)
89 * Coding style cleanups, better error messages/handling
90 * Fixed an off-by-one error in memory allocation
91 *
92 * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
93 * -v0.6.1 Fix a silly error with status checking
94 *
95 * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org>
96 * - v0.6 Correct brightness controls to reflect reality
97 * based on information gleaned by Hiroshi Miura
98 * and discussions with Hiroshi Miura
99 *
100 * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org>
101 * - v0.5 support LCD brightness control
102 * based on the disclosed information by MEI.
103 *
104 * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org>
105 * - v0.4 first post version
106 * add function to retrive SIFR
107 *
108 * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org>
109 * - v0.3 get proper status of hotkey
110 *
111 * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org>
112 * - v0.2 add HotKey handler
113 *
114 * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org>
115 * - v0.1 start from toshiba_acpi driver written by John Belmonte
116 *
117 */
118
119#include <linux/kernel.h>
120#include <linux/module.h>
121#include <linux/init.h>
122#include <linux/types.h>
123#include <linux/backlight.h>
124#include <linux/ctype.h>
125#include <linux/seq_file.h>
126#include <linux/uaccess.h>
127#include <acpi/acpi_bus.h>
128#include <acpi/acpi_drivers.h>
129#include <linux/input.h>
130
131
132#ifndef ACPI_HOTKEY_COMPONENT
133#define ACPI_HOTKEY_COMPONENT 0x10000000
134#endif
135
136#define _COMPONENT ACPI_HOTKEY_COMPONENT
137
138MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
139MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
140MODULE_LICENSE("GPL");
141
142#define LOGPREFIX "pcc_acpi: "
143
144/* Define ACPI PATHs */
145/* Lets note hotkeys */
146#define METHOD_HKEY_QUERY "HINF"
147#define METHOD_HKEY_SQTY "SQTY"
148#define METHOD_HKEY_SINF "SINF"
149#define METHOD_HKEY_SSET "SSET"
150#define HKEY_NOTIFY 0x80
151
152#define ACPI_PCC_DRIVER_NAME "Panasonic Laptop Support"
153#define ACPI_PCC_DEVICE_NAME "Hotkey"
154#define ACPI_PCC_CLASS "pcc"
155
156#define ACPI_PCC_INPUT_PHYS "panasonic/hkey0"
157
158/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
159 ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
160*/
161enum SINF_BITS { SINF_NUM_BATTERIES = 0,
162 SINF_LCD_TYPE,
163 SINF_AC_MAX_BRIGHT,
164 SINF_AC_MIN_BRIGHT,
165 SINF_AC_CUR_BRIGHT,
166 SINF_DC_MAX_BRIGHT,
167 SINF_DC_MIN_BRIGHT,
168 SINF_DC_CUR_BRIGHT,
169 SINF_MUTE,
170 SINF_RESERVED,
171 SINF_ENV_STATE,
172 SINF_STICKY_KEY = 0x80,
173 };
174/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
175
176static int acpi_pcc_hotkey_add(struct acpi_device *device);
177static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
178static int acpi_pcc_hotkey_resume(struct acpi_device *device);
179
180static const struct acpi_device_id pcc_device_ids[] = {
181 { "MAT0012", 0},
182 { "MAT0013", 0},
183 { "MAT0018", 0},
184 { "MAT0019", 0},
185 { "", 0},
186};
187
188static struct acpi_driver acpi_pcc_driver = {
189 .name = ACPI_PCC_DRIVER_NAME,
190 .class = ACPI_PCC_CLASS,
191 .ids = pcc_device_ids,
192 .ops = {
193 .add = acpi_pcc_hotkey_add,
194 .remove = acpi_pcc_hotkey_remove,
195 .resume = acpi_pcc_hotkey_resume,
196 },
197};
198
199#define KEYMAP_SIZE 11
200static const int initial_keymap[KEYMAP_SIZE] = {
201 /* 0 */ KEY_RESERVED,
202 /* 1 */ KEY_BRIGHTNESSDOWN,
203 /* 2 */ KEY_BRIGHTNESSUP,
204 /* 3 */ KEY_DISPLAYTOGGLE,
205 /* 4 */ KEY_MUTE,
206 /* 5 */ KEY_VOLUMEDOWN,
207 /* 6 */ KEY_VOLUMEUP,
208 /* 7 */ KEY_SLEEP,
209 /* 8 */ KEY_PROG1, /* Change CPU boost */
210 /* 9 */ KEY_BATTERY,
211 /* 10 */ KEY_SUSPEND,
212};
213
214struct pcc_acpi {
215 acpi_handle handle;
216 unsigned long num_sifr;
217 int sticky_mode;
218 u32 *sinf;
219 struct acpi_device *device;
220 struct input_dev *input_dev;
221 struct backlight_device *backlight;
222 int keymap[KEYMAP_SIZE];
223};
224
225struct pcc_keyinput {
226 struct acpi_hotkey *hotkey;
227};
228
229/* method access functions */
230static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
231{
232 union acpi_object in_objs[] = {
233 { .integer.type = ACPI_TYPE_INTEGER,
234 .integer.value = func, },
235 { .integer.type = ACPI_TYPE_INTEGER,
236 .integer.value = val, },
237 };
238 struct acpi_object_list params = {
239 .count = ARRAY_SIZE(in_objs),
240 .pointer = in_objs,
241 };
242 acpi_status status = AE_OK;
243
244 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
245 &params, NULL);
246
247 return status == AE_OK;
248}
249
250static inline int acpi_pcc_get_sqty(struct acpi_device *device)
251{
252 unsigned long long s;
253 acpi_status status;
254
255 status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
256 NULL, &s);
257 if (ACPI_SUCCESS(status))
258 return s;
259 else {
260 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
261 "evaluation error HKEY.SQTY\n"));
262 return -EINVAL;
263 }
264}
265
266static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
267{
268 acpi_status status;
269 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
270 union acpi_object *hkey = NULL;
271 int i;
272
273 status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
274 &buffer);
275 if (ACPI_FAILURE(status)) {
276 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
277 "evaluation error HKEY.SINF\n"));
278 return 0;
279 }
280
281 hkey = buffer.pointer;
282 if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
283 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
284 goto end;
285 }
286
287 if (pcc->num_sifr < hkey->package.count) {
288 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
289 "SQTY reports bad SINF length\n"));
290 status = AE_ERROR;
291 goto end;
292 }
293
294 for (i = 0; i < hkey->package.count; i++) {
295 union acpi_object *element = &(hkey->package.elements[i]);
296 if (likely(element->type == ACPI_TYPE_INTEGER)) {
297 sinf[i] = element->integer.value;
298 } else
299 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
300 "Invalid HKEY.SINF data\n"));
301 }
302 sinf[hkey->package.count] = -1;
303
304end:
305 kfree(buffer.pointer);
306 return status == AE_OK;
307}
308
309/* backlight API interface functions */
310
311/* This driver currently treats AC and DC brightness identical,
312 * since we don't need to invent an interface to the core ACPI
313 * logic to receive events in case a power supply is plugged in
314 * or removed */
315
316static int bl_get(struct backlight_device *bd)
317{
318 struct pcc_acpi *pcc = bl_get_data(bd);
319
320 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
321 return -EIO;
322
323 return pcc->sinf[SINF_AC_CUR_BRIGHT];
324}
325
326static int bl_set_status(struct backlight_device *bd)
327{
328 struct pcc_acpi *pcc = bl_get_data(bd);
329 int bright = bd->props.brightness;
330 int rc;
331
332 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
333 return -EIO;
334
335 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
336 bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
337
338 if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
339 bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
340
341 if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
342 bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
343 return -EINVAL;
344
345 rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
346 if (rc < 0)
347 return rc;
348
349 return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
350}
351
352static struct backlight_ops pcc_backlight_ops = {
353 .get_brightness = bl_get,
354 .update_status = bl_set_status,
355};
356
357
358/* sysfs user interface functions */
359
360static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
361 char *buf)
362{
363 struct acpi_device *acpi = to_acpi_device(dev);
364 struct pcc_acpi *pcc = acpi_driver_data(acpi);
365
366 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
367 return -EIO;
368
369 return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
370}
371
372static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
373 char *buf)
374{
375 struct acpi_device *acpi = to_acpi_device(dev);
376 struct pcc_acpi *pcc = acpi_driver_data(acpi);
377
378 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
379 return -EIO;
380
381 return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
382}
383
384static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
385 char *buf)
386{
387 struct acpi_device *acpi = to_acpi_device(dev);
388 struct pcc_acpi *pcc = acpi_driver_data(acpi);
389
390 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
391 return -EIO;
392
393 return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
394}
395
396static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
397 char *buf)
398{
399 struct acpi_device *acpi = to_acpi_device(dev);
400 struct pcc_acpi *pcc = acpi_driver_data(acpi);
401
402 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
403 return -EIO;
404
405 return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
406}
407
408static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
409 const char *buf, size_t count)
410{
411 struct acpi_device *acpi = to_acpi_device(dev);
412 struct pcc_acpi *pcc = acpi_driver_data(acpi);
413 int val;
414
415 if (count && sscanf(buf, "%i", &val) == 1 &&
416 (val == 0 || val == 1)) {
417 acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
418 pcc->sticky_mode = val;
419 }
420
421 return count;
422}
423
424static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
425static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
426static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
427static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
428
429static struct attribute *pcc_sysfs_entries[] = {
430 &dev_attr_numbatt.attr,
431 &dev_attr_lcdtype.attr,
432 &dev_attr_mute.attr,
433 &dev_attr_sticky_key.attr,
434 NULL,
435};
436
437static struct attribute_group pcc_attr_group = {
438 .name = NULL, /* put in device directory */
439 .attrs = pcc_sysfs_entries,
440};
441
442
443/* hotkey input device driver */
444
445static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
446{
447 struct pcc_acpi *pcc = input_get_drvdata(dev);
448
449 if (scancode >= ARRAY_SIZE(pcc->keymap))
450 return -EINVAL;
451
452 *keycode = pcc->keymap[scancode];
453
454 return 0;
455}
456
457static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
458{
459 int i;
460
461 for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
462 if (pcc->keymap[i] == keycode)
463 return i+1;
464 }
465
466 return 0;
467}
468
469static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
470{
471 struct pcc_acpi *pcc = input_get_drvdata(dev);
472 int oldkeycode;
473
474 if (scancode >= ARRAY_SIZE(pcc->keymap))
475 return -EINVAL;
476
477 if (keycode < 0 || keycode > KEY_MAX)
478 return -EINVAL;
479
480 oldkeycode = pcc->keymap[scancode];
481 pcc->keymap[scancode] = keycode;
482
483 set_bit(keycode, dev->keybit);
484
485 if (!keymap_get_by_keycode(pcc, oldkeycode))
486 clear_bit(oldkeycode, dev->keybit);
487
488 return 0;
489}
490
491static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
492{
493 struct input_dev *hotk_input_dev = pcc->input_dev;
494 int rc;
495 int key_code, hkey_num;
496 unsigned long long result;
497
498 rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
499 NULL, &result);
500 if (!ACPI_SUCCESS(rc)) {
501 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
502 "error getting hotkey status\n"));
503 return;
504 }
505
506 acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
507
508 hkey_num = result & 0xf;
509
510 if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
511 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
512 "hotkey number out of range: %d\n",
513 hkey_num));
514 return;
515 }
516
517 key_code = pcc->keymap[hkey_num];
518
519 if (key_code != KEY_RESERVED) {
520 int pushed = (result & 0x80) ? TRUE : FALSE;
521
522 input_report_key(hotk_input_dev, key_code, pushed);
523 input_sync(hotk_input_dev);
524 }
525
526 return;
527}
528
529static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
530{
531 struct pcc_acpi *pcc = (struct pcc_acpi *) data;
532
533 switch (event) {
534 case HKEY_NOTIFY:
535 acpi_pcc_generate_keyinput(pcc);
536 break;
537 default:
538 /* nothing to do */
539 break;
540 }
541}
542
543static int acpi_pcc_init_input(struct pcc_acpi *pcc)
544{
545 int i, rc;
546
547 pcc->input_dev = input_allocate_device();
548 if (!pcc->input_dev) {
549 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
550 "Couldn't allocate input device for hotkey"));
551 return -ENOMEM;
552 }
553
554 pcc->input_dev->evbit[0] = BIT(EV_KEY);
555
556 pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
557 pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
558 pcc->input_dev->id.bustype = BUS_HOST;
559 pcc->input_dev->id.vendor = 0x0001;
560 pcc->input_dev->id.product = 0x0001;
561 pcc->input_dev->id.version = 0x0100;
562 pcc->input_dev->getkeycode = pcc_getkeycode;
563 pcc->input_dev->setkeycode = pcc_setkeycode;
564
565 /* load initial keymap */
566 memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
567
568 for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
569 __set_bit(pcc->keymap[i], pcc->input_dev->keybit);
570 __clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
571
572 input_set_drvdata(pcc->input_dev, pcc);
573
574 rc = input_register_device(pcc->input_dev);
575 if (rc < 0)
576 input_free_device(pcc->input_dev);
577
578 return rc;
579}
580
581/* kernel module interface */
582
583static int acpi_pcc_hotkey_resume(struct acpi_device *device)
584{
585 struct pcc_acpi *pcc = acpi_driver_data(device);
586 acpi_status status = AE_OK;
587
588 if (device == NULL || pcc == NULL)
589 return -EINVAL;
590
591 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
592 pcc->sticky_mode));
593
594 status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
595
596 return status == AE_OK ? 0 : -EINVAL;
597}
598
599static int acpi_pcc_hotkey_add(struct acpi_device *device)
600{
601 acpi_status status;
602 struct pcc_acpi *pcc;
603 int num_sifr, result;
604
605 if (!device)
606 return -EINVAL;
607
608 num_sifr = acpi_pcc_get_sqty(device);
609
610 if (num_sifr > 255) {
611 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
612 return -ENODEV;
613 }
614
615 pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
616 if (!pcc) {
617 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
618 "Couldn't allocate mem for pcc"));
619 return -ENOMEM;
620 }
621
622 pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
623 if (!pcc->sinf) {
624 result = -ENOMEM;
625 goto out_hotkey;
626 }
627
628 pcc->device = device;
629 pcc->handle = device->handle;
630 pcc->num_sifr = num_sifr;
631 device->driver_data = pcc;
632 strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
633 strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
634
635 result = acpi_pcc_init_input(pcc);
636 if (result) {
637 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
638 "Error installing keyinput handler\n"));
639 goto out_sinf;
640 }
641
642 /* initialize hotkey input device */
643 status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
644 acpi_pcc_hotkey_notify, pcc);
645
646 if (ACPI_FAILURE(status)) {
647 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
648 "Error installing notify handler\n"));
649 result = -ENODEV;
650 goto out_input;
651 }
652
653 /* initialize backlight */
654 pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
655 &pcc_backlight_ops);
656 if (IS_ERR(pcc->backlight))
657 goto out_notify;
658
659 if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
660 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
661 "Couldn't retrieve BIOS data\n"));
662 goto out_backlight;
663 }
664
665 /* read the initial brightness setting from the hardware */
666 pcc->backlight->props.max_brightness =
667 pcc->sinf[SINF_AC_MAX_BRIGHT];
668 pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
669
670 /* read the initial sticky key mode from the hardware */
671 pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
672
673 /* add sysfs attributes */
674 result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
675 if (result)
676 goto out_backlight;
677
678 return 0;
679
680out_backlight:
681 backlight_device_unregister(pcc->backlight);
682out_notify:
683 acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
684 acpi_pcc_hotkey_notify);
685out_input:
686 input_unregister_device(pcc->input_dev);
687 /* no need to input_free_device() since core input API refcount and
688 * free()s the device */
689out_sinf:
690 kfree(pcc->sinf);
691out_hotkey:
692 kfree(pcc);
693
694 return result;
695}
696
697static int __init acpi_pcc_init(void)
698{
699 int result = 0;
700
701 if (acpi_disabled)
702 return -ENODEV;
703
704 result = acpi_bus_register_driver(&acpi_pcc_driver);
705 if (result < 0) {
706 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
707 "Error registering hotkey driver\n"));
708 return -ENODEV;
709 }
710
711 return 0;
712}
713
714static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
715{
716 struct pcc_acpi *pcc = acpi_driver_data(device);
717
718 if (!device || !pcc)
719 return -EINVAL;
720
721 sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
722
723 backlight_device_unregister(pcc->backlight);
724
725 acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
726 acpi_pcc_hotkey_notify);
727
728 input_unregister_device(pcc->input_dev);
729 /* no need to input_free_device() since core input API refcount and
730 * free()s the device */
731
732 kfree(pcc->sinf);
733 kfree(pcc);
734
735 return 0;
736}
737
738static void __exit acpi_pcc_exit(void)
739{
740 acpi_bus_unregister_driver(&acpi_pcc_driver);
741}
742
743module_init(acpi_pcc_init);
744module_exit(acpi_pcc_exit);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
new file mode 100644
index 000000000000..537959d07148
--- /dev/null
+++ b/drivers/platform/x86/sony-laptop.c
@@ -0,0 +1,2784 @@
1/*
2 * ACPI Sony Notebook Control Driver (SNC and SPIC)
3 *
4 * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
5 * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
6 *
7 * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
8 * which are copyrighted by their respective authors.
9 *
10 * The SNY6001 driver part is based on the sonypi driver which includes
11 * material from:
12 *
13 * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
14 *
15 * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
16 *
17 * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
18 *
19 * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
20 *
21 * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
22 *
23 * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
24 *
25 * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
26 *
27 * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
28 *
29 * This program is free software; you can redistribute it and/or modify
30 * it under the terms of the GNU General Public License as published by
31 * the Free Software Foundation; either version 2 of the License, or
32 * (at your option) any later version.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * You should have received a copy of the GNU General Public License
40 * along with this program; if not, write to the Free Software
41 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
42 *
43 */
44
45#include <linux/kernel.h>
46#include <linux/module.h>
47#include <linux/moduleparam.h>
48#include <linux/init.h>
49#include <linux/smp_lock.h>
50#include <linux/types.h>
51#include <linux/backlight.h>
52#include <linux/platform_device.h>
53#include <linux/err.h>
54#include <linux/dmi.h>
55#include <linux/pci.h>
56#include <linux/interrupt.h>
57#include <linux/delay.h>
58#include <linux/input.h>
59#include <linux/kfifo.h>
60#include <linux/workqueue.h>
61#include <linux/acpi.h>
62#include <acpi/acpi_drivers.h>
63#include <acpi/acpi_bus.h>
64#include <asm/uaccess.h>
65#include <linux/sonypi.h>
66#include <linux/sony-laptop.h>
67#ifdef CONFIG_SONYPI_COMPAT
68#include <linux/poll.h>
69#include <linux/miscdevice.h>
70#endif
71
72#define DRV_PFX "sony-laptop: "
73#define dprintk(msg...) do { \
74 if (debug) printk(KERN_WARNING DRV_PFX msg); \
75} while (0)
76
77#define SONY_LAPTOP_DRIVER_VERSION "0.6"
78
79#define SONY_NC_CLASS "sony-nc"
80#define SONY_NC_HID "SNY5001"
81#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
82
83#define SONY_PIC_CLASS "sony-pic"
84#define SONY_PIC_HID "SNY6001"
85#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver"
86
87MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
88MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
89MODULE_LICENSE("GPL");
90MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
91
92static int debug;
93module_param(debug, int, 0);
94MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
95 "the development of this driver");
96
97static int no_spic; /* = 0 */
98module_param(no_spic, int, 0444);
99MODULE_PARM_DESC(no_spic,
100 "set this if you don't want to enable the SPIC device");
101
102static int compat; /* = 0 */
103module_param(compat, int, 0444);
104MODULE_PARM_DESC(compat,
105 "set this if you want to enable backward compatibility mode");
106
107static unsigned long mask = 0xffffffff;
108module_param(mask, ulong, 0644);
109MODULE_PARM_DESC(mask,
110 "set this to the mask of event you want to enable (see doc)");
111
112static int camera; /* = 0 */
113module_param(camera, int, 0444);
114MODULE_PARM_DESC(camera,
115 "set this to 1 to enable Motion Eye camera controls "
116 "(only use it if you have a C1VE or C1VN model)");
117
118#ifdef CONFIG_SONYPI_COMPAT
119static int minor = -1;
120module_param(minor, int, 0);
121MODULE_PARM_DESC(minor,
122 "minor number of the misc device for the SPIC compatibility code, "
123 "default is -1 (automatic)");
124#endif
125
126/*********** Input Devices ***********/
127
128#define SONY_LAPTOP_BUF_SIZE 128
129struct sony_laptop_input_s {
130 atomic_t users;
131 struct input_dev *jog_dev;
132 struct input_dev *key_dev;
133 struct kfifo *fifo;
134 spinlock_t fifo_lock;
135 struct workqueue_struct *wq;
136};
137static struct sony_laptop_input_s sony_laptop_input = {
138 .users = ATOMIC_INIT(0),
139};
140
141struct sony_laptop_keypress {
142 struct input_dev *dev;
143 int key;
144};
145
146/* Correspondance table between sonypi events
147 * and input layer indexes in the keymap
148 */
149static int sony_laptop_input_index[] = {
150 -1, /* 0 no event */
151 -1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */
152 -1, /* 2 SONYPI_EVENT_JOGDIAL_UP */
153 -1, /* 3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
154 -1, /* 4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
155 -1, /* 5 SONYPI_EVENT_JOGDIAL_PRESSED */
156 -1, /* 6 SONYPI_EVENT_JOGDIAL_RELEASED */
157 0, /* 7 SONYPI_EVENT_CAPTURE_PRESSED */
158 1, /* 8 SONYPI_EVENT_CAPTURE_RELEASED */
159 2, /* 9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
160 3, /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
161 4, /* 11 SONYPI_EVENT_FNKEY_ESC */
162 5, /* 12 SONYPI_EVENT_FNKEY_F1 */
163 6, /* 13 SONYPI_EVENT_FNKEY_F2 */
164 7, /* 14 SONYPI_EVENT_FNKEY_F3 */
165 8, /* 15 SONYPI_EVENT_FNKEY_F4 */
166 9, /* 16 SONYPI_EVENT_FNKEY_F5 */
167 10, /* 17 SONYPI_EVENT_FNKEY_F6 */
168 11, /* 18 SONYPI_EVENT_FNKEY_F7 */
169 12, /* 19 SONYPI_EVENT_FNKEY_F8 */
170 13, /* 20 SONYPI_EVENT_FNKEY_F9 */
171 14, /* 21 SONYPI_EVENT_FNKEY_F10 */
172 15, /* 22 SONYPI_EVENT_FNKEY_F11 */
173 16, /* 23 SONYPI_EVENT_FNKEY_F12 */
174 17, /* 24 SONYPI_EVENT_FNKEY_1 */
175 18, /* 25 SONYPI_EVENT_FNKEY_2 */
176 19, /* 26 SONYPI_EVENT_FNKEY_D */
177 20, /* 27 SONYPI_EVENT_FNKEY_E */
178 21, /* 28 SONYPI_EVENT_FNKEY_F */
179 22, /* 29 SONYPI_EVENT_FNKEY_S */
180 23, /* 30 SONYPI_EVENT_FNKEY_B */
181 24, /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
182 25, /* 32 SONYPI_EVENT_PKEY_P1 */
183 26, /* 33 SONYPI_EVENT_PKEY_P2 */
184 27, /* 34 SONYPI_EVENT_PKEY_P3 */
185 28, /* 35 SONYPI_EVENT_BACK_PRESSED */
186 -1, /* 36 SONYPI_EVENT_LID_CLOSED */
187 -1, /* 37 SONYPI_EVENT_LID_OPENED */
188 29, /* 38 SONYPI_EVENT_BLUETOOTH_ON */
189 30, /* 39 SONYPI_EVENT_BLUETOOTH_OFF */
190 31, /* 40 SONYPI_EVENT_HELP_PRESSED */
191 32, /* 41 SONYPI_EVENT_FNKEY_ONLY */
192 33, /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
193 34, /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
194 35, /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
195 36, /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
196 37, /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
197 38, /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
198 39, /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
199 40, /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
200 41, /* 50 SONYPI_EVENT_ZOOM_PRESSED */
201 42, /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
202 43, /* 52 SONYPI_EVENT_MEYE_FACE */
203 44, /* 53 SONYPI_EVENT_MEYE_OPPOSITE */
204 45, /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
205 46, /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
206 -1, /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
207 -1, /* 57 SONYPI_EVENT_BATTERY_INSERT */
208 -1, /* 58 SONYPI_EVENT_BATTERY_REMOVE */
209 -1, /* 59 SONYPI_EVENT_FNKEY_RELEASED */
210 47, /* 60 SONYPI_EVENT_WIRELESS_ON */
211 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */
212 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
213 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
214};
215
216static int sony_laptop_input_keycode_map[] = {
217 KEY_CAMERA, /* 0 SONYPI_EVENT_CAPTURE_PRESSED */
218 KEY_RESERVED, /* 1 SONYPI_EVENT_CAPTURE_RELEASED */
219 KEY_RESERVED, /* 2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
220 KEY_RESERVED, /* 3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
221 KEY_FN_ESC, /* 4 SONYPI_EVENT_FNKEY_ESC */
222 KEY_FN_F1, /* 5 SONYPI_EVENT_FNKEY_F1 */
223 KEY_FN_F2, /* 6 SONYPI_EVENT_FNKEY_F2 */
224 KEY_FN_F3, /* 7 SONYPI_EVENT_FNKEY_F3 */
225 KEY_FN_F4, /* 8 SONYPI_EVENT_FNKEY_F4 */
226 KEY_FN_F5, /* 9 SONYPI_EVENT_FNKEY_F5 */
227 KEY_FN_F6, /* 10 SONYPI_EVENT_FNKEY_F6 */
228 KEY_FN_F7, /* 11 SONYPI_EVENT_FNKEY_F7 */
229 KEY_FN_F8, /* 12 SONYPI_EVENT_FNKEY_F8 */
230 KEY_FN_F9, /* 13 SONYPI_EVENT_FNKEY_F9 */
231 KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */
232 KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */
233 KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */
234 KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */
235 KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */
236 KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */
237 KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */
238 KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */
239 KEY_FN_S, /* 22 SONYPI_EVENT_FNKEY_S */
240 KEY_FN_B, /* 23 SONYPI_EVENT_FNKEY_B */
241 KEY_BLUETOOTH, /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
242 KEY_PROG1, /* 25 SONYPI_EVENT_PKEY_P1 */
243 KEY_PROG2, /* 26 SONYPI_EVENT_PKEY_P2 */
244 KEY_PROG3, /* 27 SONYPI_EVENT_PKEY_P3 */
245 KEY_BACK, /* 28 SONYPI_EVENT_BACK_PRESSED */
246 KEY_BLUETOOTH, /* 29 SONYPI_EVENT_BLUETOOTH_ON */
247 KEY_BLUETOOTH, /* 30 SONYPI_EVENT_BLUETOOTH_OFF */
248 KEY_HELP, /* 31 SONYPI_EVENT_HELP_PRESSED */
249 KEY_FN, /* 32 SONYPI_EVENT_FNKEY_ONLY */
250 KEY_RESERVED, /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
251 KEY_RESERVED, /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
252 KEY_RESERVED, /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
253 KEY_RESERVED, /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
254 KEY_RESERVED, /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
255 KEY_RESERVED, /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
256 KEY_RESERVED, /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
257 KEY_RESERVED, /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
258 KEY_ZOOM, /* 41 SONYPI_EVENT_ZOOM_PRESSED */
259 BTN_THUMB, /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
260 KEY_RESERVED, /* 43 SONYPI_EVENT_MEYE_FACE */
261 KEY_RESERVED, /* 44 SONYPI_EVENT_MEYE_OPPOSITE */
262 KEY_RESERVED, /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
263 KEY_RESERVED, /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
264 KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */
265 KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */
266 KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
267 KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
268};
269
270/* release buttons after a short delay if pressed */
271static void do_sony_laptop_release_key(struct work_struct *work)
272{
273 struct sony_laptop_keypress kp;
274
275 while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
276 sizeof(kp)) == sizeof(kp)) {
277 msleep(10);
278 input_report_key(kp.dev, kp.key, 0);
279 input_sync(kp.dev);
280 }
281}
282static DECLARE_WORK(sony_laptop_release_key_work,
283 do_sony_laptop_release_key);
284
285/* forward event to the input subsystem */
286static void sony_laptop_report_input_event(u8 event)
287{
288 struct input_dev *jog_dev = sony_laptop_input.jog_dev;
289 struct input_dev *key_dev = sony_laptop_input.key_dev;
290 struct sony_laptop_keypress kp = { NULL };
291
292 if (event == SONYPI_EVENT_FNKEY_RELEASED) {
293 /* Nothing, not all VAIOs generate this event */
294 return;
295 }
296
297 /* report events */
298 switch (event) {
299 /* jog_dev events */
300 case SONYPI_EVENT_JOGDIAL_UP:
301 case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
302 input_report_rel(jog_dev, REL_WHEEL, 1);
303 input_sync(jog_dev);
304 return;
305
306 case SONYPI_EVENT_JOGDIAL_DOWN:
307 case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
308 input_report_rel(jog_dev, REL_WHEEL, -1);
309 input_sync(jog_dev);
310 return;
311
312 /* key_dev events */
313 case SONYPI_EVENT_JOGDIAL_PRESSED:
314 kp.key = BTN_MIDDLE;
315 kp.dev = jog_dev;
316 break;
317
318 default:
319 if (event >= ARRAY_SIZE(sony_laptop_input_index)) {
320 dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
321 break;
322 }
323 if (sony_laptop_input_index[event] != -1) {
324 kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
325 if (kp.key != KEY_UNKNOWN)
326 kp.dev = key_dev;
327 }
328 break;
329 }
330
331 if (kp.dev) {
332 input_report_key(kp.dev, kp.key, 1);
333 /* we emit the scancode so we can always remap the key */
334 input_event(kp.dev, EV_MSC, MSC_SCAN, event);
335 input_sync(kp.dev);
336 kfifo_put(sony_laptop_input.fifo,
337 (unsigned char *)&kp, sizeof(kp));
338
339 if (!work_pending(&sony_laptop_release_key_work))
340 queue_work(sony_laptop_input.wq,
341 &sony_laptop_release_key_work);
342 } else
343 dprintk("unknown input event %.2x\n", event);
344}
345
346static int sony_laptop_setup_input(struct acpi_device *acpi_device)
347{
348 struct input_dev *jog_dev;
349 struct input_dev *key_dev;
350 int i;
351 int error;
352
353 /* don't run again if already initialized */
354 if (atomic_add_return(1, &sony_laptop_input.users) > 1)
355 return 0;
356
357 /* kfifo */
358 spin_lock_init(&sony_laptop_input.fifo_lock);
359 sony_laptop_input.fifo =
360 kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
361 &sony_laptop_input.fifo_lock);
362 if (IS_ERR(sony_laptop_input.fifo)) {
363 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
364 error = PTR_ERR(sony_laptop_input.fifo);
365 goto err_dec_users;
366 }
367
368 /* init workqueue */
369 sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
370 if (!sony_laptop_input.wq) {
371 printk(KERN_ERR DRV_PFX
372 "Unabe to create workqueue.\n");
373 error = -ENXIO;
374 goto err_free_kfifo;
375 }
376
377 /* input keys */
378 key_dev = input_allocate_device();
379 if (!key_dev) {
380 error = -ENOMEM;
381 goto err_destroy_wq;
382 }
383
384 key_dev->name = "Sony Vaio Keys";
385 key_dev->id.bustype = BUS_ISA;
386 key_dev->id.vendor = PCI_VENDOR_ID_SONY;
387 key_dev->dev.parent = &acpi_device->dev;
388
389 /* Initialize the Input Drivers: special keys */
390 set_bit(EV_KEY, key_dev->evbit);
391 set_bit(EV_MSC, key_dev->evbit);
392 set_bit(MSC_SCAN, key_dev->mscbit);
393 key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
394 key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
395 key_dev->keycode = &sony_laptop_input_keycode_map;
396 for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
397 if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
398 set_bit(sony_laptop_input_keycode_map[i],
399 key_dev->keybit);
400 }
401 }
402
403 error = input_register_device(key_dev);
404 if (error)
405 goto err_free_keydev;
406
407 sony_laptop_input.key_dev = key_dev;
408
409 /* jogdial */
410 jog_dev = input_allocate_device();
411 if (!jog_dev) {
412 error = -ENOMEM;
413 goto err_unregister_keydev;
414 }
415
416 jog_dev->name = "Sony Vaio Jogdial";
417 jog_dev->id.bustype = BUS_ISA;
418 jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
419 key_dev->dev.parent = &acpi_device->dev;
420
421 jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
422 jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
423 jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
424
425 error = input_register_device(jog_dev);
426 if (error)
427 goto err_free_jogdev;
428
429 sony_laptop_input.jog_dev = jog_dev;
430
431 return 0;
432
433err_free_jogdev:
434 input_free_device(jog_dev);
435
436err_unregister_keydev:
437 input_unregister_device(key_dev);
438 /* to avoid kref underflow below at input_free_device */
439 key_dev = NULL;
440
441err_free_keydev:
442 input_free_device(key_dev);
443
444err_destroy_wq:
445 destroy_workqueue(sony_laptop_input.wq);
446
447err_free_kfifo:
448 kfifo_free(sony_laptop_input.fifo);
449
450err_dec_users:
451 atomic_dec(&sony_laptop_input.users);
452 return error;
453}
454
455static void sony_laptop_remove_input(void)
456{
457 /* cleanup only after the last user has gone */
458 if (!atomic_dec_and_test(&sony_laptop_input.users))
459 return;
460
461 /* flush workqueue first */
462 flush_workqueue(sony_laptop_input.wq);
463
464 /* destroy input devs */
465 input_unregister_device(sony_laptop_input.key_dev);
466 sony_laptop_input.key_dev = NULL;
467
468 if (sony_laptop_input.jog_dev) {
469 input_unregister_device(sony_laptop_input.jog_dev);
470 sony_laptop_input.jog_dev = NULL;
471 }
472
473 destroy_workqueue(sony_laptop_input.wq);
474 kfifo_free(sony_laptop_input.fifo);
475}
476
477/*********** Platform Device ***********/
478
479static atomic_t sony_pf_users = ATOMIC_INIT(0);
480static struct platform_driver sony_pf_driver = {
481 .driver = {
482 .name = "sony-laptop",
483 .owner = THIS_MODULE,
484 }
485};
486static struct platform_device *sony_pf_device;
487
488static int sony_pf_add(void)
489{
490 int ret = 0;
491
492 /* don't run again if already initialized */
493 if (atomic_add_return(1, &sony_pf_users) > 1)
494 return 0;
495
496 ret = platform_driver_register(&sony_pf_driver);
497 if (ret)
498 goto out;
499
500 sony_pf_device = platform_device_alloc("sony-laptop", -1);
501 if (!sony_pf_device) {
502 ret = -ENOMEM;
503 goto out_platform_registered;
504 }
505
506 ret = platform_device_add(sony_pf_device);
507 if (ret)
508 goto out_platform_alloced;
509
510 return 0;
511
512 out_platform_alloced:
513 platform_device_put(sony_pf_device);
514 sony_pf_device = NULL;
515 out_platform_registered:
516 platform_driver_unregister(&sony_pf_driver);
517 out:
518 atomic_dec(&sony_pf_users);
519 return ret;
520}
521
522static void sony_pf_remove(void)
523{
524 /* deregister only after the last user has gone */
525 if (!atomic_dec_and_test(&sony_pf_users))
526 return;
527
528 platform_device_del(sony_pf_device);
529 platform_device_put(sony_pf_device);
530 platform_driver_unregister(&sony_pf_driver);
531}
532
533/*********** SNC (SNY5001) Device ***********/
534
535/* the device uses 1-based values, while the backlight subsystem uses
536 0-based values */
537#define SONY_MAX_BRIGHTNESS 8
538
539#define SNC_VALIDATE_IN 0
540#define SNC_VALIDATE_OUT 1
541
542static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
543 char *);
544static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
545 const char *, size_t);
546static int boolean_validate(const int, const int);
547static int brightness_default_validate(const int, const int);
548
549struct sony_nc_value {
550 char *name; /* name of the entry */
551 char **acpiget; /* names of the ACPI get function */
552 char **acpiset; /* names of the ACPI set function */
553 int (*validate)(const int, const int); /* input/output validation */
554 int value; /* current setting */
555 int valid; /* Has ever been set */
556 int debug; /* active only in debug mode ? */
557 struct device_attribute devattr; /* sysfs atribute */
558};
559
560#define SNC_HANDLE_NAMES(_name, _values...) \
561 static char *snc_##_name[] = { _values, NULL }
562
563#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
564 { \
565 .name = __stringify(_name), \
566 .acpiget = _getters, \
567 .acpiset = _setters, \
568 .validate = _validate, \
569 .debug = _debug, \
570 .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
571 }
572
573#define SNC_HANDLE_NULL { .name = NULL }
574
575SNC_HANDLE_NAMES(fnkey_get, "GHKE");
576
577SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
578SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
579
580SNC_HANDLE_NAMES(cdpower_get, "GCDP");
581SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
582
583SNC_HANDLE_NAMES(audiopower_get, "GAZP");
584SNC_HANDLE_NAMES(audiopower_set, "AZPW");
585
586SNC_HANDLE_NAMES(lanpower_get, "GLNP");
587SNC_HANDLE_NAMES(lanpower_set, "LNPW");
588
589SNC_HANDLE_NAMES(lidstate_get, "GLID");
590
591SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
592SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
593
594SNC_HANDLE_NAMES(gainbass_get, "GMGB");
595SNC_HANDLE_NAMES(gainbass_set, "CMGB");
596
597SNC_HANDLE_NAMES(PID_get, "GPID");
598
599SNC_HANDLE_NAMES(CTR_get, "GCTR");
600SNC_HANDLE_NAMES(CTR_set, "SCTR");
601
602SNC_HANDLE_NAMES(PCR_get, "GPCR");
603SNC_HANDLE_NAMES(PCR_set, "SPCR");
604
605SNC_HANDLE_NAMES(CMI_get, "GCMI");
606SNC_HANDLE_NAMES(CMI_set, "SCMI");
607
608static struct sony_nc_value sony_nc_values[] = {
609 SNC_HANDLE(brightness_default, snc_brightness_def_get,
610 snc_brightness_def_set, brightness_default_validate, 0),
611 SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
612 SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
613 SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
614 boolean_validate, 0),
615 SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
616 boolean_validate, 1),
617 SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
618 boolean_validate, 0),
619 SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
620 boolean_validate, 0),
621 SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
622 boolean_validate, 0),
623 /* unknown methods */
624 SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
625 SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
626 SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
627 SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
628 SNC_HANDLE_NULL
629};
630
631static acpi_handle sony_nc_acpi_handle;
632static struct acpi_device *sony_nc_acpi_device = NULL;
633
634/*
635 * acpi_evaluate_object wrappers
636 */
637static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
638{
639 struct acpi_buffer output;
640 union acpi_object out_obj;
641 acpi_status status;
642
643 output.length = sizeof(out_obj);
644 output.pointer = &out_obj;
645
646 status = acpi_evaluate_object(handle, name, NULL, &output);
647 if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
648 *result = out_obj.integer.value;
649 return 0;
650 }
651
652 printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
653
654 return -1;
655}
656
657static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
658 int *result)
659{
660 struct acpi_object_list params;
661 union acpi_object in_obj;
662 struct acpi_buffer output;
663 union acpi_object out_obj;
664 acpi_status status;
665
666 params.count = 1;
667 params.pointer = &in_obj;
668 in_obj.type = ACPI_TYPE_INTEGER;
669 in_obj.integer.value = value;
670
671 output.length = sizeof(out_obj);
672 output.pointer = &out_obj;
673
674 status = acpi_evaluate_object(handle, name, &params, &output);
675 if (status == AE_OK) {
676 if (result != NULL) {
677 if (out_obj.type != ACPI_TYPE_INTEGER) {
678 printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
679 "return type\n");
680 return -1;
681 }
682 *result = out_obj.integer.value;
683 }
684 return 0;
685 }
686
687 printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
688
689 return -1;
690}
691
692/*
693 * sony_nc_values input/output validate functions
694 */
695
696/* brightness_default_validate:
697 *
698 * manipulate input output values to keep consistency with the
699 * backlight framework for which brightness values are 0-based.
700 */
701static int brightness_default_validate(const int direction, const int value)
702{
703 switch (direction) {
704 case SNC_VALIDATE_OUT:
705 return value - 1;
706 case SNC_VALIDATE_IN:
707 if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
708 return value + 1;
709 }
710 return -EINVAL;
711}
712
713/* boolean_validate:
714 *
715 * on input validate boolean values 0/1, on output just pass the
716 * received value.
717 */
718static int boolean_validate(const int direction, const int value)
719{
720 if (direction == SNC_VALIDATE_IN) {
721 if (value != 0 && value != 1)
722 return -EINVAL;
723 }
724 return value;
725}
726
727/*
728 * Sysfs show/store common to all sony_nc_values
729 */
730static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
731 char *buffer)
732{
733 int value;
734 struct sony_nc_value *item =
735 container_of(attr, struct sony_nc_value, devattr);
736
737 if (!*item->acpiget)
738 return -EIO;
739
740 if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
741 return -EIO;
742
743 if (item->validate)
744 value = item->validate(SNC_VALIDATE_OUT, value);
745
746 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
747}
748
749static ssize_t sony_nc_sysfs_store(struct device *dev,
750 struct device_attribute *attr,
751 const char *buffer, size_t count)
752{
753 int value;
754 struct sony_nc_value *item =
755 container_of(attr, struct sony_nc_value, devattr);
756
757 if (!item->acpiset)
758 return -EIO;
759
760 if (count > 31)
761 return -EINVAL;
762
763 value = simple_strtoul(buffer, NULL, 10);
764
765 if (item->validate)
766 value = item->validate(SNC_VALIDATE_IN, value);
767
768 if (value < 0)
769 return value;
770
771 if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
772 return -EIO;
773 item->value = value;
774 item->valid = 1;
775 return count;
776}
777
778
779/*
780 * Backlight device
781 */
782static int sony_backlight_update_status(struct backlight_device *bd)
783{
784 return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
785 bd->props.brightness + 1, NULL);
786}
787
788static int sony_backlight_get_brightness(struct backlight_device *bd)
789{
790 int value;
791
792 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
793 return 0;
794 /* brightness levels are 1-based, while backlight ones are 0-based */
795 return value - 1;
796}
797
798static struct backlight_device *sony_backlight_device;
799static struct backlight_ops sony_backlight_ops = {
800 .update_status = sony_backlight_update_status,
801 .get_brightness = sony_backlight_get_brightness,
802};
803
804/*
805 * New SNC-only Vaios event mapping to driver known keys
806 */
807struct sony_nc_event {
808 u8 data;
809 u8 event;
810};
811
812static struct sony_nc_event *sony_nc_events;
813
814/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
815 * for Fn keys
816 */
817static int sony_nc_C_enable(const struct dmi_system_id *id)
818{
819 int result = 0;
820
821 printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
822
823 sony_nc_events = id->driver_data;
824
825 if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
826 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
827 || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
828 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
829 || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
830 || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
831 printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
832 "functionalities may be missing\n");
833 return 1;
834 }
835 return 0;
836}
837
838static struct sony_nc_event sony_C_events[] = {
839 { 0x81, SONYPI_EVENT_FNKEY_F1 },
840 { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
841 { 0x85, SONYPI_EVENT_FNKEY_F5 },
842 { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
843 { 0x86, SONYPI_EVENT_FNKEY_F6 },
844 { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
845 { 0x87, SONYPI_EVENT_FNKEY_F7 },
846 { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
847 { 0x8A, SONYPI_EVENT_FNKEY_F10 },
848 { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
849 { 0x8C, SONYPI_EVENT_FNKEY_F12 },
850 { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
851 { 0, 0 },
852};
853
854/* SNC-only model map */
855static const struct dmi_system_id sony_nc_ids[] = {
856 {
857 .ident = "Sony Vaio FE Series",
858 .callback = sony_nc_C_enable,
859 .driver_data = sony_C_events,
860 .matches = {
861 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
862 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
863 },
864 },
865 {
866 .ident = "Sony Vaio FZ Series",
867 .callback = sony_nc_C_enable,
868 .driver_data = sony_C_events,
869 .matches = {
870 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
871 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"),
872 },
873 },
874 {
875 .ident = "Sony Vaio C Series",
876 .callback = sony_nc_C_enable,
877 .driver_data = sony_C_events,
878 .matches = {
879 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
880 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
881 },
882 },
883 {
884 .ident = "Sony Vaio N Series",
885 .callback = sony_nc_C_enable,
886 .driver_data = sony_C_events,
887 .matches = {
888 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
889 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"),
890 },
891 },
892 { }
893};
894
895/*
896 * ACPI callbacks
897 */
898static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
899{
900 struct sony_nc_event *evmap;
901 u32 ev = event;
902 int result;
903
904 if (ev == 0x92) {
905 /* read the key pressed from EC.GECR
906 * A call to SN07 with 0x0202 will do it as well respecting
907 * the current protocol on different OSes
908 *
909 * Note: the path for GECR may be
910 * \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
911 * \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
912 *
913 * TODO: we may want to do the same for the older GHKE -need
914 * dmi list- so this snippet may become one more callback.
915 */
916 if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
917 dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
918 else
919 ev = result & 0xFF;
920 }
921
922 if (sony_nc_events)
923 for (evmap = sony_nc_events; evmap->event; evmap++) {
924 if (evmap->data == ev) {
925 ev = evmap->event;
926 break;
927 }
928 }
929
930 dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
931 sony_laptop_report_input_event(ev);
932 acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
933}
934
935static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
936 void *context, void **return_value)
937{
938 struct acpi_device_info *info;
939 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
940
941 if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) {
942 info = buffer.pointer;
943
944 printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n",
945 (char *)&info->name, info->param_count);
946
947 kfree(buffer.pointer);
948 }
949
950 return AE_OK;
951}
952
953/*
954 * ACPI device
955 */
956static int sony_nc_resume(struct acpi_device *device)
957{
958 struct sony_nc_value *item;
959
960 for (item = sony_nc_values; item->name; item++) {
961 int ret;
962
963 if (!item->valid)
964 continue;
965 ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
966 item->value, NULL);
967 if (ret < 0) {
968 printk("%s: %d\n", __func__, ret);
969 break;
970 }
971 }
972
973 /* set the last requested brightness level */
974 if (sony_backlight_device &&
975 !sony_backlight_update_status(sony_backlight_device))
976 printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
977
978 /* re-initialize models with specific requirements */
979 dmi_check_system(sony_nc_ids);
980
981 return 0;
982}
983
984static int sony_nc_add(struct acpi_device *device)
985{
986 acpi_status status;
987 int result = 0;
988 acpi_handle handle;
989 struct sony_nc_value *item;
990
991 printk(KERN_INFO DRV_PFX "%s v%s.\n",
992 SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
993
994 sony_nc_acpi_device = device;
995 strcpy(acpi_device_class(device), "sony/hotkey");
996
997 sony_nc_acpi_handle = device->handle;
998
999 /* read device status */
1000 result = acpi_bus_get_status(device);
1001 /* bail IFF the above call was successful and the device is not present */
1002 if (!result && !device->status.present) {
1003 dprintk("Device not present\n");
1004 result = -ENODEV;
1005 goto outwalk;
1006 }
1007
1008 if (debug) {
1009 status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
1010 1, sony_walk_callback, NULL, NULL);
1011 if (ACPI_FAILURE(status)) {
1012 printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
1013 result = -ENODEV;
1014 goto outwalk;
1015 }
1016 }
1017
1018 /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
1019 * should be respected as we already checked for the device presence above */
1020 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
1021 dprintk("Invoking _INI\n");
1022 if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
1023 NULL, NULL)))
1024 dprintk("_INI Method failed\n");
1025 }
1026
1027 /* setup input devices and helper fifo */
1028 result = sony_laptop_setup_input(device);
1029 if (result) {
1030 printk(KERN_ERR DRV_PFX
1031 "Unabe to create input devices.\n");
1032 goto outwalk;
1033 }
1034
1035 status = acpi_install_notify_handler(sony_nc_acpi_handle,
1036 ACPI_DEVICE_NOTIFY,
1037 sony_acpi_notify, NULL);
1038 if (ACPI_FAILURE(status)) {
1039 printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
1040 result = -ENODEV;
1041 goto outinput;
1042 }
1043
1044 if (acpi_video_backlight_support()) {
1045 printk(KERN_INFO DRV_PFX "brightness ignored, must be "
1046 "controlled by ACPI video driver\n");
1047 } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
1048 &handle))) {
1049 sony_backlight_device = backlight_device_register("sony", NULL,
1050 NULL,
1051 &sony_backlight_ops);
1052
1053 if (IS_ERR(sony_backlight_device)) {
1054 printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
1055 sony_backlight_device = NULL;
1056 } else {
1057 sony_backlight_device->props.brightness =
1058 sony_backlight_get_brightness
1059 (sony_backlight_device);
1060 sony_backlight_device->props.max_brightness =
1061 SONY_MAX_BRIGHTNESS - 1;
1062 }
1063
1064 }
1065
1066 /* initialize models with specific requirements */
1067 dmi_check_system(sony_nc_ids);
1068
1069 result = sony_pf_add();
1070 if (result)
1071 goto outbacklight;
1072
1073 /* create sony_pf sysfs attributes related to the SNC device */
1074 for (item = sony_nc_values; item->name; ++item) {
1075
1076 if (!debug && item->debug)
1077 continue;
1078
1079 /* find the available acpiget as described in the DSDT */
1080 for (; item->acpiget && *item->acpiget; ++item->acpiget) {
1081 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
1082 *item->acpiget,
1083 &handle))) {
1084 dprintk("Found %s getter: %s\n",
1085 item->name, *item->acpiget);
1086 item->devattr.attr.mode |= S_IRUGO;
1087 break;
1088 }
1089 }
1090
1091 /* find the available acpiset as described in the DSDT */
1092 for (; item->acpiset && *item->acpiset; ++item->acpiset) {
1093 if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
1094 *item->acpiset,
1095 &handle))) {
1096 dprintk("Found %s setter: %s\n",
1097 item->name, *item->acpiset);
1098 item->devattr.attr.mode |= S_IWUSR;
1099 break;
1100 }
1101 }
1102
1103 if (item->devattr.attr.mode != 0) {
1104 result =
1105 device_create_file(&sony_pf_device->dev,
1106 &item->devattr);
1107 if (result)
1108 goto out_sysfs;
1109 }
1110 }
1111
1112 return 0;
1113
1114 out_sysfs:
1115 for (item = sony_nc_values; item->name; ++item) {
1116 device_remove_file(&sony_pf_device->dev, &item->devattr);
1117 }
1118 sony_pf_remove();
1119
1120 outbacklight:
1121 if (sony_backlight_device)
1122 backlight_device_unregister(sony_backlight_device);
1123
1124 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
1125 ACPI_DEVICE_NOTIFY,
1126 sony_acpi_notify);
1127 if (ACPI_FAILURE(status))
1128 printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
1129
1130 outinput:
1131 sony_laptop_remove_input();
1132
1133 outwalk:
1134 return result;
1135}
1136
1137static int sony_nc_remove(struct acpi_device *device, int type)
1138{
1139 acpi_status status;
1140 struct sony_nc_value *item;
1141
1142 if (sony_backlight_device)
1143 backlight_device_unregister(sony_backlight_device);
1144
1145 sony_nc_acpi_device = NULL;
1146
1147 status = acpi_remove_notify_handler(sony_nc_acpi_handle,
1148 ACPI_DEVICE_NOTIFY,
1149 sony_acpi_notify);
1150 if (ACPI_FAILURE(status))
1151 printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
1152
1153 for (item = sony_nc_values; item->name; ++item) {
1154 device_remove_file(&sony_pf_device->dev, &item->devattr);
1155 }
1156
1157 sony_pf_remove();
1158 sony_laptop_remove_input();
1159 dprintk(SONY_NC_DRIVER_NAME " removed.\n");
1160
1161 return 0;
1162}
1163
1164static const struct acpi_device_id sony_device_ids[] = {
1165 {SONY_NC_HID, 0},
1166 {SONY_PIC_HID, 0},
1167 {"", 0},
1168};
1169MODULE_DEVICE_TABLE(acpi, sony_device_ids);
1170
1171static const struct acpi_device_id sony_nc_device_ids[] = {
1172 {SONY_NC_HID, 0},
1173 {"", 0},
1174};
1175
1176static struct acpi_driver sony_nc_driver = {
1177 .name = SONY_NC_DRIVER_NAME,
1178 .class = SONY_NC_CLASS,
1179 .ids = sony_nc_device_ids,
1180 .owner = THIS_MODULE,
1181 .ops = {
1182 .add = sony_nc_add,
1183 .remove = sony_nc_remove,
1184 .resume = sony_nc_resume,
1185 },
1186};
1187
1188/*********** SPIC (SNY6001) Device ***********/
1189
1190#define SONYPI_DEVICE_TYPE1 0x00000001
1191#define SONYPI_DEVICE_TYPE2 0x00000002
1192#define SONYPI_DEVICE_TYPE3 0x00000004
1193#define SONYPI_DEVICE_TYPE4 0x00000008
1194
1195#define SONYPI_TYPE1_OFFSET 0x04
1196#define SONYPI_TYPE2_OFFSET 0x12
1197#define SONYPI_TYPE3_OFFSET 0x12
1198#define SONYPI_TYPE4_OFFSET 0x12
1199
1200struct sony_pic_ioport {
1201 struct acpi_resource_io io1;
1202 struct acpi_resource_io io2;
1203 struct list_head list;
1204};
1205
1206struct sony_pic_irq {
1207 struct acpi_resource_irq irq;
1208 struct list_head list;
1209};
1210
1211struct sonypi_eventtypes {
1212 u8 data;
1213 unsigned long mask;
1214 struct sonypi_event *events;
1215};
1216
1217struct device_ctrl {
1218 int model;
1219 int (*handle_irq)(const u8, const u8);
1220 u16 evport_offset;
1221 u8 has_camera;
1222 u8 has_bluetooth;
1223 u8 has_wwan;
1224 struct sonypi_eventtypes *event_types;
1225};
1226
1227struct sony_pic_dev {
1228 struct device_ctrl *control;
1229 struct acpi_device *acpi_dev;
1230 struct sony_pic_irq *cur_irq;
1231 struct sony_pic_ioport *cur_ioport;
1232 struct list_head interrupts;
1233 struct list_head ioports;
1234 struct mutex lock;
1235 u8 camera_power;
1236 u8 bluetooth_power;
1237 u8 wwan_power;
1238};
1239
1240static struct sony_pic_dev spic_dev = {
1241 .interrupts = LIST_HEAD_INIT(spic_dev.interrupts),
1242 .ioports = LIST_HEAD_INIT(spic_dev.ioports),
1243};
1244
1245/* Event masks */
1246#define SONYPI_JOGGER_MASK 0x00000001
1247#define SONYPI_CAPTURE_MASK 0x00000002
1248#define SONYPI_FNKEY_MASK 0x00000004
1249#define SONYPI_BLUETOOTH_MASK 0x00000008
1250#define SONYPI_PKEY_MASK 0x00000010
1251#define SONYPI_BACK_MASK 0x00000020
1252#define SONYPI_HELP_MASK 0x00000040
1253#define SONYPI_LID_MASK 0x00000080
1254#define SONYPI_ZOOM_MASK 0x00000100
1255#define SONYPI_THUMBPHRASE_MASK 0x00000200
1256#define SONYPI_MEYE_MASK 0x00000400
1257#define SONYPI_MEMORYSTICK_MASK 0x00000800
1258#define SONYPI_BATTERY_MASK 0x00001000
1259#define SONYPI_WIRELESS_MASK 0x00002000
1260
1261struct sonypi_event {
1262 u8 data;
1263 u8 event;
1264};
1265
1266/* The set of possible button release events */
1267static struct sonypi_event sonypi_releaseev[] = {
1268 { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
1269 { 0, 0 }
1270};
1271
1272/* The set of possible jogger events */
1273static struct sonypi_event sonypi_joggerev[] = {
1274 { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
1275 { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
1276 { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
1277 { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
1278 { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
1279 { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
1280 { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
1281 { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
1282 { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
1283 { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
1284 { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
1285 { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
1286 { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
1287 { 0, 0 }
1288};
1289
1290/* The set of possible capture button events */
1291static struct sonypi_event sonypi_captureev[] = {
1292 { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
1293 { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
1294 { 0x40, SONYPI_EVENT_CAPTURE_PRESSED },
1295 { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
1296 { 0, 0 }
1297};
1298
1299/* The set of possible fnkeys events */
1300static struct sonypi_event sonypi_fnkeyev[] = {
1301 { 0x10, SONYPI_EVENT_FNKEY_ESC },
1302 { 0x11, SONYPI_EVENT_FNKEY_F1 },
1303 { 0x12, SONYPI_EVENT_FNKEY_F2 },
1304 { 0x13, SONYPI_EVENT_FNKEY_F3 },
1305 { 0x14, SONYPI_EVENT_FNKEY_F4 },
1306 { 0x15, SONYPI_EVENT_FNKEY_F5 },
1307 { 0x16, SONYPI_EVENT_FNKEY_F6 },
1308 { 0x17, SONYPI_EVENT_FNKEY_F7 },
1309 { 0x18, SONYPI_EVENT_FNKEY_F8 },
1310 { 0x19, SONYPI_EVENT_FNKEY_F9 },
1311 { 0x1a, SONYPI_EVENT_FNKEY_F10 },
1312 { 0x1b, SONYPI_EVENT_FNKEY_F11 },
1313 { 0x1c, SONYPI_EVENT_FNKEY_F12 },
1314 { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
1315 { 0x21, SONYPI_EVENT_FNKEY_1 },
1316 { 0x22, SONYPI_EVENT_FNKEY_2 },
1317 { 0x31, SONYPI_EVENT_FNKEY_D },
1318 { 0x32, SONYPI_EVENT_FNKEY_E },
1319 { 0x33, SONYPI_EVENT_FNKEY_F },
1320 { 0x34, SONYPI_EVENT_FNKEY_S },
1321 { 0x35, SONYPI_EVENT_FNKEY_B },
1322 { 0x36, SONYPI_EVENT_FNKEY_ONLY },
1323 { 0, 0 }
1324};
1325
1326/* The set of possible program key events */
1327static struct sonypi_event sonypi_pkeyev[] = {
1328 { 0x01, SONYPI_EVENT_PKEY_P1 },
1329 { 0x02, SONYPI_EVENT_PKEY_P2 },
1330 { 0x04, SONYPI_EVENT_PKEY_P3 },
1331 { 0, 0 }
1332};
1333
1334/* The set of possible bluetooth events */
1335static struct sonypi_event sonypi_blueev[] = {
1336 { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
1337 { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
1338 { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
1339 { 0, 0 }
1340};
1341
1342/* The set of possible wireless events */
1343static struct sonypi_event sonypi_wlessev[] = {
1344 { 0x59, SONYPI_EVENT_WIRELESS_ON },
1345 { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
1346 { 0, 0 }
1347};
1348
1349/* The set of possible back button events */
1350static struct sonypi_event sonypi_backev[] = {
1351 { 0x20, SONYPI_EVENT_BACK_PRESSED },
1352 { 0, 0 }
1353};
1354
1355/* The set of possible help button events */
1356static struct sonypi_event sonypi_helpev[] = {
1357 { 0x3b, SONYPI_EVENT_HELP_PRESSED },
1358 { 0, 0 }
1359};
1360
1361
1362/* The set of possible lid events */
1363static struct sonypi_event sonypi_lidev[] = {
1364 { 0x51, SONYPI_EVENT_LID_CLOSED },
1365 { 0x50, SONYPI_EVENT_LID_OPENED },
1366 { 0, 0 }
1367};
1368
1369/* The set of possible zoom events */
1370static struct sonypi_event sonypi_zoomev[] = {
1371 { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
1372 { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
1373 { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
1374 { 0, 0 }
1375};
1376
1377/* The set of possible thumbphrase events */
1378static struct sonypi_event sonypi_thumbphraseev[] = {
1379 { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
1380 { 0, 0 }
1381};
1382
1383/* The set of possible motioneye camera events */
1384static struct sonypi_event sonypi_meyeev[] = {
1385 { 0x00, SONYPI_EVENT_MEYE_FACE },
1386 { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
1387 { 0, 0 }
1388};
1389
1390/* The set of possible memorystick events */
1391static struct sonypi_event sonypi_memorystickev[] = {
1392 { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
1393 { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
1394 { 0, 0 }
1395};
1396
1397/* The set of possible battery events */
1398static struct sonypi_event sonypi_batteryev[] = {
1399 { 0x20, SONYPI_EVENT_BATTERY_INSERT },
1400 { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
1401 { 0, 0 }
1402};
1403
1404static struct sonypi_eventtypes type1_events[] = {
1405 { 0, 0xffffffff, sonypi_releaseev },
1406 { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
1407 { 0x30, SONYPI_LID_MASK, sonypi_lidev },
1408 { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
1409 { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
1410 { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1411 { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
1412 { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
1413 { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1414 { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
1415 { 0 },
1416};
1417static struct sonypi_eventtypes type2_events[] = {
1418 { 0, 0xffffffff, sonypi_releaseev },
1419 { 0x38, SONYPI_LID_MASK, sonypi_lidev },
1420 { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
1421 { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
1422 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1423 { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
1424 { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
1425 { 0x11, SONYPI_BACK_MASK, sonypi_backev },
1426 { 0x21, SONYPI_HELP_MASK, sonypi_helpev },
1427 { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
1428 { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
1429 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1430 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1431 { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
1432 { 0 },
1433};
1434static struct sonypi_eventtypes type3_events[] = {
1435 { 0, 0xffffffff, sonypi_releaseev },
1436 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1437 { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
1438 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1439 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1440 { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
1441 { 0 },
1442};
1443static struct sonypi_eventtypes type4_events[] = {
1444 { 0, 0xffffffff, sonypi_releaseev },
1445 { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
1446 { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
1447 { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
1448 { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
1449 { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },
1450 { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },
1451 { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },
1452 { 0 },
1453};
1454
1455/* low level spic calls */
1456#define ITERATIONS_LONG 10000
1457#define ITERATIONS_SHORT 10
1458#define wait_on_command(command, iterations) { \
1459 unsigned int n = iterations; \
1460 while (--n && (command)) \
1461 udelay(1); \
1462 if (!n) \
1463 dprintk("command failed at %s : %s (line %d)\n", \
1464 __FILE__, __func__, __LINE__); \
1465}
1466
1467static u8 sony_pic_call1(u8 dev)
1468{
1469 u8 v1, v2;
1470
1471 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1472 ITERATIONS_LONG);
1473 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1474 v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);
1475 v2 = inb_p(spic_dev.cur_ioport->io1.minimum);
1476 dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1);
1477 return v2;
1478}
1479
1480static u8 sony_pic_call2(u8 dev, u8 fn)
1481{
1482 u8 v1;
1483
1484 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1485 ITERATIONS_LONG);
1486 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1487 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
1488 ITERATIONS_LONG);
1489 outb(fn, spic_dev.cur_ioport->io1.minimum);
1490 v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
1491 dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1);
1492 return v1;
1493}
1494
1495static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
1496{
1497 u8 v1;
1498
1499 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1500 outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
1501 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1502 outb(fn, spic_dev.cur_ioport->io1.minimum);
1503 wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
1504 outb(v, spic_dev.cur_ioport->io1.minimum);
1505 v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
1506 dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n",
1507 dev, fn, v, v1);
1508 return v1;
1509}
1510
1511/*
1512 * minidrivers for SPIC models
1513 */
1514static int type4_handle_irq(const u8 data_mask, const u8 ev)
1515{
1516 /*
1517 * 0x31 could mean we have to take some extra action and wait for
1518 * the next irq for some Type4 models, it will generate a new
1519 * irq and we can read new data from the device:
1520 * - 0x5c and 0x5f requires 0xA0
1521 * - 0x61 requires 0xB3
1522 */
1523 if (data_mask == 0x31) {
1524 if (ev == 0x5c || ev == 0x5f)
1525 sony_pic_call1(0xA0);
1526 else if (ev == 0x61)
1527 sony_pic_call1(0xB3);
1528 return 0;
1529 }
1530 return 1;
1531}
1532
1533static struct device_ctrl spic_types[] = {
1534 {
1535 .model = SONYPI_DEVICE_TYPE1,
1536 .handle_irq = NULL,
1537 .evport_offset = SONYPI_TYPE1_OFFSET,
1538 .event_types = type1_events,
1539 },
1540 {
1541 .model = SONYPI_DEVICE_TYPE2,
1542 .handle_irq = NULL,
1543 .evport_offset = SONYPI_TYPE2_OFFSET,
1544 .event_types = type2_events,
1545 },
1546 {
1547 .model = SONYPI_DEVICE_TYPE3,
1548 .handle_irq = NULL,
1549 .evport_offset = SONYPI_TYPE3_OFFSET,
1550 .event_types = type3_events,
1551 },
1552 {
1553 .model = SONYPI_DEVICE_TYPE4,
1554 .handle_irq = type4_handle_irq,
1555 .evport_offset = SONYPI_TYPE4_OFFSET,
1556 .event_types = type4_events,
1557 },
1558};
1559
1560static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
1561{
1562 struct pci_dev *pcidev;
1563
1564 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1565 PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
1566 if (pcidev) {
1567 dev->control = &spic_types[0];
1568 goto out;
1569 }
1570
1571 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1572 PCI_DEVICE_ID_INTEL_ICH6_1, NULL);
1573 if (pcidev) {
1574 dev->control = &spic_types[2];
1575 goto out;
1576 }
1577
1578 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1579 PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
1580 if (pcidev) {
1581 dev->control = &spic_types[3];
1582 goto out;
1583 }
1584
1585 pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
1586 PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
1587 if (pcidev) {
1588 dev->control = &spic_types[3];
1589 goto out;
1590 }
1591
1592 /* default */
1593 dev->control = &spic_types[1];
1594
1595out:
1596 if (pcidev)
1597 pci_dev_put(pcidev);
1598
1599 printk(KERN_INFO DRV_PFX "detected Type%d model\n",
1600 dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 :
1601 dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 :
1602 dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4);
1603}
1604
1605/* camera tests and poweron/poweroff */
1606#define SONYPI_CAMERA_PICTURE 5
1607#define SONYPI_CAMERA_CONTROL 0x10
1608
1609#define SONYPI_CAMERA_BRIGHTNESS 0
1610#define SONYPI_CAMERA_CONTRAST 1
1611#define SONYPI_CAMERA_HUE 2
1612#define SONYPI_CAMERA_COLOR 3
1613#define SONYPI_CAMERA_SHARPNESS 4
1614
1615#define SONYPI_CAMERA_EXPOSURE_MASK 0xC
1616#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3
1617#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30
1618#define SONYPI_CAMERA_MUTE_MASK 0x40
1619
1620/* the rest don't need a loop until not 0xff */
1621#define SONYPI_CAMERA_AGC 6
1622#define SONYPI_CAMERA_AGC_MASK 0x30
1623#define SONYPI_CAMERA_SHUTTER_MASK 0x7
1624
1625#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7
1626#define SONYPI_CAMERA_CONTROL 0x10
1627
1628#define SONYPI_CAMERA_STATUS 7
1629#define SONYPI_CAMERA_STATUS_READY 0x2
1630#define SONYPI_CAMERA_STATUS_POSITION 0x4
1631
1632#define SONYPI_DIRECTION_BACKWARDS 0x4
1633
1634#define SONYPI_CAMERA_REVISION 8
1635#define SONYPI_CAMERA_ROMVERSION 9
1636
1637static int __sony_pic_camera_ready(void)
1638{
1639 u8 v;
1640
1641 v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
1642 return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
1643}
1644
1645static int __sony_pic_camera_off(void)
1646{
1647 if (!camera) {
1648 printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
1649 return -ENODEV;
1650 }
1651
1652 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
1653 SONYPI_CAMERA_MUTE_MASK),
1654 ITERATIONS_SHORT);
1655
1656 if (spic_dev.camera_power) {
1657 sony_pic_call2(0x91, 0);
1658 spic_dev.camera_power = 0;
1659 }
1660 return 0;
1661}
1662
1663static int __sony_pic_camera_on(void)
1664{
1665 int i, j, x;
1666
1667 if (!camera) {
1668 printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
1669 return -ENODEV;
1670 }
1671
1672 if (spic_dev.camera_power)
1673 return 0;
1674
1675 for (j = 5; j > 0; j--) {
1676
1677 for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
1678 msleep(10);
1679 sony_pic_call1(0x93);
1680
1681 for (i = 400; i > 0; i--) {
1682 if (__sony_pic_camera_ready())
1683 break;
1684 msleep(10);
1685 }
1686 if (i)
1687 break;
1688 }
1689
1690 if (j == 0) {
1691 printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
1692 return -ENODEV;
1693 }
1694
1695 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
1696 0x5a),
1697 ITERATIONS_SHORT);
1698
1699 spic_dev.camera_power = 1;
1700 return 0;
1701}
1702
1703/* External camera command (exported to the motion eye v4l driver) */
1704int sony_pic_camera_command(int command, u8 value)
1705{
1706 if (!camera)
1707 return -EIO;
1708
1709 mutex_lock(&spic_dev.lock);
1710
1711 switch (command) {
1712 case SONY_PIC_COMMAND_SETCAMERA:
1713 if (value)
1714 __sony_pic_camera_on();
1715 else
1716 __sony_pic_camera_off();
1717 break;
1718 case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
1719 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
1720 ITERATIONS_SHORT);
1721 break;
1722 case SONY_PIC_COMMAND_SETCAMERACONTRAST:
1723 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
1724 ITERATIONS_SHORT);
1725 break;
1726 case SONY_PIC_COMMAND_SETCAMERAHUE:
1727 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
1728 ITERATIONS_SHORT);
1729 break;
1730 case SONY_PIC_COMMAND_SETCAMERACOLOR:
1731 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
1732 ITERATIONS_SHORT);
1733 break;
1734 case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
1735 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
1736 ITERATIONS_SHORT);
1737 break;
1738 case SONY_PIC_COMMAND_SETCAMERAPICTURE:
1739 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
1740 ITERATIONS_SHORT);
1741 break;
1742 case SONY_PIC_COMMAND_SETCAMERAAGC:
1743 wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
1744 ITERATIONS_SHORT);
1745 break;
1746 default:
1747 printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
1748 command);
1749 break;
1750 }
1751 mutex_unlock(&spic_dev.lock);
1752 return 0;
1753}
1754EXPORT_SYMBOL(sony_pic_camera_command);
1755
1756/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
1757static void sony_pic_set_wwanpower(u8 state)
1758{
1759 state = !!state;
1760 mutex_lock(&spic_dev.lock);
1761 if (spic_dev.wwan_power == state) {
1762 mutex_unlock(&spic_dev.lock);
1763 return;
1764 }
1765 sony_pic_call2(0xB0, state);
1766 spic_dev.wwan_power = state;
1767 mutex_unlock(&spic_dev.lock);
1768}
1769
1770static ssize_t sony_pic_wwanpower_store(struct device *dev,
1771 struct device_attribute *attr,
1772 const char *buffer, size_t count)
1773{
1774 unsigned long value;
1775 if (count > 31)
1776 return -EINVAL;
1777
1778 value = simple_strtoul(buffer, NULL, 10);
1779 sony_pic_set_wwanpower(value);
1780
1781 return count;
1782}
1783
1784static ssize_t sony_pic_wwanpower_show(struct device *dev,
1785 struct device_attribute *attr, char *buffer)
1786{
1787 ssize_t count;
1788 mutex_lock(&spic_dev.lock);
1789 count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
1790 mutex_unlock(&spic_dev.lock);
1791 return count;
1792}
1793
1794/* bluetooth subsystem power state */
1795static void __sony_pic_set_bluetoothpower(u8 state)
1796{
1797 state = !!state;
1798 if (spic_dev.bluetooth_power == state)
1799 return;
1800 sony_pic_call2(0x96, state);
1801 sony_pic_call1(0x82);
1802 spic_dev.bluetooth_power = state;
1803}
1804
1805static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
1806 struct device_attribute *attr,
1807 const char *buffer, size_t count)
1808{
1809 unsigned long value;
1810 if (count > 31)
1811 return -EINVAL;
1812
1813 value = simple_strtoul(buffer, NULL, 10);
1814 mutex_lock(&spic_dev.lock);
1815 __sony_pic_set_bluetoothpower(value);
1816 mutex_unlock(&spic_dev.lock);
1817
1818 return count;
1819}
1820
1821static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
1822 struct device_attribute *attr, char *buffer)
1823{
1824 ssize_t count = 0;
1825 mutex_lock(&spic_dev.lock);
1826 count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
1827 mutex_unlock(&spic_dev.lock);
1828 return count;
1829}
1830
1831/* fan speed */
1832/* FAN0 information (reverse engineered from ACPI tables) */
1833#define SONY_PIC_FAN0_STATUS 0x93
1834static int sony_pic_set_fanspeed(unsigned long value)
1835{
1836 return ec_write(SONY_PIC_FAN0_STATUS, value);
1837}
1838
1839static int sony_pic_get_fanspeed(u8 *value)
1840{
1841 return ec_read(SONY_PIC_FAN0_STATUS, value);
1842}
1843
1844static ssize_t sony_pic_fanspeed_store(struct device *dev,
1845 struct device_attribute *attr,
1846 const char *buffer, size_t count)
1847{
1848 unsigned long value;
1849 if (count > 31)
1850 return -EINVAL;
1851
1852 value = simple_strtoul(buffer, NULL, 10);
1853 if (sony_pic_set_fanspeed(value))
1854 return -EIO;
1855
1856 return count;
1857}
1858
1859static ssize_t sony_pic_fanspeed_show(struct device *dev,
1860 struct device_attribute *attr, char *buffer)
1861{
1862 u8 value = 0;
1863 if (sony_pic_get_fanspeed(&value))
1864 return -EIO;
1865
1866 return snprintf(buffer, PAGE_SIZE, "%d\n", value);
1867}
1868
1869#define SPIC_ATTR(_name, _mode) \
1870struct device_attribute spic_attr_##_name = __ATTR(_name, \
1871 _mode, sony_pic_## _name ##_show, \
1872 sony_pic_## _name ##_store)
1873
1874static SPIC_ATTR(bluetoothpower, 0644);
1875static SPIC_ATTR(wwanpower, 0644);
1876static SPIC_ATTR(fanspeed, 0644);
1877
1878static struct attribute *spic_attributes[] = {
1879 &spic_attr_bluetoothpower.attr,
1880 &spic_attr_wwanpower.attr,
1881 &spic_attr_fanspeed.attr,
1882 NULL
1883};
1884
1885static struct attribute_group spic_attribute_group = {
1886 .attrs = spic_attributes
1887};
1888
1889/******** SONYPI compatibility **********/
1890#ifdef CONFIG_SONYPI_COMPAT
1891
1892/* battery / brightness / temperature addresses */
1893#define SONYPI_BAT_FLAGS 0x81
1894#define SONYPI_LCD_LIGHT 0x96
1895#define SONYPI_BAT1_PCTRM 0xa0
1896#define SONYPI_BAT1_LEFT 0xa2
1897#define SONYPI_BAT1_MAXRT 0xa4
1898#define SONYPI_BAT2_PCTRM 0xa8
1899#define SONYPI_BAT2_LEFT 0xaa
1900#define SONYPI_BAT2_MAXRT 0xac
1901#define SONYPI_BAT1_MAXTK 0xb0
1902#define SONYPI_BAT1_FULL 0xb2
1903#define SONYPI_BAT2_MAXTK 0xb8
1904#define SONYPI_BAT2_FULL 0xba
1905#define SONYPI_TEMP_STATUS 0xC1
1906
1907struct sonypi_compat_s {
1908 struct fasync_struct *fifo_async;
1909 struct kfifo *fifo;
1910 spinlock_t fifo_lock;
1911 wait_queue_head_t fifo_proc_list;
1912 atomic_t open_count;
1913};
1914static struct sonypi_compat_s sonypi_compat = {
1915 .open_count = ATOMIC_INIT(0),
1916};
1917
1918static int sonypi_misc_fasync(int fd, struct file *filp, int on)
1919{
1920 int retval;
1921
1922 retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
1923 if (retval < 0)
1924 return retval;
1925 return 0;
1926}
1927
1928static int sonypi_misc_release(struct inode *inode, struct file *file)
1929{
1930 atomic_dec(&sonypi_compat.open_count);
1931 return 0;
1932}
1933
1934static int sonypi_misc_open(struct inode *inode, struct file *file)
1935{
1936 /* Flush input queue on first open */
1937 lock_kernel();
1938 if (atomic_inc_return(&sonypi_compat.open_count) == 1)
1939 kfifo_reset(sonypi_compat.fifo);
1940 unlock_kernel();
1941 return 0;
1942}
1943
1944static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
1945 size_t count, loff_t *pos)
1946{
1947 ssize_t ret;
1948 unsigned char c;
1949
1950 if ((kfifo_len(sonypi_compat.fifo) == 0) &&
1951 (file->f_flags & O_NONBLOCK))
1952 return -EAGAIN;
1953
1954 ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
1955 kfifo_len(sonypi_compat.fifo) != 0);
1956 if (ret)
1957 return ret;
1958
1959 while (ret < count &&
1960 (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
1961 if (put_user(c, buf++))
1962 return -EFAULT;
1963 ret++;
1964 }
1965
1966 if (ret > 0) {
1967 struct inode *inode = file->f_path.dentry->d_inode;
1968 inode->i_atime = current_fs_time(inode->i_sb);
1969 }
1970
1971 return ret;
1972}
1973
1974static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
1975{
1976 poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
1977 if (kfifo_len(sonypi_compat.fifo))
1978 return POLLIN | POLLRDNORM;
1979 return 0;
1980}
1981
1982static int ec_read16(u8 addr, u16 *value)
1983{
1984 u8 val_lb, val_hb;
1985 if (ec_read(addr, &val_lb))
1986 return -1;
1987 if (ec_read(addr + 1, &val_hb))
1988 return -1;
1989 *value = val_lb | (val_hb << 8);
1990 return 0;
1991}
1992
1993static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
1994 unsigned int cmd, unsigned long arg)
1995{
1996 int ret = 0;
1997 void __user *argp = (void __user *)arg;
1998 u8 val8;
1999 u16 val16;
2000 int value;
2001
2002 mutex_lock(&spic_dev.lock);
2003 switch (cmd) {
2004 case SONYPI_IOCGBRT:
2005 if (sony_backlight_device == NULL) {
2006 ret = -EIO;
2007 break;
2008 }
2009 if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
2010 ret = -EIO;
2011 break;
2012 }
2013 val8 = ((value & 0xff) - 1) << 5;
2014 if (copy_to_user(argp, &val8, sizeof(val8)))
2015 ret = -EFAULT;
2016 break;
2017 case SONYPI_IOCSBRT:
2018 if (sony_backlight_device == NULL) {
2019 ret = -EIO;
2020 break;
2021 }
2022 if (copy_from_user(&val8, argp, sizeof(val8))) {
2023 ret = -EFAULT;
2024 break;
2025 }
2026 if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
2027 (val8 >> 5) + 1, NULL)) {
2028 ret = -EIO;
2029 break;
2030 }
2031 /* sync the backlight device status */
2032 sony_backlight_device->props.brightness =
2033 sony_backlight_get_brightness(sony_backlight_device);
2034 break;
2035 case SONYPI_IOCGBAT1CAP:
2036 if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
2037 ret = -EIO;
2038 break;
2039 }
2040 if (copy_to_user(argp, &val16, sizeof(val16)))
2041 ret = -EFAULT;
2042 break;
2043 case SONYPI_IOCGBAT1REM:
2044 if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
2045 ret = -EIO;
2046 break;
2047 }
2048 if (copy_to_user(argp, &val16, sizeof(val16)))
2049 ret = -EFAULT;
2050 break;
2051 case SONYPI_IOCGBAT2CAP:
2052 if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
2053 ret = -EIO;
2054 break;
2055 }
2056 if (copy_to_user(argp, &val16, sizeof(val16)))
2057 ret = -EFAULT;
2058 break;
2059 case SONYPI_IOCGBAT2REM:
2060 if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
2061 ret = -EIO;
2062 break;
2063 }
2064 if (copy_to_user(argp, &val16, sizeof(val16)))
2065 ret = -EFAULT;
2066 break;
2067 case SONYPI_IOCGBATFLAGS:
2068 if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
2069 ret = -EIO;
2070 break;
2071 }
2072 val8 &= 0x07;
2073 if (copy_to_user(argp, &val8, sizeof(val8)))
2074 ret = -EFAULT;
2075 break;
2076 case SONYPI_IOCGBLUE:
2077 val8 = spic_dev.bluetooth_power;
2078 if (copy_to_user(argp, &val8, sizeof(val8)))
2079 ret = -EFAULT;
2080 break;
2081 case SONYPI_IOCSBLUE:
2082 if (copy_from_user(&val8, argp, sizeof(val8))) {
2083 ret = -EFAULT;
2084 break;
2085 }
2086 __sony_pic_set_bluetoothpower(val8);
2087 break;
2088 /* FAN Controls */
2089 case SONYPI_IOCGFAN:
2090 if (sony_pic_get_fanspeed(&val8)) {
2091 ret = -EIO;
2092 break;
2093 }
2094 if (copy_to_user(argp, &val8, sizeof(val8)))
2095 ret = -EFAULT;
2096 break;
2097 case SONYPI_IOCSFAN:
2098 if (copy_from_user(&val8, argp, sizeof(val8))) {
2099 ret = -EFAULT;
2100 break;
2101 }
2102 if (sony_pic_set_fanspeed(val8))
2103 ret = -EIO;
2104 break;
2105 /* GET Temperature (useful under APM) */
2106 case SONYPI_IOCGTEMP:
2107 if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
2108 ret = -EIO;
2109 break;
2110 }
2111 if (copy_to_user(argp, &val8, sizeof(val8)))
2112 ret = -EFAULT;
2113 break;
2114 default:
2115 ret = -EINVAL;
2116 }
2117 mutex_unlock(&spic_dev.lock);
2118 return ret;
2119}
2120
2121static const struct file_operations sonypi_misc_fops = {
2122 .owner = THIS_MODULE,
2123 .read = sonypi_misc_read,
2124 .poll = sonypi_misc_poll,
2125 .open = sonypi_misc_open,
2126 .release = sonypi_misc_release,
2127 .fasync = sonypi_misc_fasync,
2128 .ioctl = sonypi_misc_ioctl,
2129};
2130
2131static struct miscdevice sonypi_misc_device = {
2132 .minor = MISC_DYNAMIC_MINOR,
2133 .name = "sonypi",
2134 .fops = &sonypi_misc_fops,
2135};
2136
2137static void sonypi_compat_report_event(u8 event)
2138{
2139 kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
2140 kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
2141 wake_up_interruptible(&sonypi_compat.fifo_proc_list);
2142}
2143
2144static int sonypi_compat_init(void)
2145{
2146 int error;
2147
2148 spin_lock_init(&sonypi_compat.fifo_lock);
2149 sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
2150 &sonypi_compat.fifo_lock);
2151 if (IS_ERR(sonypi_compat.fifo)) {
2152 printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
2153 return PTR_ERR(sonypi_compat.fifo);
2154 }
2155
2156 init_waitqueue_head(&sonypi_compat.fifo_proc_list);
2157
2158 if (minor != -1)
2159 sonypi_misc_device.minor = minor;
2160 error = misc_register(&sonypi_misc_device);
2161 if (error) {
2162 printk(KERN_ERR DRV_PFX "misc_register failed\n");
2163 goto err_free_kfifo;
2164 }
2165 if (minor == -1)
2166 printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
2167 sonypi_misc_device.minor);
2168
2169 return 0;
2170
2171err_free_kfifo:
2172 kfifo_free(sonypi_compat.fifo);
2173 return error;
2174}
2175
2176static void sonypi_compat_exit(void)
2177{
2178 misc_deregister(&sonypi_misc_device);
2179 kfifo_free(sonypi_compat.fifo);
2180}
2181#else
2182static int sonypi_compat_init(void) { return 0; }
2183static void sonypi_compat_exit(void) { }
2184static void sonypi_compat_report_event(u8 event) { }
2185#endif /* CONFIG_SONYPI_COMPAT */
2186
2187/*
2188 * ACPI callbacks
2189 */
2190static acpi_status
2191sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
2192{
2193 u32 i;
2194 struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
2195
2196 switch (resource->type) {
2197 case ACPI_RESOURCE_TYPE_START_DEPENDENT:
2198 {
2199 /* start IO enumeration */
2200 struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
2201 if (!ioport)
2202 return AE_ERROR;
2203
2204 list_add(&ioport->list, &dev->ioports);
2205 return AE_OK;
2206 }
2207
2208 case ACPI_RESOURCE_TYPE_END_DEPENDENT:
2209 /* end IO enumeration */
2210 return AE_OK;
2211
2212 case ACPI_RESOURCE_TYPE_IRQ:
2213 {
2214 struct acpi_resource_irq *p = &resource->data.irq;
2215 struct sony_pic_irq *interrupt = NULL;
2216 if (!p || !p->interrupt_count) {
2217 /*
2218 * IRQ descriptors may have no IRQ# bits set,
2219 * particularly those those w/ _STA disabled
2220 */
2221 dprintk("Blank IRQ resource\n");
2222 return AE_OK;
2223 }
2224 for (i = 0; i < p->interrupt_count; i++) {
2225 if (!p->interrupts[i]) {
2226 printk(KERN_WARNING DRV_PFX
2227 "Invalid IRQ %d\n",
2228 p->interrupts[i]);
2229 continue;
2230 }
2231 interrupt = kzalloc(sizeof(*interrupt),
2232 GFP_KERNEL);
2233 if (!interrupt)
2234 return AE_ERROR;
2235
2236 list_add(&interrupt->list, &dev->interrupts);
2237 interrupt->irq.triggering = p->triggering;
2238 interrupt->irq.polarity = p->polarity;
2239 interrupt->irq.sharable = p->sharable;
2240 interrupt->irq.interrupt_count = 1;
2241 interrupt->irq.interrupts[0] = p->interrupts[i];
2242 }
2243 return AE_OK;
2244 }
2245 case ACPI_RESOURCE_TYPE_IO:
2246 {
2247 struct acpi_resource_io *io = &resource->data.io;
2248 struct sony_pic_ioport *ioport =
2249 list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
2250 if (!io) {
2251 dprintk("Blank IO resource\n");
2252 return AE_OK;
2253 }
2254
2255 if (!ioport->io1.minimum) {
2256 memcpy(&ioport->io1, io, sizeof(*io));
2257 dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,
2258 ioport->io1.address_length);
2259 }
2260 else if (!ioport->io2.minimum) {
2261 memcpy(&ioport->io2, io, sizeof(*io));
2262 dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,
2263 ioport->io2.address_length);
2264 }
2265 else {
2266 printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
2267 return AE_ERROR;
2268 }
2269 return AE_OK;
2270 }
2271 default:
2272 dprintk("Resource %d isn't an IRQ nor an IO port\n",
2273 resource->type);
2274
2275 case ACPI_RESOURCE_TYPE_END_TAG:
2276 return AE_OK;
2277 }
2278 return AE_CTRL_TERMINATE;
2279}
2280
2281static int sony_pic_possible_resources(struct acpi_device *device)
2282{
2283 int result = 0;
2284 acpi_status status = AE_OK;
2285
2286 if (!device)
2287 return -EINVAL;
2288
2289 /* get device status */
2290 /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
2291 dprintk("Evaluating _STA\n");
2292 result = acpi_bus_get_status(device);
2293 if (result) {
2294 printk(KERN_WARNING DRV_PFX "Unable to read status\n");
2295 goto end;
2296 }
2297
2298 if (!device->status.enabled)
2299 dprintk("Device disabled\n");
2300 else
2301 dprintk("Device enabled\n");
2302
2303 /*
2304 * Query and parse 'method'
2305 */
2306 dprintk("Evaluating %s\n", METHOD_NAME__PRS);
2307 status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
2308 sony_pic_read_possible_resource, &spic_dev);
2309 if (ACPI_FAILURE(status)) {
2310 printk(KERN_WARNING DRV_PFX
2311 "Failure evaluating %s\n",
2312 METHOD_NAME__PRS);
2313 result = -ENODEV;
2314 }
2315end:
2316 return result;
2317}
2318
2319/*
2320 * Disable the spic device by calling its _DIS method
2321 */
2322static int sony_pic_disable(struct acpi_device *device)
2323{
2324 acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL,
2325 NULL);
2326
2327 if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND)
2328 return -ENXIO;
2329
2330 dprintk("Device disabled\n");
2331 return 0;
2332}
2333
2334
2335/*
2336 * Based on drivers/acpi/pci_link.c:acpi_pci_link_set
2337 *
2338 * Call _SRS to set current resources
2339 */
2340static int sony_pic_enable(struct acpi_device *device,
2341 struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
2342{
2343 acpi_status status;
2344 int result = 0;
2345 /* Type 1 resource layout is:
2346 * IO
2347 * IO
2348 * IRQNoFlags
2349 * End
2350 *
2351 * Type 2 and 3 resource layout is:
2352 * IO
2353 * IRQNoFlags
2354 * End
2355 */
2356 struct {
2357 struct acpi_resource res1;
2358 struct acpi_resource res2;
2359 struct acpi_resource res3;
2360 struct acpi_resource res4;
2361 } *resource;
2362 struct acpi_buffer buffer = { 0, NULL };
2363
2364 if (!ioport || !irq)
2365 return -EINVAL;
2366
2367 /* init acpi_buffer */
2368 resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
2369 if (!resource)
2370 return -ENOMEM;
2371
2372 buffer.length = sizeof(*resource) + 1;
2373 buffer.pointer = resource;
2374
2375 /* setup Type 1 resources */
2376 if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) {
2377
2378 /* setup io resources */
2379 resource->res1.type = ACPI_RESOURCE_TYPE_IO;
2380 resource->res1.length = sizeof(struct acpi_resource);
2381 memcpy(&resource->res1.data.io, &ioport->io1,
2382 sizeof(struct acpi_resource_io));
2383
2384 resource->res2.type = ACPI_RESOURCE_TYPE_IO;
2385 resource->res2.length = sizeof(struct acpi_resource);
2386 memcpy(&resource->res2.data.io, &ioport->io2,
2387 sizeof(struct acpi_resource_io));
2388
2389 /* setup irq resource */
2390 resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;
2391 resource->res3.length = sizeof(struct acpi_resource);
2392 memcpy(&resource->res3.data.irq, &irq->irq,
2393 sizeof(struct acpi_resource_irq));
2394 /* we requested a shared irq */
2395 resource->res3.data.irq.sharable = ACPI_SHARED;
2396
2397 resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
2398
2399 }
2400 /* setup Type 2/3 resources */
2401 else {
2402 /* setup io resource */
2403 resource->res1.type = ACPI_RESOURCE_TYPE_IO;
2404 resource->res1.length = sizeof(struct acpi_resource);
2405 memcpy(&resource->res1.data.io, &ioport->io1,
2406 sizeof(struct acpi_resource_io));
2407
2408 /* setup irq resource */
2409 resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;
2410 resource->res2.length = sizeof(struct acpi_resource);
2411 memcpy(&resource->res2.data.irq, &irq->irq,
2412 sizeof(struct acpi_resource_irq));
2413 /* we requested a shared irq */
2414 resource->res2.data.irq.sharable = ACPI_SHARED;
2415
2416 resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
2417 }
2418
2419 /* Attempt to set the resource */
2420 dprintk("Evaluating _SRS\n");
2421 status = acpi_set_current_resources(device->handle, &buffer);
2422
2423 /* check for total failure */
2424 if (ACPI_FAILURE(status)) {
2425 printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
2426 result = -ENODEV;
2427 goto end;
2428 }
2429
2430 /* Necessary device initializations calls (from sonypi) */
2431 sony_pic_call1(0x82);
2432 sony_pic_call2(0x81, 0xff);
2433 sony_pic_call1(compat ? 0x92 : 0x82);
2434
2435end:
2436 kfree(resource);
2437 return result;
2438}
2439
2440/*****************
2441 *
2442 * ISR: some event is available
2443 *
2444 *****************/
2445static irqreturn_t sony_pic_irq(int irq, void *dev_id)
2446{
2447 int i, j;
2448 u8 ev = 0;
2449 u8 data_mask = 0;
2450 u8 device_event = 0;
2451
2452 struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
2453
2454 ev = inb_p(dev->cur_ioport->io1.minimum);
2455 if (dev->cur_ioport->io2.minimum)
2456 data_mask = inb_p(dev->cur_ioport->io2.minimum);
2457 else
2458 data_mask = inb_p(dev->cur_ioport->io1.minimum +
2459 dev->control->evport_offset);
2460
2461 dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
2462 ev, data_mask, dev->cur_ioport->io1.minimum,
2463 dev->control->evport_offset);
2464
2465 if (ev == 0x00 || ev == 0xff)
2466 return IRQ_HANDLED;
2467
2468 for (i = 0; dev->control->event_types[i].mask; i++) {
2469
2470 if ((data_mask & dev->control->event_types[i].data) !=
2471 dev->control->event_types[i].data)
2472 continue;
2473
2474 if (!(mask & dev->control->event_types[i].mask))
2475 continue;
2476
2477 for (j = 0; dev->control->event_types[i].events[j].event; j++) {
2478 if (ev == dev->control->event_types[i].events[j].data) {
2479 device_event =
2480 dev->control->
2481 event_types[i].events[j].event;
2482 goto found;
2483 }
2484 }
2485 }
2486 /* Still not able to decode the event try to pass
2487 * it over to the minidriver
2488 */
2489 if (dev->control->handle_irq &&
2490 dev->control->handle_irq(data_mask, ev) == 0)
2491 return IRQ_HANDLED;
2492
2493 dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
2494 ev, data_mask, dev->cur_ioport->io1.minimum,
2495 dev->control->evport_offset);
2496 return IRQ_HANDLED;
2497
2498found:
2499 sony_laptop_report_input_event(device_event);
2500 acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
2501 sonypi_compat_report_event(device_event);
2502
2503 return IRQ_HANDLED;
2504}
2505
2506/*****************
2507 *
2508 * ACPI driver
2509 *
2510 *****************/
2511static int sony_pic_remove(struct acpi_device *device, int type)
2512{
2513 struct sony_pic_ioport *io, *tmp_io;
2514 struct sony_pic_irq *irq, *tmp_irq;
2515
2516 if (sony_pic_disable(device)) {
2517 printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
2518 return -ENXIO;
2519 }
2520
2521 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
2522 release_region(spic_dev.cur_ioport->io1.minimum,
2523 spic_dev.cur_ioport->io1.address_length);
2524 if (spic_dev.cur_ioport->io2.minimum)
2525 release_region(spic_dev.cur_ioport->io2.minimum,
2526 spic_dev.cur_ioport->io2.address_length);
2527
2528 sonypi_compat_exit();
2529
2530 sony_laptop_remove_input();
2531
2532 /* pf attrs */
2533 sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
2534 sony_pf_remove();
2535
2536 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
2537 list_del(&io->list);
2538 kfree(io);
2539 }
2540 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
2541 list_del(&irq->list);
2542 kfree(irq);
2543 }
2544 spic_dev.cur_ioport = NULL;
2545 spic_dev.cur_irq = NULL;
2546
2547 dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
2548 return 0;
2549}
2550
2551static int sony_pic_add(struct acpi_device *device)
2552{
2553 int result;
2554 struct sony_pic_ioport *io, *tmp_io;
2555 struct sony_pic_irq *irq, *tmp_irq;
2556
2557 printk(KERN_INFO DRV_PFX "%s v%s.\n",
2558 SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
2559
2560 spic_dev.acpi_dev = device;
2561 strcpy(acpi_device_class(device), "sony/hotkey");
2562 sony_pic_detect_device_type(&spic_dev);
2563 mutex_init(&spic_dev.lock);
2564
2565 /* read _PRS resources */
2566 result = sony_pic_possible_resources(device);
2567 if (result) {
2568 printk(KERN_ERR DRV_PFX
2569 "Unabe to read possible resources.\n");
2570 goto err_free_resources;
2571 }
2572
2573 /* setup input devices and helper fifo */
2574 result = sony_laptop_setup_input(device);
2575 if (result) {
2576 printk(KERN_ERR DRV_PFX
2577 "Unabe to create input devices.\n");
2578 goto err_free_resources;
2579 }
2580
2581 if (sonypi_compat_init())
2582 goto err_remove_input;
2583
2584 /* request io port */
2585 list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
2586 if (request_region(io->io1.minimum, io->io1.address_length,
2587 "Sony Programable I/O Device")) {
2588 dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
2589 io->io1.minimum, io->io1.maximum,
2590 io->io1.address_length);
2591 /* Type 1 have 2 ioports */
2592 if (io->io2.minimum) {
2593 if (request_region(io->io2.minimum,
2594 io->io2.address_length,
2595 "Sony Programable I/O Device")) {
2596 dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
2597 io->io2.minimum, io->io2.maximum,
2598 io->io2.address_length);
2599 spic_dev.cur_ioport = io;
2600 break;
2601 }
2602 else {
2603 dprintk("Unable to get I/O port2: "
2604 "0x%.4x (0x%.4x) + 0x%.2x\n",
2605 io->io2.minimum, io->io2.maximum,
2606 io->io2.address_length);
2607 release_region(io->io1.minimum,
2608 io->io1.address_length);
2609 }
2610 }
2611 else {
2612 spic_dev.cur_ioport = io;
2613 break;
2614 }
2615 }
2616 }
2617 if (!spic_dev.cur_ioport) {
2618 printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
2619 result = -ENODEV;
2620 goto err_remove_compat;
2621 }
2622
2623 /* request IRQ */
2624 list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
2625 if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
2626 IRQF_SHARED, "sony-laptop", &spic_dev)) {
2627 dprintk("IRQ: %d - triggering: %d - "
2628 "polarity: %d - shr: %d\n",
2629 irq->irq.interrupts[0],
2630 irq->irq.triggering,
2631 irq->irq.polarity,
2632 irq->irq.sharable);
2633 spic_dev.cur_irq = irq;
2634 break;
2635 }
2636 }
2637 if (!spic_dev.cur_irq) {
2638 printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
2639 result = -ENODEV;
2640 goto err_release_region;
2641 }
2642
2643 /* set resource status _SRS */
2644 result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
2645 if (result) {
2646 printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
2647 goto err_free_irq;
2648 }
2649
2650 spic_dev.bluetooth_power = -1;
2651 /* create device attributes */
2652 result = sony_pf_add();
2653 if (result)
2654 goto err_disable_device;
2655
2656 result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
2657 if (result)
2658 goto err_remove_pf;
2659
2660 return 0;
2661
2662err_remove_pf:
2663 sony_pf_remove();
2664
2665err_disable_device:
2666 sony_pic_disable(device);
2667
2668err_free_irq:
2669 free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
2670
2671err_release_region:
2672 release_region(spic_dev.cur_ioport->io1.minimum,
2673 spic_dev.cur_ioport->io1.address_length);
2674 if (spic_dev.cur_ioport->io2.minimum)
2675 release_region(spic_dev.cur_ioport->io2.minimum,
2676 spic_dev.cur_ioport->io2.address_length);
2677
2678err_remove_compat:
2679 sonypi_compat_exit();
2680
2681err_remove_input:
2682 sony_laptop_remove_input();
2683
2684err_free_resources:
2685 list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
2686 list_del(&io->list);
2687 kfree(io);
2688 }
2689 list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
2690 list_del(&irq->list);
2691 kfree(irq);
2692 }
2693 spic_dev.cur_ioport = NULL;
2694 spic_dev.cur_irq = NULL;
2695
2696 return result;
2697}
2698
2699static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
2700{
2701 if (sony_pic_disable(device))
2702 return -ENXIO;
2703 return 0;
2704}
2705
2706static int sony_pic_resume(struct acpi_device *device)
2707{
2708 sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
2709 return 0;
2710}
2711
2712static const struct acpi_device_id sony_pic_device_ids[] = {
2713 {SONY_PIC_HID, 0},
2714 {"", 0},
2715};
2716
2717static struct acpi_driver sony_pic_driver = {
2718 .name = SONY_PIC_DRIVER_NAME,
2719 .class = SONY_PIC_CLASS,
2720 .ids = sony_pic_device_ids,
2721 .owner = THIS_MODULE,
2722 .ops = {
2723 .add = sony_pic_add,
2724 .remove = sony_pic_remove,
2725 .suspend = sony_pic_suspend,
2726 .resume = sony_pic_resume,
2727 },
2728};
2729
2730static struct dmi_system_id __initdata sonypi_dmi_table[] = {
2731 {
2732 .ident = "Sony Vaio",
2733 .matches = {
2734 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
2735 DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
2736 },
2737 },
2738 {
2739 .ident = "Sony Vaio",
2740 .matches = {
2741 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
2742 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
2743 },
2744 },
2745 { }
2746};
2747
2748static int __init sony_laptop_init(void)
2749{
2750 int result;
2751
2752 if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
2753 result = acpi_bus_register_driver(&sony_pic_driver);
2754 if (result) {
2755 printk(KERN_ERR DRV_PFX
2756 "Unable to register SPIC driver.");
2757 goto out;
2758 }
2759 }
2760
2761 result = acpi_bus_register_driver(&sony_nc_driver);
2762 if (result) {
2763 printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
2764 goto out_unregister_pic;
2765 }
2766
2767 return 0;
2768
2769out_unregister_pic:
2770 if (!no_spic)
2771 acpi_bus_unregister_driver(&sony_pic_driver);
2772out:
2773 return result;
2774}
2775
2776static void __exit sony_laptop_exit(void)
2777{
2778 acpi_bus_unregister_driver(&sony_nc_driver);
2779 if (!no_spic)
2780 acpi_bus_unregister_driver(&sony_pic_driver);
2781}
2782
2783module_init(sony_laptop_init);
2784module_exit(sony_laptop_exit);
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c
new file mode 100644
index 000000000000..b4a4aa9ee482
--- /dev/null
+++ b/drivers/platform/x86/tc1100-wmi.c
@@ -0,0 +1,289 @@
1/*
2 * HP Compaq TC1100 Tablet WMI Extras Driver
3 *
4 * Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 * Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
6 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
7 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
8 *
9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
24 *
25 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/types.h>
32#include <acpi/acpi.h>
33#include <acpi/acpi_bus.h>
34#include <acpi/acpi_drivers.h>
35#include <linux/platform_device.h>
36
37#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
38
39#define TC1100_INSTANCE_WIRELESS 1
40#define TC1100_INSTANCE_JOGDIAL 2
41
42#define TC1100_LOGPREFIX "tc1100-wmi: "
43#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
44
45MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
46MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
47MODULE_LICENSE("GPL");
48MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
49
50static int tc1100_probe(struct platform_device *device);
51static int tc1100_remove(struct platform_device *device);
52static int tc1100_suspend(struct platform_device *device, pm_message_t state);
53static int tc1100_resume(struct platform_device *device);
54
55static struct platform_driver tc1100_driver = {
56 .driver = {
57 .name = "tc1100-wmi",
58 .owner = THIS_MODULE,
59 },
60 .probe = tc1100_probe,
61 .remove = tc1100_remove,
62 .suspend = tc1100_suspend,
63 .resume = tc1100_resume,
64};
65
66static struct platform_device *tc1100_device;
67
68struct tc1100_data {
69 u32 wireless;
70 u32 jogdial;
71};
72
73static struct tc1100_data suspend_data;
74
75/* --------------------------------------------------------------------------
76 Device Management
77 -------------------------------------------------------------------------- */
78
79static int get_state(u32 *out, u8 instance)
80{
81 u32 tmp;
82 acpi_status status;
83 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
84 union acpi_object *obj;
85
86 if (!out)
87 return -EINVAL;
88
89 if (instance > 2)
90 return -ENODEV;
91
92 status = wmi_query_block(GUID, instance, &result);
93 if (ACPI_FAILURE(status))
94 return -ENODEV;
95
96 obj = (union acpi_object *) result.pointer;
97 if (obj && obj->type == ACPI_TYPE_BUFFER &&
98 obj->buffer.length == sizeof(u32)) {
99 tmp = *((u32 *) obj->buffer.pointer);
100 } else {
101 tmp = 0;
102 }
103
104 if (result.length > 0 && result.pointer)
105 kfree(result.pointer);
106
107 switch (instance) {
108 case TC1100_INSTANCE_WIRELESS:
109 *out = (tmp == 3) ? 1 : 0;
110 return 0;
111 case TC1100_INSTANCE_JOGDIAL:
112 *out = (tmp == 1) ? 1 : 0;
113 return 0;
114 default:
115 return -ENODEV;
116 }
117}
118
119static int set_state(u32 *in, u8 instance)
120{
121 u32 value;
122 acpi_status status;
123 struct acpi_buffer input;
124
125 if (!in)
126 return -EINVAL;
127
128 if (instance > 2)
129 return -ENODEV;
130
131 switch (instance) {
132 case TC1100_INSTANCE_WIRELESS:
133 value = (*in) ? 1 : 2;
134 break;
135 case TC1100_INSTANCE_JOGDIAL:
136 value = (*in) ? 0 : 1;
137 break;
138 default:
139 return -ENODEV;
140 }
141
142 input.length = sizeof(u32);
143 input.pointer = &value;
144
145 status = wmi_set_block(GUID, instance, &input);
146 if (ACPI_FAILURE(status))
147 return -ENODEV;
148
149 return 0;
150}
151
152/* --------------------------------------------------------------------------
153 FS Interface (/sys)
154 -------------------------------------------------------------------------- */
155
156/*
157 * Read/ write bool sysfs macro
158 */
159#define show_set_bool(value, instance) \
160static ssize_t \
161show_bool_##value(struct device *dev, struct device_attribute *attr, \
162 char *buf) \
163{ \
164 u32 result; \
165 acpi_status status = get_state(&result, instance); \
166 if (ACPI_SUCCESS(status)) \
167 return sprintf(buf, "%d\n", result); \
168 return sprintf(buf, "Read error\n"); \
169} \
170\
171static ssize_t \
172set_bool_##value(struct device *dev, struct device_attribute *attr, \
173 const char *buf, size_t count) \
174{ \
175 u32 tmp = simple_strtoul(buf, NULL, 10); \
176 acpi_status status = set_state(&tmp, instance); \
177 if (ACPI_FAILURE(status)) \
178 return -EINVAL; \
179 return count; \
180} \
181static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
182 show_bool_##value, set_bool_##value);
183
184show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
185show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
186
187static void remove_fs(void)
188{
189 device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
190 device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
191}
192
193static int add_fs(void)
194{
195 int ret;
196
197 ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
198 if (ret)
199 goto add_sysfs_error;
200
201 ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
202 if (ret)
203 goto add_sysfs_error;
204
205 return ret;
206
207add_sysfs_error:
208 remove_fs();
209 return ret;
210}
211
212/* --------------------------------------------------------------------------
213 Driver Model
214 -------------------------------------------------------------------------- */
215
216static int tc1100_probe(struct platform_device *device)
217{
218 int result = 0;
219
220 result = add_fs();
221 return result;
222}
223
224
225static int tc1100_remove(struct platform_device *device)
226{
227 remove_fs();
228 return 0;
229}
230
231static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
232{
233 int ret;
234
235 ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
236 if (ret)
237 return ret;
238
239 ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
240 if (ret)
241 return ret;
242
243 return ret;
244}
245
246static int tc1100_resume(struct platform_device *dev)
247{
248 int ret;
249
250 ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
251 if (ret)
252 return ret;
253
254 ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
255 if (ret)
256 return ret;
257
258 return ret;
259}
260
261static int __init tc1100_init(void)
262{
263 int result = 0;
264
265 if (!wmi_has_guid(GUID))
266 return -ENODEV;
267
268 result = platform_driver_register(&tc1100_driver);
269 if (result)
270 return result;
271
272 tc1100_device = platform_device_alloc("tc1100-wmi", -1);
273 platform_device_add(tc1100_device);
274
275 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
276
277 return result;
278}
279
280static void __exit tc1100_exit(void)
281{
282 platform_device_del(tc1100_device);
283 platform_driver_unregister(&tc1100_driver);
284
285 printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
286}
287
288module_init(tc1100_init);
289module_exit(tc1100_exit);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
new file mode 100644
index 000000000000..3478453eba7a
--- /dev/null
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -0,0 +1,6948 @@
1/*
2 * thinkpad_acpi.c - ThinkPad ACPI Extras
3 *
4 *
5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
6 * Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#define TPACPI_VERSION "0.21"
25#define TPACPI_SYSFS_VERSION 0x020200
26
27/*
28 * Changelog:
29 * 2007-10-20 changelog trimmed down
30 *
31 * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
32 * drivers/misc.
33 *
34 * 2006-11-22 0.13 new maintainer
35 * changelog now lives in git commit history, and will
36 * not be updated further in-file.
37 *
38 * 2005-03-17 0.11 support for 600e, 770x
39 * thanks to Jamie Lentin <lentinj@dial.pipex.com>
40 *
41 * 2005-01-16 0.9 use MODULE_VERSION
42 * thanks to Henrik Brix Andersen <brix@gentoo.org>
43 * fix parameter passing on module loading
44 * thanks to Rusty Russell <rusty@rustcorp.com.au>
45 * thanks to Jim Radford <radford@blackbean.org>
46 * 2004-11-08 0.8 fix init error case, don't return from a macro
47 * thanks to Chris Wright <chrisw@osdl.org>
48 */
49
50#include <linux/kernel.h>
51#include <linux/module.h>
52#include <linux/init.h>
53#include <linux/types.h>
54#include <linux/string.h>
55#include <linux/list.h>
56#include <linux/mutex.h>
57#include <linux/kthread.h>
58#include <linux/freezer.h>
59#include <linux/delay.h>
60
61#include <linux/nvram.h>
62#include <linux/proc_fs.h>
63#include <linux/sysfs.h>
64#include <linux/backlight.h>
65#include <linux/fb.h>
66#include <linux/platform_device.h>
67#include <linux/hwmon.h>
68#include <linux/hwmon-sysfs.h>
69#include <linux/input.h>
70#include <linux/leds.h>
71#include <linux/rfkill.h>
72#include <asm/uaccess.h>
73
74#include <linux/dmi.h>
75#include <linux/jiffies.h>
76#include <linux/workqueue.h>
77
78#include <acpi/acpi_drivers.h>
79
80#include <linux/pci_ids.h>
81
82
83/* ThinkPad CMOS commands */
84#define TP_CMOS_VOLUME_DOWN 0
85#define TP_CMOS_VOLUME_UP 1
86#define TP_CMOS_VOLUME_MUTE 2
87#define TP_CMOS_BRIGHTNESS_UP 4
88#define TP_CMOS_BRIGHTNESS_DOWN 5
89#define TP_CMOS_THINKLIGHT_ON 12
90#define TP_CMOS_THINKLIGHT_OFF 13
91
92/* NVRAM Addresses */
93enum tp_nvram_addr {
94 TP_NVRAM_ADDR_HK2 = 0x57,
95 TP_NVRAM_ADDR_THINKLIGHT = 0x58,
96 TP_NVRAM_ADDR_VIDEO = 0x59,
97 TP_NVRAM_ADDR_BRIGHTNESS = 0x5e,
98 TP_NVRAM_ADDR_MIXER = 0x60,
99};
100
101/* NVRAM bit masks */
102enum {
103 TP_NVRAM_MASK_HKT_THINKPAD = 0x08,
104 TP_NVRAM_MASK_HKT_ZOOM = 0x20,
105 TP_NVRAM_MASK_HKT_DISPLAY = 0x40,
106 TP_NVRAM_MASK_HKT_HIBERNATE = 0x80,
107 TP_NVRAM_MASK_THINKLIGHT = 0x10,
108 TP_NVRAM_MASK_HKT_DISPEXPND = 0x30,
109 TP_NVRAM_MASK_HKT_BRIGHTNESS = 0x20,
110 TP_NVRAM_MASK_LEVEL_BRIGHTNESS = 0x0f,
111 TP_NVRAM_POS_LEVEL_BRIGHTNESS = 0,
112 TP_NVRAM_MASK_MUTE = 0x40,
113 TP_NVRAM_MASK_HKT_VOLUME = 0x80,
114 TP_NVRAM_MASK_LEVEL_VOLUME = 0x0f,
115 TP_NVRAM_POS_LEVEL_VOLUME = 0,
116};
117
118/* ACPI HIDs */
119#define TPACPI_ACPI_HKEY_HID "IBM0068"
120
121/* Input IDs */
122#define TPACPI_HKEY_INPUT_PRODUCT 0x5054 /* "TP" */
123#define TPACPI_HKEY_INPUT_VERSION 0x4101
124
125
126/****************************************************************************
127 * Main driver
128 */
129
130#define TPACPI_NAME "thinkpad"
131#define TPACPI_DESC "ThinkPad ACPI Extras"
132#define TPACPI_FILE TPACPI_NAME "_acpi"
133#define TPACPI_URL "http://ibm-acpi.sf.net/"
134#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net"
135
136#define TPACPI_PROC_DIR "ibm"
137#define TPACPI_ACPI_EVENT_PREFIX "ibm"
138#define TPACPI_DRVR_NAME TPACPI_FILE
139#define TPACPI_DRVR_SHORTNAME "tpacpi"
140#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
141
142#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
143#define TPACPI_WORKQUEUE_NAME "ktpacpid"
144
145#define TPACPI_MAX_ACPI_ARGS 3
146
147/* rfkill switches */
148enum {
149 TPACPI_RFK_BLUETOOTH_SW_ID = 0,
150 TPACPI_RFK_WWAN_SW_ID,
151};
152
153/* Debugging */
154#define TPACPI_LOG TPACPI_FILE ": "
155#define TPACPI_ERR KERN_ERR TPACPI_LOG
156#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
157#define TPACPI_INFO KERN_INFO TPACPI_LOG
158#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG
159
160#define TPACPI_DBG_ALL 0xffff
161#define TPACPI_DBG_INIT 0x0001
162#define TPACPI_DBG_EXIT 0x0002
163#define dbg_printk(a_dbg_level, format, arg...) \
164 do { if (dbg_level & a_dbg_level) \
165 printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
166 } while (0)
167#ifdef CONFIG_THINKPAD_ACPI_DEBUG
168#define vdbg_printk(a_dbg_level, format, arg...) \
169 dbg_printk(a_dbg_level, format, ## arg)
170static const char *str_supported(int is_supported);
171#else
172#define vdbg_printk(a_dbg_level, format, arg...)
173#endif
174
175#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
176#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
177#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
178
179
180/****************************************************************************
181 * Driver-wide structs and misc. variables
182 */
183
184struct ibm_struct;
185
186struct tp_acpi_drv_struct {
187 const struct acpi_device_id *hid;
188 struct acpi_driver *driver;
189
190 void (*notify) (struct ibm_struct *, u32);
191 acpi_handle *handle;
192 u32 type;
193 struct acpi_device *device;
194};
195
196struct ibm_struct {
197 char *name;
198
199 int (*read) (char *);
200 int (*write) (char *);
201 void (*exit) (void);
202 void (*resume) (void);
203 void (*suspend) (pm_message_t state);
204
205 struct list_head all_drivers;
206
207 struct tp_acpi_drv_struct *acpi;
208
209 struct {
210 u8 acpi_driver_registered:1;
211 u8 acpi_notify_installed:1;
212 u8 proc_created:1;
213 u8 init_called:1;
214 u8 experimental:1;
215 } flags;
216};
217
218struct ibm_init_struct {
219 char param[32];
220
221 int (*init) (struct ibm_init_struct *);
222 struct ibm_struct *data;
223};
224
225static struct {
226#ifdef CONFIG_THINKPAD_ACPI_BAY
227 u32 bay_status:1;
228 u32 bay_eject:1;
229 u32 bay_status2:1;
230 u32 bay_eject2:1;
231#endif
232 u32 bluetooth:1;
233 u32 hotkey:1;
234 u32 hotkey_mask:1;
235 u32 hotkey_wlsw:1;
236 u32 hotkey_tablet:1;
237 u32 light:1;
238 u32 light_status:1;
239 u32 bright_16levels:1;
240 u32 bright_acpimode:1;
241 u32 wan:1;
242 u32 fan_ctrl_status_undef:1;
243 u32 input_device_registered:1;
244 u32 platform_drv_registered:1;
245 u32 platform_drv_attrs_registered:1;
246 u32 sensors_pdrv_registered:1;
247 u32 sensors_pdrv_attrs_registered:1;
248 u32 sensors_pdev_attrs_registered:1;
249 u32 hotkey_poll_active:1;
250} tp_features;
251
252static struct {
253 u16 hotkey_mask_ff:1;
254 u16 bright_cmos_ec_unsync:1;
255} tp_warned;
256
257struct thinkpad_id_data {
258 unsigned int vendor; /* ThinkPad vendor:
259 * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
260
261 char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
262 char *ec_version_str; /* Something like 1ZHT51WW-1.04a */
263
264 u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
265 u16 ec_model;
266
267 char *model_str; /* ThinkPad T43 */
268 char *nummodel_str; /* 9384A9C for a 9384-A9C model */
269};
270static struct thinkpad_id_data thinkpad_id;
271
272static enum {
273 TPACPI_LIFE_INIT = 0,
274 TPACPI_LIFE_RUNNING,
275 TPACPI_LIFE_EXITING,
276} tpacpi_lifecycle;
277
278static int experimental;
279static u32 dbg_level;
280
281static struct workqueue_struct *tpacpi_wq;
282
283/* Special LED class that can defer work */
284struct tpacpi_led_classdev {
285 struct led_classdev led_classdev;
286 struct work_struct work;
287 enum led_brightness new_brightness;
288 unsigned int led;
289};
290
291/****************************************************************************
292 ****************************************************************************
293 *
294 * ACPI Helpers and device model
295 *
296 ****************************************************************************
297 ****************************************************************************/
298
299/*************************************************************************
300 * ACPI basic handles
301 */
302
303static acpi_handle root_handle;
304
305#define TPACPI_HANDLE(object, parent, paths...) \
306 static acpi_handle object##_handle; \
307 static acpi_handle *object##_parent = &parent##_handle; \
308 static char *object##_path; \
309 static char *object##_paths[] = { paths }
310
311TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
312 "\\_SB.PCI.ISA.EC", /* 570 */
313 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
314 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
315 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
316 "\\_SB.PCI0.ICH3.EC0", /* R31 */
317 "\\_SB.PCI0.LPC.EC", /* all others */
318 );
319
320TPACPI_HANDLE(ecrd, ec, "ECRD"); /* 570 */
321TPACPI_HANDLE(ecwr, ec, "ECWR"); /* 570 */
322
323TPACPI_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, */
324 /* T4x, X31, X40 */
325 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
326 "\\CMS", /* R40, R40e */
327 ); /* all others */
328
329TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
330 "^HKEY", /* R30, R31 */
331 "HKEY", /* all others */
332 ); /* 570 */
333
334TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
335 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
336 "\\_SB.PCI0.VID0", /* 770e */
337 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
338 "\\_SB.PCI0.AGP.VID", /* all others */
339 ); /* R30, R31 */
340
341
342/*************************************************************************
343 * ACPI helpers
344 */
345
346static int acpi_evalf(acpi_handle handle,
347 void *res, char *method, char *fmt, ...)
348{
349 char *fmt0 = fmt;
350 struct acpi_object_list params;
351 union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS];
352 struct acpi_buffer result, *resultp;
353 union acpi_object out_obj;
354 acpi_status status;
355 va_list ap;
356 char res_type;
357 int success;
358 int quiet;
359
360 if (!*fmt) {
361 printk(TPACPI_ERR "acpi_evalf() called with empty format\n");
362 return 0;
363 }
364
365 if (*fmt == 'q') {
366 quiet = 1;
367 fmt++;
368 } else
369 quiet = 0;
370
371 res_type = *(fmt++);
372
373 params.count = 0;
374 params.pointer = &in_objs[0];
375
376 va_start(ap, fmt);
377 while (*fmt) {
378 char c = *(fmt++);
379 switch (c) {
380 case 'd': /* int */
381 in_objs[params.count].integer.value = va_arg(ap, int);
382 in_objs[params.count++].type = ACPI_TYPE_INTEGER;
383 break;
384 /* add more types as needed */
385 default:
386 printk(TPACPI_ERR "acpi_evalf() called "
387 "with invalid format character '%c'\n", c);
388 return 0;
389 }
390 }
391 va_end(ap);
392
393 if (res_type != 'v') {
394 result.length = sizeof(out_obj);
395 result.pointer = &out_obj;
396 resultp = &result;
397 } else
398 resultp = NULL;
399
400 status = acpi_evaluate_object(handle, method, &params, resultp);
401
402 switch (res_type) {
403 case 'd': /* int */
404 if (res)
405 *(int *)res = out_obj.integer.value;
406 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
407 break;
408 case 'v': /* void */
409 success = status == AE_OK;
410 break;
411 /* add more types as needed */
412 default:
413 printk(TPACPI_ERR "acpi_evalf() called "
414 "with invalid format character '%c'\n", res_type);
415 return 0;
416 }
417
418 if (!success && !quiet)
419 printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
420 method, fmt0, status);
421
422 return success;
423}
424
425static int acpi_ec_read(int i, u8 *p)
426{
427 int v;
428
429 if (ecrd_handle) {
430 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
431 return 0;
432 *p = v;
433 } else {
434 if (ec_read(i, p) < 0)
435 return 0;
436 }
437
438 return 1;
439}
440
441static int acpi_ec_write(int i, u8 v)
442{
443 if (ecwr_handle) {
444 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
445 return 0;
446 } else {
447 if (ec_write(i, v) < 0)
448 return 0;
449 }
450
451 return 1;
452}
453
454#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
455static int _sta(acpi_handle handle)
456{
457 int status;
458
459 if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
460 status = 0;
461
462 return status;
463}
464#endif
465
466static int issue_thinkpad_cmos_command(int cmos_cmd)
467{
468 if (!cmos_handle)
469 return -ENXIO;
470
471 if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
472 return -EIO;
473
474 return 0;
475}
476
477/*************************************************************************
478 * ACPI device model
479 */
480
481#define TPACPI_ACPIHANDLE_INIT(object) \
482 drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
483 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
484
485static void drv_acpi_handle_init(char *name,
486 acpi_handle *handle, acpi_handle parent,
487 char **paths, int num_paths, char **path)
488{
489 int i;
490 acpi_status status;
491
492 vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
493 name);
494
495 for (i = 0; i < num_paths; i++) {
496 status = acpi_get_handle(parent, paths[i], handle);
497 if (ACPI_SUCCESS(status)) {
498 *path = paths[i];
499 dbg_printk(TPACPI_DBG_INIT,
500 "Found ACPI handle %s for %s\n",
501 *path, name);
502 return;
503 }
504 }
505
506 vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
507 name);
508 *handle = NULL;
509}
510
511static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
512{
513 struct ibm_struct *ibm = data;
514
515 if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
516 return;
517
518 if (!ibm || !ibm->acpi || !ibm->acpi->notify)
519 return;
520
521 ibm->acpi->notify(ibm, event);
522}
523
524static int __init setup_acpi_notify(struct ibm_struct *ibm)
525{
526 acpi_status status;
527 int rc;
528
529 BUG_ON(!ibm->acpi);
530
531 if (!*ibm->acpi->handle)
532 return 0;
533
534 vdbg_printk(TPACPI_DBG_INIT,
535 "setting up ACPI notify for %s\n", ibm->name);
536
537 rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
538 if (rc < 0) {
539 printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n",
540 ibm->name, rc);
541 return -ENODEV;
542 }
543
544 ibm->acpi->device->driver_data = ibm;
545 sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
546 TPACPI_ACPI_EVENT_PREFIX,
547 ibm->name);
548
549 status = acpi_install_notify_handler(*ibm->acpi->handle,
550 ibm->acpi->type, dispatch_acpi_notify, ibm);
551 if (ACPI_FAILURE(status)) {
552 if (status == AE_ALREADY_EXISTS) {
553 printk(TPACPI_NOTICE
554 "another device driver is already "
555 "handling %s events\n", ibm->name);
556 } else {
557 printk(TPACPI_ERR
558 "acpi_install_notify_handler(%s) failed: %d\n",
559 ibm->name, status);
560 }
561 return -ENODEV;
562 }
563 ibm->flags.acpi_notify_installed = 1;
564 return 0;
565}
566
567static int __init tpacpi_device_add(struct acpi_device *device)
568{
569 return 0;
570}
571
572static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
573{
574 int rc;
575
576 dbg_printk(TPACPI_DBG_INIT,
577 "registering %s as an ACPI driver\n", ibm->name);
578
579 BUG_ON(!ibm->acpi);
580
581 ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
582 if (!ibm->acpi->driver) {
583 printk(TPACPI_ERR
584 "failed to allocate memory for ibm->acpi->driver\n");
585 return -ENOMEM;
586 }
587
588 sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name);
589 ibm->acpi->driver->ids = ibm->acpi->hid;
590
591 ibm->acpi->driver->ops.add = &tpacpi_device_add;
592
593 rc = acpi_bus_register_driver(ibm->acpi->driver);
594 if (rc < 0) {
595 printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n",
596 ibm->name, rc);
597 kfree(ibm->acpi->driver);
598 ibm->acpi->driver = NULL;
599 } else if (!rc)
600 ibm->flags.acpi_driver_registered = 1;
601
602 return rc;
603}
604
605
606/****************************************************************************
607 ****************************************************************************
608 *
609 * Procfs Helpers
610 *
611 ****************************************************************************
612 ****************************************************************************/
613
614static int dispatch_procfs_read(char *page, char **start, off_t off,
615 int count, int *eof, void *data)
616{
617 struct ibm_struct *ibm = data;
618 int len;
619
620 if (!ibm || !ibm->read)
621 return -EINVAL;
622
623 len = ibm->read(page);
624 if (len < 0)
625 return len;
626
627 if (len <= off + count)
628 *eof = 1;
629 *start = page + off;
630 len -= off;
631 if (len > count)
632 len = count;
633 if (len < 0)
634 len = 0;
635
636 return len;
637}
638
639static int dispatch_procfs_write(struct file *file,
640 const char __user *userbuf,
641 unsigned long count, void *data)
642{
643 struct ibm_struct *ibm = data;
644 char *kernbuf;
645 int ret;
646
647 if (!ibm || !ibm->write)
648 return -EINVAL;
649
650 kernbuf = kmalloc(count + 2, GFP_KERNEL);
651 if (!kernbuf)
652 return -ENOMEM;
653
654 if (copy_from_user(kernbuf, userbuf, count)) {
655 kfree(kernbuf);
656 return -EFAULT;
657 }
658
659 kernbuf[count] = 0;
660 strcat(kernbuf, ",");
661 ret = ibm->write(kernbuf);
662 if (ret == 0)
663 ret = count;
664
665 kfree(kernbuf);
666
667 return ret;
668}
669
670static char *next_cmd(char **cmds)
671{
672 char *start = *cmds;
673 char *end;
674
675 while ((end = strchr(start, ',')) && end == start)
676 start = end + 1;
677
678 if (!end)
679 return NULL;
680
681 *end = 0;
682 *cmds = end + 1;
683 return start;
684}
685
686
687/****************************************************************************
688 ****************************************************************************
689 *
690 * Device model: input, hwmon and platform
691 *
692 ****************************************************************************
693 ****************************************************************************/
694
695static struct platform_device *tpacpi_pdev;
696static struct platform_device *tpacpi_sensors_pdev;
697static struct device *tpacpi_hwmon;
698static struct input_dev *tpacpi_inputdev;
699static struct mutex tpacpi_inputdev_send_mutex;
700static LIST_HEAD(tpacpi_all_drivers);
701
702static int tpacpi_suspend_handler(struct platform_device *pdev,
703 pm_message_t state)
704{
705 struct ibm_struct *ibm, *itmp;
706
707 list_for_each_entry_safe(ibm, itmp,
708 &tpacpi_all_drivers,
709 all_drivers) {
710 if (ibm->suspend)
711 (ibm->suspend)(state);
712 }
713
714 return 0;
715}
716
717static int tpacpi_resume_handler(struct platform_device *pdev)
718{
719 struct ibm_struct *ibm, *itmp;
720
721 list_for_each_entry_safe(ibm, itmp,
722 &tpacpi_all_drivers,
723 all_drivers) {
724 if (ibm->resume)
725 (ibm->resume)();
726 }
727
728 return 0;
729}
730
731static struct platform_driver tpacpi_pdriver = {
732 .driver = {
733 .name = TPACPI_DRVR_NAME,
734 .owner = THIS_MODULE,
735 },
736 .suspend = tpacpi_suspend_handler,
737 .resume = tpacpi_resume_handler,
738};
739
740static struct platform_driver tpacpi_hwmon_pdriver = {
741 .driver = {
742 .name = TPACPI_HWMON_DRVR_NAME,
743 .owner = THIS_MODULE,
744 },
745};
746
747/*************************************************************************
748 * sysfs support helpers
749 */
750
751struct attribute_set {
752 unsigned int members, max_members;
753 struct attribute_group group;
754};
755
756struct attribute_set_obj {
757 struct attribute_set s;
758 struct attribute *a;
759} __attribute__((packed));
760
761static struct attribute_set *create_attr_set(unsigned int max_members,
762 const char *name)
763{
764 struct attribute_set_obj *sobj;
765
766 if (max_members == 0)
767 return NULL;
768
769 /* Allocates space for implicit NULL at the end too */
770 sobj = kzalloc(sizeof(struct attribute_set_obj) +
771 max_members * sizeof(struct attribute *),
772 GFP_KERNEL);
773 if (!sobj)
774 return NULL;
775 sobj->s.max_members = max_members;
776 sobj->s.group.attrs = &sobj->a;
777 sobj->s.group.name = name;
778
779 return &sobj->s;
780}
781
782#define destroy_attr_set(_set) \
783 kfree(_set);
784
785/* not multi-threaded safe, use it in a single thread per set */
786static int add_to_attr_set(struct attribute_set *s, struct attribute *attr)
787{
788 if (!s || !attr)
789 return -EINVAL;
790
791 if (s->members >= s->max_members)
792 return -ENOMEM;
793
794 s->group.attrs[s->members] = attr;
795 s->members++;
796
797 return 0;
798}
799
800static int add_many_to_attr_set(struct attribute_set *s,
801 struct attribute **attr,
802 unsigned int count)
803{
804 int i, res;
805
806 for (i = 0; i < count; i++) {
807 res = add_to_attr_set(s, attr[i]);
808 if (res)
809 return res;
810 }
811
812 return 0;
813}
814
815static void delete_attr_set(struct attribute_set *s, struct kobject *kobj)
816{
817 sysfs_remove_group(kobj, &s->group);
818 destroy_attr_set(s);
819}
820
821#define register_attr_set_with_sysfs(_attr_set, _kobj) \
822 sysfs_create_group(_kobj, &_attr_set->group)
823
824static int parse_strtoul(const char *buf,
825 unsigned long max, unsigned long *value)
826{
827 char *endp;
828
829 while (*buf && isspace(*buf))
830 buf++;
831 *value = simple_strtoul(buf, &endp, 0);
832 while (*endp && isspace(*endp))
833 endp++;
834 if (*endp || *value > max)
835 return -EINVAL;
836
837 return 0;
838}
839
840static void tpacpi_disable_brightness_delay(void)
841{
842 if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
843 printk(TPACPI_NOTICE
844 "ACPI backlight control delay disabled\n");
845}
846
847static int __init tpacpi_query_bcl_levels(acpi_handle handle)
848{
849 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
850 union acpi_object *obj;
851 int rc;
852
853 if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
854 obj = (union acpi_object *)buffer.pointer;
855 if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
856 printk(TPACPI_ERR "Unknown _BCL data, "
857 "please report this to %s\n", TPACPI_MAIL);
858 rc = 0;
859 } else {
860 rc = obj->package.count;
861 }
862 } else {
863 return 0;
864 }
865
866 kfree(buffer.pointer);
867 return rc;
868}
869
870static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
871 u32 lvl, void *context, void **rv)
872{
873 char name[ACPI_PATH_SEGMENT_LENGTH];
874 struct acpi_buffer buffer = { sizeof(name), &name };
875
876 if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
877 !strncmp("_BCL", name, sizeof(name) - 1)) {
878 BUG_ON(!rv || !*rv);
879 **(int **)rv = tpacpi_query_bcl_levels(handle);
880 return AE_CTRL_TERMINATE;
881 } else {
882 return AE_OK;
883 }
884}
885
886/*
887 * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
888 */
889static int __init tpacpi_check_std_acpi_brightness_support(void)
890{
891 int status;
892 int bcl_levels = 0;
893 void *bcl_ptr = &bcl_levels;
894
895 if (!vid_handle) {
896 TPACPI_ACPIHANDLE_INIT(vid);
897 }
898 if (!vid_handle)
899 return 0;
900
901 /*
902 * Search for a _BCL method, and execute it. This is safe on all
903 * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
904 * BIOS in ACPI backlight control mode. We do NOT have to care
905 * about calling the _BCL method in an enabled video device, any
906 * will do for our purposes.
907 */
908
909 status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
910 tpacpi_acpi_walk_find_bcl, NULL,
911 &bcl_ptr);
912
913 if (ACPI_SUCCESS(status) && bcl_levels > 2) {
914 tp_features.bright_acpimode = 1;
915 return (bcl_levels - 2);
916 }
917
918 return 0;
919}
920
921static int __init tpacpi_new_rfkill(const unsigned int id,
922 struct rfkill **rfk,
923 const enum rfkill_type rfktype,
924 const char *name,
925 int (*toggle_radio)(void *, enum rfkill_state),
926 int (*get_state)(void *, enum rfkill_state *))
927{
928 int res;
929 enum rfkill_state initial_state;
930
931 *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
932 if (!*rfk) {
933 printk(TPACPI_ERR
934 "failed to allocate memory for rfkill class\n");
935 return -ENOMEM;
936 }
937
938 (*rfk)->name = name;
939 (*rfk)->get_state = get_state;
940 (*rfk)->toggle_radio = toggle_radio;
941
942 if (!get_state(NULL, &initial_state))
943 (*rfk)->state = initial_state;
944
945 res = rfkill_register(*rfk);
946 if (res < 0) {
947 printk(TPACPI_ERR
948 "failed to register %s rfkill switch: %d\n",
949 name, res);
950 rfkill_free(*rfk);
951 *rfk = NULL;
952 return res;
953 }
954
955 return 0;
956}
957
958/*************************************************************************
959 * thinkpad-acpi driver attributes
960 */
961
962/* interface_version --------------------------------------------------- */
963static ssize_t tpacpi_driver_interface_version_show(
964 struct device_driver *drv,
965 char *buf)
966{
967 return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
968}
969
970static DRIVER_ATTR(interface_version, S_IRUGO,
971 tpacpi_driver_interface_version_show, NULL);
972
973/* debug_level --------------------------------------------------------- */
974static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
975 char *buf)
976{
977 return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
978}
979
980static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
981 const char *buf, size_t count)
982{
983 unsigned long t;
984
985 if (parse_strtoul(buf, 0xffff, &t))
986 return -EINVAL;
987
988 dbg_level = t;
989
990 return count;
991}
992
993static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
994 tpacpi_driver_debug_show, tpacpi_driver_debug_store);
995
996/* version ------------------------------------------------------------- */
997static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
998 char *buf)
999{
1000 return snprintf(buf, PAGE_SIZE, "%s v%s\n",
1001 TPACPI_DESC, TPACPI_VERSION);
1002}
1003
1004static DRIVER_ATTR(version, S_IRUGO,
1005 tpacpi_driver_version_show, NULL);
1006
1007/* --------------------------------------------------------------------- */
1008
1009static struct driver_attribute *tpacpi_driver_attributes[] = {
1010 &driver_attr_debug_level, &driver_attr_version,
1011 &driver_attr_interface_version,
1012};
1013
1014static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
1015{
1016 int i, res;
1017
1018 i = 0;
1019 res = 0;
1020 while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
1021 res = driver_create_file(drv, tpacpi_driver_attributes[i]);
1022 i++;
1023 }
1024
1025 return res;
1026}
1027
1028static void tpacpi_remove_driver_attributes(struct device_driver *drv)
1029{
1030 int i;
1031
1032 for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
1033 driver_remove_file(drv, tpacpi_driver_attributes[i]);
1034}
1035
1036/****************************************************************************
1037 ****************************************************************************
1038 *
1039 * Subdrivers
1040 *
1041 ****************************************************************************
1042 ****************************************************************************/
1043
1044/*************************************************************************
1045 * thinkpad-acpi init subdriver
1046 */
1047
1048static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
1049{
1050 printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
1051 printk(TPACPI_INFO "%s\n", TPACPI_URL);
1052
1053 printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n",
1054 (thinkpad_id.bios_version_str) ?
1055 thinkpad_id.bios_version_str : "unknown",
1056 (thinkpad_id.ec_version_str) ?
1057 thinkpad_id.ec_version_str : "unknown");
1058
1059 if (thinkpad_id.vendor && thinkpad_id.model_str)
1060 printk(TPACPI_INFO "%s %s, model %s\n",
1061 (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
1062 "IBM" : ((thinkpad_id.vendor ==
1063 PCI_VENDOR_ID_LENOVO) ?
1064 "Lenovo" : "Unknown vendor"),
1065 thinkpad_id.model_str,
1066 (thinkpad_id.nummodel_str) ?
1067 thinkpad_id.nummodel_str : "unknown");
1068
1069 return 0;
1070}
1071
1072static int thinkpad_acpi_driver_read(char *p)
1073{
1074 int len = 0;
1075
1076 len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC);
1077 len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION);
1078
1079 return len;
1080}
1081
1082static struct ibm_struct thinkpad_acpi_driver_data = {
1083 .name = "driver",
1084 .read = thinkpad_acpi_driver_read,
1085};
1086
1087/*************************************************************************
1088 * Hotkey subdriver
1089 */
1090
1091enum { /* hot key scan codes (derived from ACPI DSDT) */
1092 TP_ACPI_HOTKEYSCAN_FNF1 = 0,
1093 TP_ACPI_HOTKEYSCAN_FNF2,
1094 TP_ACPI_HOTKEYSCAN_FNF3,
1095 TP_ACPI_HOTKEYSCAN_FNF4,
1096 TP_ACPI_HOTKEYSCAN_FNF5,
1097 TP_ACPI_HOTKEYSCAN_FNF6,
1098 TP_ACPI_HOTKEYSCAN_FNF7,
1099 TP_ACPI_HOTKEYSCAN_FNF8,
1100 TP_ACPI_HOTKEYSCAN_FNF9,
1101 TP_ACPI_HOTKEYSCAN_FNF10,
1102 TP_ACPI_HOTKEYSCAN_FNF11,
1103 TP_ACPI_HOTKEYSCAN_FNF12,
1104 TP_ACPI_HOTKEYSCAN_FNBACKSPACE,
1105 TP_ACPI_HOTKEYSCAN_FNINSERT,
1106 TP_ACPI_HOTKEYSCAN_FNDELETE,
1107 TP_ACPI_HOTKEYSCAN_FNHOME,
1108 TP_ACPI_HOTKEYSCAN_FNEND,
1109 TP_ACPI_HOTKEYSCAN_FNPAGEUP,
1110 TP_ACPI_HOTKEYSCAN_FNPAGEDOWN,
1111 TP_ACPI_HOTKEYSCAN_FNSPACE,
1112 TP_ACPI_HOTKEYSCAN_VOLUMEUP,
1113 TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
1114 TP_ACPI_HOTKEYSCAN_MUTE,
1115 TP_ACPI_HOTKEYSCAN_THINKPAD,
1116};
1117
1118enum { /* Keys available through NVRAM polling */
1119 TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U,
1120 TPACPI_HKEY_NVRAM_GOOD_MASK = 0x00fb8000U,
1121};
1122
1123enum { /* Positions of some of the keys in hotkey masks */
1124 TP_ACPI_HKEY_DISPSWTCH_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF7,
1125 TP_ACPI_HKEY_DISPXPAND_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF8,
1126 TP_ACPI_HKEY_HIBERNATE_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
1127 TP_ACPI_HKEY_BRGHTUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
1128 TP_ACPI_HKEY_BRGHTDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
1129 TP_ACPI_HKEY_THNKLGHT_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
1130 TP_ACPI_HKEY_ZOOM_MASK = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
1131 TP_ACPI_HKEY_VOLUP_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
1132 TP_ACPI_HKEY_VOLDWN_MASK = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
1133 TP_ACPI_HKEY_MUTE_MASK = 1 << TP_ACPI_HOTKEYSCAN_MUTE,
1134 TP_ACPI_HKEY_THINKPAD_MASK = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD,
1135};
1136
1137enum { /* NVRAM to ACPI HKEY group map */
1138 TP_NVRAM_HKEY_GROUP_HK2 = TP_ACPI_HKEY_THINKPAD_MASK |
1139 TP_ACPI_HKEY_ZOOM_MASK |
1140 TP_ACPI_HKEY_DISPSWTCH_MASK |
1141 TP_ACPI_HKEY_HIBERNATE_MASK,
1142 TP_NVRAM_HKEY_GROUP_BRIGHTNESS = TP_ACPI_HKEY_BRGHTUP_MASK |
1143 TP_ACPI_HKEY_BRGHTDWN_MASK,
1144 TP_NVRAM_HKEY_GROUP_VOLUME = TP_ACPI_HKEY_VOLUP_MASK |
1145 TP_ACPI_HKEY_VOLDWN_MASK |
1146 TP_ACPI_HKEY_MUTE_MASK,
1147};
1148
1149#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1150struct tp_nvram_state {
1151 u16 thinkpad_toggle:1;
1152 u16 zoom_toggle:1;
1153 u16 display_toggle:1;
1154 u16 thinklight_toggle:1;
1155 u16 hibernate_toggle:1;
1156 u16 displayexp_toggle:1;
1157 u16 display_state:1;
1158 u16 brightness_toggle:1;
1159 u16 volume_toggle:1;
1160 u16 mute:1;
1161
1162 u8 brightness_level;
1163 u8 volume_level;
1164};
1165
1166static struct task_struct *tpacpi_hotkey_task;
1167static u32 hotkey_source_mask; /* bit mask 0=ACPI,1=NVRAM */
1168static int hotkey_poll_freq = 10; /* Hz */
1169static struct mutex hotkey_thread_mutex;
1170static struct mutex hotkey_thread_data_mutex;
1171static unsigned int hotkey_config_change;
1172
1173#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1174
1175#define hotkey_source_mask 0U
1176
1177#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1178
1179static struct mutex hotkey_mutex;
1180
1181static enum { /* Reasons for waking up */
1182 TP_ACPI_WAKEUP_NONE = 0, /* None or unknown */
1183 TP_ACPI_WAKEUP_BAYEJ, /* Bay ejection request */
1184 TP_ACPI_WAKEUP_UNDOCK, /* Undock request */
1185} hotkey_wakeup_reason;
1186
1187static int hotkey_autosleep_ack;
1188
1189static int hotkey_orig_status;
1190static u32 hotkey_orig_mask;
1191static u32 hotkey_all_mask;
1192static u32 hotkey_reserved_mask;
1193static u32 hotkey_mask;
1194
1195static unsigned int hotkey_report_mode;
1196
1197static u16 *hotkey_keycode_map;
1198
1199static struct attribute_set *hotkey_dev_attributes;
1200
1201#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1202#define HOTKEY_CONFIG_CRITICAL_START \
1203 do { \
1204 mutex_lock(&hotkey_thread_data_mutex); \
1205 hotkey_config_change++; \
1206 } while (0);
1207#define HOTKEY_CONFIG_CRITICAL_END \
1208 mutex_unlock(&hotkey_thread_data_mutex);
1209#else
1210#define HOTKEY_CONFIG_CRITICAL_START
1211#define HOTKEY_CONFIG_CRITICAL_END
1212#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1213
1214/* HKEY.MHKG() return bits */
1215#define TP_HOTKEY_TABLET_MASK (1 << 3)
1216
1217static int hotkey_get_wlsw(int *status)
1218{
1219 if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
1220 return -EIO;
1221 return 0;
1222}
1223
1224static int hotkey_get_tablet_mode(int *status)
1225{
1226 int s;
1227
1228 if (!acpi_evalf(hkey_handle, &s, "MHKG", "d"))
1229 return -EIO;
1230
1231 *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
1232 return 0;
1233}
1234
1235/*
1236 * Call with hotkey_mutex held
1237 */
1238static int hotkey_mask_get(void)
1239{
1240 u32 m = 0;
1241
1242 if (tp_features.hotkey_mask) {
1243 if (!acpi_evalf(hkey_handle, &m, "DHKN", "d"))
1244 return -EIO;
1245 }
1246 hotkey_mask = m | (hotkey_source_mask & hotkey_mask);
1247
1248 return 0;
1249}
1250
1251/*
1252 * Call with hotkey_mutex held
1253 */
1254static int hotkey_mask_set(u32 mask)
1255{
1256 int i;
1257 int rc = 0;
1258
1259 if (tp_features.hotkey_mask) {
1260 if (!tp_warned.hotkey_mask_ff &&
1261 (mask == 0xffff || mask == 0xffffff ||
1262 mask == 0xffffffff)) {
1263 tp_warned.hotkey_mask_ff = 1;
1264 printk(TPACPI_NOTICE
1265 "setting the hotkey mask to 0x%08x is likely "
1266 "not the best way to go about it\n", mask);
1267 printk(TPACPI_NOTICE
1268 "please consider using the driver defaults, "
1269 "and refer to up-to-date thinkpad-acpi "
1270 "documentation\n");
1271 }
1272
1273 HOTKEY_CONFIG_CRITICAL_START
1274 for (i = 0; i < 32; i++) {
1275 u32 m = 1 << i;
1276 /* enable in firmware mask only keys not in NVRAM
1277 * mode, but enable the key in the cached hotkey_mask
1278 * regardless of mode, or the key will end up
1279 * disabled by hotkey_mask_get() */
1280 if (!acpi_evalf(hkey_handle,
1281 NULL, "MHKM", "vdd", i + 1,
1282 !!((mask & ~hotkey_source_mask) & m))) {
1283 rc = -EIO;
1284 break;
1285 } else {
1286 hotkey_mask = (hotkey_mask & ~m) | (mask & m);
1287 }
1288 }
1289 HOTKEY_CONFIG_CRITICAL_END
1290
1291 /* hotkey_mask_get must be called unconditionally below */
1292 if (!hotkey_mask_get() && !rc &&
1293 (hotkey_mask & ~hotkey_source_mask) !=
1294 (mask & ~hotkey_source_mask)) {
1295 printk(TPACPI_NOTICE
1296 "requested hot key mask 0x%08x, but "
1297 "firmware forced it to 0x%08x\n",
1298 mask, hotkey_mask);
1299 }
1300 } else {
1301#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1302 HOTKEY_CONFIG_CRITICAL_START
1303 hotkey_mask = mask & hotkey_source_mask;
1304 HOTKEY_CONFIG_CRITICAL_END
1305 hotkey_mask_get();
1306 if (hotkey_mask != mask) {
1307 printk(TPACPI_NOTICE
1308 "requested hot key mask 0x%08x, "
1309 "forced to 0x%08x (NVRAM poll mask is "
1310 "0x%08x): no firmware mask support\n",
1311 mask, hotkey_mask, hotkey_source_mask);
1312 }
1313#else
1314 hotkey_mask_get();
1315 rc = -ENXIO;
1316#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1317 }
1318
1319 return rc;
1320}
1321
1322static int hotkey_status_get(int *status)
1323{
1324 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
1325 return -EIO;
1326
1327 return 0;
1328}
1329
1330static int hotkey_status_set(int status)
1331{
1332 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
1333 return -EIO;
1334
1335 return 0;
1336}
1337
1338static void tpacpi_input_send_tabletsw(void)
1339{
1340 int state;
1341
1342 if (tp_features.hotkey_tablet &&
1343 !hotkey_get_tablet_mode(&state)) {
1344 mutex_lock(&tpacpi_inputdev_send_mutex);
1345
1346 input_report_switch(tpacpi_inputdev,
1347 SW_TABLET_MODE, !!state);
1348 input_sync(tpacpi_inputdev);
1349
1350 mutex_unlock(&tpacpi_inputdev_send_mutex);
1351 }
1352}
1353
1354static void tpacpi_input_send_key(unsigned int scancode)
1355{
1356 unsigned int keycode;
1357
1358 keycode = hotkey_keycode_map[scancode];
1359
1360 if (keycode != KEY_RESERVED) {
1361 mutex_lock(&tpacpi_inputdev_send_mutex);
1362
1363 input_report_key(tpacpi_inputdev, keycode, 1);
1364 if (keycode == KEY_UNKNOWN)
1365 input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
1366 scancode);
1367 input_sync(tpacpi_inputdev);
1368
1369 input_report_key(tpacpi_inputdev, keycode, 0);
1370 if (keycode == KEY_UNKNOWN)
1371 input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
1372 scancode);
1373 input_sync(tpacpi_inputdev);
1374
1375 mutex_unlock(&tpacpi_inputdev_send_mutex);
1376 }
1377}
1378
1379#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1380static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
1381
1382static void tpacpi_hotkey_send_key(unsigned int scancode)
1383{
1384 tpacpi_input_send_key(scancode);
1385 if (hotkey_report_mode < 2) {
1386 acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
1387 0x80, 0x1001 + scancode);
1388 }
1389}
1390
1391static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m)
1392{
1393 u8 d;
1394
1395 if (m & TP_NVRAM_HKEY_GROUP_HK2) {
1396 d = nvram_read_byte(TP_NVRAM_ADDR_HK2);
1397 n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD);
1398 n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM);
1399 n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
1400 n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
1401 }
1402 if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
1403 d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
1404 n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
1405 }
1406 if (m & TP_ACPI_HKEY_DISPXPAND_MASK) {
1407 d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO);
1408 n->displayexp_toggle =
1409 !!(d & TP_NVRAM_MASK_HKT_DISPEXPND);
1410 }
1411 if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) {
1412 d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
1413 n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
1414 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
1415 n->brightness_toggle =
1416 !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS);
1417 }
1418 if (m & TP_NVRAM_HKEY_GROUP_VOLUME) {
1419 d = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
1420 n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME)
1421 >> TP_NVRAM_POS_LEVEL_VOLUME;
1422 n->mute = !!(d & TP_NVRAM_MASK_MUTE);
1423 n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME);
1424 }
1425}
1426
1427#define TPACPI_COMPARE_KEY(__scancode, __member) \
1428 do { \
1429 if ((mask & (1 << __scancode)) && \
1430 oldn->__member != newn->__member) \
1431 tpacpi_hotkey_send_key(__scancode); \
1432 } while (0)
1433
1434#define TPACPI_MAY_SEND_KEY(__scancode) \
1435 do { if (mask & (1 << __scancode)) \
1436 tpacpi_hotkey_send_key(__scancode); } while (0)
1437
1438static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
1439 struct tp_nvram_state *newn,
1440 u32 mask)
1441{
1442 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
1443 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
1444 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
1445 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle);
1446
1447 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle);
1448
1449 TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
1450
1451 /* handle volume */
1452 if (oldn->volume_toggle != newn->volume_toggle) {
1453 if (oldn->mute != newn->mute) {
1454 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
1455 }
1456 if (oldn->volume_level > newn->volume_level) {
1457 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
1458 } else if (oldn->volume_level < newn->volume_level) {
1459 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
1460 } else if (oldn->mute == newn->mute) {
1461 /* repeated key presses that didn't change state */
1462 if (newn->mute) {
1463 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
1464 } else if (newn->volume_level != 0) {
1465 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
1466 } else {
1467 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
1468 }
1469 }
1470 }
1471
1472 /* handle brightness */
1473 if (oldn->brightness_toggle != newn->brightness_toggle) {
1474 if (oldn->brightness_level < newn->brightness_level) {
1475 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
1476 } else if (oldn->brightness_level > newn->brightness_level) {
1477 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
1478 } else {
1479 /* repeated key presses that didn't change state */
1480 if (newn->brightness_level != 0) {
1481 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
1482 } else {
1483 TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
1484 }
1485 }
1486 }
1487}
1488
1489#undef TPACPI_COMPARE_KEY
1490#undef TPACPI_MAY_SEND_KEY
1491
1492static int hotkey_kthread(void *data)
1493{
1494 struct tp_nvram_state s[2];
1495 u32 mask;
1496 unsigned int si, so;
1497 unsigned long t;
1498 unsigned int change_detector, must_reset;
1499
1500 mutex_lock(&hotkey_thread_mutex);
1501
1502 if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
1503 goto exit;
1504
1505 set_freezable();
1506
1507 so = 0;
1508 si = 1;
1509 t = 0;
1510
1511 /* Initial state for compares */
1512 mutex_lock(&hotkey_thread_data_mutex);
1513 change_detector = hotkey_config_change;
1514 mask = hotkey_source_mask & hotkey_mask;
1515 mutex_unlock(&hotkey_thread_data_mutex);
1516 hotkey_read_nvram(&s[so], mask);
1517
1518 while (!kthread_should_stop() && hotkey_poll_freq) {
1519 if (t == 0)
1520 t = 1000/hotkey_poll_freq;
1521 t = msleep_interruptible(t);
1522 if (unlikely(kthread_should_stop()))
1523 break;
1524 must_reset = try_to_freeze();
1525 if (t > 0 && !must_reset)
1526 continue;
1527
1528 mutex_lock(&hotkey_thread_data_mutex);
1529 if (must_reset || hotkey_config_change != change_detector) {
1530 /* forget old state on thaw or config change */
1531 si = so;
1532 t = 0;
1533 change_detector = hotkey_config_change;
1534 }
1535 mask = hotkey_source_mask & hotkey_mask;
1536 mutex_unlock(&hotkey_thread_data_mutex);
1537
1538 if (likely(mask)) {
1539 hotkey_read_nvram(&s[si], mask);
1540 if (likely(si != so)) {
1541 hotkey_compare_and_issue_event(&s[so], &s[si],
1542 mask);
1543 }
1544 }
1545
1546 so = si;
1547 si ^= 1;
1548 }
1549
1550exit:
1551 mutex_unlock(&hotkey_thread_mutex);
1552 return 0;
1553}
1554
1555static void hotkey_poll_stop_sync(void)
1556{
1557 if (tpacpi_hotkey_task) {
1558 if (frozen(tpacpi_hotkey_task) ||
1559 freezing(tpacpi_hotkey_task))
1560 thaw_process(tpacpi_hotkey_task);
1561
1562 kthread_stop(tpacpi_hotkey_task);
1563 tpacpi_hotkey_task = NULL;
1564 mutex_lock(&hotkey_thread_mutex);
1565 /* at this point, the thread did exit */
1566 mutex_unlock(&hotkey_thread_mutex);
1567 }
1568}
1569
1570/* call with hotkey_mutex held */
1571static void hotkey_poll_setup(int may_warn)
1572{
1573 if ((hotkey_source_mask & hotkey_mask) != 0 &&
1574 hotkey_poll_freq > 0 &&
1575 (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) {
1576 if (!tpacpi_hotkey_task) {
1577 tpacpi_hotkey_task = kthread_run(hotkey_kthread,
1578 NULL, TPACPI_NVRAM_KTHREAD_NAME);
1579 if (IS_ERR(tpacpi_hotkey_task)) {
1580 tpacpi_hotkey_task = NULL;
1581 printk(TPACPI_ERR
1582 "could not create kernel thread "
1583 "for hotkey polling\n");
1584 }
1585 }
1586 } else {
1587 hotkey_poll_stop_sync();
1588 if (may_warn &&
1589 hotkey_source_mask != 0 && hotkey_poll_freq == 0) {
1590 printk(TPACPI_NOTICE
1591 "hot keys 0x%08x require polling, "
1592 "which is currently disabled\n",
1593 hotkey_source_mask);
1594 }
1595 }
1596}
1597
1598static void hotkey_poll_setup_safe(int may_warn)
1599{
1600 mutex_lock(&hotkey_mutex);
1601 hotkey_poll_setup(may_warn);
1602 mutex_unlock(&hotkey_mutex);
1603}
1604
1605#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1606
1607static void hotkey_poll_setup_safe(int __unused)
1608{
1609}
1610
1611#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1612
1613static int hotkey_inputdev_open(struct input_dev *dev)
1614{
1615 switch (tpacpi_lifecycle) {
1616 case TPACPI_LIFE_INIT:
1617 /*
1618 * hotkey_init will call hotkey_poll_setup_safe
1619 * at the appropriate moment
1620 */
1621 return 0;
1622 case TPACPI_LIFE_EXITING:
1623 return -EBUSY;
1624 case TPACPI_LIFE_RUNNING:
1625 hotkey_poll_setup_safe(0);
1626 return 0;
1627 }
1628
1629 /* Should only happen if tpacpi_lifecycle is corrupt */
1630 BUG();
1631 return -EBUSY;
1632}
1633
1634static void hotkey_inputdev_close(struct input_dev *dev)
1635{
1636 /* disable hotkey polling when possible */
1637 if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING)
1638 hotkey_poll_setup_safe(0);
1639}
1640
1641/* sysfs hotkey enable ------------------------------------------------- */
1642static ssize_t hotkey_enable_show(struct device *dev,
1643 struct device_attribute *attr,
1644 char *buf)
1645{
1646 int res, status;
1647
1648 res = hotkey_status_get(&status);
1649 if (res)
1650 return res;
1651
1652 return snprintf(buf, PAGE_SIZE, "%d\n", status);
1653}
1654
1655static ssize_t hotkey_enable_store(struct device *dev,
1656 struct device_attribute *attr,
1657 const char *buf, size_t count)
1658{
1659 unsigned long t;
1660 int res;
1661
1662 if (parse_strtoul(buf, 1, &t))
1663 return -EINVAL;
1664
1665 res = hotkey_status_set(t);
1666
1667 return (res) ? res : count;
1668}
1669
1670static struct device_attribute dev_attr_hotkey_enable =
1671 __ATTR(hotkey_enable, S_IWUSR | S_IRUGO,
1672 hotkey_enable_show, hotkey_enable_store);
1673
1674/* sysfs hotkey mask --------------------------------------------------- */
1675static ssize_t hotkey_mask_show(struct device *dev,
1676 struct device_attribute *attr,
1677 char *buf)
1678{
1679 int res;
1680
1681 if (mutex_lock_interruptible(&hotkey_mutex))
1682 return -ERESTARTSYS;
1683 res = hotkey_mask_get();
1684 mutex_unlock(&hotkey_mutex);
1685
1686 return (res)?
1687 res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask);
1688}
1689
1690static ssize_t hotkey_mask_store(struct device *dev,
1691 struct device_attribute *attr,
1692 const char *buf, size_t count)
1693{
1694 unsigned long t;
1695 int res;
1696
1697 if (parse_strtoul(buf, 0xffffffffUL, &t))
1698 return -EINVAL;
1699
1700 if (mutex_lock_interruptible(&hotkey_mutex))
1701 return -ERESTARTSYS;
1702
1703 res = hotkey_mask_set(t);
1704
1705#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1706 hotkey_poll_setup(1);
1707#endif
1708
1709 mutex_unlock(&hotkey_mutex);
1710
1711 return (res) ? res : count;
1712}
1713
1714static struct device_attribute dev_attr_hotkey_mask =
1715 __ATTR(hotkey_mask, S_IWUSR | S_IRUGO,
1716 hotkey_mask_show, hotkey_mask_store);
1717
1718/* sysfs hotkey bios_enabled ------------------------------------------- */
1719static ssize_t hotkey_bios_enabled_show(struct device *dev,
1720 struct device_attribute *attr,
1721 char *buf)
1722{
1723 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
1724}
1725
1726static struct device_attribute dev_attr_hotkey_bios_enabled =
1727 __ATTR(hotkey_bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
1728
1729/* sysfs hotkey bios_mask ---------------------------------------------- */
1730static ssize_t hotkey_bios_mask_show(struct device *dev,
1731 struct device_attribute *attr,
1732 char *buf)
1733{
1734 return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
1735}
1736
1737static struct device_attribute dev_attr_hotkey_bios_mask =
1738 __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
1739
1740/* sysfs hotkey all_mask ----------------------------------------------- */
1741static ssize_t hotkey_all_mask_show(struct device *dev,
1742 struct device_attribute *attr,
1743 char *buf)
1744{
1745 return snprintf(buf, PAGE_SIZE, "0x%08x\n",
1746 hotkey_all_mask | hotkey_source_mask);
1747}
1748
1749static struct device_attribute dev_attr_hotkey_all_mask =
1750 __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL);
1751
1752/* sysfs hotkey recommended_mask --------------------------------------- */
1753static ssize_t hotkey_recommended_mask_show(struct device *dev,
1754 struct device_attribute *attr,
1755 char *buf)
1756{
1757 return snprintf(buf, PAGE_SIZE, "0x%08x\n",
1758 (hotkey_all_mask | hotkey_source_mask)
1759 & ~hotkey_reserved_mask);
1760}
1761
1762static struct device_attribute dev_attr_hotkey_recommended_mask =
1763 __ATTR(hotkey_recommended_mask, S_IRUGO,
1764 hotkey_recommended_mask_show, NULL);
1765
1766#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1767
1768/* sysfs hotkey hotkey_source_mask ------------------------------------- */
1769static ssize_t hotkey_source_mask_show(struct device *dev,
1770 struct device_attribute *attr,
1771 char *buf)
1772{
1773 return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask);
1774}
1775
1776static ssize_t hotkey_source_mask_store(struct device *dev,
1777 struct device_attribute *attr,
1778 const char *buf, size_t count)
1779{
1780 unsigned long t;
1781
1782 if (parse_strtoul(buf, 0xffffffffUL, &t) ||
1783 ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0))
1784 return -EINVAL;
1785
1786 if (mutex_lock_interruptible(&hotkey_mutex))
1787 return -ERESTARTSYS;
1788
1789 HOTKEY_CONFIG_CRITICAL_START
1790 hotkey_source_mask = t;
1791 HOTKEY_CONFIG_CRITICAL_END
1792
1793 hotkey_poll_setup(1);
1794
1795 mutex_unlock(&hotkey_mutex);
1796
1797 return count;
1798}
1799
1800static struct device_attribute dev_attr_hotkey_source_mask =
1801 __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO,
1802 hotkey_source_mask_show, hotkey_source_mask_store);
1803
1804/* sysfs hotkey hotkey_poll_freq --------------------------------------- */
1805static ssize_t hotkey_poll_freq_show(struct device *dev,
1806 struct device_attribute *attr,
1807 char *buf)
1808{
1809 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq);
1810}
1811
1812static ssize_t hotkey_poll_freq_store(struct device *dev,
1813 struct device_attribute *attr,
1814 const char *buf, size_t count)
1815{
1816 unsigned long t;
1817
1818 if (parse_strtoul(buf, 25, &t))
1819 return -EINVAL;
1820
1821 if (mutex_lock_interruptible(&hotkey_mutex))
1822 return -ERESTARTSYS;
1823
1824 hotkey_poll_freq = t;
1825
1826 hotkey_poll_setup(1);
1827 mutex_unlock(&hotkey_mutex);
1828
1829 return count;
1830}
1831
1832static struct device_attribute dev_attr_hotkey_poll_freq =
1833 __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO,
1834 hotkey_poll_freq_show, hotkey_poll_freq_store);
1835
1836#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
1837
1838/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
1839static ssize_t hotkey_radio_sw_show(struct device *dev,
1840 struct device_attribute *attr,
1841 char *buf)
1842{
1843 int res, s;
1844 res = hotkey_get_wlsw(&s);
1845 if (res < 0)
1846 return res;
1847
1848 return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
1849}
1850
1851static struct device_attribute dev_attr_hotkey_radio_sw =
1852 __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
1853
1854static void hotkey_radio_sw_notify_change(void)
1855{
1856 if (tp_features.hotkey_wlsw)
1857 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1858 "hotkey_radio_sw");
1859}
1860
1861/* sysfs hotkey tablet mode (pollable) --------------------------------- */
1862static ssize_t hotkey_tablet_mode_show(struct device *dev,
1863 struct device_attribute *attr,
1864 char *buf)
1865{
1866 int res, s;
1867 res = hotkey_get_tablet_mode(&s);
1868 if (res < 0)
1869 return res;
1870
1871 return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
1872}
1873
1874static struct device_attribute dev_attr_hotkey_tablet_mode =
1875 __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL);
1876
1877static void hotkey_tablet_mode_notify_change(void)
1878{
1879 if (tp_features.hotkey_tablet)
1880 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1881 "hotkey_tablet_mode");
1882}
1883
1884/* sysfs hotkey report_mode -------------------------------------------- */
1885static ssize_t hotkey_report_mode_show(struct device *dev,
1886 struct device_attribute *attr,
1887 char *buf)
1888{
1889 return snprintf(buf, PAGE_SIZE, "%d\n",
1890 (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
1891}
1892
1893static struct device_attribute dev_attr_hotkey_report_mode =
1894 __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
1895
1896/* sysfs wakeup reason (pollable) -------------------------------------- */
1897static ssize_t hotkey_wakeup_reason_show(struct device *dev,
1898 struct device_attribute *attr,
1899 char *buf)
1900{
1901 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
1902}
1903
1904static struct device_attribute dev_attr_hotkey_wakeup_reason =
1905 __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
1906
1907static void hotkey_wakeup_reason_notify_change(void)
1908{
1909 if (tp_features.hotkey_mask)
1910 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1911 "wakeup_reason");
1912}
1913
1914/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
1915static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
1916 struct device_attribute *attr,
1917 char *buf)
1918{
1919 return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
1920}
1921
1922static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
1923 __ATTR(wakeup_hotunplug_complete, S_IRUGO,
1924 hotkey_wakeup_hotunplug_complete_show, NULL);
1925
1926static void hotkey_wakeup_hotunplug_complete_notify_change(void)
1927{
1928 if (tp_features.hotkey_mask)
1929 sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
1930 "wakeup_hotunplug_complete");
1931}
1932
1933/* --------------------------------------------------------------------- */
1934
1935static struct attribute *hotkey_attributes[] __initdata = {
1936 &dev_attr_hotkey_enable.attr,
1937 &dev_attr_hotkey_bios_enabled.attr,
1938 &dev_attr_hotkey_report_mode.attr,
1939#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1940 &dev_attr_hotkey_mask.attr,
1941 &dev_attr_hotkey_all_mask.attr,
1942 &dev_attr_hotkey_recommended_mask.attr,
1943 &dev_attr_hotkey_source_mask.attr,
1944 &dev_attr_hotkey_poll_freq.attr,
1945#endif
1946};
1947
1948static struct attribute *hotkey_mask_attributes[] __initdata = {
1949 &dev_attr_hotkey_bios_mask.attr,
1950#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1951 &dev_attr_hotkey_mask.attr,
1952 &dev_attr_hotkey_all_mask.attr,
1953 &dev_attr_hotkey_recommended_mask.attr,
1954#endif
1955 &dev_attr_hotkey_wakeup_reason.attr,
1956 &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
1957};
1958
1959static void bluetooth_update_rfk(void);
1960static void wan_update_rfk(void);
1961static void tpacpi_send_radiosw_update(void)
1962{
1963 int wlsw;
1964
1965 /* Sync these BEFORE sending any rfkill events */
1966 if (tp_features.bluetooth)
1967 bluetooth_update_rfk();
1968 if (tp_features.wan)
1969 wan_update_rfk();
1970
1971 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
1972 mutex_lock(&tpacpi_inputdev_send_mutex);
1973
1974 input_report_switch(tpacpi_inputdev,
1975 SW_RFKILL_ALL, !!wlsw);
1976 input_sync(tpacpi_inputdev);
1977
1978 mutex_unlock(&tpacpi_inputdev_send_mutex);
1979 }
1980 hotkey_radio_sw_notify_change();
1981}
1982
1983static void hotkey_exit(void)
1984{
1985#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
1986 hotkey_poll_stop_sync();
1987#endif
1988
1989 if (hotkey_dev_attributes)
1990 delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
1991
1992 kfree(hotkey_keycode_map);
1993
1994 if (tp_features.hotkey) {
1995 dbg_printk(TPACPI_DBG_EXIT,
1996 "restoring original hot key mask\n");
1997 /* no short-circuit boolean operator below! */
1998 if ((hotkey_mask_set(hotkey_orig_mask) |
1999 hotkey_status_set(hotkey_orig_status)) != 0)
2000 printk(TPACPI_ERR
2001 "failed to restore hot key mask "
2002 "to BIOS defaults\n");
2003 }
2004}
2005
2006static int __init hotkey_init(struct ibm_init_struct *iibm)
2007{
2008 /* Requirements for changing the default keymaps:
2009 *
2010 * 1. Many of the keys are mapped to KEY_RESERVED for very
2011 * good reasons. Do not change them unless you have deep
2012 * knowledge on the IBM and Lenovo ThinkPad firmware for
2013 * the various ThinkPad models. The driver behaves
2014 * differently for KEY_RESERVED: such keys have their
2015 * hot key mask *unset* in mask_recommended, and also
2016 * in the initial hot key mask programmed into the
2017 * firmware at driver load time, which means the firm-
2018 * ware may react very differently if you change them to
2019 * something else;
2020 *
2021 * 2. You must be subscribed to the linux-thinkpad and
2022 * ibm-acpi-devel mailing lists, and you should read the
2023 * list archives since 2007 if you want to change the
2024 * keymaps. This requirement exists so that you will
2025 * know the past history of problems with the thinkpad-
2026 * acpi driver keymaps, and also that you will be
2027 * listening to any bug reports;
2028 *
2029 * 3. Do not send thinkpad-acpi specific patches directly to
2030 * for merging, *ever*. Send them to the linux-acpi
2031 * mailinglist for comments. Merging is to be done only
2032 * through acpi-test and the ACPI maintainer.
2033 *
2034 * If the above is too much to ask, don't change the keymap.
2035 * Ask the thinkpad-acpi maintainer to do it, instead.
2036 */
2037 static u16 ibm_keycode_map[] __initdata = {
2038 /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
2039 KEY_FN_F1, KEY_FN_F2, KEY_COFFEE, KEY_SLEEP,
2040 KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
2041 KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
2042
2043 /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
2044 KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
2045 KEY_UNKNOWN, /* 0x0D: FN+INSERT */
2046 KEY_UNKNOWN, /* 0x0E: FN+DELETE */
2047
2048 /* brightness: firmware always reacts to them, unless
2049 * X.org did some tricks in the radeon BIOS scratch
2050 * registers of *some* models */
2051 KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
2052 KEY_RESERVED, /* 0x10: FN+END (brightness down) */
2053
2054 /* Thinklight: firmware always react to it */
2055 KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
2056
2057 KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
2058 KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
2059
2060 /* Volume: firmware always react to it and reprograms
2061 * the built-in *extra* mixer. Never map it to control
2062 * another mixer by default. */
2063 KEY_RESERVED, /* 0x14: VOLUME UP */
2064 KEY_RESERVED, /* 0x15: VOLUME DOWN */
2065 KEY_RESERVED, /* 0x16: MUTE */
2066
2067 KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
2068
2069 /* (assignments unknown, please report if found) */
2070 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2071 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2072 };
2073 static u16 lenovo_keycode_map[] __initdata = {
2074 /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
2075 KEY_FN_F1, KEY_COFFEE, KEY_BATTERY, KEY_SLEEP,
2076 KEY_WLAN, KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
2077 KEY_FN_F9, KEY_FN_F10, KEY_FN_F11, KEY_SUSPEND,
2078
2079 /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
2080 KEY_UNKNOWN, /* 0x0C: FN+BACKSPACE */
2081 KEY_UNKNOWN, /* 0x0D: FN+INSERT */
2082 KEY_UNKNOWN, /* 0x0E: FN+DELETE */
2083
2084 /* These either have to go through ACPI video, or
2085 * act like in the IBM ThinkPads, so don't ever
2086 * enable them by default */
2087 KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */
2088 KEY_RESERVED, /* 0x10: FN+END (brightness down) */
2089
2090 KEY_RESERVED, /* 0x11: FN+PGUP (thinklight toggle) */
2091
2092 KEY_UNKNOWN, /* 0x12: FN+PGDOWN */
2093 KEY_ZOOM, /* 0x13: FN+SPACE (zoom) */
2094
2095 /* Volume: z60/z61, T60 (BIOS version?): firmware always
2096 * react to it and reprograms the built-in *extra* mixer.
2097 * Never map it to control another mixer by default.
2098 *
2099 * T60?, T61, R60?, R61: firmware and EC tries to send
2100 * these over the regular keyboard, so these are no-ops,
2101 * but there are still weird bugs re. MUTE, so do not
2102 * change unless you get test reports from all Lenovo
2103 * models. May cause the BIOS to interfere with the
2104 * HDA mixer.
2105 */
2106 KEY_RESERVED, /* 0x14: VOLUME UP */
2107 KEY_RESERVED, /* 0x15: VOLUME DOWN */
2108 KEY_RESERVED, /* 0x16: MUTE */
2109
2110 KEY_VENDOR, /* 0x17: Thinkpad/AccessIBM/Lenovo */
2111
2112 /* (assignments unknown, please report if found) */
2113 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2114 KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
2115 };
2116
2117#define TPACPI_HOTKEY_MAP_LEN ARRAY_SIZE(ibm_keycode_map)
2118#define TPACPI_HOTKEY_MAP_SIZE sizeof(ibm_keycode_map)
2119#define TPACPI_HOTKEY_MAP_TYPESIZE sizeof(ibm_keycode_map[0])
2120
2121 int res, i;
2122 int status;
2123 int hkeyv;
2124
2125 vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
2126
2127 BUG_ON(!tpacpi_inputdev);
2128 BUG_ON(tpacpi_inputdev->open != NULL ||
2129 tpacpi_inputdev->close != NULL);
2130
2131 TPACPI_ACPIHANDLE_INIT(hkey);
2132 mutex_init(&hotkey_mutex);
2133
2134#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
2135 mutex_init(&hotkey_thread_mutex);
2136 mutex_init(&hotkey_thread_data_mutex);
2137#endif
2138
2139 /* hotkey not supported on 570 */
2140 tp_features.hotkey = hkey_handle != NULL;
2141
2142 vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
2143 str_supported(tp_features.hotkey));
2144
2145 if (!tp_features.hotkey)
2146 return 1;
2147
2148 tpacpi_disable_brightness_delay();
2149
2150 hotkey_dev_attributes = create_attr_set(13, NULL);
2151 if (!hotkey_dev_attributes)
2152 return -ENOMEM;
2153 res = add_many_to_attr_set(hotkey_dev_attributes,
2154 hotkey_attributes,
2155 ARRAY_SIZE(hotkey_attributes));
2156 if (res)
2157 goto err_exit;
2158
2159 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
2160 A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
2161 for HKEY interface version 0x100 */
2162 if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
2163 if ((hkeyv >> 8) != 1) {
2164 printk(TPACPI_ERR "unknown version of the "
2165 "HKEY interface: 0x%x\n", hkeyv);
2166 printk(TPACPI_ERR "please report this to %s\n",
2167 TPACPI_MAIL);
2168 } else {
2169 /*
2170 * MHKV 0x100 in A31, R40, R40e,
2171 * T4x, X31, and later
2172 */
2173 tp_features.hotkey_mask = 1;
2174 }
2175 }
2176
2177 vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
2178 str_supported(tp_features.hotkey_mask));
2179
2180 if (tp_features.hotkey_mask) {
2181 if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
2182 "MHKA", "qd")) {
2183 printk(TPACPI_ERR
2184 "missing MHKA handler, "
2185 "please report this to %s\n",
2186 TPACPI_MAIL);
2187 /* FN+F12, FN+F4, FN+F3 */
2188 hotkey_all_mask = 0x080cU;
2189 }
2190 }
2191
2192 /* hotkey_source_mask *must* be zero for
2193 * the first hotkey_mask_get */
2194 res = hotkey_status_get(&hotkey_orig_status);
2195 if (res)
2196 goto err_exit;
2197
2198 if (tp_features.hotkey_mask) {
2199 res = hotkey_mask_get();
2200 if (res)
2201 goto err_exit;
2202
2203 hotkey_orig_mask = hotkey_mask;
2204 res = add_many_to_attr_set(
2205 hotkey_dev_attributes,
2206 hotkey_mask_attributes,
2207 ARRAY_SIZE(hotkey_mask_attributes));
2208 if (res)
2209 goto err_exit;
2210 }
2211
2212#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
2213 if (tp_features.hotkey_mask) {
2214 hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
2215 & ~hotkey_all_mask;
2216 } else {
2217 hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
2218 }
2219
2220 vdbg_printk(TPACPI_DBG_INIT,
2221 "hotkey source mask 0x%08x, polling freq %d\n",
2222 hotkey_source_mask, hotkey_poll_freq);
2223#endif
2224
2225 /* Not all thinkpads have a hardware radio switch */
2226 if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
2227 tp_features.hotkey_wlsw = 1;
2228 printk(TPACPI_INFO
2229 "radio switch found; radios are %s\n",
2230 enabled(status, 0));
2231 }
2232 if (tp_features.hotkey_wlsw)
2233 res = add_to_attr_set(hotkey_dev_attributes,
2234 &dev_attr_hotkey_radio_sw.attr);
2235
2236 /* For X41t, X60t, X61t Tablets... */
2237 if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
2238 tp_features.hotkey_tablet = 1;
2239 printk(TPACPI_INFO
2240 "possible tablet mode switch found; "
2241 "ThinkPad in %s mode\n",
2242 (status & TP_HOTKEY_TABLET_MASK)?
2243 "tablet" : "laptop");
2244 res = add_to_attr_set(hotkey_dev_attributes,
2245 &dev_attr_hotkey_tablet_mode.attr);
2246 }
2247
2248 if (!res)
2249 res = register_attr_set_with_sysfs(
2250 hotkey_dev_attributes,
2251 &tpacpi_pdev->dev.kobj);
2252 if (res)
2253 goto err_exit;
2254
2255 /* Set up key map */
2256
2257 hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
2258 GFP_KERNEL);
2259 if (!hotkey_keycode_map) {
2260 printk(TPACPI_ERR
2261 "failed to allocate memory for key map\n");
2262 res = -ENOMEM;
2263 goto err_exit;
2264 }
2265
2266 if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
2267 dbg_printk(TPACPI_DBG_INIT,
2268 "using Lenovo default hot key map\n");
2269 memcpy(hotkey_keycode_map, &lenovo_keycode_map,
2270 TPACPI_HOTKEY_MAP_SIZE);
2271 } else {
2272 dbg_printk(TPACPI_DBG_INIT,
2273 "using IBM default hot key map\n");
2274 memcpy(hotkey_keycode_map, &ibm_keycode_map,
2275 TPACPI_HOTKEY_MAP_SIZE);
2276 }
2277
2278 set_bit(EV_KEY, tpacpi_inputdev->evbit);
2279 set_bit(EV_MSC, tpacpi_inputdev->evbit);
2280 set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
2281 tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
2282 tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
2283 tpacpi_inputdev->keycode = hotkey_keycode_map;
2284 for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
2285 if (hotkey_keycode_map[i] != KEY_RESERVED) {
2286 set_bit(hotkey_keycode_map[i],
2287 tpacpi_inputdev->keybit);
2288 } else {
2289 if (i < sizeof(hotkey_reserved_mask)*8)
2290 hotkey_reserved_mask |= 1 << i;
2291 }
2292 }
2293
2294 if (tp_features.hotkey_wlsw) {
2295 set_bit(EV_SW, tpacpi_inputdev->evbit);
2296 set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
2297 }
2298 if (tp_features.hotkey_tablet) {
2299 set_bit(EV_SW, tpacpi_inputdev->evbit);
2300 set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
2301 }
2302
2303 /* Do not issue duplicate brightness change events to
2304 * userspace */
2305 if (!tp_features.bright_acpimode)
2306 /* update bright_acpimode... */
2307 tpacpi_check_std_acpi_brightness_support();
2308
2309 if (tp_features.bright_acpimode) {
2310 printk(TPACPI_INFO
2311 "This ThinkPad has standard ACPI backlight "
2312 "brightness control, supported by the ACPI "
2313 "video driver\n");
2314 printk(TPACPI_NOTICE
2315 "Disabling thinkpad-acpi brightness events "
2316 "by default...\n");
2317
2318 /* The hotkey_reserved_mask change below is not
2319 * necessary while the keys are at KEY_RESERVED in the
2320 * default map, but better safe than sorry, leave it
2321 * here as a marker of what we have to do, especially
2322 * when we finally become able to set this at runtime
2323 * on response to X.org requests */
2324 hotkey_reserved_mask |=
2325 (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
2326 | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
2327 }
2328
2329 dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
2330 res = hotkey_status_set(1);
2331 if (res) {
2332 hotkey_exit();
2333 return res;
2334 }
2335 res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
2336 & ~hotkey_reserved_mask)
2337 | hotkey_orig_mask);
2338 if (res < 0 && res != -ENXIO) {
2339 hotkey_exit();
2340 return res;
2341 }
2342
2343 dbg_printk(TPACPI_DBG_INIT,
2344 "legacy hot key reporting over procfs %s\n",
2345 (hotkey_report_mode < 2) ?
2346 "enabled" : "disabled");
2347
2348 tpacpi_inputdev->open = &hotkey_inputdev_open;
2349 tpacpi_inputdev->close = &hotkey_inputdev_close;
2350
2351 hotkey_poll_setup_safe(1);
2352 tpacpi_send_radiosw_update();
2353 tpacpi_input_send_tabletsw();
2354
2355 return 0;
2356
2357err_exit:
2358 delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
2359 hotkey_dev_attributes = NULL;
2360
2361 return (res < 0)? res : 1;
2362}
2363
2364static void hotkey_notify(struct ibm_struct *ibm, u32 event)
2365{
2366 u32 hkey;
2367 unsigned int scancode;
2368 int send_acpi_ev;
2369 int ignore_acpi_ev;
2370 int unk_ev;
2371
2372 if (event != 0x80) {
2373 printk(TPACPI_ERR
2374 "unknown HKEY notification event %d\n", event);
2375 /* forward it to userspace, maybe it knows how to handle it */
2376 acpi_bus_generate_netlink_event(
2377 ibm->acpi->device->pnp.device_class,
2378 ibm->acpi->device->dev.bus_id,
2379 event, 0);
2380 return;
2381 }
2382
2383 while (1) {
2384 if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
2385 printk(TPACPI_ERR "failed to retrieve HKEY event\n");
2386 return;
2387 }
2388
2389 if (hkey == 0) {
2390 /* queue empty */
2391 return;
2392 }
2393
2394 send_acpi_ev = 1;
2395 ignore_acpi_ev = 0;
2396 unk_ev = 0;
2397
2398 switch (hkey >> 12) {
2399 case 1:
2400 /* 0x1000-0x1FFF: key presses */
2401 scancode = hkey & 0xfff;
2402 if (scancode > 0 && scancode < 0x21) {
2403 scancode--;
2404 if (!(hotkey_source_mask & (1 << scancode))) {
2405 tpacpi_input_send_key(scancode);
2406 send_acpi_ev = 0;
2407 } else {
2408 ignore_acpi_ev = 1;
2409 }
2410 } else {
2411 unk_ev = 1;
2412 }
2413 break;
2414 case 2:
2415 /* Wakeup reason */
2416 switch (hkey) {
2417 case 0x2304: /* suspend, undock */
2418 case 0x2404: /* hibernation, undock */
2419 hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
2420 ignore_acpi_ev = 1;
2421 break;
2422 case 0x2305: /* suspend, bay eject */
2423 case 0x2405: /* hibernation, bay eject */
2424 hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
2425 ignore_acpi_ev = 1;
2426 break;
2427 default:
2428 unk_ev = 1;
2429 }
2430 if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
2431 printk(TPACPI_INFO
2432 "woke up due to a hot-unplug "
2433 "request...\n");
2434 hotkey_wakeup_reason_notify_change();
2435 }
2436 break;
2437 case 3:
2438 /* bay-related wakeups */
2439 if (hkey == 0x3003) {
2440 hotkey_autosleep_ack = 1;
2441 printk(TPACPI_INFO
2442 "bay ejected\n");
2443 hotkey_wakeup_hotunplug_complete_notify_change();
2444 } else {
2445 unk_ev = 1;
2446 }
2447 break;
2448 case 4:
2449 /* dock-related wakeups */
2450 if (hkey == 0x4003) {
2451 hotkey_autosleep_ack = 1;
2452 printk(TPACPI_INFO
2453 "undocked\n");
2454 hotkey_wakeup_hotunplug_complete_notify_change();
2455 } else {
2456 unk_ev = 1;
2457 }
2458 break;
2459 case 5:
2460 /* 0x5000-0x5FFF: human interface helpers */
2461 switch (hkey) {
2462 case 0x5010: /* Lenovo new BIOS: brightness changed */
2463 case 0x500b: /* X61t: tablet pen inserted into bay */
2464 case 0x500c: /* X61t: tablet pen removed from bay */
2465 break;
2466 case 0x5009: /* X41t-X61t: swivel up (tablet mode) */
2467 case 0x500a: /* X41t-X61t: swivel down (normal mode) */
2468 tpacpi_input_send_tabletsw();
2469 hotkey_tablet_mode_notify_change();
2470 send_acpi_ev = 0;
2471 break;
2472 case 0x5001:
2473 case 0x5002:
2474 /* LID switch events. Do not propagate */
2475 ignore_acpi_ev = 1;
2476 break;
2477 default:
2478 unk_ev = 1;
2479 }
2480 break;
2481 case 7:
2482 /* 0x7000-0x7FFF: misc */
2483 if (tp_features.hotkey_wlsw && hkey == 0x7000) {
2484 tpacpi_send_radiosw_update();
2485 send_acpi_ev = 0;
2486 break;
2487 }
2488 /* fallthrough to default */
2489 default:
2490 unk_ev = 1;
2491 }
2492 if (unk_ev) {
2493 printk(TPACPI_NOTICE
2494 "unhandled HKEY event 0x%04x\n", hkey);
2495 }
2496
2497 /* Legacy events */
2498 if (!ignore_acpi_ev &&
2499 (send_acpi_ev || hotkey_report_mode < 2)) {
2500 acpi_bus_generate_proc_event(ibm->acpi->device,
2501 event, hkey);
2502 }
2503
2504 /* netlink events */
2505 if (!ignore_acpi_ev && send_acpi_ev) {
2506 acpi_bus_generate_netlink_event(
2507 ibm->acpi->device->pnp.device_class,
2508 ibm->acpi->device->dev.bus_id,
2509 event, hkey);
2510 }
2511 }
2512}
2513
2514static void hotkey_suspend(pm_message_t state)
2515{
2516 /* Do these on suspend, we get the events on early resume! */
2517 hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
2518 hotkey_autosleep_ack = 0;
2519}
2520
2521static void hotkey_resume(void)
2522{
2523 tpacpi_disable_brightness_delay();
2524
2525 if (hotkey_mask_get())
2526 printk(TPACPI_ERR
2527 "error while trying to read hot key mask "
2528 "from firmware\n");
2529 tpacpi_send_radiosw_update();
2530 hotkey_tablet_mode_notify_change();
2531 hotkey_wakeup_reason_notify_change();
2532 hotkey_wakeup_hotunplug_complete_notify_change();
2533 hotkey_poll_setup_safe(0);
2534}
2535
2536/* procfs -------------------------------------------------------------- */
2537static int hotkey_read(char *p)
2538{
2539 int res, status;
2540 int len = 0;
2541
2542 if (!tp_features.hotkey) {
2543 len += sprintf(p + len, "status:\t\tnot supported\n");
2544 return len;
2545 }
2546
2547 if (mutex_lock_interruptible(&hotkey_mutex))
2548 return -ERESTARTSYS;
2549 res = hotkey_status_get(&status);
2550 if (!res)
2551 res = hotkey_mask_get();
2552 mutex_unlock(&hotkey_mutex);
2553 if (res)
2554 return res;
2555
2556 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
2557 if (tp_features.hotkey_mask) {
2558 len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask);
2559 len += sprintf(p + len,
2560 "commands:\tenable, disable, reset, <mask>\n");
2561 } else {
2562 len += sprintf(p + len, "mask:\t\tnot supported\n");
2563 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
2564 }
2565
2566 return len;
2567}
2568
2569static int hotkey_write(char *buf)
2570{
2571 int res, status;
2572 u32 mask;
2573 char *cmd;
2574
2575 if (!tp_features.hotkey)
2576 return -ENODEV;
2577
2578 if (mutex_lock_interruptible(&hotkey_mutex))
2579 return -ERESTARTSYS;
2580
2581 status = -1;
2582 mask = hotkey_mask;
2583
2584 res = 0;
2585 while ((cmd = next_cmd(&buf))) {
2586 if (strlencmp(cmd, "enable") == 0) {
2587 status = 1;
2588 } else if (strlencmp(cmd, "disable") == 0) {
2589 status = 0;
2590 } else if (strlencmp(cmd, "reset") == 0) {
2591 status = hotkey_orig_status;
2592 mask = hotkey_orig_mask;
2593 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
2594 /* mask set */
2595 } else if (sscanf(cmd, "%x", &mask) == 1) {
2596 /* mask set */
2597 } else {
2598 res = -EINVAL;
2599 goto errexit;
2600 }
2601 }
2602 if (status != -1)
2603 res = hotkey_status_set(status);
2604
2605 if (!res && mask != hotkey_mask)
2606 res = hotkey_mask_set(mask);
2607
2608errexit:
2609 mutex_unlock(&hotkey_mutex);
2610 return res;
2611}
2612
2613static const struct acpi_device_id ibm_htk_device_ids[] = {
2614 {TPACPI_ACPI_HKEY_HID, 0},
2615 {"", 0},
2616};
2617
2618static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
2619 .hid = ibm_htk_device_ids,
2620 .notify = hotkey_notify,
2621 .handle = &hkey_handle,
2622 .type = ACPI_DEVICE_NOTIFY,
2623};
2624
2625static struct ibm_struct hotkey_driver_data = {
2626 .name = "hotkey",
2627 .read = hotkey_read,
2628 .write = hotkey_write,
2629 .exit = hotkey_exit,
2630 .resume = hotkey_resume,
2631 .suspend = hotkey_suspend,
2632 .acpi = &ibm_hotkey_acpidriver,
2633};
2634
2635/*************************************************************************
2636 * Bluetooth subdriver
2637 */
2638
2639enum {
2640 /* ACPI GBDC/SBDC bits */
2641 TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */
2642 TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */
2643 TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
2644};
2645
2646static struct rfkill *tpacpi_bluetooth_rfkill;
2647
2648static int bluetooth_get_radiosw(void)
2649{
2650 int status;
2651
2652 if (!tp_features.bluetooth)
2653 return -ENODEV;
2654
2655 /* WLSW overrides bluetooth in firmware/hardware, reflect that */
2656 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2657 return RFKILL_STATE_HARD_BLOCKED;
2658
2659 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2660 return -EIO;
2661
2662 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
2663 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2664}
2665
2666static void bluetooth_update_rfk(void)
2667{
2668 int status;
2669
2670 if (!tpacpi_bluetooth_rfkill)
2671 return;
2672
2673 status = bluetooth_get_radiosw();
2674 if (status < 0)
2675 return;
2676 rfkill_force_state(tpacpi_bluetooth_rfkill, status);
2677}
2678
2679static int bluetooth_set_radiosw(int radio_on, int update_rfk)
2680{
2681 int status;
2682
2683 if (!tp_features.bluetooth)
2684 return -ENODEV;
2685
2686 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2687 * reason to risk weird behaviour. */
2688 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2689 && radio_on)
2690 return -EPERM;
2691
2692 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
2693 return -EIO;
2694 if (radio_on)
2695 status |= TP_ACPI_BLUETOOTH_RADIOSSW;
2696 else
2697 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
2698 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
2699 return -EIO;
2700
2701 if (update_rfk)
2702 bluetooth_update_rfk();
2703
2704 return 0;
2705}
2706
2707/* sysfs bluetooth enable ---------------------------------------------- */
2708static ssize_t bluetooth_enable_show(struct device *dev,
2709 struct device_attribute *attr,
2710 char *buf)
2711{
2712 int status;
2713
2714 status = bluetooth_get_radiosw();
2715 if (status < 0)
2716 return status;
2717
2718 return snprintf(buf, PAGE_SIZE, "%d\n",
2719 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2720}
2721
2722static ssize_t bluetooth_enable_store(struct device *dev,
2723 struct device_attribute *attr,
2724 const char *buf, size_t count)
2725{
2726 unsigned long t;
2727 int res;
2728
2729 if (parse_strtoul(buf, 1, &t))
2730 return -EINVAL;
2731
2732 res = bluetooth_set_radiosw(t, 1);
2733
2734 return (res) ? res : count;
2735}
2736
2737static struct device_attribute dev_attr_bluetooth_enable =
2738 __ATTR(bluetooth_enable, S_IWUSR | S_IRUGO,
2739 bluetooth_enable_show, bluetooth_enable_store);
2740
2741/* --------------------------------------------------------------------- */
2742
2743static struct attribute *bluetooth_attributes[] = {
2744 &dev_attr_bluetooth_enable.attr,
2745 NULL
2746};
2747
2748static const struct attribute_group bluetooth_attr_group = {
2749 .attrs = bluetooth_attributes,
2750};
2751
2752static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
2753{
2754 int bts = bluetooth_get_radiosw();
2755
2756 if (bts < 0)
2757 return bts;
2758
2759 *state = bts;
2760 return 0;
2761}
2762
2763static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
2764{
2765 return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
2766}
2767
2768static void bluetooth_exit(void)
2769{
2770 if (tpacpi_bluetooth_rfkill)
2771 rfkill_unregister(tpacpi_bluetooth_rfkill);
2772
2773 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2774 &bluetooth_attr_group);
2775}
2776
2777static int __init bluetooth_init(struct ibm_init_struct *iibm)
2778{
2779 int res;
2780 int status = 0;
2781
2782 vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
2783
2784 TPACPI_ACPIHANDLE_INIT(hkey);
2785
2786 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
2787 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
2788 tp_features.bluetooth = hkey_handle &&
2789 acpi_evalf(hkey_handle, &status, "GBDC", "qd");
2790
2791 vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
2792 str_supported(tp_features.bluetooth),
2793 status);
2794
2795 if (tp_features.bluetooth &&
2796 !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
2797 /* no bluetooth hardware present in system */
2798 tp_features.bluetooth = 0;
2799 dbg_printk(TPACPI_DBG_INIT,
2800 "bluetooth hardware not installed\n");
2801 }
2802
2803 if (!tp_features.bluetooth)
2804 return 1;
2805
2806 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2807 &bluetooth_attr_group);
2808 if (res)
2809 return res;
2810
2811 res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
2812 &tpacpi_bluetooth_rfkill,
2813 RFKILL_TYPE_BLUETOOTH,
2814 "tpacpi_bluetooth_sw",
2815 tpacpi_bluetooth_rfk_set,
2816 tpacpi_bluetooth_rfk_get);
2817 if (res) {
2818 bluetooth_exit();
2819 return res;
2820 }
2821
2822 return 0;
2823}
2824
2825/* procfs -------------------------------------------------------------- */
2826static int bluetooth_read(char *p)
2827{
2828 int len = 0;
2829 int status = bluetooth_get_radiosw();
2830
2831 if (!tp_features.bluetooth)
2832 len += sprintf(p + len, "status:\t\tnot supported\n");
2833 else {
2834 len += sprintf(p + len, "status:\t\t%s\n",
2835 (status == RFKILL_STATE_UNBLOCKED) ?
2836 "enabled" : "disabled");
2837 len += sprintf(p + len, "commands:\tenable, disable\n");
2838 }
2839
2840 return len;
2841}
2842
2843static int bluetooth_write(char *buf)
2844{
2845 char *cmd;
2846
2847 if (!tp_features.bluetooth)
2848 return -ENODEV;
2849
2850 while ((cmd = next_cmd(&buf))) {
2851 if (strlencmp(cmd, "enable") == 0) {
2852 bluetooth_set_radiosw(1, 1);
2853 } else if (strlencmp(cmd, "disable") == 0) {
2854 bluetooth_set_radiosw(0, 1);
2855 } else
2856 return -EINVAL;
2857 }
2858
2859 return 0;
2860}
2861
2862static struct ibm_struct bluetooth_driver_data = {
2863 .name = "bluetooth",
2864 .read = bluetooth_read,
2865 .write = bluetooth_write,
2866 .exit = bluetooth_exit,
2867};
2868
2869/*************************************************************************
2870 * Wan subdriver
2871 */
2872
2873enum {
2874 /* ACPI GWAN/SWAN bits */
2875 TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */
2876 TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */
2877 TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
2878};
2879
2880static struct rfkill *tpacpi_wan_rfkill;
2881
2882static int wan_get_radiosw(void)
2883{
2884 int status;
2885
2886 if (!tp_features.wan)
2887 return -ENODEV;
2888
2889 /* WLSW overrides WWAN in firmware/hardware, reflect that */
2890 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
2891 return RFKILL_STATE_HARD_BLOCKED;
2892
2893 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2894 return -EIO;
2895
2896 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
2897 RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
2898}
2899
2900static void wan_update_rfk(void)
2901{
2902 int status;
2903
2904 if (!tpacpi_wan_rfkill)
2905 return;
2906
2907 status = wan_get_radiosw();
2908 if (status < 0)
2909 return;
2910 rfkill_force_state(tpacpi_wan_rfkill, status);
2911}
2912
2913static int wan_set_radiosw(int radio_on, int update_rfk)
2914{
2915 int status;
2916
2917 if (!tp_features.wan)
2918 return -ENODEV;
2919
2920 /* WLSW overrides bluetooth in firmware/hardware, but there is no
2921 * reason to risk weird behaviour. */
2922 if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
2923 && radio_on)
2924 return -EPERM;
2925
2926 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
2927 return -EIO;
2928 if (radio_on)
2929 status |= TP_ACPI_WANCARD_RADIOSSW;
2930 else
2931 status &= ~TP_ACPI_WANCARD_RADIOSSW;
2932 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
2933 return -EIO;
2934
2935 if (update_rfk)
2936 wan_update_rfk();
2937
2938 return 0;
2939}
2940
2941/* sysfs wan enable ---------------------------------------------------- */
2942static ssize_t wan_enable_show(struct device *dev,
2943 struct device_attribute *attr,
2944 char *buf)
2945{
2946 int status;
2947
2948 status = wan_get_radiosw();
2949 if (status < 0)
2950 return status;
2951
2952 return snprintf(buf, PAGE_SIZE, "%d\n",
2953 (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
2954}
2955
2956static ssize_t wan_enable_store(struct device *dev,
2957 struct device_attribute *attr,
2958 const char *buf, size_t count)
2959{
2960 unsigned long t;
2961 int res;
2962
2963 if (parse_strtoul(buf, 1, &t))
2964 return -EINVAL;
2965
2966 res = wan_set_radiosw(t, 1);
2967
2968 return (res) ? res : count;
2969}
2970
2971static struct device_attribute dev_attr_wan_enable =
2972 __ATTR(wwan_enable, S_IWUSR | S_IRUGO,
2973 wan_enable_show, wan_enable_store);
2974
2975/* --------------------------------------------------------------------- */
2976
2977static struct attribute *wan_attributes[] = {
2978 &dev_attr_wan_enable.attr,
2979 NULL
2980};
2981
2982static const struct attribute_group wan_attr_group = {
2983 .attrs = wan_attributes,
2984};
2985
2986static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
2987{
2988 int wans = wan_get_radiosw();
2989
2990 if (wans < 0)
2991 return wans;
2992
2993 *state = wans;
2994 return 0;
2995}
2996
2997static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
2998{
2999 return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
3000}
3001
3002static void wan_exit(void)
3003{
3004 if (tpacpi_wan_rfkill)
3005 rfkill_unregister(tpacpi_wan_rfkill);
3006
3007 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
3008 &wan_attr_group);
3009}
3010
3011static int __init wan_init(struct ibm_init_struct *iibm)
3012{
3013 int res;
3014 int status = 0;
3015
3016 vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
3017
3018 TPACPI_ACPIHANDLE_INIT(hkey);
3019
3020 tp_features.wan = hkey_handle &&
3021 acpi_evalf(hkey_handle, &status, "GWAN", "qd");
3022
3023 vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
3024 str_supported(tp_features.wan),
3025 status);
3026
3027 if (tp_features.wan &&
3028 !(status & TP_ACPI_WANCARD_HWPRESENT)) {
3029 /* no wan hardware present in system */
3030 tp_features.wan = 0;
3031 dbg_printk(TPACPI_DBG_INIT,
3032 "wan hardware not installed\n");
3033 }
3034
3035 if (!tp_features.wan)
3036 return 1;
3037
3038 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
3039 &wan_attr_group);
3040 if (res)
3041 return res;
3042
3043 res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
3044 &tpacpi_wan_rfkill,
3045 RFKILL_TYPE_WWAN,
3046 "tpacpi_wwan_sw",
3047 tpacpi_wan_rfk_set,
3048 tpacpi_wan_rfk_get);
3049 if (res) {
3050 wan_exit();
3051 return res;
3052 }
3053
3054 return 0;
3055}
3056
3057/* procfs -------------------------------------------------------------- */
3058static int wan_read(char *p)
3059{
3060 int len = 0;
3061 int status = wan_get_radiosw();
3062
3063 if (!tp_features.wan)
3064 len += sprintf(p + len, "status:\t\tnot supported\n");
3065 else {
3066 len += sprintf(p + len, "status:\t\t%s\n",
3067 (status == RFKILL_STATE_UNBLOCKED) ?
3068 "enabled" : "disabled");
3069 len += sprintf(p + len, "commands:\tenable, disable\n");
3070 }
3071
3072 return len;
3073}
3074
3075static int wan_write(char *buf)
3076{
3077 char *cmd;
3078
3079 if (!tp_features.wan)
3080 return -ENODEV;
3081
3082 while ((cmd = next_cmd(&buf))) {
3083 if (strlencmp(cmd, "enable") == 0) {
3084 wan_set_radiosw(1, 1);
3085 } else if (strlencmp(cmd, "disable") == 0) {
3086 wan_set_radiosw(0, 1);
3087 } else
3088 return -EINVAL;
3089 }
3090
3091 return 0;
3092}
3093
3094static struct ibm_struct wan_driver_data = {
3095 .name = "wan",
3096 .read = wan_read,
3097 .write = wan_write,
3098 .exit = wan_exit,
3099};
3100
3101/*************************************************************************
3102 * Video subdriver
3103 */
3104
3105#ifdef CONFIG_THINKPAD_ACPI_VIDEO
3106
3107enum video_access_mode {
3108 TPACPI_VIDEO_NONE = 0,
3109 TPACPI_VIDEO_570, /* 570 */
3110 TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */
3111 TPACPI_VIDEO_NEW, /* all others */
3112};
3113
3114enum { /* video status flags, based on VIDEO_570 */
3115 TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */
3116 TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */
3117 TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */
3118};
3119
3120enum { /* TPACPI_VIDEO_570 constants */
3121 TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */
3122 TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to
3123 * video_status_flags */
3124 TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */
3125 TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */
3126};
3127
3128static enum video_access_mode video_supported;
3129static int video_orig_autosw;
3130
3131static int video_autosw_get(void);
3132static int video_autosw_set(int enable);
3133
3134TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
3135
3136static int __init video_init(struct ibm_init_struct *iibm)
3137{
3138 int ivga;
3139
3140 vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
3141
3142 TPACPI_ACPIHANDLE_INIT(vid);
3143 TPACPI_ACPIHANDLE_INIT(vid2);
3144
3145 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
3146 /* G41, assume IVGA doesn't change */
3147 vid_handle = vid2_handle;
3148
3149 if (!vid_handle)
3150 /* video switching not supported on R30, R31 */
3151 video_supported = TPACPI_VIDEO_NONE;
3152 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
3153 /* 570 */
3154 video_supported = TPACPI_VIDEO_570;
3155 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
3156 /* 600e/x, 770e, 770x */
3157 video_supported = TPACPI_VIDEO_770;
3158 else
3159 /* all others */
3160 video_supported = TPACPI_VIDEO_NEW;
3161
3162 vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
3163 str_supported(video_supported != TPACPI_VIDEO_NONE),
3164 video_supported);
3165
3166 return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
3167}
3168
3169static void video_exit(void)
3170{
3171 dbg_printk(TPACPI_DBG_EXIT,
3172 "restoring original video autoswitch mode\n");
3173 if (video_autosw_set(video_orig_autosw))
3174 printk(TPACPI_ERR "error while trying to restore original "
3175 "video autoswitch mode\n");
3176}
3177
3178static int video_outputsw_get(void)
3179{
3180 int status = 0;
3181 int i;
3182
3183 switch (video_supported) {
3184 case TPACPI_VIDEO_570:
3185 if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
3186 TP_ACPI_VIDEO_570_PHSCMD))
3187 return -EIO;
3188 status = i & TP_ACPI_VIDEO_570_PHSMASK;
3189 break;
3190 case TPACPI_VIDEO_770:
3191 if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
3192 return -EIO;
3193 if (i)
3194 status |= TP_ACPI_VIDEO_S_LCD;
3195 if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
3196 return -EIO;
3197 if (i)
3198 status |= TP_ACPI_VIDEO_S_CRT;
3199 break;
3200 case TPACPI_VIDEO_NEW:
3201 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
3202 !acpi_evalf(NULL, &i, "\\VCDC", "d"))
3203 return -EIO;
3204 if (i)
3205 status |= TP_ACPI_VIDEO_S_CRT;
3206
3207 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
3208 !acpi_evalf(NULL, &i, "\\VCDL", "d"))
3209 return -EIO;
3210 if (i)
3211 status |= TP_ACPI_VIDEO_S_LCD;
3212 if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
3213 return -EIO;
3214 if (i)
3215 status |= TP_ACPI_VIDEO_S_DVI;
3216 break;
3217 default:
3218 return -ENOSYS;
3219 }
3220
3221 return status;
3222}
3223
3224static int video_outputsw_set(int status)
3225{
3226 int autosw;
3227 int res = 0;
3228
3229 switch (video_supported) {
3230 case TPACPI_VIDEO_570:
3231 res = acpi_evalf(NULL, NULL,
3232 "\\_SB.PHS2", "vdd",
3233 TP_ACPI_VIDEO_570_PHS2CMD,
3234 status | TP_ACPI_VIDEO_570_PHS2SET);
3235 break;
3236 case TPACPI_VIDEO_770:
3237 autosw = video_autosw_get();
3238 if (autosw < 0)
3239 return autosw;
3240
3241 res = video_autosw_set(1);
3242 if (res)
3243 return res;
3244 res = acpi_evalf(vid_handle, NULL,
3245 "ASWT", "vdd", status * 0x100, 0);
3246 if (!autosw && video_autosw_set(autosw)) {
3247 printk(TPACPI_ERR
3248 "video auto-switch left enabled due to error\n");
3249 return -EIO;
3250 }
3251 break;
3252 case TPACPI_VIDEO_NEW:
3253 res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
3254 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
3255 break;
3256 default:
3257 return -ENOSYS;
3258 }
3259
3260 return (res)? 0 : -EIO;
3261}
3262
3263static int video_autosw_get(void)
3264{
3265 int autosw = 0;
3266
3267 switch (video_supported) {
3268 case TPACPI_VIDEO_570:
3269 if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
3270 return -EIO;
3271 break;
3272 case TPACPI_VIDEO_770:
3273 case TPACPI_VIDEO_NEW:
3274 if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
3275 return -EIO;
3276 break;
3277 default:
3278 return -ENOSYS;
3279 }
3280
3281 return autosw & 1;
3282}
3283
3284static int video_autosw_set(int enable)
3285{
3286 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
3287 return -EIO;
3288 return 0;
3289}
3290
3291static int video_outputsw_cycle(void)
3292{
3293 int autosw = video_autosw_get();
3294 int res;
3295
3296 if (autosw < 0)
3297 return autosw;
3298
3299 switch (video_supported) {
3300 case TPACPI_VIDEO_570:
3301 res = video_autosw_set(1);
3302 if (res)
3303 return res;
3304 res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
3305 break;
3306 case TPACPI_VIDEO_770:
3307 case TPACPI_VIDEO_NEW:
3308 res = video_autosw_set(1);
3309 if (res)
3310 return res;
3311 res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
3312 break;
3313 default:
3314 return -ENOSYS;
3315 }
3316 if (!autosw && video_autosw_set(autosw)) {
3317 printk(TPACPI_ERR
3318 "video auto-switch left enabled due to error\n");
3319 return -EIO;
3320 }
3321
3322 return (res)? 0 : -EIO;
3323}
3324
3325static int video_expand_toggle(void)
3326{
3327 switch (video_supported) {
3328 case TPACPI_VIDEO_570:
3329 return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
3330 0 : -EIO;
3331 case TPACPI_VIDEO_770:
3332 return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
3333 0 : -EIO;
3334 case TPACPI_VIDEO_NEW:
3335 return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
3336 0 : -EIO;
3337 default:
3338 return -ENOSYS;
3339 }
3340 /* not reached */
3341}
3342
3343static int video_read(char *p)
3344{
3345 int status, autosw;
3346 int len = 0;
3347
3348 if (video_supported == TPACPI_VIDEO_NONE) {
3349 len += sprintf(p + len, "status:\t\tnot supported\n");
3350 return len;
3351 }
3352
3353 status = video_outputsw_get();
3354 if (status < 0)
3355 return status;
3356
3357 autosw = video_autosw_get();
3358 if (autosw < 0)
3359 return autosw;
3360
3361 len += sprintf(p + len, "status:\t\tsupported\n");
3362 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
3363 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
3364 if (video_supported == TPACPI_VIDEO_NEW)
3365 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
3366 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
3367 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
3368 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
3369 if (video_supported == TPACPI_VIDEO_NEW)
3370 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
3371 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
3372 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
3373
3374 return len;
3375}
3376
3377static int video_write(char *buf)
3378{
3379 char *cmd;
3380 int enable, disable, status;
3381 int res;
3382
3383 if (video_supported == TPACPI_VIDEO_NONE)
3384 return -ENODEV;
3385
3386 enable = 0;
3387 disable = 0;
3388
3389 while ((cmd = next_cmd(&buf))) {
3390 if (strlencmp(cmd, "lcd_enable") == 0) {
3391 enable |= TP_ACPI_VIDEO_S_LCD;
3392 } else if (strlencmp(cmd, "lcd_disable") == 0) {
3393 disable |= TP_ACPI_VIDEO_S_LCD;
3394 } else if (strlencmp(cmd, "crt_enable") == 0) {
3395 enable |= TP_ACPI_VIDEO_S_CRT;
3396 } else if (strlencmp(cmd, "crt_disable") == 0) {
3397 disable |= TP_ACPI_VIDEO_S_CRT;
3398 } else if (video_supported == TPACPI_VIDEO_NEW &&
3399 strlencmp(cmd, "dvi_enable") == 0) {
3400 enable |= TP_ACPI_VIDEO_S_DVI;
3401 } else if (video_supported == TPACPI_VIDEO_NEW &&
3402 strlencmp(cmd, "dvi_disable") == 0) {
3403 disable |= TP_ACPI_VIDEO_S_DVI;
3404 } else if (strlencmp(cmd, "auto_enable") == 0) {
3405 res = video_autosw_set(1);
3406 if (res)
3407 return res;
3408 } else if (strlencmp(cmd, "auto_disable") == 0) {
3409 res = video_autosw_set(0);
3410 if (res)
3411 return res;
3412 } else if (strlencmp(cmd, "video_switch") == 0) {
3413 res = video_outputsw_cycle();
3414 if (res)
3415 return res;
3416 } else if (strlencmp(cmd, "expand_toggle") == 0) {
3417 res = video_expand_toggle();
3418 if (res)
3419 return res;
3420 } else
3421 return -EINVAL;
3422 }
3423
3424 if (enable || disable) {
3425 status = video_outputsw_get();
3426 if (status < 0)
3427 return status;
3428 res = video_outputsw_set((status & ~disable) | enable);
3429 if (res)
3430 return res;
3431 }
3432
3433 return 0;
3434}
3435
3436static struct ibm_struct video_driver_data = {
3437 .name = "video",
3438 .read = video_read,
3439 .write = video_write,
3440 .exit = video_exit,
3441};
3442
3443#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
3444
3445/*************************************************************************
3446 * Light (thinklight) subdriver
3447 */
3448
3449TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
3450TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */
3451
3452static int light_get_status(void)
3453{
3454 int status = 0;
3455
3456 if (tp_features.light_status) {
3457 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
3458 return -EIO;
3459 return (!!status);
3460 }
3461
3462 return -ENXIO;
3463}
3464
3465static int light_set_status(int status)
3466{
3467 int rc;
3468
3469 if (tp_features.light) {
3470 if (cmos_handle) {
3471 rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
3472 (status)?
3473 TP_CMOS_THINKLIGHT_ON :
3474 TP_CMOS_THINKLIGHT_OFF);
3475 } else {
3476 rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
3477 (status)? 1 : 0);
3478 }
3479 return (rc)? 0 : -EIO;
3480 }
3481
3482 return -ENXIO;
3483}
3484
3485static void light_set_status_worker(struct work_struct *work)
3486{
3487 struct tpacpi_led_classdev *data =
3488 container_of(work, struct tpacpi_led_classdev, work);
3489
3490 if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
3491 light_set_status((data->new_brightness != LED_OFF));
3492}
3493
3494static void light_sysfs_set(struct led_classdev *led_cdev,
3495 enum led_brightness brightness)
3496{
3497 struct tpacpi_led_classdev *data =
3498 container_of(led_cdev,
3499 struct tpacpi_led_classdev,
3500 led_classdev);
3501 data->new_brightness = brightness;
3502 queue_work(tpacpi_wq, &data->work);
3503}
3504
3505static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
3506{
3507 return (light_get_status() == 1)? LED_FULL : LED_OFF;
3508}
3509
3510static struct tpacpi_led_classdev tpacpi_led_thinklight = {
3511 .led_classdev = {
3512 .name = "tpacpi::thinklight",
3513 .brightness_set = &light_sysfs_set,
3514 .brightness_get = &light_sysfs_get,
3515 }
3516};
3517
3518static int __init light_init(struct ibm_init_struct *iibm)
3519{
3520 int rc;
3521
3522 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
3523
3524 TPACPI_ACPIHANDLE_INIT(ledb);
3525 TPACPI_ACPIHANDLE_INIT(lght);
3526 TPACPI_ACPIHANDLE_INIT(cmos);
3527 INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
3528
3529 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
3530 tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
3531
3532 if (tp_features.light)
3533 /* light status not supported on
3534 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
3535 tp_features.light_status =
3536 acpi_evalf(ec_handle, NULL, "KBLT", "qv");
3537
3538 vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
3539 str_supported(tp_features.light),
3540 str_supported(tp_features.light_status));
3541
3542 if (!tp_features.light)
3543 return 1;
3544
3545 rc = led_classdev_register(&tpacpi_pdev->dev,
3546 &tpacpi_led_thinklight.led_classdev);
3547
3548 if (rc < 0) {
3549 tp_features.light = 0;
3550 tp_features.light_status = 0;
3551 } else {
3552 rc = 0;
3553 }
3554
3555 return rc;
3556}
3557
3558static void light_exit(void)
3559{
3560 led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
3561 if (work_pending(&tpacpi_led_thinklight.work))
3562 flush_workqueue(tpacpi_wq);
3563}
3564
3565static int light_read(char *p)
3566{
3567 int len = 0;
3568 int status;
3569
3570 if (!tp_features.light) {
3571 len += sprintf(p + len, "status:\t\tnot supported\n");
3572 } else if (!tp_features.light_status) {
3573 len += sprintf(p + len, "status:\t\tunknown\n");
3574 len += sprintf(p + len, "commands:\ton, off\n");
3575 } else {
3576 status = light_get_status();
3577 if (status < 0)
3578 return status;
3579 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
3580 len += sprintf(p + len, "commands:\ton, off\n");
3581 }
3582
3583 return len;
3584}
3585
3586static int light_write(char *buf)
3587{
3588 char *cmd;
3589 int newstatus = 0;
3590
3591 if (!tp_features.light)
3592 return -ENODEV;
3593
3594 while ((cmd = next_cmd(&buf))) {
3595 if (strlencmp(cmd, "on") == 0) {
3596 newstatus = 1;
3597 } else if (strlencmp(cmd, "off") == 0) {
3598 newstatus = 0;
3599 } else
3600 return -EINVAL;
3601 }
3602
3603 return light_set_status(newstatus);
3604}
3605
3606static struct ibm_struct light_driver_data = {
3607 .name = "light",
3608 .read = light_read,
3609 .write = light_write,
3610 .exit = light_exit,
3611};
3612
3613/*************************************************************************
3614 * Dock subdriver
3615 */
3616
3617#ifdef CONFIG_THINKPAD_ACPI_DOCK
3618
3619static void dock_notify(struct ibm_struct *ibm, u32 event);
3620static int dock_read(char *p);
3621static int dock_write(char *buf);
3622
3623TPACPI_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
3624 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
3625 "\\_SB.PCI0.PCI1.DOCK", /* all others */
3626 "\\_SB.PCI.ISA.SLCE", /* 570 */
3627 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
3628
3629/* don't list other alternatives as we install a notify handler on the 570 */
3630TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
3631
3632static const struct acpi_device_id ibm_pci_device_ids[] = {
3633 {PCI_ROOT_HID_STRING, 0},
3634 {"", 0},
3635};
3636
3637static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
3638 {
3639 .notify = dock_notify,
3640 .handle = &dock_handle,
3641 .type = ACPI_SYSTEM_NOTIFY,
3642 },
3643 {
3644 /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
3645 * We just use it to get notifications of dock hotplug
3646 * in very old thinkpads */
3647 .hid = ibm_pci_device_ids,
3648 .notify = dock_notify,
3649 .handle = &pci_handle,
3650 .type = ACPI_SYSTEM_NOTIFY,
3651 },
3652};
3653
3654static struct ibm_struct dock_driver_data[2] = {
3655 {
3656 .name = "dock",
3657 .read = dock_read,
3658 .write = dock_write,
3659 .acpi = &ibm_dock_acpidriver[0],
3660 },
3661 {
3662 .name = "dock",
3663 .acpi = &ibm_dock_acpidriver[1],
3664 },
3665};
3666
3667#define dock_docked() (_sta(dock_handle) & 1)
3668
3669static int __init dock_init(struct ibm_init_struct *iibm)
3670{
3671 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
3672
3673 TPACPI_ACPIHANDLE_INIT(dock);
3674
3675 vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
3676 str_supported(dock_handle != NULL));
3677
3678 return (dock_handle)? 0 : 1;
3679}
3680
3681static int __init dock_init2(struct ibm_init_struct *iibm)
3682{
3683 int dock2_needed;
3684
3685 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
3686
3687 if (dock_driver_data[0].flags.acpi_driver_registered &&
3688 dock_driver_data[0].flags.acpi_notify_installed) {
3689 TPACPI_ACPIHANDLE_INIT(pci);
3690 dock2_needed = (pci_handle != NULL);
3691 vdbg_printk(TPACPI_DBG_INIT,
3692 "dock PCI handler for the TP 570 is %s\n",
3693 str_supported(dock2_needed));
3694 } else {
3695 vdbg_printk(TPACPI_DBG_INIT,
3696 "dock subdriver part 2 not required\n");
3697 dock2_needed = 0;
3698 }
3699
3700 return (dock2_needed)? 0 : 1;
3701}
3702
3703static void dock_notify(struct ibm_struct *ibm, u32 event)
3704{
3705 int docked = dock_docked();
3706 int pci = ibm->acpi->hid && ibm->acpi->device &&
3707 acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
3708 int data;
3709
3710 if (event == 1 && !pci) /* 570 */
3711 data = 1; /* button */
3712 else if (event == 1 && pci) /* 570 */
3713 data = 3; /* dock */
3714 else if (event == 3 && docked)
3715 data = 1; /* button */
3716 else if (event == 3 && !docked)
3717 data = 2; /* undock */
3718 else if (event == 0 && docked)
3719 data = 3; /* dock */
3720 else {
3721 printk(TPACPI_ERR "unknown dock event %d, status %d\n",
3722 event, _sta(dock_handle));
3723 data = 0; /* unknown */
3724 }
3725 acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
3726 acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
3727 ibm->acpi->device->dev.bus_id,
3728 event, data);
3729}
3730
3731static int dock_read(char *p)
3732{
3733 int len = 0;
3734 int docked = dock_docked();
3735
3736 if (!dock_handle)
3737 len += sprintf(p + len, "status:\t\tnot supported\n");
3738 else if (!docked)
3739 len += sprintf(p + len, "status:\t\tundocked\n");
3740 else {
3741 len += sprintf(p + len, "status:\t\tdocked\n");
3742 len += sprintf(p + len, "commands:\tdock, undock\n");
3743 }
3744
3745 return len;
3746}
3747
3748static int dock_write(char *buf)
3749{
3750 char *cmd;
3751
3752 if (!dock_docked())
3753 return -ENODEV;
3754
3755 while ((cmd = next_cmd(&buf))) {
3756 if (strlencmp(cmd, "undock") == 0) {
3757 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
3758 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
3759 return -EIO;
3760 } else if (strlencmp(cmd, "dock") == 0) {
3761 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
3762 return -EIO;
3763 } else
3764 return -EINVAL;
3765 }
3766
3767 return 0;
3768}
3769
3770#endif /* CONFIG_THINKPAD_ACPI_DOCK */
3771
3772/*************************************************************************
3773 * Bay subdriver
3774 */
3775
3776#ifdef CONFIG_THINKPAD_ACPI_BAY
3777
3778TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
3779 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
3780 "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
3781 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
3782 ); /* A21e, R30, R31 */
3783TPACPI_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
3784 "_EJ0", /* all others */
3785 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
3786TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
3787 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
3788 ); /* all others */
3789TPACPI_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
3790 "_EJ0", /* 770x */
3791 ); /* all others */
3792
3793static int __init bay_init(struct ibm_init_struct *iibm)
3794{
3795 vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
3796
3797 TPACPI_ACPIHANDLE_INIT(bay);
3798 if (bay_handle)
3799 TPACPI_ACPIHANDLE_INIT(bay_ej);
3800 TPACPI_ACPIHANDLE_INIT(bay2);
3801 if (bay2_handle)
3802 TPACPI_ACPIHANDLE_INIT(bay2_ej);
3803
3804 tp_features.bay_status = bay_handle &&
3805 acpi_evalf(bay_handle, NULL, "_STA", "qv");
3806 tp_features.bay_status2 = bay2_handle &&
3807 acpi_evalf(bay2_handle, NULL, "_STA", "qv");
3808
3809 tp_features.bay_eject = bay_handle && bay_ej_handle &&
3810 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
3811 tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
3812 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
3813
3814 vdbg_printk(TPACPI_DBG_INIT,
3815 "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
3816 str_supported(tp_features.bay_status),
3817 str_supported(tp_features.bay_eject),
3818 str_supported(tp_features.bay_status2),
3819 str_supported(tp_features.bay_eject2));
3820
3821 return (tp_features.bay_status || tp_features.bay_eject ||
3822 tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
3823}
3824
3825static void bay_notify(struct ibm_struct *ibm, u32 event)
3826{
3827 acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
3828 acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
3829 ibm->acpi->device->dev.bus_id,
3830 event, 0);
3831}
3832
3833#define bay_occupied(b) (_sta(b##_handle) & 1)
3834
3835static int bay_read(char *p)
3836{
3837 int len = 0;
3838 int occupied = bay_occupied(bay);
3839 int occupied2 = bay_occupied(bay2);
3840 int eject, eject2;
3841
3842 len += sprintf(p + len, "status:\t\t%s\n",
3843 tp_features.bay_status ?
3844 (occupied ? "occupied" : "unoccupied") :
3845 "not supported");
3846 if (tp_features.bay_status2)
3847 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
3848 "occupied" : "unoccupied");
3849
3850 eject = tp_features.bay_eject && occupied;
3851 eject2 = tp_features.bay_eject2 && occupied2;
3852
3853 if (eject && eject2)
3854 len += sprintf(p + len, "commands:\teject, eject2\n");
3855 else if (eject)
3856 len += sprintf(p + len, "commands:\teject\n");
3857 else if (eject2)
3858 len += sprintf(p + len, "commands:\teject2\n");
3859
3860 return len;
3861}
3862
3863static int bay_write(char *buf)
3864{
3865 char *cmd;
3866
3867 if (!tp_features.bay_eject && !tp_features.bay_eject2)
3868 return -ENODEV;
3869
3870 while ((cmd = next_cmd(&buf))) {
3871 if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
3872 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
3873 return -EIO;
3874 } else if (tp_features.bay_eject2 &&
3875 strlencmp(cmd, "eject2") == 0) {
3876 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
3877 return -EIO;
3878 } else
3879 return -EINVAL;
3880 }
3881
3882 return 0;
3883}
3884
3885static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
3886 .notify = bay_notify,
3887 .handle = &bay_handle,
3888 .type = ACPI_SYSTEM_NOTIFY,
3889};
3890
3891static struct ibm_struct bay_driver_data = {
3892 .name = "bay",
3893 .read = bay_read,
3894 .write = bay_write,
3895 .acpi = &ibm_bay_acpidriver,
3896};
3897
3898#endif /* CONFIG_THINKPAD_ACPI_BAY */
3899
3900/*************************************************************************
3901 * CMOS subdriver
3902 */
3903
3904/* sysfs cmos_command -------------------------------------------------- */
3905static ssize_t cmos_command_store(struct device *dev,
3906 struct device_attribute *attr,
3907 const char *buf, size_t count)
3908{
3909 unsigned long cmos_cmd;
3910 int res;
3911
3912 if (parse_strtoul(buf, 21, &cmos_cmd))
3913 return -EINVAL;
3914
3915 res = issue_thinkpad_cmos_command(cmos_cmd);
3916 return (res)? res : count;
3917}
3918
3919static struct device_attribute dev_attr_cmos_command =
3920 __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
3921
3922/* --------------------------------------------------------------------- */
3923
3924static int __init cmos_init(struct ibm_init_struct *iibm)
3925{
3926 int res;
3927
3928 vdbg_printk(TPACPI_DBG_INIT,
3929 "initializing cmos commands subdriver\n");
3930
3931 TPACPI_ACPIHANDLE_INIT(cmos);
3932
3933 vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
3934 str_supported(cmos_handle != NULL));
3935
3936 res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
3937 if (res)
3938 return res;
3939
3940 return (cmos_handle)? 0 : 1;
3941}
3942
3943static void cmos_exit(void)
3944{
3945 device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
3946}
3947
3948static int cmos_read(char *p)
3949{
3950 int len = 0;
3951
3952 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
3953 R30, R31, T20-22, X20-21 */
3954 if (!cmos_handle)
3955 len += sprintf(p + len, "status:\t\tnot supported\n");
3956 else {
3957 len += sprintf(p + len, "status:\t\tsupported\n");
3958 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
3959 }
3960
3961 return len;
3962}
3963
3964static int cmos_write(char *buf)
3965{
3966 char *cmd;
3967 int cmos_cmd, res;
3968
3969 while ((cmd = next_cmd(&buf))) {
3970 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
3971 cmos_cmd >= 0 && cmos_cmd <= 21) {
3972 /* cmos_cmd set */
3973 } else
3974 return -EINVAL;
3975
3976 res = issue_thinkpad_cmos_command(cmos_cmd);
3977 if (res)
3978 return res;
3979 }
3980
3981 return 0;
3982}
3983
3984static struct ibm_struct cmos_driver_data = {
3985 .name = "cmos",
3986 .read = cmos_read,
3987 .write = cmos_write,
3988 .exit = cmos_exit,
3989};
3990
3991/*************************************************************************
3992 * LED subdriver
3993 */
3994
3995enum led_access_mode {
3996 TPACPI_LED_NONE = 0,
3997 TPACPI_LED_570, /* 570 */
3998 TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
3999 TPACPI_LED_NEW, /* all others */
4000};
4001
4002enum { /* For TPACPI_LED_OLD */
4003 TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */
4004 TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */
4005 TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */
4006};
4007
4008enum led_status_t {
4009 TPACPI_LED_OFF = 0,
4010 TPACPI_LED_ON,
4011 TPACPI_LED_BLINK,
4012};
4013
4014static enum led_access_mode led_supported;
4015
4016TPACPI_HANDLE(led, ec, "SLED", /* 570 */
4017 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, */
4018 /* T20-22, X20-21 */
4019 "LED", /* all others */
4020 ); /* R30, R31 */
4021
4022#define TPACPI_LED_NUMLEDS 8
4023static struct tpacpi_led_classdev *tpacpi_leds;
4024static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
4025static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
4026 /* there's a limit of 19 chars + NULL before 2.6.26 */
4027 "tpacpi::power",
4028 "tpacpi:orange:batt",
4029 "tpacpi:green:batt",
4030 "tpacpi::dock_active",
4031 "tpacpi::bay_active",
4032 "tpacpi::dock_batt",
4033 "tpacpi::unknown_led",
4034 "tpacpi::standby",
4035};
4036
4037static int led_get_status(const unsigned int led)
4038{
4039 int status;
4040 enum led_status_t led_s;
4041
4042 switch (led_supported) {
4043 case TPACPI_LED_570:
4044 if (!acpi_evalf(ec_handle,
4045 &status, "GLED", "dd", 1 << led))
4046 return -EIO;
4047 led_s = (status == 0)?
4048 TPACPI_LED_OFF :
4049 ((status == 1)?
4050 TPACPI_LED_ON :
4051 TPACPI_LED_BLINK);
4052 tpacpi_led_state_cache[led] = led_s;
4053 return led_s;
4054 default:
4055 return -ENXIO;
4056 }
4057
4058 /* not reached */
4059}
4060
4061static int led_set_status(const unsigned int led,
4062 const enum led_status_t ledstatus)
4063{
4064 /* off, on, blink. Index is led_status_t */
4065 static const unsigned int led_sled_arg1[] = { 0, 1, 3 };
4066 static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 };
4067
4068 int rc = 0;
4069
4070 switch (led_supported) {
4071 case TPACPI_LED_570:
4072 /* 570 */
4073 if (led > 7)
4074 return -EINVAL;
4075 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4076 (1 << led), led_sled_arg1[ledstatus]))
4077 rc = -EIO;
4078 break;
4079 case TPACPI_LED_OLD:
4080 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
4081 if (led > 7)
4082 return -EINVAL;
4083 rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
4084 if (rc >= 0)
4085 rc = ec_write(TPACPI_LED_EC_HLBL,
4086 (ledstatus == TPACPI_LED_BLINK) << led);
4087 if (rc >= 0)
4088 rc = ec_write(TPACPI_LED_EC_HLCL,
4089 (ledstatus != TPACPI_LED_OFF) << led);
4090 break;
4091 case TPACPI_LED_NEW:
4092 /* all others */
4093 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4094 led, led_led_arg1[ledstatus]))
4095 rc = -EIO;
4096 break;
4097 default:
4098 rc = -ENXIO;
4099 }
4100
4101 if (!rc)
4102 tpacpi_led_state_cache[led] = ledstatus;
4103
4104 return rc;
4105}
4106
4107static void led_sysfs_set_status(unsigned int led,
4108 enum led_brightness brightness)
4109{
4110 led_set_status(led,
4111 (brightness == LED_OFF) ?
4112 TPACPI_LED_OFF :
4113 (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
4114 TPACPI_LED_BLINK : TPACPI_LED_ON);
4115}
4116
4117static void led_set_status_worker(struct work_struct *work)
4118{
4119 struct tpacpi_led_classdev *data =
4120 container_of(work, struct tpacpi_led_classdev, work);
4121
4122 if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
4123 led_sysfs_set_status(data->led, data->new_brightness);
4124}
4125
4126static void led_sysfs_set(struct led_classdev *led_cdev,
4127 enum led_brightness brightness)
4128{
4129 struct tpacpi_led_classdev *data = container_of(led_cdev,
4130 struct tpacpi_led_classdev, led_classdev);
4131
4132 data->new_brightness = brightness;
4133 queue_work(tpacpi_wq, &data->work);
4134}
4135
4136static int led_sysfs_blink_set(struct led_classdev *led_cdev,
4137 unsigned long *delay_on, unsigned long *delay_off)
4138{
4139 struct tpacpi_led_classdev *data = container_of(led_cdev,
4140 struct tpacpi_led_classdev, led_classdev);
4141
4142 /* Can we choose the flash rate? */
4143 if (*delay_on == 0 && *delay_off == 0) {
4144 /* yes. set them to the hardware blink rate (1 Hz) */
4145 *delay_on = 500; /* ms */
4146 *delay_off = 500; /* ms */
4147 } else if ((*delay_on != 500) || (*delay_off != 500))
4148 return -EINVAL;
4149
4150 data->new_brightness = TPACPI_LED_BLINK;
4151 queue_work(tpacpi_wq, &data->work);
4152
4153 return 0;
4154}
4155
4156static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
4157{
4158 int rc;
4159
4160 struct tpacpi_led_classdev *data = container_of(led_cdev,
4161 struct tpacpi_led_classdev, led_classdev);
4162
4163 rc = led_get_status(data->led);
4164
4165 if (rc == TPACPI_LED_OFF || rc < 0)
4166 rc = LED_OFF; /* no error handling in led class :( */
4167 else
4168 rc = LED_FULL;
4169
4170 return rc;
4171}
4172
4173static void led_exit(void)
4174{
4175 unsigned int i;
4176
4177 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4178 if (tpacpi_leds[i].led_classdev.name)
4179 led_classdev_unregister(&tpacpi_leds[i].led_classdev);
4180 }
4181
4182 kfree(tpacpi_leds);
4183}
4184
4185static int __init led_init(struct ibm_init_struct *iibm)
4186{
4187 unsigned int i;
4188 int rc;
4189
4190 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
4191
4192 TPACPI_ACPIHANDLE_INIT(led);
4193
4194 if (!led_handle)
4195 /* led not supported on R30, R31 */
4196 led_supported = TPACPI_LED_NONE;
4197 else if (strlencmp(led_path, "SLED") == 0)
4198 /* 570 */
4199 led_supported = TPACPI_LED_570;
4200 else if (strlencmp(led_path, "SYSL") == 0)
4201 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
4202 led_supported = TPACPI_LED_OLD;
4203 else
4204 /* all others */
4205 led_supported = TPACPI_LED_NEW;
4206
4207 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
4208 str_supported(led_supported), led_supported);
4209
4210 tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
4211 GFP_KERNEL);
4212 if (!tpacpi_leds) {
4213 printk(TPACPI_ERR "Out of memory for LED data\n");
4214 return -ENOMEM;
4215 }
4216
4217 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4218 tpacpi_leds[i].led = i;
4219
4220 tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
4221 tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
4222 if (led_supported == TPACPI_LED_570)
4223 tpacpi_leds[i].led_classdev.brightness_get =
4224 &led_sysfs_get;
4225
4226 tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
4227
4228 INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
4229
4230 rc = led_classdev_register(&tpacpi_pdev->dev,
4231 &tpacpi_leds[i].led_classdev);
4232 if (rc < 0) {
4233 tpacpi_leds[i].led_classdev.name = NULL;
4234 led_exit();
4235 return rc;
4236 }
4237 }
4238
4239 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
4240}
4241
4242#define str_led_status(s) \
4243 ((s) == TPACPI_LED_OFF ? "off" : \
4244 ((s) == TPACPI_LED_ON ? "on" : "blinking"))
4245
4246static int led_read(char *p)
4247{
4248 int len = 0;
4249
4250 if (!led_supported) {
4251 len += sprintf(p + len, "status:\t\tnot supported\n");
4252 return len;
4253 }
4254 len += sprintf(p + len, "status:\t\tsupported\n");
4255
4256 if (led_supported == TPACPI_LED_570) {
4257 /* 570 */
4258 int i, status;
4259 for (i = 0; i < 8; i++) {
4260 status = led_get_status(i);
4261 if (status < 0)
4262 return -EIO;
4263 len += sprintf(p + len, "%d:\t\t%s\n",
4264 i, str_led_status(status));
4265 }
4266 }
4267
4268 len += sprintf(p + len, "commands:\t"
4269 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
4270
4271 return len;
4272}
4273
4274static int led_write(char *buf)
4275{
4276 char *cmd;
4277 int led, rc;
4278 enum led_status_t s;
4279
4280 if (!led_supported)
4281 return -ENODEV;
4282
4283 while ((cmd = next_cmd(&buf))) {
4284 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
4285 return -EINVAL;
4286
4287 if (strstr(cmd, "off")) {
4288 s = TPACPI_LED_OFF;
4289 } else if (strstr(cmd, "on")) {
4290 s = TPACPI_LED_ON;
4291 } else if (strstr(cmd, "blink")) {
4292 s = TPACPI_LED_BLINK;
4293 } else {
4294 return -EINVAL;
4295 }
4296
4297 rc = led_set_status(led, s);
4298 if (rc < 0)
4299 return rc;
4300 }
4301
4302 return 0;
4303}
4304
4305static struct ibm_struct led_driver_data = {
4306 .name = "led",
4307 .read = led_read,
4308 .write = led_write,
4309 .exit = led_exit,
4310};
4311
4312/*************************************************************************
4313 * Beep subdriver
4314 */
4315
4316TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
4317
4318static int __init beep_init(struct ibm_init_struct *iibm)
4319{
4320 vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
4321
4322 TPACPI_ACPIHANDLE_INIT(beep);
4323
4324 vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
4325 str_supported(beep_handle != NULL));
4326
4327 return (beep_handle)? 0 : 1;
4328}
4329
4330static int beep_read(char *p)
4331{
4332 int len = 0;
4333
4334 if (!beep_handle)
4335 len += sprintf(p + len, "status:\t\tnot supported\n");
4336 else {
4337 len += sprintf(p + len, "status:\t\tsupported\n");
4338 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
4339 }
4340
4341 return len;
4342}
4343
4344static int beep_write(char *buf)
4345{
4346 char *cmd;
4347 int beep_cmd;
4348
4349 if (!beep_handle)
4350 return -ENODEV;
4351
4352 while ((cmd = next_cmd(&buf))) {
4353 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
4354 beep_cmd >= 0 && beep_cmd <= 17) {
4355 /* beep_cmd set */
4356 } else
4357 return -EINVAL;
4358 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
4359 return -EIO;
4360 }
4361
4362 return 0;
4363}
4364
4365static struct ibm_struct beep_driver_data = {
4366 .name = "beep",
4367 .read = beep_read,
4368 .write = beep_write,
4369};
4370
4371/*************************************************************************
4372 * Thermal subdriver
4373 */
4374
4375enum thermal_access_mode {
4376 TPACPI_THERMAL_NONE = 0, /* No thermal support */
4377 TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */
4378 TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */
4379 TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */
4380 TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */
4381};
4382
4383enum { /* TPACPI_THERMAL_TPEC_* */
4384 TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
4385 TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
4386 TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
4387};
4388
4389#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
4390struct ibm_thermal_sensors_struct {
4391 s32 temp[TPACPI_MAX_THERMAL_SENSORS];
4392};
4393
4394static enum thermal_access_mode thermal_read_mode;
4395
4396/* idx is zero-based */
4397static int thermal_get_sensor(int idx, s32 *value)
4398{
4399 int t;
4400 s8 tmp;
4401 char tmpi[5];
4402
4403 t = TP_EC_THERMAL_TMP0;
4404
4405 switch (thermal_read_mode) {
4406#if TPACPI_MAX_THERMAL_SENSORS >= 16
4407 case TPACPI_THERMAL_TPEC_16:
4408 if (idx >= 8 && idx <= 15) {
4409 t = TP_EC_THERMAL_TMP8;
4410 idx -= 8;
4411 }
4412 /* fallthrough */
4413#endif
4414 case TPACPI_THERMAL_TPEC_8:
4415 if (idx <= 7) {
4416 if (!acpi_ec_read(t + idx, &tmp))
4417 return -EIO;
4418 *value = tmp * 1000;
4419 return 0;
4420 }
4421 break;
4422
4423 case TPACPI_THERMAL_ACPI_UPDT:
4424 if (idx <= 7) {
4425 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
4426 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
4427 return -EIO;
4428 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
4429 return -EIO;
4430 *value = (t - 2732) * 100;
4431 return 0;
4432 }
4433 break;
4434
4435 case TPACPI_THERMAL_ACPI_TMP07:
4436 if (idx <= 7) {
4437 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
4438 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
4439 return -EIO;
4440 if (t > 127 || t < -127)
4441 t = TP_EC_THERMAL_TMP_NA;
4442 *value = t * 1000;
4443 return 0;
4444 }
4445 break;
4446
4447 case TPACPI_THERMAL_NONE:
4448 default:
4449 return -ENOSYS;
4450 }
4451
4452 return -EINVAL;
4453}
4454
4455static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
4456{
4457 int res, i;
4458 int n;
4459
4460 n = 8;
4461 i = 0;
4462
4463 if (!s)
4464 return -EINVAL;
4465
4466 if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
4467 n = 16;
4468
4469 for (i = 0 ; i < n; i++) {
4470 res = thermal_get_sensor(i, &s->temp[i]);
4471 if (res)
4472 return res;
4473 }
4474
4475 return n;
4476}
4477
4478/* sysfs temp##_input -------------------------------------------------- */
4479
4480static ssize_t thermal_temp_input_show(struct device *dev,
4481 struct device_attribute *attr,
4482 char *buf)
4483{
4484 struct sensor_device_attribute *sensor_attr =
4485 to_sensor_dev_attr(attr);
4486 int idx = sensor_attr->index;
4487 s32 value;
4488 int res;
4489
4490 res = thermal_get_sensor(idx, &value);
4491 if (res)
4492 return res;
4493 if (value == TP_EC_THERMAL_TMP_NA * 1000)
4494 return -ENXIO;
4495
4496 return snprintf(buf, PAGE_SIZE, "%d\n", value);
4497}
4498
4499#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
4500 SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, \
4501 thermal_temp_input_show, NULL, _idxB)
4502
4503static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
4504 THERMAL_SENSOR_ATTR_TEMP(1, 0),
4505 THERMAL_SENSOR_ATTR_TEMP(2, 1),
4506 THERMAL_SENSOR_ATTR_TEMP(3, 2),
4507 THERMAL_SENSOR_ATTR_TEMP(4, 3),
4508 THERMAL_SENSOR_ATTR_TEMP(5, 4),
4509 THERMAL_SENSOR_ATTR_TEMP(6, 5),
4510 THERMAL_SENSOR_ATTR_TEMP(7, 6),
4511 THERMAL_SENSOR_ATTR_TEMP(8, 7),
4512 THERMAL_SENSOR_ATTR_TEMP(9, 8),
4513 THERMAL_SENSOR_ATTR_TEMP(10, 9),
4514 THERMAL_SENSOR_ATTR_TEMP(11, 10),
4515 THERMAL_SENSOR_ATTR_TEMP(12, 11),
4516 THERMAL_SENSOR_ATTR_TEMP(13, 12),
4517 THERMAL_SENSOR_ATTR_TEMP(14, 13),
4518 THERMAL_SENSOR_ATTR_TEMP(15, 14),
4519 THERMAL_SENSOR_ATTR_TEMP(16, 15),
4520};
4521
4522#define THERMAL_ATTRS(X) \
4523 &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
4524
4525static struct attribute *thermal_temp_input_attr[] = {
4526 THERMAL_ATTRS(8),
4527 THERMAL_ATTRS(9),
4528 THERMAL_ATTRS(10),
4529 THERMAL_ATTRS(11),
4530 THERMAL_ATTRS(12),
4531 THERMAL_ATTRS(13),
4532 THERMAL_ATTRS(14),
4533 THERMAL_ATTRS(15),
4534 THERMAL_ATTRS(0),
4535 THERMAL_ATTRS(1),
4536 THERMAL_ATTRS(2),
4537 THERMAL_ATTRS(3),
4538 THERMAL_ATTRS(4),
4539 THERMAL_ATTRS(5),
4540 THERMAL_ATTRS(6),
4541 THERMAL_ATTRS(7),
4542 NULL
4543};
4544
4545static const struct attribute_group thermal_temp_input16_group = {
4546 .attrs = thermal_temp_input_attr
4547};
4548
4549static const struct attribute_group thermal_temp_input8_group = {
4550 .attrs = &thermal_temp_input_attr[8]
4551};
4552
4553#undef THERMAL_SENSOR_ATTR_TEMP
4554#undef THERMAL_ATTRS
4555
4556/* --------------------------------------------------------------------- */
4557
4558static int __init thermal_init(struct ibm_init_struct *iibm)
4559{
4560 u8 t, ta1, ta2;
4561 int i;
4562 int acpi_tmp7;
4563 int res;
4564
4565 vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
4566
4567 acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
4568
4569 if (thinkpad_id.ec_model) {
4570 /*
4571 * Direct EC access mode: sensors at registers
4572 * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
4573 * non-implemented, thermal sensors return 0x80 when
4574 * not available
4575 */
4576
4577 ta1 = ta2 = 0;
4578 for (i = 0; i < 8; i++) {
4579 if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
4580 ta1 |= t;
4581 } else {
4582 ta1 = 0;
4583 break;
4584 }
4585 if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
4586 ta2 |= t;
4587 } else {
4588 ta1 = 0;
4589 break;
4590 }
4591 }
4592 if (ta1 == 0) {
4593 /* This is sheer paranoia, but we handle it anyway */
4594 if (acpi_tmp7) {
4595 printk(TPACPI_ERR
4596 "ThinkPad ACPI EC access misbehaving, "
4597 "falling back to ACPI TMPx access "
4598 "mode\n");
4599 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
4600 } else {
4601 printk(TPACPI_ERR
4602 "ThinkPad ACPI EC access misbehaving, "
4603 "disabling thermal sensors access\n");
4604 thermal_read_mode = TPACPI_THERMAL_NONE;
4605 }
4606 } else {
4607 thermal_read_mode =
4608 (ta2 != 0) ?
4609 TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
4610 }
4611 } else if (acpi_tmp7) {
4612 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
4613 /* 600e/x, 770e, 770x */
4614 thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
4615 } else {
4616 /* Standard ACPI TMPx access, max 8 sensors */
4617 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
4618 }
4619 } else {
4620 /* temperatures not supported on 570, G4x, R30, R31, R32 */
4621 thermal_read_mode = TPACPI_THERMAL_NONE;
4622 }
4623
4624 vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
4625 str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
4626 thermal_read_mode);
4627
4628 switch (thermal_read_mode) {
4629 case TPACPI_THERMAL_TPEC_16:
4630 res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
4631 &thermal_temp_input16_group);
4632 if (res)
4633 return res;
4634 break;
4635 case TPACPI_THERMAL_TPEC_8:
4636 case TPACPI_THERMAL_ACPI_TMP07:
4637 case TPACPI_THERMAL_ACPI_UPDT:
4638 res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
4639 &thermal_temp_input8_group);
4640 if (res)
4641 return res;
4642 break;
4643 case TPACPI_THERMAL_NONE:
4644 default:
4645 return 1;
4646 }
4647
4648 return 0;
4649}
4650
4651static void thermal_exit(void)
4652{
4653 switch (thermal_read_mode) {
4654 case TPACPI_THERMAL_TPEC_16:
4655 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
4656 &thermal_temp_input16_group);
4657 break;
4658 case TPACPI_THERMAL_TPEC_8:
4659 case TPACPI_THERMAL_ACPI_TMP07:
4660 case TPACPI_THERMAL_ACPI_UPDT:
4661 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
4662 &thermal_temp_input16_group);
4663 break;
4664 case TPACPI_THERMAL_NONE:
4665 default:
4666 break;
4667 }
4668}
4669
4670static int thermal_read(char *p)
4671{
4672 int len = 0;
4673 int n, i;
4674 struct ibm_thermal_sensors_struct t;
4675
4676 n = thermal_get_sensors(&t);
4677 if (unlikely(n < 0))
4678 return n;
4679
4680 len += sprintf(p + len, "temperatures:\t");
4681
4682 if (n > 0) {
4683 for (i = 0; i < (n - 1); i++)
4684 len += sprintf(p + len, "%d ", t.temp[i] / 1000);
4685 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
4686 } else
4687 len += sprintf(p + len, "not supported\n");
4688
4689 return len;
4690}
4691
4692static struct ibm_struct thermal_driver_data = {
4693 .name = "thermal",
4694 .read = thermal_read,
4695 .exit = thermal_exit,
4696};
4697
4698/*************************************************************************
4699 * EC Dump subdriver
4700 */
4701
4702static u8 ecdump_regs[256];
4703
4704static int ecdump_read(char *p)
4705{
4706 int len = 0;
4707 int i, j;
4708 u8 v;
4709
4710 len += sprintf(p + len, "EC "
4711 " +00 +01 +02 +03 +04 +05 +06 +07"
4712 " +08 +09 +0a +0b +0c +0d +0e +0f\n");
4713 for (i = 0; i < 256; i += 16) {
4714 len += sprintf(p + len, "EC 0x%02x:", i);
4715 for (j = 0; j < 16; j++) {
4716 if (!acpi_ec_read(i + j, &v))
4717 break;
4718 if (v != ecdump_regs[i + j])
4719 len += sprintf(p + len, " *%02x", v);
4720 else
4721 len += sprintf(p + len, " %02x", v);
4722 ecdump_regs[i + j] = v;
4723 }
4724 len += sprintf(p + len, "\n");
4725 if (j != 16)
4726 break;
4727 }
4728
4729 /* These are way too dangerous to advertise openly... */
4730#if 0
4731 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
4732 " (<offset> is 00-ff, <value> is 00-ff)\n");
4733 len += sprintf(p + len, "commands:\t0x<offset> <value> "
4734 " (<offset> is 00-ff, <value> is 0-255)\n");
4735#endif
4736 return len;
4737}
4738
4739static int ecdump_write(char *buf)
4740{
4741 char *cmd;
4742 int i, v;
4743
4744 while ((cmd = next_cmd(&buf))) {
4745 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
4746 /* i and v set */
4747 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
4748 /* i and v set */
4749 } else
4750 return -EINVAL;
4751 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
4752 if (!acpi_ec_write(i, v))
4753 return -EIO;
4754 } else
4755 return -EINVAL;
4756 }
4757
4758 return 0;
4759}
4760
4761static struct ibm_struct ecdump_driver_data = {
4762 .name = "ecdump",
4763 .read = ecdump_read,
4764 .write = ecdump_write,
4765 .flags.experimental = 1,
4766};
4767
4768/*************************************************************************
4769 * Backlight/brightness subdriver
4770 */
4771
4772#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
4773
4774enum {
4775 TP_EC_BACKLIGHT = 0x31,
4776
4777 /* TP_EC_BACKLIGHT bitmasks */
4778 TP_EC_BACKLIGHT_LVLMSK = 0x1F,
4779 TP_EC_BACKLIGHT_CMDMSK = 0xE0,
4780 TP_EC_BACKLIGHT_MAPSW = 0x20,
4781};
4782
4783static struct backlight_device *ibm_backlight_device;
4784static int brightness_mode;
4785static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
4786
4787static struct mutex brightness_mutex;
4788
4789/*
4790 * ThinkPads can read brightness from two places: EC 0x31, or
4791 * CMOS NVRAM byte 0x5E, bits 0-3.
4792 *
4793 * EC 0x31 has the following layout
4794 * Bit 7: unknown function
4795 * Bit 6: unknown function
4796 * Bit 5: Z: honour scale changes, NZ: ignore scale changes
4797 * Bit 4: must be set to zero to avoid problems
4798 * Bit 3-0: backlight brightness level
4799 *
4800 * brightness_get_raw returns status data in the EC 0x31 layout
4801 */
4802static int brightness_get_raw(int *status)
4803{
4804 u8 lec = 0, lcmos = 0, level = 0;
4805
4806 if (brightness_mode & 1) {
4807 if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
4808 return -EIO;
4809 level = lec & TP_EC_BACKLIGHT_LVLMSK;
4810 };
4811 if (brightness_mode & 2) {
4812 lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
4813 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
4814 >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
4815 lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
4816 level = lcmos;
4817 }
4818
4819 if (brightness_mode == 3) {
4820 *status = lec; /* Prefer EC, CMOS is just a backing store */
4821 lec &= TP_EC_BACKLIGHT_LVLMSK;
4822 if (lec == lcmos)
4823 tp_warned.bright_cmos_ec_unsync = 0;
4824 else {
4825 if (!tp_warned.bright_cmos_ec_unsync) {
4826 printk(TPACPI_ERR
4827 "CMOS NVRAM (%u) and EC (%u) do not "
4828 "agree on display brightness level\n",
4829 (unsigned int) lcmos,
4830 (unsigned int) lec);
4831 tp_warned.bright_cmos_ec_unsync = 1;
4832 }
4833 return -EIO;
4834 }
4835 } else {
4836 *status = level;
4837 }
4838
4839 return 0;
4840}
4841
4842/* May return EINTR which can always be mapped to ERESTARTSYS */
4843static int brightness_set(int value)
4844{
4845 int cmos_cmd, inc, i, res;
4846 int current_value;
4847 int command_bits;
4848
4849 if (value > ((tp_features.bright_16levels)? 15 : 7) ||
4850 value < 0)
4851 return -EINVAL;
4852
4853 res = mutex_lock_interruptible(&brightness_mutex);
4854 if (res < 0)
4855 return res;
4856
4857 res = brightness_get_raw(&current_value);
4858 if (res < 0)
4859 goto errout;
4860
4861 command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
4862 current_value &= TP_EC_BACKLIGHT_LVLMSK;
4863
4864 cmos_cmd = value > current_value ?
4865 TP_CMOS_BRIGHTNESS_UP :
4866 TP_CMOS_BRIGHTNESS_DOWN;
4867 inc = (value > current_value)? 1 : -1;
4868
4869 res = 0;
4870 for (i = current_value; i != value; i += inc) {
4871 if ((brightness_mode & 2) &&
4872 issue_thinkpad_cmos_command(cmos_cmd)) {
4873 res = -EIO;
4874 goto errout;
4875 }
4876 if ((brightness_mode & 1) &&
4877 !acpi_ec_write(TP_EC_BACKLIGHT,
4878 (i + inc) | command_bits)) {
4879 res = -EIO;
4880 goto errout;;
4881 }
4882 }
4883
4884errout:
4885 mutex_unlock(&brightness_mutex);
4886 return res;
4887}
4888
4889/* sysfs backlight class ----------------------------------------------- */
4890
4891static int brightness_update_status(struct backlight_device *bd)
4892{
4893 /* it is the backlight class's job (caller) to handle
4894 * EINTR and other errors properly */
4895 return brightness_set(
4896 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
4897 bd->props.power == FB_BLANK_UNBLANK) ?
4898 bd->props.brightness : 0);
4899}
4900
4901static int brightness_get(struct backlight_device *bd)
4902{
4903 int status, res;
4904
4905 res = brightness_get_raw(&status);
4906 if (res < 0)
4907 return 0; /* FIXME: teach backlight about error handling */
4908
4909 return status & TP_EC_BACKLIGHT_LVLMSK;
4910}
4911
4912static struct backlight_ops ibm_backlight_data = {
4913 .get_brightness = brightness_get,
4914 .update_status = brightness_update_status,
4915};
4916
4917/* --------------------------------------------------------------------- */
4918
4919static int __init brightness_init(struct ibm_init_struct *iibm)
4920{
4921 int b;
4922
4923 vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
4924
4925 mutex_init(&brightness_mutex);
4926
4927 /*
4928 * We always attempt to detect acpi support, so as to switch
4929 * Lenovo Vista BIOS to ACPI brightness mode even if we are not
4930 * going to publish a backlight interface
4931 */
4932 b = tpacpi_check_std_acpi_brightness_support();
4933 if (b > 0) {
4934
4935 if (acpi_video_backlight_support()) {
4936 if (brightness_enable > 1) {
4937 printk(TPACPI_NOTICE
4938 "Standard ACPI backlight interface "
4939 "available, not loading native one.\n");
4940 return 1;
4941 } else if (brightness_enable == 1) {
4942 printk(TPACPI_NOTICE
4943 "Backlight control force enabled, even if standard "
4944 "ACPI backlight interface is available\n");
4945 }
4946 } else {
4947 if (brightness_enable > 1) {
4948 printk(TPACPI_NOTICE
4949 "Standard ACPI backlight interface not "
4950 "available, thinkpad_acpi native "
4951 "brightness control enabled\n");
4952 }
4953 }
4954 }
4955
4956 if (!brightness_enable) {
4957 dbg_printk(TPACPI_DBG_INIT,
4958 "brightness support disabled by "
4959 "module parameter\n");
4960 return 1;
4961 }
4962
4963 if (b > 16) {
4964 printk(TPACPI_ERR
4965 "Unsupported brightness interface, "
4966 "please contact %s\n", TPACPI_MAIL);
4967 return 1;
4968 }
4969 if (b == 16)
4970 tp_features.bright_16levels = 1;
4971
4972 if (!brightness_mode) {
4973 if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
4974 brightness_mode = 2;
4975 else
4976 brightness_mode = 3;
4977
4978 dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
4979 brightness_mode);
4980 }
4981
4982 if (brightness_mode > 3)
4983 return -EINVAL;
4984
4985 if (brightness_get_raw(&b) < 0)
4986 return 1;
4987
4988 if (tp_features.bright_16levels)
4989 printk(TPACPI_INFO
4990 "detected a 16-level brightness capable ThinkPad\n");
4991
4992 ibm_backlight_device = backlight_device_register(
4993 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
4994 &ibm_backlight_data);
4995 if (IS_ERR(ibm_backlight_device)) {
4996 printk(TPACPI_ERR "Could not register backlight device\n");
4997 return PTR_ERR(ibm_backlight_device);
4998 }
4999 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
5000
5001 ibm_backlight_device->props.max_brightness =
5002 (tp_features.bright_16levels)? 15 : 7;
5003 ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
5004 backlight_update_status(ibm_backlight_device);
5005
5006 return 0;
5007}
5008
5009static void brightness_exit(void)
5010{
5011 if (ibm_backlight_device) {
5012 vdbg_printk(TPACPI_DBG_EXIT,
5013 "calling backlight_device_unregister()\n");
5014 backlight_device_unregister(ibm_backlight_device);
5015 }
5016}
5017
5018static int brightness_read(char *p)
5019{
5020 int len = 0;
5021 int level;
5022
5023 level = brightness_get(NULL);
5024 if (level < 0) {
5025 len += sprintf(p + len, "level:\t\tunreadable\n");
5026 } else {
5027 len += sprintf(p + len, "level:\t\t%d\n", level);
5028 len += sprintf(p + len, "commands:\tup, down\n");
5029 len += sprintf(p + len, "commands:\tlevel <level>"
5030 " (<level> is 0-%d)\n",
5031 (tp_features.bright_16levels) ? 15 : 7);
5032 }
5033
5034 return len;
5035}
5036
5037static int brightness_write(char *buf)
5038{
5039 int level;
5040 int rc;
5041 char *cmd;
5042 int max_level = (tp_features.bright_16levels) ? 15 : 7;
5043
5044 level = brightness_get(NULL);
5045 if (level < 0)
5046 return level;
5047
5048 while ((cmd = next_cmd(&buf))) {
5049 if (strlencmp(cmd, "up") == 0) {
5050 if (level < max_level)
5051 level++;
5052 } else if (strlencmp(cmd, "down") == 0) {
5053 if (level > 0)
5054 level--;
5055 } else if (sscanf(cmd, "level %d", &level) == 1 &&
5056 level >= 0 && level <= max_level) {
5057 /* new level set */
5058 } else
5059 return -EINVAL;
5060 }
5061
5062 /*
5063 * Now we know what the final level should be, so we try to set it.
5064 * Doing it this way makes the syscall restartable in case of EINTR
5065 */
5066 rc = brightness_set(level);
5067 return (rc == -EINTR)? ERESTARTSYS : rc;
5068}
5069
5070static struct ibm_struct brightness_driver_data = {
5071 .name = "brightness",
5072 .read = brightness_read,
5073 .write = brightness_write,
5074 .exit = brightness_exit,
5075};
5076
5077/*************************************************************************
5078 * Volume subdriver
5079 */
5080
5081static int volume_offset = 0x30;
5082
5083static int volume_read(char *p)
5084{
5085 int len = 0;
5086 u8 level;
5087
5088 if (!acpi_ec_read(volume_offset, &level)) {
5089 len += sprintf(p + len, "level:\t\tunreadable\n");
5090 } else {
5091 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
5092 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
5093 len += sprintf(p + len, "commands:\tup, down, mute\n");
5094 len += sprintf(p + len, "commands:\tlevel <level>"
5095 " (<level> is 0-15)\n");
5096 }
5097
5098 return len;
5099}
5100
5101static int volume_write(char *buf)
5102{
5103 int cmos_cmd, inc, i;
5104 u8 level, mute;
5105 int new_level, new_mute;
5106 char *cmd;
5107
5108 while ((cmd = next_cmd(&buf))) {
5109 if (!acpi_ec_read(volume_offset, &level))
5110 return -EIO;
5111 new_mute = mute = level & 0x40;
5112 new_level = level = level & 0xf;
5113
5114 if (strlencmp(cmd, "up") == 0) {
5115 if (mute)
5116 new_mute = 0;
5117 else
5118 new_level = level == 15 ? 15 : level + 1;
5119 } else if (strlencmp(cmd, "down") == 0) {
5120 if (mute)
5121 new_mute = 0;
5122 else
5123 new_level = level == 0 ? 0 : level - 1;
5124 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
5125 new_level >= 0 && new_level <= 15) {
5126 /* new_level set */
5127 } else if (strlencmp(cmd, "mute") == 0) {
5128 new_mute = 0x40;
5129 } else
5130 return -EINVAL;
5131
5132 if (new_level != level) {
5133 /* mute doesn't change */
5134
5135 cmos_cmd = (new_level > level) ?
5136 TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
5137 inc = new_level > level ? 1 : -1;
5138
5139 if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
5140 !acpi_ec_write(volume_offset, level)))
5141 return -EIO;
5142
5143 for (i = level; i != new_level; i += inc)
5144 if (issue_thinkpad_cmos_command(cmos_cmd) ||
5145 !acpi_ec_write(volume_offset, i + inc))
5146 return -EIO;
5147
5148 if (mute &&
5149 (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
5150 !acpi_ec_write(volume_offset, new_level + mute))) {
5151 return -EIO;
5152 }
5153 }
5154
5155 if (new_mute != mute) {
5156 /* level doesn't change */
5157
5158 cmos_cmd = (new_mute) ?
5159 TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
5160
5161 if (issue_thinkpad_cmos_command(cmos_cmd) ||
5162 !acpi_ec_write(volume_offset, level + new_mute))
5163 return -EIO;
5164 }
5165 }
5166
5167 return 0;
5168}
5169
5170static struct ibm_struct volume_driver_data = {
5171 .name = "volume",
5172 .read = volume_read,
5173 .write = volume_write,
5174};
5175
5176/*************************************************************************
5177 * Fan subdriver
5178 */
5179
5180/*
5181 * FAN ACCESS MODES
5182 *
5183 * TPACPI_FAN_RD_ACPI_GFAN:
5184 * ACPI GFAN method: returns fan level
5185 *
5186 * see TPACPI_FAN_WR_ACPI_SFAN
5187 * EC 0x2f (HFSP) not available if GFAN exists
5188 *
5189 * TPACPI_FAN_WR_ACPI_SFAN:
5190 * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
5191 *
5192 * EC 0x2f (HFSP) might be available *for reading*, but do not use
5193 * it for writing.
5194 *
5195 * TPACPI_FAN_WR_TPEC:
5196 * ThinkPad EC register 0x2f (HFSP): fan control loop mode
5197 * Supported on almost all ThinkPads
5198 *
5199 * Fan speed changes of any sort (including those caused by the
5200 * disengaged mode) are usually done slowly by the firmware as the
5201 * maximum ammount of fan duty cycle change per second seems to be
5202 * limited.
5203 *
5204 * Reading is not available if GFAN exists.
5205 * Writing is not available if SFAN exists.
5206 *
5207 * Bits
5208 * 7 automatic mode engaged;
5209 * (default operation mode of the ThinkPad)
5210 * fan level is ignored in this mode.
5211 * 6 full speed mode (takes precedence over bit 7);
5212 * not available on all thinkpads. May disable
5213 * the tachometer while the fan controller ramps up
5214 * the speed (which can take up to a few *minutes*).
5215 * Speeds up fan to 100% duty-cycle, which is far above
5216 * the standard RPM levels. It is not impossible that
5217 * it could cause hardware damage.
5218 * 5-3 unused in some models. Extra bits for fan level
5219 * in others, but still useless as all values above
5220 * 7 map to the same speed as level 7 in these models.
5221 * 2-0 fan level (0..7 usually)
5222 * 0x00 = stop
5223 * 0x07 = max (set when temperatures critical)
5224 * Some ThinkPads may have other levels, see
5225 * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
5226 *
5227 * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
5228 * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
5229 * does so, its initial value is meaningless (0x07).
5230 *
5231 * For firmware bugs, refer to:
5232 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
5233 *
5234 * ----
5235 *
5236 * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
5237 * Main fan tachometer reading (in RPM)
5238 *
5239 * This register is present on all ThinkPads with a new-style EC, and
5240 * it is known not to be present on the A21m/e, and T22, as there is
5241 * something else in offset 0x84 according to the ACPI DSDT. Other
5242 * ThinkPads from this same time period (and earlier) probably lack the
5243 * tachometer as well.
5244 *
5245 * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
5246 * was never fixed by IBM to report the EC firmware version string
5247 * probably support the tachometer (like the early X models), so
5248 * detecting it is quite hard. We need more data to know for sure.
5249 *
5250 * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
5251 * might result.
5252 *
5253 * FIRMWARE BUG: may go stale while the EC is switching to full speed
5254 * mode.
5255 *
5256 * For firmware bugs, refer to:
5257 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
5258 *
5259 * TPACPI_FAN_WR_ACPI_FANS:
5260 * ThinkPad X31, X40, X41. Not available in the X60.
5261 *
5262 * FANS ACPI handle: takes three arguments: low speed, medium speed,
5263 * high speed. ACPI DSDT seems to map these three speeds to levels
5264 * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
5265 * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
5266 *
5267 * The speeds are stored on handles
5268 * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
5269 *
5270 * There are three default speed sets, acessible as handles:
5271 * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
5272 *
5273 * ACPI DSDT switches which set is in use depending on various
5274 * factors.
5275 *
5276 * TPACPI_FAN_WR_TPEC is also available and should be used to
5277 * command the fan. The X31/X40/X41 seems to have 8 fan levels,
5278 * but the ACPI tables just mention level 7.
5279 */
5280
5281enum { /* Fan control constants */
5282 fan_status_offset = 0x2f, /* EC register 0x2f */
5283 fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
5284 * 0x84 must be read before 0x85 */
5285
5286 TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
5287 TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
5288
5289 TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */
5290};
5291
5292enum fan_status_access_mode {
5293 TPACPI_FAN_NONE = 0, /* No fan status or control */
5294 TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */
5295 TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */
5296};
5297
5298enum fan_control_access_mode {
5299 TPACPI_FAN_WR_NONE = 0, /* No fan control */
5300 TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */
5301 TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */
5302 TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */
5303};
5304
5305enum fan_control_commands {
5306 TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */
5307 TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */
5308 TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd,
5309 * and also watchdog cmd */
5310};
5311
5312static int fan_control_allowed;
5313
5314static enum fan_status_access_mode fan_status_access_mode;
5315static enum fan_control_access_mode fan_control_access_mode;
5316static enum fan_control_commands fan_control_commands;
5317
5318static u8 fan_control_initial_status;
5319static u8 fan_control_desired_level;
5320static u8 fan_control_resume_level;
5321static int fan_watchdog_maxinterval;
5322
5323static struct mutex fan_mutex;
5324
5325static void fan_watchdog_fire(struct work_struct *ignored);
5326static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
5327
5328TPACPI_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
5329TPACPI_HANDLE(gfan, ec, "GFAN", /* 570 */
5330 "\\FSPD", /* 600e/x, 770e, 770x */
5331 ); /* all others */
5332TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
5333 "JFNS", /* 770x-JL */
5334 ); /* all others */
5335
5336/*
5337 * Call with fan_mutex held
5338 */
5339static void fan_update_desired_level(u8 status)
5340{
5341 if ((status &
5342 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
5343 if (status > 7)
5344 fan_control_desired_level = 7;
5345 else
5346 fan_control_desired_level = status;
5347 }
5348}
5349
5350static int fan_get_status(u8 *status)
5351{
5352 u8 s;
5353
5354 /* TODO:
5355 * Add TPACPI_FAN_RD_ACPI_FANS ? */
5356
5357 switch (fan_status_access_mode) {
5358 case TPACPI_FAN_RD_ACPI_GFAN:
5359 /* 570, 600e/x, 770e, 770x */
5360
5361 if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
5362 return -EIO;
5363
5364 if (likely(status))
5365 *status = s & 0x07;
5366
5367 break;
5368
5369 case TPACPI_FAN_RD_TPEC:
5370 /* all except 570, 600e/x, 770e, 770x */
5371 if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
5372 return -EIO;
5373
5374 if (likely(status))
5375 *status = s;
5376
5377 break;
5378
5379 default:
5380 return -ENXIO;
5381 }
5382
5383 return 0;
5384}
5385
5386static int fan_get_status_safe(u8 *status)
5387{
5388 int rc;
5389 u8 s;
5390
5391 if (mutex_lock_interruptible(&fan_mutex))
5392 return -ERESTARTSYS;
5393 rc = fan_get_status(&s);
5394 if (!rc)
5395 fan_update_desired_level(s);
5396 mutex_unlock(&fan_mutex);
5397
5398 if (status)
5399 *status = s;
5400
5401 return rc;
5402}
5403
5404static int fan_get_speed(unsigned int *speed)
5405{
5406 u8 hi, lo;
5407
5408 switch (fan_status_access_mode) {
5409 case TPACPI_FAN_RD_TPEC:
5410 /* all except 570, 600e/x, 770e, 770x */
5411 if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
5412 !acpi_ec_read(fan_rpm_offset + 1, &hi)))
5413 return -EIO;
5414
5415 if (likely(speed))
5416 *speed = (hi << 8) | lo;
5417
5418 break;
5419
5420 default:
5421 return -ENXIO;
5422 }
5423
5424 return 0;
5425}
5426
5427static int fan_set_level(int level)
5428{
5429 if (!fan_control_allowed)
5430 return -EPERM;
5431
5432 switch (fan_control_access_mode) {
5433 case TPACPI_FAN_WR_ACPI_SFAN:
5434 if (level >= 0 && level <= 7) {
5435 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
5436 return -EIO;
5437 } else
5438 return -EINVAL;
5439 break;
5440
5441 case TPACPI_FAN_WR_ACPI_FANS:
5442 case TPACPI_FAN_WR_TPEC:
5443 if (!(level & TP_EC_FAN_AUTO) &&
5444 !(level & TP_EC_FAN_FULLSPEED) &&
5445 ((level < 0) || (level > 7)))
5446 return -EINVAL;
5447
5448 /* safety net should the EC not support AUTO
5449 * or FULLSPEED mode bits and just ignore them */
5450 if (level & TP_EC_FAN_FULLSPEED)
5451 level |= 7; /* safety min speed 7 */
5452 else if (level & TP_EC_FAN_AUTO)
5453 level |= 4; /* safety min speed 4 */
5454
5455 if (!acpi_ec_write(fan_status_offset, level))
5456 return -EIO;
5457 else
5458 tp_features.fan_ctrl_status_undef = 0;
5459 break;
5460
5461 default:
5462 return -ENXIO;
5463 }
5464 return 0;
5465}
5466
5467static int fan_set_level_safe(int level)
5468{
5469 int rc;
5470
5471 if (!fan_control_allowed)
5472 return -EPERM;
5473
5474 if (mutex_lock_interruptible(&fan_mutex))
5475 return -ERESTARTSYS;
5476
5477 if (level == TPACPI_FAN_LAST_LEVEL)
5478 level = fan_control_desired_level;
5479
5480 rc = fan_set_level(level);
5481 if (!rc)
5482 fan_update_desired_level(level);
5483
5484 mutex_unlock(&fan_mutex);
5485 return rc;
5486}
5487
5488static int fan_set_enable(void)
5489{
5490 u8 s;
5491 int rc;
5492
5493 if (!fan_control_allowed)
5494 return -EPERM;
5495
5496 if (mutex_lock_interruptible(&fan_mutex))
5497 return -ERESTARTSYS;
5498
5499 switch (fan_control_access_mode) {
5500 case TPACPI_FAN_WR_ACPI_FANS:
5501 case TPACPI_FAN_WR_TPEC:
5502 rc = fan_get_status(&s);
5503 if (rc < 0)
5504 break;
5505
5506 /* Don't go out of emergency fan mode */
5507 if (s != 7) {
5508 s &= 0x07;
5509 s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
5510 }
5511
5512 if (!acpi_ec_write(fan_status_offset, s))
5513 rc = -EIO;
5514 else {
5515 tp_features.fan_ctrl_status_undef = 0;
5516 rc = 0;
5517 }
5518 break;
5519
5520 case TPACPI_FAN_WR_ACPI_SFAN:
5521 rc = fan_get_status(&s);
5522 if (rc < 0)
5523 break;
5524
5525 s &= 0x07;
5526
5527 /* Set fan to at least level 4 */
5528 s |= 4;
5529
5530 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
5531 rc = -EIO;
5532 else
5533 rc = 0;
5534 break;
5535
5536 default:
5537 rc = -ENXIO;
5538 }
5539
5540 mutex_unlock(&fan_mutex);
5541 return rc;
5542}
5543
5544static int fan_set_disable(void)
5545{
5546 int rc;
5547
5548 if (!fan_control_allowed)
5549 return -EPERM;
5550
5551 if (mutex_lock_interruptible(&fan_mutex))
5552 return -ERESTARTSYS;
5553
5554 rc = 0;
5555 switch (fan_control_access_mode) {
5556 case TPACPI_FAN_WR_ACPI_FANS:
5557 case TPACPI_FAN_WR_TPEC:
5558 if (!acpi_ec_write(fan_status_offset, 0x00))
5559 rc = -EIO;
5560 else {
5561 fan_control_desired_level = 0;
5562 tp_features.fan_ctrl_status_undef = 0;
5563 }
5564 break;
5565
5566 case TPACPI_FAN_WR_ACPI_SFAN:
5567 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
5568 rc = -EIO;
5569 else
5570 fan_control_desired_level = 0;
5571 break;
5572
5573 default:
5574 rc = -ENXIO;
5575 }
5576
5577
5578 mutex_unlock(&fan_mutex);
5579 return rc;
5580}
5581
5582static int fan_set_speed(int speed)
5583{
5584 int rc;
5585
5586 if (!fan_control_allowed)
5587 return -EPERM;
5588
5589 if (mutex_lock_interruptible(&fan_mutex))
5590 return -ERESTARTSYS;
5591
5592 rc = 0;
5593 switch (fan_control_access_mode) {
5594 case TPACPI_FAN_WR_ACPI_FANS:
5595 if (speed >= 0 && speed <= 65535) {
5596 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
5597 speed, speed, speed))
5598 rc = -EIO;
5599 } else
5600 rc = -EINVAL;
5601 break;
5602
5603 default:
5604 rc = -ENXIO;
5605 }
5606
5607 mutex_unlock(&fan_mutex);
5608 return rc;
5609}
5610
5611static void fan_watchdog_reset(void)
5612{
5613 static int fan_watchdog_active;
5614
5615 if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
5616 return;
5617
5618 if (fan_watchdog_active)
5619 cancel_delayed_work(&fan_watchdog_task);
5620
5621 if (fan_watchdog_maxinterval > 0 &&
5622 tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
5623 fan_watchdog_active = 1;
5624 if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
5625 msecs_to_jiffies(fan_watchdog_maxinterval
5626 * 1000))) {
5627 printk(TPACPI_ERR
5628 "failed to queue the fan watchdog, "
5629 "watchdog will not trigger\n");
5630 }
5631 } else
5632 fan_watchdog_active = 0;
5633}
5634
5635static void fan_watchdog_fire(struct work_struct *ignored)
5636{
5637 int rc;
5638
5639 if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
5640 return;
5641
5642 printk(TPACPI_NOTICE "fan watchdog: enabling fan\n");
5643 rc = fan_set_enable();
5644 if (rc < 0) {
5645 printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, "
5646 "will try again later...\n", -rc);
5647 /* reschedule for later */
5648 fan_watchdog_reset();
5649 }
5650}
5651
5652/*
5653 * SYSFS fan layout: hwmon compatible (device)
5654 *
5655 * pwm*_enable:
5656 * 0: "disengaged" mode
5657 * 1: manual mode
5658 * 2: native EC "auto" mode (recommended, hardware default)
5659 *
5660 * pwm*: set speed in manual mode, ignored otherwise.
5661 * 0 is level 0; 255 is level 7. Intermediate points done with linear
5662 * interpolation.
5663 *
5664 * fan*_input: tachometer reading, RPM
5665 *
5666 *
5667 * SYSFS fan layout: extensions
5668 *
5669 * fan_watchdog (driver):
5670 * fan watchdog interval in seconds, 0 disables (default), max 120
5671 */
5672
5673/* sysfs fan pwm1_enable ----------------------------------------------- */
5674static ssize_t fan_pwm1_enable_show(struct device *dev,
5675 struct device_attribute *attr,
5676 char *buf)
5677{
5678 int res, mode;
5679 u8 status;
5680
5681 res = fan_get_status_safe(&status);
5682 if (res)
5683 return res;
5684
5685 if (unlikely(tp_features.fan_ctrl_status_undef)) {
5686 if (status != fan_control_initial_status) {
5687 tp_features.fan_ctrl_status_undef = 0;
5688 } else {
5689 /* Return most likely status. In fact, it
5690 * might be the only possible status */
5691 status = TP_EC_FAN_AUTO;
5692 }
5693 }
5694
5695 if (status & TP_EC_FAN_FULLSPEED) {
5696 mode = 0;
5697 } else if (status & TP_EC_FAN_AUTO) {
5698 mode = 2;
5699 } else
5700 mode = 1;
5701
5702 return snprintf(buf, PAGE_SIZE, "%d\n", mode);
5703}
5704
5705static ssize_t fan_pwm1_enable_store(struct device *dev,
5706 struct device_attribute *attr,
5707 const char *buf, size_t count)
5708{
5709 unsigned long t;
5710 int res, level;
5711
5712 if (parse_strtoul(buf, 2, &t))
5713 return -EINVAL;
5714
5715 switch (t) {
5716 case 0:
5717 level = TP_EC_FAN_FULLSPEED;
5718 break;
5719 case 1:
5720 level = TPACPI_FAN_LAST_LEVEL;
5721 break;
5722 case 2:
5723 level = TP_EC_FAN_AUTO;
5724 break;
5725 case 3:
5726 /* reserved for software-controlled auto mode */
5727 return -ENOSYS;
5728 default:
5729 return -EINVAL;
5730 }
5731
5732 res = fan_set_level_safe(level);
5733 if (res == -ENXIO)
5734 return -EINVAL;
5735 else if (res < 0)
5736 return res;
5737
5738 fan_watchdog_reset();
5739
5740 return count;
5741}
5742
5743static struct device_attribute dev_attr_fan_pwm1_enable =
5744 __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
5745 fan_pwm1_enable_show, fan_pwm1_enable_store);
5746
5747/* sysfs fan pwm1 ------------------------------------------------------ */
5748static ssize_t fan_pwm1_show(struct device *dev,
5749 struct device_attribute *attr,
5750 char *buf)
5751{
5752 int res;
5753 u8 status;
5754
5755 res = fan_get_status_safe(&status);
5756 if (res)
5757 return res;
5758
5759 if (unlikely(tp_features.fan_ctrl_status_undef)) {
5760 if (status != fan_control_initial_status) {
5761 tp_features.fan_ctrl_status_undef = 0;
5762 } else {
5763 status = TP_EC_FAN_AUTO;
5764 }
5765 }
5766
5767 if ((status &
5768 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
5769 status = fan_control_desired_level;
5770
5771 if (status > 7)
5772 status = 7;
5773
5774 return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
5775}
5776
5777static ssize_t fan_pwm1_store(struct device *dev,
5778 struct device_attribute *attr,
5779 const char *buf, size_t count)
5780{
5781 unsigned long s;
5782 int rc;
5783 u8 status, newlevel;
5784
5785 if (parse_strtoul(buf, 255, &s))
5786 return -EINVAL;
5787
5788 /* scale down from 0-255 to 0-7 */
5789 newlevel = (s >> 5) & 0x07;
5790
5791 if (mutex_lock_interruptible(&fan_mutex))
5792 return -ERESTARTSYS;
5793
5794 rc = fan_get_status(&status);
5795 if (!rc && (status &
5796 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
5797 rc = fan_set_level(newlevel);
5798 if (rc == -ENXIO)
5799 rc = -EINVAL;
5800 else if (!rc) {
5801 fan_update_desired_level(newlevel);
5802 fan_watchdog_reset();
5803 }
5804 }
5805
5806 mutex_unlock(&fan_mutex);
5807 return (rc)? rc : count;
5808}
5809
5810static struct device_attribute dev_attr_fan_pwm1 =
5811 __ATTR(pwm1, S_IWUSR | S_IRUGO,
5812 fan_pwm1_show, fan_pwm1_store);
5813
5814/* sysfs fan fan1_input ------------------------------------------------ */
5815static ssize_t fan_fan1_input_show(struct device *dev,
5816 struct device_attribute *attr,
5817 char *buf)
5818{
5819 int res;
5820 unsigned int speed;
5821
5822 res = fan_get_speed(&speed);
5823 if (res < 0)
5824 return res;
5825
5826 return snprintf(buf, PAGE_SIZE, "%u\n", speed);
5827}
5828
5829static struct device_attribute dev_attr_fan_fan1_input =
5830 __ATTR(fan1_input, S_IRUGO,
5831 fan_fan1_input_show, NULL);
5832
5833/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
5834static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
5835 char *buf)
5836{
5837 return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
5838}
5839
5840static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
5841 const char *buf, size_t count)
5842{
5843 unsigned long t;
5844
5845 if (parse_strtoul(buf, 120, &t))
5846 return -EINVAL;
5847
5848 if (!fan_control_allowed)
5849 return -EPERM;
5850
5851 fan_watchdog_maxinterval = t;
5852 fan_watchdog_reset();
5853
5854 return count;
5855}
5856
5857static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
5858 fan_fan_watchdog_show, fan_fan_watchdog_store);
5859
5860/* --------------------------------------------------------------------- */
5861static struct attribute *fan_attributes[] = {
5862 &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
5863 &dev_attr_fan_fan1_input.attr,
5864 NULL
5865};
5866
5867static const struct attribute_group fan_attr_group = {
5868 .attrs = fan_attributes,
5869};
5870
5871static int __init fan_init(struct ibm_init_struct *iibm)
5872{
5873 int rc;
5874
5875 vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
5876
5877 mutex_init(&fan_mutex);
5878 fan_status_access_mode = TPACPI_FAN_NONE;
5879 fan_control_access_mode = TPACPI_FAN_WR_NONE;
5880 fan_control_commands = 0;
5881 fan_watchdog_maxinterval = 0;
5882 tp_features.fan_ctrl_status_undef = 0;
5883 fan_control_desired_level = 7;
5884
5885 TPACPI_ACPIHANDLE_INIT(fans);
5886 TPACPI_ACPIHANDLE_INIT(gfan);
5887 TPACPI_ACPIHANDLE_INIT(sfan);
5888
5889 if (gfan_handle) {
5890 /* 570, 600e/x, 770e, 770x */
5891 fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
5892 } else {
5893 /* all other ThinkPads: note that even old-style
5894 * ThinkPad ECs supports the fan control register */
5895 if (likely(acpi_ec_read(fan_status_offset,
5896 &fan_control_initial_status))) {
5897 fan_status_access_mode = TPACPI_FAN_RD_TPEC;
5898
5899 /* In some ThinkPads, neither the EC nor the ACPI
5900 * DSDT initialize the fan status, and it ends up
5901 * being set to 0x07 when it *could* be either
5902 * 0x07 or 0x80.
5903 *
5904 * Enable for TP-1Y (T43), TP-78 (R51e),
5905 * TP-76 (R52), TP-70 (T43, R52), which are known
5906 * to be buggy. */
5907 if (fan_control_initial_status == 0x07) {
5908 switch (thinkpad_id.ec_model) {
5909 case 0x5931: /* TP-1Y */
5910 case 0x3837: /* TP-78 */
5911 case 0x3637: /* TP-76 */
5912 case 0x3037: /* TP-70 */
5913 printk(TPACPI_NOTICE
5914 "fan_init: initial fan status "
5915 "is unknown, assuming it is "
5916 "in auto mode\n");
5917 tp_features.fan_ctrl_status_undef = 1;
5918 ;;
5919 }
5920 }
5921 } else {
5922 printk(TPACPI_ERR
5923 "ThinkPad ACPI EC access misbehaving, "
5924 "fan status and control unavailable\n");
5925 return 1;
5926 }
5927 }
5928
5929 if (sfan_handle) {
5930 /* 570, 770x-JL */
5931 fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
5932 fan_control_commands |=
5933 TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
5934 } else {
5935 if (!gfan_handle) {
5936 /* gfan without sfan means no fan control */
5937 /* all other models implement TP EC 0x2f control */
5938
5939 if (fans_handle) {
5940 /* X31, X40, X41 */
5941 fan_control_access_mode =
5942 TPACPI_FAN_WR_ACPI_FANS;
5943 fan_control_commands |=
5944 TPACPI_FAN_CMD_SPEED |
5945 TPACPI_FAN_CMD_LEVEL |
5946 TPACPI_FAN_CMD_ENABLE;
5947 } else {
5948 fan_control_access_mode = TPACPI_FAN_WR_TPEC;
5949 fan_control_commands |=
5950 TPACPI_FAN_CMD_LEVEL |
5951 TPACPI_FAN_CMD_ENABLE;
5952 }
5953 }
5954 }
5955
5956 vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
5957 str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
5958 fan_control_access_mode != TPACPI_FAN_WR_NONE),
5959 fan_status_access_mode, fan_control_access_mode);
5960
5961 /* fan control master switch */
5962 if (!fan_control_allowed) {
5963 fan_control_access_mode = TPACPI_FAN_WR_NONE;
5964 fan_control_commands = 0;
5965 dbg_printk(TPACPI_DBG_INIT,
5966 "fan control features disabled by parameter\n");
5967 }
5968
5969 /* update fan_control_desired_level */
5970 if (fan_status_access_mode != TPACPI_FAN_NONE)
5971 fan_get_status_safe(NULL);
5972
5973 if (fan_status_access_mode != TPACPI_FAN_NONE ||
5974 fan_control_access_mode != TPACPI_FAN_WR_NONE) {
5975 rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
5976 &fan_attr_group);
5977 if (rc < 0)
5978 return rc;
5979
5980 rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
5981 &driver_attr_fan_watchdog);
5982 if (rc < 0) {
5983 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
5984 &fan_attr_group);
5985 return rc;
5986 }
5987 return 0;
5988 } else
5989 return 1;
5990}
5991
5992static void fan_exit(void)
5993{
5994 vdbg_printk(TPACPI_DBG_EXIT,
5995 "cancelling any pending fan watchdog tasks\n");
5996
5997 /* FIXME: can we really do this unconditionally? */
5998 sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
5999 driver_remove_file(&tpacpi_hwmon_pdriver.driver,
6000 &driver_attr_fan_watchdog);
6001
6002 cancel_delayed_work(&fan_watchdog_task);
6003 flush_workqueue(tpacpi_wq);
6004}
6005
6006static void fan_suspend(pm_message_t state)
6007{
6008 int rc;
6009
6010 if (!fan_control_allowed)
6011 return;
6012
6013 /* Store fan status in cache */
6014 fan_control_resume_level = 0;
6015 rc = fan_get_status_safe(&fan_control_resume_level);
6016 if (rc < 0)
6017 printk(TPACPI_NOTICE
6018 "failed to read fan level for later "
6019 "restore during resume: %d\n", rc);
6020
6021 /* if it is undefined, don't attempt to restore it.
6022 * KEEP THIS LAST */
6023 if (tp_features.fan_ctrl_status_undef)
6024 fan_control_resume_level = 0;
6025}
6026
6027static void fan_resume(void)
6028{
6029 u8 current_level = 7;
6030 bool do_set = false;
6031 int rc;
6032
6033 /* DSDT *always* updates status on resume */
6034 tp_features.fan_ctrl_status_undef = 0;
6035
6036 if (!fan_control_allowed ||
6037 !fan_control_resume_level ||
6038 (fan_get_status_safe(&current_level) < 0))
6039 return;
6040
6041 switch (fan_control_access_mode) {
6042 case TPACPI_FAN_WR_ACPI_SFAN:
6043 /* never decrease fan level */
6044 do_set = (fan_control_resume_level > current_level);
6045 break;
6046 case TPACPI_FAN_WR_ACPI_FANS:
6047 case TPACPI_FAN_WR_TPEC:
6048 /* never decrease fan level, scale is:
6049 * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO
6050 *
6051 * We expect the firmware to set either 7 or AUTO, but we
6052 * handle FULLSPEED out of paranoia.
6053 *
6054 * So, we can safely only restore FULLSPEED or 7, anything
6055 * else could slow the fan. Restoring AUTO is useless, at
6056 * best that's exactly what the DSDT already set (it is the
6057 * slower it uses).
6058 *
6059 * Always keep in mind that the DSDT *will* have set the
6060 * fans to what the vendor supposes is the best level. We
6061 * muck with it only to speed the fan up.
6062 */
6063 if (fan_control_resume_level != 7 &&
6064 !(fan_control_resume_level & TP_EC_FAN_FULLSPEED))
6065 return;
6066 else
6067 do_set = !(current_level & TP_EC_FAN_FULLSPEED) &&
6068 (current_level != fan_control_resume_level);
6069 break;
6070 default:
6071 return;
6072 }
6073 if (do_set) {
6074 printk(TPACPI_NOTICE
6075 "restoring fan level to 0x%02x\n",
6076 fan_control_resume_level);
6077 rc = fan_set_level_safe(fan_control_resume_level);
6078 if (rc < 0)
6079 printk(TPACPI_NOTICE
6080 "failed to restore fan level: %d\n", rc);
6081 }
6082}
6083
6084static int fan_read(char *p)
6085{
6086 int len = 0;
6087 int rc;
6088 u8 status;
6089 unsigned int speed = 0;
6090
6091 switch (fan_status_access_mode) {
6092 case TPACPI_FAN_RD_ACPI_GFAN:
6093 /* 570, 600e/x, 770e, 770x */
6094 rc = fan_get_status_safe(&status);
6095 if (rc < 0)
6096 return rc;
6097
6098 len += sprintf(p + len, "status:\t\t%s\n"
6099 "level:\t\t%d\n",
6100 (status != 0) ? "enabled" : "disabled", status);
6101 break;
6102
6103 case TPACPI_FAN_RD_TPEC:
6104 /* all except 570, 600e/x, 770e, 770x */
6105 rc = fan_get_status_safe(&status);
6106 if (rc < 0)
6107 return rc;
6108
6109 if (unlikely(tp_features.fan_ctrl_status_undef)) {
6110 if (status != fan_control_initial_status)
6111 tp_features.fan_ctrl_status_undef = 0;
6112 else
6113 /* Return most likely status. In fact, it
6114 * might be the only possible status */
6115 status = TP_EC_FAN_AUTO;
6116 }
6117
6118 len += sprintf(p + len, "status:\t\t%s\n",
6119 (status != 0) ? "enabled" : "disabled");
6120
6121 rc = fan_get_speed(&speed);
6122 if (rc < 0)
6123 return rc;
6124
6125 len += sprintf(p + len, "speed:\t\t%d\n", speed);
6126
6127 if (status & TP_EC_FAN_FULLSPEED)
6128 /* Disengaged mode takes precedence */
6129 len += sprintf(p + len, "level:\t\tdisengaged\n");
6130 else if (status & TP_EC_FAN_AUTO)
6131 len += sprintf(p + len, "level:\t\tauto\n");
6132 else
6133 len += sprintf(p + len, "level:\t\t%d\n", status);
6134 break;
6135
6136 case TPACPI_FAN_NONE:
6137 default:
6138 len += sprintf(p + len, "status:\t\tnot supported\n");
6139 }
6140
6141 if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
6142 len += sprintf(p + len, "commands:\tlevel <level>");
6143
6144 switch (fan_control_access_mode) {
6145 case TPACPI_FAN_WR_ACPI_SFAN:
6146 len += sprintf(p + len, " (<level> is 0-7)\n");
6147 break;
6148
6149 default:
6150 len += sprintf(p + len, " (<level> is 0-7, "
6151 "auto, disengaged, full-speed)\n");
6152 break;
6153 }
6154 }
6155
6156 if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
6157 len += sprintf(p + len, "commands:\tenable, disable\n"
6158 "commands:\twatchdog <timeout> (<timeout> "
6159 "is 0 (off), 1-120 (seconds))\n");
6160
6161 if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
6162 len += sprintf(p + len, "commands:\tspeed <speed>"
6163 " (<speed> is 0-65535)\n");
6164
6165 return len;
6166}
6167
6168static int fan_write_cmd_level(const char *cmd, int *rc)
6169{
6170 int level;
6171
6172 if (strlencmp(cmd, "level auto") == 0)
6173 level = TP_EC_FAN_AUTO;
6174 else if ((strlencmp(cmd, "level disengaged") == 0) |
6175 (strlencmp(cmd, "level full-speed") == 0))
6176 level = TP_EC_FAN_FULLSPEED;
6177 else if (sscanf(cmd, "level %d", &level) != 1)
6178 return 0;
6179
6180 *rc = fan_set_level_safe(level);
6181 if (*rc == -ENXIO)
6182 printk(TPACPI_ERR "level command accepted for unsupported "
6183 "access mode %d", fan_control_access_mode);
6184
6185 return 1;
6186}
6187
6188static int fan_write_cmd_enable(const char *cmd, int *rc)
6189{
6190 if (strlencmp(cmd, "enable") != 0)
6191 return 0;
6192
6193 *rc = fan_set_enable();
6194 if (*rc == -ENXIO)
6195 printk(TPACPI_ERR "enable command accepted for unsupported "
6196 "access mode %d", fan_control_access_mode);
6197
6198 return 1;
6199}
6200
6201static int fan_write_cmd_disable(const char *cmd, int *rc)
6202{
6203 if (strlencmp(cmd, "disable") != 0)
6204 return 0;
6205
6206 *rc = fan_set_disable();
6207 if (*rc == -ENXIO)
6208 printk(TPACPI_ERR "disable command accepted for unsupported "
6209 "access mode %d", fan_control_access_mode);
6210
6211 return 1;
6212}
6213
6214static int fan_write_cmd_speed(const char *cmd, int *rc)
6215{
6216 int speed;
6217
6218 /* TODO:
6219 * Support speed <low> <medium> <high> ? */
6220
6221 if (sscanf(cmd, "speed %d", &speed) != 1)
6222 return 0;
6223
6224 *rc = fan_set_speed(speed);
6225 if (*rc == -ENXIO)
6226 printk(TPACPI_ERR "speed command accepted for unsupported "
6227 "access mode %d", fan_control_access_mode);
6228
6229 return 1;
6230}
6231
6232static int fan_write_cmd_watchdog(const char *cmd, int *rc)
6233{
6234 int interval;
6235
6236 if (sscanf(cmd, "watchdog %d", &interval) != 1)
6237 return 0;
6238
6239 if (interval < 0 || interval > 120)
6240 *rc = -EINVAL;
6241 else
6242 fan_watchdog_maxinterval = interval;
6243
6244 return 1;
6245}
6246
6247static int fan_write(char *buf)
6248{
6249 char *cmd;
6250 int rc = 0;
6251
6252 while (!rc && (cmd = next_cmd(&buf))) {
6253 if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
6254 fan_write_cmd_level(cmd, &rc)) &&
6255 !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
6256 (fan_write_cmd_enable(cmd, &rc) ||
6257 fan_write_cmd_disable(cmd, &rc) ||
6258 fan_write_cmd_watchdog(cmd, &rc))) &&
6259 !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
6260 fan_write_cmd_speed(cmd, &rc))
6261 )
6262 rc = -EINVAL;
6263 else if (!rc)
6264 fan_watchdog_reset();
6265 }
6266
6267 return rc;
6268}
6269
6270static struct ibm_struct fan_driver_data = {
6271 .name = "fan",
6272 .read = fan_read,
6273 .write = fan_write,
6274 .exit = fan_exit,
6275 .suspend = fan_suspend,
6276 .resume = fan_resume,
6277};
6278
6279/****************************************************************************
6280 ****************************************************************************
6281 *
6282 * Infrastructure
6283 *
6284 ****************************************************************************
6285 ****************************************************************************/
6286
6287/* sysfs name ---------------------------------------------------------- */
6288static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
6289 struct device_attribute *attr,
6290 char *buf)
6291{
6292 return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
6293}
6294
6295static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
6296 __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
6297
6298/* --------------------------------------------------------------------- */
6299
6300/* /proc support */
6301static struct proc_dir_entry *proc_dir;
6302
6303/*
6304 * Module and infrastructure proble, init and exit handling
6305 */
6306
6307static int force_load;
6308
6309#ifdef CONFIG_THINKPAD_ACPI_DEBUG
6310static const char * __init str_supported(int is_supported)
6311{
6312 static char text_unsupported[] __initdata = "not supported";
6313
6314 return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
6315}
6316#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
6317
6318static void ibm_exit(struct ibm_struct *ibm)
6319{
6320 dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
6321
6322 list_del_init(&ibm->all_drivers);
6323
6324 if (ibm->flags.acpi_notify_installed) {
6325 dbg_printk(TPACPI_DBG_EXIT,
6326 "%s: acpi_remove_notify_handler\n", ibm->name);
6327 BUG_ON(!ibm->acpi);
6328 acpi_remove_notify_handler(*ibm->acpi->handle,
6329 ibm->acpi->type,
6330 dispatch_acpi_notify);
6331 ibm->flags.acpi_notify_installed = 0;
6332 ibm->flags.acpi_notify_installed = 0;
6333 }
6334
6335 if (ibm->flags.proc_created) {
6336 dbg_printk(TPACPI_DBG_EXIT,
6337 "%s: remove_proc_entry\n", ibm->name);
6338 remove_proc_entry(ibm->name, proc_dir);
6339 ibm->flags.proc_created = 0;
6340 }
6341
6342 if (ibm->flags.acpi_driver_registered) {
6343 dbg_printk(TPACPI_DBG_EXIT,
6344 "%s: acpi_bus_unregister_driver\n", ibm->name);
6345 BUG_ON(!ibm->acpi);
6346 acpi_bus_unregister_driver(ibm->acpi->driver);
6347 kfree(ibm->acpi->driver);
6348 ibm->acpi->driver = NULL;
6349 ibm->flags.acpi_driver_registered = 0;
6350 }
6351
6352 if (ibm->flags.init_called && ibm->exit) {
6353 ibm->exit();
6354 ibm->flags.init_called = 0;
6355 }
6356
6357 dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
6358}
6359
6360static int __init ibm_init(struct ibm_init_struct *iibm)
6361{
6362 int ret;
6363 struct ibm_struct *ibm = iibm->data;
6364 struct proc_dir_entry *entry;
6365
6366 BUG_ON(ibm == NULL);
6367
6368 INIT_LIST_HEAD(&ibm->all_drivers);
6369
6370 if (ibm->flags.experimental && !experimental)
6371 return 0;
6372
6373 dbg_printk(TPACPI_DBG_INIT,
6374 "probing for %s\n", ibm->name);
6375
6376 if (iibm->init) {
6377 ret = iibm->init(iibm);
6378 if (ret > 0)
6379 return 0; /* probe failed */
6380 if (ret)
6381 return ret;
6382
6383 ibm->flags.init_called = 1;
6384 }
6385
6386 if (ibm->acpi) {
6387 if (ibm->acpi->hid) {
6388 ret = register_tpacpi_subdriver(ibm);
6389 if (ret)
6390 goto err_out;
6391 }
6392
6393 if (ibm->acpi->notify) {
6394 ret = setup_acpi_notify(ibm);
6395 if (ret == -ENODEV) {
6396 printk(TPACPI_NOTICE "disabling subdriver %s\n",
6397 ibm->name);
6398 ret = 0;
6399 goto err_out;
6400 }
6401 if (ret < 0)
6402 goto err_out;
6403 }
6404 }
6405
6406 dbg_printk(TPACPI_DBG_INIT,
6407 "%s installed\n", ibm->name);
6408
6409 if (ibm->read) {
6410 entry = create_proc_entry(ibm->name,
6411 S_IFREG | S_IRUGO | S_IWUSR,
6412 proc_dir);
6413 if (!entry) {
6414 printk(TPACPI_ERR "unable to create proc entry %s\n",
6415 ibm->name);
6416 ret = -ENODEV;
6417 goto err_out;
6418 }
6419 entry->owner = THIS_MODULE;
6420 entry->data = ibm;
6421 entry->read_proc = &dispatch_procfs_read;
6422 if (ibm->write)
6423 entry->write_proc = &dispatch_procfs_write;
6424 ibm->flags.proc_created = 1;
6425 }
6426
6427 list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
6428
6429 return 0;
6430
6431err_out:
6432 dbg_printk(TPACPI_DBG_INIT,
6433 "%s: at error exit path with result %d\n",
6434 ibm->name, ret);
6435
6436 ibm_exit(ibm);
6437 return (ret < 0)? ret : 0;
6438}
6439
6440/* Probing */
6441
6442/* returns 0 - probe ok, or < 0 - probe error.
6443 * Probe ok doesn't mean thinkpad found.
6444 * On error, kfree() cleanup on tp->* is not performed, caller must do it */
6445static int __must_check __init get_thinkpad_model_data(
6446 struct thinkpad_id_data *tp)
6447{
6448 const struct dmi_device *dev = NULL;
6449 char ec_fw_string[18];
6450 char const *s;
6451
6452 if (!tp)
6453 return -EINVAL;
6454
6455 memset(tp, 0, sizeof(*tp));
6456
6457 if (dmi_name_in_vendors("IBM"))
6458 tp->vendor = PCI_VENDOR_ID_IBM;
6459 else if (dmi_name_in_vendors("LENOVO"))
6460 tp->vendor = PCI_VENDOR_ID_LENOVO;
6461 else
6462 return 0;
6463
6464 s = dmi_get_system_info(DMI_BIOS_VERSION);
6465 tp->bios_version_str = kstrdup(s, GFP_KERNEL);
6466 if (s && !tp->bios_version_str)
6467 return -ENOMEM;
6468 if (!tp->bios_version_str)
6469 return 0;
6470 tp->bios_model = tp->bios_version_str[0]
6471 | (tp->bios_version_str[1] << 8);
6472
6473 /*
6474 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
6475 * X32 or newer, all Z series; Some models must have an
6476 * up-to-date BIOS or they will not be detected.
6477 *
6478 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
6479 */
6480 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
6481 if (sscanf(dev->name,
6482 "IBM ThinkPad Embedded Controller -[%17c",
6483 ec_fw_string) == 1) {
6484 ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
6485 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
6486
6487 tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
6488 if (!tp->ec_version_str)
6489 return -ENOMEM;
6490 tp->ec_model = ec_fw_string[0]
6491 | (ec_fw_string[1] << 8);
6492 break;
6493 }
6494 }
6495
6496 s = dmi_get_system_info(DMI_PRODUCT_VERSION);
6497 if (s && !strnicmp(s, "ThinkPad", 8)) {
6498 tp->model_str = kstrdup(s, GFP_KERNEL);
6499 if (!tp->model_str)
6500 return -ENOMEM;
6501 }
6502
6503 s = dmi_get_system_info(DMI_PRODUCT_NAME);
6504 tp->nummodel_str = kstrdup(s, GFP_KERNEL);
6505 if (s && !tp->nummodel_str)
6506 return -ENOMEM;
6507
6508 return 0;
6509}
6510
6511static int __init probe_for_thinkpad(void)
6512{
6513 int is_thinkpad;
6514
6515 if (acpi_disabled)
6516 return -ENODEV;
6517
6518 /*
6519 * Non-ancient models have better DMI tagging, but very old models
6520 * don't.
6521 */
6522 is_thinkpad = (thinkpad_id.model_str != NULL);
6523
6524 /* ec is required because many other handles are relative to it */
6525 TPACPI_ACPIHANDLE_INIT(ec);
6526 if (!ec_handle) {
6527 if (is_thinkpad)
6528 printk(TPACPI_ERR
6529 "Not yet supported ThinkPad detected!\n");
6530 return -ENODEV;
6531 }
6532
6533 /*
6534 * Risks a regression on very old machines, but reduces potential
6535 * false positives a damn great deal
6536 */
6537 if (!is_thinkpad)
6538 is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
6539
6540 if (!is_thinkpad && !force_load)
6541 return -ENODEV;
6542
6543 return 0;
6544}
6545
6546
6547/* Module init, exit, parameters */
6548
6549static struct ibm_init_struct ibms_init[] __initdata = {
6550 {
6551 .init = thinkpad_acpi_driver_init,
6552 .data = &thinkpad_acpi_driver_data,
6553 },
6554 {
6555 .init = hotkey_init,
6556 .data = &hotkey_driver_data,
6557 },
6558 {
6559 .init = bluetooth_init,
6560 .data = &bluetooth_driver_data,
6561 },
6562 {
6563 .init = wan_init,
6564 .data = &wan_driver_data,
6565 },
6566#ifdef CONFIG_THINKPAD_ACPI_VIDEO
6567 {
6568 .init = video_init,
6569 .data = &video_driver_data,
6570 },
6571#endif
6572 {
6573 .init = light_init,
6574 .data = &light_driver_data,
6575 },
6576#ifdef CONFIG_THINKPAD_ACPI_DOCK
6577 {
6578 .init = dock_init,
6579 .data = &dock_driver_data[0],
6580 },
6581 {
6582 .init = dock_init2,
6583 .data = &dock_driver_data[1],
6584 },
6585#endif
6586#ifdef CONFIG_THINKPAD_ACPI_BAY
6587 {
6588 .init = bay_init,
6589 .data = &bay_driver_data,
6590 },
6591#endif
6592 {
6593 .init = cmos_init,
6594 .data = &cmos_driver_data,
6595 },
6596 {
6597 .init = led_init,
6598 .data = &led_driver_data,
6599 },
6600 {
6601 .init = beep_init,
6602 .data = &beep_driver_data,
6603 },
6604 {
6605 .init = thermal_init,
6606 .data = &thermal_driver_data,
6607 },
6608 {
6609 .data = &ecdump_driver_data,
6610 },
6611 {
6612 .init = brightness_init,
6613 .data = &brightness_driver_data,
6614 },
6615 {
6616 .data = &volume_driver_data,
6617 },
6618 {
6619 .init = fan_init,
6620 .data = &fan_driver_data,
6621 },
6622};
6623
6624static int __init set_ibm_param(const char *val, struct kernel_param *kp)
6625{
6626 unsigned int i;
6627 struct ibm_struct *ibm;
6628
6629 if (!kp || !kp->name || !val)
6630 return -EINVAL;
6631
6632 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
6633 ibm = ibms_init[i].data;
6634 WARN_ON(ibm == NULL);
6635
6636 if (!ibm || !ibm->name)
6637 continue;
6638
6639 if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
6640 if (strlen(val) > sizeof(ibms_init[i].param) - 2)
6641 return -ENOSPC;
6642 strcpy(ibms_init[i].param, val);
6643 strcat(ibms_init[i].param, ",");
6644 return 0;
6645 }
6646 }
6647
6648 return -EINVAL;
6649}
6650
6651module_param(experimental, int, 0);
6652MODULE_PARM_DESC(experimental,
6653 "Enables experimental features when non-zero");
6654
6655module_param_named(debug, dbg_level, uint, 0);
6656MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
6657
6658module_param(force_load, bool, 0);
6659MODULE_PARM_DESC(force_load,
6660 "Attempts to load the driver even on a "
6661 "mis-identified ThinkPad when true");
6662
6663module_param_named(fan_control, fan_control_allowed, bool, 0);
6664MODULE_PARM_DESC(fan_control,
6665 "Enables setting fan parameters features when true");
6666
6667module_param_named(brightness_mode, brightness_mode, int, 0);
6668MODULE_PARM_DESC(brightness_mode,
6669 "Selects brightness control strategy: "
6670 "0=auto, 1=EC, 2=CMOS, 3=both");
6671
6672module_param(brightness_enable, uint, 0);
6673MODULE_PARM_DESC(brightness_enable,
6674 "Enables backlight control when 1, disables when 0");
6675
6676module_param(hotkey_report_mode, uint, 0);
6677MODULE_PARM_DESC(hotkey_report_mode,
6678 "used for backwards compatibility with userspace, "
6679 "see documentation");
6680
6681#define TPACPI_PARAM(feature) \
6682 module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
6683 MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
6684 "at module load, see documentation")
6685
6686TPACPI_PARAM(hotkey);
6687TPACPI_PARAM(bluetooth);
6688TPACPI_PARAM(video);
6689TPACPI_PARAM(light);
6690#ifdef CONFIG_THINKPAD_ACPI_DOCK
6691TPACPI_PARAM(dock);
6692#endif
6693#ifdef CONFIG_THINKPAD_ACPI_BAY
6694TPACPI_PARAM(bay);
6695#endif /* CONFIG_THINKPAD_ACPI_BAY */
6696TPACPI_PARAM(cmos);
6697TPACPI_PARAM(led);
6698TPACPI_PARAM(beep);
6699TPACPI_PARAM(ecdump);
6700TPACPI_PARAM(brightness);
6701TPACPI_PARAM(volume);
6702TPACPI_PARAM(fan);
6703
6704static void thinkpad_acpi_module_exit(void)
6705{
6706 struct ibm_struct *ibm, *itmp;
6707
6708 tpacpi_lifecycle = TPACPI_LIFE_EXITING;
6709
6710 list_for_each_entry_safe_reverse(ibm, itmp,
6711 &tpacpi_all_drivers,
6712 all_drivers) {
6713 ibm_exit(ibm);
6714 }
6715
6716 dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
6717
6718 if (tpacpi_inputdev) {
6719 if (tp_features.input_device_registered)
6720 input_unregister_device(tpacpi_inputdev);
6721 else
6722 input_free_device(tpacpi_inputdev);
6723 }
6724
6725 if (tpacpi_hwmon)
6726 hwmon_device_unregister(tpacpi_hwmon);
6727
6728 if (tp_features.sensors_pdev_attrs_registered)
6729 device_remove_file(&tpacpi_sensors_pdev->dev,
6730 &dev_attr_thinkpad_acpi_pdev_name);
6731 if (tpacpi_sensors_pdev)
6732 platform_device_unregister(tpacpi_sensors_pdev);
6733 if (tpacpi_pdev)
6734 platform_device_unregister(tpacpi_pdev);
6735
6736 if (tp_features.sensors_pdrv_attrs_registered)
6737 tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
6738 if (tp_features.platform_drv_attrs_registered)
6739 tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
6740
6741 if (tp_features.sensors_pdrv_registered)
6742 platform_driver_unregister(&tpacpi_hwmon_pdriver);
6743
6744 if (tp_features.platform_drv_registered)
6745 platform_driver_unregister(&tpacpi_pdriver);
6746
6747 if (proc_dir)
6748 remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
6749
6750 if (tpacpi_wq)
6751 destroy_workqueue(tpacpi_wq);
6752
6753 kfree(thinkpad_id.bios_version_str);
6754 kfree(thinkpad_id.ec_version_str);
6755 kfree(thinkpad_id.model_str);
6756}
6757
6758
6759static int __init thinkpad_acpi_module_init(void)
6760{
6761 int ret, i;
6762
6763 tpacpi_lifecycle = TPACPI_LIFE_INIT;
6764
6765 /* Parameter checking */
6766 if (hotkey_report_mode > 2)
6767 return -EINVAL;
6768
6769 /* Driver-level probe */
6770
6771 ret = get_thinkpad_model_data(&thinkpad_id);
6772 if (ret) {
6773 printk(TPACPI_ERR
6774 "unable to get DMI data: %d\n", ret);
6775 thinkpad_acpi_module_exit();
6776 return ret;
6777 }
6778 ret = probe_for_thinkpad();
6779 if (ret) {
6780 thinkpad_acpi_module_exit();
6781 return ret;
6782 }
6783
6784 /* Driver initialization */
6785
6786 TPACPI_ACPIHANDLE_INIT(ecrd);
6787 TPACPI_ACPIHANDLE_INIT(ecwr);
6788
6789 tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
6790 if (!tpacpi_wq) {
6791 thinkpad_acpi_module_exit();
6792 return -ENOMEM;
6793 }
6794
6795 proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
6796 if (!proc_dir) {
6797 printk(TPACPI_ERR
6798 "unable to create proc dir " TPACPI_PROC_DIR);
6799 thinkpad_acpi_module_exit();
6800 return -ENODEV;
6801 }
6802 proc_dir->owner = THIS_MODULE;
6803
6804 ret = platform_driver_register(&tpacpi_pdriver);
6805 if (ret) {
6806 printk(TPACPI_ERR
6807 "unable to register main platform driver\n");
6808 thinkpad_acpi_module_exit();
6809 return ret;
6810 }
6811 tp_features.platform_drv_registered = 1;
6812
6813 ret = platform_driver_register(&tpacpi_hwmon_pdriver);
6814 if (ret) {
6815 printk(TPACPI_ERR
6816 "unable to register hwmon platform driver\n");
6817 thinkpad_acpi_module_exit();
6818 return ret;
6819 }
6820 tp_features.sensors_pdrv_registered = 1;
6821
6822 ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
6823 if (!ret) {
6824 tp_features.platform_drv_attrs_registered = 1;
6825 ret = tpacpi_create_driver_attributes(
6826 &tpacpi_hwmon_pdriver.driver);
6827 }
6828 if (ret) {
6829 printk(TPACPI_ERR
6830 "unable to create sysfs driver attributes\n");
6831 thinkpad_acpi_module_exit();
6832 return ret;
6833 }
6834 tp_features.sensors_pdrv_attrs_registered = 1;
6835
6836
6837 /* Device initialization */
6838 tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
6839 NULL, 0);
6840 if (IS_ERR(tpacpi_pdev)) {
6841 ret = PTR_ERR(tpacpi_pdev);
6842 tpacpi_pdev = NULL;
6843 printk(TPACPI_ERR "unable to register platform device\n");
6844 thinkpad_acpi_module_exit();
6845 return ret;
6846 }
6847 tpacpi_sensors_pdev = platform_device_register_simple(
6848 TPACPI_HWMON_DRVR_NAME,
6849 -1, NULL, 0);
6850 if (IS_ERR(tpacpi_sensors_pdev)) {
6851 ret = PTR_ERR(tpacpi_sensors_pdev);
6852 tpacpi_sensors_pdev = NULL;
6853 printk(TPACPI_ERR
6854 "unable to register hwmon platform device\n");
6855 thinkpad_acpi_module_exit();
6856 return ret;
6857 }
6858 ret = device_create_file(&tpacpi_sensors_pdev->dev,
6859 &dev_attr_thinkpad_acpi_pdev_name);
6860 if (ret) {
6861 printk(TPACPI_ERR
6862 "unable to create sysfs hwmon device attributes\n");
6863 thinkpad_acpi_module_exit();
6864 return ret;
6865 }
6866 tp_features.sensors_pdev_attrs_registered = 1;
6867 tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
6868 if (IS_ERR(tpacpi_hwmon)) {
6869 ret = PTR_ERR(tpacpi_hwmon);
6870 tpacpi_hwmon = NULL;
6871 printk(TPACPI_ERR "unable to register hwmon device\n");
6872 thinkpad_acpi_module_exit();
6873 return ret;
6874 }
6875 mutex_init(&tpacpi_inputdev_send_mutex);
6876 tpacpi_inputdev = input_allocate_device();
6877 if (!tpacpi_inputdev) {
6878 printk(TPACPI_ERR "unable to allocate input device\n");
6879 thinkpad_acpi_module_exit();
6880 return -ENOMEM;
6881 } else {
6882 /* Prepare input device, but don't register */
6883 tpacpi_inputdev->name = "ThinkPad Extra Buttons";
6884 tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
6885 tpacpi_inputdev->id.bustype = BUS_HOST;
6886 tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
6887 thinkpad_id.vendor :
6888 PCI_VENDOR_ID_IBM;
6889 tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
6890 tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
6891 }
6892 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
6893 ret = ibm_init(&ibms_init[i]);
6894 if (ret >= 0 && *ibms_init[i].param)
6895 ret = ibms_init[i].data->write(ibms_init[i].param);
6896 if (ret < 0) {
6897 thinkpad_acpi_module_exit();
6898 return ret;
6899 }
6900 }
6901 ret = input_register_device(tpacpi_inputdev);
6902 if (ret < 0) {
6903 printk(TPACPI_ERR "unable to register input device\n");
6904 thinkpad_acpi_module_exit();
6905 return ret;
6906 } else {
6907 tp_features.input_device_registered = 1;
6908 }
6909
6910 tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
6911 return 0;
6912}
6913
6914/* Please remove this in year 2009 */
6915MODULE_ALIAS("ibm_acpi");
6916
6917MODULE_ALIAS(TPACPI_DRVR_SHORTNAME);
6918
6919/*
6920 * DMI matching for module autoloading
6921 *
6922 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
6923 * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
6924 *
6925 * Only models listed in thinkwiki will be supported, so add yours
6926 * if it is not there yet.
6927 */
6928#define IBM_BIOS_MODULE_ALIAS(__type) \
6929 MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
6930
6931/* Non-ancient thinkpads */
6932MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");
6933MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");
6934
6935/* Ancient thinkpad BIOSes have to be identified by
6936 * BIOS type or model number, and there are far less
6937 * BIOS types than model numbers... */
6938IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");
6939IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");
6940IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
6941
6942MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
6943MODULE_DESCRIPTION(TPACPI_DESC);
6944MODULE_VERSION(TPACPI_VERSION);
6945MODULE_LICENSE("GPL");
6946
6947module_init(thinkpad_acpi_module_init);
6948module_exit(thinkpad_acpi_module_exit);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
new file mode 100644
index 000000000000..40e60fc2e596
--- /dev/null
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -0,0 +1,863 @@
1/*
2 * toshiba_acpi.c - Toshiba Laptop ACPI Extras
3 *
4 *
5 * Copyright (C) 2002-2004 John Belmonte
6 * Copyright (C) 2008 Philip Langdale
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 *
23 * The devolpment page for this driver is located at
24 * http://memebeam.org/toys/ToshibaAcpiDriver.
25 *
26 * Credits:
27 * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse
28 * engineering the Windows drivers
29 * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5
30 * Rob Miller - TV out and hotkeys help
31 *
32 *
33 * TODO
34 *
35 */
36
37#define TOSHIBA_ACPI_VERSION "0.19"
38#define PROC_INTERFACE_VERSION 1
39
40#include <linux/kernel.h>
41#include <linux/module.h>
42#include <linux/init.h>
43#include <linux/types.h>
44#include <linux/proc_fs.h>
45#include <linux/backlight.h>
46#include <linux/platform_device.h>
47#include <linux/rfkill.h>
48#include <linux/input-polldev.h>
49
50#include <asm/uaccess.h>
51
52#include <acpi/acpi_drivers.h>
53
54MODULE_AUTHOR("John Belmonte");
55MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
56MODULE_LICENSE("GPL");
57
58#define MY_LOGPREFIX "toshiba_acpi: "
59#define MY_ERR KERN_ERR MY_LOGPREFIX
60#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
61#define MY_INFO KERN_INFO MY_LOGPREFIX
62
63/* Toshiba ACPI method paths */
64#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
65#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
66#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
67#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
68
69/* Toshiba HCI interface definitions
70 *
71 * HCI is Toshiba's "Hardware Control Interface" which is supposed to
72 * be uniform across all their models. Ideally we would just call
73 * dedicated ACPI methods instead of using this primitive interface.
74 * However the ACPI methods seem to be incomplete in some areas (for
75 * example they allow setting, but not reading, the LCD brightness value),
76 * so this is still useful.
77 */
78
79#define HCI_WORDS 6
80
81/* operations */
82#define HCI_SET 0xff00
83#define HCI_GET 0xfe00
84
85/* return codes */
86#define HCI_SUCCESS 0x0000
87#define HCI_FAILURE 0x1000
88#define HCI_NOT_SUPPORTED 0x8000
89#define HCI_EMPTY 0x8c00
90
91/* registers */
92#define HCI_FAN 0x0004
93#define HCI_SYSTEM_EVENT 0x0016
94#define HCI_VIDEO_OUT 0x001c
95#define HCI_HOTKEY_EVENT 0x001e
96#define HCI_LCD_BRIGHTNESS 0x002a
97#define HCI_WIRELESS 0x0056
98
99/* field definitions */
100#define HCI_LCD_BRIGHTNESS_BITS 3
101#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
102#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
103#define HCI_VIDEO_OUT_LCD 0x1
104#define HCI_VIDEO_OUT_CRT 0x2
105#define HCI_VIDEO_OUT_TV 0x4
106#define HCI_WIRELESS_KILL_SWITCH 0x01
107#define HCI_WIRELESS_BT_PRESENT 0x0f
108#define HCI_WIRELESS_BT_ATTACH 0x40
109#define HCI_WIRELESS_BT_POWER 0x80
110
111static const struct acpi_device_id toshiba_device_ids[] = {
112 {"TOS6200", 0},
113 {"TOS6208", 0},
114 {"TOS1900", 0},
115 {"", 0},
116};
117MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
118
119/* utility
120 */
121
122static __inline__ void _set_bit(u32 * word, u32 mask, int value)
123{
124 *word = (*word & ~mask) | (mask * value);
125}
126
127/* acpi interface wrappers
128 */
129
130static int is_valid_acpi_path(const char *methodName)
131{
132 acpi_handle handle;
133 acpi_status status;
134
135 status = acpi_get_handle(NULL, (char *)methodName, &handle);
136 return !ACPI_FAILURE(status);
137}
138
139static int write_acpi_int(const char *methodName, int val)
140{
141 struct acpi_object_list params;
142 union acpi_object in_objs[1];
143 acpi_status status;
144
145 params.count = ARRAY_SIZE(in_objs);
146 params.pointer = in_objs;
147 in_objs[0].type = ACPI_TYPE_INTEGER;
148 in_objs[0].integer.value = val;
149
150 status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
151 return (status == AE_OK);
152}
153
154#if 0
155static int read_acpi_int(const char *methodName, int *pVal)
156{
157 struct acpi_buffer results;
158 union acpi_object out_objs[1];
159 acpi_status status;
160
161 results.length = sizeof(out_objs);
162 results.pointer = out_objs;
163
164 status = acpi_evaluate_object(0, (char *)methodName, 0, &results);
165 *pVal = out_objs[0].integer.value;
166
167 return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER);
168}
169#endif
170
171static const char *method_hci /*= 0*/ ;
172
173/* Perform a raw HCI call. Here we don't care about input or output buffer
174 * format.
175 */
176static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
177{
178 struct acpi_object_list params;
179 union acpi_object in_objs[HCI_WORDS];
180 struct acpi_buffer results;
181 union acpi_object out_objs[HCI_WORDS + 1];
182 acpi_status status;
183 int i;
184
185 params.count = HCI_WORDS;
186 params.pointer = in_objs;
187 for (i = 0; i < HCI_WORDS; ++i) {
188 in_objs[i].type = ACPI_TYPE_INTEGER;
189 in_objs[i].integer.value = in[i];
190 }
191
192 results.length = sizeof(out_objs);
193 results.pointer = out_objs;
194
195 status = acpi_evaluate_object(NULL, (char *)method_hci, &params,
196 &results);
197 if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
198 for (i = 0; i < out_objs->package.count; ++i) {
199 out[i] = out_objs->package.elements[i].integer.value;
200 }
201 }
202
203 return status;
204}
205
206/* common hci tasks (get or set one or two value)
207 *
208 * In addition to the ACPI status, the HCI system returns a result which
209 * may be useful (such as "not supported").
210 */
211
212static acpi_status hci_write1(u32 reg, u32 in1, u32 * result)
213{
214 u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
215 u32 out[HCI_WORDS];
216 acpi_status status = hci_raw(in, out);
217 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
218 return status;
219}
220
221static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result)
222{
223 u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
224 u32 out[HCI_WORDS];
225 acpi_status status = hci_raw(in, out);
226 *out1 = out[2];
227 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
228 return status;
229}
230
231static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result)
232{
233 u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
234 u32 out[HCI_WORDS];
235 acpi_status status = hci_raw(in, out);
236 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
237 return status;
238}
239
240static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result)
241{
242 u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
243 u32 out[HCI_WORDS];
244 acpi_status status = hci_raw(in, out);
245 *out1 = out[2];
246 *out2 = out[3];
247 *result = (status == AE_OK) ? out[0] : HCI_FAILURE;
248 return status;
249}
250
251struct toshiba_acpi_dev {
252 struct platform_device *p_dev;
253 struct rfkill *rfk_dev;
254 struct input_polled_dev *poll_dev;
255
256 const char *bt_name;
257 const char *rfk_name;
258
259 bool last_rfk_state;
260
261 struct mutex mutex;
262};
263
264static struct toshiba_acpi_dev toshiba_acpi = {
265 .bt_name = "Toshiba Bluetooth",
266 .rfk_name = "Toshiba RFKill Switch",
267 .last_rfk_state = false,
268};
269
270/* Bluetooth rfkill handlers */
271
272static u32 hci_get_bt_present(bool *present)
273{
274 u32 hci_result;
275 u32 value, value2;
276
277 value = 0;
278 value2 = 0;
279 hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
280 if (hci_result == HCI_SUCCESS)
281 *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
282
283 return hci_result;
284}
285
286static u32 hci_get_bt_on(bool *on)
287{
288 u32 hci_result;
289 u32 value, value2;
290
291 value = 0;
292 value2 = 0x0001;
293 hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
294 if (hci_result == HCI_SUCCESS)
295 *on = (value & HCI_WIRELESS_BT_POWER) &&
296 (value & HCI_WIRELESS_BT_ATTACH);
297
298 return hci_result;
299}
300
301static u32 hci_get_radio_state(bool *radio_state)
302{
303 u32 hci_result;
304 u32 value, value2;
305
306 value = 0;
307 value2 = 0x0001;
308 hci_read2(HCI_WIRELESS, &value, &value2, &hci_result);
309
310 *radio_state = value & HCI_WIRELESS_KILL_SWITCH;
311 return hci_result;
312}
313
314static int bt_rfkill_toggle_radio(void *data, enum rfkill_state state)
315{
316 u32 result1, result2;
317 u32 value;
318 bool radio_state;
319 struct toshiba_acpi_dev *dev = data;
320
321 value = (state == RFKILL_STATE_UNBLOCKED);
322
323 if (hci_get_radio_state(&radio_state) != HCI_SUCCESS)
324 return -EFAULT;
325
326 switch (state) {
327 case RFKILL_STATE_UNBLOCKED:
328 if (!radio_state)
329 return -EPERM;
330 break;
331 case RFKILL_STATE_SOFT_BLOCKED:
332 break;
333 default:
334 return -EINVAL;
335 }
336
337 mutex_lock(&dev->mutex);
338 hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1);
339 hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2);
340 mutex_unlock(&dev->mutex);
341
342 if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS)
343 return -EFAULT;
344
345 return 0;
346}
347
348static void bt_poll_rfkill(struct input_polled_dev *poll_dev)
349{
350 bool state_changed;
351 bool new_rfk_state;
352 bool value;
353 u32 hci_result;
354 struct toshiba_acpi_dev *dev = poll_dev->private;
355
356 hci_result = hci_get_radio_state(&value);
357 if (hci_result != HCI_SUCCESS)
358 return; /* Can't do anything useful */
359
360 new_rfk_state = value;
361
362 mutex_lock(&dev->mutex);
363 state_changed = new_rfk_state != dev->last_rfk_state;
364 dev->last_rfk_state = new_rfk_state;
365 mutex_unlock(&dev->mutex);
366
367 if (unlikely(state_changed)) {
368 rfkill_force_state(dev->rfk_dev,
369 new_rfk_state ?
370 RFKILL_STATE_SOFT_BLOCKED :
371 RFKILL_STATE_HARD_BLOCKED);
372 input_report_switch(poll_dev->input, SW_RFKILL_ALL,
373 new_rfk_state);
374 input_sync(poll_dev->input);
375 }
376}
377
378static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ;
379static struct backlight_device *toshiba_backlight_device;
380static int force_fan;
381static int last_key_event;
382static int key_event_valid;
383
384typedef struct _ProcItem {
385 const char *name;
386 char *(*read_func) (char *);
387 unsigned long (*write_func) (const char *, unsigned long);
388} ProcItem;
389
390/* proc file handlers
391 */
392
393static int
394dispatch_read(char *page, char **start, off_t off, int count, int *eof,
395 ProcItem * item)
396{
397 char *p = page;
398 int len;
399
400 if (off == 0)
401 p = item->read_func(p);
402
403 /* ISSUE: I don't understand this code */
404 len = (p - page);
405 if (len <= off + count)
406 *eof = 1;
407 *start = page + off;
408 len -= off;
409 if (len > count)
410 len = count;
411 if (len < 0)
412 len = 0;
413 return len;
414}
415
416static int
417dispatch_write(struct file *file, const char __user * buffer,
418 unsigned long count, ProcItem * item)
419{
420 int result;
421 char *tmp_buffer;
422
423 /* Arg buffer points to userspace memory, which can't be accessed
424 * directly. Since we're making a copy, zero-terminate the
425 * destination so that sscanf can be used on it safely.
426 */
427 tmp_buffer = kmalloc(count + 1, GFP_KERNEL);
428 if (!tmp_buffer)
429 return -ENOMEM;
430
431 if (copy_from_user(tmp_buffer, buffer, count)) {
432 result = -EFAULT;
433 } else {
434 tmp_buffer[count] = 0;
435 result = item->write_func(tmp_buffer, count);
436 }
437 kfree(tmp_buffer);
438 return result;
439}
440
441static int get_lcd(struct backlight_device *bd)
442{
443 u32 hci_result;
444 u32 value;
445
446 hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result);
447 if (hci_result == HCI_SUCCESS) {
448 return (value >> HCI_LCD_BRIGHTNESS_SHIFT);
449 } else
450 return -EFAULT;
451}
452
453static char *read_lcd(char *p)
454{
455 int value = get_lcd(NULL);
456
457 if (value >= 0) {
458 p += sprintf(p, "brightness: %d\n", value);
459 p += sprintf(p, "brightness_levels: %d\n",
460 HCI_LCD_BRIGHTNESS_LEVELS);
461 } else {
462 printk(MY_ERR "Error reading LCD brightness\n");
463 }
464
465 return p;
466}
467
468static int set_lcd(int value)
469{
470 u32 hci_result;
471
472 value = value << HCI_LCD_BRIGHTNESS_SHIFT;
473 hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result);
474 if (hci_result != HCI_SUCCESS)
475 return -EFAULT;
476
477 return 0;
478}
479
480static int set_lcd_status(struct backlight_device *bd)
481{
482 return set_lcd(bd->props.brightness);
483}
484
485static unsigned long write_lcd(const char *buffer, unsigned long count)
486{
487 int value;
488 int ret;
489
490 if (sscanf(buffer, " brightness : %i", &value) == 1 &&
491 value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) {
492 ret = set_lcd(value);
493 if (ret == 0)
494 ret = count;
495 } else {
496 ret = -EINVAL;
497 }
498 return ret;
499}
500
501static char *read_video(char *p)
502{
503 u32 hci_result;
504 u32 value;
505
506 hci_read1(HCI_VIDEO_OUT, &value, &hci_result);
507 if (hci_result == HCI_SUCCESS) {
508 int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
509 int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
510 int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
511 p += sprintf(p, "lcd_out: %d\n", is_lcd);
512 p += sprintf(p, "crt_out: %d\n", is_crt);
513 p += sprintf(p, "tv_out: %d\n", is_tv);
514 } else {
515 printk(MY_ERR "Error reading video out status\n");
516 }
517
518 return p;
519}
520
521static unsigned long write_video(const char *buffer, unsigned long count)
522{
523 int value;
524 int remain = count;
525 int lcd_out = -1;
526 int crt_out = -1;
527 int tv_out = -1;
528 u32 hci_result;
529 u32 video_out;
530
531 /* scan expression. Multiple expressions may be delimited with ;
532 *
533 * NOTE: to keep scanning simple, invalid fields are ignored
534 */
535 while (remain) {
536 if (sscanf(buffer, " lcd_out : %i", &value) == 1)
537 lcd_out = value & 1;
538 else if (sscanf(buffer, " crt_out : %i", &value) == 1)
539 crt_out = value & 1;
540 else if (sscanf(buffer, " tv_out : %i", &value) == 1)
541 tv_out = value & 1;
542 /* advance to one character past the next ; */
543 do {
544 ++buffer;
545 --remain;
546 }
547 while (remain && *(buffer - 1) != ';');
548 }
549
550 hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result);
551 if (hci_result == HCI_SUCCESS) {
552 unsigned int new_video_out = video_out;
553 if (lcd_out != -1)
554 _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out);
555 if (crt_out != -1)
556 _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out);
557 if (tv_out != -1)
558 _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
559 /* To avoid unnecessary video disruption, only write the new
560 * video setting if something changed. */
561 if (new_video_out != video_out)
562 write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
563 } else {
564 return -EFAULT;
565 }
566
567 return count;
568}
569
570static char *read_fan(char *p)
571{
572 u32 hci_result;
573 u32 value;
574
575 hci_read1(HCI_FAN, &value, &hci_result);
576 if (hci_result == HCI_SUCCESS) {
577 p += sprintf(p, "running: %d\n", (value > 0));
578 p += sprintf(p, "force_on: %d\n", force_fan);
579 } else {
580 printk(MY_ERR "Error reading fan status\n");
581 }
582
583 return p;
584}
585
586static unsigned long write_fan(const char *buffer, unsigned long count)
587{
588 int value;
589 u32 hci_result;
590
591 if (sscanf(buffer, " force_on : %i", &value) == 1 &&
592 value >= 0 && value <= 1) {
593 hci_write1(HCI_FAN, value, &hci_result);
594 if (hci_result != HCI_SUCCESS)
595 return -EFAULT;
596 else
597 force_fan = value;
598 } else {
599 return -EINVAL;
600 }
601
602 return count;
603}
604
605static char *read_keys(char *p)
606{
607 u32 hci_result;
608 u32 value;
609
610 if (!key_event_valid) {
611 hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
612 if (hci_result == HCI_SUCCESS) {
613 key_event_valid = 1;
614 last_key_event = value;
615 } else if (hci_result == HCI_EMPTY) {
616 /* better luck next time */
617 } else if (hci_result == HCI_NOT_SUPPORTED) {
618 /* This is a workaround for an unresolved issue on
619 * some machines where system events sporadically
620 * become disabled. */
621 hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
622 printk(MY_NOTICE "Re-enabled hotkeys\n");
623 } else {
624 printk(MY_ERR "Error reading hotkey status\n");
625 goto end;
626 }
627 }
628
629 p += sprintf(p, "hotkey_ready: %d\n", key_event_valid);
630 p += sprintf(p, "hotkey: 0x%04x\n", last_key_event);
631
632 end:
633 return p;
634}
635
636static unsigned long write_keys(const char *buffer, unsigned long count)
637{
638 int value;
639
640 if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && value == 0) {
641 key_event_valid = 0;
642 } else {
643 return -EINVAL;
644 }
645
646 return count;
647}
648
649static char *read_version(char *p)
650{
651 p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION);
652 p += sprintf(p, "proc_interface: %d\n",
653 PROC_INTERFACE_VERSION);
654 return p;
655}
656
657/* proc and module init
658 */
659
660#define PROC_TOSHIBA "toshiba"
661
662static ProcItem proc_items[] = {
663 {"lcd", read_lcd, write_lcd},
664 {"video", read_video, write_video},
665 {"fan", read_fan, write_fan},
666 {"keys", read_keys, write_keys},
667 {"version", read_version, NULL},
668 {NULL}
669};
670
671static acpi_status __init add_device(void)
672{
673 struct proc_dir_entry *proc;
674 ProcItem *item;
675
676 for (item = proc_items; item->name; ++item) {
677 proc = create_proc_read_entry(item->name,
678 S_IFREG | S_IRUGO | S_IWUSR,
679 toshiba_proc_dir,
680 (read_proc_t *) dispatch_read,
681 item);
682 if (proc)
683 proc->owner = THIS_MODULE;
684 if (proc && item->write_func)
685 proc->write_proc = (write_proc_t *) dispatch_write;
686 }
687
688 return AE_OK;
689}
690
691static acpi_status remove_device(void)
692{
693 ProcItem *item;
694
695 for (item = proc_items; item->name; ++item)
696 remove_proc_entry(item->name, toshiba_proc_dir);
697 return AE_OK;
698}
699
700static struct backlight_ops toshiba_backlight_data = {
701 .get_brightness = get_lcd,
702 .update_status = set_lcd_status,
703};
704
705static void toshiba_acpi_exit(void)
706{
707 if (toshiba_acpi.poll_dev) {
708 input_unregister_polled_device(toshiba_acpi.poll_dev);
709 input_free_polled_device(toshiba_acpi.poll_dev);
710 }
711
712 if (toshiba_acpi.rfk_dev)
713 rfkill_unregister(toshiba_acpi.rfk_dev);
714
715 if (toshiba_backlight_device)
716 backlight_device_unregister(toshiba_backlight_device);
717
718 remove_device();
719
720 if (toshiba_proc_dir)
721 remove_proc_entry(PROC_TOSHIBA, acpi_root_dir);
722
723 platform_device_unregister(toshiba_acpi.p_dev);
724
725 return;
726}
727
728static int __init toshiba_acpi_init(void)
729{
730 acpi_status status = AE_OK;
731 u32 hci_result;
732 bool bt_present;
733 bool bt_on;
734 bool radio_on;
735 int ret = 0;
736
737 if (acpi_disabled)
738 return -ENODEV;
739
740 /* simple device detection: look for HCI method */
741 if (is_valid_acpi_path(METHOD_HCI_1))
742 method_hci = METHOD_HCI_1;
743 else if (is_valid_acpi_path(METHOD_HCI_2))
744 method_hci = METHOD_HCI_2;
745 else
746 return -ENODEV;
747
748 printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
749 TOSHIBA_ACPI_VERSION);
750 printk(MY_INFO " HCI method: %s\n", method_hci);
751
752 mutex_init(&toshiba_acpi.mutex);
753
754 toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi",
755 -1, NULL, 0);
756 if (IS_ERR(toshiba_acpi.p_dev)) {
757 ret = PTR_ERR(toshiba_acpi.p_dev);
758 printk(MY_ERR "unable to register platform device\n");
759 toshiba_acpi.p_dev = NULL;
760 toshiba_acpi_exit();
761 return ret;
762 }
763
764 force_fan = 0;
765 key_event_valid = 0;
766
767 /* enable event fifo */
768 hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
769
770 toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
771 if (!toshiba_proc_dir) {
772 toshiba_acpi_exit();
773 return -ENODEV;
774 } else {
775 toshiba_proc_dir->owner = THIS_MODULE;
776 status = add_device();
777 if (ACPI_FAILURE(status)) {
778 toshiba_acpi_exit();
779 return -ENODEV;
780 }
781 }
782
783 toshiba_backlight_device = backlight_device_register("toshiba",
784 &toshiba_acpi.p_dev->dev,
785 NULL,
786 &toshiba_backlight_data);
787 if (IS_ERR(toshiba_backlight_device)) {
788 ret = PTR_ERR(toshiba_backlight_device);
789
790 printk(KERN_ERR "Could not register toshiba backlight device\n");
791 toshiba_backlight_device = NULL;
792 toshiba_acpi_exit();
793 return ret;
794 }
795 toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
796
797 /* Register rfkill switch for Bluetooth */
798 if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) {
799 toshiba_acpi.rfk_dev = rfkill_allocate(&toshiba_acpi.p_dev->dev,
800 RFKILL_TYPE_BLUETOOTH);
801 if (!toshiba_acpi.rfk_dev) {
802 printk(MY_ERR "unable to allocate rfkill device\n");
803 toshiba_acpi_exit();
804 return -ENOMEM;
805 }
806
807 toshiba_acpi.rfk_dev->name = toshiba_acpi.bt_name;
808 toshiba_acpi.rfk_dev->toggle_radio = bt_rfkill_toggle_radio;
809 toshiba_acpi.rfk_dev->user_claim_unsupported = 1;
810 toshiba_acpi.rfk_dev->data = &toshiba_acpi;
811
812 if (hci_get_bt_on(&bt_on) == HCI_SUCCESS && bt_on) {
813 toshiba_acpi.rfk_dev->state = RFKILL_STATE_UNBLOCKED;
814 } else if (hci_get_radio_state(&radio_on) == HCI_SUCCESS &&
815 radio_on) {
816 toshiba_acpi.rfk_dev->state = RFKILL_STATE_SOFT_BLOCKED;
817 } else {
818 toshiba_acpi.rfk_dev->state = RFKILL_STATE_HARD_BLOCKED;
819 }
820
821 ret = rfkill_register(toshiba_acpi.rfk_dev);
822 if (ret) {
823 printk(MY_ERR "unable to register rfkill device\n");
824 toshiba_acpi_exit();
825 return -ENOMEM;
826 }
827
828 /* Register input device for kill switch */
829 toshiba_acpi.poll_dev = input_allocate_polled_device();
830 if (!toshiba_acpi.poll_dev) {
831 printk(MY_ERR
832 "unable to allocate kill-switch input device\n");
833 toshiba_acpi_exit();
834 return -ENOMEM;
835 }
836 toshiba_acpi.poll_dev->private = &toshiba_acpi;
837 toshiba_acpi.poll_dev->poll = bt_poll_rfkill;
838 toshiba_acpi.poll_dev->poll_interval = 1000; /* msecs */
839
840 toshiba_acpi.poll_dev->input->name = toshiba_acpi.rfk_name;
841 toshiba_acpi.poll_dev->input->id.bustype = BUS_HOST;
842 /* Toshiba USB ID */
843 toshiba_acpi.poll_dev->input->id.vendor = 0x0930;
844 set_bit(EV_SW, toshiba_acpi.poll_dev->input->evbit);
845 set_bit(SW_RFKILL_ALL, toshiba_acpi.poll_dev->input->swbit);
846 input_report_switch(toshiba_acpi.poll_dev->input,
847 SW_RFKILL_ALL, TRUE);
848 input_sync(toshiba_acpi.poll_dev->input);
849
850 ret = input_register_polled_device(toshiba_acpi.poll_dev);
851 if (ret) {
852 printk(MY_ERR
853 "unable to register kill-switch input device\n");
854 toshiba_acpi_exit();
855 return ret;
856 }
857 }
858
859 return 0;
860}
861
862module_init(toshiba_acpi_init);
863module_exit(toshiba_acpi_exit);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
new file mode 100644
index 000000000000..8a8b377712c9
--- /dev/null
+++ b/drivers/platform/x86/wmi.c
@@ -0,0 +1,747 @@
1/*
2 * ACPI-WMI mapping driver
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * GUID parsing code from ldm.c is:
7 * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
8 * Copyright (c) 2001-2007 Anton Altaparmakov
9 * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
10 *
11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or (at
16 * your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 *
27 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
28 */
29
30#include <linux/kernel.h>
31#include <linux/init.h>
32#include <linux/types.h>
33#include <linux/list.h>
34#include <linux/acpi.h>
35#include <acpi/acpi_bus.h>
36#include <acpi/acpi_drivers.h>
37
38ACPI_MODULE_NAME("wmi");
39MODULE_AUTHOR("Carlos Corbacho");
40MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
41MODULE_LICENSE("GPL");
42
43#define ACPI_WMI_CLASS "wmi"
44
45#undef PREFIX
46#define PREFIX "ACPI: WMI: "
47
48static DEFINE_MUTEX(wmi_data_lock);
49
50struct guid_block {
51 char guid[16];
52 union {
53 char object_id[2];
54 struct {
55 unsigned char notify_id;
56 unsigned char reserved;
57 };
58 };
59 u8 instance_count;
60 u8 flags;
61};
62
63struct wmi_block {
64 struct list_head list;
65 struct guid_block gblock;
66 acpi_handle handle;
67 wmi_notify_handler handler;
68 void *handler_data;
69};
70
71static struct wmi_block wmi_blocks;
72
73/*
74 * If the GUID data block is marked as expensive, we must enable and
75 * explicitily disable data collection.
76 */
77#define ACPI_WMI_EXPENSIVE 0x1
78#define ACPI_WMI_METHOD 0x2 /* GUID is a method */
79#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */
80#define ACPI_WMI_EVENT 0x8 /* GUID is an event */
81
82static int acpi_wmi_remove(struct acpi_device *device, int type);
83static int acpi_wmi_add(struct acpi_device *device);
84
85static const struct acpi_device_id wmi_device_ids[] = {
86 {"PNP0C14", 0},
87 {"pnp0c14", 0},
88 {"", 0},
89};
90MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
91
92static struct acpi_driver acpi_wmi_driver = {
93 .name = "wmi",
94 .class = ACPI_WMI_CLASS,
95 .ids = wmi_device_ids,
96 .ops = {
97 .add = acpi_wmi_add,
98 .remove = acpi_wmi_remove,
99 },
100};
101
102/*
103 * GUID parsing functions
104 */
105
106/**
107 * wmi_parse_hexbyte - Convert a ASCII hex number to a byte
108 * @src: Pointer to at least 2 characters to convert.
109 *
110 * Convert a two character ASCII hex string to a number.
111 *
112 * Return: 0-255 Success, the byte was parsed correctly
113 * -1 Error, an invalid character was supplied
114 */
115static int wmi_parse_hexbyte(const u8 *src)
116{
117 unsigned int x; /* For correct wrapping */
118 int h;
119
120 /* high part */
121 x = src[0];
122 if (x - '0' <= '9' - '0') {
123 h = x - '0';
124 } else if (x - 'a' <= 'f' - 'a') {
125 h = x - 'a' + 10;
126 } else if (x - 'A' <= 'F' - 'A') {
127 h = x - 'A' + 10;
128 } else {
129 return -1;
130 }
131 h <<= 4;
132
133 /* low part */
134 x = src[1];
135 if (x - '0' <= '9' - '0')
136 return h | (x - '0');
137 if (x - 'a' <= 'f' - 'a')
138 return h | (x - 'a' + 10);
139 if (x - 'A' <= 'F' - 'A')
140 return h | (x - 'A' + 10);
141 return -1;
142}
143
144/**
145 * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary
146 * @src: Memory block holding binary GUID (16 bytes)
147 * @dest: Memory block to hold byte swapped binary GUID (16 bytes)
148 *
149 * Byte swap a binary GUID to match it's real GUID value
150 */
151static void wmi_swap_bytes(u8 *src, u8 *dest)
152{
153 int i;
154
155 for (i = 0; i <= 3; i++)
156 memcpy(dest + i, src + (3 - i), 1);
157
158 for (i = 0; i <= 1; i++)
159 memcpy(dest + 4 + i, src + (5 - i), 1);
160
161 for (i = 0; i <= 1; i++)
162 memcpy(dest + 6 + i, src + (7 - i), 1);
163
164 memcpy(dest + 8, src + 8, 8);
165}
166
167/**
168 * wmi_parse_guid - Convert GUID from ASCII to binary
169 * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
170 * @dest: Memory block to hold binary GUID (16 bytes)
171 *
172 * N.B. The GUID need not be NULL terminated.
173 *
174 * Return: 'true' @dest contains binary GUID
175 * 'false' @dest contents are undefined
176 */
177static bool wmi_parse_guid(const u8 *src, u8 *dest)
178{
179 static const int size[] = { 4, 2, 2, 2, 6 };
180 int i, j, v;
181
182 if (src[8] != '-' || src[13] != '-' ||
183 src[18] != '-' || src[23] != '-')
184 return false;
185
186 for (j = 0; j < 5; j++, src++) {
187 for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) {
188 v = wmi_parse_hexbyte(src);
189 if (v < 0)
190 return false;
191 }
192 }
193
194 return true;
195}
196
197static bool find_guid(const char *guid_string, struct wmi_block **out)
198{
199 char tmp[16], guid_input[16];
200 struct wmi_block *wblock;
201 struct guid_block *block;
202 struct list_head *p;
203
204 wmi_parse_guid(guid_string, tmp);
205 wmi_swap_bytes(tmp, guid_input);
206
207 list_for_each(p, &wmi_blocks.list) {
208 wblock = list_entry(p, struct wmi_block, list);
209 block = &wblock->gblock;
210
211 if (memcmp(block->guid, guid_input, 16) == 0) {
212 if (out)
213 *out = wblock;
214 return 1;
215 }
216 }
217 return 0;
218}
219
220static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
221{
222 struct guid_block *block = NULL;
223 char method[5];
224 struct acpi_object_list input;
225 union acpi_object params[1];
226 acpi_status status;
227 acpi_handle handle;
228
229 block = &wblock->gblock;
230 handle = wblock->handle;
231
232 if (!block)
233 return AE_NOT_EXIST;
234
235 input.count = 1;
236 input.pointer = params;
237 params[0].type = ACPI_TYPE_INTEGER;
238 params[0].integer.value = enable;
239
240 snprintf(method, 5, "WE%02X", block->notify_id);
241 status = acpi_evaluate_object(handle, method, &input, NULL);
242
243 if (status != AE_OK && status != AE_NOT_FOUND)
244 return status;
245 else
246 return AE_OK;
247}
248
249/*
250 * Exported WMI functions
251 */
252/**
253 * wmi_evaluate_method - Evaluate a WMI method
254 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
255 * @instance: Instance index
256 * @method_id: Method ID to call
257 * &in: Buffer containing input for the method call
258 * &out: Empty buffer to return the method results
259 *
260 * Call an ACPI-WMI method
261 */
262acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
263u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
264{
265 struct guid_block *block = NULL;
266 struct wmi_block *wblock = NULL;
267 acpi_handle handle;
268 acpi_status status;
269 struct acpi_object_list input;
270 union acpi_object params[3];
271 char method[4] = "WM";
272
273 if (!find_guid(guid_string, &wblock))
274 return AE_ERROR;
275
276 block = &wblock->gblock;
277 handle = wblock->handle;
278
279 if (!(block->flags & ACPI_WMI_METHOD))
280 return AE_BAD_DATA;
281
282 if (block->instance_count < instance)
283 return AE_BAD_PARAMETER;
284
285 input.count = 2;
286 input.pointer = params;
287 params[0].type = ACPI_TYPE_INTEGER;
288 params[0].integer.value = instance;
289 params[1].type = ACPI_TYPE_INTEGER;
290 params[1].integer.value = method_id;
291
292 if (in) {
293 input.count = 3;
294
295 if (block->flags & ACPI_WMI_STRING) {
296 params[2].type = ACPI_TYPE_STRING;
297 } else {
298 params[2].type = ACPI_TYPE_BUFFER;
299 }
300 params[2].buffer.length = in->length;
301 params[2].buffer.pointer = in->pointer;
302 }
303
304 strncat(method, block->object_id, 2);
305
306 status = acpi_evaluate_object(handle, method, &input, out);
307
308 return status;
309}
310EXPORT_SYMBOL_GPL(wmi_evaluate_method);
311
312/**
313 * wmi_query_block - Return contents of a WMI block
314 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
315 * @instance: Instance index
316 * &out: Empty buffer to return the contents of the data block to
317 *
318 * Return the contents of an ACPI-WMI data block to a buffer
319 */
320acpi_status wmi_query_block(const char *guid_string, u8 instance,
321struct acpi_buffer *out)
322{
323 struct guid_block *block = NULL;
324 struct wmi_block *wblock = NULL;
325 acpi_handle handle, wc_handle;
326 acpi_status status, wc_status = AE_ERROR;
327 struct acpi_object_list input, wc_input;
328 union acpi_object wc_params[1], wq_params[1];
329 char method[4];
330 char wc_method[4] = "WC";
331
332 if (!guid_string || !out)
333 return AE_BAD_PARAMETER;
334
335 if (!find_guid(guid_string, &wblock))
336 return AE_ERROR;
337
338 block = &wblock->gblock;
339 handle = wblock->handle;
340
341 if (block->instance_count < instance)
342 return AE_BAD_PARAMETER;
343
344 /* Check GUID is a data block */
345 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
346 return AE_ERROR;
347
348 input.count = 1;
349 input.pointer = wq_params;
350 wq_params[0].type = ACPI_TYPE_INTEGER;
351 wq_params[0].integer.value = instance;
352
353 /*
354 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
355 * enable collection.
356 */
357 if (block->flags & ACPI_WMI_EXPENSIVE) {
358 wc_input.count = 1;
359 wc_input.pointer = wc_params;
360 wc_params[0].type = ACPI_TYPE_INTEGER;
361 wc_params[0].integer.value = 1;
362
363 strncat(wc_method, block->object_id, 2);
364
365 /*
366 * Some GUIDs break the specification by declaring themselves
367 * expensive, but have no corresponding WCxx method. So we
368 * should not fail if this happens.
369 */
370 wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
371 if (ACPI_SUCCESS(wc_status))
372 wc_status = acpi_evaluate_object(handle, wc_method,
373 &wc_input, NULL);
374 }
375
376 strcpy(method, "WQ");
377 strncat(method, block->object_id, 2);
378
379 status = acpi_evaluate_object(handle, method, &input, out);
380
381 /*
382 * If ACPI_WMI_EXPENSIVE, call the relevant WCxx method, even if
383 * the WQxx method failed - we should disable collection anyway.
384 */
385 if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
386 wc_params[0].integer.value = 0;
387 status = acpi_evaluate_object(handle,
388 wc_method, &wc_input, NULL);
389 }
390
391 return status;
392}
393EXPORT_SYMBOL_GPL(wmi_query_block);
394
395/**
396 * wmi_set_block - Write to a WMI block
397 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
398 * @instance: Instance index
399 * &in: Buffer containing new values for the data block
400 *
401 * Write the contents of the input buffer to an ACPI-WMI data block
402 */
403acpi_status wmi_set_block(const char *guid_string, u8 instance,
404const struct acpi_buffer *in)
405{
406 struct guid_block *block = NULL;
407 struct wmi_block *wblock = NULL;
408 acpi_handle handle;
409 struct acpi_object_list input;
410 union acpi_object params[2];
411 char method[4] = "WS";
412
413 if (!guid_string || !in)
414 return AE_BAD_DATA;
415
416 if (!find_guid(guid_string, &wblock))
417 return AE_ERROR;
418
419 block = &wblock->gblock;
420 handle = wblock->handle;
421
422 if (block->instance_count < instance)
423 return AE_BAD_PARAMETER;
424
425 /* Check GUID is a data block */
426 if (block->flags & (ACPI_WMI_EVENT | ACPI_WMI_METHOD))
427 return AE_ERROR;
428
429 input.count = 2;
430 input.pointer = params;
431 params[0].type = ACPI_TYPE_INTEGER;
432 params[0].integer.value = instance;
433
434 if (block->flags & ACPI_WMI_STRING) {
435 params[1].type = ACPI_TYPE_STRING;
436 } else {
437 params[1].type = ACPI_TYPE_BUFFER;
438 }
439 params[1].buffer.length = in->length;
440 params[1].buffer.pointer = in->pointer;
441
442 strncat(method, block->object_id, 2);
443
444 return acpi_evaluate_object(handle, method, &input, NULL);
445}
446EXPORT_SYMBOL_GPL(wmi_set_block);
447
448/**
449 * wmi_install_notify_handler - Register handler for WMI events
450 * @handler: Function to handle notifications
451 * @data: Data to be returned to handler when event is fired
452 *
453 * Register a handler for events sent to the ACPI-WMI mapper device.
454 */
455acpi_status wmi_install_notify_handler(const char *guid,
456wmi_notify_handler handler, void *data)
457{
458 struct wmi_block *block;
459 acpi_status status;
460
461 if (!guid || !handler)
462 return AE_BAD_PARAMETER;
463
464 find_guid(guid, &block);
465 if (!block)
466 return AE_NOT_EXIST;
467
468 if (block->handler)
469 return AE_ALREADY_ACQUIRED;
470
471 block->handler = handler;
472 block->handler_data = data;
473
474 status = wmi_method_enable(block, 1);
475
476 return status;
477}
478EXPORT_SYMBOL_GPL(wmi_install_notify_handler);
479
480/**
481 * wmi_uninstall_notify_handler - Unregister handler for WMI events
482 *
483 * Unregister handler for events sent to the ACPI-WMI mapper device.
484 */
485acpi_status wmi_remove_notify_handler(const char *guid)
486{
487 struct wmi_block *block;
488 acpi_status status;
489
490 if (!guid)
491 return AE_BAD_PARAMETER;
492
493 find_guid(guid, &block);
494 if (!block)
495 return AE_NOT_EXIST;
496
497 if (!block->handler)
498 return AE_NULL_ENTRY;
499
500 status = wmi_method_enable(block, 0);
501
502 block->handler = NULL;
503 block->handler_data = NULL;
504
505 return status;
506}
507EXPORT_SYMBOL_GPL(wmi_remove_notify_handler);
508
509/**
510 * wmi_get_event_data - Get WMI data associated with an event
511 *
512 * @event - Event to find
513 * &out - Buffer to hold event data
514 *
515 * Returns extra data associated with an event in WMI.
516 */
517acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
518{
519 struct acpi_object_list input;
520 union acpi_object params[1];
521 struct guid_block *gblock;
522 struct wmi_block *wblock;
523 struct list_head *p;
524
525 input.count = 1;
526 input.pointer = params;
527 params[0].type = ACPI_TYPE_INTEGER;
528 params[0].integer.value = event;
529
530 list_for_each(p, &wmi_blocks.list) {
531 wblock = list_entry(p, struct wmi_block, list);
532 gblock = &wblock->gblock;
533
534 if ((gblock->flags & ACPI_WMI_EVENT) &&
535 (gblock->notify_id == event))
536 return acpi_evaluate_object(wblock->handle, "_WED",
537 &input, out);
538 }
539
540 return AE_NOT_FOUND;
541}
542EXPORT_SYMBOL_GPL(wmi_get_event_data);
543
544/**
545 * wmi_has_guid - Check if a GUID is available
546 * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
547 *
548 * Check if a given GUID is defined by _WDG
549 */
550bool wmi_has_guid(const char *guid_string)
551{
552 return find_guid(guid_string, NULL);
553}
554EXPORT_SYMBOL_GPL(wmi_has_guid);
555
556/*
557 * Parse the _WDG method for the GUID data blocks
558 */
559static __init acpi_status parse_wdg(acpi_handle handle)
560{
561 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
562 union acpi_object *obj;
563 struct guid_block *gblock;
564 struct wmi_block *wblock;
565 acpi_status status;
566 u32 i, total;
567
568 status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
569
570 if (ACPI_FAILURE(status))
571 return status;
572
573 obj = (union acpi_object *) out.pointer;
574
575 if (obj->type != ACPI_TYPE_BUFFER)
576 return AE_ERROR;
577
578 total = obj->buffer.length / sizeof(struct guid_block);
579
580 gblock = kzalloc(obj->buffer.length, GFP_KERNEL);
581 if (!gblock)
582 return AE_NO_MEMORY;
583
584 memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
585
586 for (i = 0; i < total; i++) {
587 wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
588 if (!wblock)
589 return AE_NO_MEMORY;
590
591 wblock->gblock = gblock[i];
592 wblock->handle = handle;
593 list_add_tail(&wblock->list, &wmi_blocks.list);
594 }
595
596 kfree(out.pointer);
597 kfree(gblock);
598
599 return status;
600}
601
602/*
603 * WMI can have EmbeddedControl access regions. In which case, we just want to
604 * hand these off to the EC driver.
605 */
606static acpi_status
607acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
608 u32 bits, acpi_integer * value,
609 void *handler_context, void *region_context)
610{
611 int result = 0, i = 0;
612 u8 temp = 0;
613
614 if ((address > 0xFF) || !value)
615 return AE_BAD_PARAMETER;
616
617 if (function != ACPI_READ && function != ACPI_WRITE)
618 return AE_BAD_PARAMETER;
619
620 if (bits != 8)
621 return AE_BAD_PARAMETER;
622
623 if (function == ACPI_READ) {
624 result = ec_read(address, &temp);
625 (*value) |= ((acpi_integer)temp) << i;
626 } else {
627 temp = 0xff & ((*value) >> i);
628 result = ec_write(address, temp);
629 }
630
631 switch (result) {
632 case -EINVAL:
633 return AE_BAD_PARAMETER;
634 break;
635 case -ENODEV:
636 return AE_NOT_FOUND;
637 break;
638 case -ETIME:
639 return AE_TIME;
640 break;
641 default:
642 return AE_OK;
643 }
644}
645
646static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data)
647{
648 struct guid_block *block;
649 struct wmi_block *wblock;
650 struct list_head *p;
651 struct acpi_device *device = data;
652
653 list_for_each(p, &wmi_blocks.list) {
654 wblock = list_entry(p, struct wmi_block, list);
655 block = &wblock->gblock;
656
657 if ((block->flags & ACPI_WMI_EVENT) &&
658 (block->notify_id == event)) {
659 if (wblock->handler)
660 wblock->handler(event, wblock->handler_data);
661
662 acpi_bus_generate_netlink_event(
663 device->pnp.device_class, dev_name(&device->dev),
664 event, 0);
665 break;
666 }
667 }
668}
669
670static int acpi_wmi_remove(struct acpi_device *device, int type)
671{
672 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
673 acpi_wmi_notify);
674
675 acpi_remove_address_space_handler(device->handle,
676 ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
677
678 return 0;
679}
680
681static int __init acpi_wmi_add(struct acpi_device *device)
682{
683 acpi_status status;
684 int result = 0;
685
686 status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
687 acpi_wmi_notify, device);
688 if (ACPI_FAILURE(status)) {
689 printk(KERN_ERR PREFIX "Error installing notify handler\n");
690 return -ENODEV;
691 }
692
693 status = acpi_install_address_space_handler(device->handle,
694 ACPI_ADR_SPACE_EC,
695 &acpi_wmi_ec_space_handler,
696 NULL, NULL);
697 if (ACPI_FAILURE(status))
698 return -ENODEV;
699
700 status = parse_wdg(device->handle);
701 if (ACPI_FAILURE(status)) {
702 printk(KERN_ERR PREFIX "Error installing EC region handler\n");
703 return -ENODEV;
704 }
705
706 return result;
707}
708
709static int __init acpi_wmi_init(void)
710{
711 acpi_status result;
712
713 INIT_LIST_HEAD(&wmi_blocks.list);
714
715 if (acpi_disabled)
716 return -ENODEV;
717
718 result = acpi_bus_register_driver(&acpi_wmi_driver);
719
720 if (result < 0) {
721 printk(KERN_INFO PREFIX "Error loading mapper\n");
722 } else {
723 printk(KERN_INFO PREFIX "Mapper loaded\n");
724 }
725
726 return result;
727}
728
729static void __exit acpi_wmi_exit(void)
730{
731 struct list_head *p, *tmp;
732 struct wmi_block *wblock;
733
734 acpi_bus_unregister_driver(&acpi_wmi_driver);
735
736 list_for_each_safe(p, tmp, &wmi_blocks.list) {
737 wblock = list_entry(p, struct wmi_block, list);
738
739 list_del(p);
740 kfree(wblock);
741 }
742
743 printk(KERN_INFO PREFIX "Mapper unloaded\n");
744}
745
746subsys_initcall(acpi_wmi_init);
747module_exit(acpi_wmi_exit);