create drivers/platform/x86/ from drivers/misc/
authorLen Brown <len.brown@intel.com>
Mon, 1 Dec 2008 05:09:47 +0000 (00:09 -0500)
committerLen Brown <len.brown@intel.com>
Fri, 19 Dec 2008 09:42:32 +0000 (04:42 -0500)
Move x86 platform specific drivers from drivers/misc/
to a new home under drivers/platform/x86/.

The community has been maintaining x86 vendor-specific
platform specific drivers under /drivers/misc/ for a few years.
The oldest ones started life under drivers/acpi.
They moved out of drivers/acpi/ because they don't actually
implement the ACPI specification, but either simply
use ACPI, or implement vendor-specific ACPI extensions.

In the future we anticipate...
drivers/misc/ will go away.
other architectures will create drivers/platform/<arch>

Signed-off-by: Len Brown <len.brown@intel.com>

32 files changed:
drivers/Kconfig
drivers/Makefile
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/acer-wmi.c [deleted file]
drivers/misc/asus-laptop.c [deleted file]
drivers/misc/compal-laptop.c [deleted file]
drivers/misc/eeepc-laptop.c [deleted file]
drivers/misc/fujitsu-laptop.c [deleted file]
drivers/misc/hp-wmi.c [deleted file]
drivers/misc/intel_menlow.c [deleted file]
drivers/misc/msi-laptop.c [deleted file]
drivers/misc/panasonic-laptop.c [deleted file]
drivers/misc/sony-laptop.c [deleted file]
drivers/misc/tc1100-wmi.c [deleted file]
drivers/misc/thinkpad_acpi.c [deleted file]
drivers/platform/Kconfig [new file with mode: 0644]
drivers/platform/Makefile [new file with mode: 0644]
drivers/platform/x86/Kconfig [new file with mode: 0644]
drivers/platform/x86/Makefile [new file with mode: 0644]
drivers/platform/x86/acer-wmi.c [new file with mode: 0644]
drivers/platform/x86/asus-laptop.c [new file with mode: 0644]
drivers/platform/x86/compal-laptop.c [new file with mode: 0644]
drivers/platform/x86/eeepc-laptop.c [new file with mode: 0644]
drivers/platform/x86/fujitsu-laptop.c [new file with mode: 0644]
drivers/platform/x86/hp-wmi.c [new file with mode: 0644]
drivers/platform/x86/intel_menlow.c [new file with mode: 0644]
drivers/platform/x86/msi-laptop.c [new file with mode: 0644]
drivers/platform/x86/panasonic-laptop.c [new file with mode: 0644]
drivers/platform/x86/sony-laptop.c [new file with mode: 0644]
drivers/platform/x86/tc1100-wmi.c [new file with mode: 0644]
drivers/platform/x86/thinkpad_acpi.c [new file with mode: 0644]

index 2f557f5..00cf955 100644 (file)
@@ -107,4 +107,6 @@ source "drivers/uio/Kconfig"
 source "drivers/xen/Kconfig"
 
 source "drivers/staging/Kconfig"
+
+source "drivers/platform/Kconfig"
 endmenu
index fceb71a..46d4828 100644 (file)
@@ -102,3 +102,4 @@ obj-$(CONFIG_SSB)           += ssb/
 obj-$(CONFIG_VIRTIO)           += virtio/
 obj-$(CONFIG_REGULATOR)                += regulator/
 obj-$(CONFIG_STAGING)          += staging/
+obj-y                          += platform/
index dcf6169..8574879 100644 (file)
@@ -136,97 +136,6 @@ config TIFM_7XX1
          To compile this driver as a module, choose M here: the module will
          be called tifm_7xx1.
 
-config ACER_WMI
-       tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
-       depends on X86
-       depends on EXPERIMENTAL
-       depends on ACPI
-       depends on LEDS_CLASS
-       depends on NEW_LEDS
-       depends on BACKLIGHT_CLASS_DEVICE
-       depends on SERIO_I8042
-       depends on RFKILL
-       select ACPI_WMI
-       ---help---
-         This is a driver for newer Acer (and Wistron) laptops. It adds
-         wireless radio and bluetooth control, and on some laptops,
-         exposes the mail LED and LCD backlight.
-
-         For more information about this driver see
-         <file:Documentation/laptops/acer-wmi.txt>
-
-         If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
-         here.
-
-config ASUS_LAPTOP
-       tristate "Asus Laptop Extras (EXPERIMENTAL)"
-       depends on X86
-       depends on ACPI
-       depends on EXPERIMENTAL && !ACPI_ASUS
-       depends on LEDS_CLASS
-       depends on NEW_LEDS
-       depends on BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This is the new Linux driver for Asus laptops. It may also support some
-         MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
-         standard ACPI events that go through /proc/acpi/events. It also adds
-         support for video output switching, LCD backlight control, Bluetooth and
-         Wlan control, and most importantly, allows you to blink those fancy LEDs.
-
-         For more information and a userspace daemon for handling the extra
-         buttons see <http://acpi4asus.sf.net/>.
-
-         If you have an ACPI-compatible ASUS laptop, say Y or M here.
-
-config FUJITSU_LAPTOP
-       tristate "Fujitsu Laptop Extras"
-       depends on X86
-       depends on ACPI
-       depends on INPUT
-       depends on BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This is a driver for laptops built by Fujitsu:
-
-           * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks
-           * Possibly other Fujitsu laptop models
-           * Tested with S6410 and S7020
-
-         It adds support for LCD brightness control and some hotkeys.
-
-         If you have a Fujitsu laptop, say Y or M here.
-
-config FUJITSU_LAPTOP_DEBUG
-       bool "Verbose debug mode for Fujitsu Laptop Extras"
-       depends on FUJITSU_LAPTOP
-       default n
-       ---help---
-         Enables extra debug output from the fujitsu extras driver, at the
-         expense of a slight increase in driver size.
-
-         If you are not sure, say N here.
-
-config TC1100_WMI
-       tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
-       depends on X86 && !X86_64
-       depends on EXPERIMENTAL
-       depends on ACPI
-       select ACPI_WMI
-       ---help---
-         This is a driver for the WMI extensions (wireless and bluetooth power
-         control) of the HP Compaq TC1100 tablet.
-
-config HP_WMI
-       tristate "HP WMI extras"
-       depends on ACPI_WMI
-       depends on INPUT
-       depends on RFKILL
-       help
-        Say Y here if you want to support WMI-based hotkeys on HP laptops and
-        to read data from WMI such as docking or ambient light sensor state.
-
-        To compile this driver as a module, choose M here: the module will
-        be called hp-wmi.
-
 config ICS932S401
        tristate "Integrated Circuits ICS932S401"
        depends on I2C && EXPERIMENTAL
@@ -237,170 +146,6 @@ config ICS932S401
          This driver can also be built as a module. If so, the module
          will be called ics932s401.
 
-config MSI_LAPTOP
-       tristate "MSI Laptop Extras"
-       depends on X86
-       depends on ACPI
-       depends on BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This is a driver for laptops built by MSI (MICRO-STAR
-         INTERNATIONAL):
-
-         MSI MegaBook S270 (MS-1013)
-         Cytron/TCM/Medion/Tchibo MD96100/SAM2000
-
-         It adds support for Bluetooth, WLAN and LCD brightness control.
-
-         More information about this driver is available at
-         <http://0pointer.de/lennart/tchibo.html>.
-
-         If you have an MSI S270 laptop, say Y or M here.
-
-config PANASONIC_LAPTOP
-       tristate "Panasonic Laptop Extras"
-       depends on X86 && INPUT && ACPI
-       depends on BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This driver adds support for access to backlight control and hotkeys
-         on Panasonic Let's Note laptops.
-
-         If you have a Panasonic Let's note laptop (such as the R1(N variant),
-         R2, R3, R5, T2, W2 and Y2 series), say Y.
-
-config COMPAL_LAPTOP
-       tristate "Compal Laptop Extras"
-       depends on X86
-       depends on ACPI
-       depends on BACKLIGHT_CLASS_DEVICE
-       ---help---
-         This is a driver for laptops built by Compal:
-
-         Compal FL90/IFL90
-         Compal FL91/IFL91
-         Compal FL92/JFL92
-         Compal FT00/IFT00
-
-         It adds support for Bluetooth, WLAN and LCD brightness control.
-
-         If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
-
-config SONY_LAPTOP
-       tristate "Sony Laptop Extras"
-       depends on X86 && ACPI
-       select BACKLIGHT_CLASS_DEVICE
-       depends on INPUT
-         ---help---
-         This mini-driver drives the SNC and SPIC devices present in the ACPI
-         BIOS of the Sony Vaio laptops.
-
-         It gives access to some extra laptop functionalities like Bluetooth,
-         screen brightness control, Fn keys and allows powering on/off some
-         devices.
-
-         Read <file:Documentation/laptops/sony-laptop.txt> for more information.
-
-config SONYPI_COMPAT
-       bool "Sonypi compatibility"
-       depends on SONY_LAPTOP
-         ---help---
-         Build the sonypi driver compatibility code into the sony-laptop driver.
-
-config THINKPAD_ACPI
-       tristate "ThinkPad ACPI Laptop Extras"
-       depends on X86 && ACPI
-       select BACKLIGHT_LCD_SUPPORT
-       select BACKLIGHT_CLASS_DEVICE
-       select HWMON
-       select NVRAM
-       select INPUT
-       select NEW_LEDS
-       select LEDS_CLASS
-       select NET
-       select RFKILL
-       ---help---
-         This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
-         support for Fn-Fx key combinations, Bluetooth control, video
-         output switching, ThinkLight control, UltraBay eject and more.
-         For more information about this driver see
-         <file:Documentation/laptops/thinkpad-acpi.txt> and
-         <http://ibm-acpi.sf.net/> .
-
-         This driver was formerly known as ibm-acpi.
-
-         If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
-
-config THINKPAD_ACPI_DEBUG
-       bool "Verbose debug mode"
-       depends on THINKPAD_ACPI
-       default n
-       ---help---
-         Enables extra debugging information, at the expense of a slightly
-         increase in driver size.
-
-         If you are not sure, say N here.
-
-config THINKPAD_ACPI_DOCK
-       bool "Legacy Docking Station Support"
-       depends on THINKPAD_ACPI
-       depends on ACPI_DOCK=n
-       default n
-       ---help---
-         Allows the thinkpad_acpi driver to handle docking station events.
-         This support was made obsolete by the generic ACPI docking station
-         support (CONFIG_ACPI_DOCK).  It will allow locking and removing the
-         laptop from the docking station, but will not properly connect PCI
-         devices.
-
-         If you are not sure, say N here.
-
-config THINKPAD_ACPI_BAY
-       bool "Legacy Removable Bay Support"
-       depends on THINKPAD_ACPI
-       default y
-       ---help---
-         Allows the thinkpad_acpi driver to handle removable bays.  It will
-         electrically disable the device in the bay, and also generate
-         notifications when the bay lever is ejected or inserted.
-
-         If you are not sure, say Y here.
-
-config THINKPAD_ACPI_VIDEO
-       bool "Video output control support"
-       depends on THINKPAD_ACPI
-       default y
-       ---help---
-         Allows the thinkpad_acpi driver to provide an interface to control
-         the various video output ports.
-
-         This feature often won't work well, depending on ThinkPad model,
-         display state, video output devices in use, whether there is a X
-         server running, phase of the moon, and the current mood of
-         Schroedinger's cat.  If you can use X.org's RandR to control
-         your ThinkPad's video output ports instead of this feature,
-         don't think twice: do it and say N here to save some memory.
-
-         If you are not sure, say Y here.
-
-config THINKPAD_ACPI_HOTKEY_POLL
-       bool "Support NVRAM polling for hot keys"
-       depends on THINKPAD_ACPI
-       default y
-       ---help---
-         Some thinkpad models benefit from NVRAM polling to detect a few of
-         the hot key press events.  If you know your ThinkPad model does not
-         need to do NVRAM polling to support any of the hot keys you use,
-         unselecting this option will save about 1kB of memory.
-
-         ThinkPads T40 and newer, R52 and newer, and X31 and newer are
-         unlikely to need NVRAM polling in their latest BIOS versions.
-
-         NVRAM polling can detect at most the following keys: ThinkPad/Access
-         IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
-         Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
-
-         If you are not sure, say Y here.  The driver enables polling only if
-         it is strictly necessary to do so.
-
 config ATMEL_SSC
        tristate "Device driver for Atmel SSC peripheral"
        depends on AVR32 || ARCH_AT91
@@ -413,31 +158,6 @@ config ATMEL_SSC
 
          If unsure, say N.
 
-config INTEL_MENLOW
-       tristate "Thermal Management driver for Intel menlow platform"
-       depends on ACPI_THERMAL
-       select THERMAL
-       depends on X86
-       ---help---
-         ACPI thermal management enhancement driver on
-         Intel Menlow platform.
-
-         If unsure, say N.
-
-config EEEPC_LAPTOP
-       tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
-       depends on X86
-       depends on ACPI
-       depends on BACKLIGHT_CLASS_DEVICE
-       depends on HWMON
-       depends on EXPERIMENTAL
-       depends on RFKILL
-       ---help---
-         This driver supports the Fn-Fx keys on Eee PC laptops.
-         It also adds the ability to switch camera/wlan on/off.
-
-         If you have an Eee PC laptop, say Y or M here.
-
 config ENCLOSURE_SERVICES
        tristate "Enclosure Services"
        default n
index 027e1ea..d5749a7 100644 (file)
@@ -4,28 +4,16 @@
 
 obj-$(CONFIG_IBM_ASM)          += ibmasm/
 obj-$(CONFIG_HDPU_FEATURES)    += hdpuftrs/
-obj-$(CONFIG_ASUS_LAPTOP)      += asus-laptop.o
-obj-$(CONFIG_EEEPC_LAPTOP)     += eeepc-laptop.o
-obj-$(CONFIG_MSI_LAPTOP)       += msi-laptop.o
-obj-$(CONFIG_COMPAL_LAPTOP)    += compal-laptop.o
-obj-$(CONFIG_ACER_WMI)         += acer-wmi.o
 obj-$(CONFIG_ATMEL_PWM)                += atmel_pwm.o
 obj-$(CONFIG_ATMEL_SSC)                += atmel-ssc.o
 obj-$(CONFIG_ATMEL_TCLIB)      += atmel_tclib.o
-obj-$(CONFIG_HP_WMI)           += hp-wmi.o
 obj-$(CONFIG_ICS932S401)       += ics932s401.o
-obj-$(CONFIG_TC1100_WMI)       += tc1100-wmi.o
 obj-$(CONFIG_LKDTM)            += lkdtm.o
 obj-$(CONFIG_TIFM_CORE)        += tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
 obj-$(CONFIG_PHANTOM)          += phantom.o
 obj-$(CONFIG_SGI_IOC4)         += ioc4.o
-obj-$(CONFIG_SONY_LAPTOP)      += sony-laptop.o
-obj-$(CONFIG_THINKPAD_ACPI)    += thinkpad_acpi.o
-obj-$(CONFIG_FUJITSU_LAPTOP)   += fujitsu-laptop.o
-obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
 obj-$(CONFIG_EEPROM_93CX6)     += eeprom_93cx6.o
-obj-$(CONFIG_INTEL_MENLOW)     += intel_menlow.o
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)       += kgdbts.o
 obj-$(CONFIG_SGI_XP)           += sgi-xp/
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c
deleted file mode 100644 (file)
index 94c9f91..0000000
+++ /dev/null
@@ -1,1345 +0,0 @@
-/*
- *  Acer WMI Laptop Extras
- *
- *  Copyright (C) 2007-2008    Carlos Corbacho <carlos@strangeworlds.co.uk>
- *
- *  Based on acer_acpi:
- *    Copyright (C) 2005-2007  E.M. Smith
- *    Copyright (C) 2007-2008  Carlos Corbacho <cathectic@gmail.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/dmi.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-#include <linux/leds.h>
-#include <linux/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/i8042.h>
-#include <linux/rfkill.h>
-#include <linux/workqueue.h>
-#include <linux/debugfs.h>
-
-#include <acpi/acpi_drivers.h>
-
-MODULE_AUTHOR("Carlos Corbacho");
-MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
-MODULE_LICENSE("GPL");
-
-#define ACER_LOGPREFIX "acer-wmi: "
-#define ACER_ERR KERN_ERR ACER_LOGPREFIX
-#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
-#define ACER_INFO KERN_INFO ACER_LOGPREFIX
-
-/*
- * The following defines quirks to get some specific functions to work
- * which are known to not be supported over ACPI-WMI (such as the mail LED
- * on WMID based Acer's)
- */
-struct acer_quirks {
-       const char *vendor;
-       const char *model;
-       u16 quirks;
-};
-
-/*
- * Magic Number
- * Meaning is unknown - this number is required for writing to ACPI for AMW0
- * (it's also used in acerhk when directly accessing the BIOS)
- */
-#define ACER_AMW0_WRITE        0x9610
-
-/*
- * Bit masks for the AMW0 interface
- */
-#define ACER_AMW0_WIRELESS_MASK  0x35
-#define ACER_AMW0_BLUETOOTH_MASK 0x34
-#define ACER_AMW0_MAILLED_MASK   0x31
-
-/*
- * Method IDs for WMID interface
- */
-#define ACER_WMID_GET_WIRELESS_METHODID                1
-#define ACER_WMID_GET_BLUETOOTH_METHODID       2
-#define ACER_WMID_GET_BRIGHTNESS_METHODID      3
-#define ACER_WMID_SET_WIRELESS_METHODID                4
-#define ACER_WMID_SET_BLUETOOTH_METHODID       5
-#define ACER_WMID_SET_BRIGHTNESS_METHODID      6
-#define ACER_WMID_GET_THREEG_METHODID          10
-#define ACER_WMID_SET_THREEG_METHODID          11
-
-/*
- * Acer ACPI method GUIDs
- */
-#define AMW0_GUID1             "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
-#define AMW0_GUID2             "431F16ED-0C2B-444C-B267-27DEB140CF9C"
-#define WMID_GUID1             "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
-#define WMID_GUID2             "95764E09-FB56-4e83-B31A-37761F60994A"
-
-MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
-MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
-
-/* Temporary workaround until the WMI sysfs interface goes in */
-MODULE_ALIAS("dmi:*:*Acer*:*:");
-
-/*
- * Interface capability flags
- */
-#define ACER_CAP_MAILLED               (1<<0)
-#define ACER_CAP_WIRELESS              (1<<1)
-#define ACER_CAP_BLUETOOTH             (1<<2)
-#define ACER_CAP_BRIGHTNESS            (1<<3)
-#define ACER_CAP_THREEG                        (1<<4)
-#define ACER_CAP_ANY                   (0xFFFFFFFF)
-
-/*
- * Interface type flags
- */
-enum interface_flags {
-       ACER_AMW0,
-       ACER_AMW0_V2,
-       ACER_WMID,
-};
-
-#define ACER_DEFAULT_WIRELESS  0
-#define ACER_DEFAULT_BLUETOOTH 0
-#define ACER_DEFAULT_MAILLED   0
-#define ACER_DEFAULT_THREEG    0
-
-static int max_brightness = 0xF;
-
-static int mailled = -1;
-static int brightness = -1;
-static int threeg = -1;
-static int force_series;
-
-module_param(mailled, int, 0444);
-module_param(brightness, int, 0444);
-module_param(threeg, int, 0444);
-module_param(force_series, int, 0444);
-MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
-MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
-MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
-MODULE_PARM_DESC(force_series, "Force a different laptop series");
-
-struct acer_data {
-       int mailled;
-       int threeg;
-       int brightness;
-};
-
-struct acer_debug {
-       struct dentry *root;
-       struct dentry *devices;
-       u32 wmid_devices;
-};
-
-static struct rfkill *wireless_rfkill;
-static struct rfkill *bluetooth_rfkill;
-
-/* Each low-level interface must define at least some of the following */
-struct wmi_interface {
-       /* The WMI device type */
-       u32 type;
-
-       /* The capabilities this interface provides */
-       u32 capability;
-
-       /* Private data for the current interface */
-       struct acer_data data;
-
-       /* debugfs entries associated with this interface */
-       struct acer_debug debug;
-};
-
-/* The static interface pointer, points to the currently detected interface */
-static struct wmi_interface *interface;
-
-/*
- * Embedded Controller quirks
- * Some laptops require us to directly access the EC to either enable or query
- * features that are not available through WMI.
- */
-
-struct quirk_entry {
-       u8 wireless;
-       u8 mailled;
-       s8 brightness;
-       u8 bluetooth;
-};
-
-static struct quirk_entry *quirks;
-
-static void set_quirks(void)
-{
-       if (!interface)
-               return;
-
-       if (quirks->mailled)
-               interface->capability |= ACER_CAP_MAILLED;
-
-       if (quirks->brightness)
-               interface->capability |= ACER_CAP_BRIGHTNESS;
-}
-
-static int dmi_matched(const struct dmi_system_id *dmi)
-{
-       quirks = dmi->driver_data;
-       return 0;
-}
-
-static struct quirk_entry quirk_unknown = {
-};
-
-static struct quirk_entry quirk_acer_aspire_1520 = {
-       .brightness = -1,
-};
-
-static struct quirk_entry quirk_acer_travelmate_2490 = {
-       .mailled = 1,
-};
-
-/* This AMW0 laptop has no bluetooth */
-static struct quirk_entry quirk_medion_md_98300 = {
-       .wireless = 1,
-};
-
-static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
-       .wireless = 2,
-};
-
-static struct dmi_system_id acer_quirks[] = {
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 1360",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
-               },
-               .driver_data = &quirk_acer_aspire_1520,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 1520",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
-               },
-               .driver_data = &quirk_acer_aspire_1520,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 3100",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 3610",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 5100",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 5610",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 5630",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 5650",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 5680",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer Aspire 9110",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer TravelMate 2490",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Acer TravelMate 4200",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
-               },
-               .driver_data = &quirk_acer_travelmate_2490,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Fujitsu Siemens Amilo Li 1718",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
-               },
-               .driver_data = &quirk_fujitsu_amilo_li_1718,
-       },
-       {
-               .callback = dmi_matched,
-               .ident = "Medion MD 98300",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
-               },
-               .driver_data = &quirk_medion_md_98300,
-       },
-       {}
-};
-
-/* Find which quirks are needed for a particular vendor/ model pair */
-static void find_quirks(void)
-{
-       if (!force_series) {
-               dmi_check_system(acer_quirks);
-       } else if (force_series == 2490) {
-               quirks = &quirk_acer_travelmate_2490;
-       }
-
-       if (quirks == NULL)
-               quirks = &quirk_unknown;
-
-       set_quirks();
-}
-
-/*
- * General interface convenience methods
- */
-
-static bool has_cap(u32 cap)
-{
-       if ((interface->capability & cap) != 0)
-               return 1;
-
-       return 0;
-}
-
-/*
- * AMW0 (V1) interface
- */
-struct wmab_args {
-       u32 eax;
-       u32 ebx;
-       u32 ecx;
-       u32 edx;
-};
-
-struct wmab_ret {
-       u32 eax;
-       u32 ebx;
-       u32 ecx;
-       u32 edx;
-       u32 eex;
-};
-
-static acpi_status wmab_execute(struct wmab_args *regbuf,
-struct acpi_buffer *result)
-{
-       struct acpi_buffer input;
-       acpi_status status;
-       input.length = sizeof(struct wmab_args);
-       input.pointer = (u8 *)regbuf;
-
-       status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
-
-       return status;
-}
-
-static acpi_status AMW0_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
-{
-       int err;
-       u8 result;
-
-       switch (cap) {
-       case ACER_CAP_MAILLED:
-               switch (quirks->mailled) {
-               default:
-                       err = ec_read(0xA, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = (result >> 7) & 0x1;
-                       return AE_OK;
-               }
-               break;
-       case ACER_CAP_WIRELESS:
-               switch (quirks->wireless) {
-               case 1:
-                       err = ec_read(0x7B, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = result & 0x1;
-                       return AE_OK;
-               case 2:
-                       err = ec_read(0x71, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = result & 0x1;
-                       return AE_OK;
-               default:
-                       err = ec_read(0xA, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = (result >> 2) & 0x1;
-                       return AE_OK;
-               }
-               break;
-       case ACER_CAP_BLUETOOTH:
-               switch (quirks->bluetooth) {
-               default:
-                       err = ec_read(0xA, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = (result >> 4) & 0x1;
-                       return AE_OK;
-               }
-               break;
-       case ACER_CAP_BRIGHTNESS:
-               switch (quirks->brightness) {
-               default:
-                       err = ec_read(0x83, &result);
-                       if (err)
-                               return AE_ERROR;
-                       *value = result;
-                       return AE_OK;
-               }
-               break;
-       default:
-               return AE_ERROR;
-       }
-       return AE_OK;
-}
-
-static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
-{
-       struct wmab_args args;
-
-       args.eax = ACER_AMW0_WRITE;
-       args.ebx = value ? (1<<8) : 0;
-       args.ecx = args.edx = 0;
-
-       switch (cap) {
-       case ACER_CAP_MAILLED:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               args.ebx |= ACER_AMW0_MAILLED_MASK;
-               break;
-       case ACER_CAP_WIRELESS:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               args.ebx |= ACER_AMW0_WIRELESS_MASK;
-               break;
-       case ACER_CAP_BLUETOOTH:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
-               break;
-       case ACER_CAP_BRIGHTNESS:
-               if (value > max_brightness)
-                       return AE_BAD_PARAMETER;
-               switch (quirks->brightness) {
-               default:
-                       return ec_write(0x83, value);
-                       break;
-               }
-       default:
-               return AE_ERROR;
-       }
-
-       /* Actually do the set */
-       return wmab_execute(&args, NULL);
-}
-
-static acpi_status AMW0_find_mailled(void)
-{
-       struct wmab_args args;
-       struct wmab_ret ret;
-       acpi_status status = AE_OK;
-       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
-
-       args.eax = 0x86;
-       args.ebx = args.ecx = args.edx = 0;
-
-       status = wmab_execute(&args, &out);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = (union acpi_object *) out.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-       obj->buffer.length == sizeof(struct wmab_ret)) {
-               ret = *((struct wmab_ret *) obj->buffer.pointer);
-       } else {
-               return AE_ERROR;
-       }
-
-       if (ret.eex & 0x1)
-               interface->capability |= ACER_CAP_MAILLED;
-
-       kfree(out.pointer);
-
-       return AE_OK;
-}
-
-static acpi_status AMW0_set_capabilities(void)
-{
-       struct wmab_args args;
-       struct wmab_ret ret;
-       acpi_status status = AE_OK;
-       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
-
-       /*
-        * On laptops with this strange GUID (non Acer), normal probing doesn't
-        * work.
-        */
-       if (wmi_has_guid(AMW0_GUID2)) {
-               interface->capability |= ACER_CAP_WIRELESS;
-               return AE_OK;
-       }
-
-       args.eax = ACER_AMW0_WRITE;
-       args.ecx = args.edx = 0;
-
-       args.ebx = 0xa2 << 8;
-       args.ebx |= ACER_AMW0_WIRELESS_MASK;
-
-       status = wmab_execute(&args, &out);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = (union acpi_object *) out.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-       obj->buffer.length == sizeof(struct wmab_ret)) {
-               ret = *((struct wmab_ret *) obj->buffer.pointer);
-       } else {
-               return AE_ERROR;
-       }
-
-       if (ret.eax & 0x1)
-               interface->capability |= ACER_CAP_WIRELESS;
-
-       args.ebx = 2 << 8;
-       args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
-
-       status = wmab_execute(&args, &out);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = (union acpi_object *) out.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER
-       && obj->buffer.length == sizeof(struct wmab_ret)) {
-               ret = *((struct wmab_ret *) obj->buffer.pointer);
-       } else {
-               return AE_ERROR;
-       }
-
-       if (ret.eax & 0x1)
-               interface->capability |= ACER_CAP_BLUETOOTH;
-
-       kfree(out.pointer);
-
-       /*
-        * This appears to be safe to enable, since all Wistron based laptops
-        * appear to use the same EC register for brightness, even if they
-        * differ for wireless, etc
-        */
-       if (quirks->brightness >= 0)
-               interface->capability |= ACER_CAP_BRIGHTNESS;
-
-       return AE_OK;
-}
-
-static struct wmi_interface AMW0_interface = {
-       .type = ACER_AMW0,
-};
-
-static struct wmi_interface AMW0_V2_interface = {
-       .type = ACER_AMW0_V2,
-};
-
-/*
- * New interface (The WMID interface)
- */
-static acpi_status
-WMI_execute_u32(u32 method_id, u32 in, u32 *out)
-{
-       struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
-       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
-       u32 tmp;
-       acpi_status status;
-
-       status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
-
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = (union acpi_object *) result.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-               obj->buffer.length == sizeof(u32)) {
-               tmp = *((u32 *) obj->buffer.pointer);
-       } else {
-               tmp = 0;
-       }
-
-       if (out)
-               *out = tmp;
-
-       kfree(result.pointer);
-
-       return status;
-}
-
-static acpi_status WMID_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
-{
-       acpi_status status;
-       u8 tmp;
-       u32 result, method_id = 0;
-
-       switch (cap) {
-       case ACER_CAP_WIRELESS:
-               method_id = ACER_WMID_GET_WIRELESS_METHODID;
-               break;
-       case ACER_CAP_BLUETOOTH:
-               method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
-               break;
-       case ACER_CAP_BRIGHTNESS:
-               method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
-               break;
-       case ACER_CAP_THREEG:
-               method_id = ACER_WMID_GET_THREEG_METHODID;
-               break;
-       case ACER_CAP_MAILLED:
-               if (quirks->mailled == 1) {
-                       ec_read(0x9f, &tmp);
-                       *value = tmp & 0x1;
-                       return 0;
-               }
-       default:
-               return AE_ERROR;
-       }
-       status = WMI_execute_u32(method_id, 0, &result);
-
-       if (ACPI_SUCCESS(status))
-               *value = (u8)result;
-
-       return status;
-}
-
-static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
-{
-       u32 method_id = 0;
-       char param;
-
-       switch (cap) {
-       case ACER_CAP_BRIGHTNESS:
-               if (value > max_brightness)
-                       return AE_BAD_PARAMETER;
-               method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
-               break;
-       case ACER_CAP_WIRELESS:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               method_id = ACER_WMID_SET_WIRELESS_METHODID;
-               break;
-       case ACER_CAP_BLUETOOTH:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
-               break;
-       case ACER_CAP_THREEG:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               method_id = ACER_WMID_SET_THREEG_METHODID;
-               break;
-       case ACER_CAP_MAILLED:
-               if (value > 1)
-                       return AE_BAD_PARAMETER;
-               if (quirks->mailled == 1) {
-                       param = value ? 0x92 : 0x93;
-                       i8042_command(&param, 0x1059);
-                       return 0;
-               }
-               break;
-       default:
-               return AE_ERROR;
-       }
-       return WMI_execute_u32(method_id, (u32)value, NULL);
-}
-
-static acpi_status WMID_set_capabilities(void)
-{
-       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object *obj;
-       acpi_status status;
-       u32 devices;
-
-       status = wmi_query_block(WMID_GUID2, 1, &out);
-       if (ACPI_FAILURE(status))
-               return status;
-
-       obj = (union acpi_object *) out.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-               obj->buffer.length == sizeof(u32)) {
-               devices = *((u32 *) obj->buffer.pointer);
-       } else {
-               return AE_ERROR;
-       }
-
-       /* Not sure on the meaning of the relevant bits yet to detect these */
-       interface->capability |= ACER_CAP_WIRELESS;
-       interface->capability |= ACER_CAP_THREEG;
-
-       /* WMID always provides brightness methods */
-       interface->capability |= ACER_CAP_BRIGHTNESS;
-
-       if (devices & 0x10)
-               interface->capability |= ACER_CAP_BLUETOOTH;
-
-       if (!(devices & 0x20))
-               max_brightness = 0x9;
-
-       return status;
-}
-
-static struct wmi_interface wmid_interface = {
-       .type = ACER_WMID,
-};
-
-/*
- * Generic Device (interface-independent)
- */
-
-static acpi_status get_u32(u32 *value, u32 cap)
-{
-       acpi_status status = AE_ERROR;
-
-       switch (interface->type) {
-       case ACER_AMW0:
-               status = AMW0_get_u32(value, cap, interface);
-               break;
-       case ACER_AMW0_V2:
-               if (cap == ACER_CAP_MAILLED) {
-                       status = AMW0_get_u32(value, cap, interface);
-                       break;
-               }
-       case ACER_WMID:
-               status = WMID_get_u32(value, cap, interface);
-               break;
-       }
-
-       return status;
-}
-
-static acpi_status set_u32(u32 value, u32 cap)
-{
-       acpi_status status;
-
-       if (interface->capability & cap) {
-               switch (interface->type) {
-               case ACER_AMW0:
-                       return AMW0_set_u32(value, cap, interface);
-               case ACER_AMW0_V2:
-                       if (cap == ACER_CAP_MAILLED)
-                               return AMW0_set_u32(value, cap, interface);
-
-                       /*
-                        * On some models, some WMID methods don't toggle
-                        * properly. For those cases, we want to run the AMW0
-                        * method afterwards to be certain we've really toggled
-                        * the device state.
-                        */
-                       if (cap == ACER_CAP_WIRELESS ||
-                               cap == ACER_CAP_BLUETOOTH) {
-                               status = WMID_set_u32(value, cap, interface);
-                               if (ACPI_FAILURE(status))
-                                       return status;
-
-                               return AMW0_set_u32(value, cap, interface);
-                       }
-               case ACER_WMID:
-                       return WMID_set_u32(value, cap, interface);
-               default:
-                       return AE_BAD_PARAMETER;
-               }
-       }
-       return AE_BAD_PARAMETER;
-}
-
-static void __init acer_commandline_init(void)
-{
-       /*
-        * These will all fail silently if the value given is invalid, or the
-        * capability isn't available on the given interface
-        */
-       set_u32(mailled, ACER_CAP_MAILLED);
-       set_u32(threeg, ACER_CAP_THREEG);
-       set_u32(brightness, ACER_CAP_BRIGHTNESS);
-}
-
-/*
- * LED device (Mail LED only, no other LEDs known yet)
- */
-static void mail_led_set(struct led_classdev *led_cdev,
-enum led_brightness value)
-{
-       set_u32(value, ACER_CAP_MAILLED);
-}
-
-static struct led_classdev mail_led = {
-       .name = "acer-wmi::mail",
-       .brightness_set = mail_led_set,
-};
-
-static int __devinit acer_led_init(struct device *dev)
-{
-       return led_classdev_register(dev, &mail_led);
-}
-
-static void acer_led_exit(void)
-{
-       led_classdev_unregister(&mail_led);
-}
-
-/*
- * Backlight device
- */
-static struct backlight_device *acer_backlight_device;
-
-static int read_brightness(struct backlight_device *bd)
-{
-       u32 value;
-       get_u32(&value, ACER_CAP_BRIGHTNESS);
-       return value;
-}
-
-static int update_bl_status(struct backlight_device *bd)
-{
-       int intensity = bd->props.brightness;
-
-       if (bd->props.power != FB_BLANK_UNBLANK)
-               intensity = 0;
-       if (bd->props.fb_blank != FB_BLANK_UNBLANK)
-               intensity = 0;
-
-       set_u32(intensity, ACER_CAP_BRIGHTNESS);
-
-       return 0;
-}
-
-static struct backlight_ops acer_bl_ops = {
-       .get_brightness = read_brightness,
-       .update_status = update_bl_status,
-};
-
-static int __devinit acer_backlight_init(struct device *dev)
-{
-       struct backlight_device *bd;
-
-       bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
-       if (IS_ERR(bd)) {
-               printk(ACER_ERR "Could not register Acer backlight device\n");
-               acer_backlight_device = NULL;
-               return PTR_ERR(bd);
-       }
-
-       acer_backlight_device = bd;
-
-       bd->props.power = FB_BLANK_UNBLANK;
-       bd->props.brightness = max_brightness;
-       bd->props.max_brightness = max_brightness;
-       backlight_update_status(bd);
-       return 0;
-}
-
-static void acer_backlight_exit(void)
-{
-       backlight_device_unregister(acer_backlight_device);
-}
-
-/*
- * Rfkill devices
- */
-static void acer_rfkill_update(struct work_struct *ignored);
-static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
-static void acer_rfkill_update(struct work_struct *ignored)
-{
-       u32 state;
-       acpi_status status;
-
-       status = get_u32(&state, ACER_CAP_WIRELESS);
-       if (ACPI_SUCCESS(status))
-               rfkill_force_state(wireless_rfkill, state ?
-                       RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
-
-       if (has_cap(ACER_CAP_BLUETOOTH)) {
-               status = get_u32(&state, ACER_CAP_BLUETOOTH);
-               if (ACPI_SUCCESS(status))
-                       rfkill_force_state(bluetooth_rfkill, state ?
-                               RFKILL_STATE_UNBLOCKED :
-                               RFKILL_STATE_SOFT_BLOCKED);
-       }
-
-       schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
-}
-
-static int acer_rfkill_set(void *data, enum rfkill_state state)
-{
-       acpi_status status;
-       u32 *cap = data;
-       status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-       return 0;
-}
-
-static struct rfkill * acer_rfkill_register(struct device *dev,
-enum rfkill_type type, char *name, u32 cap)
-{
-       int err;
-       u32 state;
-       u32 *data;
-       struct rfkill *rfkill_dev;
-
-       rfkill_dev = rfkill_allocate(dev, type);
-       if (!rfkill_dev)
-               return ERR_PTR(-ENOMEM);
-       rfkill_dev->name = name;
-       get_u32(&state, cap);
-       rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
-               RFKILL_STATE_SOFT_BLOCKED;
-       data = kzalloc(sizeof(u32), GFP_KERNEL);
-       if (!data) {
-               rfkill_free(rfkill_dev);
-               return ERR_PTR(-ENOMEM);
-       }
-       *data = cap;
-       rfkill_dev->data = data;
-       rfkill_dev->toggle_radio = acer_rfkill_set;
-       rfkill_dev->user_claim_unsupported = 1;
-
-       err = rfkill_register(rfkill_dev);
-       if (err) {
-               kfree(rfkill_dev->data);
-               rfkill_free(rfkill_dev);
-               return ERR_PTR(err);
-       }
-       return rfkill_dev;
-}
-
-static int acer_rfkill_init(struct device *dev)
-{
-       wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
-               "acer-wireless", ACER_CAP_WIRELESS);
-       if (IS_ERR(wireless_rfkill))
-               return PTR_ERR(wireless_rfkill);
-
-       if (has_cap(ACER_CAP_BLUETOOTH)) {
-               bluetooth_rfkill = acer_rfkill_register(dev,
-                       RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
-                       ACER_CAP_BLUETOOTH);
-               if (IS_ERR(bluetooth_rfkill)) {
-                       kfree(wireless_rfkill->data);
-                       rfkill_unregister(wireless_rfkill);
-                       return PTR_ERR(bluetooth_rfkill);
-               }
-       }
-
-       schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
-
-       return 0;
-}
-
-static void acer_rfkill_exit(void)
-{
-       cancel_delayed_work_sync(&acer_rfkill_work);
-       kfree(wireless_rfkill->data);
-       rfkill_unregister(wireless_rfkill);
-       if (has_cap(ACER_CAP_BLUETOOTH)) {
-               kfree(wireless_rfkill->data);
-               rfkill_unregister(bluetooth_rfkill);
-       }
-       return;
-}
-
-/*
- * sysfs interface
- */
-static ssize_t show_bool_threeg(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       u32 result; \
-       acpi_status status = get_u32(&result, ACER_CAP_THREEG);
-       if (ACPI_SUCCESS(status))
-               return sprintf(buf, "%u\n", result);
-       return sprintf(buf, "Read error\n");
-}
-
-static ssize_t set_bool_threeg(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-       u32 tmp = simple_strtoul(buf, NULL, 10);
-       acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
-               if (ACPI_FAILURE(status))
-                       return -EINVAL;
-       return count;
-}
-static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
-       set_bool_threeg);
-
-static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
-       char *buf)
-{
-       switch (interface->type) {
-       case ACER_AMW0:
-               return sprintf(buf, "AMW0\n");
-       case ACER_AMW0_V2:
-               return sprintf(buf, "AMW0 v2\n");
-       case ACER_WMID:
-               return sprintf(buf, "WMID\n");
-       default:
-               return sprintf(buf, "Error!\n");
-       }
-}
-
-static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
-       show_interface, NULL);
-
-/*
- * debugfs functions
- */
-static u32 get_wmid_devices(void)
-{
-       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object *obj;
-       acpi_status status;
-
-       status = wmi_query_block(WMID_GUID2, 1, &out);
-       if (ACPI_FAILURE(status))
-               return 0;
-
-       obj = (union acpi_object *) out.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-               obj->buffer.length == sizeof(u32)) {
-               return *((u32 *) obj->buffer.pointer);
-       } else {
-               return 0;
-       }
-}
-
-/*
- * Platform device
- */
-static int __devinit acer_platform_probe(struct platform_device *device)
-{
-       int err;
-
-       if (has_cap(ACER_CAP_MAILLED)) {
-               err = acer_led_init(&device->dev);
-               if (err)
-                       goto error_mailled;
-       }
-
-       if (has_cap(ACER_CAP_BRIGHTNESS)) {
-               err = acer_backlight_init(&device->dev);
-               if (err)
-                       goto error_brightness;
-       }
-
-       err = acer_rfkill_init(&device->dev);
-
-       return err;
-
-error_brightness:
-       acer_led_exit();
-error_mailled:
-       return err;
-}
-
-static int acer_platform_remove(struct platform_device *device)
-{
-       if (has_cap(ACER_CAP_MAILLED))
-               acer_led_exit();
-       if (has_cap(ACER_CAP_BRIGHTNESS))
-               acer_backlight_exit();
-
-       acer_rfkill_exit();
-       return 0;
-}
-
-static int acer_platform_suspend(struct platform_device *dev,
-pm_message_t state)
-{
-       u32 value;
-       struct acer_data *data = &interface->data;
-
-       if (!data)
-               return -ENOMEM;
-
-       if (has_cap(ACER_CAP_MAILLED)) {
-               get_u32(&value, ACER_CAP_MAILLED);
-               data->mailled = value;
-       }
-
-       if (has_cap(ACER_CAP_BRIGHTNESS)) {
-               get_u32(&value, ACER_CAP_BRIGHTNESS);
-               data->brightness = value;
-       }
-
-       return 0;
-}
-
-static int acer_platform_resume(struct platform_device *device)
-{
-       struct acer_data *data = &interface->data;
-
-       if (!data)
-               return -ENOMEM;
-
-       if (has_cap(ACER_CAP_MAILLED))
-               set_u32(data->mailled, ACER_CAP_MAILLED);
-
-       if (has_cap(ACER_CAP_BRIGHTNESS))
-               set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
-
-       return 0;
-}
-
-static struct platform_driver acer_platform_driver = {
-       .driver = {
-               .name = "acer-wmi",
-               .owner = THIS_MODULE,
-       },
-       .probe = acer_platform_probe,
-       .remove = acer_platform_remove,
-       .suspend = acer_platform_suspend,
-       .resume = acer_platform_resume,
-};
-
-static struct platform_device *acer_platform_device;
-
-static int remove_sysfs(struct platform_device *device)
-{
-       if (has_cap(ACER_CAP_THREEG))
-               device_remove_file(&device->dev, &dev_attr_threeg);
-
-       device_remove_file(&device->dev, &dev_attr_interface);
-
-       return 0;
-}
-
-static int create_sysfs(void)
-{
-       int retval = -ENOMEM;
-
-       if (has_cap(ACER_CAP_THREEG)) {
-               retval = device_create_file(&acer_platform_device->dev,
-                       &dev_attr_threeg);
-               if (retval)
-                       goto error_sysfs;
-       }
-
-       retval = device_create_file(&acer_platform_device->dev,
-               &dev_attr_interface);
-       if (retval)
-               goto error_sysfs;
-
-       return 0;
-
-error_sysfs:
-               remove_sysfs(acer_platform_device);
-       return retval;
-}
-
-static void remove_debugfs(void)
-{
-       debugfs_remove(interface->debug.devices);
-       debugfs_remove(interface->debug.root);
-}
-
-static int create_debugfs(void)
-{
-       interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
-       if (!interface->debug.root) {
-               printk(ACER_ERR "Failed to create debugfs directory");
-               return -ENOMEM;
-       }
-
-       interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
-                                       interface->debug.root,
-                                       &interface->debug.wmid_devices);
-       if (!interface->debug.devices)
-               goto error_debugfs;
-
-       return 0;
-
-error_debugfs:
-       remove_debugfs();
-       return -ENOMEM;
-}
-
-static int __init acer_wmi_init(void)
-{
-       int err;
-
-       printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
-
-       find_quirks();
-
-       /*
-        * Detect which ACPI-WMI interface we're using.
-        */
-       if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
-               interface = &AMW0_V2_interface;
-
-       if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
-               interface = &wmid_interface;
-
-       if (wmi_has_guid(WMID_GUID2) && interface) {
-               if (ACPI_FAILURE(WMID_set_capabilities())) {
-                       printk(ACER_ERR "Unable to detect available WMID "
-                                       "devices\n");
-                       return -ENODEV;
-               }
-       } else if (!wmi_has_guid(WMID_GUID2) && interface) {
-               printk(ACER_ERR "No WMID device detection method found\n");
-               return -ENODEV;
-       }
-
-       if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
-               interface = &AMW0_interface;
-
-               if (ACPI_FAILURE(AMW0_set_capabilities())) {
-                       printk(ACER_ERR "Unable to detect available AMW0 "
-                                       "devices\n");
-                       return -ENODEV;
-               }
-       }
-
-       if (wmi_has_guid(AMW0_GUID1))
-               AMW0_find_mailled();
-
-       if (!interface) {
-               printk(ACER_ERR "No or unsupported WMI interface, unable to "
-                               "load\n");
-               return -ENODEV;
-       }
-
-       set_quirks();
-
-       if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
-               interface->capability &= ~ACER_CAP_BRIGHTNESS;
-               printk(ACER_INFO "Brightness must be controlled by "
-                      "generic video driver\n");
-       }
-
-       if (platform_driver_register(&acer_platform_driver)) {
-               printk(ACER_ERR "Unable to register platform driver.\n");
-               goto error_platform_register;
-       }
-       acer_platform_device = platform_device_alloc("acer-wmi", -1);
-       platform_device_add(acer_platform_device);
-
-       err = create_sysfs();
-       if (err)
-               return err;
-
-       if (wmi_has_guid(WMID_GUID2)) {
-               interface->debug.wmid_devices = get_wmid_devices();
-               err = create_debugfs();
-               if (err)
-                       return err;
-       }
-
-       /* Override any initial settings with values from the commandline */
-       acer_commandline_init();
-
-       return 0;
-
-error_platform_register:
-       return -ENODEV;
-}
-
-static void __exit acer_wmi_exit(void)
-{
-       remove_sysfs(acer_platform_device);
-       remove_debugfs();
-       platform_device_del(acer_platform_device);
-       platform_driver_unregister(&acer_platform_driver);
-
-       printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
-       return;
-}
-
-module_init(acer_wmi_init);
-module_exit(acer_wmi_exit);
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c
deleted file mode 100644 (file)
index 8fb8b35..0000000
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- *  asus-laptop.c - Asus Laptop Support
- *
- *
- *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
- *  Copyright (C) 2006-2007 Corentin Chary
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *
- *  The development page for this driver is located at
- *  http://sourceforge.net/projects/acpi4asus/
- *
- *  Credits:
- *  Pontus Fuchs   - Helper functions, cleanup
- *  Johann Wiesner - Small compile fixes
- *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
- *  Eric Burghard  - LED display support for W1N
- *  Josh Green     - Light Sens support
- *  Thomas Tuttle  - His first patch for led support was very helpfull
- *  Sam Lin        - GPS support
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/err.h>
-#include <linux/proc_fs.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <linux/leds.h>
-#include <linux/platform_device.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <asm/uaccess.h>
-
-#define ASUS_LAPTOP_VERSION "0.42"
-
-#define ASUS_HOTK_NAME          "Asus Laptop Support"
-#define ASUS_HOTK_CLASS         "hotkey"
-#define ASUS_HOTK_DEVICE_NAME   "Hotkey"
-#define ASUS_HOTK_FILE          "asus-laptop"
-#define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
-
-/*
- * Some events we use, same for all Asus
- */
-#define ATKD_BR_UP       0x10
-#define ATKD_BR_DOWN     0x20
-#define ATKD_LCD_ON      0x33
-#define ATKD_LCD_OFF     0x34
-
-/*
- * Known bits returned by \_SB.ATKD.HWRS
- */
-#define WL_HWRS     0x80
-#define BT_HWRS     0x100
-
-/*
- * Flags for hotk status
- * WL_ON and BT_ON are also used for wireless_status()
- */
-#define WL_ON       0x01       //internal Wifi
-#define BT_ON       0x02       //internal Bluetooth
-#define MLED_ON     0x04       //mail LED
-#define TLED_ON     0x08       //touchpad LED
-#define RLED_ON     0x10       //Record LED
-#define PLED_ON     0x20       //Phone LED
-#define GLED_ON     0x40       //Gaming LED
-#define LCD_ON      0x80       //LCD backlight
-#define GPS_ON      0x100      //GPS
-
-#define ASUS_LOG    ASUS_HOTK_FILE ": "
-#define ASUS_ERR    KERN_ERR    ASUS_LOG
-#define ASUS_WARNING    KERN_WARNING    ASUS_LOG
-#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
-#define ASUS_INFO   KERN_INFO   ASUS_LOG
-#define ASUS_DEBUG  KERN_DEBUG  ASUS_LOG
-
-MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
-MODULE_DESCRIPTION(ASUS_HOTK_NAME);
-MODULE_LICENSE("GPL");
-
-/* WAPF defines the behavior of the Fn+Fx wlan key
- * The significance of values is yet to be found, but
- * most of the time:
- * 0x0 will do nothing
- * 0x1 will allow to control the device with Fn+Fx key.
- * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
- * 0x5 like 0x1 or 0x4
- * So, if something doesn't work as you want, just try other values =)
- */
-static uint wapf = 1;
-module_param(wapf, uint, 0644);
-MODULE_PARM_DESC(wapf, "WAPF value");
-
-#define ASUS_HANDLE(object, paths...)                                  \
-       static acpi_handle  object##_handle = NULL;                     \
-       static char *object##_paths[] = { paths }
-
-/* LED */
-ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
-ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
-ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED");        /* W1JC */
-ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED");        /* A7J */
-ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED");        /* G1, G2 (probably) */
-
-/* LEDD */
-ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
-
-/* Bluetooth and WLAN
- * WLED and BLED are not handled like other XLED, because in some dsdt
- * they also control the WLAN/Bluetooth device.
- */
-ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
-ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
-ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */
-
-/* Brightness */
-ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
-ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
-
-/* Backlight */
-ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10",    /* All new models */
-           "\\_SB.PCI0.ISA.EC0._Q10",  /* A1x */
-           "\\_SB.PCI0.PX40.ECD0._Q10",        /* L3C */
-           "\\_SB.PCI0.PX40.EC0.Q10",  /* M1A */
-           "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
-           "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
-           "\\_SB.PCI0.PX40.Q10",      /* S1x */
-           "\\Q10");           /* A2x, L2D, L3D, M2E */
-
-/* Display */
-ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
-ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD",   /*  A6B, A6K A6R A7D F3JM L4R M6R A3G
-                                                          M6A M6V VX-1 V6J V6V W3Z */
-           "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
-                                          S5A M5A z33A W1Jc W2V G1 */
-           "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
-           "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
-           "\\_SB.PCI0.PCI1.VGAC.NMAP",        /* L3C */
-           "\\_SB.PCI0.VGA.GETD",      /* Z96F */
-           "\\ACTD",           /* A2D */
-           "\\ADVG",           /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
-           "\\DNXT",           /* P30 */
-           "\\INFB",           /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
-           "\\SSTE");          /* A3F A6F A3N A3L M6N W3N W6A */
-
-ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC");       /* Z71A Z71V */
-ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL");        /* Z71A Z71V */
-
-/* GPS */
-/* R2H use different handle for GPS on/off */
-ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");  /* R2H */
-ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
-ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
-
-/*
- * This is the main structure, we can use it to store anything interesting
- * about the hotk device
- */
-struct asus_hotk {
-       char *name;             //laptop name
-       struct acpi_device *device;     //the device we are in
-       acpi_handle handle;     //the handle of the hotk device
-       char status;            //status of the hotk, for LEDs, ...
-       u32 ledd_status;        //status of the LED display
-       u8 light_level;         //light sensor level
-       u8 light_switch;        //light sensor switch value
-       u16 event_count[128];   //count for each event TODO make this better
-};
-
-/*
- * This header is made available to allow proper configuration given model,
- * revision number , ... this info cannot go in struct asus_hotk because it is
- * available before the hotk
- */
-static struct acpi_table_header *asus_info;
-
-/* The actual device the driver binds to */
-static struct asus_hotk *hotk;
-
-/*
- * The hotkey driver declaration
- */
-static const struct acpi_device_id asus_device_ids[] = {
-       {"ATK0100", 0},
-       {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, asus_device_ids);
-
-static int asus_hotk_add(struct acpi_device *device);
-static int asus_hotk_remove(struct acpi_device *device, int type);
-static struct acpi_driver asus_hotk_driver = {
-       .name = ASUS_HOTK_NAME,
-       .class = ASUS_HOTK_CLASS,
-       .ids = asus_device_ids,
-       .ops = {
-               .add = asus_hotk_add,
-               .remove = asus_hotk_remove,
-               },
-};
-
-/* The backlight device /sys/class/backlight */
-static struct backlight_device *asus_backlight_device;
-
-/*
- * The backlight class declaration
- */
-static int read_brightness(struct backlight_device *bd);
-static int update_bl_status(struct backlight_device *bd);
-static struct backlight_ops asusbl_ops = {
-       .get_brightness = read_brightness,
-       .update_status = update_bl_status,
-};
-
-/* These functions actually update the LED's, and are called from a
- * workqueue. By doing this as separate work rather than when the LED
- * subsystem asks, we avoid messing with the Asus ACPI stuff during a
- * potentially bad time, such as a timer interrupt. */
-static struct workqueue_struct *led_workqueue;
-
-#define ASUS_LED(object, ledname)                                      \
-       static void object##_led_set(struct led_classdev *led_cdev,     \
-                                    enum led_brightness value);        \
-       static void object##_led_update(struct work_struct *ignored);   \
-       static int object##_led_wk;                                     \
-       static DECLARE_WORK(object##_led_work, object##_led_update);    \
-       static struct led_classdev object##_led = {                     \
-               .name           = "asus::" ledname,                     \
-               .brightness_set = object##_led_set,                     \
-       }
-
-ASUS_LED(mled, "mail");
-ASUS_LED(tled, "touchpad");
-ASUS_LED(rled, "record");
-ASUS_LED(pled, "phone");
-ASUS_LED(gled, "gaming");
-
-/*
- * This function evaluates an ACPI method, given an int as parameter, the
- * method is searched within the scope of the handle, can be NULL. The output
- * of the method is written is output, which can also be NULL
- *
- * returns 0 if write is successful, -1 else.
- */
-static int write_acpi_int(acpi_handle handle, const char *method, int val,
-                         struct acpi_buffer *output)
-{
-       struct acpi_object_list params; //list of input parameters (an int here)
-       union acpi_object in_obj;       //the only param we use
-       acpi_status status;
-
-       if (!handle)
-               return 0;
-
-       params.count = 1;
-       params.pointer = &in_obj;
-       in_obj.type = ACPI_TYPE_INTEGER;
-       in_obj.integer.value = val;
-
-       status = acpi_evaluate_object(handle, (char *)method, &params, output);
-       if (status == AE_OK)
-               return 0;
-       else
-               return -1;
-}
-
-static int read_wireless_status(int mask)
-{
-       unsigned long long status;
-       acpi_status rv = AE_OK;
-
-       if (!wireless_status_handle)
-               return (hotk->status & mask) ? 1 : 0;
-
-       rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
-       if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading Wireless status\n");
-       else
-               return (status & mask) ? 1 : 0;
-
-       return (hotk->status & mask) ? 1 : 0;
-}
-
-static int read_gps_status(void)
-{
-       unsigned long long status;
-       acpi_status rv = AE_OK;
-
-       rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
-       if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading GPS status\n");
-       else
-               return status ? 1 : 0;
-
-       return (hotk->status & GPS_ON) ? 1 : 0;
-}
-
-/* Generic LED functions */
-static int read_status(int mask)
-{
-       /* There is a special method for both wireless devices */
-       if (mask == BT_ON || mask == WL_ON)
-               return read_wireless_status(mask);
-       else if (mask == GPS_ON)
-               return read_gps_status();
-
-       return (hotk->status & mask) ? 1 : 0;
-}
-
-static void write_status(acpi_handle handle, int out, int mask)
-{
-       hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
-
-       switch (mask) {
-       case MLED_ON:
-               out = !(out & 0x1);
-               break;
-       case GLED_ON:
-               out = (out & 0x1) + 1;
-               break;
-       case GPS_ON:
-               handle = (out) ? gps_on_handle : gps_off_handle;
-               out = 0x02;
-               break;
-       default:
-               out &= 0x1;
-               break;
-       }
-
-       if (write_acpi_int(handle, NULL, out, NULL))
-               printk(ASUS_WARNING " write failed %x\n", mask);
-}
-
-/* /sys/class/led handlers */
-#define ASUS_LED_HANDLER(object, mask)                                 \
-       static void object##_led_set(struct led_classdev *led_cdev,     \
-                                    enum led_brightness value)         \
-       {                                                               \
-               object##_led_wk = (value > 0) ? 1 : 0;                  \
-               queue_work(led_workqueue, &object##_led_work);          \
-       }                                                               \
-       static void object##_led_update(struct work_struct *ignored)    \
-       {                                                               \
-               int value = object##_led_wk;                            \
-               write_status(object##_set_handle, value, (mask));       \
-       }
-
-ASUS_LED_HANDLER(mled, MLED_ON);
-ASUS_LED_HANDLER(pled, PLED_ON);
-ASUS_LED_HANDLER(rled, RLED_ON);
-ASUS_LED_HANDLER(tled, TLED_ON);
-ASUS_LED_HANDLER(gled, GLED_ON);
-
-static int get_lcd_state(void)
-{
-       return read_status(LCD_ON);
-}
-
-static int set_lcd_state(int value)
-{
-       int lcd = 0;
-       acpi_status status = 0;
-
-       lcd = value ? 1 : 0;
-
-       if (lcd == get_lcd_state())
-               return 0;
-
-       if (lcd_switch_handle) {
-               status = acpi_evaluate_object(lcd_switch_handle,
-                                             NULL, NULL, NULL);
-
-               if (ACPI_FAILURE(status))
-                       printk(ASUS_WARNING "Error switching LCD\n");
-       }
-
-       write_status(NULL, lcd, LCD_ON);
-       return 0;
-}
-
-static void lcd_blank(int blank)
-{
-       struct backlight_device *bd = asus_backlight_device;
-
-       if (bd) {
-               bd->props.power = blank;
-               backlight_update_status(bd);
-       }
-}
-
-static int read_brightness(struct backlight_device *bd)
-{
-       unsigned long long value;
-       acpi_status rv = AE_OK;
-
-       rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
-       if (ACPI_FAILURE(rv))
-               printk(ASUS_WARNING "Error reading brightness\n");
-
-       return value;
-}
-
-static int set_brightness(struct backlight_device *bd, int value)
-{
-       int ret = 0;
-
-       value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
-       /* 0 <= value <= 15 */
-
-       if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
-               printk(ASUS_WARNING "Error changing brightness\n");
-               ret = -EIO;
-       }
-
-       return ret;
-}
-
-static int update_bl_status(struct backlight_device *bd)
-{
-       int rv;
-       int value = bd->props.brightness;
-
-       rv = set_brightness(bd, value);
-       if (rv)
-               return rv;
-
-       value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
-       return set_lcd_state(value);
-}
-
-/*
- * Platform device handlers
- */
-
-/*
- * We write our info in page, we begin at offset off and cannot write more
- * than count bytes. We set eof to 1 if we handle those 2 values. We return the
- * number of bytes written in page
- */
-static ssize_t show_infos(struct device *dev,
-                         struct device_attribute *attr, char *page)
-{
-       int len = 0;
-       unsigned long long temp;
-       char buf[16];           //enough for all info
-       acpi_status rv = AE_OK;
-
-       /*
-        * We use the easy way, we don't care of off and count, so we don't set eof
-        * to 1
-        */
-
-       len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
-       len += sprintf(page + len, "Model reference    : %s\n", hotk->name);
-       /*
-        * The SFUN method probably allows the original driver to get the list
-        * of features supported by a given model. For now, 0x0100 or 0x0800
-        * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
-        * The significance of others is yet to be found.
-        */
-       rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
-       if (!ACPI_FAILURE(rv))
-               len += sprintf(page + len, "SFUN value         : 0x%04x\n",
-                              (uint) temp);
-       /*
-        * Another value for userspace: the ASYM method returns 0x02 for
-        * battery low and 0x04 for battery critical, its readings tend to be
-        * more accurate than those provided by _BST.
-        * Note: since not all the laptops provide this method, errors are
-        * silently ignored.
-        */
-       rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
-       if (!ACPI_FAILURE(rv))
-               len += sprintf(page + len, "ASYM value         : 0x%04x\n",
-                              (uint) temp);
-       if (asus_info) {
-               snprintf(buf, 16, "%d", asus_info->length);
-               len += sprintf(page + len, "DSDT length        : %s\n", buf);
-               snprintf(buf, 16, "%d", asus_info->checksum);
-               len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
-               snprintf(buf, 16, "%d", asus_info->revision);
-               len += sprintf(page + len, "DSDT revision      : %s\n", buf);
-               snprintf(buf, 7, "%s", asus_info->oem_id);
-               len += sprintf(page + len, "OEM id             : %s\n", buf);
-               snprintf(buf, 9, "%s", asus_info->oem_table_id);
-               len += sprintf(page + len, "OEM table id       : %s\n", buf);
-               snprintf(buf, 16, "%x", asus_info->oem_revision);
-               len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
-               snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
-               len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
-               snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
-               len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
-       }
-
-       return len;
-}
-
-static int parse_arg(const char *buf, unsigned long count, int *val)
-{
-       if (!count)
-               return 0;
-       if (count > 31)
-               return -EINVAL;
-       if (sscanf(buf, "%i", val) != 1)
-               return -EINVAL;
-       return count;
-}
-
-static ssize_t store_status(const char *buf, size_t count,
-                           acpi_handle handle, int mask)
-{
-       int rv, value;
-       int out = 0;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               out = value ? 1 : 0;
-
-       write_status(handle, out, mask);
-
-       return rv;
-}
-
-/*
- * LEDD display
- */
-static ssize_t show_ledd(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "0x%08x\n", hotk->ledd_status);
-}
-
-static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
-                       printk(ASUS_WARNING "LED display write failed\n");
-               else
-                       hotk->ledd_status = (u32) value;
-       }
-       return rv;
-}
-
-/*
- * WLAN
- */
-static ssize_t show_wlan(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", read_status(WL_ON));
-}
-
-static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       return store_status(buf, count, wl_switch_handle, WL_ON);
-}
-
-/*
- * Bluetooth
- */
-static ssize_t show_bluetooth(struct device *dev,
-                             struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", read_status(BT_ON));
-}
-
-static ssize_t store_bluetooth(struct device *dev,
-                              struct device_attribute *attr, const char *buf,
-                              size_t count)
-{
-       return store_status(buf, count, bt_switch_handle, BT_ON);
-}
-
-/*
- * Display
- */
-static void set_display(int value)
-{
-       /* no sanity check needed for now */
-       if (write_acpi_int(display_set_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting display\n");
-       return;
-}
-
-static int read_display(void)
-{
-       unsigned long long value = 0;
-       acpi_status rv = AE_OK;
-
-       /* In most of the case, we know how to set the display, but sometime
-          we can't read it */
-       if (display_get_handle) {
-               rv = acpi_evaluate_integer(display_get_handle, NULL,
-                                          NULL, &value);
-               if (ACPI_FAILURE(rv))
-                       printk(ASUS_WARNING "Error reading display status\n");
-       }
-
-       value &= 0x0F;          /* needed for some models, shouldn't hurt others */
-
-       return value;
-}
-
-/*
- * Now, *this* one could be more user-friendly, but so far, no-one has
- * complained. The significance of bits is the same as in store_disp()
- */
-static ssize_t show_disp(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", read_display());
-}
-
-/*
- * Experimental support for display switching. As of now: 1 should activate
- * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
- * Any combination (bitwise) of these will suffice. I never actually tested 4
- * displays hooked up simultaneously, so be warned. See the acpi4asus README
- * for more info.
- */
-static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               set_display(value);
-       return rv;
-}
-
-/*
- * Light Sens
- */
-static void set_light_sens_switch(int value)
-{
-       if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor switch\n");
-       hotk->light_switch = value;
-}
-
-static ssize_t show_lssw(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", hotk->light_switch);
-}
-
-static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               set_light_sens_switch(value ? 1 : 0);
-
-       return rv;
-}
-
-static void set_light_sens_level(int value)
-{
-       if (write_acpi_int(ls_level_handle, NULL, value, NULL))
-               printk(ASUS_WARNING "Error setting light sensor level\n");
-       hotk->light_level = value;
-}
-
-static ssize_t show_lslvl(struct device *dev,
-                         struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", hotk->light_level);
-}
-
-static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
-               /* 0 <= value <= 15 */
-               set_light_sens_level(value);
-       }
-
-       return rv;
-}
-
-/*
- * GPS
- */
-static ssize_t show_gps(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "%d\n", read_status(GPS_ON));
-}
-
-static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
-                        const char *buf, size_t count)
-{
-       return store_status(buf, count, NULL, GPS_ON);
-}
-
-static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
-{
-       /* TODO Find a better way to handle events count. */
-       if (!hotk)
-               return;
-
-       /*
-        * We need to tell the backlight device when the backlight power is
-        * switched
-        */
-       if (event == ATKD_LCD_ON) {
-               write_status(NULL, 1, LCD_ON);
-               lcd_blank(FB_BLANK_UNBLANK);
-       } else if (event == ATKD_LCD_OFF) {
-               write_status(NULL, 0, LCD_ON);
-               lcd_blank(FB_BLANK_POWERDOWN);
-       }
-
-       acpi_bus_generate_proc_event(hotk->device, event,
-                               hotk->event_count[event % 128]++);
-
-       return;
-}
-
-#define ASUS_CREATE_DEVICE_ATTR(_name)                                 \
-       struct device_attribute dev_attr_##_name = {                    \
-               .attr = {                                               \
-                       .name = __stringify(_name),                     \
-                       .mode = 0 },                                    \
-               .show   = NULL,                                         \
-               .store  = NULL,                                         \
-       }
-
-#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store)              \
-       do {                                                            \
-               dev_attr_##_name.attr.mode = _mode;                     \
-               dev_attr_##_name.show = _show;                          \
-               dev_attr_##_name.store = _store;                        \
-       } while(0)
-
-static ASUS_CREATE_DEVICE_ATTR(infos);
-static ASUS_CREATE_DEVICE_ATTR(wlan);
-static ASUS_CREATE_DEVICE_ATTR(bluetooth);
-static ASUS_CREATE_DEVICE_ATTR(display);
-static ASUS_CREATE_DEVICE_ATTR(ledd);
-static ASUS_CREATE_DEVICE_ATTR(ls_switch);
-static ASUS_CREATE_DEVICE_ATTR(ls_level);
-static ASUS_CREATE_DEVICE_ATTR(gps);
-
-static struct attribute *asuspf_attributes[] = {
-       &dev_attr_infos.attr,
-       &dev_attr_wlan.attr,
-       &dev_attr_bluetooth.attr,
-       &dev_attr_display.attr,
-       &dev_attr_ledd.attr,
-       &dev_attr_ls_switch.attr,
-       &dev_attr_ls_level.attr,
-       &dev_attr_gps.attr,
-       NULL
-};
-
-static struct attribute_group asuspf_attribute_group = {
-       .attrs = asuspf_attributes
-};
-
-static struct platform_driver asuspf_driver = {
-       .driver = {
-                  .name = ASUS_HOTK_FILE,
-                  .owner = THIS_MODULE,
-                  }
-};
-
-static struct platform_device *asuspf_device;
-
-static void asus_hotk_add_fs(void)
-{
-       ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
-
-       if (wl_switch_handle)
-               ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
-
-       if (bt_switch_handle)
-               ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
-                                    show_bluetooth, store_bluetooth);
-
-       if (display_set_handle && display_get_handle)
-               ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
-       else if (display_set_handle)
-               ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
-
-       if (ledd_set_handle)
-               ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
-
-       if (ls_switch_handle && ls_level_handle) {
-               ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
-               ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
-       }
-
-       if (gps_status_handle && gps_on_handle && gps_off_handle)
-               ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
-}
-
-static int asus_handle_init(char *name, acpi_handle * handle,
-                           char **paths, int num_paths)
-{
-       int i;
-       acpi_status status;
-
-       for (i = 0; i < num_paths; i++) {
-               status = acpi_get_handle(NULL, paths[i], handle);
-               if (ACPI_SUCCESS(status))
-                       return 0;
-       }
-
-       *handle = NULL;
-       return -ENODEV;
-}
-
-#define ASUS_HANDLE_INIT(object)                                       \
-       asus_handle_init(#object, &object##_handle, object##_paths,     \
-                        ARRAY_SIZE(object##_paths))
-
-/*
- * This function is used to initialize the hotk with right values. In this
- * method, we can make all the detection we want, and modify the hotk struct
- */
-static int asus_hotk_get_info(void)
-{
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *model = NULL;
-       unsigned long long bsts_result, hwrs_result;
-       char *string = NULL;
-       acpi_status status;
-
-       /*
-        * Get DSDT headers early enough to allow for differentiating between
-        * models, but late enough to allow acpi_bus_register_driver() to fail
-        * before doing anything ACPI-specific. Should we encounter a machine,
-        * which needs special handling (i.e. its hotkey device has a different
-        * HID), this bit will be moved. A global variable asus_info contains
-        * the DSDT header.
-        */
-       status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
-
-       /* We have to write 0 on init this far for all ASUS models */
-       if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
-               printk(ASUS_ERR "Hotkey initialization failed\n");
-               return -ENODEV;
-       }
-
-       /* This needs to be called for some laptops to init properly */
-       status =
-           acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_WARNING "Error calling BSTS\n");
-       else if (bsts_result)
-               printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
-                      (uint) bsts_result);
-
-       /* This too ... */
-       write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
-
-       /*
-        * Try to match the object returned by INIT to the specific model.
-        * Handle every possible object (or the lack of thereof) the DSDT
-        * writers might throw at us. When in trouble, we pass NULL to
-        * asus_model_match() and try something completely different.
-        */
-       if (buffer.pointer) {
-               model = buffer.pointer;
-               switch (model->type) {
-               case ACPI_TYPE_STRING:
-                       string = model->string.pointer;
-                       break;
-               case ACPI_TYPE_BUFFER:
-                       string = model->buffer.pointer;
-                       break;
-               default:
-                       string = "";
-                       break;
-               }
-       }
-       hotk->name = kstrdup(string, GFP_KERNEL);
-       if (!hotk->name)
-               return -ENOMEM;
-
-       if (*string)
-               printk(ASUS_NOTICE "  %s model detected\n", string);
-
-       ASUS_HANDLE_INIT(mled_set);
-       ASUS_HANDLE_INIT(tled_set);
-       ASUS_HANDLE_INIT(rled_set);
-       ASUS_HANDLE_INIT(pled_set);
-       ASUS_HANDLE_INIT(gled_set);
-
-       ASUS_HANDLE_INIT(ledd_set);
-
-       /*
-        * The HWRS method return informations about the hardware.
-        * 0x80 bit is for WLAN, 0x100 for Bluetooth.
-        * The significance of others is yet to be found.
-        * If we don't find the method, we assume the device are present.
-        */
-       status =
-           acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
-       if (ACPI_FAILURE(status))
-               hwrs_result = WL_HWRS | BT_HWRS;
-
-       if (hwrs_result & WL_HWRS)
-               ASUS_HANDLE_INIT(wl_switch);
-       if (hwrs_result & BT_HWRS)
-               ASUS_HANDLE_INIT(bt_switch);
-
-       ASUS_HANDLE_INIT(wireless_status);
-
-       ASUS_HANDLE_INIT(brightness_set);
-       ASUS_HANDLE_INIT(brightness_get);
-
-       ASUS_HANDLE_INIT(lcd_switch);
-
-       ASUS_HANDLE_INIT(display_set);
-       ASUS_HANDLE_INIT(display_get);
-
-       /* There is a lot of models with "ALSL", but a few get
-          a real light sens, so we need to check it. */
-       if (!ASUS_HANDLE_INIT(ls_switch))
-               ASUS_HANDLE_INIT(ls_level);
-
-       ASUS_HANDLE_INIT(gps_on);
-       ASUS_HANDLE_INIT(gps_off);
-       ASUS_HANDLE_INIT(gps_status);
-
-       kfree(model);
-
-       return AE_OK;
-}
-
-static int asus_hotk_check(void)
-{
-       int result = 0;
-
-       result = acpi_bus_get_status(hotk->device);
-       if (result)
-               return result;
-
-       if (hotk->device->status.present) {
-               result = asus_hotk_get_info();
-       } else {
-               printk(ASUS_ERR "Hotkey device not present, aborting\n");
-               return -EINVAL;
-       }
-
-       return result;
-}
-
-static int asus_hotk_found;
-
-static int asus_hotk_add(struct acpi_device *device)
-{
-       acpi_status status = AE_OK;
-       int result;
-
-       if (!device)
-               return -EINVAL;
-
-       printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
-              ASUS_LAPTOP_VERSION);
-
-       hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
-       if (!hotk)
-               return -ENOMEM;
-
-       hotk->handle = device->handle;
-       strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
-       strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
-       device->driver_data = hotk;
-       hotk->device = device;
-
-       result = asus_hotk_check();
-       if (result)
-               goto end;
-
-       asus_hotk_add_fs();
-
-       /*
-        * We install the handler, it will receive the hotk in parameter, so, we
-        * could add other data to the hotk struct
-        */
-       status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                            asus_hotk_notify, hotk);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error installing notify handler\n");
-
-       asus_hotk_found = 1;
-
-       /* WLED and BLED are on by default */
-       write_status(bt_switch_handle, 1, BT_ON);
-       write_status(wl_switch_handle, 1, WL_ON);
-
-       /* If the h/w switch is off, we need to check the real status */
-       write_status(NULL, read_status(BT_ON), BT_ON);
-       write_status(NULL, read_status(WL_ON), WL_ON);
-
-       /* LCD Backlight is on by default */
-       write_status(NULL, 1, LCD_ON);
-
-       /* LED display is off by default */
-       hotk->ledd_status = 0xFFF;
-
-       /* Set initial values of light sensor and level */
-       hotk->light_switch = 1; /* Default to light sensor disabled */
-       hotk->light_level = 0;  /* level 5 for sensor sensitivity */
-
-       if (ls_switch_handle)
-               set_light_sens_switch(hotk->light_switch);
-
-       if (ls_level_handle)
-               set_light_sens_level(hotk->light_level);
-
-       /* GPS is on by default */
-       write_status(NULL, 1, GPS_ON);
-
-      end:
-       if (result) {
-               kfree(hotk->name);
-               kfree(hotk);
-       }
-
-       return result;
-}
-
-static int asus_hotk_remove(struct acpi_device *device, int type)
-{
-       acpi_status status = 0;
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
-                                           asus_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(ASUS_ERR "Error removing notify handler\n");
-
-       kfree(hotk->name);
-       kfree(hotk);
-
-       return 0;
-}
-
-static void asus_backlight_exit(void)
-{
-       if (asus_backlight_device)
-               backlight_device_unregister(asus_backlight_device);
-}
-
-#define  ASUS_LED_UNREGISTER(object)                           \
-       if (object##_led.dev)                                   \
-               led_classdev_unregister(&object##_led)
-
-static void asus_led_exit(void)
-{
-       destroy_workqueue(led_workqueue);
-       ASUS_LED_UNREGISTER(mled);
-       ASUS_LED_UNREGISTER(tled);
-       ASUS_LED_UNREGISTER(pled);
-       ASUS_LED_UNREGISTER(rled);
-       ASUS_LED_UNREGISTER(gled);
-}
-
-static void __exit asus_laptop_exit(void)
-{
-       asus_backlight_exit();
-       asus_led_exit();
-
-       acpi_bus_unregister_driver(&asus_hotk_driver);
-       sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
-       platform_device_unregister(asuspf_device);
-       platform_driver_unregister(&asuspf_driver);
-}
-
-static int asus_backlight_init(struct device *dev)
-{
-       struct backlight_device *bd;
-
-       if (brightness_set_handle && lcd_switch_handle) {
-               bd = backlight_device_register(ASUS_HOTK_FILE, dev,
-                                              NULL, &asusbl_ops);
-               if (IS_ERR(bd)) {
-                       printk(ASUS_ERR
-                              "Could not register asus backlight device\n");
-                       asus_backlight_device = NULL;
-                       return PTR_ERR(bd);
-               }
-
-               asus_backlight_device = bd;
-
-               bd->props.max_brightness = 15;
-               bd->props.brightness = read_brightness(NULL);
-               bd->props.power = FB_BLANK_UNBLANK;
-               backlight_update_status(bd);
-       }
-       return 0;
-}
-
-static int asus_led_register(acpi_handle handle,
-                            struct led_classdev *ldev, struct device *dev)
-{
-       if (!handle)
-               return 0;
-
-       return led_classdev_register(dev, ldev);
-}
-
-#define ASUS_LED_REGISTER(object, device)                              \
-       asus_led_register(object##_set_handle, &object##_led, device)
-
-static int asus_led_init(struct device *dev)
-{
-       int rv;
-
-       rv = ASUS_LED_REGISTER(mled, dev);
-       if (rv)
-               goto out;
-
-       rv = ASUS_LED_REGISTER(tled, dev);
-       if (rv)
-               goto out1;
-
-       rv = ASUS_LED_REGISTER(rled, dev);
-       if (rv)
-               goto out2;
-
-       rv = ASUS_LED_REGISTER(pled, dev);
-       if (rv)
-               goto out3;
-
-       rv = ASUS_LED_REGISTER(gled, dev);
-       if (rv)
-               goto out4;
-
-       led_workqueue = create_singlethread_workqueue("led_workqueue");
-       if (!led_workqueue)
-               goto out5;
-
-       return 0;
-out5:
-       rv = -ENOMEM;
-       ASUS_LED_UNREGISTER(gled);
-out4:
-       ASUS_LED_UNREGISTER(pled);
-out3:
-       ASUS_LED_UNREGISTER(rled);
-out2:
-       ASUS_LED_UNREGISTER(tled);
-out1:
-       ASUS_LED_UNREGISTER(mled);
-out:
-       return rv;
-}
-
-static int __init asus_laptop_init(void)
-{
-       struct device *dev;
-       int result;
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       result = acpi_bus_register_driver(&asus_hotk_driver);
-       if (result < 0)
-               return result;
-
-       /*
-        * This is a bit of a kludge.  We only want this module loaded
-        * for ASUS systems, but there's currently no way to probe the
-        * ACPI namespace for ASUS HIDs.  So we just return failure if
-        * we didn't find one, which will cause the module to be
-        * unloaded.
-        */
-       if (!asus_hotk_found) {
-               acpi_bus_unregister_driver(&asus_hotk_driver);
-               return -ENODEV;
-       }
-
-       dev = acpi_get_physical_device(hotk->device->handle);
-
-       if (!acpi_video_backlight_support()) {
-               result = asus_backlight_init(dev);
-               if (result)
-                       goto fail_backlight;
-       } else
-               printk(ASUS_INFO "Brightness ignored, must be controlled by "
-                      "ACPI video driver\n");
-
-       result = asus_led_init(dev);
-       if (result)
-               goto fail_led;
-
-       /* Register platform stuff */
-       result = platform_driver_register(&asuspf_driver);
-       if (result)
-               goto fail_platform_driver;
-
-       asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
-       if (!asuspf_device) {
-               result = -ENOMEM;
-               goto fail_platform_device1;
-       }
-
-       result = platform_device_add(asuspf_device);
-       if (result)
-               goto fail_platform_device2;
-
-       result = sysfs_create_group(&asuspf_device->dev.kobj,
-                                   &asuspf_attribute_group);
-       if (result)
-               goto fail_sysfs;
-
-       return 0;
-
-      fail_sysfs:
-       platform_device_del(asuspf_device);
-
-      fail_platform_device2:
-       platform_device_put(asuspf_device);
-
-      fail_platform_device1:
-       platform_driver_unregister(&asuspf_driver);
-
-      fail_platform_driver:
-       asus_led_exit();
-
-      fail_led:
-       asus_backlight_exit();
-
-      fail_backlight:
-
-       return result;
-}
-
-module_init(asus_laptop_init);
-module_exit(asus_laptop_exit);
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c
deleted file mode 100644 (file)
index 11003bb..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-/*-*-linux-c-*-*/
-
-/*
-  Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
-
-  based on MSI driver
-
-  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-  02110-1301, USA.
- */
-
-/*
- * comapl-laptop.c - Compal laptop support.
- *
- * This driver exports a few files in /sys/devices/platform/compal-laptop/:
- *
- *   wlan - wlan subsystem state: contains 0 or 1 (rw)
- *
- *   bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
- *
- *   raw - raw value taken from embedded controller register (ro)
- *
- * In addition to these platform device attributes the driver
- * registers itself in the Linux backlight control subsystem and is
- * available to userspace under /sys/class/backlight/compal-laptop/.
- *
- * This driver might work on other laptops produced by Compal. If you
- * want to try it you can pass force=1 as argument to the module which
- * will force it to load even when the DMI data doesn't identify the
- * laptop as FL9x.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/acpi.h>
-#include <linux/dmi.h>
-#include <linux/backlight.h>
-#include <linux/platform_device.h>
-#include <linux/autoconf.h>
-
-#define COMPAL_DRIVER_VERSION "0.2.6"
-
-#define COMPAL_LCD_LEVEL_MAX 8
-
-#define COMPAL_EC_COMMAND_WIRELESS 0xBB
-#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
-
-#define KILLSWITCH_MASK 0x10
-#define WLAN_MASK      0x01
-#define BT_MASK        0x02
-
-static int force;
-module_param(force, bool, 0);
-MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
-
-/* Hardware access */
-
-static int set_lcd_level(int level)
-{
-       if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
-               return -EINVAL;
-
-       ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
-
-       return 0;
-}
-
-static int get_lcd_level(void)
-{
-       u8 result;
-
-       ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
-
-       return (int) result;
-}
-
-static int set_wlan_state(int state)
-{
-       u8 result, value;
-
-       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
-
-       if ((result & KILLSWITCH_MASK) == 0)
-               return -EINVAL;
-       else {
-               if (state)
-                       value = (u8) (result | WLAN_MASK);
-               else
-                       value = (u8) (result & ~WLAN_MASK);
-               ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
-       }
-
-       return 0;
-}
-
-static int set_bluetooth_state(int state)
-{
-       u8 result, value;
-
-       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
-
-       if ((result & KILLSWITCH_MASK) == 0)
-               return -EINVAL;
-       else {
-               if (state)
-                       value = (u8) (result | BT_MASK);
-               else
-                       value = (u8) (result & ~BT_MASK);
-               ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
-       }
-
-       return 0;
-}
-
-static int get_wireless_state(int *wlan, int *bluetooth)
-{
-       u8 result;
-
-       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
-
-       if (wlan) {
-               if ((result & KILLSWITCH_MASK) == 0)
-                       *wlan = 0;
-               else
-                       *wlan = result & WLAN_MASK;
-       }
-
-       if (bluetooth) {
-               if ((result & KILLSWITCH_MASK) == 0)
-                       *bluetooth = 0;
-               else
-                       *bluetooth = (result & BT_MASK) >> 1;
-       }
-
-       return 0;
-}
-
-/* Backlight device stuff */
-
-static int bl_get_brightness(struct backlight_device *b)
-{
-       return get_lcd_level();
-}
-
-
-static int bl_update_status(struct backlight_device *b)
-{
-       return set_lcd_level(b->props.brightness);
-}
-
-static struct backlight_ops compalbl_ops = {
-       .get_brightness = bl_get_brightness,
-       .update_status  = bl_update_status,
-};
-
-static struct backlight_device *compalbl_device;
-
-/* Platform device */
-
-static ssize_t show_wlan(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       int ret, enabled;
-
-       ret = get_wireless_state(&enabled, NULL);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t show_raw(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       u8 result;
-
-       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
-
-       return sprintf(buf, "%i\n", result);
-}
-
-static ssize_t show_bluetooth(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-       int ret, enabled;
-
-       ret = get_wireless_state(NULL, &enabled);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t store_wlan_state(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-       int state, ret;
-
-       if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
-               return -EINVAL;
-
-       ret = set_wlan_state(state);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t store_bluetooth_state(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-       int state, ret;
-
-       if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
-               return -EINVAL;
-
-       ret = set_bluetooth_state(state);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
-static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
-static DEVICE_ATTR(raw, 0444, show_raw, NULL);
-
-static struct attribute *compal_attributes[] = {
-       &dev_attr_bluetooth.attr,
-       &dev_attr_wlan.attr,
-       &dev_attr_raw.attr,
-       NULL
-};
-
-static struct attribute_group compal_attribute_group = {
-       .attrs = compal_attributes
-};
-
-static struct platform_driver compal_driver = {
-       .driver = {
-               .name = "compal-laptop",
-               .owner = THIS_MODULE,
-       }
-};
-
-static struct platform_device *compal_device;
-
-/* Initialization */
-
-static int dmi_check_cb(const struct dmi_system_id *id)
-{
-       printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
-               id->ident);
-
-       return 0;
-}
-
-static struct dmi_system_id __initdata compal_dmi_table[] = {
-       {
-               .ident = "FL90/IFL90",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "FL90/IFL90",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "FL91/IFL91",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "FL92/JFL92",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "FT00/IFT00",
-               .matches = {
-                       DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
-                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
-               },
-               .callback = dmi_check_cb
-       },
-       { }
-};
-
-static int __init compal_init(void)
-{
-       int ret;
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       if (!force && !dmi_check_system(compal_dmi_table))
-               return -ENODEV;
-
-       /* Register backlight stuff */
-
-       if (!acpi_video_backlight_support()) {
-               compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
-                                                           &compalbl_ops);
-               if (IS_ERR(compalbl_device))
-                       return PTR_ERR(compalbl_device);
-
-               compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
-       }
-
-       ret = platform_driver_register(&compal_driver);
-       if (ret)
-               goto fail_backlight;
-
-       /* Register platform stuff */
-
-       compal_device = platform_device_alloc("compal-laptop", -1);
-       if (!compal_device) {
-               ret = -ENOMEM;
-               goto fail_platform_driver;
-       }
-
-       ret = platform_device_add(compal_device);
-       if (ret)
-               goto fail_platform_device1;
-
-       ret = sysfs_create_group(&compal_device->dev.kobj,
-               &compal_attribute_group);
-       if (ret)
-               goto fail_platform_device2;
-
-       printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
-               " successfully loaded.\n");
-
-       return 0;
-
-fail_platform_device2:
-
-       platform_device_del(compal_device);
-
-fail_platform_device1:
-
-       platform_device_put(compal_device);
-
-fail_platform_driver:
-
-       platform_driver_unregister(&compal_driver);
-
-fail_backlight:
-
-       backlight_device_unregister(compalbl_device);
-
-       return ret;
-}
-
-static void __exit compal_cleanup(void)
-{
-
-       sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
-       platform_device_unregister(compal_device);
-       platform_driver_unregister(&compal_driver);
-       backlight_device_unregister(compalbl_device);
-
-       printk(KERN_INFO "compal-laptop: driver unloaded.\n");
-}
-
-module_init(compal_init);
-module_exit(compal_cleanup);
-
-MODULE_AUTHOR("Cezary Jackiewicz");
-MODULE_DESCRIPTION("Compal Laptop Support");
-MODULE_VERSION(COMPAL_DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
-MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c
deleted file mode 100644 (file)
index 02fe2b8..0000000
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
- *  eepc-laptop.c - Asus Eee PC extras
- *
- *  Based on asus_acpi.c as patched for the Eee PC by Asus:
- *  ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
- *  Based on eee.c from eeepc-linux
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <linux/uaccess.h>
-#include <linux/input.h>
-#include <linux/rfkill.h>
-
-#define EEEPC_LAPTOP_VERSION   "0.1"
-
-#define EEEPC_HOTK_NAME                "Eee PC Hotkey Driver"
-#define EEEPC_HOTK_FILE                "eeepc"
-#define EEEPC_HOTK_CLASS       "hotkey"
-#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
-#define EEEPC_HOTK_HID         "ASUS010"
-
-#define EEEPC_LOG      EEEPC_HOTK_FILE ": "
-#define EEEPC_ERR      KERN_ERR        EEEPC_LOG
-#define EEEPC_WARNING  KERN_WARNING    EEEPC_LOG
-#define EEEPC_NOTICE   KERN_NOTICE     EEEPC_LOG
-#define EEEPC_INFO     KERN_INFO       EEEPC_LOG
-
-/*
- * Definitions for Asus EeePC
- */
-#define        NOTIFY_WLAN_ON  0x10
-#define NOTIFY_BRN_MIN 0x20
-#define NOTIFY_BRN_MAX 0x2f
-
-enum {
-       DISABLE_ASL_WLAN = 0x0001,
-       DISABLE_ASL_BLUETOOTH = 0x0002,
-       DISABLE_ASL_IRDA = 0x0004,
-       DISABLE_ASL_CAMERA = 0x0008,
-       DISABLE_ASL_TV = 0x0010,
-       DISABLE_ASL_GPS = 0x0020,
-       DISABLE_ASL_DISPLAYSWITCH = 0x0040,
-       DISABLE_ASL_MODEM = 0x0080,
-       DISABLE_ASL_CARDREADER = 0x0100
-};
-
-enum {
-       CM_ASL_WLAN = 0,
-       CM_ASL_BLUETOOTH,
-       CM_ASL_IRDA,
-       CM_ASL_1394,
-       CM_ASL_CAMERA,
-       CM_ASL_TV,
-       CM_ASL_GPS,
-       CM_ASL_DVDROM,
-       CM_ASL_DISPLAYSWITCH,
-       CM_ASL_PANELBRIGHT,
-       CM_ASL_BIOSFLASH,
-       CM_ASL_ACPIFLASH,
-       CM_ASL_CPUFV,
-       CM_ASL_CPUTEMPERATURE,
-       CM_ASL_FANCPU,
-       CM_ASL_FANCHASSIS,
-       CM_ASL_USBPORT1,
-       CM_ASL_USBPORT2,
-       CM_ASL_USBPORT3,
-       CM_ASL_MODEM,
-       CM_ASL_CARDREADER,
-       CM_ASL_LID
-};
-
-static const char *cm_getv[] = {
-       "WLDG", NULL, NULL, NULL,
-       "CAMG", NULL, NULL, NULL,
-       NULL, "PBLG", NULL, NULL,
-       "CFVG", NULL, NULL, NULL,
-       "USBG", NULL, NULL, "MODG",
-       "CRDG", "LIDG"
-};
-
-static const char *cm_setv[] = {
-       "WLDS", NULL, NULL, NULL,
-       "CAMS", NULL, NULL, NULL,
-       "SDSP", "PBLS", "HDPS", NULL,
-       "CFVS", NULL, NULL, NULL,
-       "USBG", NULL, NULL, "MODS",
-       "CRDS", NULL
-};
-
-#define EEEPC_EC       "\\_SB.PCI0.SBRG.EC0."
-
-#define EEEPC_EC_FAN_PWM       EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
-#define EEEPC_EC_SC02          0x63
-#define EEEPC_EC_FAN_HRPM      EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
-#define EEEPC_EC_FAN_LRPM      EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
-#define EEEPC_EC_FAN_CTRL      EEEPC_EC "SFB3" /* Byte containing SF25  */
-#define EEEPC_EC_SFB3          0xD3
-
-/*
- * This is the main structure, we can use it to store useful information
- * about the hotk device
- */
-struct eeepc_hotk {
-       struct acpi_device *device;     /* the device we are in */
-       acpi_handle handle;             /* the handle of the hotk device */
-       u32 cm_supported;               /* the control methods supported
-                                          by this BIOS */
-       uint init_flag;                 /* Init flags */
-       u16 event_count[128];           /* count for each event */
-       struct input_dev *inputdev;
-       u16 *keycode_map;
-       struct rfkill *eeepc_wlan_rfkill;
-       struct rfkill *eeepc_bluetooth_rfkill;
-};
-
-/* The actual device the driver binds to */
-static struct eeepc_hotk *ehotk;
-
-/* Platform device/driver */
-static struct platform_driver platform_driver = {
-       .driver = {
-               .name = EEEPC_HOTK_FILE,
-               .owner = THIS_MODULE,
-       }
-};
-
-static struct platform_device *platform_device;
-
-struct key_entry {
-       char type;
-       u8 code;
-       u16 keycode;
-};
-
-enum { KE_KEY, KE_END };
-
-static struct key_entry eeepc_keymap[] = {
-       /* Sleep already handled via generic ACPI code */
-       {KE_KEY, 0x10, KEY_WLAN },
-       {KE_KEY, 0x12, KEY_PROG1 },
-       {KE_KEY, 0x13, KEY_MUTE },
-       {KE_KEY, 0x14, KEY_VOLUMEDOWN },
-       {KE_KEY, 0x15, KEY_VOLUMEUP },
-       {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
-       {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
-       {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
-       {KE_END, 0},
-};
-
-/*
- * The hotkey driver declaration
- */
-static int eeepc_hotk_add(struct acpi_device *device);
-static int eeepc_hotk_remove(struct acpi_device *device, int type);
-
-static const struct acpi_device_id eeepc_device_ids[] = {
-       {EEEPC_HOTK_HID, 0},
-       {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
-
-static struct acpi_driver eeepc_hotk_driver = {
-       .name = EEEPC_HOTK_NAME,
-       .class = EEEPC_HOTK_CLASS,
-       .ids = eeepc_device_ids,
-       .ops = {
-               .add = eeepc_hotk_add,
-               .remove = eeepc_hotk_remove,
-       },
-};
-
-/* The backlight device /sys/class/backlight */
-static struct backlight_device *eeepc_backlight_device;
-
-/* The hwmon device */
-static struct device *eeepc_hwmon_device;
-
-/*
- * The backlight class declaration
- */
-static int read_brightness(struct backlight_device *bd);
-static int update_bl_status(struct backlight_device *bd);
-static struct backlight_ops eeepcbl_ops = {
-       .get_brightness = read_brightness,
-       .update_status = update_bl_status,
-};
-
-MODULE_AUTHOR("Corentin Chary, Eric Cooper");
-MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
-MODULE_LICENSE("GPL");
-
-/*
- * ACPI Helpers
- */
-static int write_acpi_int(acpi_handle handle, const char *method, int val,
-                         struct acpi_buffer *output)
-{
-       struct acpi_object_list params;
-       union acpi_object in_obj;
-       acpi_status status;
-
-       params.count = 1;
-       params.pointer = &in_obj;
-       in_obj.type = ACPI_TYPE_INTEGER;
-       in_obj.integer.value = val;
-
-       status = acpi_evaluate_object(handle, (char *)method, &params, output);
-       return (status == AE_OK ? 0 : -1);
-}
-
-static int read_acpi_int(acpi_handle handle, const char *method, int *val)
-{
-       acpi_status status;
-       unsigned long long result;
-
-       status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
-       if (ACPI_FAILURE(status)) {
-               *val = -1;
-               return -1;
-       } else {
-               *val = result;
-               return 0;
-       }
-}
-
-static int set_acpi(int cm, int value)
-{
-       if (ehotk->cm_supported & (0x1 << cm)) {
-               const char *method = cm_setv[cm];
-               if (method == NULL)
-                       return -ENODEV;
-               if (write_acpi_int(ehotk->handle, method, value, NULL))
-                       printk(EEEPC_WARNING "Error writing %s\n", method);
-       }
-       return 0;
-}
-
-static int get_acpi(int cm)
-{
-       int value = -1;
-       if ((ehotk->cm_supported & (0x1 << cm))) {
-               const char *method = cm_getv[cm];
-               if (method == NULL)
-                       return -ENODEV;
-               if (read_acpi_int(ehotk->handle, method, &value))
-                       printk(EEEPC_WARNING "Error reading %s\n", method);
-       }
-       return value;
-}
-
-/*
- * Backlight
- */
-static int read_brightness(struct backlight_device *bd)
-{
-       return get_acpi(CM_ASL_PANELBRIGHT);
-}
-
-static int set_brightness(struct backlight_device *bd, int value)
-{
-       value = max(0, min(15, value));
-       return set_acpi(CM_ASL_PANELBRIGHT, value);
-}
-
-static int update_bl_status(struct backlight_device *bd)
-{
-       return set_brightness(bd, bd->props.brightness);
-}
-
-/*
- * Rfkill helpers
- */
-
-static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
-{
-       if (state == RFKILL_STATE_SOFT_BLOCKED)
-               return set_acpi(CM_ASL_WLAN, 0);
-       else
-               return set_acpi(CM_ASL_WLAN, 1);
-}
-
-static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
-{
-       if (get_acpi(CM_ASL_WLAN) == 1)
-               *state = RFKILL_STATE_UNBLOCKED;
-       else
-               *state = RFKILL_STATE_SOFT_BLOCKED;
-       return 0;
-}
-
-static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
-{
-       if (state == RFKILL_STATE_SOFT_BLOCKED)
-               return set_acpi(CM_ASL_BLUETOOTH, 0);
-       else
-               return set_acpi(CM_ASL_BLUETOOTH, 1);
-}
-
-static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
-{
-       if (get_acpi(CM_ASL_BLUETOOTH) == 1)
-               *state = RFKILL_STATE_UNBLOCKED;
-       else
-               *state = RFKILL_STATE_SOFT_BLOCKED;
-       return 0;
-}
-
-/*
- * Sys helpers
- */
-static int parse_arg(const char *buf, unsigned long count, int *val)
-{
-       if (!count)
-               return 0;
-       if (sscanf(buf, "%i", val) != 1)
-               return -EINVAL;
-       return count;
-}
-
-static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               set_acpi(cm, value);
-       return rv;
-}
-
-static ssize_t show_sys_acpi(int cm, char *buf)
-{
-       return sprintf(buf, "%d\n", get_acpi(cm));
-}
-
-#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm)                           \
-       static ssize_t show_##_name(struct device *dev,                 \
-                                   struct device_attribute *attr,      \
-                                   char *buf)                          \
-       {                                                               \
-               return show_sys_acpi(_cm, buf);                         \
-       }                                                               \
-       static ssize_t store_##_name(struct device *dev,                \
-                                    struct device_attribute *attr,     \
-                                    const char *buf, size_t count)     \
-       {                                                               \
-               return store_sys_acpi(_cm, buf, count);                 \
-       }                                                               \
-       static struct device_attribute dev_attr_##_name = {             \
-               .attr = {                                               \
-                       .name = __stringify(_name),                     \
-                       .mode = 0644 },                                 \
-               .show   = show_##_name,                                 \
-               .store  = store_##_name,                                \
-       }
-
-EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
-EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
-EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
-
-static struct attribute *platform_attributes[] = {
-       &dev_attr_camera.attr,
-       &dev_attr_cardr.attr,
-       &dev_attr_disp.attr,
-       NULL
-};
-
-static struct attribute_group platform_attribute_group = {
-       .attrs = platform_attributes
-};
-
-/*
- * Hotkey functions
- */
-static struct key_entry *eepc_get_entry_by_scancode(int code)
-{
-       struct key_entry *key;
-
-       for (key = eeepc_keymap; key->type != KE_END; key++)
-               if (code == key->code)
-                       return key;
-
-       return NULL;
-}
-
-static struct key_entry *eepc_get_entry_by_keycode(int code)
-{
-       struct key_entry *key;
-
-       for (key = eeepc_keymap; key->type != KE_END; key++)
-               if (code == key->keycode && key->type == KE_KEY)
-                       return key;
-
-       return NULL;
-}
-
-static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
-{
-       struct key_entry *key = eepc_get_entry_by_scancode(scancode);
-
-       if (key && key->type == KE_KEY) {
-               *keycode = key->keycode;
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
-{
-       struct key_entry *key;
-       int old_keycode;
-
-       if (keycode < 0 || keycode > KEY_MAX)
-               return -EINVAL;
-
-       key = eepc_get_entry_by_scancode(scancode);
-       if (key && key->type == KE_KEY) {
-               old_keycode = key->keycode;
-               key->keycode = keycode;
-               set_bit(keycode, dev->keybit);
-               if (!eepc_get_entry_by_keycode(old_keycode))
-                       clear_bit(old_keycode, dev->keybit);
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int eeepc_hotk_check(void)
-{
-       const struct key_entry *key;
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       int result;
-
-       result = acpi_bus_get_status(ehotk->device);
-       if (result)
-               return result;
-       if (ehotk->device->status.present) {
-               if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
-                                   &buffer)) {
-                       printk(EEEPC_ERR "Hotkey initialization failed\n");
-                       return -ENODEV;
-               } else {
-                       printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
-                              ehotk->init_flag);
-               }
-               /* get control methods supported */
-               if (read_acpi_int(ehotk->handle, "CMSG"
-                                  , &ehotk->cm_supported)) {
-                       printk(EEEPC_ERR
-                              "Get control methods supported failed\n");
-                       return -ENODEV;
-               } else {
-                       printk(EEEPC_INFO
-                              "Get control methods supported: 0x%x\n",
-                              ehotk->cm_supported);
-               }
-               ehotk->inputdev = input_allocate_device();
-               if (!ehotk->inputdev) {
-                       printk(EEEPC_INFO "Unable to allocate input device\n");
-                       return 0;
-               }
-               ehotk->inputdev->name = "Asus EeePC extra buttons";
-               ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
-               ehotk->inputdev->id.bustype = BUS_HOST;
-               ehotk->inputdev->getkeycode = eeepc_getkeycode;
-               ehotk->inputdev->setkeycode = eeepc_setkeycode;
-
-               for (key = eeepc_keymap; key->type != KE_END; key++) {
-                       switch (key->type) {
-                       case KE_KEY:
-                               set_bit(EV_KEY, ehotk->inputdev->evbit);
-                               set_bit(key->keycode, ehotk->inputdev->keybit);
-                               break;
-                       }
-               }
-               result = input_register_device(ehotk->inputdev);
-               if (result) {
-                       printk(EEEPC_INFO "Unable to register input device\n");
-                       input_free_device(ehotk->inputdev);
-                       return 0;
-               }
-       } else {
-               printk(EEEPC_ERR "Hotkey device not present, aborting\n");
-               return -EINVAL;
-       }
-       return 0;
-}
-
-static void notify_brn(void)
-{
-       struct backlight_device *bd = eeepc_backlight_device;
-       bd->props.brightness = read_brightness(bd);
-}
-
-static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
-{
-       static struct key_entry *key;
-       if (!ehotk)
-               return;
-       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
-               notify_brn();
-       acpi_bus_generate_proc_event(ehotk->device, event,
-                                    ehotk->event_count[event % 128]++);
-       if (ehotk->inputdev) {
-               key = eepc_get_entry_by_scancode(event);
-               if (key) {
-                       switch (key->type) {
-                       case KE_KEY:
-                               input_report_key(ehotk->inputdev, key->keycode,
-                                                1);
-                               input_sync(ehotk->inputdev);
-                               input_report_key(ehotk->inputdev, key->keycode,
-                                                0);
-                               input_sync(ehotk->inputdev);
-                               break;
-                       }
-               }
-       }
-}
-
-static int eeepc_hotk_add(struct acpi_device *device)
-{
-       acpi_status status = AE_OK;
-       int result;
-
-       if (!device)
-                return -EINVAL;
-       printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
-       ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
-       if (!ehotk)
-               return -ENOMEM;
-       ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
-       ehotk->handle = device->handle;
-       strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
-       strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
-       device->driver_data = ehotk;
-       ehotk->device = device;
-       result = eeepc_hotk_check();
-       if (result)
-               goto end;
-       status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
-                                            eeepc_hotk_notify, ehotk);
-       if (ACPI_FAILURE(status))
-               printk(EEEPC_ERR "Error installing notify handler\n");
-
-       if (get_acpi(CM_ASL_WLAN) != -1) {
-               ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
-                                                          RFKILL_TYPE_WLAN);
-
-               if (!ehotk->eeepc_wlan_rfkill)
-                       goto end;
-
-               ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
-               ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
-               ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
-               if (get_acpi(CM_ASL_WLAN) == 1)
-                       ehotk->eeepc_wlan_rfkill->state =
-                               RFKILL_STATE_UNBLOCKED;
-               else
-                       ehotk->eeepc_wlan_rfkill->state =
-                               RFKILL_STATE_SOFT_BLOCKED;
-               rfkill_register(ehotk->eeepc_wlan_rfkill);
-       }
-
-       if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
-               ehotk->eeepc_bluetooth_rfkill =
-                       rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
-
-               if (!ehotk->eeepc_bluetooth_rfkill)
-                       goto end;
-
-               ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
-               ehotk->eeepc_bluetooth_rfkill->toggle_radio =
-                       eeepc_bluetooth_rfkill_set;
-               ehotk->eeepc_bluetooth_rfkill->get_state =
-                       eeepc_bluetooth_rfkill_state;
-               if (get_acpi(CM_ASL_BLUETOOTH) == 1)
-                       ehotk->eeepc_bluetooth_rfkill->state =
-                               RFKILL_STATE_UNBLOCKED;
-               else
-                       ehotk->eeepc_bluetooth_rfkill->state =
-                               RFKILL_STATE_SOFT_BLOCKED;
-               rfkill_register(ehotk->eeepc_bluetooth_rfkill);
-       }
-
- end:
-       if (result) {
-               kfree(ehotk);
-               ehotk = NULL;
-       }
-       return result;
-}
-
-static int eeepc_hotk_remove(struct acpi_device *device, int type)
-{
-       acpi_status status = 0;
-
-       if (!device || !acpi_driver_data(device))
-                return -EINVAL;
-       status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
-                                           eeepc_hotk_notify);
-       if (ACPI_FAILURE(status))
-               printk(EEEPC_ERR "Error removing notify handler\n");
-       kfree(ehotk);
-       return 0;
-}
-
-/*
- * Hwmon
- */
-static int eeepc_get_fan_pwm(void)
-{
-       int value = 0;
-
-       read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
-       value = value * 255 / 100;
-       return (value);
-}
-
-static void eeepc_set_fan_pwm(int value)
-{
-       value = SENSORS_LIMIT(value, 0, 255);
-       value = value * 100 / 255;
-       ec_write(EEEPC_EC_SC02, value);
-}
-
-static int eeepc_get_fan_rpm(void)
-{
-       int high = 0;
-       int low = 0;
-
-       read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
-       read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
-       return (high << 8 | low);
-}
-
-static int eeepc_get_fan_ctrl(void)
-{
-       int value = 0;
-
-       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
-       return ((value & 0x02 ? 1 : 0));
-}
-
-static void eeepc_set_fan_ctrl(int manual)
-{
-       int value = 0;
-
-       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
-       if (manual)
-               value |= 0x02;
-       else
-               value &= ~0x02;
-       ec_write(EEEPC_EC_SFB3, value);
-}
-
-static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
-{
-       int rv, value;
-
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               set(value);
-       return rv;
-}
-
-static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
-{
-       return sprintf(buf, "%d\n", get());
-}
-
-#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get)             \
-       static ssize_t show_##_name(struct device *dev,                 \
-                                   struct device_attribute *attr,      \
-                                   char *buf)                          \
-       {                                                               \
-               return show_sys_hwmon(_set, buf);                       \
-       }                                                               \
-       static ssize_t store_##_name(struct device *dev,                \
-                                    struct device_attribute *attr,     \
-                                    const char *buf, size_t count)     \
-       {                                                               \
-               return store_sys_hwmon(_get, buf, count);               \
-       }                                                               \
-       static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
-
-EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
-EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
-                        eeepc_get_fan_pwm, eeepc_set_fan_pwm);
-EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
-                        eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
-
-static ssize_t
-show_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       return sprintf(buf, "eeepc\n");
-}
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
-
-static struct attribute *hwmon_attributes[] = {
-       &sensor_dev_attr_pwm1.dev_attr.attr,
-       &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
-       &sensor_dev_attr_name.dev_attr.attr,
-       NULL
-};
-
-static struct attribute_group hwmon_attribute_group = {
-       .attrs = hwmon_attributes
-};
-
-/*
- * exit/init
- */
-static void eeepc_backlight_exit(void)
-{
-       if (eeepc_backlight_device)
-               backlight_device_unregister(eeepc_backlight_device);
-       if (ehotk->inputdev)
-               input_unregister_device(ehotk->inputdev);
-       if (ehotk->eeepc_wlan_rfkill)
-               rfkill_unregister(ehotk->eeepc_wlan_rfkill);
-       if (ehotk->eeepc_bluetooth_rfkill)
-               rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
-       eeepc_backlight_device = NULL;
-}
-
-static void eeepc_hwmon_exit(void)
-{
-       struct device *hwmon;
-
-       hwmon = eeepc_hwmon_device;
-       if (!hwmon)
-               return ;
-       sysfs_remove_group(&hwmon->kobj,
-                          &hwmon_attribute_group);
-       hwmon_device_unregister(hwmon);
-       eeepc_hwmon_device = NULL;
-}
-
-static void __exit eeepc_laptop_exit(void)
-{
-       eeepc_backlight_exit();
-       eeepc_hwmon_exit();
-       acpi_bus_unregister_driver(&eeepc_hotk_driver);
-       sysfs_remove_group(&platform_device->dev.kobj,
-                          &platform_attribute_group);
-       platform_device_unregister(platform_device);
-       platform_driver_unregister(&platform_driver);
-}
-
-static int eeepc_backlight_init(struct device *dev)
-{
-       struct backlight_device *bd;
-
-       bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
-                                      NULL, &eeepcbl_ops);
-       if (IS_ERR(bd)) {
-               printk(EEEPC_ERR
-                      "Could not register eeepc backlight device\n");
-               eeepc_backlight_device = NULL;
-               return PTR_ERR(bd);
-       }
-       eeepc_backlight_device = bd;
-       bd->props.max_brightness = 15;
-       bd->props.brightness = read_brightness(NULL);
-       bd->props.power = FB_BLANK_UNBLANK;
-       backlight_update_status(bd);
-       return 0;
-}
-
-static int eeepc_hwmon_init(struct device *dev)
-{
-       struct device *hwmon;
-       int result;
-
-       hwmon = hwmon_device_register(dev);
-       if (IS_ERR(hwmon)) {
-               printk(EEEPC_ERR
-                      "Could not register eeepc hwmon device\n");
-               eeepc_hwmon_device = NULL;
-               return PTR_ERR(hwmon);
-       }
-       eeepc_hwmon_device = hwmon;
-       result = sysfs_create_group(&hwmon->kobj,
-                                   &hwmon_attribute_group);
-       if (result)
-               eeepc_hwmon_exit();
-       return result;
-}
-
-static int __init eeepc_laptop_init(void)
-{
-       struct device *dev;
-       int result;
-
-       if (acpi_disabled)
-               return -ENODEV;
-       result = acpi_bus_register_driver(&eeepc_hotk_driver);
-       if (result < 0)
-               return result;
-       if (!ehotk) {
-               acpi_bus_unregister_driver(&eeepc_hotk_driver);
-               return -ENODEV;
-       }
-       dev = acpi_get_physical_device(ehotk->device->handle);
-
-       if (!acpi_video_backlight_support()) {
-               result = eeepc_backlight_init(dev);
-               if (result)
-                       goto fail_backlight;
-       } else
-               printk(EEEPC_INFO "Backlight controlled by ACPI video "
-                      "driver\n");
-
-       result = eeepc_hwmon_init(dev);
-       if (result)
-               goto fail_hwmon;
-       /* Register platform stuff */
-       result = platform_driver_register(&platform_driver);
-       if (result)
-               goto fail_platform_driver;
-       platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
-       if (!platform_device) {
-               result = -ENOMEM;
-               goto fail_platform_device1;
-       }
-       result = platform_device_add(platform_device);
-       if (result)
-               goto fail_platform_device2;
-       result = sysfs_create_group(&platform_device->dev.kobj,
-                                   &platform_attribute_group);
-       if (result)
-               goto fail_sysfs;
-       return 0;
-fail_sysfs:
-       platform_device_del(platform_device);
-fail_platform_device2:
-       platform_device_put(platform_device);
-fail_platform_device1:
-       platform_driver_unregister(&platform_driver);
-fail_platform_driver:
-       eeepc_hwmon_exit();
-fail_hwmon:
-       eeepc_backlight_exit();
-fail_backlight:
-       return result;
-}
-
-module_init(eeepc_laptop_init);
-module_exit(eeepc_laptop_exit);
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c
deleted file mode 100644 (file)
index a7dd3e9..0000000
+++ /dev/null
@@ -1,1126 +0,0 @@
-/*-*-linux-c-*-*/
-
-/*
-  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
-  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
-  Based on earlier work:
-    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
-    Adrian Yee <brewt-fujitsu@brewt.org>
-
-  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
-  by its respective authors.
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-  02110-1301, USA.
- */
-
-/*
- * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
- * features made available on a range of Fujitsu laptops including the
- * P2xxx/P5xxx/S6xxx/S7xxx series.
- *
- * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
- * others may be added at a later date.
- *
- *   lcd_level - Screen brightness: contains a single integer in the
- *   range 0..7. (rw)
- *
- * In addition to these platform device attributes the driver
- * registers itself in the Linux backlight control subsystem and is
- * available to userspace under /sys/class/backlight/fujitsu-laptop/.
- *
- * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
- * also supported by this driver.
- *
- * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
- * P8010.  It should work on most P-series and S-series Lifebooks, but
- * YMMV.
- *
- * The module parameter use_alt_lcd_levels switches between different ACPI
- * brightness controls which are used by different Fujitsu laptops.  In most
- * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
- * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/acpi.h>
-#include <linux/dmi.h>
-#include <linux/backlight.h>
-#include <linux/input.h>
-#include <linux/kfifo.h>
-#include <linux/video_output.h>
-#include <linux/platform_device.h>
-
-#define FUJITSU_DRIVER_VERSION "0.4.3"
-
-#define FUJITSU_LCD_N_LEVELS 8
-
-#define ACPI_FUJITSU_CLASS              "fujitsu"
-#define ACPI_FUJITSU_HID                "FUJ02B1"
-#define ACPI_FUJITSU_DRIVER_NAME       "Fujitsu laptop FUJ02B1 ACPI brightness driver"
-#define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
-#define ACPI_FUJITSU_HOTKEY_HID        "FUJ02E3"
-#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
-#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
-
-#define ACPI_FUJITSU_NOTIFY_CODE1     0x80
-
-#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
-#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87
-
-/* Hotkey details */
-#define KEY1_CODE      0x410   /* codes for the keys in the GIRB register */
-#define KEY2_CODE      0x411
-#define KEY3_CODE      0x412
-#define KEY4_CODE      0x413
-
-#define MAX_HOTKEY_RINGBUFFER_SIZE 100
-#define RINGBUFFERSIZE 40
-
-/* Debugging */
-#define FUJLAPTOP_LOG     ACPI_FUJITSU_HID ": "
-#define FUJLAPTOP_ERR     KERN_ERR FUJLAPTOP_LOG
-#define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
-#define FUJLAPTOP_INFO    KERN_INFO FUJLAPTOP_LOG
-#define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG
-
-#define FUJLAPTOP_DBG_ALL        0xffff
-#define FUJLAPTOP_DBG_ERROR      0x0001
-#define FUJLAPTOP_DBG_WARN       0x0002
-#define FUJLAPTOP_DBG_INFO       0x0004
-#define FUJLAPTOP_DBG_TRACE      0x0008
-
-#define dbg_printk(a_dbg_level, format, arg...) \
-       do { if (dbg_level & a_dbg_level) \
-               printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
-       } while (0)
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-#define vdbg_printk(a_dbg_level, format, arg...) \
-       dbg_printk(a_dbg_level, format, ## arg)
-#else
-#define vdbg_printk(a_dbg_level, format, arg...)
-#endif
-
-/* Device controlling the backlight and associated keys */
-struct fujitsu_t {
-       acpi_handle acpi_handle;
-       struct acpi_device *dev;
-       struct input_dev *input;
-       char phys[32];
-       struct backlight_device *bl_device;
-       struct platform_device *pf_device;
-       int keycode1, keycode2, keycode3, keycode4;
-
-       unsigned int max_brightness;
-       unsigned int brightness_changed;
-       unsigned int brightness_level;
-};
-
-static struct fujitsu_t *fujitsu;
-static int use_alt_lcd_levels = -1;
-static int disable_brightness_keys = -1;
-static int disable_brightness_adjust = -1;
-
-/* Device used to access other hotkeys on the laptop */
-struct fujitsu_hotkey_t {
-       acpi_handle acpi_handle;
-       struct acpi_device *dev;
-       struct input_dev *input;
-       char phys[32];
-       struct platform_device *pf_device;
-       struct kfifo *fifo;
-       spinlock_t fifo_lock;
-
-       unsigned int irb;       /* info about the pressed buttons */
-};
-
-static struct fujitsu_hotkey_t *fujitsu_hotkey;
-
-static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
-                                      void *data);
-
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-static u32 dbg_level = 0x03;
-#endif
-
-static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
-
-/* Hardware access for LCD brightness control */
-
-static int set_lcd_level(int level)
-{
-       acpi_status status = AE_OK;
-       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list arg_list = { 1, &arg0 };
-       acpi_handle handle = NULL;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
-                   level);
-
-       if (level < 0 || level >= fujitsu->max_brightness)
-               return -EINVAL;
-
-       if (!fujitsu)
-               return -EINVAL;
-
-       status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
-       if (ACPI_FAILURE(status)) {
-               vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
-               return -ENODEV;
-       }
-
-       arg0.integer.value = level;
-
-       status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return 0;
-}
-
-static int set_lcd_level_alt(int level)
-{
-       acpi_status status = AE_OK;
-       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list arg_list = { 1, &arg0 };
-       acpi_handle handle = NULL;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
-                   level);
-
-       if (level < 0 || level >= fujitsu->max_brightness)
-               return -EINVAL;
-
-       if (!fujitsu)
-               return -EINVAL;
-
-       status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
-       if (ACPI_FAILURE(status)) {
-               vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
-               return -ENODEV;
-       }
-
-       arg0.integer.value = level;
-
-       status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return 0;
-}
-
-static int get_lcd_level(void)
-{
-       unsigned long long state = 0;
-       acpi_status status = AE_OK;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
-
-       status =
-           acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
-       if (status < 0)
-               return status;
-
-       fujitsu->brightness_level = state & 0x0fffffff;
-
-       if (state & 0x80000000)
-               fujitsu->brightness_changed = 1;
-       else
-               fujitsu->brightness_changed = 0;
-
-       return fujitsu->brightness_level;
-}
-
-static int get_max_brightness(void)
-{
-       unsigned long long state = 0;
-       acpi_status status = AE_OK;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
-
-       status =
-           acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
-       if (status < 0)
-               return status;
-
-       fujitsu->max_brightness = state;
-
-       return fujitsu->max_brightness;
-}
-
-static int get_lcd_level_alt(void)
-{
-       unsigned long long state = 0;
-       acpi_status status = AE_OK;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n");
-
-       status =
-           acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state);
-       if (status < 0)
-               return status;
-
-       fujitsu->brightness_level = state & 0x0fffffff;
-
-       if (state & 0x80000000)
-               fujitsu->brightness_changed = 1;
-       else
-               fujitsu->brightness_changed = 0;
-
-       return fujitsu->brightness_level;
-}
-
-/* Backlight device stuff */
-
-static int bl_get_brightness(struct backlight_device *b)
-{
-       if (use_alt_lcd_levels)
-               return get_lcd_level_alt();
-       else
-               return get_lcd_level();
-}
-
-static int bl_update_status(struct backlight_device *b)
-{
-       if (use_alt_lcd_levels)
-               return set_lcd_level_alt(b->props.brightness);
-       else
-               return set_lcd_level(b->props.brightness);
-}
-
-static struct backlight_ops fujitsubl_ops = {
-       .get_brightness = bl_get_brightness,
-       .update_status = bl_update_status,
-};
-
-/* Platform LCD brightness device */
-
-static ssize_t
-show_max_brightness(struct device *dev,
-                   struct device_attribute *attr, char *buf)
-{
-
-       int ret;
-
-       ret = get_max_brightness();
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t
-show_brightness_changed(struct device *dev,
-                       struct device_attribute *attr, char *buf)
-{
-
-       int ret;
-
-       ret = fujitsu->brightness_changed;
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t show_lcd_level(struct device *dev,
-                             struct device_attribute *attr, char *buf)
-{
-
-       int ret;
-
-       if (use_alt_lcd_levels)
-               ret = get_lcd_level_alt();
-       else
-               ret = get_lcd_level();
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t store_lcd_level(struct device *dev,
-                              struct device_attribute *attr, const char *buf,
-                              size_t count)
-{
-
-       int level, ret;
-
-       if (sscanf(buf, "%i", &level) != 1
-           || (level < 0 || level >= fujitsu->max_brightness))
-               return -EINVAL;
-
-       if (use_alt_lcd_levels)
-               ret = set_lcd_level_alt(level);
-       else
-               ret = set_lcd_level(level);
-       if (ret < 0)
-               return ret;
-
-       if (use_alt_lcd_levels)
-               ret = get_lcd_level_alt();
-       else
-               ret = get_lcd_level();
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-/* Hardware access for hotkey device */
-
-static int get_irb(void)
-{
-       unsigned long long state = 0;
-       acpi_status status = AE_OK;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n");
-
-       status =
-           acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL,
-                                 &state);
-       if (status < 0)
-               return status;
-
-       fujitsu_hotkey->irb = state;
-
-       return fujitsu_hotkey->irb;
-}
-
-static ssize_t
-ignore_store(struct device *dev,
-            struct device_attribute *attr, const char *buf, size_t count)
-{
-       return count;
-}
-
-static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
-static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
-                  ignore_store);
-static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
-
-static struct attribute *fujitsupf_attributes[] = {
-       &dev_attr_brightness_changed.attr,
-       &dev_attr_max_brightness.attr,
-       &dev_attr_lcd_level.attr,
-       NULL
-};
-
-static struct attribute_group fujitsupf_attribute_group = {
-       .attrs = fujitsupf_attributes
-};
-
-static struct platform_driver fujitsupf_driver = {
-       .driver = {
-                  .name = "fujitsu-laptop",
-                  .owner = THIS_MODULE,
-                  }
-};
-
-static void dmi_check_cb_common(const struct dmi_system_id *id)
-{
-       acpi_handle handle;
-       int have_blnf;
-       printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n",
-              id->ident);
-       have_blnf = ACPI_SUCCESS
-           (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle));
-       if (use_alt_lcd_levels == -1) {
-               vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n");
-               use_alt_lcd_levels = 1;
-       }
-       if (disable_brightness_keys == -1) {
-               vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                           "auto-detecting disable_keys\n");
-               disable_brightness_keys = have_blnf ? 1 : 0;
-       }
-       if (disable_brightness_adjust == -1) {
-               vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                           "auto-detecting disable_adjust\n");
-               disable_brightness_adjust = have_blnf ? 0 : 1;
-       }
-}
-
-static int dmi_check_cb_s6410(const struct dmi_system_id *id)
-{
-       dmi_check_cb_common(id);
-       fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
-       fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
-       return 0;
-}
-
-static int dmi_check_cb_s6420(const struct dmi_system_id *id)
-{
-       dmi_check_cb_common(id);
-       fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
-       fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
-       return 0;
-}
-
-static int dmi_check_cb_p8010(const struct dmi_system_id *id)
-{
-       dmi_check_cb_common(id);
-       fujitsu->keycode1 = KEY_HELP;   /* "Support" */
-       fujitsu->keycode3 = KEY_SWITCHVIDEOMODE;        /* "Presentation" */
-       fujitsu->keycode4 = KEY_WWW;    /* "Internet" */
-       return 0;
-}
-
-static struct dmi_system_id fujitsu_dmi_table[] = {
-       {
-        .ident = "Fujitsu Siemens S6410",
-        .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
-                    DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
-                    },
-        .callback = dmi_check_cb_s6410},
-       {
-        .ident = "Fujitsu Siemens S6420",
-        .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
-                    DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
-                    },
-        .callback = dmi_check_cb_s6420},
-       {
-        .ident = "Fujitsu LifeBook P8010",
-        .matches = {
-                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
-                    DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
-                    },
-        .callback = dmi_check_cb_p8010},
-       {}
-};
-
-/* ACPI device for LCD brightness control */
-
-static int acpi_fujitsu_add(struct acpi_device *device)
-{
-       acpi_status status;
-       acpi_handle handle;
-       int result = 0;
-       int state = 0;
-       struct input_dev *input;
-       int error;
-
-       if (!device)
-               return -EINVAL;
-
-       fujitsu->acpi_handle = device->handle;
-       sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
-       sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
-       device->driver_data = fujitsu;
-
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_DEVICE_NOTIFY,
-                                            acpi_fujitsu_notify, fujitsu);
-
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR "Error installing notify handler\n");
-               error = -ENODEV;
-               goto err_stop;
-       }
-
-       fujitsu->input = input = input_allocate_device();
-       if (!input) {
-               error = -ENOMEM;
-               goto err_uninstall_notify;
-       }
-
-       snprintf(fujitsu->phys, sizeof(fujitsu->phys),
-                "%s/video/input0", acpi_device_hid(device));
-
-       input->name = acpi_device_name(device);
-       input->phys = fujitsu->phys;
-       input->id.bustype = BUS_HOST;
-       input->id.product = 0x06;
-       input->dev.parent = &device->dev;
-       input->evbit[0] = BIT(EV_KEY);
-       set_bit(KEY_BRIGHTNESSUP, input->keybit);
-       set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
-       set_bit(KEY_UNKNOWN, input->keybit);
-
-       error = input_register_device(input);
-       if (error)
-               goto err_free_input_dev;
-
-       result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
-       if (result) {
-               printk(KERN_ERR "Error reading power state\n");
-               goto end;
-       }
-
-       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
-              acpi_device_name(device), acpi_device_bid(device),
-              !device->power.state ? "on" : "off");
-
-       fujitsu->dev = device;
-
-       if (ACPI_SUCCESS
-           (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
-               vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
-               if (ACPI_FAILURE
-                   (acpi_evaluate_object
-                    (device->handle, METHOD_NAME__INI, NULL, NULL)))
-                       printk(KERN_ERR "_INI Method failed\n");
-       }
-
-       /* do config (detect defaults) */
-       use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
-       disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0;
-       disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
-       vdbg_printk(FUJLAPTOP_DBG_INFO,
-                   "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n",
-                   use_alt_lcd_levels, disable_brightness_keys,
-                   disable_brightness_adjust);
-
-       if (get_max_brightness() <= 0)
-               fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
-       if (use_alt_lcd_levels)
-               get_lcd_level_alt();
-       else
-               get_lcd_level();
-
-       return result;
-
-end:
-err_free_input_dev:
-       input_free_device(input);
-err_uninstall_notify:
-       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
-                                  acpi_fujitsu_notify);
-err_stop:
-
-       return result;
-}
-
-static int acpi_fujitsu_remove(struct acpi_device *device, int type)
-{
-       acpi_status status;
-       struct fujitsu_t *fujitsu = NULL;
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       fujitsu = acpi_driver_data(device);
-
-       status = acpi_remove_notify_handler(fujitsu->acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           acpi_fujitsu_notify);
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       fujitsu->acpi_handle = NULL;
-
-       return 0;
-}
-
-/* Brightness notify */
-
-static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
-{
-       struct input_dev *input;
-       int keycode;
-       int oldb, newb;
-
-       input = fujitsu->input;
-
-       switch (event) {
-       case ACPI_FUJITSU_NOTIFY_CODE1:
-               keycode = 0;
-               oldb = fujitsu->brightness_level;
-               get_lcd_level();  /* the alt version always yields changed */
-               newb = fujitsu->brightness_level;
-
-               vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                           "brightness button event [%i -> %i (%i)]\n",
-                           oldb, newb, fujitsu->brightness_changed);
-
-               if (oldb == newb && fujitsu->brightness_changed) {
-                       keycode = 0;
-                       if (disable_brightness_keys != 1) {
-                               if (oldb == 0) {
-                                       acpi_bus_generate_proc_event
-                                           (fujitsu->dev,
-                                            ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
-                                            0);
-                                       keycode = KEY_BRIGHTNESSDOWN;
-                               } else if (oldb ==
-                                          (fujitsu->max_brightness) - 1) {
-                                       acpi_bus_generate_proc_event
-                                           (fujitsu->dev,
-                                            ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
-                                            0);
-                                       keycode = KEY_BRIGHTNESSUP;
-                               }
-                       }
-               } else if (oldb < newb) {
-                       if (disable_brightness_adjust != 1) {
-                               if (use_alt_lcd_levels)
-                                       set_lcd_level_alt(newb);
-                               else
-                                       set_lcd_level(newb);
-                       }
-                       if (disable_brightness_keys != 1) {
-                               acpi_bus_generate_proc_event(fujitsu->dev,
-                                       ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
-                               keycode = KEY_BRIGHTNESSUP;
-                       }
-               } else if (oldb > newb) {
-                       if (disable_brightness_adjust != 1) {
-                               if (use_alt_lcd_levels)
-                                       set_lcd_level_alt(newb);
-                               else
-                                       set_lcd_level(newb);
-                       }
-                       if (disable_brightness_keys != 1) {
-                               acpi_bus_generate_proc_event(fujitsu->dev,
-                                       ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
-                               keycode = KEY_BRIGHTNESSDOWN;
-                       }
-               } else {
-                       keycode = KEY_UNKNOWN;
-               }
-               break;
-       default:
-               keycode = KEY_UNKNOWN;
-               vdbg_printk(FUJLAPTOP_DBG_WARN,
-                           "unsupported event [0x%x]\n", event);
-               break;
-       }
-
-       if (keycode != 0) {
-               input_report_key(input, keycode, 1);
-               input_sync(input);
-               input_report_key(input, keycode, 0);
-               input_sync(input);
-       }
-
-       return;
-}
-
-/* ACPI device for hotkey handling */
-
-static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
-{
-       acpi_status status;
-       acpi_handle handle;
-       int result = 0;
-       int state = 0;
-       struct input_dev *input;
-       int error;
-       int i;
-
-       if (!device)
-               return -EINVAL;
-
-       fujitsu_hotkey->acpi_handle = device->handle;
-       sprintf(acpi_device_name(device), "%s",
-               ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
-       sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
-       device->driver_data = fujitsu_hotkey;
-
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_DEVICE_NOTIFY,
-                                            acpi_fujitsu_hotkey_notify,
-                                            fujitsu_hotkey);
-
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR "Error installing notify handler\n");
-               error = -ENODEV;
-               goto err_stop;
-       }
-
-       /* kfifo */
-       spin_lock_init(&fujitsu_hotkey->fifo_lock);
-       fujitsu_hotkey->fifo =
-           kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
-                       &fujitsu_hotkey->fifo_lock);
-       if (IS_ERR(fujitsu_hotkey->fifo)) {
-               printk(KERN_ERR "kfifo_alloc failed\n");
-               error = PTR_ERR(fujitsu_hotkey->fifo);
-               goto err_stop;
-       }
-
-       fujitsu_hotkey->input = input = input_allocate_device();
-       if (!input) {
-               error = -ENOMEM;
-               goto err_uninstall_notify;
-       }
-
-       snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
-                "%s/video/input0", acpi_device_hid(device));
-
-       input->name = acpi_device_name(device);
-       input->phys = fujitsu_hotkey->phys;
-       input->id.bustype = BUS_HOST;
-       input->id.product = 0x06;
-       input->dev.parent = &device->dev;
-       input->evbit[0] = BIT(EV_KEY);
-       set_bit(fujitsu->keycode1, input->keybit);
-       set_bit(fujitsu->keycode2, input->keybit);
-       set_bit(fujitsu->keycode3, input->keybit);
-       set_bit(fujitsu->keycode4, input->keybit);
-       set_bit(KEY_UNKNOWN, input->keybit);
-
-       error = input_register_device(input);
-       if (error)
-               goto err_free_input_dev;
-
-       result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
-       if (result) {
-               printk(KERN_ERR "Error reading power state\n");
-               goto end;
-       }
-
-       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
-              acpi_device_name(device), acpi_device_bid(device),
-              !device->power.state ? "on" : "off");
-
-       fujitsu_hotkey->dev = device;
-
-       if (ACPI_SUCCESS
-           (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
-               vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
-               if (ACPI_FAILURE
-                   (acpi_evaluate_object
-                    (device->handle, METHOD_NAME__INI, NULL, NULL)))
-                       printk(KERN_ERR "_INI Method failed\n");
-       }
-
-       i = 0;                  /* Discard hotkey ringbuffer */
-       while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
-       vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
-
-       return result;
-
-end:
-err_free_input_dev:
-       input_free_device(input);
-err_uninstall_notify:
-       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
-                                  acpi_fujitsu_hotkey_notify);
-       kfifo_free(fujitsu_hotkey->fifo);
-err_stop:
-
-       return result;
-}
-
-static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
-{
-       acpi_status status;
-       struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
-
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
-       fujitsu_hotkey = acpi_driver_data(device);
-
-       status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           acpi_fujitsu_hotkey_notify);
-
-       fujitsu_hotkey->acpi_handle = NULL;
-
-       kfifo_free(fujitsu_hotkey->fifo);
-
-       return 0;
-}
-
-static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
-                                      void *data)
-{
-       struct input_dev *input;
-       int keycode, keycode_r;
-       unsigned int irb = 1;
-       int i, status;
-
-       input = fujitsu_hotkey->input;
-
-       vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
-
-       switch (event) {
-       case ACPI_FUJITSU_NOTIFY_CODE1:
-               i = 0;
-               while ((irb = get_irb()) != 0
-                      && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
-                       vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n",
-                                   irb);
-
-                       switch (irb & 0x4ff) {
-                       case KEY1_CODE:
-                               keycode = fujitsu->keycode1;
-                               break;
-                       case KEY2_CODE:
-                               keycode = fujitsu->keycode2;
-                               break;
-                       case KEY3_CODE:
-                               keycode = fujitsu->keycode3;
-                               break;
-                       case KEY4_CODE:
-                               keycode = fujitsu->keycode4;
-                               break;
-                       case 0:
-                               keycode = 0;
-                               break;
-                       default:
-                               vdbg_printk(FUJLAPTOP_DBG_WARN,
-                                           "Unknown GIRB result [%x]\n", irb);
-                               keycode = -1;
-                               break;
-                       }
-                       if (keycode > 0) {
-                               vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                                       "Push keycode into ringbuffer [%d]\n",
-                                       keycode);
-                               status = kfifo_put(fujitsu_hotkey->fifo,
-                                                  (unsigned char *)&keycode,
-                                                  sizeof(keycode));
-                               if (status != sizeof(keycode)) {
-                                       vdbg_printk(FUJLAPTOP_DBG_WARN,
-                                           "Could not push keycode [0x%x]\n",
-                                           keycode);
-                               } else {
-                                       input_report_key(input, keycode, 1);
-                                       input_sync(input);
-                               }
-                       } else if (keycode == 0) {
-                               while ((status =
-                                       kfifo_get
-                                       (fujitsu_hotkey->fifo, (unsigned char *)
-                                        &keycode_r,
-                                        sizeof
-                                        (keycode_r))) == sizeof(keycode_r)) {
-                                       input_report_key(input, keycode_r, 0);
-                                       input_sync(input);
-                                       vdbg_printk(FUJLAPTOP_DBG_TRACE,
-                                         "Pop keycode from ringbuffer [%d]\n",
-                                         keycode_r);
-                               }
-                       }
-               }
-
-               break;
-       default:
-               keycode = KEY_UNKNOWN;
-               vdbg_printk(FUJLAPTOP_DBG_WARN,
-                           "Unsupported event [0x%x]\n", event);
-               input_report_key(input, keycode, 1);
-               input_sync(input);
-               input_report_key(input, keycode, 0);
-               input_sync(input);
-               break;
-       }
-
-       return;
-}
-
-/* Initialization */
-
-static const struct acpi_device_id fujitsu_device_ids[] = {
-       {ACPI_FUJITSU_HID, 0},
-       {"", 0},
-};
-
-static struct acpi_driver acpi_fujitsu_driver = {
-       .name = ACPI_FUJITSU_DRIVER_NAME,
-       .class = ACPI_FUJITSU_CLASS,
-       .ids = fujitsu_device_ids,
-       .ops = {
-               .add = acpi_fujitsu_add,
-               .remove = acpi_fujitsu_remove,
-               },
-};
-
-static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
-       {ACPI_FUJITSU_HOTKEY_HID, 0},
-       {"", 0},
-};
-
-static struct acpi_driver acpi_fujitsu_hotkey_driver = {
-       .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
-       .class = ACPI_FUJITSU_CLASS,
-       .ids = fujitsu_hotkey_device_ids,
-       .ops = {
-               .add = acpi_fujitsu_hotkey_add,
-               .remove = acpi_fujitsu_hotkey_remove,
-               },
-};
-
-static int __init fujitsu_init(void)
-{
-       int ret, result, max_brightness;
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
-       if (!fujitsu)
-               return -ENOMEM;
-       memset(fujitsu, 0, sizeof(struct fujitsu_t));
-       fujitsu->keycode1 = KEY_PROG1;
-       fujitsu->keycode2 = KEY_PROG2;
-       fujitsu->keycode3 = KEY_PROG3;
-       fujitsu->keycode4 = KEY_PROG4;
-       dmi_check_system(fujitsu_dmi_table);
-
-       result = acpi_bus_register_driver(&acpi_fujitsu_driver);
-       if (result < 0) {
-               ret = -ENODEV;
-               goto fail_acpi;
-       }
-
-       /* Register platform stuff */
-
-       fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
-       if (!fujitsu->pf_device) {
-               ret = -ENOMEM;
-               goto fail_platform_driver;
-       }
-
-       ret = platform_device_add(fujitsu->pf_device);
-       if (ret)
-               goto fail_platform_device1;
-
-       ret =
-           sysfs_create_group(&fujitsu->pf_device->dev.kobj,
-                              &fujitsupf_attribute_group);
-       if (ret)
-               goto fail_platform_device2;
-
-       /* Register backlight stuff */
-
-       if (!acpi_video_backlight_support()) {
-               fujitsu->bl_device =
-                       backlight_device_register("fujitsu-laptop", NULL, NULL,
-                                                 &fujitsubl_ops);
-               if (IS_ERR(fujitsu->bl_device))
-                       return PTR_ERR(fujitsu->bl_device);
-               max_brightness = fujitsu->max_brightness;
-               fujitsu->bl_device->props.max_brightness = max_brightness - 1;
-               fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
-       }
-
-       ret = platform_driver_register(&fujitsupf_driver);
-       if (ret)
-               goto fail_backlight;
-
-       /* Register hotkey driver */
-
-       fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
-       if (!fujitsu_hotkey) {
-               ret = -ENOMEM;
-               goto fail_hotkey;
-       }
-       memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
-
-       result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
-       if (result < 0) {
-               ret = -ENODEV;
-               goto fail_hotkey1;
-       }
-
-       printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
-              " successfully loaded.\n");
-
-       return 0;
-
-fail_hotkey1:
-
-       kfree(fujitsu_hotkey);
-
-fail_hotkey:
-
-       platform_driver_unregister(&fujitsupf_driver);
-
-fail_backlight:
-
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
-
-fail_platform_device2:
-
-       platform_device_del(fujitsu->pf_device);
-
-fail_platform_device1:
-
-       platform_device_put(fujitsu->pf_device);
-
-fail_platform_driver:
-
-       acpi_bus_unregister_driver(&acpi_fujitsu_driver);
-
-fail_acpi:
-
-       kfree(fujitsu);
-
-       return ret;
-}
-
-static void __exit fujitsu_cleanup(void)
-{
-       sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
-                          &fujitsupf_attribute_group);
-       platform_device_unregister(fujitsu->pf_device);
-       platform_driver_unregister(&fujitsupf_driver);
-       if (fujitsu->bl_device)
-               backlight_device_unregister(fujitsu->bl_device);
-
-       acpi_bus_unregister_driver(&acpi_fujitsu_driver);
-
-       kfree(fujitsu);
-
-       acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
-
-       kfree(fujitsu_hotkey);
-
-       printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
-}
-
-module_init(fujitsu_init);
-module_exit(fujitsu_cleanup);
-
-module_param(use_alt_lcd_levels, uint, 0644);
-MODULE_PARM_DESC(use_alt_lcd_levels,
-                "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
-module_param(disable_brightness_keys, uint, 0644);
-MODULE_PARM_DESC(disable_brightness_keys,
-                "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
-module_param(disable_brightness_adjust, uint, 0644);
-MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-module_param_named(debug, dbg_level, uint, 0644);
-MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
-#endif
-
-MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
-MODULE_DESCRIPTION("Fujitsu laptop extras support");
-MODULE_VERSION(FUJITSU_DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
-MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
-
-static struct pnp_device_id pnp_ids[] = {
-       {.id = "FUJ02bf"},
-       {.id = "FUJ02B1"},
-       {.id = "FUJ02E3"},
-       {.id = ""}
-};
-
-MODULE_DEVICE_TABLE(pnp, pnp_ids);
diff --git a/drivers/misc/hp-wmi.c b/drivers/misc/hp-wmi.c
deleted file mode 100644 (file)
index 4b7c24c..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * HP WMI hotkeys
- *
- * Copyright (C) 2008 Red Hat <mjg@redhat.com>
- *
- * Portions based on wistron_btns.c:
- * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
- * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
- * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/input.h>
-#include <acpi/acpi_drivers.h>
-#include <linux/platform_device.h>
-#include <linux/acpi.h>
-#include <linux/rfkill.h>
-#include <linux/string.h>
-
-MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
-MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
-MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
-
-#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
-#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
-
-#define HPWMI_DISPLAY_QUERY 0x1
-#define HPWMI_HDDTEMP_QUERY 0x2
-#define HPWMI_ALS_QUERY 0x3
-#define HPWMI_DOCK_QUERY 0x4
-#define HPWMI_WIRELESS_QUERY 0x5
-#define HPWMI_HOTKEY_QUERY 0xc
-
-static int __init hp_wmi_bios_setup(struct platform_device *device);
-static int __exit hp_wmi_bios_remove(struct platform_device *device);
-
-struct bios_args {
-       u32 signature;
-       u32 command;
-       u32 commandtype;
-       u32 datasize;
-       u32 data;
-};
-
-struct bios_return {
-       u32 sigpass;
-       u32 return_code;
-       u32 value;
-};
-
-struct key_entry {
-       char type;              /* See KE_* below */
-       u16 code;
-       u16 keycode;
-};
-
-enum { KE_KEY, KE_SW, KE_END };
-
-static struct key_entry hp_wmi_keymap[] = {
-       {KE_SW, 0x01, SW_DOCK},
-       {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
-       {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
-       {KE_KEY, 0x20e6, KEY_PROG1},
-       {KE_KEY, 0x2142, KEY_MEDIA},
-       {KE_KEY, 0x213b, KEY_INFO},
-       {KE_KEY, 0x231b, KEY_HELP},
-       {KE_END, 0}
-};
-
-static struct input_dev *hp_wmi_input_dev;
-static struct platform_device *hp_wmi_platform_dev;
-
-static struct rfkill *wifi_rfkill;
-static struct rfkill *bluetooth_rfkill;
-static struct rfkill *wwan_rfkill;
-
-static struct platform_driver hp_wmi_driver = {
-       .driver = {
-                  .name = "hp-wmi",
-                  .owner = THIS_MODULE,
-       },
-       .probe = hp_wmi_bios_setup,
-       .remove = hp_wmi_bios_remove,
-};
-
-static int hp_wmi_perform_query(int query, int write, int value)
-{
-       struct bios_return bios_return;
-       acpi_status status;
-       union acpi_object *obj;
-       struct bios_args args = {
-               .signature = 0x55434553,
-               .command = write ? 0x2 : 0x1,
-               .commandtype = query,
-               .datasize = write ? 0x4 : 0,
-               .data = value,
-       };
-       struct acpi_buffer input = { sizeof(struct bios_args), &args };
-       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
-
-       status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
-
-       obj = output.pointer;
-
-       if (!obj || obj->type != ACPI_TYPE_BUFFER)
-               return -EINVAL;
-
-       bios_return = *((struct bios_return *)obj->buffer.pointer);
-       if (bios_return.return_code > 0)
-               return bios_return.return_code * -1;
-       else
-               return bios_return.value;
-}
-
-static int hp_wmi_display_state(void)
-{
-       return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
-}
-
-static int hp_wmi_hddtemp_state(void)
-{
-       return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
-}
-
-static int hp_wmi_als_state(void)
-{
-       return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
-}
-
-static int hp_wmi_dock_state(void)
-{
-       return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
-}
-
-static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
-{
-       if (state)
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
-       else
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
-}
-
-static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
-{
-       if (state)
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
-       else
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
-}
-
-static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
-{
-       if (state)
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
-       else
-               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
-}
-
-static int hp_wmi_wifi_state(void)
-{
-       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
-       if (wireless & 0x100)
-               return RFKILL_STATE_UNBLOCKED;
-       else
-               return RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static int hp_wmi_bluetooth_state(void)
-{
-       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
-       if (wireless & 0x10000)
-               return RFKILL_STATE_UNBLOCKED;
-       else
-               return RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static int hp_wmi_wwan_state(void)
-{
-       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
-       if (wireless & 0x1000000)
-               return RFKILL_STATE_UNBLOCKED;
-       else
-               return RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static ssize_t show_display(struct device *dev, struct device_attribute *attr,
-                           char *buf)
-{
-       int value = hp_wmi_display_state();
-       if (value < 0)
-               return -EINVAL;
-       return sprintf(buf, "%d\n", value);
-}
-
-static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
-                           char *buf)
-{
-       int value = hp_wmi_hddtemp_state();
-       if (value < 0)
-               return -EINVAL;
-       return sprintf(buf, "%d\n", value);
-}
-
-static ssize_t show_als(struct device *dev, struct device_attribute *attr,
-                       char *buf)
-{
-       int value = hp_wmi_als_state();
-       if (value < 0)
-               return -EINVAL;
-       return sprintf(buf, "%d\n", value);
-}
-
-static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
-                        char *buf)
-{
-       int value = hp_wmi_dock_state();
-       if (value < 0)
-               return -EINVAL;
-       return sprintf(buf, "%d\n", value);
-}
-
-static ssize_t set_als(struct device *dev, struct device_attribute *attr,
-                      const char *buf, size_t count)
-{
-       u32 tmp = simple_strtoul(buf, NULL, 10);
-       hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
-       return count;
-}
-
-static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
-static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
-static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
-static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
-
-static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
-{
-       struct key_entry *key;
-
-       for (key = hp_wmi_keymap; key->type != KE_END; key++)
-               if (code == key->code)
-                       return key;
-
-       return NULL;
-}
-
-static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
-{
-       struct key_entry *key;
-
-       for (key = hp_wmi_keymap; key->type != KE_END; key++)
-               if (key->type == KE_KEY && keycode == key->keycode)
-                       return key;
-
-       return NULL;
-}
-
-static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
-{
-       struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
-
-       if (key && key->type == KE_KEY) {
-               *keycode = key->keycode;
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
-{
-       struct key_entry *key;
-       int old_keycode;
-
-       if (keycode < 0 || keycode > KEY_MAX)
-               return -EINVAL;
-
-       key = hp_wmi_get_entry_by_scancode(scancode);
-       if (key && key->type == KE_KEY) {
-               old_keycode = key->keycode;
-               key->keycode = keycode;
-               set_bit(keycode, dev->keybit);
-               if (!hp_wmi_get_entry_by_keycode(old_keycode))
-                       clear_bit(old_keycode, dev->keybit);
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-static void hp_wmi_notify(u32 value, void *context)
-{
-       struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
-       static struct key_entry *key;
-       union acpi_object *obj;
-
-       wmi_get_event_data(value, &response);
-
-       obj = (union acpi_object *)response.pointer;
-
-       if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
-               int eventcode = *((u8 *) obj->buffer.pointer);
-               if (eventcode == 0x4)
-                       eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
-                                                        0);
-               key = hp_wmi_get_entry_by_scancode(eventcode);
-               if (key) {
-                       switch (key->type) {
-                       case KE_KEY:
-                               input_report_key(hp_wmi_input_dev,
-                                                key->keycode, 1);
-                               input_sync(hp_wmi_input_dev);
-                               input_report_key(hp_wmi_input_dev,
-                                                key->keycode, 0);
-                               input_sync(hp_wmi_input_dev);
-                               break;
-                       case KE_SW:
-                               input_report_switch(hp_wmi_input_dev,
-                                                   key->keycode,
-                                                   hp_wmi_dock_state());
-                               input_sync(hp_wmi_input_dev);
-                               break;
-                       }
-               } else if (eventcode == 0x5) {
-                       if (wifi_rfkill)
-                               rfkill_force_state(wifi_rfkill,
-                                                  hp_wmi_wifi_state());
-                       if (bluetooth_rfkill)
-                               rfkill_force_state(bluetooth_rfkill,
-                                                  hp_wmi_bluetooth_state());
-                       if (wwan_rfkill)
-                               rfkill_force_state(wwan_rfkill,
-                                                  hp_wmi_wwan_state());
-               } else
-                       printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
-                              eventcode);
-       } else
-               printk(KERN_INFO "HP WMI: Unknown response received\n");
-}
-
-static int __init hp_wmi_input_setup(void)
-{
-       struct key_entry *key;
-       int err;
-
-       hp_wmi_input_dev = input_allocate_device();
-
-       hp_wmi_input_dev->name = "HP WMI hotkeys";
-       hp_wmi_input_dev->phys = "wmi/input0";
-       hp_wmi_input_dev->id.bustype = BUS_HOST;
-       hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
-       hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
-
-       for (key = hp_wmi_keymap; key->type != KE_END; key++) {
-               switch (key->type) {
-               case KE_KEY:
-                       set_bit(EV_KEY, hp_wmi_input_dev->evbit);
-                       set_bit(key->keycode, hp_wmi_input_dev->keybit);
-                       break;
-               case KE_SW:
-                       set_bit(EV_SW, hp_wmi_input_dev->evbit);
-                       set_bit(key->keycode, hp_wmi_input_dev->swbit);
-                       break;
-               }
-       }
-
-       err = input_register_device(hp_wmi_input_dev);
-
-       if (err) {
-               input_free_device(hp_wmi_input_dev);
-               return err;
-       }
-
-       return 0;
-}
-
-static void cleanup_sysfs(struct platform_device *device)
-{
-       device_remove_file(&device->dev, &dev_attr_display);
-       device_remove_file(&device->dev, &dev_attr_hddtemp);
-       device_remove_file(&device->dev, &dev_attr_als);
-       device_remove_file(&device->dev, &dev_attr_dock);
-}
-
-static int __init hp_wmi_bios_setup(struct platform_device *device)
-{
-       int err;
-       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
-       err = device_create_file(&device->dev, &dev_attr_display);
-       if (err)
-               goto add_sysfs_error;
-       err = device_create_file(&device->dev, &dev_attr_hddtemp);
-       if (err)
-               goto add_sysfs_error;
-       err = device_create_file(&device->dev, &dev_attr_als);
-       if (err)
-               goto add_sysfs_error;
-       err = device_create_file(&device->dev, &dev_attr_dock);
-       if (err)
-               goto add_sysfs_error;
-
-       if (wireless & 0x1) {
-               wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
-               wifi_rfkill->name = "hp-wifi";
-               wifi_rfkill->state = hp_wmi_wifi_state();
-               wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
-               wifi_rfkill->user_claim_unsupported = 1;
-               rfkill_register(wifi_rfkill);
-       }
-
-       if (wireless & 0x2) {
-               bluetooth_rfkill = rfkill_allocate(&device->dev,
-                                                  RFKILL_TYPE_BLUETOOTH);
-               bluetooth_rfkill->name = "hp-bluetooth";
-               bluetooth_rfkill->state = hp_wmi_bluetooth_state();
-               bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
-               bluetooth_rfkill->user_claim_unsupported = 1;
-               rfkill_register(bluetooth_rfkill);
-       }
-
-       if (wireless & 0x4) {
-               wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
-               wwan_rfkill->name = "hp-wwan";
-               wwan_rfkill->state = hp_wmi_wwan_state();
-               wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
-               wwan_rfkill->user_claim_unsupported = 1;
-               rfkill_register(wwan_rfkill);
-       }
-
-       return 0;
-add_sysfs_error:
-       cleanup_sysfs(device);
-       return err;
-}
-
-static int __exit hp_wmi_bios_remove(struct platform_device *device)
-{
-       cleanup_sysfs(device);
-
-       if (wifi_rfkill)
-               rfkill_unregister(wifi_rfkill);
-       if (bluetooth_rfkill)
-               rfkill_unregister(bluetooth_rfkill);
-       if (wwan_rfkill)
-               rfkill_unregister(wwan_rfkill);
-
-       return 0;
-}
-
-static int __init hp_wmi_init(void)
-{
-       int err;
-
-       if (wmi_has_guid(HPWMI_EVENT_GUID)) {
-               err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
-                                                hp_wmi_notify, NULL);
-               if (!err)
-                       hp_wmi_input_setup();
-       }
-
-       if (wmi_has_guid(HPWMI_BIOS_GUID)) {
-               err = platform_driver_register(&hp_wmi_driver);
-               if (err)
-                       return 0;
-               hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
-               if (!hp_wmi_platform_dev) {
-                       platform_driver_unregister(&hp_wmi_driver);
-                       return 0;
-               }
-               platform_device_add(hp_wmi_platform_dev);
-       }
-
-       return 0;
-}
-
-static void __exit hp_wmi_exit(void)
-{
-       if (wmi_has_guid(HPWMI_EVENT_GUID)) {
-               wmi_remove_notify_handler(HPWMI_EVENT_GUID);
-               input_unregister_device(hp_wmi_input_dev);
-       }
-       if (hp_wmi_platform_dev) {
-               platform_device_del(hp_wmi_platform_dev);
-               platform_driver_unregister(&hp_wmi_driver);
-       }
-}
-
-module_init(hp_wmi_init);
-module_exit(hp_wmi_exit);
diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c
deleted file mode 100644 (file)
index 27b7662..0000000
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- *  intel_menlow.c - Intel menlow Driver for thermal management extension
- *
- *  Copyright (C) 2008 Intel Corp
- *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
- *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
- *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  This driver creates the sys I/F for programming the sensors.
- *  It also implements the driver for intel menlow memory controller (hardware
- *  id is INT0002) which makes use of the platform specific ACPI methods
- *  to get/set bandwidth.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/pm.h>
-
-#include <linux/thermal.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-
-MODULE_AUTHOR("Thomas Sujith");
-MODULE_AUTHOR("Zhang Rui");
-MODULE_DESCRIPTION("Intel Menlow platform specific driver");
-MODULE_LICENSE("GPL");
-
-/*
- * Memory controller device control
- */
-
-#define MEMORY_GET_BANDWIDTH "GTHS"
-#define MEMORY_SET_BANDWIDTH "STHS"
-#define MEMORY_ARG_CUR_BANDWIDTH 1
-#define MEMORY_ARG_MAX_BANDWIDTH 0
-
-/*
- * GTHS returning 'n' would mean that [0,n-1] states are supported
- * In that case max_cstate would be n-1
- * GTHS returning '0' would mean that no bandwidth control states are supported
- */
-static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
-                                       unsigned long *max_state)
-{
-       struct acpi_device *device = cdev->devdata;
-       acpi_handle handle = device->handle;
-       unsigned long long value;
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
-       acpi_status status = AE_OK;
-
-       arg_list.count = 1;
-       arg_list.pointer = &arg;
-       arg.type = ACPI_TYPE_INTEGER;
-       arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
-       status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
-                                      &arg_list, &value);
-       if (ACPI_FAILURE(status))
-               return -EFAULT;
-
-       if (!value)
-               return -EINVAL;
-
-       *max_state = value - 1;
-       return 0;
-}
-
-static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
-                                   char *buf)
-{
-       unsigned long value;
-       if (memory_get_int_max_bandwidth(cdev, &value))
-               return -EINVAL;
-
-       return sprintf(buf, "%ld\n", value);
-}
-
-static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
-                                   char *buf)
-{
-       struct acpi_device *device = cdev->devdata;
-       acpi_handle handle = device->handle;
-       unsigned long long value;
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
-       acpi_status status = AE_OK;
-
-       arg_list.count = 1;
-       arg_list.pointer = &arg;
-       arg.type = ACPI_TYPE_INTEGER;
-       arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
-       status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
-                                      &arg_list, &value);
-       if (ACPI_FAILURE(status))
-               return -EFAULT;
-
-       return sprintf(buf, "%llu\n", value);
-}
-
-static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
-                                   unsigned int state)
-{
-       struct acpi_device *device = cdev->devdata;
-       acpi_handle handle = device->handle;
-       struct acpi_object_list arg_list;
-       union acpi_object arg;
-       acpi_status status;
-       unsigned long long temp;
-       unsigned long max_state;
-
-       if (memory_get_int_max_bandwidth(cdev, &max_state))
-               return -EFAULT;
-
-       if (state > max_state)
-               return -EINVAL;
-
-       arg_list.count = 1;
-       arg_list.pointer = &arg;
-       arg.type = ACPI_TYPE_INTEGER;
-       arg.integer.value = state;
-
-       status =
-           acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
-                                 &temp);
-
-       printk(KERN_INFO
-              "Bandwidth value was %d: status is %d\n", state, status);
-       if (ACPI_FAILURE(status))
-               return -EFAULT;
-
-       return 0;
-}
-
-static struct thermal_cooling_device_ops memory_cooling_ops = {
-       .get_max_state = memory_get_max_bandwidth,
-       .get_cur_state = memory_get_cur_bandwidth,
-       .set_cur_state = memory_set_cur_bandwidth,
-};
-
-/*
- * Memory Device Management
- */
-static int intel_menlow_memory_add(struct acpi_device *device)
-{
-       int result = -ENODEV;
-       acpi_status status = AE_OK;
-       acpi_handle dummy;
-       struct thermal_cooling_device *cdev;
-
-       if (!device)
-               return -EINVAL;
-
-       status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
-       if (ACPI_FAILURE(status))
-               goto end;
-
-       status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
-       if (ACPI_FAILURE(status))
-               goto end;
-
-       cdev = thermal_cooling_device_register("Memory controller", device,
-                                              &memory_cooling_ops);
-       if (IS_ERR(cdev)) {
-               result = PTR_ERR(cdev);
-               goto end;
-       }
-
-       device->driver_data = cdev;
-       result = sysfs_create_link(&device->dev.kobj,
-                               &cdev->device.kobj, "thermal_cooling");
-       if (result)
-               goto unregister;
-
-       result = sysfs_create_link(&cdev->device.kobj,
-                               &device->dev.kobj, "device");
-       if (result) {
-               sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
-               goto unregister;
-       }
-
- end:
-       return result;
-
- unregister:
-       thermal_cooling_device_unregister(cdev);
-       return result;
-
-}
-
-static int intel_menlow_memory_remove(struct acpi_device *device, int type)
-{
-       struct thermal_cooling_device *cdev = acpi_driver_data(device);
-
-       if (!device || !cdev)
-               return -EINVAL;
-
-       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
-       sysfs_remove_link(&cdev->device.kobj, "device");
-       thermal_cooling_device_unregister(cdev);
-
-       return 0;
-}
-
-static const struct acpi_device_id intel_menlow_memory_ids[] = {
-       {"INT0002", 0},
-       {"", 0},
-};
-
-static struct acpi_driver intel_menlow_memory_driver = {
-       .name = "intel_menlow_thermal_control",
-       .ids = intel_menlow_memory_ids,
-       .ops = {
-               .add = intel_menlow_memory_add,
-               .remove = intel_menlow_memory_remove,
-               },
-};
-
-/*
- * Sensor control on menlow platform
- */
-
-#define THERMAL_AUX0 0
-#define THERMAL_AUX1 1
-#define GET_AUX0 "GAX0"
-#define GET_AUX1 "GAX1"
-#define SET_AUX0 "SAX0"
-#define SET_AUX1 "SAX1"
-
-struct intel_menlow_attribute {
-       struct device_attribute attr;
-       struct device *device;
-       acpi_handle handle;
-       struct list_head node;
-};
-
-static LIST_HEAD(intel_menlow_attr_list);
-static DEFINE_MUTEX(intel_menlow_attr_lock);
-
-/*
- * sensor_get_auxtrip - get the current auxtrip value from sensor
- * @name: Thermalzone name
- * @auxtype : AUX0/AUX1
- * @buf: syfs buffer
- */
-static int sensor_get_auxtrip(acpi_handle handle, int index,
-                                                       unsigned long long *value)
-{
-       acpi_status status;
-
-       if ((index != 0 && index != 1) || !value)
-               return -EINVAL;
-
-       status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
-                                      NULL, value);
-       if (ACPI_FAILURE(status))
-               return -EIO;
-
-       return 0;
-}
-
-/*
- * sensor_set_auxtrip - set the new auxtrip value to sensor
- * @name: Thermalzone name
- * @auxtype : AUX0/AUX1
- * @buf: syfs buffer
- */
-static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
-{
-       acpi_status status;
-       union acpi_object arg = {
-               ACPI_TYPE_INTEGER
-       };
-       struct acpi_object_list args = {
-               1, &arg
-       };
-       unsigned long long temp;
-
-       if (index != 0 && index != 1)
-               return -EINVAL;
-
-       status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
-                                      NULL, &temp);
-       if (ACPI_FAILURE(status))
-               return -EIO;
-       if ((index && value < temp) || (!index && value > temp))
-               return -EINVAL;
-
-       arg.integer.value = value;
-       status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
-                                      &args, &temp);
-       if (ACPI_FAILURE(status))
-               return -EIO;
-
-       /* do we need to check the return value of SAX0/SAX1 ? */
-
-       return 0;
-}
-
-#define to_intel_menlow_attr(_attr)    \
-       container_of(_attr, struct intel_menlow_attribute, attr)
-
-static ssize_t aux0_show(struct device *dev,
-                        struct device_attribute *dev_attr, char *buf)
-{
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       unsigned long long value;
-       int result;
-
-       result = sensor_get_auxtrip(attr->handle, 0, &value);
-
-       return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
-}
-
-static ssize_t aux1_show(struct device *dev,
-                        struct device_attribute *dev_attr, char *buf)
-{
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       unsigned long long value;
-       int result;
-
-       result = sensor_get_auxtrip(attr->handle, 1, &value);
-
-       return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
-}
-
-static ssize_t aux0_store(struct device *dev,
-                         struct device_attribute *dev_attr,
-                         const char *buf, size_t count)
-{
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       int value;
-       int result;
-
-       /*Sanity check; should be a positive integer */
-       if (!sscanf(buf, "%d", &value))
-               return -EINVAL;
-
-       if (value < 0)
-               return -EINVAL;
-
-       result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
-       return result ? result : count;
-}
-
-static ssize_t aux1_store(struct device *dev,
-                         struct device_attribute *dev_attr,
-                         const char *buf, size_t count)
-{
-       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
-       int value;
-       int result;
-
-       /*Sanity check; should be a positive integer */
-       if (!sscanf(buf, "%d", &value))
-               return -EINVAL;
-
-       if (value < 0)
-               return -EINVAL;
-
-       result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
-       return result ? result : count;
-}
-
-/* BIOS can enable/disable the thermal user application in dabney platform */
-#define BIOS_ENABLED "\\_TZ.GSTS"
-static ssize_t bios_enabled_show(struct device *dev,
-                                struct device_attribute *attr, char *buf)
-{
-       acpi_status status;
-       unsigned long long bios_enabled;
-
-       status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
-}
-
-static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
-                                         void *store, struct device *dev,
-                                         acpi_handle handle)
-{
-       struct intel_menlow_attribute *attr;
-       int result;
-
-       attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
-       if (!attr)
-               return -ENOMEM;
-
-       attr->attr.attr.name = name;
-       attr->attr.attr.mode = mode;
-       attr->attr.show = show;
-       attr->attr.store = store;
-       attr->device = dev;
-       attr->handle = handle;
-
-       result = device_create_file(dev, &attr->attr);
-       if (result)
-               return result;
-
-       mutex_lock(&intel_menlow_attr_lock);
-       list_add_tail(&attr->node, &intel_menlow_attr_list);
-       mutex_unlock(&intel_menlow_attr_lock);
-
-       return 0;
-}
-
-static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
-                                               void *context, void **rv)
-{
-       acpi_status status;
-       acpi_handle dummy;
-       struct thermal_zone_device *thermal;
-       int result;
-
-       result = acpi_bus_get_private_data(handle, (void **)&thermal);
-       if (result)
-               return 0;
-
-       /* _TZ must have the AUX0/1 methods */
-       status = acpi_get_handle(handle, GET_AUX0, &dummy);
-       if (ACPI_FAILURE(status))
-               goto not_found;
-
-       status = acpi_get_handle(handle, SET_AUX0, &dummy);
-       if (ACPI_FAILURE(status))
-               goto not_found;
-
-       result = intel_menlow_add_one_attribute("aux0", 0644,
-                                               aux0_show, aux0_store,
-                                               &thermal->device, handle);
-       if (result)
-               return AE_ERROR;
-
-       status = acpi_get_handle(handle, GET_AUX1, &dummy);
-       if (ACPI_FAILURE(status))
-               goto not_found;
-
-       status = acpi_get_handle(handle, SET_AUX1, &dummy);
-       if (ACPI_FAILURE(status))
-               goto not_found;
-
-       result = intel_menlow_add_one_attribute("aux1", 0644,
-                                               aux1_show, aux1_store,
-                                               &thermal->device, handle);
-       if (result)
-               return AE_ERROR;
-
-       /*
-        * create the "dabney_enabled" attribute which means the user app
-        * should be loaded or not
-        */
-
-       result = intel_menlow_add_one_attribute("bios_enabled", 0444,
-                                               bios_enabled_show, NULL,
-                                               &thermal->device, handle);
-       if (result)
-               return AE_ERROR;
-
- not_found:
-       if (status == AE_NOT_FOUND)
-               return AE_OK;
-       else
-               return status;
-}
-
-static void intel_menlow_unregister_sensor(void)
-{
-       struct intel_menlow_attribute *pos, *next;
-
-       mutex_lock(&intel_menlow_attr_lock);
-       list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
-               list_del(&pos->node);
-               device_remove_file(pos->device, &pos->attr);
-               kfree(pos);
-       }
-       mutex_unlock(&intel_menlow_attr_lock);
-
-       return;
-}
-
-static int __init intel_menlow_module_init(void)
-{
-       int result = -ENODEV;
-       acpi_status status;
-       unsigned long long enable;
-
-       if (acpi_disabled)
-               return result;
-
-       /* Looking for the \_TZ.GSTS method */
-       status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
-       if (ACPI_FAILURE(status) || !enable)
-               return -ENODEV;
-
-       /* Looking for ACPI device MEM0 with hardware id INT0002 */
-       result = acpi_bus_register_driver(&intel_menlow_memory_driver);
-       if (result)
-               return result;
-
-       /* Looking for sensors in each ACPI thermal zone */
-       status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
-                                    ACPI_UINT32_MAX,
-                                    intel_menlow_register_sensor, NULL, NULL);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return 0;
-}
-
-static void __exit intel_menlow_module_exit(void)
-{
-       acpi_bus_unregister_driver(&intel_menlow_memory_driver);
-       intel_menlow_unregister_sensor();
-}
-
-module_init(intel_menlow_module_init);
-module_exit(intel_menlow_module_exit);
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c
deleted file mode 100644 (file)
index 759763d..0000000
+++ /dev/null
@@ -1,437 +0,0 @@
-/*-*-linux-c-*-*/
-
-/*
-  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
-
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-  02110-1301, USA.
- */
-
-/*
- * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
- * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
- *
- * Driver also supports S271, S420 models.
- *
- * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
- *
- *   lcd_level - Screen brightness: contains a single integer in the
- *   range 0..8. (rw)
- *
- *   auto_brightness - Enable automatic brightness control: contains
- *   either 0 or 1. If set to 1 the hardware adjusts the screen
- *   brightness automatically when the power cord is
- *   plugged/unplugged. (rw)
- *
- *   wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
- *
- *   bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
- *   Please note that this file is constantly 0 if no Bluetooth
- *   hardware is available. (ro)
- *
- * In addition to these platform device attributes the driver
- * registers itself in the Linux backlight control subsystem and is
- * available to userspace under /sys/class/backlight/msi-laptop-bl/.
- *
- * This driver might work on other laptops produced by MSI. If you
- * want to try it you can pass force=1 as argument to the module which
- * will force it to load even when the DMI data doesn't identify the
- * laptop as MSI S270. YMMV.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/acpi.h>
-#include <linux/dmi.h>
-#include <linux/backlight.h>
-#include <linux/platform_device.h>
-
-#define MSI_DRIVER_VERSION "0.5"
-
-#define MSI_LCD_LEVEL_MAX 9
-
-#define MSI_EC_COMMAND_WIRELESS 0x10
-#define MSI_EC_COMMAND_LCD_LEVEL 0x11
-
-static int force;
-module_param(force, bool, 0);
-MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
-
-static int auto_brightness;
-module_param(auto_brightness, int, 0);
-MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
-
-/* Hardware access */
-
-static int set_lcd_level(int level)
-{
-       u8 buf[2];
-
-       if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
-               return -EINVAL;
-
-       buf[0] = 0x80;
-       buf[1] = (u8) (level*31);
-
-       return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
-}
-
-static int get_lcd_level(void)
-{
-       u8 wdata = 0, rdata;
-       int result;
-
-       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
-       if (result < 0)
-               return result;
-
-       return (int) rdata / 31;
-}
-
-static int get_auto_brightness(void)
-{
-       u8 wdata = 4, rdata;
-       int result;
-
-       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
-       if (result < 0)
-               return result;
-
-       return !!(rdata & 8);
-}
-
-static int set_auto_brightness(int enable)
-{
-       u8 wdata[2], rdata;
-       int result;
-
-       wdata[0] = 4;
-
-       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
-       if (result < 0)
-               return result;
-
-       wdata[0] = 0x84;
-       wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
-
-       return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
-}
-
-static int get_wireless_state(int *wlan, int *bluetooth)
-{
-       u8 wdata = 0, rdata;
-       int result;
-
-       result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
-       if (result < 0)
-               return -1;
-
-       if (wlan)
-               *wlan = !!(rdata & 8);
-
-       if (bluetooth)
-               *bluetooth = !!(rdata & 128);
-
-       return 0;
-}
-
-/* Backlight device stuff */
-
-static int bl_get_brightness(struct backlight_device *b)
-{
-       return get_lcd_level();
-}
-
-
-static int bl_update_status(struct backlight_device *b)
-{
-       return set_lcd_level(b->props.brightness);
-}
-
-static struct backlight_ops msibl_ops = {
-       .get_brightness = bl_get_brightness,
-       .update_status  = bl_update_status,
-};
-
-static struct backlight_device *msibl_device;
-
-/* Platform device */
-
-static ssize_t show_wlan(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-
-       int ret, enabled;
-
-       ret = get_wireless_state(&enabled, NULL);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t show_bluetooth(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-
-       int ret, enabled;
-
-       ret = get_wireless_state(NULL, &enabled);
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", enabled);
-}
-
-static ssize_t show_lcd_level(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-
-       int ret;
-
-       ret = get_lcd_level();
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t store_lcd_level(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-
-       int level, ret;
-
-       if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
-               return -EINVAL;
-
-       ret = set_lcd_level(level);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static ssize_t show_auto_brightness(struct device *dev,
-       struct device_attribute *attr, char *buf)
-{
-
-       int ret;
-
-       ret = get_auto_brightness();
-       if (ret < 0)
-               return ret;
-
-       return sprintf(buf, "%i\n", ret);
-}
-
-static ssize_t store_auto_brightness(struct device *dev,
-       struct device_attribute *attr, const char *buf, size_t count)
-{
-
-       int enable, ret;
-
-       if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
-               return -EINVAL;
-
-       ret = set_auto_brightness(enable);
-       if (ret < 0)
-               return ret;
-
-       return count;
-}
-
-static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
-static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
-static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
-static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
-
-static struct attribute *msipf_attributes[] = {
-       &dev_attr_lcd_level.attr,
-       &dev_attr_auto_brightness.attr,
-       &dev_attr_bluetooth.attr,
-       &dev_attr_wlan.attr,
-       NULL
-};
-
-static struct attribute_group msipf_attribute_group = {
-       .attrs = msipf_attributes
-};
-
-static struct platform_driver msipf_driver = {
-       .driver = {
-               .name = "msi-laptop-pf",
-               .owner = THIS_MODULE,
-       }
-};
-
-static struct platform_device *msipf_device;
-
-/* Initialization */
-
-static int dmi_check_cb(const struct dmi_system_id *id)
-{
-        printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
-        return 0;
-}
-
-static struct dmi_system_id __initdata msi_dmi_table[] = {
-       {
-               .ident = "MSI S270",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
-                       DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
-                       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "MSI S271",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
-                       DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
-                       DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "MSI S420",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
-                       DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
-                       DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
-               },
-               .callback = dmi_check_cb
-       },
-       {
-               .ident = "Medion MD96100",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
-                       DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
-                       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
-               },
-               .callback = dmi_check_cb
-       },
-       { }
-};
-
-static int __init msi_init(void)
-{
-       int ret;
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       if (!force && !dmi_check_system(msi_dmi_table))
-               return -ENODEV;
-
-       if (auto_brightness < 0 || auto_brightness > 2)
-               return -EINVAL;
-
-       /* Register backlight stuff */
-
-       if (acpi_video_backlight_support()) {
-               printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
-                      "by ACPI video driver\n");
-       } else {
-               msibl_device = backlight_device_register("msi-laptop-bl", NULL,
-                                                        NULL, &msibl_ops);
-               if (IS_ERR(msibl_device))
-                       return PTR_ERR(msibl_device);
-               msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
-       }
-
-       ret = platform_driver_register(&msipf_driver);
-       if (ret)
-               goto fail_backlight;
-
-       /* Register platform stuff */
-
-       msipf_device = platform_device_alloc("msi-laptop-pf", -1);
-       if (!msipf_device) {
-               ret = -ENOMEM;
-               goto fail_platform_driver;
-       }
-
-       ret = platform_device_add(msipf_device);
-       if (ret)
-               goto fail_platform_device1;
-
-       ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
-       if (ret)
-               goto fail_platform_device2;
-
-       /* Disable automatic brightness control by default because
-        * this module was probably loaded to do brightness control in
-        * software. */
-
-       if (auto_brightness != 2)
-               set_auto_brightness(auto_brightness);
-
-       printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
-
-       return 0;
-
-fail_platform_device2:
-
-       platform_device_del(msipf_device);
-
-fail_platform_device1:
-
-       platform_device_put(msipf_device);
-
-fail_platform_driver:
-
-       platform_driver_unregister(&msipf_driver);
-
-fail_backlight:
-
-       backlight_device_unregister(msibl_device);
-
-       return ret;
-}
-
-static void __exit msi_cleanup(void)
-{
-
-       sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
-       platform_device_unregister(msipf_device);
-       platform_driver_unregister(&msipf_driver);
-       backlight_device_unregister(msibl_device);
-
-       /* Enable automatic brightness control again */
-       if (auto_brightness != 2)
-               set_auto_brightness(1);
-
-       printk(KERN_INFO "msi-laptop: driver unloaded.\n");
-}
-
-module_init(msi_init);
-module_exit(msi_cleanup);
-
-MODULE_AUTHOR("Lennart Poettering");
-MODULE_DESCRIPTION("MSI Laptop Support");
-MODULE_VERSION(MSI_DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
-MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
-MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
-MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
diff --git a/drivers/misc/panasonic-laptop.c b/drivers/misc/panasonic-laptop.c
deleted file mode 100644 (file)
index 4a1bc64..0000000
+++ /dev/null
@@ -1,766 +0,0 @@
-/*
- *  Panasonic HotKey and LCD brightness control driver
- *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
- *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
- *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
- *  (C) 2004 David Bronaugh <dbronaugh>
- *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
- *
- *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  publicshed by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
- *
- *---------------------------------------------------------------------------
- *
- * ChangeLog:
- *     Sep.23, 2008    Harald Welte <laforge@gnumonks.org>
- *             -v0.95  rename driver from drivers/acpi/pcc_acpi.c to
- *                     drivers/misc/panasonic-laptop.c
- *
- *     Jul.04, 2008    Harald Welte <laforge@gnumonks.org>
- *             -v0.94  replace /proc interface with device attributes
- *                     support {set,get}keycode on th input device
- *
- *      Jun.27, 2008   Harald Welte <laforge@gnumonks.org>
- *             -v0.92  merge with 2.6.26-rc6 input API changes
- *                     remove broken <= 2.6.15 kernel support
- *                     resolve all compiler warnings
- *                     various coding style fixes (checkpatch.pl)
- *                     add support for backlight api
- *                     major code restructuring
- *
- *     Dac.28, 2007    Harald Welte <laforge@gnumonks.org>
- *             -v0.91  merge with 2.6.24-rc6 ACPI changes
- *
- *     Nov.04, 2006    Hiroshi Miura <miura@da-cha.org>
- *             -v0.9   remove warning about section reference.
- *                     remove acpi_os_free
- *                     add /proc/acpi/pcc/brightness interface for HAL access
- *                     merge dbronaugh's enhancement
- *                     Aug.17, 2004 David Bronaugh (dbronaugh)
- *                             - Added screen brightness setting interface
- *                               Thanks to FreeBSD crew (acpi_panasonic.c)
- *                               for the ideas I needed to accomplish it
- *
- *     May.29, 2006    Hiroshi Miura <miura@da-cha.org>
- *             -v0.8.4 follow to change keyinput structure
- *                     thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
- *                     Jacob Bower <jacob.bower@ic.ac.uk> and
- *                     Hiroshi Yokota for providing solutions.
- *
- *     Oct.02, 2004    Hiroshi Miura <miura@da-cha.org>
- *             -v0.8.2 merge code of YOKOTA Hiroshi
- *                                     <yokota@netlab.is.tsukuba.ac.jp>.
- *                     Add sticky key mode interface.
- *                     Refactoring acpi_pcc_generate_keyinput().
- *
- *     Sep.15, 2004    Hiroshi Miura <miura@da-cha.org>
- *             -v0.8   Generate key input event on input subsystem.
- *                     This is based on yet another driver written by
- *                                                     Ryuta Nakanishi.
- *
- *     Sep.10, 2004    Hiroshi Miura <miura@da-cha.org>
- *             -v0.7   Change proc interface functions using seq_file
- *                     facility as same as other ACPI drivers.
- *
- *     Aug.28, 2004    Hiroshi Miura <miura@da-cha.org>
- *             -v0.6.4 Fix a silly error with status checking
- *
- *     Aug.25, 2004    Hiroshi Miura <miura@da-cha.org>
- *             -v0.6.3 replace read_acpi_int by standard function
- *                                                     acpi_evaluate_integer
- *                     some clean up and make smart copyright notice.
- *                     fix return value of pcc_acpi_get_key()
- *                     fix checking return value of acpi_bus_register_driver()
- *
- *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
- *              -v0.6.2 Add check on ACPI data (num_sifr)
- *                      Coding style cleanups, better error messages/handling
- *                     Fixed an off-by-one error in memory allocation
- *
- *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
- *              -v0.6.1 Fix a silly error with status checking
- *
- *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
- *              - v0.6  Correct brightness controls to reflect reality
- *                      based on information gleaned by Hiroshi Miura
- *                      and discussions with Hiroshi Miura
- *
- *     Aug.10, 2004    Hiroshi Miura <miura@da-cha.org>
- *             - v0.5  support LCD brightness control
- *                     based on the disclosed information by MEI.
- *
- *     Jul.25, 2004    Hiroshi Miura <miura@da-cha.org>
- *             - v0.4  first post version
- *                     add function to retrive SIFR
- *
- *     Jul.24, 2004    Hiroshi Miura <miura@da-cha.org>
- *             - v0.3  get proper status of hotkey
- *
- *      Jul.22, 2004   Hiroshi Miura <miura@da-cha.org>
- *             - v0.2  add HotKey handler
- *
- *      Jul.17, 2004   Hiroshi Miura <miura@da-cha.org>
- *             - v0.1  start from toshiba_acpi driver written by John Belmonte
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/backlight.h>
-#include <linux/ctype.h>
-#include <linux/seq_file.h>
-#include <linux/uaccess.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-#include <linux/input.h>
-
-
-#ifndef ACPI_HOTKEY_COMPONENT
-#define ACPI_HOTKEY_COMPONENT  0x10000000
-#endif
-
-#define _COMPONENT             ACPI_HOTKEY_COMPONENT
-
-MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
-MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
-MODULE_LICENSE("GPL");
-
-#define LOGPREFIX "pcc_acpi: "
-
-/* Define ACPI PATHs */
-/* Lets note hotkeys */
-#define METHOD_HKEY_QUERY      "HINF"
-#define METHOD_HKEY_SQTY       "SQTY"
-#define METHOD_HKEY_SINF       "SINF"
-#define METHOD_HKEY_SSET       "SSET"
-#define HKEY_NOTIFY             0x80
-
-#define ACPI_PCC_DRIVER_NAME   "Panasonic Laptop Support"
-#define ACPI_PCC_DEVICE_NAME   "Hotkey"
-#define ACPI_PCC_CLASS         "pcc"
-
-#define ACPI_PCC_INPUT_PHYS    "panasonic/hkey0"
-
-/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
-   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
-*/
-enum SINF_BITS { SINF_NUM_BATTERIES = 0,
-                SINF_LCD_TYPE,
-                SINF_AC_MAX_BRIGHT,
-                SINF_AC_MIN_BRIGHT,
-                SINF_AC_CUR_BRIGHT,
-                SINF_DC_MAX_BRIGHT,
-                SINF_DC_MIN_BRIGHT,
-                SINF_DC_CUR_BRIGHT,
-                SINF_MUTE,
-                SINF_RESERVED,
-                SINF_ENV_STATE,
-                SINF_STICKY_KEY = 0x80,
-       };
-/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
-
-static int acpi_pcc_hotkey_add(struct acpi_device *device);
-static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
-static int acpi_pcc_hotkey_resume(struct acpi_device *device);
-
-static const struct acpi_device_id pcc_device_ids[] = {
-       { "MAT0012", 0},
-       { "MAT0013", 0},
-       { "MAT0018", 0},
-       { "MAT0019", 0},
-       { "", 0},
-};
-
-static struct acpi_driver acpi_pcc_driver = {
-       .name =         ACPI_PCC_DRIVER_NAME,
-       .class =        ACPI_PCC_CLASS,
-       .ids =          pcc_device_ids,
-       .ops =          {
-                               .add =          acpi_pcc_hotkey_add,
-                               .remove =       acpi_pcc_hotkey_remove,
-                               .resume =       acpi_pcc_hotkey_resume,
-                       },
-};
-
-#define KEYMAP_SIZE            11
-static const int initial_keymap[KEYMAP_SIZE] = {
-       /*  0 */ KEY_RESERVED,
-       /*  1 */ KEY_BRIGHTNESSDOWN,
-       /*  2 */ KEY_BRIGHTNESSUP,
-       /*  3 */ KEY_DISPLAYTOGGLE,
-       /*  4 */ KEY_MUTE,
-       /*  5 */ KEY_VOLUMEDOWN,
-       /*  6 */ KEY_VOLUMEUP,
-       /*  7 */ KEY_SLEEP,
-       /*  8 */ KEY_PROG1, /* Change CPU boost */
-       /*  9 */ KEY_BATTERY,
-       /* 10 */ KEY_SUSPEND,
-};
-
-struct pcc_acpi {
-       acpi_handle             handle;
-       unsigned long           num_sifr;
-       int                     sticky_mode;
-       u32                     *sinf;
-       struct acpi_device      *device;
-       struct input_dev        *input_dev;
-       struct backlight_device *backlight;
-       int                     keymap[KEYMAP_SIZE];
-};
-
-struct pcc_keyinput {
-       struct acpi_hotkey      *hotkey;
-};
-
-/* method access functions */
-static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
-{
-       union acpi_object in_objs[] = {
-               { .integer.type  = ACPI_TYPE_INTEGER,
-                 .integer.value = func, },
-               { .integer.type  = ACPI_TYPE_INTEGER,
-                 .integer.value = val, },
-       };
-       struct acpi_object_list params = {
-               .count   = ARRAY_SIZE(in_objs),
-               .pointer = in_objs,
-       };
-       acpi_status status = AE_OK;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
-
-       status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
-                                     &params, NULL);
-
-       return status == AE_OK;
-}
-
-static inline int acpi_pcc_get_sqty(struct acpi_device *device)
-{
-       unsigned long long s;
-       acpi_status status;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
-
-       status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
-                                      NULL, &s);
-       if (ACPI_SUCCESS(status))
-               return s;
-       else {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "evaluation error HKEY.SQTY\n"));
-               return -EINVAL;
-       }
-}
-
-static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
-{
-       acpi_status status;
-       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-       union acpi_object *hkey = NULL;
-       int i;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
-
-       status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
-                                     &buffer);
-       if (ACPI_FAILURE(status)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "evaluation error HKEY.SINF\n"));
-               return 0;
-       }
-
-       hkey = buffer.pointer;
-       if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
-               goto end;
-       }
-
-       if (pcc->num_sifr < hkey->package.count) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                "SQTY reports bad SINF length\n"));
-               status = AE_ERROR;
-               goto end;
-       }
-
-       for (i = 0; i < hkey->package.count; i++) {
-               union acpi_object *element = &(hkey->package.elements[i]);
-               if (likely(element->type == ACPI_TYPE_INTEGER)) {
-                       sinf[i] = element->integer.value;
-               } else
-                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                        "Invalid HKEY.SINF data\n"));
-       }
-       sinf[hkey->package.count] = -1;
-
-end:
-       kfree(buffer.pointer);
-       return status == AE_OK;
-}
-
-/* backlight API interface functions */
-
-/* This driver currently treats AC and DC brightness identical,
- * since we don't need to invent an interface to the core ACPI
- * logic to receive events in case a power supply is plugged in
- * or removed */
-
-static int bl_get(struct backlight_device *bd)
-{
-       struct pcc_acpi *pcc = bl_get_data(bd);
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       return pcc->sinf[SINF_AC_CUR_BRIGHT];
-}
-
-static int bl_set_status(struct backlight_device *bd)
-{
-       struct pcc_acpi *pcc = bl_get_data(bd);
-       int bright = bd->props.brightness;
-       int rc;
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
-               bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
-
-       if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
-               bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
-
-       if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
-           bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
-               return -EINVAL;
-
-       rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
-       if (rc < 0)
-               return rc;
-
-       return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
-}
-
-static struct backlight_ops pcc_backlight_ops = {
-       .get_brightness = bl_get,
-       .update_status  = bl_set_status,
-};
-
-
-/* sysfs user interface functions */
-
-static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
-                           char *buf)
-{
-       struct acpi_device *acpi = to_acpi_device(dev);
-       struct pcc_acpi *pcc = acpi_driver_data(acpi);
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
-}
-
-static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
-                           char *buf)
-{
-       struct acpi_device *acpi = to_acpi_device(dev);
-       struct pcc_acpi *pcc = acpi_driver_data(acpi);
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
-}
-
-static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
-                        char *buf)
-{
-       struct acpi_device *acpi = to_acpi_device(dev);
-       struct pcc_acpi *pcc = acpi_driver_data(acpi);
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
-}
-
-static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
-                          char *buf)
-{
-       struct acpi_device *acpi = to_acpi_device(dev);
-       struct pcc_acpi *pcc = acpi_driver_data(acpi);
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
-               return -EIO;
-
-       return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
-}
-
-static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
-                         const char *buf, size_t count)
-{
-       struct acpi_device *acpi = to_acpi_device(dev);
-       struct pcc_acpi *pcc = acpi_driver_data(acpi);
-       int val;
-
-       if (count && sscanf(buf, "%i", &val) == 1 &&
-           (val == 0 || val == 1)) {
-               acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
-               pcc->sticky_mode = val;
-       }
-
-       return count;
-}
-
-static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
-static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
-static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
-static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
-
-static struct attribute *pcc_sysfs_entries[] = {
-       &dev_attr_numbatt.attr,
-       &dev_attr_lcdtype.attr,
-       &dev_attr_mute.attr,
-       &dev_attr_sticky_key.attr,
-       NULL,
-};
-
-static struct attribute_group pcc_attr_group = {
-       .name   = NULL,         /* put in device directory */
-       .attrs  = pcc_sysfs_entries,
-};
-
-
-/* hotkey input device driver */
-
-static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
-{
-       struct pcc_acpi *pcc = input_get_drvdata(dev);
-
-       if (scancode >= ARRAY_SIZE(pcc->keymap))
-               return -EINVAL;
-
-       *keycode = pcc->keymap[scancode];
-
-       return 0;
-}
-
-static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
-               if (pcc->keymap[i] == keycode)
-                       return i+1;
-       }
-
-       return 0;
-}
-
-static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
-{
-       struct pcc_acpi *pcc = input_get_drvdata(dev);
-       int oldkeycode;
-
-       if (scancode >= ARRAY_SIZE(pcc->keymap))
-               return -EINVAL;
-
-       if (keycode < 0 || keycode > KEY_MAX)
-               return -EINVAL;
-
-       oldkeycode = pcc->keymap[scancode];
-       pcc->keymap[scancode] = keycode;
-
-       set_bit(keycode, dev->keybit);
-
-       if (!keymap_get_by_keycode(pcc, oldkeycode))
-               clear_bit(oldkeycode, dev->keybit);
-
-       return 0;
-}
-
-static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
-{
-       struct input_dev *hotk_input_dev = pcc->input_dev;
-       int rc;
-       int key_code, hkey_num;
-       unsigned long long result;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
-
-       rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
-                                  NULL, &result);
-       if (!ACPI_SUCCESS(rc)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                "error getting hotkey status\n"));
-               return;
-       }
-
-       acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
-
-       hkey_num = result & 0xf;
-
-       if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "hotkey number out of range: %d\n",
-                                 hkey_num));
-               return;
-       }
-
-       key_code = pcc->keymap[hkey_num];
-
-       if (key_code != KEY_RESERVED) {
-               int pushed = (result & 0x80) ? TRUE : FALSE;
-
-               input_report_key(hotk_input_dev, key_code, pushed);
-               input_sync(hotk_input_dev);
-       }
-
-       return;
-}
-
-static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
-{
-       struct pcc_acpi *pcc = (struct pcc_acpi *) data;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
-
-       switch (event) {
-       case HKEY_NOTIFY:
-               acpi_pcc_generate_keyinput(pcc);
-               break;
-       default:
-               /* nothing to do */
-               break;
-       }
-}
-
-static int acpi_pcc_init_input(struct pcc_acpi *pcc)
-{
-       int i, rc;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
-
-       pcc->input_dev = input_allocate_device();
-       if (!pcc->input_dev) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Couldn't allocate input device for hotkey"));
-               return -ENOMEM;
-       }
-
-       pcc->input_dev->evbit[0] = BIT(EV_KEY);
-
-       pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
-       pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
-       pcc->input_dev->id.bustype = BUS_HOST;
-       pcc->input_dev->id.vendor = 0x0001;
-       pcc->input_dev->id.product = 0x0001;
-       pcc->input_dev->id.version = 0x0100;
-       pcc->input_dev->getkeycode = pcc_getkeycode;
-       pcc->input_dev->setkeycode = pcc_setkeycode;
-
-       /* load initial keymap */
-       memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
-
-       for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
-               __set_bit(pcc->keymap[i], pcc->input_dev->keybit);
-       __clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
-
-       input_set_drvdata(pcc->input_dev, pcc);
-
-       rc = input_register_device(pcc->input_dev);
-       if (rc < 0)
-               input_free_device(pcc->input_dev);
-
-       return rc;
-}
-
-/* kernel module interface */
-
-static int acpi_pcc_hotkey_resume(struct acpi_device *device)
-{
-       struct pcc_acpi *pcc = acpi_driver_data(device);
-       acpi_status status = AE_OK;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
-
-       if (device == NULL || pcc == NULL)
-               return -EINVAL;
-
-       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
-                         pcc->sticky_mode));
-
-       status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
-
-       return status == AE_OK ? 0 : -EINVAL;
-}
-
-static int acpi_pcc_hotkey_add(struct acpi_device *device)
-{
-       acpi_status status;
-       struct pcc_acpi *pcc;
-       int num_sifr, result;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
-
-       if (!device)
-               return -EINVAL;
-
-       num_sifr = acpi_pcc_get_sqty(device);
-
-       if (num_sifr > 255) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
-               return -ENODEV;
-       }
-
-       pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
-       if (!pcc) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Couldn't allocate mem for pcc"));
-               return -ENOMEM;
-       }
-
-       pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
-       if (!pcc->sinf) {
-               result = -ENOMEM;
-               goto out_hotkey;
-       }
-
-       pcc->device = device;
-       pcc->handle = device->handle;
-       pcc->num_sifr = num_sifr;
-       device->driver_data = pcc;
-       strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
-       strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
-
-       result = acpi_pcc_init_input(pcc);
-       if (result) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Error installing keyinput handler\n"));
-               goto out_sinf;
-       }
-
-       /* initialize hotkey input device */
-       status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
-                                            acpi_pcc_hotkey_notify, pcc);
-
-       if (ACPI_FAILURE(status)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Error installing notify handler\n"));
-               result = -ENODEV;
-               goto out_input;
-       }
-
-       /* initialize backlight */
-       pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
-                                                  &pcc_backlight_ops);
-       if (IS_ERR(pcc->backlight))
-               goto out_notify;
-
-       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                "Couldn't retrieve BIOS data\n"));
-               goto out_backlight;
-       }
-
-       /* read the initial brightness setting from the hardware */
-       pcc->backlight->props.max_brightness =
-                                       pcc->sinf[SINF_AC_MAX_BRIGHT];
-       pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
-
-       /* read the initial sticky key mode from the hardware */
-       pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
-
-       /* add sysfs attributes */
-       result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
-       if (result)
-               goto out_backlight;
-
-       return 0;
-
-out_backlight:
-       backlight_device_unregister(pcc->backlight);
-out_notify:
-       acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
-                                  acpi_pcc_hotkey_notify);
-out_input:
-       input_unregister_device(pcc->input_dev);
-       /* no need to input_free_device() since core input API refcount and
-        * free()s the device */
-out_sinf:
-       kfree(pcc->sinf);
-out_hotkey:
-       kfree(pcc);
-
-       return result;
-}
-
-static int __init acpi_pcc_init(void)
-{
-       int result = 0;
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_init");
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       result = acpi_bus_register_driver(&acpi_pcc_driver);
-       if (result < 0) {
-               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
-                                 "Error registering hotkey driver\n"));
-               return -ENODEV;
-       }
-
-       return 0;
-}
-
-static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
-{
-       struct pcc_acpi *pcc = acpi_driver_data(device);
-
-       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
-
-       if (!device || !pcc)
-               return -EINVAL;
-
-       sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
-
-       backlight_device_unregister(pcc->backlight);
-
-       acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
-                                  acpi_pcc_hotkey_notify);
-
-       input_unregister_device(pcc->input_dev);
-       /* no need to input_free_device() since core input API refcount and
-        * free()s the device */
-
-       kfree(pcc->sinf);
-       kfree(pcc);
-
-       return 0;
-}
-
-static void __exit acpi_pcc_exit(void)
-{
-       ACPI_FUNCTION_TRACE("acpi_pcc_exit");
-
-       acpi_bus_unregister_driver(&acpi_pcc_driver);
-}
-
-module_init(acpi_pcc_init);
-module_exit(acpi_pcc_exit);
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c
deleted file mode 100644 (file)
index 571b211..0000000
+++ /dev/null
@@ -1,2781 +0,0 @@
-/*
- * ACPI Sony Notebook Control Driver (SNC and SPIC)
- *
- * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
- * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
- *
- * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
- * which are copyrighted by their respective authors.
- *
- * The SNY6001 driver part is based on the sonypi driver which includes
- * material from:
- *
- * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
- *
- * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
- *
- * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
- *
- * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
- *
- * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
- *
- * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
- *
- * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
- *
- * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/smp_lock.h>
-#include <linux/types.h>
-#include <linux/backlight.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/dmi.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/input.h>
-#include <linux/kfifo.h>
-#include <linux/workqueue.h>
-#include <linux/acpi.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <asm/uaccess.h>
-#include <linux/sonypi.h>
-#include <linux/sony-laptop.h>
-#ifdef CONFIG_SONYPI_COMPAT
-#include <linux/poll.h>
-#include <linux/miscdevice.h>
-#endif
-
-#define DRV_PFX                        "sony-laptop: "
-#define dprintk(msg...)                do {                    \
-       if (debug) printk(KERN_WARNING DRV_PFX  msg);   \
-} while (0)
-
-#define SONY_LAPTOP_DRIVER_VERSION     "0.6"
-
-#define SONY_NC_CLASS          "sony-nc"
-#define SONY_NC_HID            "SNY5001"
-#define SONY_NC_DRIVER_NAME    "Sony Notebook Control Driver"
-
-#define SONY_PIC_CLASS         "sony-pic"
-#define SONY_PIC_HID           "SNY6001"
-#define SONY_PIC_DRIVER_NAME   "Sony Programmable IO Control Driver"
-
-MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
-MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
-
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
-                "the development of this driver");
-
-static int no_spic;            /* = 0 */
-module_param(no_spic, int, 0444);
-MODULE_PARM_DESC(no_spic,
-                "set this if you don't want to enable the SPIC device");
-
-static int compat;             /* = 0 */
-module_param(compat, int, 0444);
-MODULE_PARM_DESC(compat,
-                "set this if you want to enable backward compatibility mode");
-
-static unsigned long mask = 0xffffffff;
-module_param(mask, ulong, 0644);
-MODULE_PARM_DESC(mask,
-                "set this to the mask of event you want to enable (see doc)");
-
-static int camera;             /* = 0 */
-module_param(camera, int, 0444);
-MODULE_PARM_DESC(camera,
-                "set this to 1 to enable Motion Eye camera controls "
-                "(only use it if you have a C1VE or C1VN model)");
-
-#ifdef CONFIG_SONYPI_COMPAT
-static int minor = -1;
-module_param(minor, int, 0);
-MODULE_PARM_DESC(minor,
-                "minor number of the misc device for the SPIC compatibility code, "
-                "default is -1 (automatic)");
-#endif
-
-/*********** Input Devices ***********/
-
-#define SONY_LAPTOP_BUF_SIZE   128
-struct sony_laptop_input_s {
-       atomic_t                users;
-       struct input_dev        *jog_dev;
-       struct input_dev        *key_dev;
-       struct kfifo            *fifo;
-       spinlock_t              fifo_lock;
-       struct workqueue_struct *wq;
-};
-static struct sony_laptop_input_s sony_laptop_input = {
-       .users = ATOMIC_INIT(0),
-};
-
-struct sony_laptop_keypress {
-       struct input_dev *dev;
-       int key;
-};
-
-/* Correspondance table between sonypi events
- * and input layer indexes in the keymap
- */
-static int sony_laptop_input_index[] = {
-       -1,     /*  0 no event */
-       -1,     /*  1 SONYPI_EVENT_JOGDIAL_DOWN */
-       -1,     /*  2 SONYPI_EVENT_JOGDIAL_UP */
-       -1,     /*  3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
-       -1,     /*  4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
-       -1,     /*  5 SONYPI_EVENT_JOGDIAL_PRESSED */
-       -1,     /*  6 SONYPI_EVENT_JOGDIAL_RELEASED */
-        0,     /*  7 SONYPI_EVENT_CAPTURE_PRESSED */
-        1,     /*  8 SONYPI_EVENT_CAPTURE_RELEASED */
-        2,     /*  9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
-        3,     /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
-        4,     /* 11 SONYPI_EVENT_FNKEY_ESC */
-        5,     /* 12 SONYPI_EVENT_FNKEY_F1 */
-        6,     /* 13 SONYPI_EVENT_FNKEY_F2 */
-        7,     /* 14 SONYPI_EVENT_FNKEY_F3 */
-        8,     /* 15 SONYPI_EVENT_FNKEY_F4 */
-        9,     /* 16 SONYPI_EVENT_FNKEY_F5 */
-       10,     /* 17 SONYPI_EVENT_FNKEY_F6 */
-       11,     /* 18 SONYPI_EVENT_FNKEY_F7 */
-       12,     /* 19 SONYPI_EVENT_FNKEY_F8 */
-       13,     /* 20 SONYPI_EVENT_FNKEY_F9 */
-       14,     /* 21 SONYPI_EVENT_FNKEY_F10 */
-       15,     /* 22 SONYPI_EVENT_FNKEY_F11 */
-       16,     /* 23 SONYPI_EVENT_FNKEY_F12 */
-       17,     /* 24 SONYPI_EVENT_FNKEY_1 */
-       18,     /* 25 SONYPI_EVENT_FNKEY_2 */
-       19,     /* 26 SONYPI_EVENT_FNKEY_D */
-       20,     /* 27 SONYPI_EVENT_FNKEY_E */
-       21,     /* 28 SONYPI_EVENT_FNKEY_F */
-       22,     /* 29 SONYPI_EVENT_FNKEY_S */
-       23,     /* 30 SONYPI_EVENT_FNKEY_B */
-       24,     /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
-       25,     /* 32 SONYPI_EVENT_PKEY_P1 */
-       26,     /* 33 SONYPI_EVENT_PKEY_P2 */
-       27,     /* 34 SONYPI_EVENT_PKEY_P3 */
-       28,     /* 35 SONYPI_EVENT_BACK_PRESSED */
-       -1,     /* 36 SONYPI_EVENT_LID_CLOSED */
-       -1,     /* 37 SONYPI_EVENT_LID_OPENED */
-       29,     /* 38 SONYPI_EVENT_BLUETOOTH_ON */
-       30,     /* 39 SONYPI_EVENT_BLUETOOTH_OFF */
-       31,     /* 40 SONYPI_EVENT_HELP_PRESSED */
-       32,     /* 41 SONYPI_EVENT_FNKEY_ONLY */
-       33,     /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
-       34,     /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
-       35,     /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
-       36,     /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
-       37,     /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
-       38,     /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
-       39,     /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
-       40,     /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
-       41,     /* 50 SONYPI_EVENT_ZOOM_PRESSED */
-       42,     /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
-       43,     /* 52 SONYPI_EVENT_MEYE_FACE */
-       44,     /* 53 SONYPI_EVENT_MEYE_OPPOSITE */
-       45,     /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
-       46,     /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
-       -1,     /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
-       -1,     /* 57 SONYPI_EVENT_BATTERY_INSERT */
-       -1,     /* 58 SONYPI_EVENT_BATTERY_REMOVE */
-       -1,     /* 59 SONYPI_EVENT_FNKEY_RELEASED */
-       47,     /* 60 SONYPI_EVENT_WIRELESS_ON */
-       48,     /* 61 SONYPI_EVENT_WIRELESS_OFF */
-       49,     /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
-       50,     /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
-};
-
-static int sony_laptop_input_keycode_map[] = {
-       KEY_CAMERA,     /*  0 SONYPI_EVENT_CAPTURE_PRESSED */
-       KEY_RESERVED,   /*  1 SONYPI_EVENT_CAPTURE_RELEASED */
-       KEY_RESERVED,   /*  2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
-       KEY_RESERVED,   /*  3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
-       KEY_FN_ESC,     /*  4 SONYPI_EVENT_FNKEY_ESC */
-       KEY_FN_F1,      /*  5 SONYPI_EVENT_FNKEY_F1 */
-       KEY_FN_F2,      /*  6 SONYPI_EVENT_FNKEY_F2 */
-       KEY_FN_F3,      /*  7 SONYPI_EVENT_FNKEY_F3 */
-       KEY_FN_F4,      /*  8 SONYPI_EVENT_FNKEY_F4 */
-       KEY_FN_F5,      /*  9 SONYPI_EVENT_FNKEY_F5 */
-       KEY_FN_F6,      /* 10 SONYPI_EVENT_FNKEY_F6 */
-       KEY_FN_F7,      /* 11 SONYPI_EVENT_FNKEY_F7 */
-       KEY_FN_F8,      /* 12 SONYPI_EVENT_FNKEY_F8 */
-       KEY_FN_F9,      /* 13 SONYPI_EVENT_FNKEY_F9 */
-       KEY_FN_F10,     /* 14 SONYPI_EVENT_FNKEY_F10 */
-       KEY_FN_F11,     /* 15 SONYPI_EVENT_FNKEY_F11 */
-       KEY_FN_F12,     /* 16 SONYPI_EVENT_FNKEY_F12 */
-       KEY_FN_F1,      /* 17 SONYPI_EVENT_FNKEY_1 */
-       KEY_FN_F2,      /* 18 SONYPI_EVENT_FNKEY_2 */
-       KEY_FN_D,       /* 19 SONYPI_EVENT_FNKEY_D */
-       KEY_FN_E,       /* 20 SONYPI_EVENT_FNKEY_E */
-       KEY_FN_F,       /* 21 SONYPI_EVENT_FNKEY_F */
-       KEY_FN_S,       /* 22 SONYPI_EVENT_FNKEY_S */
-       KEY_FN_B,       /* 23 SONYPI_EVENT_FNKEY_B */
-       KEY_BLUETOOTH,  /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
-       KEY_PROG1,      /* 25 SONYPI_EVENT_PKEY_P1 */
-       KEY_PROG2,      /* 26 SONYPI_EVENT_PKEY_P2 */
-       KEY_PROG3,      /* 27 SONYPI_EVENT_PKEY_P3 */
-       KEY_BACK,       /* 28 SONYPI_EVENT_BACK_PRESSED */
-       KEY_BLUETOOTH,  /* 29 SONYPI_EVENT_BLUETOOTH_ON */
-       KEY_BLUETOOTH,  /* 30 SONYPI_EVENT_BLUETOOTH_OFF */
-       KEY_HELP,       /* 31 SONYPI_EVENT_HELP_PRESSED */
-       KEY_FN,         /* 32 SONYPI_EVENT_FNKEY_ONLY */
-       KEY_RESERVED,   /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
-       KEY_RESERVED,   /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
-       KEY_RESERVED,   /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
-       KEY_RESERVED,   /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
-       KEY_RESERVED,   /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
-       KEY_RESERVED,   /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
-       KEY_RESERVED,   /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
-       KEY_RESERVED,   /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
-       KEY_ZOOM,       /* 41 SONYPI_EVENT_ZOOM_PRESSED */
-       BTN_THUMB,      /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
-       KEY_RESERVED,   /* 43 SONYPI_EVENT_MEYE_FACE */
-       KEY_RESERVED,   /* 44 SONYPI_EVENT_MEYE_OPPOSITE */
-       KEY_RESERVED,   /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
-       KEY_RESERVED,   /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
-       KEY_WLAN,       /* 47 SONYPI_EVENT_WIRELESS_ON */
-       KEY_WLAN,       /* 48 SONYPI_EVENT_WIRELESS_OFF */
-       KEY_ZOOMIN,     /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
-       KEY_ZOOMOUT     /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
-};
-
-/* release buttons after a short delay if pressed */
-static void do_sony_laptop_release_key(struct work_struct *work)
-{
-       struct sony_laptop_keypress kp;
-
-       while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
-                        sizeof(kp)) == sizeof(kp)) {
-               msleep(10);
-               input_report_key(kp.dev, kp.key, 0);
-               input_sync(kp.dev);
-       }
-}
-static DECLARE_WORK(sony_laptop_release_key_work,
-               do_sony_laptop_release_key);
-
-/* forward event to the input subsystem */
-static void sony_laptop_report_input_event(u8 event)
-{
-       struct input_dev *jog_dev = sony_laptop_input.jog_dev;
-       struct input_dev *key_dev = sony_laptop_input.key_dev;
-       struct sony_laptop_keypress kp = { NULL };
-
-       if (event == SONYPI_EVENT_FNKEY_RELEASED) {
-               /* Nothing, not all VAIOs generate this event */
-               return;
-       }
-
-       /* report events */
-       switch (event) {
-       /* jog_dev events */
-       case SONYPI_EVENT_JOGDIAL_UP:
-       case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
-               input_report_rel(jog_dev, REL_WHEEL, 1);
-               input_sync(jog_dev);
-               return;
-
-       case SONYPI_EVENT_JOGDIAL_DOWN:
-       case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
-               input_report_rel(jog_dev, REL_WHEEL, -1);
-               input_sync(jog_dev);
-               return;
-
-       /* key_dev events */
-       case SONYPI_EVENT_JOGDIAL_PRESSED:
-               kp.key = BTN_MIDDLE;
-               kp.dev = jog_dev;
-               break;
-
-       default:
-               if (event >= ARRAY_SIZE(sony_laptop_input_index)) {
-                       dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
-                       break;
-               }
-               if (sony_laptop_input_index[event] != -1) {
-                       kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
-                       if (kp.key != KEY_UNKNOWN)
-                               kp.dev = key_dev;
-               }
-               break;
-       }
-
-       if (kp.dev) {
-               input_report_key(kp.dev, kp.key, 1);
-               /* we emit the scancode so we can always remap the key */
-               input_event(kp.dev, EV_MSC, MSC_SCAN, event);
-               input_sync(kp.dev);
-               kfifo_put(sony_laptop_input.fifo,
-                         (unsigned char *)&kp, sizeof(kp));
-
-               if (!work_pending(&sony_laptop_release_key_work))
-                       queue_work(sony_laptop_input.wq,
-                                       &sony_laptop_release_key_work);
-       } else
-               dprintk("unknown input event %.2x\n", event);
-}
-
-static int sony_laptop_setup_input(struct acpi_device *acpi_device)
-{
-       struct input_dev *jog_dev;
-       struct input_dev *key_dev;
-       int i;
-       int error;
-
-       /* don't run again if already initialized */
-       if (atomic_add_return(1, &sony_laptop_input.users) > 1)
-               return 0;
-
-       /* kfifo */
-       spin_lock_init(&sony_laptop_input.fifo_lock);
-       sony_laptop_input.fifo =
-               kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
-                           &sony_laptop_input.fifo_lock);
-       if (IS_ERR(sony_laptop_input.fifo)) {
-               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
-               error = PTR_ERR(sony_laptop_input.fifo);
-               goto err_dec_users;
-       }
-
-       /* init workqueue */
-       sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
-       if (!sony_laptop_input.wq) {
-               printk(KERN_ERR DRV_PFX
-                               "Unabe to create workqueue.\n");
-               error = -ENXIO;
-               goto err_free_kfifo;
-       }
-
-       /* input keys */
-       key_dev = input_allocate_device();
-       if (!key_dev) {
-               error = -ENOMEM;
-               goto err_destroy_wq;
-       }
-
-       key_dev->name = "Sony Vaio Keys";
-       key_dev->id.bustype = BUS_ISA;
-       key_dev->id.vendor = PCI_VENDOR_ID_SONY;
-       key_dev->dev.parent = &acpi_device->dev;
-
-       /* Initialize the Input Drivers: special keys */
-       set_bit(EV_KEY, key_dev->evbit);
-       set_bit(EV_MSC, key_dev->evbit);
-       set_bit(MSC_SCAN, key_dev->mscbit);
-       key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
-       key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
-       key_dev->keycode = &sony_laptop_input_keycode_map;
-       for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
-               if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
-                       set_bit(sony_laptop_input_keycode_map[i],
-                               key_dev->keybit);
-               }
-       }
-
-       error = input_register_device(key_dev);
-       if (error)
-               goto err_free_keydev;
-
-       sony_laptop_input.key_dev = key_dev;
-
-       /* jogdial */
-       jog_dev = input_allocate_device();
-       if (!jog_dev) {
-               error = -ENOMEM;
-               goto err_unregister_keydev;
-       }
-
-       jog_dev->name = "Sony Vaio Jogdial";
-       jog_dev->id.bustype = BUS_ISA;
-       jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
-       key_dev->dev.parent = &acpi_device->dev;
-
-       jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
-       jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
-       jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
-
-       error = input_register_device(jog_dev);
-       if (error)
-               goto err_free_jogdev;
-
-       sony_laptop_input.jog_dev = jog_dev;
-
-       return 0;
-
-err_free_jogdev:
-       input_free_device(jog_dev);
-
-err_unregister_keydev:
-       input_unregister_device(key_dev);
-       /* to avoid kref underflow below at input_free_device */
-       key_dev = NULL;
-
-err_free_keydev:
-       input_free_device(key_dev);
-
-err_destroy_wq:
-       destroy_workqueue(sony_laptop_input.wq);
-
-err_free_kfifo:
-       kfifo_free(sony_laptop_input.fifo);
-
-err_dec_users:
-       atomic_dec(&sony_laptop_input.users);
-       return error;
-}
-
-static void sony_laptop_remove_input(void)
-{
-       /* cleanup only after the last user has gone */
-       if (!atomic_dec_and_test(&sony_laptop_input.users))
-               return;
-
-       /* flush workqueue first */
-       flush_workqueue(sony_laptop_input.wq);
-
-       /* destroy input devs */
-       input_unregister_device(sony_laptop_input.key_dev);
-       sony_laptop_input.key_dev = NULL;
-
-       if (sony_laptop_input.jog_dev) {
-               input_unregister_device(sony_laptop_input.jog_dev);
-               sony_laptop_input.jog_dev = NULL;
-       }
-
-       destroy_workqueue(sony_laptop_input.wq);
-       kfifo_free(sony_laptop_input.fifo);
-}
-
-/*********** Platform Device ***********/
-
-static atomic_t sony_pf_users = ATOMIC_INIT(0);
-static struct platform_driver sony_pf_driver = {
-       .driver = {
-                  .name = "sony-laptop",
-                  .owner = THIS_MODULE,
-                  }
-};
-static struct platform_device *sony_pf_device;
-
-static int sony_pf_add(void)
-{
-       int ret = 0;
-
-       /* don't run again if already initialized */
-       if (atomic_add_return(1, &sony_pf_users) > 1)
-               return 0;
-
-       ret = platform_driver_register(&sony_pf_driver);
-       if (ret)
-               goto out;
-
-       sony_pf_device = platform_device_alloc("sony-laptop", -1);
-       if (!sony_pf_device) {
-               ret = -ENOMEM;
-               goto out_platform_registered;
-       }
-
-       ret = platform_device_add(sony_pf_device);
-       if (ret)
-               goto out_platform_alloced;
-
-       return 0;
-
-      out_platform_alloced:
-       platform_device_put(sony_pf_device);
-       sony_pf_device = NULL;
-      out_platform_registered:
-       platform_driver_unregister(&sony_pf_driver);
-      out:
-       atomic_dec(&sony_pf_users);
-       return ret;
-}
-
-static void sony_pf_remove(void)
-{
-       /* deregister only after the last user has gone */
-       if (!atomic_dec_and_test(&sony_pf_users))
-               return;
-
-       platform_device_del(sony_pf_device);
-       platform_device_put(sony_pf_device);
-       platform_driver_unregister(&sony_pf_driver);
-}
-
-/*********** SNC (SNY5001) Device ***********/
-
-/* the device uses 1-based values, while the backlight subsystem uses
-   0-based values */
-#define SONY_MAX_BRIGHTNESS    8
-
-#define SNC_VALIDATE_IN                0
-#define SNC_VALIDATE_OUT       1
-
-static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
-                             char *);
-static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
-                              const char *, size_t);
-static int boolean_validate(const int, const int);
-static int brightness_default_validate(const int, const int);
-
-struct sony_nc_value {
-       char *name;             /* name of the entry */
-       char **acpiget;         /* names of the ACPI get function */
-       char **acpiset;         /* names of the ACPI set function */
-       int (*validate)(const int, const int);  /* input/output validation */
-       int value;              /* current setting */
-       int valid;              /* Has ever been set */
-       int debug;              /* active only in debug mode ? */
-       struct device_attribute devattr;        /* sysfs atribute */
-};
-
-#define SNC_HANDLE_NAMES(_name, _values...) \
-       static char *snc_##_name[] = { _values, NULL }
-
-#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
-       { \
-               .name           = __stringify(_name), \
-               .acpiget        = _getters, \
-               .acpiset        = _setters, \
-               .validate       = _validate, \
-               .debug          = _debug, \
-               .devattr        = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
-       }
-
-#define SNC_HANDLE_NULL        { .name = NULL }
-
-SNC_HANDLE_NAMES(fnkey_get, "GHKE");
-
-SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
-SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
-
-SNC_HANDLE_NAMES(cdpower_get, "GCDP");
-SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
-
-SNC_HANDLE_NAMES(audiopower_get, "GAZP");
-SNC_HANDLE_NAMES(audiopower_set, "AZPW");
-
-SNC_HANDLE_NAMES(lanpower_get, "GLNP");
-SNC_HANDLE_NAMES(lanpower_set, "LNPW");
-
-SNC_HANDLE_NAMES(lidstate_get, "GLID");
-
-SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
-SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
-
-SNC_HANDLE_NAMES(gainbass_get, "GMGB");
-SNC_HANDLE_NAMES(gainbass_set, "CMGB");
-
-SNC_HANDLE_NAMES(PID_get, "GPID");
-
-SNC_HANDLE_NAMES(CTR_get, "GCTR");
-SNC_HANDLE_NAMES(CTR_set, "SCTR");
-
-SNC_HANDLE_NAMES(PCR_get, "GPCR");
-SNC_HANDLE_NAMES(PCR_set, "SPCR");
-
-SNC_HANDLE_NAMES(CMI_get, "GCMI");
-SNC_HANDLE_NAMES(CMI_set, "SCMI");
-
-static struct sony_nc_value sony_nc_values[] = {
-       SNC_HANDLE(brightness_default, snc_brightness_def_get,
-                       snc_brightness_def_set, brightness_default_validate, 0),
-       SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
-       SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
-       SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
-                       boolean_validate, 0),
-       SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
-                       boolean_validate, 1),
-       SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
-                       boolean_validate, 0),
-       SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
-                       boolean_validate, 0),
-       SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
-                       boolean_validate, 0),
-       /* unknown methods */
-       SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
-       SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
-       SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
-       SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
-       SNC_HANDLE_NULL
-};
-
-static acpi_handle sony_nc_acpi_handle;
-static struct acpi_device *sony_nc_acpi_device = NULL;
-
-/*
- * acpi_evaluate_object wrappers
- */
-static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
-{
-       struct acpi_buffer output;
-       union acpi_object out_obj;
-       acpi_status status;
-
-       output.length = sizeof(out_obj);
-       output.pointer = &out_obj;
-
-       status = acpi_evaluate_object(handle, name, NULL, &output);
-       if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
-               *result = out_obj.integer.value;
-               return 0;
-       }
-
-       printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
-
-       return -1;
-}
-
-static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
-                           int *result)
-{
-       struct acpi_object_list params;
-       union acpi_object in_obj;
-       struct acpi_buffer output;
-       union acpi_object out_obj;
-       acpi_status status;
-
-       params.count = 1;
-       params.pointer = &in_obj;
-       in_obj.type = ACPI_TYPE_INTEGER;
-       in_obj.integer.value = value;
-
-       output.length = sizeof(out_obj);
-       output.pointer = &out_obj;
-
-       status = acpi_evaluate_object(handle, name, &params, &output);
-       if (status == AE_OK) {
-               if (result != NULL) {
-                       if (out_obj.type != ACPI_TYPE_INTEGER) {
-                               printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
-                                      "return type\n");
-                               return -1;
-                       }
-                       *result = out_obj.integer.value;
-               }
-               return 0;
-       }
-
-       printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
-
-       return -1;
-}
-
-/*
- * sony_nc_values input/output validate functions
- */
-
-/* brightness_default_validate:
- *
- * manipulate input output values to keep consistency with the
- * backlight framework for which brightness values are 0-based.
- */
-static int brightness_default_validate(const int direction, const int value)
-{
-       switch (direction) {
-               case SNC_VALIDATE_OUT:
-                       return value - 1;
-               case SNC_VALIDATE_IN:
-                       if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
-                               return value + 1;
-       }
-       return -EINVAL;
-}
-
-/* boolean_validate:
- *
- * on input validate boolean values 0/1, on output just pass the
- * received value.
- */
-static int boolean_validate(const int direction, const int value)
-{
-       if (direction == SNC_VALIDATE_IN) {
-               if (value != 0 && value != 1)
-                       return -EINVAL;
-       }
-       return value;
-}
-
-/*
- * Sysfs show/store common to all sony_nc_values
- */
-static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
-                             char *buffer)
-{
-       int value;
-       struct sony_nc_value *item =
-           container_of(attr, struct sony_nc_value, devattr);
-
-       if (!*item->acpiget)
-               return -EIO;
-
-       if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
-               return -EIO;
-
-       if (item->validate)
-               value = item->validate(SNC_VALIDATE_OUT, value);
-
-       return snprintf(buffer, PAGE_SIZE, "%d\n", value);
-}
-
-static ssize_t sony_nc_sysfs_store(struct device *dev,
-                              struct device_attribute *attr,
-                              const char *buffer, size_t count)
-{
-       int value;
-       struct sony_nc_value *item =
-           container_of(attr, struct sony_nc_value, devattr);
-
-       if (!item->acpiset)
-               return -EIO;
-
-       if (count > 31)
-               return -EINVAL;
-
-       value = simple_strtoul(buffer, NULL, 10);
-
-       if (item->validate)
-               value = item->validate(SNC_VALIDATE_IN, value);
-
-       if (value < 0)
-               return value;
-
-       if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
-               return -EIO;
-       item->value = value;
-       item->valid = 1;
-       return count;
-}
-
-
-/*
- * Backlight device
- */
-static int sony_backlight_update_status(struct backlight_device *bd)
-{
-       return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
-                               bd->props.brightness + 1, NULL);
-}
-
-static int sony_backlight_get_brightness(struct backlight_device *bd)
-{
-       int value;
-
-       if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
-               return 0;
-       /* brightness levels are 1-based, while backlight ones are 0-based */
-       return value - 1;
-}
-
-static struct backlight_device *sony_backlight_device;
-static struct backlight_ops sony_backlight_ops = {
-       .update_status = sony_backlight_update_status,
-       .get_brightness = sony_backlight_get_brightness,
-};
-
-/*
- * New SNC-only Vaios event mapping to driver known keys
- */
-struct sony_nc_event {
-       u8      data;
-       u8      event;
-};
-
-static struct sony_nc_event *sony_nc_events;
-
-/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
- * for Fn keys
- */
-static int sony_nc_C_enable(const struct dmi_system_id *id)
-{
-       int result = 0;
-
-       printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
-
-       sony_nc_events = id->driver_data;
-
-       if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
-                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
-                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
-                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
-                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
-                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
-               printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
-                               "functionalities may be missing\n");
-               return 1;
-       }
-       return 0;
-}
-
-static struct sony_nc_event sony_C_events[] = {
-       { 0x81, SONYPI_EVENT_FNKEY_F1 },
-       { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x85, SONYPI_EVENT_FNKEY_F5 },
-       { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x86, SONYPI_EVENT_FNKEY_F6 },
-       { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x87, SONYPI_EVENT_FNKEY_F7 },
-       { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x8A, SONYPI_EVENT_FNKEY_F10 },
-       { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x8C, SONYPI_EVENT_FNKEY_F12 },
-       { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0, 0 },
-};
-
-/* SNC-only model map */
-static const struct dmi_system_id sony_nc_ids[] = {
-               {
-                       .ident = "Sony Vaio FE Series",
-                       .callback = sony_nc_C_enable,
-                       .driver_data = sony_C_events,
-                       .matches = {
-                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
-                       },
-               },
-               {
-                       .ident = "Sony Vaio FZ Series",
-                       .callback = sony_nc_C_enable,
-                       .driver_data = sony_C_events,
-                       .matches = {
-                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"),
-                       },
-               },
-               {
-                       .ident = "Sony Vaio C Series",
-                       .callback = sony_nc_C_enable,
-                       .driver_data = sony_C_events,
-                       .matches = {
-                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
-                       },
-               },
-               {
-                       .ident = "Sony Vaio N Series",
-                       .callback = sony_nc_C_enable,
-                       .driver_data = sony_C_events,
-                       .matches = {
-                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"),
-                       },
-               },
-               { }
-};
-
-/*
- * ACPI callbacks
- */
-static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
-{
-       struct sony_nc_event *evmap;
-       u32 ev = event;
-       int result;
-
-       if (ev == 0x92) {
-               /* read the key pressed from EC.GECR
-                * A call to SN07 with 0x0202 will do it as well respecting
-                * the current protocol on different OSes
-                *
-                * Note: the path for GECR may be
-                *   \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
-                *   \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
-                *
-                * TODO: we may want to do the same for the older GHKE -need
-                *       dmi list- so this snippet may become one more callback.
-                */
-               if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
-                       dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
-               else
-                       ev = result & 0xFF;
-       }
-
-       if (sony_nc_events)
-               for (evmap = sony_nc_events; evmap->event; evmap++) {
-                       if (evmap->data == ev) {
-                               ev = evmap->event;
-                               break;
-                       }
-               }
-
-       dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
-       sony_laptop_report_input_event(ev);
-       acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
-}
-
-static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
-                                     void *context, void **return_value)
-{
-       struct acpi_namespace_node *node;
-       union acpi_operand_object *operand;
-
-       node = (struct acpi_namespace_node *)handle;
-       operand = (union acpi_operand_object *)node->object;
-
-       printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
-              (u32) operand->method.param_count);
-
-       return AE_OK;
-}
-
-/*
- * ACPI device
- */
-static int sony_nc_resume(struct acpi_device *device)
-{
-       struct sony_nc_value *item;
-
-       for (item = sony_nc_values; item->name; item++) {
-               int ret;
-
-               if (!item->valid)
-                       continue;
-               ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
-                                      item->value, NULL);
-               if (ret < 0) {
-                       printk("%s: %d\n", __func__, ret);
-                       break;
-               }
-       }
-
-       /* set the last requested brightness level */
-       if (sony_backlight_device &&
-                       !sony_backlight_update_status(sony_backlight_device))
-               printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
-
-       /* re-initialize models with specific requirements */
-       dmi_check_system(sony_nc_ids);
-
-       return 0;
-}
-
-static int sony_nc_add(struct acpi_device *device)
-{
-       acpi_status status;
-       int result = 0;
-       acpi_handle handle;
-       struct sony_nc_value *item;
-
-       printk(KERN_INFO DRV_PFX "%s v%s.\n",
-               SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
-       sony_nc_acpi_device = device;
-       strcpy(acpi_device_class(device), "sony/hotkey");
-
-       sony_nc_acpi_handle = device->handle;
-
-       /* read device status */
-       result = acpi_bus_get_status(device);
-       /* bail IFF the above call was successful and the device is not present */
-       if (!result && !device->status.present) {
-               dprintk("Device not present\n");
-               result = -ENODEV;
-               goto outwalk;
-       }
-
-       if (debug) {
-               status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
-                                            1, sony_walk_callback, NULL, NULL);
-               if (ACPI_FAILURE(status)) {
-                       printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
-                       result = -ENODEV;
-                       goto outwalk;
-               }
-       }
-
-       /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
-        * should be respected as we already checked for the device presence above */
-       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
-               dprintk("Invoking _INI\n");
-               if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
-                                               NULL, NULL)))
-                       dprintk("_INI Method failed\n");
-       }
-
-       /* setup input devices and helper fifo */
-       result = sony_laptop_setup_input(device);
-       if (result) {
-               printk(KERN_ERR DRV_PFX
-                               "Unabe to create input devices.\n");
-               goto outwalk;
-       }
-
-       status = acpi_install_notify_handler(sony_nc_acpi_handle,
-                                            ACPI_DEVICE_NOTIFY,
-                                            sony_acpi_notify, NULL);
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
-               result = -ENODEV;
-               goto outinput;
-       }
-
-       if (acpi_video_backlight_support()) {
-               printk(KERN_INFO DRV_PFX "brightness ignored, must be "
-                      "controlled by ACPI video driver\n");
-       } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
-                                               &handle))) {
-               sony_backlight_device = backlight_device_register("sony", NULL,
-                                                                 NULL,
-                                                                 &sony_backlight_ops);
-
-               if (IS_ERR(sony_backlight_device)) {
-                       printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
-                       sony_backlight_device = NULL;
-               } else {
-                       sony_backlight_device->props.brightness =
-                           sony_backlight_get_brightness
-                           (sony_backlight_device);
-                       sony_backlight_device->props.max_brightness =
-                           SONY_MAX_BRIGHTNESS - 1;
-               }
-
-       }
-
-       /* initialize models with specific requirements */
-       dmi_check_system(sony_nc_ids);
-
-       result = sony_pf_add();
-       if (result)
-               goto outbacklight;
-
-       /* create sony_pf sysfs attributes related to the SNC device */
-       for (item = sony_nc_values; item->name; ++item) {
-
-               if (!debug && item->debug)
-                       continue;
-
-               /* find the available acpiget as described in the DSDT */
-               for (; item->acpiget && *item->acpiget; ++item->acpiget) {
-                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
-                                                        *item->acpiget,
-                                                        &handle))) {
-                               dprintk("Found %s getter: %s\n",
-                                               item->name, *item->acpiget);
-                               item->devattr.attr.mode |= S_IRUGO;
-                               break;
-                       }
-               }
-
-               /* find the available acpiset as described in the DSDT */
-               for (; item->acpiset && *item->acpiset; ++item->acpiset) {
-                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
-                                                        *item->acpiset,
-                                                        &handle))) {
-                               dprintk("Found %s setter: %s\n",
-                                               item->name, *item->acpiset);
-                               item->devattr.attr.mode |= S_IWUSR;
-                               break;
-                       }
-               }
-
-               if (item->devattr.attr.mode != 0) {
-                       result =
-                           device_create_file(&sony_pf_device->dev,
-                                              &item->devattr);
-                       if (result)
-                               goto out_sysfs;
-               }
-       }
-
-       return 0;
-
-      out_sysfs:
-       for (item = sony_nc_values; item->name; ++item) {
-               device_remove_file(&sony_pf_device->dev, &item->devattr);
-       }
-       sony_pf_remove();
-
-      outbacklight:
-       if (sony_backlight_device)
-               backlight_device_unregister(sony_backlight_device);
-
-       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           sony_acpi_notify);
-       if (ACPI_FAILURE(status))
-               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
-
-      outinput:
-       sony_laptop_remove_input();
-
-      outwalk:
-       return result;
-}
-
-static int sony_nc_remove(struct acpi_device *device, int type)
-{
-       acpi_status status;
-       struct sony_nc_value *item;
-
-       if (sony_backlight_device)
-               backlight_device_unregister(sony_backlight_device);
-
-       sony_nc_acpi_device = NULL;
-
-       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           sony_acpi_notify);
-       if (ACPI_FAILURE(status))
-               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
-
-       for (item = sony_nc_values; item->name; ++item) {
-               device_remove_file(&sony_pf_device->dev, &item->devattr);
-       }
-
-       sony_pf_remove();
-       sony_laptop_remove_input();
-       dprintk(SONY_NC_DRIVER_NAME " removed.\n");
-
-       return 0;
-}
-
-static const struct acpi_device_id sony_device_ids[] = {
-       {SONY_NC_HID, 0},
-       {SONY_PIC_HID, 0},
-       {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, sony_device_ids);
-
-static const struct acpi_device_id sony_nc_device_ids[] = {
-       {SONY_NC_HID, 0},
-       {"", 0},
-};
-
-static struct acpi_driver sony_nc_driver = {
-       .name = SONY_NC_DRIVER_NAME,
-       .class = SONY_NC_CLASS,
-       .ids = sony_nc_device_ids,
-       .owner = THIS_MODULE,
-       .ops = {
-               .add = sony_nc_add,
-               .remove = sony_nc_remove,
-               .resume = sony_nc_resume,
-               },
-};
-
-/*********** SPIC (SNY6001) Device ***********/
-
-#define SONYPI_DEVICE_TYPE1    0x00000001
-#define SONYPI_DEVICE_TYPE2    0x00000002
-#define SONYPI_DEVICE_TYPE3    0x00000004
-#define SONYPI_DEVICE_TYPE4    0x00000008
-
-#define SONYPI_TYPE1_OFFSET    0x04
-#define SONYPI_TYPE2_OFFSET    0x12
-#define SONYPI_TYPE3_OFFSET    0x12
-#define SONYPI_TYPE4_OFFSET    0x12
-
-struct sony_pic_ioport {
-       struct acpi_resource_io io1;
-       struct acpi_resource_io io2;
-       struct list_head        list;
-};
-
-struct sony_pic_irq {
-       struct acpi_resource_irq        irq;
-       struct list_head                list;
-};
-
-struct sonypi_eventtypes {
-       u8                      data;
-       unsigned long           mask;
-       struct sonypi_event     *events;
-};
-
-struct device_ctrl {
-       int                             model;
-       int                             (*handle_irq)(const u8, const u8);
-       u16                             evport_offset;
-       u8                              has_camera;
-       u8                              has_bluetooth;
-       u8                              has_wwan;
-       struct sonypi_eventtypes        *event_types;
-};
-
-struct sony_pic_dev {
-       struct device_ctrl      *control;
-       struct acpi_device      *acpi_dev;
-       struct sony_pic_irq     *cur_irq;
-       struct sony_pic_ioport  *cur_ioport;
-       struct list_head        interrupts;
-       struct list_head        ioports;
-       struct mutex            lock;
-       u8                      camera_power;
-       u8                      bluetooth_power;
-       u8                      wwan_power;
-};
-
-static struct sony_pic_dev spic_dev = {
-       .interrupts     = LIST_HEAD_INIT(spic_dev.interrupts),
-       .ioports        = LIST_HEAD_INIT(spic_dev.ioports),
-};
-
-/* Event masks */
-#define SONYPI_JOGGER_MASK                     0x00000001
-#define SONYPI_CAPTURE_MASK                    0x00000002
-#define SONYPI_FNKEY_MASK                      0x00000004
-#define SONYPI_BLUETOOTH_MASK                  0x00000008
-#define SONYPI_PKEY_MASK                       0x00000010
-#define SONYPI_BACK_MASK                       0x00000020
-#define SONYPI_HELP_MASK                       0x00000040
-#define SONYPI_LID_MASK                                0x00000080
-#define SONYPI_ZOOM_MASK                       0x00000100
-#define SONYPI_THUMBPHRASE_MASK                        0x00000200
-#define SONYPI_MEYE_MASK                       0x00000400
-#define SONYPI_MEMORYSTICK_MASK                        0x00000800
-#define SONYPI_BATTERY_MASK                    0x00001000
-#define SONYPI_WIRELESS_MASK                   0x00002000
-
-struct sonypi_event {
-       u8      data;
-       u8      event;
-};
-
-/* The set of possible button release events */
-static struct sonypi_event sonypi_releaseev[] = {
-       { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
-       { 0, 0 }
-};
-
-/* The set of possible jogger events  */
-static struct sonypi_event sonypi_joggerev[] = {
-       { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
-       { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
-       { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
-       { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
-       { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
-       { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
-       { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
-       { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
-       { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
-       { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
-       { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
-       { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
-       { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
-       { 0, 0 }
-};
-
-/* The set of possible capture button events */
-static struct sonypi_event sonypi_captureev[] = {
-       { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
-       { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
-       { 0x40, SONYPI_EVENT_CAPTURE_PRESSED },
-       { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
-       { 0, 0 }
-};
-
-/* The set of possible fnkeys events */
-static struct sonypi_event sonypi_fnkeyev[] = {
-       { 0x10, SONYPI_EVENT_FNKEY_ESC },
-       { 0x11, SONYPI_EVENT_FNKEY_F1 },
-       { 0x12, SONYPI_EVENT_FNKEY_F2 },
-       { 0x13, SONYPI_EVENT_FNKEY_F3 },
-       { 0x14, SONYPI_EVENT_FNKEY_F4 },
-       { 0x15, SONYPI_EVENT_FNKEY_F5 },
-       { 0x16, SONYPI_EVENT_FNKEY_F6 },
-       { 0x17, SONYPI_EVENT_FNKEY_F7 },
-       { 0x18, SONYPI_EVENT_FNKEY_F8 },
-       { 0x19, SONYPI_EVENT_FNKEY_F9 },
-       { 0x1a, SONYPI_EVENT_FNKEY_F10 },
-       { 0x1b, SONYPI_EVENT_FNKEY_F11 },
-       { 0x1c, SONYPI_EVENT_FNKEY_F12 },
-       { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
-       { 0x21, SONYPI_EVENT_FNKEY_1 },
-       { 0x22, SONYPI_EVENT_FNKEY_2 },
-       { 0x31, SONYPI_EVENT_FNKEY_D },
-       { 0x32, SONYPI_EVENT_FNKEY_E },
-       { 0x33, SONYPI_EVENT_FNKEY_F },
-       { 0x34, SONYPI_EVENT_FNKEY_S },
-       { 0x35, SONYPI_EVENT_FNKEY_B },
-       { 0x36, SONYPI_EVENT_FNKEY_ONLY },
-       { 0, 0 }
-};
-
-/* The set of possible program key events */
-static struct sonypi_event sonypi_pkeyev[] = {
-       { 0x01, SONYPI_EVENT_PKEY_P1 },
-       { 0x02, SONYPI_EVENT_PKEY_P2 },
-       { 0x04, SONYPI_EVENT_PKEY_P3 },
-       { 0, 0 }
-};
-
-/* The set of possible bluetooth events */
-static struct sonypi_event sonypi_blueev[] = {
-       { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
-       { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
-       { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
-       { 0, 0 }
-};
-
-/* The set of possible wireless events */
-static struct sonypi_event sonypi_wlessev[] = {
-       { 0x59, SONYPI_EVENT_WIRELESS_ON },
-       { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
-       { 0, 0 }
-};
-
-/* The set of possible back button events */
-static struct sonypi_event sonypi_backev[] = {
-       { 0x20, SONYPI_EVENT_BACK_PRESSED },
-       { 0, 0 }
-};
-
-/* The set of possible help button events */
-static struct sonypi_event sonypi_helpev[] = {
-       { 0x3b, SONYPI_EVENT_HELP_PRESSED },
-       { 0, 0 }
-};
-
-
-/* The set of possible lid events */
-static struct sonypi_event sonypi_lidev[] = {
-       { 0x51, SONYPI_EVENT_LID_CLOSED },
-       { 0x50, SONYPI_EVENT_LID_OPENED },
-       { 0, 0 }
-};
-
-/* The set of possible zoom events */
-static struct sonypi_event sonypi_zoomev[] = {
-       { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
-       { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
-       { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
-       { 0, 0 }
-};
-
-/* The set of possible thumbphrase events */
-static struct sonypi_event sonypi_thumbphraseev[] = {
-       { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
-       { 0, 0 }
-};
-
-/* The set of possible motioneye camera events */
-static struct sonypi_event sonypi_meyeev[] = {
-       { 0x00, SONYPI_EVENT_MEYE_FACE },
-       { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
-       { 0, 0 }
-};
-
-/* The set of possible memorystick events */
-static struct sonypi_event sonypi_memorystickev[] = {
-       { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
-       { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
-       { 0, 0 }
-};
-
-/* The set of possible battery events */
-static struct sonypi_event sonypi_batteryev[] = {
-       { 0x20, SONYPI_EVENT_BATTERY_INSERT },
-       { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
-       { 0, 0 }
-};
-
-static struct sonypi_eventtypes type1_events[] = {
-       { 0, 0xffffffff, sonypi_releaseev },
-       { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
-       { 0x30, SONYPI_LID_MASK, sonypi_lidev },
-       { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
-       { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
-       { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
-       { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
-       { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
-       { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
-       { 0 },
-};
-static struct sonypi_eventtypes type2_events[] = {
-       { 0, 0xffffffff, sonypi_releaseev },
-       { 0x38, SONYPI_LID_MASK, sonypi_lidev },
-       { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
-       { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
-       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
-       { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
-       { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0x11, SONYPI_BACK_MASK, sonypi_backev },
-       { 0x21, SONYPI_HELP_MASK, sonypi_helpev },
-       { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
-       { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
-       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
-       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
-       { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0 },
-};
-static struct sonypi_eventtypes type3_events[] = {
-       { 0, 0xffffffff, sonypi_releaseev },
-       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
-       { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
-       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
-       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
-       { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0 },
-};
-static struct sonypi_eventtypes type4_events[] = {
-       { 0, 0xffffffff, sonypi_releaseev },
-       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
-       { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
-       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
-       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
-       { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },
-       { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },
-       { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },
-       { 0 },
-};
-
-/* low level spic calls */
-#define ITERATIONS_LONG                10000
-#define ITERATIONS_SHORT       10
-#define wait_on_command(command, iterations) {                         \
-       unsigned int n = iterations;                                    \
-       while (--n && (command))                                        \
-               udelay(1);                                              \
-       if (!n)                                                         \
-               dprintk("command failed at %s : %s (line %d)\n",        \
-                               __FILE__, __func__, __LINE__);  \
-}
-
-static u8 sony_pic_call1(u8 dev)
-{
-       u8 v1, v2;
-
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
-                       ITERATIONS_LONG);
-       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
-       v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);
-       v2 = inb_p(spic_dev.cur_ioport->io1.minimum);
-       dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1);
-       return v2;
-}
-
-static u8 sony_pic_call2(u8 dev, u8 fn)
-{
-       u8 v1;
-
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
-                       ITERATIONS_LONG);
-       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
-                       ITERATIONS_LONG);
-       outb(fn, spic_dev.cur_ioport->io1.minimum);
-       v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
-       dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1);
-       return v1;
-}
-
-static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
-{
-       u8 v1;
-
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
-       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
-       outb(fn, spic_dev.cur_ioport->io1.minimum);
-       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
-       outb(v, spic_dev.cur_ioport->io1.minimum);
-       v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
-       dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n",
-                       dev, fn, v, v1);
-       return v1;
-}
-
-/*
- * minidrivers for SPIC models
- */
-static int type4_handle_irq(const u8 data_mask, const u8 ev)
-{
-       /*
-        * 0x31 could mean we have to take some extra action and wait for
-        * the next irq for some Type4 models, it will generate a new
-        * irq and we can read new data from the device:
-        *  - 0x5c and 0x5f requires 0xA0
-        *  - 0x61 requires 0xB3
-        */
-       if (data_mask == 0x31) {
-               if (ev == 0x5c || ev == 0x5f)
-                       sony_pic_call1(0xA0);
-               else if (ev == 0x61)
-                       sony_pic_call1(0xB3);
-               return 0;
-       }
-       return 1;
-}
-
-static struct device_ctrl spic_types[] = {
-       {
-               .model = SONYPI_DEVICE_TYPE1,
-               .handle_irq = NULL,
-               .evport_offset = SONYPI_TYPE1_OFFSET,
-               .event_types = type1_events,
-       },
-       {
-               .model = SONYPI_DEVICE_TYPE2,
-               .handle_irq = NULL,
-               .evport_offset = SONYPI_TYPE2_OFFSET,
-               .event_types = type2_events,
-       },
-       {
-               .model = SONYPI_DEVICE_TYPE3,
-               .handle_irq = NULL,
-               .evport_offset = SONYPI_TYPE3_OFFSET,
-               .event_types = type3_events,
-       },
-       {
-               .model = SONYPI_DEVICE_TYPE4,
-               .handle_irq = type4_handle_irq,
-               .evport_offset = SONYPI_TYPE4_OFFSET,
-               .event_types = type4_events,
-       },
-};
-
-static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
-{
-       struct pci_dev *pcidev;
-
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                       PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
-       if (pcidev) {
-               dev->control = &spic_types[0];
-               goto out;
-       }
-
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                       PCI_DEVICE_ID_INTEL_ICH6_1, NULL);
-       if (pcidev) {
-               dev->control = &spic_types[2];
-               goto out;
-       }
-
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                       PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
-       if (pcidev) {
-               dev->control = &spic_types[3];
-               goto out;
-       }
-
-       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
-                       PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
-       if (pcidev) {
-               dev->control = &spic_types[3];
-               goto out;
-       }
-
-       /* default */
-       dev->control = &spic_types[1];
-
-out:
-       if (pcidev)
-               pci_dev_put(pcidev);
-
-       printk(KERN_INFO DRV_PFX "detected Type%d model\n",
-                       dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 :
-                       dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 :
-                       dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4);
-}
-
-/* camera tests and poweron/poweroff */
-#define SONYPI_CAMERA_PICTURE          5
-#define SONYPI_CAMERA_CONTROL          0x10
-
-#define SONYPI_CAMERA_BRIGHTNESS               0
-#define SONYPI_CAMERA_CONTRAST                 1
-#define SONYPI_CAMERA_HUE                      2
-#define SONYPI_CAMERA_COLOR                    3
-#define SONYPI_CAMERA_SHARPNESS                        4
-
-#define SONYPI_CAMERA_EXPOSURE_MASK            0xC
-#define SONYPI_CAMERA_WHITE_BALANCE_MASK       0x3
-#define SONYPI_CAMERA_PICTURE_MODE_MASK                0x30
-#define SONYPI_CAMERA_MUTE_MASK                        0x40
-
-/* the rest don't need a loop until not 0xff */
-#define SONYPI_CAMERA_AGC                      6
-#define SONYPI_CAMERA_AGC_MASK                 0x30
-#define SONYPI_CAMERA_SHUTTER_MASK             0x7
-
-#define SONYPI_CAMERA_SHUTDOWN_REQUEST         7
-#define SONYPI_CAMERA_CONTROL                  0x10
-
-#define SONYPI_CAMERA_STATUS                   7
-#define SONYPI_CAMERA_STATUS_READY             0x2
-#define SONYPI_CAMERA_STATUS_POSITION          0x4
-
-#define SONYPI_DIRECTION_BACKWARDS             0x4
-
-#define SONYPI_CAMERA_REVISION                         8
-#define SONYPI_CAMERA_ROMVERSION               9
-
-static int __sony_pic_camera_ready(void)
-{
-       u8 v;
-
-       v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
-       return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
-}
-
-static int __sony_pic_camera_off(void)
-{
-       if (!camera) {
-               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
-               return -ENODEV;
-       }
-
-       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
-                               SONYPI_CAMERA_MUTE_MASK),
-                       ITERATIONS_SHORT);
-
-       if (spic_dev.camera_power) {
-               sony_pic_call2(0x91, 0);
-               spic_dev.camera_power = 0;
-       }
-       return 0;
-}
-
-static int __sony_pic_camera_on(void)
-{
-       int i, j, x;
-
-       if (!camera) {
-               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
-               return -ENODEV;
-       }
-
-       if (spic_dev.camera_power)
-               return 0;
-
-       for (j = 5; j > 0; j--) {
-
-               for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
-                       msleep(10);
-               sony_pic_call1(0x93);
-
-               for (i = 400; i > 0; i--) {
-                       if (__sony_pic_camera_ready())
-                               break;
-                       msleep(10);
-               }
-               if (i)
-                       break;
-       }
-
-       if (j == 0) {
-               printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
-               return -ENODEV;
-       }
-
-       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
-                               0x5a),
-                       ITERATIONS_SHORT);
-
-       spic_dev.camera_power = 1;
-       return 0;
-}
-
-/* External camera command (exported to the motion eye v4l driver) */
-int sony_pic_camera_command(int command, u8 value)
-{
-       if (!camera)
-               return -EIO;
-
-       mutex_lock(&spic_dev.lock);
-
-       switch (command) {
-       case SONY_PIC_COMMAND_SETCAMERA:
-               if (value)
-                       __sony_pic_camera_on();
-               else
-                       __sony_pic_camera_off();
-               break;
-       case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERACONTRAST:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERAHUE:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERACOLOR:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERAPICTURE:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
-                               ITERATIONS_SHORT);
-               break;
-       case SONY_PIC_COMMAND_SETCAMERAAGC:
-               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
-                               ITERATIONS_SHORT);
-               break;
-       default:
-               printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
-                      command);
-               break;
-       }
-       mutex_unlock(&spic_dev.lock);
-       return 0;
-}
-EXPORT_SYMBOL(sony_pic_camera_command);
-
-/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
-static void sony_pic_set_wwanpower(u8 state)
-{
-       state = !!state;
-       mutex_lock(&spic_dev.lock);
-       if (spic_dev.wwan_power == state) {
-               mutex_unlock(&spic_dev.lock);
-               return;
-       }
-       sony_pic_call2(0xB0, state);
-       spic_dev.wwan_power = state;
-       mutex_unlock(&spic_dev.lock);
-}
-
-static ssize_t sony_pic_wwanpower_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buffer, size_t count)
-{
-       unsigned long value;
-       if (count > 31)
-               return -EINVAL;
-
-       value = simple_strtoul(buffer, NULL, 10);
-       sony_pic_set_wwanpower(value);
-
-       return count;
-}
-
-static ssize_t sony_pic_wwanpower_show(struct device *dev,
-               struct device_attribute *attr, char *buffer)
-{
-       ssize_t count;
-       mutex_lock(&spic_dev.lock);
-       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
-       mutex_unlock(&spic_dev.lock);
-       return count;
-}
-
-/* bluetooth subsystem power state */
-static void __sony_pic_set_bluetoothpower(u8 state)
-{
-       state = !!state;
-       if (spic_dev.bluetooth_power == state)
-               return;
-       sony_pic_call2(0x96, state);
-       sony_pic_call1(0x82);
-       spic_dev.bluetooth_power = state;
-}
-
-static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buffer, size_t count)
-{
-       unsigned long value;
-       if (count > 31)
-               return -EINVAL;
-
-       value = simple_strtoul(buffer, NULL, 10);
-       mutex_lock(&spic_dev.lock);
-       __sony_pic_set_bluetoothpower(value);
-       mutex_unlock(&spic_dev.lock);
-
-       return count;
-}
-
-static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
-               struct device_attribute *attr, char *buffer)
-{
-       ssize_t count = 0;
-       mutex_lock(&spic_dev.lock);
-       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
-       mutex_unlock(&spic_dev.lock);
-       return count;
-}
-
-/* fan speed */
-/* FAN0 information (reverse engineered from ACPI tables) */
-#define SONY_PIC_FAN0_STATUS   0x93
-static int sony_pic_set_fanspeed(unsigned long value)
-{
-       return ec_write(SONY_PIC_FAN0_STATUS, value);
-}
-
-static int sony_pic_get_fanspeed(u8 *value)
-{
-       return ec_read(SONY_PIC_FAN0_STATUS, value);
-}
-
-static ssize_t sony_pic_fanspeed_store(struct device *dev,
-               struct device_attribute *attr,
-               const char *buffer, size_t count)
-{
-       unsigned long value;
-       if (count > 31)
-               return -EINVAL;
-
-       value = simple_strtoul(buffer, NULL, 10);
-       if (sony_pic_set_fanspeed(value))
-               return -EIO;
-
-       return count;
-}
-
-static ssize_t sony_pic_fanspeed_show(struct device *dev,
-               struct device_attribute *attr, char *buffer)
-{
-       u8 value = 0;
-       if (sony_pic_get_fanspeed(&value))
-               return -EIO;
-
-       return snprintf(buffer, PAGE_SIZE, "%d\n", value);
-}
-
-#define SPIC_ATTR(_name, _mode)                                        \
-struct device_attribute spic_attr_##_name = __ATTR(_name,      \
-               _mode, sony_pic_## _name ##_show,               \
-               sony_pic_## _name ##_store)
-
-static SPIC_ATTR(bluetoothpower, 0644);
-static SPIC_ATTR(wwanpower, 0644);
-static SPIC_ATTR(fanspeed, 0644);
-
-static struct attribute *spic_attributes[] = {
-       &spic_attr_bluetoothpower.attr,
-       &spic_attr_wwanpower.attr,
-       &spic_attr_fanspeed.attr,
-       NULL
-};
-
-static struct attribute_group spic_attribute_group = {
-       .attrs = spic_attributes
-};
-
-/******** SONYPI compatibility **********/
-#ifdef CONFIG_SONYPI_COMPAT
-
-/* battery / brightness / temperature  addresses */
-#define SONYPI_BAT_FLAGS       0x81
-#define SONYPI_LCD_LIGHT       0x96
-#define SONYPI_BAT1_PCTRM      0xa0
-#define SONYPI_BAT1_LEFT       0xa2
-#define SONYPI_BAT1_MAXRT      0xa4
-#define SONYPI_BAT2_PCTRM      0xa8
-#define SONYPI_BAT2_LEFT       0xaa
-#define SONYPI_BAT2_MAXRT      0xac
-#define SONYPI_BAT1_MAXTK      0xb0
-#define SONYPI_BAT1_FULL       0xb2
-#define SONYPI_BAT2_MAXTK      0xb8
-#define SONYPI_BAT2_FULL       0xba
-#define SONYPI_TEMP_STATUS     0xC1
-
-struct sonypi_compat_s {
-       struct fasync_struct    *fifo_async;
-       struct kfifo            *fifo;
-       spinlock_t              fifo_lock;
-       wait_queue_head_t       fifo_proc_list;
-       atomic_t                open_count;
-};
-static struct sonypi_compat_s sonypi_compat = {
-       .open_count = ATOMIC_INIT(0),
-};
-
-static int sonypi_misc_fasync(int fd, struct file *filp, int on)
-{
-       int retval;
-
-       retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
-       if (retval < 0)
-               return retval;
-       return 0;
-}
-
-static int sonypi_misc_release(struct inode *inode, struct file *file)
-{
-       atomic_dec(&sonypi_compat.open_count);
-       return 0;
-}
-
-static int sonypi_misc_open(struct inode *inode, struct file *file)
-{
-       /* Flush input queue on first open */
-       lock_kernel();
-       if (atomic_inc_return(&sonypi_compat.open_count) == 1)
-               kfifo_reset(sonypi_compat.fifo);
-       unlock_kernel();
-       return 0;
-}
-
-static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
-                               size_t count, loff_t *pos)
-{
-       ssize_t ret;
-       unsigned char c;
-
-       if ((kfifo_len(sonypi_compat.fifo) == 0) &&
-           (file->f_flags & O_NONBLOCK))
-               return -EAGAIN;
-
-       ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
-                                      kfifo_len(sonypi_compat.fifo) != 0);
-       if (ret)
-               return ret;
-
-       while (ret < count &&
-              (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
-               if (put_user(c, buf++))
-                       return -EFAULT;
-               ret++;
-       }
-
-       if (ret > 0) {
-               struct inode *inode = file->f_path.dentry->d_inode;
-               inode->i_atime = current_fs_time(inode->i_sb);
-       }
-
-       return ret;
-}
-
-static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
-{
-       poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
-       if (kfifo_len(sonypi_compat.fifo))
-               return POLLIN | POLLRDNORM;
-       return 0;
-}
-
-static int ec_read16(u8 addr, u16 *value)
-{
-       u8 val_lb, val_hb;
-       if (ec_read(addr, &val_lb))
-               return -1;
-       if (ec_read(addr + 1, &val_hb))
-               return -1;
-       *value = val_lb | (val_hb << 8);
-       return 0;
-}
-
-static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
-                            unsigned int cmd, unsigned long arg)
-{
-       int ret = 0;
-       void __user *argp = (void __user *)arg;
-       u8 val8;
-       u16 val16;
-       int value;
-
-       mutex_lock(&spic_dev.lock);
-       switch (cmd) {
-       case SONYPI_IOCGBRT:
-               if (sony_backlight_device == NULL) {
-                       ret = -EIO;
-                       break;
-               }
-               if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
-                       ret = -EIO;
-                       break;
-               }
-               val8 = ((value & 0xff) - 1) << 5;
-               if (copy_to_user(argp, &val8, sizeof(val8)))
-                               ret = -EFAULT;
-               break;
-       case SONYPI_IOCSBRT:
-               if (sony_backlight_device == NULL) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_from_user(&val8, argp, sizeof(val8))) {
-                       ret = -EFAULT;
-                       break;
-               }
-               if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
-                               (val8 >> 5) + 1, NULL)) {
-                       ret = -EIO;
-                       break;
-               }
-               /* sync the backlight device status */
-               sony_backlight_device->props.brightness =
-                   sony_backlight_get_brightness(sony_backlight_device);
-               break;
-       case SONYPI_IOCGBAT1CAP:
-               if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val16, sizeof(val16)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCGBAT1REM:
-               if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val16, sizeof(val16)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCGBAT2CAP:
-               if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val16, sizeof(val16)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCGBAT2REM:
-               if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val16, sizeof(val16)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCGBATFLAGS:
-               if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
-                       ret = -EIO;
-                       break;
-               }
-               val8 &= 0x07;
-               if (copy_to_user(argp, &val8, sizeof(val8)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCGBLUE:
-               val8 = spic_dev.bluetooth_power;
-               if (copy_to_user(argp, &val8, sizeof(val8)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCSBLUE:
-               if (copy_from_user(&val8, argp, sizeof(val8))) {
-                       ret = -EFAULT;
-                       break;
-               }
-               __sony_pic_set_bluetoothpower(val8);
-               break;
-       /* FAN Controls */
-       case SONYPI_IOCGFAN:
-               if (sony_pic_get_fanspeed(&val8)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val8, sizeof(val8)))
-                       ret = -EFAULT;
-               break;
-       case SONYPI_IOCSFAN:
-               if (copy_from_user(&val8, argp, sizeof(val8))) {
-                       ret = -EFAULT;
-                       break;
-               }
-               if (sony_pic_set_fanspeed(val8))
-                       ret = -EIO;
-               break;
-       /* GET Temperature (useful under APM) */
-       case SONYPI_IOCGTEMP:
-               if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
-                       ret = -EIO;
-                       break;
-               }
-               if (copy_to_user(argp, &val8, sizeof(val8)))
-                       ret = -EFAULT;
-               break;
-       default:
-               ret = -EINVAL;
-       }
-       mutex_unlock(&spic_dev.lock);
-       return ret;
-}
-
-static const struct file_operations sonypi_misc_fops = {
-       .owner          = THIS_MODULE,
-       .read           = sonypi_misc_read,
-       .poll           = sonypi_misc_poll,
-       .open           = sonypi_misc_open,
-       .release        = sonypi_misc_release,
-       .fasync         = sonypi_misc_fasync,
-       .ioctl          = sonypi_misc_ioctl,
-};
-
-static struct miscdevice sonypi_misc_device = {
-       .minor          = MISC_DYNAMIC_MINOR,
-       .name           = "sonypi",
-       .fops           = &sonypi_misc_fops,
-};
-
-static void sonypi_compat_report_event(u8 event)
-{
-       kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
-       kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
-       wake_up_interruptible(&sonypi_compat.fifo_proc_list);
-}
-
-static int sonypi_compat_init(void)
-{
-       int error;
-
-       spin_lock_init(&sonypi_compat.fifo_lock);
-       sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
-                                        &sonypi_compat.fifo_lock);
-       if (IS_ERR(sonypi_compat.fifo)) {
-               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
-               return PTR_ERR(sonypi_compat.fifo);
-       }
-
-       init_waitqueue_head(&sonypi_compat.fifo_proc_list);
-
-       if (minor != -1)
-               sonypi_misc_device.minor = minor;
-       error = misc_register(&sonypi_misc_device);
-       if (error) {
-               printk(KERN_ERR DRV_PFX "misc_register failed\n");
-               goto err_free_kfifo;
-       }
-       if (minor == -1)
-               printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
-                      sonypi_misc_device.minor);
-
-       return 0;
-
-err_free_kfifo:
-       kfifo_free(sonypi_compat.fifo);
-       return error;
-}
-
-static void sonypi_compat_exit(void)
-{
-       misc_deregister(&sonypi_misc_device);
-       kfifo_free(sonypi_compat.fifo);
-}
-#else
-static int sonypi_compat_init(void) { return 0; }
-static void sonypi_compat_exit(void) { }
-static void sonypi_compat_report_event(u8 event) { }
-#endif /* CONFIG_SONYPI_COMPAT */
-
-/*
- * ACPI callbacks
- */
-static acpi_status
-sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
-{
-       u32 i;
-       struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
-
-       switch (resource->type) {
-       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
-               {
-                       /* start IO enumeration */
-                       struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
-                       if (!ioport)
-                               return AE_ERROR;
-
-                       list_add(&ioport->list, &dev->ioports);
-                       return AE_OK;
-               }
-
-       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
-               /* end IO enumeration */
-               return AE_OK;
-
-       case ACPI_RESOURCE_TYPE_IRQ:
-               {
-                       struct acpi_resource_irq *p = &resource->data.irq;
-                       struct sony_pic_irq *interrupt = NULL;
-                       if (!p || !p->interrupt_count) {
-                               /*
-                                * IRQ descriptors may have no IRQ# bits set,
-                                * particularly those those w/ _STA disabled
-                                */
-                               dprintk("Blank IRQ resource\n");
-                               return AE_OK;
-                       }
-                       for (i = 0; i < p->interrupt_count; i++) {
-                               if (!p->interrupts[i]) {
-                                       printk(KERN_WARNING DRV_PFX
-                                                       "Invalid IRQ %d\n",
-                                                       p->interrupts[i]);
-                                       continue;
-                               }
-                               interrupt = kzalloc(sizeof(*interrupt),
-                                               GFP_KERNEL);
-                               if (!interrupt)
-                                       return AE_ERROR;
-
-                               list_add(&interrupt->list, &dev->interrupts);
-                               interrupt->irq.triggering = p->triggering;
-                               interrupt->irq.polarity = p->polarity;
-                               interrupt->irq.sharable = p->sharable;
-                               interrupt->irq.interrupt_count = 1;
-                               interrupt->irq.interrupts[0] = p->interrupts[i];
-                       }
-                       return AE_OK;
-               }
-       case ACPI_RESOURCE_TYPE_IO:
-               {
-                       struct acpi_resource_io *io = &resource->data.io;
-                       struct sony_pic_ioport *ioport =
-                               list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
-                       if (!io) {
-                               dprintk("Blank IO resource\n");
-                               return AE_OK;
-                       }
-
-                       if (!ioport->io1.minimum) {
-                               memcpy(&ioport->io1, io, sizeof(*io));
-                               dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,
-                                               ioport->io1.address_length);
-                       }
-                       else if (!ioport->io2.minimum) {
-                               memcpy(&ioport->io2, io, sizeof(*io));
-                               dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,
-                                               ioport->io2.address_length);
-                       }
-                       else {
-                               printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
-                               return AE_ERROR;
-                       }
-                       return AE_OK;
-               }
-       default:
-               dprintk("Resource %d isn't an IRQ nor an IO port\n",
-                               resource->type);
-
-       case ACPI_RESOURCE_TYPE_END_TAG:
-               return AE_OK;
-       }
-       return AE_CTRL_TERMINATE;
-}
-
-static int sony_pic_possible_resources(struct acpi_device *device)
-{
-       int result = 0;
-       acpi_status status = AE_OK;
-
-       if (!device)
-               return -EINVAL;
-
-       /* get device status */
-       /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
-       dprintk("Evaluating _STA\n");
-       result = acpi_bus_get_status(device);
-       if (result) {
-               printk(KERN_WARNING DRV_PFX "Unable to read status\n");
-               goto end;
-       }
-
-       if (!device->status.enabled)
-               dprintk("Device disabled\n");
-       else
-               dprintk("Device enabled\n");
-
-       /*
-        * Query and parse 'method'
-        */
-       dprintk("Evaluating %s\n", METHOD_NAME__PRS);
-       status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
-                       sony_pic_read_possible_resource, &spic_dev);
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_WARNING DRV_PFX
-                               "Failure evaluating %s\n",
-                               METHOD_NAME__PRS);
-               result = -ENODEV;
-       }
-end:
-       return result;
-}
-
-/*
- *  Disable the spic device by calling its _DIS method
- */
-static int sony_pic_disable(struct acpi_device *device)
-{
-       acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL,
-                                              NULL);
-
-       if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND)
-               return -ENXIO;
-
-       dprintk("Device disabled\n");
-       return 0;
-}
-
-
-/*
- *  Based on drivers/acpi/pci_link.c:acpi_pci_link_set
- *
- *  Call _SRS to set current resources
- */
-static int sony_pic_enable(struct acpi_device *device,
-               struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
-{
-       acpi_status status;
-       int result = 0;
-       /* Type 1 resource layout is:
-        *    IO
-        *    IO
-        *    IRQNoFlags
-        *    End
-        *
-        * Type 2 and 3 resource layout is:
-        *    IO
-        *    IRQNoFlags
-        *    End
-        */
-       struct {
-               struct acpi_resource res1;
-               struct acpi_resource res2;
-               struct acpi_resource res3;
-               struct acpi_resource res4;
-       } *resource;
-       struct acpi_buffer buffer = { 0, NULL };
-
-       if (!ioport || !irq)
-               return -EINVAL;
-
-       /* init acpi_buffer */
-       resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
-       if (!resource)
-               return -ENOMEM;
-
-       buffer.length = sizeof(*resource) + 1;
-       buffer.pointer = resource;
-
-       /* setup Type 1 resources */
-       if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) {
-
-               /* setup io resources */
-               resource->res1.type = ACPI_RESOURCE_TYPE_IO;
-               resource->res1.length = sizeof(struct acpi_resource);
-               memcpy(&resource->res1.data.io, &ioport->io1,
-                               sizeof(struct acpi_resource_io));
-
-               resource->res2.type = ACPI_RESOURCE_TYPE_IO;
-               resource->res2.length = sizeof(struct acpi_resource);
-               memcpy(&resource->res2.data.io, &ioport->io2,
-                               sizeof(struct acpi_resource_io));
-
-               /* setup irq resource */
-               resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;
-               resource->res3.length = sizeof(struct acpi_resource);
-               memcpy(&resource->res3.data.irq, &irq->irq,
-                               sizeof(struct acpi_resource_irq));
-               /* we requested a shared irq */
-               resource->res3.data.irq.sharable = ACPI_SHARED;
-
-               resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
-
-       }
-       /* setup Type 2/3 resources */
-       else {
-               /* setup io resource */
-               resource->res1.type = ACPI_RESOURCE_TYPE_IO;
-               resource->res1.length = sizeof(struct acpi_resource);
-               memcpy(&resource->res1.data.io, &ioport->io1,
-                               sizeof(struct acpi_resource_io));
-
-               /* setup irq resource */
-               resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;
-               resource->res2.length = sizeof(struct acpi_resource);
-               memcpy(&resource->res2.data.irq, &irq->irq,
-                               sizeof(struct acpi_resource_irq));
-               /* we requested a shared irq */
-               resource->res2.data.irq.sharable = ACPI_SHARED;
-
-               resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
-       }
-
-       /* Attempt to set the resource */
-       dprintk("Evaluating _SRS\n");
-       status = acpi_set_current_resources(device->handle, &buffer);
-
-       /* check for total failure */
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
-               result = -ENODEV;
-               goto end;
-       }
-
-       /* Necessary device initializations calls (from sonypi) */
-       sony_pic_call1(0x82);
-       sony_pic_call2(0x81, 0xff);
-       sony_pic_call1(compat ? 0x92 : 0x82);
-
-end:
-       kfree(resource);
-       return result;
-}
-
-/*****************
- *
- * ISR: some event is available
- *
- *****************/
-static irqreturn_t sony_pic_irq(int irq, void *dev_id)
-{
-       int i, j;
-       u8 ev = 0;
-       u8 data_mask = 0;
-       u8 device_event = 0;
-
-       struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
-
-       ev = inb_p(dev->cur_ioport->io1.minimum);
-       if (dev->cur_ioport->io2.minimum)
-               data_mask = inb_p(dev->cur_ioport->io2.minimum);
-       else
-               data_mask = inb_p(dev->cur_ioport->io1.minimum +
-                               dev->control->evport_offset);
-
-       dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
-                       ev, data_mask, dev->cur_ioport->io1.minimum,
-                       dev->control->evport_offset);
-
-       if (ev == 0x00 || ev == 0xff)
-               return IRQ_HANDLED;
-
-       for (i = 0; dev->control->event_types[i].mask; i++) {
-
-               if ((data_mask & dev->control->event_types[i].data) !=
-                   dev->control->event_types[i].data)
-                       continue;
-
-               if (!(mask & dev->control->event_types[i].mask))
-                       continue;
-
-               for (j = 0; dev->control->event_types[i].events[j].event; j++) {
-                       if (ev == dev->control->event_types[i].events[j].data) {
-                               device_event =
-                                       dev->control->
-                                               event_types[i].events[j].event;
-                               goto found;
-                       }
-               }
-       }
-       /* Still not able to decode the event try to pass
-        * it over to the minidriver
-        */
-       if (dev->control->handle_irq &&
-                       dev->control->handle_irq(data_mask, ev) == 0)
-               return IRQ_HANDLED;
-
-       dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
-                       ev, data_mask, dev->cur_ioport->io1.minimum,
-                       dev->control->evport_offset);
-       return IRQ_HANDLED;
-
-found:
-       sony_laptop_report_input_event(device_event);
-       acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
-       sonypi_compat_report_event(device_event);
-
-       return IRQ_HANDLED;
-}
-
-/*****************
- *
- *  ACPI driver
- *
- *****************/
-static int sony_pic_remove(struct acpi_device *device, int type)
-{
-       struct sony_pic_ioport *io, *tmp_io;
-       struct sony_pic_irq *irq, *tmp_irq;
-
-       if (sony_pic_disable(device)) {
-               printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
-               return -ENXIO;
-       }
-
-       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
-       release_region(spic_dev.cur_ioport->io1.minimum,
-                       spic_dev.cur_ioport->io1.address_length);
-       if (spic_dev.cur_ioport->io2.minimum)
-               release_region(spic_dev.cur_ioport->io2.minimum,
-                               spic_dev.cur_ioport->io2.address_length);
-
-       sonypi_compat_exit();
-
-       sony_laptop_remove_input();
-
-       /* pf attrs */
-       sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
-       sony_pf_remove();
-
-       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
-               list_del(&io->list);
-               kfree(io);
-       }
-       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
-               list_del(&irq->list);
-               kfree(irq);
-       }
-       spic_dev.cur_ioport = NULL;
-       spic_dev.cur_irq = NULL;
-
-       dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
-       return 0;
-}
-
-static int sony_pic_add(struct acpi_device *device)
-{
-       int result;
-       struct sony_pic_ioport *io, *tmp_io;
-       struct sony_pic_irq *irq, *tmp_irq;
-
-       printk(KERN_INFO DRV_PFX "%s v%s.\n",
-               SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
-       spic_dev.acpi_dev = device;
-       strcpy(acpi_device_class(device), "sony/hotkey");
-       sony_pic_detect_device_type(&spic_dev);
-       mutex_init(&spic_dev.lock);
-
-       /* read _PRS resources */
-       result = sony_pic_possible_resources(device);
-       if (result) {
-               printk(KERN_ERR DRV_PFX
-                               "Unabe to read possible resources.\n");
-               goto err_free_resources;
-       }
-
-       /* setup input devices and helper fifo */
-       result = sony_laptop_setup_input(device);
-       if (result) {
-               printk(KERN_ERR DRV_PFX
-                               "Unabe to create input devices.\n");
-               goto err_free_resources;
-       }
-
-       if (sonypi_compat_init())
-               goto err_remove_input;
-
-       /* request io port */
-       list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
-               if (request_region(io->io1.minimum, io->io1.address_length,
-                                       "Sony Programable I/O Device")) {
-                       dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
-                                       io->io1.minimum, io->io1.maximum,
-                                       io->io1.address_length);
-                       /* Type 1 have 2 ioports */
-                       if (io->io2.minimum) {
-                               if (request_region(io->io2.minimum,
-                                               io->io2.address_length,
-                                               "Sony Programable I/O Device")) {
-                                       dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
-                                                       io->io2.minimum, io->io2.maximum,
-                                                       io->io2.address_length);
-                                       spic_dev.cur_ioport = io;
-                                       break;
-                               }
-                               else {
-                                       dprintk("Unable to get I/O port2: "
-                                                       "0x%.4x (0x%.4x) + 0x%.2x\n",
-                                                       io->io2.minimum, io->io2.maximum,
-                                                       io->io2.address_length);
-                                       release_region(io->io1.minimum,
-                                                       io->io1.address_length);
-                               }
-                       }
-                       else {
-                               spic_dev.cur_ioport = io;
-                               break;
-                       }
-               }
-       }
-       if (!spic_dev.cur_ioport) {
-               printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
-               result = -ENODEV;
-               goto err_remove_compat;
-       }
-
-       /* request IRQ */
-       list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
-               if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
-                                       IRQF_SHARED, "sony-laptop", &spic_dev)) {
-                       dprintk("IRQ: %d - triggering: %d - "
-                                       "polarity: %d - shr: %d\n",
-                                       irq->irq.interrupts[0],
-                                       irq->irq.triggering,
-                                       irq->irq.polarity,
-                                       irq->irq.sharable);
-                       spic_dev.cur_irq = irq;
-                       break;
-               }
-       }
-       if (!spic_dev.cur_irq) {
-               printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
-               result = -ENODEV;
-               goto err_release_region;
-       }
-
-       /* set resource status _SRS */
-       result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
-       if (result) {
-               printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
-               goto err_free_irq;
-       }
-
-       spic_dev.bluetooth_power = -1;
-       /* create device attributes */
-       result = sony_pf_add();
-       if (result)
-               goto err_disable_device;
-
-       result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
-       if (result)
-               goto err_remove_pf;
-
-       return 0;
-
-err_remove_pf:
-       sony_pf_remove();
-
-err_disable_device:
-       sony_pic_disable(device);
-
-err_free_irq:
-       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
-
-err_release_region:
-       release_region(spic_dev.cur_ioport->io1.minimum,
-                       spic_dev.cur_ioport->io1.address_length);
-       if (spic_dev.cur_ioport->io2.minimum)
-               release_region(spic_dev.cur_ioport->io2.minimum,
-                               spic_dev.cur_ioport->io2.address_length);
-
-err_remove_compat:
-       sonypi_compat_exit();
-
-err_remove_input:
-       sony_laptop_remove_input();
-
-err_free_resources:
-       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
-               list_del(&io->list);
-               kfree(io);
-       }
-       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
-               list_del(&irq->list);
-               kfree(irq);
-       }
-       spic_dev.cur_ioport = NULL;
-       spic_dev.cur_irq = NULL;
-
-       return result;
-}
-
-static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
-{
-       if (sony_pic_disable(device))
-               return -ENXIO;
-       return 0;
-}
-
-static int sony_pic_resume(struct acpi_device *device)
-{
-       sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
-       return 0;
-}
-
-static const struct acpi_device_id sony_pic_device_ids[] = {
-       {SONY_PIC_HID, 0},
-       {"", 0},
-};
-
-static struct acpi_driver sony_pic_driver = {
-       .name = SONY_PIC_DRIVER_NAME,
-       .class = SONY_PIC_CLASS,
-       .ids = sony_pic_device_ids,
-       .owner = THIS_MODULE,
-       .ops = {
-               .add = sony_pic_add,
-               .remove = sony_pic_remove,
-               .suspend = sony_pic_suspend,
-               .resume = sony_pic_resume,
-               },
-};
-
-static struct dmi_system_id __initdata sonypi_dmi_table[] = {
-       {
-               .ident = "Sony Vaio",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
-               },
-       },
-       {
-               .ident = "Sony Vaio",
-               .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
-               },
-       },
-       { }
-};
-
-static int __init sony_laptop_init(void)
-{
-       int result;
-
-       if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
-               result = acpi_bus_register_driver(&sony_pic_driver);
-               if (result) {
-                       printk(KERN_ERR DRV_PFX
-                                       "Unable to register SPIC driver.");
-                       goto out;
-               }
-       }
-
-       result = acpi_bus_register_driver(&sony_nc_driver);
-       if (result) {
-               printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
-               goto out_unregister_pic;
-       }
-
-       return 0;
-
-out_unregister_pic:
-       if (!no_spic)
-               acpi_bus_unregister_driver(&sony_pic_driver);
-out:
-       return result;
-}
-
-static void __exit sony_laptop_exit(void)
-{
-       acpi_bus_unregister_driver(&sony_nc_driver);
-       if (!no_spic)
-               acpi_bus_unregister_driver(&sony_pic_driver);
-}
-
-module_init(sony_laptop_init);
-module_exit(sony_laptop_exit);
diff --git a/drivers/misc/tc1100-wmi.c b/drivers/misc/tc1100-wmi.c
deleted file mode 100644 (file)
index f25e4c9..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- *  HP Compaq TC1100 Tablet WMI Extras Driver
- *
- *  Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
- *  Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
- *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
- *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or (at
- *  your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <acpi/acpi.h>
-#include <acpi/actypes.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-#include <linux/platform_device.h>
-
-#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
-
-#define TC1100_INSTANCE_WIRELESS               1
-#define TC1100_INSTANCE_JOGDIAL                2
-
-#define TC1100_LOGPREFIX "tc1100-wmi: "
-#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
-
-MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
-MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
-
-static int tc1100_probe(struct platform_device *device);
-static int tc1100_remove(struct platform_device *device);
-static int tc1100_suspend(struct platform_device *device, pm_message_t state);
-static int tc1100_resume(struct platform_device *device);
-
-static struct platform_driver tc1100_driver = {
-       .driver = {
-               .name = "tc1100-wmi",
-               .owner = THIS_MODULE,
-       },
-       .probe = tc1100_probe,
-       .remove = tc1100_remove,
-       .suspend = tc1100_suspend,
-       .resume = tc1100_resume,
-};
-
-static struct platform_device *tc1100_device;
-
-struct tc1100_data {
-       u32 wireless;
-       u32 jogdial;
-};
-
-static struct tc1100_data suspend_data;
-
-/* --------------------------------------------------------------------------
-                               Device Management
-   -------------------------------------------------------------------------- */
-
-static int get_state(u32 *out, u8 instance)
-{
-       u32 tmp;
-       acpi_status status;
-       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
-
-       if (!out)
-               return -EINVAL;
-
-       if (instance > 2)
-               return -ENODEV;
-
-       status = wmi_query_block(GUID, instance, &result);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       obj = (union acpi_object *) result.pointer;
-       if (obj && obj->type == ACPI_TYPE_BUFFER &&
-               obj->buffer.length == sizeof(u32)) {
-               tmp = *((u32 *) obj->buffer.pointer);
-       } else {
-               tmp = 0;
-       }
-
-       if (result.length > 0 && result.pointer)
-               kfree(result.pointer);
-
-       switch (instance) {
-       case TC1100_INSTANCE_WIRELESS:
-               *out = (tmp == 3) ? 1 : 0;
-               return 0;
-       case TC1100_INSTANCE_JOGDIAL:
-               *out = (tmp == 1) ? 1 : 0;
-               return 0;
-       default:
-               return -ENODEV;
-       }
-}
-
-static int set_state(u32 *in, u8 instance)
-{
-       u32 value;
-       acpi_status status;
-       struct acpi_buffer input;
-
-       if (!in)
-               return -EINVAL;
-
-       if (instance > 2)
-               return -ENODEV;
-
-       switch (instance) {
-       case TC1100_INSTANCE_WIRELESS:
-               value = (*in) ? 1 : 2;
-               break;
-       case TC1100_INSTANCE_JOGDIAL:
-               value = (*in) ? 0 : 1;
-               break;
-       default:
-               return -ENODEV;
-       }
-
-       input.length = sizeof(u32);
-       input.pointer = &value;
-
-       status = wmi_set_block(GUID, instance, &input);
-       if (ACPI_FAILURE(status))
-               return -ENODEV;
-
-       return 0;
-}
-
-/* --------------------------------------------------------------------------
-                               FS Interface (/sys)
-   -------------------------------------------------------------------------- */
-
-/*
- * Read/ write bool sysfs macro
- */
-#define show_set_bool(value, instance) \
-static ssize_t \
-show_bool_##value(struct device *dev, struct device_attribute *attr, \
-       char *buf) \
-{ \
-       u32 result; \
-       acpi_status status = get_state(&result, instance); \
-       if (ACPI_SUCCESS(status)) \
-               return sprintf(buf, "%d\n", result); \
-       return sprintf(buf, "Read error\n"); \
-} \
-\
-static ssize_t \
-set_bool_##value(struct device *dev, struct device_attribute *attr, \
-       const char *buf, size_t count) \
-{ \
-       u32 tmp = simple_strtoul(buf, NULL, 10); \
-       acpi_status status = set_state(&tmp, instance); \
-               if (ACPI_FAILURE(status)) \
-                       return -EINVAL; \
-       return count; \
-} \
-static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
-       show_bool_##value, set_bool_##value);
-
-show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
-show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
-
-static void remove_fs(void)
-{
-       device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
-       device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
-}
-
-static int add_fs(void)
-{
-       int ret;
-
-       ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
-       if (ret)
-               goto add_sysfs_error;
-
-       ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
-       if (ret)
-               goto add_sysfs_error;
-
-       return ret;
-
-add_sysfs_error:
-       remove_fs();
-       return ret;
-}
-
-/* --------------------------------------------------------------------------
-                               Driver Model
-   -------------------------------------------------------------------------- */
-
-static int tc1100_probe(struct platform_device *device)
-{
-       int result = 0;
-
-       result = add_fs();
-       return result;
-}
-
-
-static int tc1100_remove(struct platform_device *device)
-{
-       remove_fs();
-       return 0;
-}
-
-static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
-{
-       int ret;
-
-       ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
-       if (ret)
-               return ret;
-
-       ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
-       if (ret)
-               return ret;
-
-       return ret;
-}
-
-static int tc1100_resume(struct platform_device *dev)
-{
-       int ret;
-
-       ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
-       if (ret)
-               return ret;
-
-       ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
-       if (ret)
-               return ret;
-
-       return ret;
-}
-
-static int __init tc1100_init(void)
-{
-       int result = 0;
-
-       if (!wmi_has_guid(GUID))
-               return -ENODEV;
-
-       result = platform_driver_register(&tc1100_driver);
-       if (result)
-               return result;
-
-       tc1100_device = platform_device_alloc("tc1100-wmi", -1);
-       platform_device_add(tc1100_device);
-
-       printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
-
-       return result;
-}
-
-static void __exit tc1100_exit(void)
-{
-       platform_device_del(tc1100_device);
-       platform_driver_unregister(&tc1100_driver);
-
-       printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
-}
-
-module_init(tc1100_init);
-module_exit(tc1100_exit);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
deleted file mode 100644 (file)
index 899766e..0000000
+++ /dev/null
@@ -1,6949 +0,0 @@
-/*
- *  thinkpad_acpi.c - ThinkPad ACPI Extras
- *
- *
- *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
- *  Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- *  02110-1301, USA.
- */
-
-#define TPACPI_VERSION "0.21"
-#define TPACPI_SYSFS_VERSION 0x020200
-
-/*
- *  Changelog:
- *  2007-10-20         changelog trimmed down
- *
- *  2007-03-27  0.14   renamed to thinkpad_acpi and moved to
- *                     drivers/misc.
- *
- *  2006-11-22 0.13    new maintainer
- *                     changelog now lives in git commit history, and will
- *                     not be updated further in-file.
- *
- *  2005-03-17 0.11    support for 600e, 770x
- *                         thanks to Jamie Lentin <lentinj@dial.pipex.com>
- *
- *  2005-01-16 0.9     use MODULE_VERSION
- *                         thanks to Henrik Brix Andersen <brix@gentoo.org>
- *                     fix parameter passing on module loading
- *                         thanks to Rusty Russell <rusty@rustcorp.com.au>
- *                         thanks to Jim Radford <radford@blackbean.org>
- *  2004-11-08 0.8     fix init error case, don't return from a macro
- *                         thanks to Chris Wright <chrisw@osdl.org>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/string.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/delay.h>
-
-#include <linux/nvram.h>
-#include <linux/proc_fs.h>
-#include <linux/sysfs.h>
-#include <linux/backlight.h>
-#include <linux/fb.h>
-#include <linux/platform_device.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/input.h>
-#include <linux/leds.h>
-#include <linux/rfkill.h>
-#include <asm/uaccess.h>
-
-#include <linux/dmi.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-
-#include <acpi/acpi_drivers.h>
-#include <acpi/acnamesp.h>
-
-#include <linux/pci_ids.h>
-
-
-/* ThinkPad CMOS commands */
-#define TP_CMOS_VOLUME_DOWN    0
-#define TP_CMOS_VOLUME_UP      1
-#define TP_CMOS_VOLUME_MUTE    2
-#define TP_CMOS_BRIGHTNESS_UP  4
-#define TP_CMOS_BRIGHTNESS_DOWN        5
-#define TP_CMOS_THINKLIGHT_ON  12
-#define TP_CMOS_THINKLIGHT_OFF 13
-
-/* NVRAM Addresses */
-enum tp_nvram_addr {
-       TP_NVRAM_ADDR_HK2               = 0x57,
-       TP_NVRAM_ADDR_THINKLIGHT        = 0x58,
-       TP_NVRAM_ADDR_VIDEO             = 0x59,
-       TP_NVRAM_ADDR_BRIGHTNESS        = 0x5e,
-       TP_NVRAM_ADDR_MIXER             = 0x60,
-};
-
-/* NVRAM bit masks */
-enum {
-       TP_NVRAM_MASK_HKT_THINKPAD      = 0x08,
-       TP_NVRAM_MASK_HKT_ZOOM          = 0x20,
-       TP_NVRAM_MASK_HKT_DISPLAY       = 0x40,
-       TP_NVRAM_MASK_HKT_HIBERNATE     = 0x80,
-       TP_NVRAM_MASK_THINKLIGHT        = 0x10,
-       TP_NVRAM_MASK_HKT_DISPEXPND     = 0x30,
-       TP_NVRAM_MASK_HKT_BRIGHTNESS    = 0x20,
-       TP_NVRAM_MASK_LEVEL_BRIGHTNESS  = 0x0f,
-       TP_NVRAM_POS_LEVEL_BRIGHTNESS   = 0,
-       TP_NVRAM_MASK_MUTE              = 0x40,
-       TP_NVRAM_MASK_HKT_VOLUME        = 0x80,
-       TP_NVRAM_MASK_LEVEL_VOLUME      = 0x0f,
-       TP_NVRAM_POS_LEVEL_VOLUME       = 0,
-};
-
-/* ACPI HIDs */
-#define TPACPI_ACPI_HKEY_HID           "IBM0068"
-
-/* Input IDs */
-#define TPACPI_HKEY_INPUT_PRODUCT      0x5054 /* "TP" */
-#define TPACPI_HKEY_INPUT_VERSION      0x4101
-
-
-/****************************************************************************
- * Main driver
- */
-
-#define TPACPI_NAME "thinkpad"
-#define TPACPI_DESC "ThinkPad ACPI Extras"
-#define TPACPI_FILE TPACPI_NAME "_acpi"
-#define TPACPI_URL "http://ibm-acpi.sf.net/"
-#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net"
-
-#define TPACPI_PROC_DIR "ibm"
-#define TPACPI_ACPI_EVENT_PREFIX "ibm"
-#define TPACPI_DRVR_NAME TPACPI_FILE
-#define TPACPI_DRVR_SHORTNAME "tpacpi"
-#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
-
-#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
-#define TPACPI_WORKQUEUE_NAME "ktpacpid"
-
-#define TPACPI_MAX_ACPI_ARGS 3
-
-/* rfkill switches */
-enum {
-       TPACPI_RFK_BLUETOOTH_SW_ID = 0,
-       TPACPI_RFK_WWAN_SW_ID,
-};
-
-/* Debugging */
-#define TPACPI_LOG TPACPI_FILE ": "
-#define TPACPI_ERR        KERN_ERR    TPACPI_LOG
-#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
-#define TPACPI_INFO   KERN_INFO   TPACPI_LOG
-#define TPACPI_DEBUG  KERN_DEBUG  TPACPI_LOG
-
-#define TPACPI_DBG_ALL         0xffff
-#define TPACPI_DBG_INIT                0x0001
-#define TPACPI_DBG_EXIT                0x0002
-#define dbg_printk(a_dbg_level, format, arg...) \
-       do { if (dbg_level & a_dbg_level) \
-               printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
-       } while (0)
-#ifdef CONFIG_THINKPAD_ACPI_DEBUG
-#define vdbg_printk(a_dbg_level, format, arg...) \
-       dbg_printk(a_dbg_level, format, ## arg)
-static const char *str_supported(int is_supported);
-#else
-#define vdbg_printk(a_dbg_level, format, arg...)
-#endif
-
-#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
-#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
-#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
-
-
-/****************************************************************************
- * Driver-wide structs and misc. variables
- */
-
-struct ibm_struct;
-
-struct tp_acpi_drv_struct {
-       const struct acpi_device_id *hid;
-       struct acpi_driver *driver;
-
-       void (*notify) (struct ibm_struct *, u32);
-       acpi_handle *handle;
-       u32 type;
-       struct acpi_device *device;
-};
-
-struct ibm_struct {
-       char *name;
-
-       int (*read) (char *);
-       int (*write) (char *);
-       void (*exit) (void);
-       void (*resume) (void);
-       void (*suspend) (pm_message_t state);
-
-       struct list_head all_drivers;
-
-       struct tp_acpi_drv_struct *acpi;
-
-       struct {
-               u8 acpi_driver_registered:1;
-               u8 acpi_notify_installed:1;
-               u8 proc_created:1;
-               u8 init_called:1;
-               u8 experimental:1;
-       } flags;
-};
-
-struct ibm_init_struct {
-       char param[32];
-
-       int (*init) (struct ibm_init_struct *);
-       struct ibm_struct *data;
-};
-
-static struct {
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-       u32 bay_status:1;
-       u32 bay_eject:1;
-       u32 bay_status2:1;
-       u32 bay_eject2:1;
-#endif
-       u32 bluetooth:1;
-       u32 hotkey:1;
-       u32 hotkey_mask:1;
-       u32 hotkey_wlsw:1;
-       u32 hotkey_tablet:1;
-       u32 light:1;
-       u32 light_status:1;
-       u32 bright_16levels:1;
-       u32 bright_acpimode:1;
-       u32 wan:1;
-       u32 fan_ctrl_status_undef:1;
-       u32 input_device_registered:1;
-       u32 platform_drv_registered:1;
-       u32 platform_drv_attrs_registered:1;
-       u32 sensors_pdrv_registered:1;
-       u32 sensors_pdrv_attrs_registered:1;
-       u32 sensors_pdev_attrs_registered:1;
-       u32 hotkey_poll_active:1;
-} tp_features;
-
-static struct {
-       u16 hotkey_mask_ff:1;
-       u16 bright_cmos_ec_unsync:1;
-} tp_warned;
-
-struct thinkpad_id_data {
-       unsigned int vendor;    /* ThinkPad vendor:
-                                * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
-
-       char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
-       char *ec_version_str;   /* Something like 1ZHT51WW-1.04a */
-
-       u16 bios_model;         /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
-       u16 ec_model;
-
-       char *model_str;        /* ThinkPad T43 */
-       char *nummodel_str;     /* 9384A9C for a 9384-A9C model */
-};
-static struct thinkpad_id_data thinkpad_id;
-
-static enum {
-       TPACPI_LIFE_INIT = 0,
-       TPACPI_LIFE_RUNNING,
-       TPACPI_LIFE_EXITING,
-} tpacpi_lifecycle;
-
-static int experimental;
-static u32 dbg_level;
-
-static struct workqueue_struct *tpacpi_wq;
-
-/* Special LED class that can defer work */
-struct tpacpi_led_classdev {
-       struct led_classdev led_classdev;
-       struct work_struct work;
-       enum led_brightness new_brightness;
-       unsigned int led;
-};
-
-/****************************************************************************
- ****************************************************************************
- *
- * ACPI Helpers and device model
- *
- ****************************************************************************
- ****************************************************************************/
-
-/*************************************************************************
- * ACPI basic handles
- */
-
-static acpi_handle root_handle;
-
-#define TPACPI_HANDLE(object, parent, paths...)                        \
-       static acpi_handle  object##_handle;                    \
-       static acpi_handle *object##_parent = &parent##_handle; \
-       static char        *object##_path;                      \
-       static char        *object##_paths[] = { paths }
-
-TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0",  /* 240, 240x */
-          "\\_SB.PCI.ISA.EC",  /* 570 */
-          "\\_SB.PCI0.ISA0.EC0",       /* 600e/x, 770e, 770x */
-          "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
-          "\\_SB.PCI0.AD4S.EC0",       /* i1400, R30 */
-          "\\_SB.PCI0.ICH3.EC0",       /* R31 */
-          "\\_SB.PCI0.LPC.EC", /* all others */
-          );
-
-TPACPI_HANDLE(ecrd, ec, "ECRD");       /* 570 */
-TPACPI_HANDLE(ecwr, ec, "ECWR");       /* 570 */
-
-TPACPI_HANDLE(cmos, root, "\\UCMS",    /* R50, R50e, R50p, R51, */
-                                       /* T4x, X31, X40 */
-          "\\CMOS",            /* A3x, G4x, R32, T23, T30, X22-24, X30 */
-          "\\CMS",             /* R40, R40e */
-          );                   /* all others */
-
-TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY",  /* 600e/x, 770e, 770x */
-          "^HKEY",             /* R30, R31 */
-          "HKEY",              /* all others */
-          );                   /* 570 */
-
-TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA",  /* 570 */
-          "\\_SB.PCI0.AGP0.VID0",      /* 600e/x, 770x */
-          "\\_SB.PCI0.VID0",   /* 770e */
-          "\\_SB.PCI0.VID",    /* A21e, G4x, R50e, X30, X40 */
-          "\\_SB.PCI0.AGP.VID",        /* all others */
-          );                           /* R30, R31 */
-
-
-/*************************************************************************
- * ACPI helpers
- */
-
-static int acpi_evalf(acpi_handle handle,
-                     void *res, char *method, char *fmt, ...)
-{
-       char *fmt0 = fmt;
-       struct acpi_object_list params;
-       union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS];
-       struct acpi_buffer result, *resultp;
-       union acpi_object out_obj;
-       acpi_status status;
-       va_list ap;
-       char res_type;
-       int success;
-       int quiet;
-
-       if (!*fmt) {
-               printk(TPACPI_ERR "acpi_evalf() called with empty format\n");
-               return 0;
-       }
-
-       if (*fmt == 'q') {
-               quiet = 1;
-               fmt++;
-       } else
-               quiet = 0;
-
-       res_type = *(fmt++);
-
-       params.count = 0;
-       params.pointer = &in_objs[0];
-
-       va_start(ap, fmt);
-       while (*fmt) {
-               char c = *(fmt++);
-               switch (c) {
-               case 'd':       /* int */
-                       in_objs[params.count].integer.value = va_arg(ap, int);
-                       in_objs[params.count++].type = ACPI_TYPE_INTEGER;
-                       break;
-                       /* add more types as needed */
-               default:
-                       printk(TPACPI_ERR "acpi_evalf() called "
-                              "with invalid format character '%c'\n", c);
-                       return 0;
-               }
-       }
-       va_end(ap);
-
-       if (res_type != 'v') {
-               result.length = sizeof(out_obj);
-               result.pointer = &out_obj;
-               resultp = &result;
-       } else
-               resultp = NULL;
-
-       status = acpi_evaluate_object(handle, method, &params, resultp);
-
-       switch (res_type) {
-       case 'd':               /* int */
-               if (res)
-                       *(int *)res = out_obj.integer.value;
-               success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
-               break;
-       case 'v':               /* void */
-               success = status == AE_OK;
-               break;
-               /* add more types as needed */
-       default:
-               printk(TPACPI_ERR "acpi_evalf() called "
-                      "with invalid format character '%c'\n", res_type);
-               return 0;
-       }
-
-       if (!success && !quiet)
-               printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
-                      method, fmt0, status);
-
-       return success;
-}
-
-static int acpi_ec_read(int i, u8 *p)
-{
-       int v;
-
-       if (ecrd_handle) {
-               if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
-                       return 0;
-               *p = v;
-       } else {
-               if (ec_read(i, p) < 0)
-                       return 0;
-       }
-
-       return 1;
-}
-
-static int acpi_ec_write(int i, u8 v)
-{
-       if (ecwr_handle) {
-               if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
-                       return 0;
-       } else {
-               if (ec_write(i, v) < 0)
-                       return 0;
-       }
-
-       return 1;
-}
-
-#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
-static int _sta(acpi_handle handle)
-{
-       int status;
-
-       if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
-               status = 0;
-
-       return status;
-}
-#endif
-
-static int issue_thinkpad_cmos_command(int cmos_cmd)
-{
-       if (!cmos_handle)
-               return -ENXIO;
-
-       if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
-               return -EIO;
-
-       return 0;
-}
-
-/*************************************************************************
- * ACPI device model
- */
-
-#define TPACPI_ACPIHANDLE_INIT(object) \
-       drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
-               object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
-
-static void drv_acpi_handle_init(char *name,
-                          acpi_handle *handle, acpi_handle parent,
-                          char **paths, int num_paths, char **path)
-{
-       int i;
-       acpi_status status;
-
-       vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
-               name);
-
-       for (i = 0; i < num_paths; i++) {
-               status = acpi_get_handle(parent, paths[i], handle);
-               if (ACPI_SUCCESS(status)) {
-                       *path = paths[i];
-                       dbg_printk(TPACPI_DBG_INIT,
-                                  "Found ACPI handle %s for %s\n",
-                                  *path, name);
-                       return;
-               }
-       }
-
-       vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
-                   name);
-       *handle = NULL;
-}
-
-static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
-{
-       struct ibm_struct *ibm = data;
-
-       if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
-               return;
-
-       if (!ibm || !ibm->acpi || !ibm->acpi->notify)
-               return;
-
-       ibm->acpi->notify(ibm, event);
-}
-
-static int __init setup_acpi_notify(struct ibm_struct *ibm)
-{
-       acpi_status status;
-       int rc;
-
-       BUG_ON(!ibm->acpi);
-
-       if (!*ibm->acpi->handle)
-               return 0;
-
-       vdbg_printk(TPACPI_DBG_INIT,
-               "setting up ACPI notify for %s\n", ibm->name);
-
-       rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
-       if (rc < 0) {
-               printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n",
-                       ibm->name, rc);
-               return -ENODEV;
-       }
-
-       ibm->acpi->device->driver_data = ibm;
-       sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
-               TPACPI_ACPI_EVENT_PREFIX,
-               ibm->name);
-
-       status = acpi_install_notify_handler(*ibm->acpi->handle,
-                       ibm->acpi->type, dispatch_acpi_notify, ibm);
-       if (ACPI_FAILURE(status)) {
-               if (status == AE_ALREADY_EXISTS) {
-                       printk(TPACPI_NOTICE
-                              "another device driver is already "
-                              "handling %s events\n", ibm->name);
-               } else {
-                       printk(TPACPI_ERR
-                              "acpi_install_notify_handler(%s) failed: %d\n",
-                              ibm->name, status);
-               }
-               return -ENODEV;
-       }
-       ibm->flags.acpi_notify_installed = 1;
-       return 0;
-}
-
-static int __init tpacpi_device_add(struct acpi_device *device)
-{
-       return 0;
-}
-
-static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
-{
-       int rc;
-
-       dbg_printk(TPACPI_DBG_INIT,
-               "registering %s as an ACPI driver\n", ibm->name);
-
-       BUG_ON(!ibm->acpi);
-
-       ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
-       if (!ibm->acpi->driver) {
-               printk(TPACPI_ERR
-                      "failed to allocate memory for ibm->acpi->driver\n");
-               return -ENOMEM;
-       }
-
-       sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name);
-       ibm->acpi->driver->ids = ibm->acpi->hid;
-
-       ibm->acpi->driver->ops.add = &tpacpi_device_add;
-
-       rc = acpi_bus_register_driver(ibm->acpi->driver);
-       if (rc < 0) {
-               printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n",
-                      ibm->name, rc);
-               kfree(ibm->acpi->driver);
-               ibm->acpi->driver = NULL;
-       } else if (!rc)
-               ibm->flags.acpi_driver_registered = 1;
-
-       return rc;
-}
-
-
-/****************************************************************************
- ****************************************************************************
- *
- * Procfs Helpers
- *
- ****************************************************************************
- ****************************************************************************/
-
-static int dispatch_procfs_read(char *page, char **start, off_t off,
-                       int count, int *eof, void *data)
-{
-       struct ibm_struct *ibm = data;
-       int len;
-
-       if (!ibm || !ibm->read)
-               return -EINVAL;
-
-       len = ibm->read(page);
-       if (len < 0)
-               return len;
-
-       if (len <= off + count)
-               *eof = 1;
-       *start = page + off;
-       len -= off;
-       if (len > count)
-               len = count;
-       if (len < 0)
-               len = 0;
-
-       return len;
-}
-
-static int dispatch_procfs_write(struct file *file,
-                       const char __user *userbuf,
-                       unsigned long count, void *data)
-{
-       struct ibm_struct *ibm = data;
-       char *kernbuf;
-       int ret;
-
-       if (!ibm || !ibm->write)
-               return -EINVAL;
-
-       kernbuf = kmalloc(count + 2, GFP_KERNEL);
-       if (!kernbuf)
-               return -ENOMEM;
-
-       if (copy_from_user(kernbuf, userbuf, count)) {
-               kfree(kernbuf);
-               return -EFAULT;
-       }
-
-       kernbuf[count] = 0;
-       strcat(kernbuf, ",");
-       ret = ibm->write(kernbuf);
-       if (ret == 0)
-               ret = count;
-
-       kfree(kernbuf);
-
-       return ret;
-}
-
-static char *next_cmd(char **cmds)
-{
-       char *start = *cmds;
-       char *end;
-
-       while ((end = strchr(start, ',')) && end == start)
-               start = end + 1;
-
-       if (!end)
-               return NULL;
-
-       *end = 0;
-       *cmds = end + 1;
-       return start;
-}
-
-
-/****************************************************************************
- ****************************************************************************
- *
- * Device model: input, hwmon and platform
- *
- ****************************************************************************
- ****************************************************************************/
-
-static struct platform_device *tpacpi_pdev;
-static struct platform_device *tpacpi_sensors_pdev;
-static struct device *tpacpi_hwmon;
-static struct input_dev *tpacpi_inputdev;
-static struct mutex tpacpi_inputdev_send_mutex;
-static LIST_HEAD(tpacpi_all_drivers);
-
-static int tpacpi_suspend_handler(struct platform_device *pdev,
-                                 pm_message_t state)
-{
-       struct ibm_struct *ibm, *itmp;
-
-       list_for_each_entry_safe(ibm, itmp,
-                                &tpacpi_all_drivers,
-                                all_drivers) {
-               if (ibm->suspend)
-                       (ibm->suspend)(state);
-       }
-
-       return 0;
-}
-
-static int tpacpi_resume_handler(struct platform_device *pdev)
-{
-       struct ibm_struct *ibm, *itmp;
-
-       list_for_each_entry_safe(ibm, itmp,
-                                &tpacpi_all_drivers,
-                                all_drivers) {
-               if (ibm->resume)
-                       (ibm->resume)();
-       }
-
-       return 0;
-}
-
-static struct platform_driver tpacpi_pdriver = {
-       .driver = {
-               .name = TPACPI_DRVR_NAME,
-               .owner = THIS_MODULE,
-       },
-       .suspend = tpacpi_suspend_handler,
-       .resume = tpacpi_resume_handler,
-};
-
-static struct platform_driver tpacpi_hwmon_pdriver = {
-       .driver = {
-               .name = TPACPI_HWMON_DRVR_NAME,
-               .owner = THIS_MODULE,
-       },
-};
-
-/*************************************************************************
- * sysfs support helpers
- */
-
-struct attribute_set {
-       unsigned int members, max_members;
-       struct attribute_group group;
-};
-
-struct attribute_set_obj {
-       struct attribute_set s;
-       struct attribute *a;
-} __attribute__((packed));
-
-static struct attribute_set *create_attr_set(unsigned int max_members,
-                                               const char *name)
-{
-       struct attribute_set_obj *sobj;
-
-       if (max_members == 0)
-               return NULL;
-
-       /* Allocates space for implicit NULL at the end too */
-       sobj = kzalloc(sizeof(struct attribute_set_obj) +
-                   max_members * sizeof(struct attribute *),
-                   GFP_KERNEL);
-       if (!sobj)
-               return NULL;
-       sobj->s.max_members = max_members;
-       sobj->s.group.attrs = &sobj->a;
-       sobj->s.group.name = name;
-
-       return &sobj->s;
-}
-
-#define destroy_attr_set(_set) \
-       kfree(_set);
-
-/* not multi-threaded safe, use it in a single thread per set */
-static int add_to_attr_set(struct attribute_set *s, struct attribute *attr)
-{
-       if (!s || !attr)
-               return -EINVAL;
-
-       if (s->members >= s->max_members)
-               return -ENOMEM;
-
-       s->group.attrs[s->members] = attr;
-       s->members++;
-
-       return 0;
-}
-
-static int add_many_to_attr_set(struct attribute_set *s,
-                       struct attribute **attr,
-                       unsigned int count)
-{
-       int i, res;
-
-       for (i = 0; i < count; i++) {
-               res = add_to_attr_set(s, attr[i]);
-               if (res)
-                       return res;
-       }
-
-       return 0;
-}
-
-static void delete_attr_set(struct attribute_set *s, struct kobject *kobj)
-{
-       sysfs_remove_group(kobj, &s->group);
-       destroy_attr_set(s);
-}
-
-#define register_attr_set_with_sysfs(_attr_set, _kobj) \
-       sysfs_create_group(_kobj, &_attr_set->group)
-
-static int parse_strtoul(const char *buf,
-               unsigned long max, unsigned long *value)
-{
-       char *endp;
-
-       while (*buf && isspace(*buf))
-               buf++;
-       *value = simple_strtoul(buf, &endp, 0);
-       while (*endp && isspace(*endp))
-               endp++;
-       if (*endp || *value > max)
-               return -EINVAL;
-
-       return 0;
-}
-
-static void tpacpi_disable_brightness_delay(void)
-{
-       if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
-               printk(TPACPI_NOTICE
-                       "ACPI backlight control delay disabled\n");
-}
-
-static int __init tpacpi_query_bcl_levels(acpi_handle handle)
-{
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
-       int rc;
-
-       if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
-               obj = (union acpi_object *)buffer.pointer;
-               if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
-                       printk(TPACPI_ERR "Unknown _BCL data, "
-                              "please report this to %s\n", TPACPI_MAIL);
-                       rc = 0;
-               } else {
-                       rc = obj->package.count;
-               }
-       } else {
-               return 0;
-       }
-
-       kfree(buffer.pointer);
-       return rc;
-}
-
-static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
-                                       u32 lvl, void *context, void **rv)
-{
-       char name[ACPI_PATH_SEGMENT_LENGTH];
-       struct acpi_buffer buffer = { sizeof(name), &name };
-
-       if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
-           !strncmp("_BCL", name, sizeof(name) - 1)) {
-               BUG_ON(!rv || !*rv);
-               **(int **)rv = tpacpi_query_bcl_levels(handle);
-               return AE_CTRL_TERMINATE;
-       } else {
-               return AE_OK;
-       }
-}
-
-/*
- * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
- */
-static int __init tpacpi_check_std_acpi_brightness_support(void)
-{
-       int status;
-       int bcl_levels = 0;
-       void *bcl_ptr = &bcl_levels;
-
-       if (!vid_handle) {
-               TPACPI_ACPIHANDLE_INIT(vid);
-       }
-       if (!vid_handle)
-               return 0;
-
-       /*
-        * Search for a _BCL method, and execute it.  This is safe on all
-        * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
-        * BIOS in ACPI backlight control mode.  We do NOT have to care
-        * about calling the _BCL method in an enabled video device, any
-        * will do for our purposes.
-        */
-
-       status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
-                                    tpacpi_acpi_walk_find_bcl, NULL,
-                                    &bcl_ptr);
-
-       if (ACPI_SUCCESS(status) && bcl_levels > 2) {
-               tp_features.bright_acpimode = 1;
-               return (bcl_levels - 2);
-       }
-
-       return 0;
-}
-
-static int __init tpacpi_new_rfkill(const unsigned int id,
-                       struct rfkill **rfk,
-                       const enum rfkill_type rfktype,
-                       const char *name,
-                       int (*toggle_radio)(void *, enum rfkill_state),
-                       int (*get_state)(void *, enum rfkill_state *))
-{
-       int res;
-       enum rfkill_state initial_state;
-
-       *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
-       if (!*rfk) {
-               printk(TPACPI_ERR
-                       "failed to allocate memory for rfkill class\n");
-               return -ENOMEM;
-       }
-
-       (*rfk)->name = name;
-       (*rfk)->get_state = get_state;
-       (*rfk)->toggle_radio = toggle_radio;
-
-       if (!get_state(NULL, &initial_state))
-               (*rfk)->state = initial_state;
-
-       res = rfkill_register(*rfk);
-       if (res < 0) {
-               printk(TPACPI_ERR
-                       "failed to register %s rfkill switch: %d\n",
-                       name, res);
-               rfkill_free(*rfk);
-               *rfk = NULL;
-               return res;
-       }
-
-       return 0;
-}
-
-/*************************************************************************
- * thinkpad-acpi driver attributes
- */
-
-/* interface_version --------------------------------------------------- */
-static ssize_t tpacpi_driver_interface_version_show(
-                               struct device_driver *drv,
-                               char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
-}
-
-static DRIVER_ATTR(interface_version, S_IRUGO,
-               tpacpi_driver_interface_version_show, NULL);
-
-/* debug_level --------------------------------------------------------- */
-static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
-                                               char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
-}
-
-static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
-                                               const char *buf, size_t count)
-{
-       unsigned long t;
-
-       if (parse_strtoul(buf, 0xffff, &t))
-               return -EINVAL;
-
-       dbg_level = t;
-
-       return count;
-}
-
-static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
-               tpacpi_driver_debug_show, tpacpi_driver_debug_store);
-
-/* version ------------------------------------------------------------- */
-static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
-                                               char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%s v%s\n",
-                       TPACPI_DESC, TPACPI_VERSION);
-}
-
-static DRIVER_ATTR(version, S_IRUGO,
-               tpacpi_driver_version_show, NULL);
-
-/* --------------------------------------------------------------------- */
-
-static struct driver_attribute *tpacpi_driver_attributes[] = {
-       &driver_attr_debug_level, &driver_attr_version,
-       &driver_attr_interface_version,
-};
-
-static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
-{
-       int i, res;
-
-       i = 0;
-       res = 0;
-       while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
-               res = driver_create_file(drv, tpacpi_driver_attributes[i]);
-               i++;
-       }
-
-       return res;
-}
-
-static void tpacpi_remove_driver_attributes(struct device_driver *drv)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
-               driver_remove_file(drv, tpacpi_driver_attributes[i]);
-}
-
-/****************************************************************************
- ****************************************************************************
- *
- * Subdrivers
- *
- ****************************************************************************
- ****************************************************************************/
-
-/*************************************************************************
- * thinkpad-acpi init subdriver
- */
-
-static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
-{
-       printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
-       printk(TPACPI_INFO "%s\n", TPACPI_URL);
-
-       printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n",
-               (thinkpad_id.bios_version_str) ?
-                       thinkpad_id.bios_version_str : "unknown",
-               (thinkpad_id.ec_version_str) ?
-                       thinkpad_id.ec_version_str : "unknown");
-
-       if (thinkpad_id.vendor && thinkpad_id.model_str)
-               printk(TPACPI_INFO "%s %s, model %s\n",
-                       (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
-                               "IBM" : ((thinkpad_id.vendor ==
-                                               PCI_VENDOR_ID_LENOVO) ?
-                                       "Lenovo" : "Unknown vendor"),
-                       thinkpad_id.model_str,
-                       (thinkpad_id.nummodel_str) ?
-                               thinkpad_id.nummodel_str : "unknown");
-
-       return 0;
-}
-
-static int thinkpad_acpi_driver_read(char *p)
-{
-       int len = 0;
-
-       len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC);
-       len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION);
-
-       return len;
-}
-
-static struct ibm_struct thinkpad_acpi_driver_data = {
-       .name = "driver",
-       .read = thinkpad_acpi_driver_read,
-};
-
-/*************************************************************************
- * Hotkey subdriver
- */
-
-enum { /* hot key scan codes (derived from ACPI DSDT) */
-       TP_ACPI_HOTKEYSCAN_FNF1         = 0,
-       TP_ACPI_HOTKEYSCAN_FNF2,
-       TP_ACPI_HOTKEYSCAN_FNF3,
-       TP_ACPI_HOTKEYSCAN_FNF4,
-       TP_ACPI_HOTKEYSCAN_FNF5,
-       TP_ACPI_HOTKEYSCAN_FNF6,
-       TP_ACPI_HOTKEYSCAN_FNF7,
-       TP_ACPI_HOTKEYSCAN_FNF8,
-       TP_ACPI_HOTKEYSCAN_FNF9,
-       TP_ACPI_HOTKEYSCAN_FNF10,
-       TP_ACPI_HOTKEYSCAN_FNF11,
-       TP_ACPI_HOTKEYSCAN_FNF12,
-       TP_ACPI_HOTKEYSCAN_FNBACKSPACE,
-       TP_ACPI_HOTKEYSCAN_FNINSERT,
-       TP_ACPI_HOTKEYSCAN_FNDELETE,
-       TP_ACPI_HOTKEYSCAN_FNHOME,
-       TP_ACPI_HOTKEYSCAN_FNEND,
-       TP_ACPI_HOTKEYSCAN_FNPAGEUP,
-       TP_ACPI_HOTKEYSCAN_FNPAGEDOWN,
-       TP_ACPI_HOTKEYSCAN_FNSPACE,
-       TP_ACPI_HOTKEYSCAN_VOLUMEUP,
-       TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
-       TP_ACPI_HOTKEYSCAN_MUTE,
-       TP_ACPI_HOTKEYSCAN_THINKPAD,
-};
-
-enum { /* Keys available through NVRAM polling */
-       TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U,
-       TPACPI_HKEY_NVRAM_GOOD_MASK  = 0x00fb8000U,
-};
-
-enum { /* Positions of some of the keys in hotkey masks */
-       TP_ACPI_HKEY_DISPSWTCH_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF7,
-       TP_ACPI_HKEY_DISPXPAND_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF8,
-       TP_ACPI_HKEY_HIBERNATE_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
-       TP_ACPI_HKEY_BRGHTUP_MASK       = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
-       TP_ACPI_HKEY_BRGHTDWN_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
-       TP_ACPI_HKEY_THNKLGHT_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
-       TP_ACPI_HKEY_ZOOM_MASK          = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
-       TP_ACPI_HKEY_VOLUP_MASK         = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
-       TP_ACPI_HKEY_VOLDWN_MASK        = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
-       TP_ACPI_HKEY_MUTE_MASK          = 1 << TP_ACPI_HOTKEYSCAN_MUTE,
-       TP_ACPI_HKEY_THINKPAD_MASK      = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD,
-};
-
-enum { /* NVRAM to ACPI HKEY group map */
-       TP_NVRAM_HKEY_GROUP_HK2         = TP_ACPI_HKEY_THINKPAD_MASK |
-                                         TP_ACPI_HKEY_ZOOM_MASK |
-                                         TP_ACPI_HKEY_DISPSWTCH_MASK |
-                                         TP_ACPI_HKEY_HIBERNATE_MASK,
-       TP_NVRAM_HKEY_GROUP_BRIGHTNESS  = TP_ACPI_HKEY_BRGHTUP_MASK |
-                                         TP_ACPI_HKEY_BRGHTDWN_MASK,
-       TP_NVRAM_HKEY_GROUP_VOLUME      = TP_ACPI_HKEY_VOLUP_MASK |
-                                         TP_ACPI_HKEY_VOLDWN_MASK |
-                                         TP_ACPI_HKEY_MUTE_MASK,
-};
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-struct tp_nvram_state {
-       u16 thinkpad_toggle:1;
-       u16 zoom_toggle:1;
-       u16 display_toggle:1;
-       u16 thinklight_toggle:1;
-       u16 hibernate_toggle:1;
-       u16 displayexp_toggle:1;
-       u16 display_state:1;
-       u16 brightness_toggle:1;
-       u16 volume_toggle:1;
-       u16 mute:1;
-
-       u8 brightness_level;
-       u8 volume_level;
-};
-
-static struct task_struct *tpacpi_hotkey_task;
-static u32 hotkey_source_mask;         /* bit mask 0=ACPI,1=NVRAM */
-static int hotkey_poll_freq = 10;      /* Hz */
-static struct mutex hotkey_thread_mutex;
-static struct mutex hotkey_thread_data_mutex;
-static unsigned int hotkey_config_change;
-
-#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-#define hotkey_source_mask 0U
-
-#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-static struct mutex hotkey_mutex;
-
-static enum {  /* Reasons for waking up */
-       TP_ACPI_WAKEUP_NONE = 0,        /* None or unknown */
-       TP_ACPI_WAKEUP_BAYEJ,           /* Bay ejection request */
-       TP_ACPI_WAKEUP_UNDOCK,          /* Undock request */
-} hotkey_wakeup_reason;
-
-static int hotkey_autosleep_ack;
-
-static int hotkey_orig_status;
-static u32 hotkey_orig_mask;
-static u32 hotkey_all_mask;
-static u32 hotkey_reserved_mask;
-static u32 hotkey_mask;
-
-static unsigned int hotkey_report_mode;
-
-static u16 *hotkey_keycode_map;
-
-static struct attribute_set *hotkey_dev_attributes;
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-#define HOTKEY_CONFIG_CRITICAL_START \
-       do { \
-               mutex_lock(&hotkey_thread_data_mutex); \
-               hotkey_config_change++; \
-       } while (0);
-#define HOTKEY_CONFIG_CRITICAL_END \
-       mutex_unlock(&hotkey_thread_data_mutex);
-#else
-#define HOTKEY_CONFIG_CRITICAL_START
-#define HOTKEY_CONFIG_CRITICAL_END
-#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-/* HKEY.MHKG() return bits */
-#define TP_HOTKEY_TABLET_MASK (1 << 3)
-
-static int hotkey_get_wlsw(int *status)
-{
-       if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
-               return -EIO;
-       return 0;
-}
-
-static int hotkey_get_tablet_mode(int *status)
-{
-       int s;
-
-       if (!acpi_evalf(hkey_handle, &s, "MHKG", "d"))
-               return -EIO;
-
-       *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
-       return 0;
-}
-
-/*
- * Call with hotkey_mutex held
- */
-static int hotkey_mask_get(void)
-{
-       u32 m = 0;
-
-       if (tp_features.hotkey_mask) {
-               if (!acpi_evalf(hkey_handle, &m, "DHKN", "d"))
-                       return -EIO;
-       }
-       hotkey_mask = m | (hotkey_source_mask & hotkey_mask);
-
-       return 0;
-}
-
-/*
- * Call with hotkey_mutex held
- */
-static int hotkey_mask_set(u32 mask)
-{
-       int i;
-       int rc = 0;
-
-       if (tp_features.hotkey_mask) {
-               if (!tp_warned.hotkey_mask_ff &&
-                   (mask == 0xffff || mask == 0xffffff ||
-                    mask == 0xffffffff)) {
-                       tp_warned.hotkey_mask_ff = 1;
-                       printk(TPACPI_NOTICE
-                              "setting the hotkey mask to 0x%08x is likely "
-                              "not the best way to go about it\n", mask);
-                       printk(TPACPI_NOTICE
-                              "please consider using the driver defaults, "
-                              "and refer to up-to-date thinkpad-acpi "
-                              "documentation\n");
-               }
-
-               HOTKEY_CONFIG_CRITICAL_START
-               for (i = 0; i < 32; i++) {
-                       u32 m = 1 << i;
-                       /* enable in firmware mask only keys not in NVRAM
-                        * mode, but enable the key in the cached hotkey_mask
-                        * regardless of mode, or the key will end up
-                        * disabled by hotkey_mask_get() */
-                       if (!acpi_evalf(hkey_handle,
-                                       NULL, "MHKM", "vdd", i + 1,
-                                       !!((mask & ~hotkey_source_mask) & m))) {
-                               rc = -EIO;
-                               break;
-                       } else {
-                               hotkey_mask = (hotkey_mask & ~m) | (mask & m);
-                       }
-               }
-               HOTKEY_CONFIG_CRITICAL_END
-
-               /* hotkey_mask_get must be called unconditionally below */
-               if (!hotkey_mask_get() && !rc &&
-                   (hotkey_mask & ~hotkey_source_mask) !=
-                    (mask & ~hotkey_source_mask)) {
-                       printk(TPACPI_NOTICE
-                              "requested hot key mask 0x%08x, but "
-                              "firmware forced it to 0x%08x\n",
-                              mask, hotkey_mask);
-               }
-       } else {
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-               HOTKEY_CONFIG_CRITICAL_START
-               hotkey_mask = mask & hotkey_source_mask;
-               HOTKEY_CONFIG_CRITICAL_END
-               hotkey_mask_get();
-               if (hotkey_mask != mask) {
-                       printk(TPACPI_NOTICE
-                              "requested hot key mask 0x%08x, "
-                              "forced to 0x%08x (NVRAM poll mask is "
-                              "0x%08x): no firmware mask support\n",
-                              mask, hotkey_mask, hotkey_source_mask);
-               }
-#else
-               hotkey_mask_get();
-               rc = -ENXIO;
-#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-       }
-
-       return rc;
-}
-
-static int hotkey_status_get(int *status)
-{
-       if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
-               return -EIO;
-
-       return 0;
-}
-
-static int hotkey_status_set(int status)
-{
-       if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
-               return -EIO;
-
-       return 0;
-}
-
-static void tpacpi_input_send_tabletsw(void)
-{
-       int state;
-
-       if (tp_features.hotkey_tablet &&
-           !hotkey_get_tablet_mode(&state)) {
-               mutex_lock(&tpacpi_inputdev_send_mutex);
-
-               input_report_switch(tpacpi_inputdev,
-                                   SW_TABLET_MODE, !!state);
-               input_sync(tpacpi_inputdev);
-
-               mutex_unlock(&tpacpi_inputdev_send_mutex);
-       }
-}
-
-static void tpacpi_input_send_key(unsigned int scancode)
-{
-       unsigned int keycode;
-
-       keycode = hotkey_keycode_map[scancode];
-
-       if (keycode != KEY_RESERVED) {
-               mutex_lock(&tpacpi_inputdev_send_mutex);
-
-               input_report_key(tpacpi_inputdev, keycode, 1);
-               if (keycode == KEY_UNKNOWN)
-                       input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
-                                   scancode);
-               input_sync(tpacpi_inputdev);
-
-               input_report_key(tpacpi_inputdev, keycode, 0);
-               if (keycode == KEY_UNKNOWN)
-                       input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
-                                   scancode);
-               input_sync(tpacpi_inputdev);
-
-               mutex_unlock(&tpacpi_inputdev_send_mutex);
-       }
-}
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
-
-static void tpacpi_hotkey_send_key(unsigned int scancode)
-{
-       tpacpi_input_send_key(scancode);
-       if (hotkey_report_mode < 2) {
-               acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
-                                               0x80, 0x1001 + scancode);
-       }
-}
-
-static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m)
-{
-       u8 d;
-
-       if (m & TP_NVRAM_HKEY_GROUP_HK2) {
-               d = nvram_read_byte(TP_NVRAM_ADDR_HK2);
-               n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD);
-               n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM);
-               n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
-               n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
-       }
-       if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
-               d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
-               n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
-       }
-       if (m & TP_ACPI_HKEY_DISPXPAND_MASK) {
-               d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO);
-               n->displayexp_toggle =
-                               !!(d & TP_NVRAM_MASK_HKT_DISPEXPND);
-       }
-       if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) {
-               d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
-               n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
-                               >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
-               n->brightness_toggle =
-                               !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS);
-       }
-       if (m & TP_NVRAM_HKEY_GROUP_VOLUME) {
-               d = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
-               n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME)
-                               >> TP_NVRAM_POS_LEVEL_VOLUME;
-               n->mute = !!(d & TP_NVRAM_MASK_MUTE);
-               n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME);
-       }
-}
-
-#define TPACPI_COMPARE_KEY(__scancode, __member) \
-       do { \
-               if ((mask & (1 << __scancode)) && \
-                   oldn->__member != newn->__member) \
-               tpacpi_hotkey_send_key(__scancode); \
-       } while (0)
-
-#define TPACPI_MAY_SEND_KEY(__scancode) \
-       do { if (mask & (1 << __scancode)) \
-               tpacpi_hotkey_send_key(__scancode); } while (0)
-
-static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
-                                          struct tp_nvram_state *newn,
-                                          u32 mask)
-{
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle);
-
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle);
-
-       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
-
-       /* handle volume */
-       if (oldn->volume_toggle != newn->volume_toggle) {
-               if (oldn->mute != newn->mute) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
-               }
-               if (oldn->volume_level > newn->volume_level) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
-               } else if (oldn->volume_level < newn->volume_level) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
-               } else if (oldn->mute == newn->mute) {
-                       /* repeated key presses that didn't change state */
-                       if (newn->mute) {
-                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
-                       } else if (newn->volume_level != 0) {
-                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
-                       } else {
-                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
-                       }
-               }
-       }
-
-       /* handle brightness */
-       if (oldn->brightness_toggle != newn->brightness_toggle) {
-               if (oldn->brightness_level < newn->brightness_level) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
-               } else if (oldn->brightness_level > newn->brightness_level) {
-                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
-               } else {
-                       /* repeated key presses that didn't change state */
-                       if (newn->brightness_level != 0) {
-                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
-                       } else {
-                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
-                       }
-               }
-       }
-}
-
-#undef TPACPI_COMPARE_KEY
-#undef TPACPI_MAY_SEND_KEY
-
-static int hotkey_kthread(void *data)
-{
-       struct tp_nvram_state s[2];
-       u32 mask;
-       unsigned int si, so;
-       unsigned long t;
-       unsigned int change_detector, must_reset;
-
-       mutex_lock(&hotkey_thread_mutex);
-
-       if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
-               goto exit;
-
-       set_freezable();
-
-       so = 0;
-       si = 1;
-       t = 0;
-
-       /* Initial state for compares */
-       mutex_lock(&hotkey_thread_data_mutex);
-       change_detector = hotkey_config_change;
-       mask = hotkey_source_mask & hotkey_mask;
-       mutex_unlock(&hotkey_thread_data_mutex);
-       hotkey_read_nvram(&s[so], mask);
-
-       while (!kthread_should_stop() && hotkey_poll_freq) {
-               if (t == 0)
-                       t = 1000/hotkey_poll_freq;
-               t = msleep_interruptible(t);
-               if (unlikely(kthread_should_stop()))
-                       break;
-               must_reset = try_to_freeze();
-               if (t > 0 && !must_reset)
-                       continue;
-
-               mutex_lock(&hotkey_thread_data_mutex);
-               if (must_reset || hotkey_config_change != change_detector) {
-                       /* forget old state on thaw or config change */
-                       si = so;
-                       t = 0;
-                       change_detector = hotkey_config_change;
-               }
-               mask = hotkey_source_mask & hotkey_mask;
-               mutex_unlock(&hotkey_thread_data_mutex);
-
-               if (likely(mask)) {
-                       hotkey_read_nvram(&s[si], mask);
-                       if (likely(si != so)) {
-                               hotkey_compare_and_issue_event(&s[so], &s[si],
-                                                               mask);
-                       }
-               }
-
-               so = si;
-               si ^= 1;
-       }
-
-exit:
-       mutex_unlock(&hotkey_thread_mutex);
-       return 0;
-}
-
-static void hotkey_poll_stop_sync(void)
-{
-       if (tpacpi_hotkey_task) {
-               if (frozen(tpacpi_hotkey_task) ||
-                   freezing(tpacpi_hotkey_task))
-                       thaw_process(tpacpi_hotkey_task);
-
-               kthread_stop(tpacpi_hotkey_task);
-               tpacpi_hotkey_task = NULL;
-               mutex_lock(&hotkey_thread_mutex);
-               /* at this point, the thread did exit */
-               mutex_unlock(&hotkey_thread_mutex);
-       }
-}
-
-/* call with hotkey_mutex held */
-static void hotkey_poll_setup(int may_warn)
-{
-       if ((hotkey_source_mask & hotkey_mask) != 0 &&
-           hotkey_poll_freq > 0 &&
-           (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) {
-               if (!tpacpi_hotkey_task) {
-                       tpacpi_hotkey_task = kthread_run(hotkey_kthread,
-                                       NULL, TPACPI_NVRAM_KTHREAD_NAME);
-                       if (IS_ERR(tpacpi_hotkey_task)) {
-                               tpacpi_hotkey_task = NULL;
-                               printk(TPACPI_ERR
-                                      "could not create kernel thread "
-                                      "for hotkey polling\n");
-                       }
-               }
-       } else {
-               hotkey_poll_stop_sync();
-               if (may_warn &&
-                   hotkey_source_mask != 0 && hotkey_poll_freq == 0) {
-                       printk(TPACPI_NOTICE
-                               "hot keys 0x%08x require polling, "
-                               "which is currently disabled\n",
-                               hotkey_source_mask);
-               }
-       }
-}
-
-static void hotkey_poll_setup_safe(int may_warn)
-{
-       mutex_lock(&hotkey_mutex);
-       hotkey_poll_setup(may_warn);
-       mutex_unlock(&hotkey_mutex);
-}
-
-#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-static void hotkey_poll_setup_safe(int __unused)
-{
-}
-
-#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-static int hotkey_inputdev_open(struct input_dev *dev)
-{
-       switch (tpacpi_lifecycle) {
-       case TPACPI_LIFE_INIT:
-               /*
-                * hotkey_init will call hotkey_poll_setup_safe
-                * at the appropriate moment
-                */
-               return 0;
-       case TPACPI_LIFE_EXITING:
-               return -EBUSY;
-       case TPACPI_LIFE_RUNNING:
-               hotkey_poll_setup_safe(0);
-               return 0;
-       }
-
-       /* Should only happen if tpacpi_lifecycle is corrupt */
-       BUG();
-       return -EBUSY;
-}
-
-static void hotkey_inputdev_close(struct input_dev *dev)
-{
-       /* disable hotkey polling when possible */
-       if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING)
-               hotkey_poll_setup_safe(0);
-}
-
-/* sysfs hotkey enable ------------------------------------------------- */
-static ssize_t hotkey_enable_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int res, status;
-
-       res = hotkey_status_get(&status);
-       if (res)
-               return res;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", status);
-}
-
-static ssize_t hotkey_enable_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-       int res;
-
-       if (parse_strtoul(buf, 1, &t))
-               return -EINVAL;
-
-       res = hotkey_status_set(t);
-
-       return (res) ? res : count;
-}
-
-static struct device_attribute dev_attr_hotkey_enable =
-       __ATTR(hotkey_enable, S_IWUSR | S_IRUGO,
-               hotkey_enable_show, hotkey_enable_store);
-
-/* sysfs hotkey mask --------------------------------------------------- */
-static ssize_t hotkey_mask_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int res;
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-       res = hotkey_mask_get();
-       mutex_unlock(&hotkey_mutex);
-
-       return (res)?
-               res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask);
-}
-
-static ssize_t hotkey_mask_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-       int res;
-
-       if (parse_strtoul(buf, 0xffffffffUL, &t))
-               return -EINVAL;
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-
-       res = hotkey_mask_set(t);
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       hotkey_poll_setup(1);
-#endif
-
-       mutex_unlock(&hotkey_mutex);
-
-       return (res) ? res : count;
-}
-
-static struct device_attribute dev_attr_hotkey_mask =
-       __ATTR(hotkey_mask, S_IWUSR | S_IRUGO,
-               hotkey_mask_show, hotkey_mask_store);
-
-/* sysfs hotkey bios_enabled ------------------------------------------- */
-static ssize_t hotkey_bios_enabled_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
-}
-
-static struct device_attribute dev_attr_hotkey_bios_enabled =
-       __ATTR(hotkey_bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
-
-/* sysfs hotkey bios_mask ---------------------------------------------- */
-static ssize_t hotkey_bios_mask_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
-}
-
-static struct device_attribute dev_attr_hotkey_bios_mask =
-       __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
-
-/* sysfs hotkey all_mask ----------------------------------------------- */
-static ssize_t hotkey_all_mask_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%08x\n",
-                               hotkey_all_mask | hotkey_source_mask);
-}
-
-static struct device_attribute dev_attr_hotkey_all_mask =
-       __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL);
-
-/* sysfs hotkey recommended_mask --------------------------------------- */
-static ssize_t hotkey_recommended_mask_show(struct device *dev,
-                                           struct device_attribute *attr,
-                                           char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%08x\n",
-                       (hotkey_all_mask | hotkey_source_mask)
-                       & ~hotkey_reserved_mask);
-}
-
-static struct device_attribute dev_attr_hotkey_recommended_mask =
-       __ATTR(hotkey_recommended_mask, S_IRUGO,
-               hotkey_recommended_mask_show, NULL);
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-
-/* sysfs hotkey hotkey_source_mask ------------------------------------- */
-static ssize_t hotkey_source_mask_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask);
-}
-
-static ssize_t hotkey_source_mask_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-
-       if (parse_strtoul(buf, 0xffffffffUL, &t) ||
-               ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0))
-               return -EINVAL;
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-
-       HOTKEY_CONFIG_CRITICAL_START
-       hotkey_source_mask = t;
-       HOTKEY_CONFIG_CRITICAL_END
-
-       hotkey_poll_setup(1);
-
-       mutex_unlock(&hotkey_mutex);
-
-       return count;
-}
-
-static struct device_attribute dev_attr_hotkey_source_mask =
-       __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO,
-               hotkey_source_mask_show, hotkey_source_mask_store);
-
-/* sysfs hotkey hotkey_poll_freq --------------------------------------- */
-static ssize_t hotkey_poll_freq_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq);
-}
-
-static ssize_t hotkey_poll_freq_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-
-       if (parse_strtoul(buf, 25, &t))
-               return -EINVAL;
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-
-       hotkey_poll_freq = t;
-
-       hotkey_poll_setup(1);
-       mutex_unlock(&hotkey_mutex);
-
-       return count;
-}
-
-static struct device_attribute dev_attr_hotkey_poll_freq =
-       __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO,
-               hotkey_poll_freq_show, hotkey_poll_freq_store);
-
-#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
-
-/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
-static ssize_t hotkey_radio_sw_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int res, s;
-       res = hotkey_get_wlsw(&s);
-       if (res < 0)
-               return res;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
-}
-
-static struct device_attribute dev_attr_hotkey_radio_sw =
-       __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
-
-static void hotkey_radio_sw_notify_change(void)
-{
-       if (tp_features.hotkey_wlsw)
-               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
-                            "hotkey_radio_sw");
-}
-
-/* sysfs hotkey tablet mode (pollable) --------------------------------- */
-static ssize_t hotkey_tablet_mode_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int res, s;
-       res = hotkey_get_tablet_mode(&s);
-       if (res < 0)
-               return res;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
-}
-
-static struct device_attribute dev_attr_hotkey_tablet_mode =
-       __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL);
-
-static void hotkey_tablet_mode_notify_change(void)
-{
-       if (tp_features.hotkey_tablet)
-               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
-                            "hotkey_tablet_mode");
-}
-
-/* sysfs hotkey report_mode -------------------------------------------- */
-static ssize_t hotkey_report_mode_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-               (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
-}
-
-static struct device_attribute dev_attr_hotkey_report_mode =
-       __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
-
-/* sysfs wakeup reason (pollable) -------------------------------------- */
-static ssize_t hotkey_wakeup_reason_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
-}
-
-static struct device_attribute dev_attr_hotkey_wakeup_reason =
-       __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
-
-static void hotkey_wakeup_reason_notify_change(void)
-{
-       if (tp_features.hotkey_mask)
-               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
-                            "wakeup_reason");
-}
-
-/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
-static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
-}
-
-static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
-       __ATTR(wakeup_hotunplug_complete, S_IRUGO,
-              hotkey_wakeup_hotunplug_complete_show, NULL);
-
-static void hotkey_wakeup_hotunplug_complete_notify_change(void)
-{
-       if (tp_features.hotkey_mask)
-               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
-                            "wakeup_hotunplug_complete");
-}
-
-/* --------------------------------------------------------------------- */
-
-static struct attribute *hotkey_attributes[] __initdata = {
-       &dev_attr_hotkey_enable.attr,
-       &dev_attr_hotkey_bios_enabled.attr,
-       &dev_attr_hotkey_report_mode.attr,
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       &dev_attr_hotkey_mask.attr,
-       &dev_attr_hotkey_all_mask.attr,
-       &dev_attr_hotkey_recommended_mask.attr,
-       &dev_attr_hotkey_source_mask.attr,
-       &dev_attr_hotkey_poll_freq.attr,
-#endif
-};
-
-static struct attribute *hotkey_mask_attributes[] __initdata = {
-       &dev_attr_hotkey_bios_mask.attr,
-#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       &dev_attr_hotkey_mask.attr,
-       &dev_attr_hotkey_all_mask.attr,
-       &dev_attr_hotkey_recommended_mask.attr,
-#endif
-       &dev_attr_hotkey_wakeup_reason.attr,
-       &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
-};
-
-static void bluetooth_update_rfk(void);
-static void wan_update_rfk(void);
-static void tpacpi_send_radiosw_update(void)
-{
-       int wlsw;
-
-       /* Sync these BEFORE sending any rfkill events */
-       if (tp_features.bluetooth)
-               bluetooth_update_rfk();
-       if (tp_features.wan)
-               wan_update_rfk();
-
-       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
-               mutex_lock(&tpacpi_inputdev_send_mutex);
-
-               input_report_switch(tpacpi_inputdev,
-                                   SW_RFKILL_ALL, !!wlsw);
-               input_sync(tpacpi_inputdev);
-
-               mutex_unlock(&tpacpi_inputdev_send_mutex);
-       }
-       hotkey_radio_sw_notify_change();
-}
-
-static void hotkey_exit(void)
-{
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       hotkey_poll_stop_sync();
-#endif
-
-       if (hotkey_dev_attributes)
-               delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
-
-       kfree(hotkey_keycode_map);
-
-       if (tp_features.hotkey) {
-               dbg_printk(TPACPI_DBG_EXIT,
-                          "restoring original hot key mask\n");
-               /* no short-circuit boolean operator below! */
-               if ((hotkey_mask_set(hotkey_orig_mask) |
-                    hotkey_status_set(hotkey_orig_status)) != 0)
-                       printk(TPACPI_ERR
-                              "failed to restore hot key mask "
-                              "to BIOS defaults\n");
-       }
-}
-
-static int __init hotkey_init(struct ibm_init_struct *iibm)
-{
-       /* Requirements for changing the default keymaps:
-        *
-        * 1. Many of the keys are mapped to KEY_RESERVED for very
-        *    good reasons.  Do not change them unless you have deep
-        *    knowledge on the IBM and Lenovo ThinkPad firmware for
-        *    the various ThinkPad models.  The driver behaves
-        *    differently for KEY_RESERVED: such keys have their
-        *    hot key mask *unset* in mask_recommended, and also
-        *    in the initial hot key mask programmed into the
-        *    firmware at driver load time, which means the firm-
-        *    ware may react very differently if you change them to
-        *    something else;
-        *
-        * 2. You must be subscribed to the linux-thinkpad and
-        *    ibm-acpi-devel mailing lists, and you should read the
-        *    list archives since 2007 if you want to change the
-        *    keymaps.  This requirement exists so that you will
-        *    know the past history of problems with the thinkpad-
-        *    acpi driver keymaps, and also that you will be
-        *    listening to any bug reports;
-        *
-        * 3. Do not send thinkpad-acpi specific patches directly to
-        *    for merging, *ever*.  Send them to the linux-acpi
-        *    mailinglist for comments.  Merging is to be done only
-        *    through acpi-test and the ACPI maintainer.
-        *
-        * If the above is too much to ask, don't change the keymap.
-        * Ask the thinkpad-acpi maintainer to do it, instead.
-        */
-       static u16 ibm_keycode_map[] __initdata = {
-               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
-               KEY_FN_F1,      KEY_FN_F2,      KEY_COFFEE,     KEY_SLEEP,
-               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
-               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
-
-               /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
-               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
-               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
-               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
-
-               /* brightness: firmware always reacts to them, unless
-                * X.org did some tricks in the radeon BIOS scratch
-                * registers of *some* models */
-               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
-               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
-
-               /* Thinklight: firmware always react to it */
-               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
-
-               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
-               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-
-               /* Volume: firmware always react to it and reprograms
-                * the built-in *extra* mixer.  Never map it to control
-                * another mixer by default. */
-               KEY_RESERVED,   /* 0x14: VOLUME UP */
-               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
-               KEY_RESERVED,   /* 0x16: MUTE */
-
-               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
-
-               /* (assignments unknown, please report if found) */
-               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-       };
-       static u16 lenovo_keycode_map[] __initdata = {
-               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
-               KEY_FN_F1,      KEY_COFFEE,     KEY_BATTERY,    KEY_SLEEP,
-               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
-               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
-
-               /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
-               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
-               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
-               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
-
-               /* These either have to go through ACPI video, or
-                * act like in the IBM ThinkPads, so don't ever
-                * enable them by default */
-               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
-               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
-
-               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
-
-               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
-               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
-
-               /* Volume: z60/z61, T60 (BIOS version?): firmware always
-                * react to it and reprograms the built-in *extra* mixer.
-                * Never map it to control another mixer by default.
-                *
-                * T60?, T61, R60?, R61: firmware and EC tries to send
-                * these over the regular keyboard, so these are no-ops,
-                * but there are still weird bugs re. MUTE, so do not
-                * change unless you get test reports from all Lenovo
-                * models.  May cause the BIOS to interfere with the
-                * HDA mixer.
-                */
-               KEY_RESERVED,   /* 0x14: VOLUME UP */
-               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
-               KEY_RESERVED,   /* 0x16: MUTE */
-
-               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
-
-               /* (assignments unknown, please report if found) */
-               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
-       };
-
-#define TPACPI_HOTKEY_MAP_LEN          ARRAY_SIZE(ibm_keycode_map)
-#define TPACPI_HOTKEY_MAP_SIZE         sizeof(ibm_keycode_map)
-#define TPACPI_HOTKEY_MAP_TYPESIZE     sizeof(ibm_keycode_map[0])
-
-       int res, i;
-       int status;
-       int hkeyv;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
-
-       BUG_ON(!tpacpi_inputdev);
-       BUG_ON(tpacpi_inputdev->open != NULL ||
-              tpacpi_inputdev->close != NULL);
-
-       TPACPI_ACPIHANDLE_INIT(hkey);
-       mutex_init(&hotkey_mutex);
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       mutex_init(&hotkey_thread_mutex);
-       mutex_init(&hotkey_thread_data_mutex);
-#endif
-
-       /* hotkey not supported on 570 */
-       tp_features.hotkey = hkey_handle != NULL;
-
-       vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
-               str_supported(tp_features.hotkey));
-
-       if (!tp_features.hotkey)
-               return 1;
-
-       tpacpi_disable_brightness_delay();
-
-       hotkey_dev_attributes = create_attr_set(13, NULL);
-       if (!hotkey_dev_attributes)
-               return -ENOMEM;
-       res = add_many_to_attr_set(hotkey_dev_attributes,
-                       hotkey_attributes,
-                       ARRAY_SIZE(hotkey_attributes));
-       if (res)
-               goto err_exit;
-
-       /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-          A30, R30, R31, T20-22, X20-21, X22-24.  Detected by checking
-          for HKEY interface version 0x100 */
-       if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
-               if ((hkeyv >> 8) != 1) {
-                       printk(TPACPI_ERR "unknown version of the "
-                              "HKEY interface: 0x%x\n", hkeyv);
-                       printk(TPACPI_ERR "please report this to %s\n",
-                              TPACPI_MAIL);
-               } else {
-                       /*
-                        * MHKV 0x100 in A31, R40, R40e,
-                        * T4x, X31, and later
-                        */
-                       tp_features.hotkey_mask = 1;
-               }
-       }
-
-       vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
-               str_supported(tp_features.hotkey_mask));
-
-       if (tp_features.hotkey_mask) {
-               if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
-                               "MHKA", "qd")) {
-                       printk(TPACPI_ERR
-                              "missing MHKA handler, "
-                              "please report this to %s\n",
-                              TPACPI_MAIL);
-                       /* FN+F12, FN+F4, FN+F3 */
-                       hotkey_all_mask = 0x080cU;
-               }
-       }
-
-       /* hotkey_source_mask *must* be zero for
-        * the first hotkey_mask_get */
-       res = hotkey_status_get(&hotkey_orig_status);
-       if (res)
-               goto err_exit;
-
-       if (tp_features.hotkey_mask) {
-               res = hotkey_mask_get();
-               if (res)
-                       goto err_exit;
-
-               hotkey_orig_mask = hotkey_mask;
-               res = add_many_to_attr_set(
-                               hotkey_dev_attributes,
-                               hotkey_mask_attributes,
-                               ARRAY_SIZE(hotkey_mask_attributes));
-               if (res)
-                       goto err_exit;
-       }
-
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
-       if (tp_features.hotkey_mask) {
-               hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
-                                       & ~hotkey_all_mask;
-       } else {
-               hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
-       }
-
-       vdbg_printk(TPACPI_DBG_INIT,
-                   "hotkey source mask 0x%08x, polling freq %d\n",
-                   hotkey_source_mask, hotkey_poll_freq);
-#endif
-
-       /* Not all thinkpads have a hardware radio switch */
-       if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
-               tp_features.hotkey_wlsw = 1;
-               printk(TPACPI_INFO
-                       "radio switch found; radios are %s\n",
-                       enabled(status, 0));
-       }
-       if (tp_features.hotkey_wlsw)
-               res = add_to_attr_set(hotkey_dev_attributes,
-                               &dev_attr_hotkey_radio_sw.attr);
-
-       /* For X41t, X60t, X61t Tablets... */
-       if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
-               tp_features.hotkey_tablet = 1;
-               printk(TPACPI_INFO
-                       "possible tablet mode switch found; "
-                       "ThinkPad in %s mode\n",
-                       (status & TP_HOTKEY_TABLET_MASK)?
-                               "tablet" : "laptop");
-               res = add_to_attr_set(hotkey_dev_attributes,
-                               &dev_attr_hotkey_tablet_mode.attr);
-       }
-
-       if (!res)
-               res = register_attr_set_with_sysfs(
-                               hotkey_dev_attributes,
-                               &tpacpi_pdev->dev.kobj);
-       if (res)
-               goto err_exit;
-
-       /* Set up key map */
-
-       hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
-                                       GFP_KERNEL);
-       if (!hotkey_keycode_map) {
-               printk(TPACPI_ERR
-                       "failed to allocate memory for key map\n");
-               res = -ENOMEM;
-               goto err_exit;
-       }
-
-       if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
-               dbg_printk(TPACPI_DBG_INIT,
-                          "using Lenovo default hot key map\n");
-               memcpy(hotkey_keycode_map, &lenovo_keycode_map,
-                       TPACPI_HOTKEY_MAP_SIZE);
-       } else {
-               dbg_printk(TPACPI_DBG_INIT,
-                          "using IBM default hot key map\n");
-               memcpy(hotkey_keycode_map, &ibm_keycode_map,
-                       TPACPI_HOTKEY_MAP_SIZE);
-       }
-
-       set_bit(EV_KEY, tpacpi_inputdev->evbit);
-       set_bit(EV_MSC, tpacpi_inputdev->evbit);
-       set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
-       tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
-       tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
-       tpacpi_inputdev->keycode = hotkey_keycode_map;
-       for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
-               if (hotkey_keycode_map[i] != KEY_RESERVED) {
-                       set_bit(hotkey_keycode_map[i],
-                               tpacpi_inputdev->keybit);
-               } else {
-                       if (i < sizeof(hotkey_reserved_mask)*8)
-                               hotkey_reserved_mask |= 1 << i;
-               }
-       }
-
-       if (tp_features.hotkey_wlsw) {
-               set_bit(EV_SW, tpacpi_inputdev->evbit);
-               set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
-       }
-       if (tp_features.hotkey_tablet) {
-               set_bit(EV_SW, tpacpi_inputdev->evbit);
-               set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
-       }
-
-       /* Do not issue duplicate brightness change events to
-        * userspace */
-       if (!tp_features.bright_acpimode)
-               /* update bright_acpimode... */
-               tpacpi_check_std_acpi_brightness_support();
-
-       if (tp_features.bright_acpimode) {
-               printk(TPACPI_INFO
-                      "This ThinkPad has standard ACPI backlight "
-                      "brightness control, supported by the ACPI "
-                      "video driver\n");
-               printk(TPACPI_NOTICE
-                      "Disabling thinkpad-acpi brightness events "
-                      "by default...\n");
-
-               /* The hotkey_reserved_mask change below is not
-                * necessary while the keys are at KEY_RESERVED in the
-                * default map, but better safe than sorry, leave it
-                * here as a marker of what we have to do, especially
-                * when we finally become able to set this at runtime
-                * on response to X.org requests */
-               hotkey_reserved_mask |=
-                       (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
-                       | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
-       }
-
-       dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
-       res = hotkey_status_set(1);
-       if (res) {
-               hotkey_exit();
-               return res;
-       }
-       res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
-                               & ~hotkey_reserved_mask)
-                               | hotkey_orig_mask);
-       if (res < 0 && res != -ENXIO) {
-               hotkey_exit();
-               return res;
-       }
-
-       dbg_printk(TPACPI_DBG_INIT,
-                       "legacy hot key reporting over procfs %s\n",
-                       (hotkey_report_mode < 2) ?
-                               "enabled" : "disabled");
-
-       tpacpi_inputdev->open = &hotkey_inputdev_open;
-       tpacpi_inputdev->close = &hotkey_inputdev_close;
-
-       hotkey_poll_setup_safe(1);
-       tpacpi_send_radiosw_update();
-       tpacpi_input_send_tabletsw();
-
-       return 0;
-
-err_exit:
-       delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
-       hotkey_dev_attributes = NULL;
-
-       return (res < 0)? res : 1;
-}
-
-static void hotkey_notify(struct ibm_struct *ibm, u32 event)
-{
-       u32 hkey;
-       unsigned int scancode;
-       int send_acpi_ev;
-       int ignore_acpi_ev;
-       int unk_ev;
-
-       if (event != 0x80) {
-               printk(TPACPI_ERR
-                      "unknown HKEY notification event %d\n", event);
-               /* forward it to userspace, maybe it knows how to handle it */
-               acpi_bus_generate_netlink_event(
-                                       ibm->acpi->device->pnp.device_class,
-                                       ibm->acpi->device->dev.bus_id,
-                                       event, 0);
-               return;
-       }
-
-       while (1) {
-               if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
-                       printk(TPACPI_ERR "failed to retrieve HKEY event\n");
-                       return;
-               }
-
-               if (hkey == 0) {
-                       /* queue empty */
-                       return;
-               }
-
-               send_acpi_ev = 1;
-               ignore_acpi_ev = 0;
-               unk_ev = 0;
-
-               switch (hkey >> 12) {
-               case 1:
-                       /* 0x1000-0x1FFF: key presses */
-                       scancode = hkey & 0xfff;
-                       if (scancode > 0 && scancode < 0x21) {
-                               scancode--;
-                               if (!(hotkey_source_mask & (1 << scancode))) {
-                                       tpacpi_input_send_key(scancode);
-                                       send_acpi_ev = 0;
-                               } else {
-                                       ignore_acpi_ev = 1;
-                               }
-                       } else {
-                               unk_ev = 1;
-                       }
-                       break;
-               case 2:
-                       /* Wakeup reason */
-                       switch (hkey) {
-                       case 0x2304: /* suspend, undock */
-                       case 0x2404: /* hibernation, undock */
-                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
-                               ignore_acpi_ev = 1;
-                               break;
-                       case 0x2305: /* suspend, bay eject */
-                       case 0x2405: /* hibernation, bay eject */
-                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
-                               ignore_acpi_ev = 1;
-                               break;
-                       default:
-                               unk_ev = 1;
-                       }
-                       if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
-                               printk(TPACPI_INFO
-                                      "woke up due to a hot-unplug "
-                                      "request...\n");
-                               hotkey_wakeup_reason_notify_change();
-                       }
-                       break;
-               case 3:
-                       /* bay-related wakeups */
-                       if (hkey == 0x3003) {
-                               hotkey_autosleep_ack = 1;
-                               printk(TPACPI_INFO
-                                      "bay ejected\n");
-                               hotkey_wakeup_hotunplug_complete_notify_change();
-                       } else {
-                               unk_ev = 1;
-                       }
-                       break;
-               case 4:
-                       /* dock-related wakeups */
-                       if (hkey == 0x4003) {
-                               hotkey_autosleep_ack = 1;
-                               printk(TPACPI_INFO
-                                      "undocked\n");
-                               hotkey_wakeup_hotunplug_complete_notify_change();
-                       } else {
-                               unk_ev = 1;
-                       }
-                       break;
-               case 5:
-                       /* 0x5000-0x5FFF: human interface helpers */
-                       switch (hkey) {
-                       case 0x5010: /* Lenovo new BIOS: brightness changed */
-                       case 0x500b: /* X61t: tablet pen inserted into bay */
-                       case 0x500c: /* X61t: tablet pen removed from bay */
-                               break;
-                       case 0x5009: /* X41t-X61t: swivel up (tablet mode) */
-                       case 0x500a: /* X41t-X61t: swivel down (normal mode) */
-                               tpacpi_input_send_tabletsw();
-                               hotkey_tablet_mode_notify_change();
-                               send_acpi_ev = 0;
-                               break;
-                       case 0x5001:
-                       case 0x5002:
-                               /* LID switch events.  Do not propagate */
-                               ignore_acpi_ev = 1;
-                               break;
-                       default:
-                               unk_ev = 1;
-                       }
-                       break;
-               case 7:
-                       /* 0x7000-0x7FFF: misc */
-                       if (tp_features.hotkey_wlsw && hkey == 0x7000) {
-                               tpacpi_send_radiosw_update();
-                               send_acpi_ev = 0;
-                               break;
-                       }
-                       /* fallthrough to default */
-               default:
-                       unk_ev = 1;
-               }
-               if (unk_ev) {
-                       printk(TPACPI_NOTICE
-                              "unhandled HKEY event 0x%04x\n", hkey);
-               }
-
-               /* Legacy events */
-               if (!ignore_acpi_ev &&
-                   (send_acpi_ev || hotkey_report_mode < 2)) {
-                       acpi_bus_generate_proc_event(ibm->acpi->device,
-                                                    event, hkey);
-               }
-
-               /* netlink events */
-               if (!ignore_acpi_ev && send_acpi_ev) {
-                       acpi_bus_generate_netlink_event(
-                                       ibm->acpi->device->pnp.device_class,
-                                       ibm->acpi->device->dev.bus_id,
-                                       event, hkey);
-               }
-       }
-}
-
-static void hotkey_suspend(pm_message_t state)
-{
-       /* Do these on suspend, we get the events on early resume! */
-       hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
-       hotkey_autosleep_ack = 0;
-}
-
-static void hotkey_resume(void)
-{
-       tpacpi_disable_brightness_delay();
-
-       if (hotkey_mask_get())
-               printk(TPACPI_ERR
-                      "error while trying to read hot key mask "
-                      "from firmware\n");
-       tpacpi_send_radiosw_update();
-       hotkey_tablet_mode_notify_change();
-       hotkey_wakeup_reason_notify_change();
-       hotkey_wakeup_hotunplug_complete_notify_change();
-       hotkey_poll_setup_safe(0);
-}
-
-/* procfs -------------------------------------------------------------- */
-static int hotkey_read(char *p)
-{
-       int res, status;
-       int len = 0;
-
-       if (!tp_features.hotkey) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
-       }
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-       res = hotkey_status_get(&status);
-       if (!res)
-               res = hotkey_mask_get();
-       mutex_unlock(&hotkey_mutex);
-       if (res)
-               return res;
-
-       len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
-       if (tp_features.hotkey_mask) {
-               len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask);
-               len += sprintf(p + len,
-                              "commands:\tenable, disable, reset, <mask>\n");
-       } else {
-               len += sprintf(p + len, "mask:\t\tnot supported\n");
-               len += sprintf(p + len, "commands:\tenable, disable, reset\n");
-       }
-
-       return len;
-}
-
-static int hotkey_write(char *buf)
-{
-       int res, status;
-       u32 mask;
-       char *cmd;
-
-       if (!tp_features.hotkey)
-               return -ENODEV;
-
-       if (mutex_lock_interruptible(&hotkey_mutex))
-               return -ERESTARTSYS;
-
-       status = -1;
-       mask = hotkey_mask;
-
-       res = 0;
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "enable") == 0) {
-                       status = 1;
-               } else if (strlencmp(cmd, "disable") == 0) {
-                       status = 0;
-               } else if (strlencmp(cmd, "reset") == 0) {
-                       status = hotkey_orig_status;
-                       mask = hotkey_orig_mask;
-               } else if (sscanf(cmd, "0x%x", &mask) == 1) {
-                       /* mask set */
-               } else if (sscanf(cmd, "%x", &mask) == 1) {
-                       /* mask set */
-               } else {
-                       res = -EINVAL;
-                       goto errexit;
-               }
-       }
-       if (status != -1)
-               res = hotkey_status_set(status);
-
-       if (!res && mask != hotkey_mask)
-               res = hotkey_mask_set(mask);
-
-errexit:
-       mutex_unlock(&hotkey_mutex);
-       return res;
-}
-
-static const struct acpi_device_id ibm_htk_device_ids[] = {
-       {TPACPI_ACPI_HKEY_HID, 0},
-       {"", 0},
-};
-
-static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
-       .hid = ibm_htk_device_ids,
-       .notify = hotkey_notify,
-       .handle = &hkey_handle,
-       .type = ACPI_DEVICE_NOTIFY,
-};
-
-static struct ibm_struct hotkey_driver_data = {
-       .name = "hotkey",
-       .read = hotkey_read,
-       .write = hotkey_write,
-       .exit = hotkey_exit,
-       .resume = hotkey_resume,
-       .suspend = hotkey_suspend,
-       .acpi = &ibm_hotkey_acpidriver,
-};
-
-/*************************************************************************
- * Bluetooth subdriver
- */
-
-enum {
-       /* ACPI GBDC/SBDC bits */
-       TP_ACPI_BLUETOOTH_HWPRESENT     = 0x01, /* Bluetooth hw available */
-       TP_ACPI_BLUETOOTH_RADIOSSW      = 0x02, /* Bluetooth radio enabled */
-       TP_ACPI_BLUETOOTH_UNK           = 0x04, /* unknown function */
-};
-
-static struct rfkill *tpacpi_bluetooth_rfkill;
-
-static int bluetooth_get_radiosw(void)
-{
-       int status;
-
-       if (!tp_features.bluetooth)
-               return -ENODEV;
-
-       /* WLSW overrides bluetooth in firmware/hardware, reflect that */
-       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
-               return RFKILL_STATE_HARD_BLOCKED;
-
-       if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
-               return -EIO;
-
-       return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
-               RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static void bluetooth_update_rfk(void)
-{
-       int status;
-
-       if (!tpacpi_bluetooth_rfkill)
-               return;
-
-       status = bluetooth_get_radiosw();
-       if (status < 0)
-               return;
-       rfkill_force_state(tpacpi_bluetooth_rfkill, status);
-}
-
-static int bluetooth_set_radiosw(int radio_on, int update_rfk)
-{
-       int status;
-
-       if (!tp_features.bluetooth)
-               return -ENODEV;
-
-       /* WLSW overrides bluetooth in firmware/hardware, but there is no
-        * reason to risk weird behaviour. */
-       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
-           && radio_on)
-               return -EPERM;
-
-       if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
-               return -EIO;
-       if (radio_on)
-               status |= TP_ACPI_BLUETOOTH_RADIOSSW;
-       else
-               status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
-       if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
-               return -EIO;
-
-       if (update_rfk)
-               bluetooth_update_rfk();
-
-       return 0;
-}
-
-/* sysfs bluetooth enable ---------------------------------------------- */
-static ssize_t bluetooth_enable_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int status;
-
-       status = bluetooth_get_radiosw();
-       if (status < 0)
-               return status;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
-}
-
-static ssize_t bluetooth_enable_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-       int res;
-
-       if (parse_strtoul(buf, 1, &t))
-               return -EINVAL;
-
-       res = bluetooth_set_radiosw(t, 1);
-
-       return (res) ? res : count;
-}
-
-static struct device_attribute dev_attr_bluetooth_enable =
-       __ATTR(bluetooth_enable, S_IWUSR | S_IRUGO,
-               bluetooth_enable_show, bluetooth_enable_store);
-
-/* --------------------------------------------------------------------- */
-
-static struct attribute *bluetooth_attributes[] = {
-       &dev_attr_bluetooth_enable.attr,
-       NULL
-};
-
-static const struct attribute_group bluetooth_attr_group = {
-       .attrs = bluetooth_attributes,
-};
-
-static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
-{
-       int bts = bluetooth_get_radiosw();
-
-       if (bts < 0)
-               return bts;
-
-       *state = bts;
-       return 0;
-}
-
-static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
-{
-       return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
-}
-
-static void bluetooth_exit(void)
-{
-       if (tpacpi_bluetooth_rfkill)
-               rfkill_unregister(tpacpi_bluetooth_rfkill);
-
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
-                       &bluetooth_attr_group);
-}
-
-static int __init bluetooth_init(struct ibm_init_struct *iibm)
-{
-       int res;
-       int status = 0;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(hkey);
-
-       /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-          G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
-       tp_features.bluetooth = hkey_handle &&
-           acpi_evalf(hkey_handle, &status, "GBDC", "qd");
-
-       vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
-               str_supported(tp_features.bluetooth),
-               status);
-
-       if (tp_features.bluetooth &&
-           !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
-               /* no bluetooth hardware present in system */
-               tp_features.bluetooth = 0;
-               dbg_printk(TPACPI_DBG_INIT,
-                          "bluetooth hardware not installed\n");
-       }
-
-       if (!tp_features.bluetooth)
-               return 1;
-
-       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
-                               &bluetooth_attr_group);
-       if (res)
-               return res;
-
-       res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
-                               &tpacpi_bluetooth_rfkill,
-                               RFKILL_TYPE_BLUETOOTH,
-                               "tpacpi_bluetooth_sw",
-                               tpacpi_bluetooth_rfk_set,
-                               tpacpi_bluetooth_rfk_get);
-       if (res) {
-               bluetooth_exit();
-               return res;
-       }
-
-       return 0;
-}
-
-/* procfs -------------------------------------------------------------- */
-static int bluetooth_read(char *p)
-{
-       int len = 0;
-       int status = bluetooth_get_radiosw();
-
-       if (!tp_features.bluetooth)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       else {
-               len += sprintf(p + len, "status:\t\t%s\n",
-                               (status == RFKILL_STATE_UNBLOCKED) ?
-                                       "enabled" : "disabled");
-               len += sprintf(p + len, "commands:\tenable, disable\n");
-       }
-
-       return len;
-}
-
-static int bluetooth_write(char *buf)
-{
-       char *cmd;
-
-       if (!tp_features.bluetooth)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "enable") == 0) {
-                       bluetooth_set_radiosw(1, 1);
-               } else if (strlencmp(cmd, "disable") == 0) {
-                       bluetooth_set_radiosw(0, 1);
-               } else
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct bluetooth_driver_data = {
-       .name = "bluetooth",
-       .read = bluetooth_read,
-       .write = bluetooth_write,
-       .exit = bluetooth_exit,
-};
-
-/*************************************************************************
- * Wan subdriver
- */
-
-enum {
-       /* ACPI GWAN/SWAN bits */
-       TP_ACPI_WANCARD_HWPRESENT       = 0x01, /* Wan hw available */
-       TP_ACPI_WANCARD_RADIOSSW        = 0x02, /* Wan radio enabled */
-       TP_ACPI_WANCARD_UNK             = 0x04, /* unknown function */
-};
-
-static struct rfkill *tpacpi_wan_rfkill;
-
-static int wan_get_radiosw(void)
-{
-       int status;
-
-       if (!tp_features.wan)
-               return -ENODEV;
-
-       /* WLSW overrides WWAN in firmware/hardware, reflect that */
-       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
-               return RFKILL_STATE_HARD_BLOCKED;
-
-       if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
-               return -EIO;
-
-       return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
-               RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
-}
-
-static void wan_update_rfk(void)
-{
-       int status;
-
-       if (!tpacpi_wan_rfkill)
-               return;
-
-       status = wan_get_radiosw();
-       if (status < 0)
-               return;
-       rfkill_force_state(tpacpi_wan_rfkill, status);
-}
-
-static int wan_set_radiosw(int radio_on, int update_rfk)
-{
-       int status;
-
-       if (!tp_features.wan)
-               return -ENODEV;
-
-       /* WLSW overrides bluetooth in firmware/hardware, but there is no
-        * reason to risk weird behaviour. */
-       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
-           && radio_on)
-               return -EPERM;
-
-       if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
-               return -EIO;
-       if (radio_on)
-               status |= TP_ACPI_WANCARD_RADIOSSW;
-       else
-               status &= ~TP_ACPI_WANCARD_RADIOSSW;
-       if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
-               return -EIO;
-
-       if (update_rfk)
-               wan_update_rfk();
-
-       return 0;
-}
-
-/* sysfs wan enable ---------------------------------------------------- */
-static ssize_t wan_enable_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int status;
-
-       status = wan_get_radiosw();
-       if (status < 0)
-               return status;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
-}
-
-static ssize_t wan_enable_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long t;
-       int res;
-
-       if (parse_strtoul(buf, 1, &t))
-               return -EINVAL;
-
-       res = wan_set_radiosw(t, 1);
-
-       return (res) ? res : count;
-}
-
-static struct device_attribute dev_attr_wan_enable =
-       __ATTR(wwan_enable, S_IWUSR | S_IRUGO,
-               wan_enable_show, wan_enable_store);
-
-/* --------------------------------------------------------------------- */
-
-static struct attribute *wan_attributes[] = {
-       &dev_attr_wan_enable.attr,
-       NULL
-};
-
-static const struct attribute_group wan_attr_group = {
-       .attrs = wan_attributes,
-};
-
-static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
-{
-       int wans = wan_get_radiosw();
-
-       if (wans < 0)
-               return wans;
-
-       *state = wans;
-       return 0;
-}
-
-static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
-{
-       return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
-}
-
-static void wan_exit(void)
-{
-       if (tpacpi_wan_rfkill)
-               rfkill_unregister(tpacpi_wan_rfkill);
-
-       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
-               &wan_attr_group);
-}
-
-static int __init wan_init(struct ibm_init_struct *iibm)
-{
-       int res;
-       int status = 0;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(hkey);
-
-       tp_features.wan = hkey_handle &&
-           acpi_evalf(hkey_handle, &status, "GWAN", "qd");
-
-       vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
-               str_supported(tp_features.wan),
-               status);
-
-       if (tp_features.wan &&
-           !(status & TP_ACPI_WANCARD_HWPRESENT)) {
-               /* no wan hardware present in system */
-               tp_features.wan = 0;
-               dbg_printk(TPACPI_DBG_INIT,
-                          "wan hardware not installed\n");
-       }
-
-       if (!tp_features.wan)
-               return 1;
-
-       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
-                               &wan_attr_group);
-       if (res)
-               return res;
-
-       res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
-                               &tpacpi_wan_rfkill,
-                               RFKILL_TYPE_WWAN,
-                               "tpacpi_wwan_sw",
-                               tpacpi_wan_rfk_set,
-                               tpacpi_wan_rfk_get);
-       if (res) {
-               wan_exit();
-               return res;
-       }
-
-       return 0;
-}
-
-/* procfs -------------------------------------------------------------- */
-static int wan_read(char *p)
-{
-       int len = 0;
-       int status = wan_get_radiosw();
-
-       if (!tp_features.wan)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       else {
-               len += sprintf(p + len, "status:\t\t%s\n",
-                               (status == RFKILL_STATE_UNBLOCKED) ?
-                                       "enabled" : "disabled");
-               len += sprintf(p + len, "commands:\tenable, disable\n");
-       }
-
-       return len;
-}
-
-static int wan_write(char *buf)
-{
-       char *cmd;
-
-       if (!tp_features.wan)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "enable") == 0) {
-                       wan_set_radiosw(1, 1);
-               } else if (strlencmp(cmd, "disable") == 0) {
-                       wan_set_radiosw(0, 1);
-               } else
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct wan_driver_data = {
-       .name = "wan",
-       .read = wan_read,
-       .write = wan_write,
-       .exit = wan_exit,
-};
-
-/*************************************************************************
- * Video subdriver
- */
-
-#ifdef CONFIG_THINKPAD_ACPI_VIDEO
-
-enum video_access_mode {
-       TPACPI_VIDEO_NONE = 0,
-       TPACPI_VIDEO_570,       /* 570 */
-       TPACPI_VIDEO_770,       /* 600e/x, 770e, 770x */
-       TPACPI_VIDEO_NEW,       /* all others */
-};
-
-enum { /* video status flags, based on VIDEO_570 */
-       TP_ACPI_VIDEO_S_LCD = 0x01,     /* LCD output enabled */
-       TP_ACPI_VIDEO_S_CRT = 0x02,     /* CRT output enabled */
-       TP_ACPI_VIDEO_S_DVI = 0x08,     /* DVI output enabled */
-};
-
-enum {  /* TPACPI_VIDEO_570 constants */
-       TP_ACPI_VIDEO_570_PHSCMD = 0x87,        /* unknown magic constant :( */
-       TP_ACPI_VIDEO_570_PHSMASK = 0x03,       /* PHS bits that map to
-                                                * video_status_flags */
-       TP_ACPI_VIDEO_570_PHS2CMD = 0x8b,       /* unknown magic constant :( */
-       TP_ACPI_VIDEO_570_PHS2SET = 0x80,       /* unknown magic constant :( */
-};
-
-static enum video_access_mode video_supported;
-static int video_orig_autosw;
-
-static int video_autosw_get(void);
-static int video_autosw_set(int enable);
-
-TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID");      /* G41 */
-
-static int __init video_init(struct ibm_init_struct *iibm)
-{
-       int ivga;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(vid);
-       TPACPI_ACPIHANDLE_INIT(vid2);
-
-       if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
-               /* G41, assume IVGA doesn't change */
-               vid_handle = vid2_handle;
-
-       if (!vid_handle)
-               /* video switching not supported on R30, R31 */
-               video_supported = TPACPI_VIDEO_NONE;
-       else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
-               /* 570 */
-               video_supported = TPACPI_VIDEO_570;
-       else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
-               /* 600e/x, 770e, 770x */
-               video_supported = TPACPI_VIDEO_770;
-       else
-               /* all others */
-               video_supported = TPACPI_VIDEO_NEW;
-
-       vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
-               str_supported(video_supported != TPACPI_VIDEO_NONE),
-               video_supported);
-
-       return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
-}
-
-static void video_exit(void)
-{
-       dbg_printk(TPACPI_DBG_EXIT,
-                  "restoring original video autoswitch mode\n");
-       if (video_autosw_set(video_orig_autosw))
-               printk(TPACPI_ERR "error while trying to restore original "
-                       "video autoswitch mode\n");
-}
-
-static int video_outputsw_get(void)
-{
-       int status = 0;
-       int i;
-
-       switch (video_supported) {
-       case TPACPI_VIDEO_570:
-               if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
-                                TP_ACPI_VIDEO_570_PHSCMD))
-                       return -EIO;
-               status = i & TP_ACPI_VIDEO_570_PHSMASK;
-               break;
-       case TPACPI_VIDEO_770:
-               if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
-                       return -EIO;
-               if (i)
-                       status |= TP_ACPI_VIDEO_S_LCD;
-               if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
-                       return -EIO;
-               if (i)
-                       status |= TP_ACPI_VIDEO_S_CRT;
-               break;
-       case TPACPI_VIDEO_NEW:
-               if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
-                   !acpi_evalf(NULL, &i, "\\VCDC", "d"))
-                       return -EIO;
-               if (i)
-                       status |= TP_ACPI_VIDEO_S_CRT;
-
-               if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
-                   !acpi_evalf(NULL, &i, "\\VCDL", "d"))
-                       return -EIO;
-               if (i)
-                       status |= TP_ACPI_VIDEO_S_LCD;
-               if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
-                       return -EIO;
-               if (i)
-                       status |= TP_ACPI_VIDEO_S_DVI;
-               break;
-       default:
-               return -ENOSYS;
-       }
-
-       return status;
-}
-
-static int video_outputsw_set(int status)
-{
-       int autosw;
-       int res = 0;
-
-       switch (video_supported) {
-       case TPACPI_VIDEO_570:
-               res = acpi_evalf(NULL, NULL,
-                                "\\_SB.PHS2", "vdd",
-                                TP_ACPI_VIDEO_570_PHS2CMD,
-                                status | TP_ACPI_VIDEO_570_PHS2SET);
-               break;
-       case TPACPI_VIDEO_770:
-               autosw = video_autosw_get();
-               if (autosw < 0)
-                       return autosw;
-
-               res = video_autosw_set(1);
-               if (res)
-                       return res;
-               res = acpi_evalf(vid_handle, NULL,
-                                "ASWT", "vdd", status * 0x100, 0);
-               if (!autosw && video_autosw_set(autosw)) {
-                       printk(TPACPI_ERR
-                              "video auto-switch left enabled due to error\n");
-                       return -EIO;
-               }
-               break;
-       case TPACPI_VIDEO_NEW:
-               res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
-                     acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
-               break;
-       default:
-               return -ENOSYS;
-       }
-
-       return (res)? 0 : -EIO;
-}
-
-static int video_autosw_get(void)
-{
-       int autosw = 0;
-
-       switch (video_supported) {
-       case TPACPI_VIDEO_570:
-               if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
-                       return -EIO;
-               break;
-       case TPACPI_VIDEO_770:
-       case TPACPI_VIDEO_NEW:
-               if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
-                       return -EIO;
-               break;
-       default:
-               return -ENOSYS;
-       }
-
-       return autosw & 1;
-}
-
-static int video_autosw_set(int enable)
-{
-       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
-               return -EIO;
-       return 0;
-}
-
-static int video_outputsw_cycle(void)
-{
-       int autosw = video_autosw_get();
-       int res;
-
-       if (autosw < 0)
-               return autosw;
-
-       switch (video_supported) {
-       case TPACPI_VIDEO_570:
-               res = video_autosw_set(1);
-               if (res)
-                       return res;
-               res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
-               break;
-       case TPACPI_VIDEO_770:
-       case TPACPI_VIDEO_NEW:
-               res = video_autosw_set(1);
-               if (res)
-                       return res;
-               res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
-               break;
-       default:
-               return -ENOSYS;
-       }
-       if (!autosw && video_autosw_set(autosw)) {
-               printk(TPACPI_ERR
-                      "video auto-switch left enabled due to error\n");
-               return -EIO;
-       }
-
-       return (res)? 0 : -EIO;
-}
-
-static int video_expand_toggle(void)
-{
-       switch (video_supported) {
-       case TPACPI_VIDEO_570:
-               return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
-                       0 : -EIO;
-       case TPACPI_VIDEO_770:
-               return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
-                       0 : -EIO;
-       case TPACPI_VIDEO_NEW:
-               return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
-                       0 : -EIO;
-       default:
-               return -ENOSYS;
-       }
-       /* not reached */
-}
-
-static int video_read(char *p)
-{
-       int status, autosw;
-       int len = 0;
-
-       if (video_supported == TPACPI_VIDEO_NONE) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
-       }
-
-       status = video_outputsw_get();
-       if (status < 0)
-               return status;
-
-       autosw = video_autosw_get();
-       if (autosw < 0)
-               return autosw;
-
-       len += sprintf(p + len, "status:\t\tsupported\n");
-       len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
-       len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
-       if (video_supported == TPACPI_VIDEO_NEW)
-               len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
-       len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
-       len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
-       len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
-       if (video_supported == TPACPI_VIDEO_NEW)
-               len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
-       len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
-       len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
-
-       return len;
-}
-
-static int video_write(char *buf)
-{
-       char *cmd;
-       int enable, disable, status;
-       int res;
-
-       if (video_supported == TPACPI_VIDEO_NONE)
-               return -ENODEV;
-
-       enable = 0;
-       disable = 0;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "lcd_enable") == 0) {
-                       enable |= TP_ACPI_VIDEO_S_LCD;
-               } else if (strlencmp(cmd, "lcd_disable") == 0) {
-                       disable |= TP_ACPI_VIDEO_S_LCD;
-               } else if (strlencmp(cmd, "crt_enable") == 0) {
-                       enable |= TP_ACPI_VIDEO_S_CRT;
-               } else if (strlencmp(cmd, "crt_disable") == 0) {
-                       disable |= TP_ACPI_VIDEO_S_CRT;
-               } else if (video_supported == TPACPI_VIDEO_NEW &&
-                          strlencmp(cmd, "dvi_enable") == 0) {
-                       enable |= TP_ACPI_VIDEO_S_DVI;
-               } else if (video_supported == TPACPI_VIDEO_NEW &&
-                          strlencmp(cmd, "dvi_disable") == 0) {
-                       disable |= TP_ACPI_VIDEO_S_DVI;
-               } else if (strlencmp(cmd, "auto_enable") == 0) {
-                       res = video_autosw_set(1);
-                       if (res)
-                               return res;
-               } else if (strlencmp(cmd, "auto_disable") == 0) {
-                       res = video_autosw_set(0);
-                       if (res)
-                               return res;
-               } else if (strlencmp(cmd, "video_switch") == 0) {
-                       res = video_outputsw_cycle();
-                       if (res)
-                               return res;
-               } else if (strlencmp(cmd, "expand_toggle") == 0) {
-                       res = video_expand_toggle();
-                       if (res)
-                               return res;
-               } else
-                       return -EINVAL;
-       }
-
-       if (enable || disable) {
-               status = video_outputsw_get();
-               if (status < 0)
-                       return status;
-               res = video_outputsw_set((status & ~disable) | enable);
-               if (res)
-                       return res;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct video_driver_data = {
-       .name = "video",
-       .read = video_read,
-       .write = video_write,
-       .exit = video_exit,
-};
-
-#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
-
-/*************************************************************************
- * Light (thinklight) subdriver
- */
-
-TPACPI_HANDLE(lght, root, "\\LGHT");   /* A21e, A2xm/p, T20-22, X20-21 */
-TPACPI_HANDLE(ledb, ec, "LEDB");               /* G4x */
-
-static int light_get_status(void)
-{
-       int status = 0;
-
-       if (tp_features.light_status) {
-               if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
-                       return -EIO;
-               return (!!status);
-       }
-
-       return -ENXIO;
-}
-
-static int light_set_status(int status)
-{
-       int rc;
-
-       if (tp_features.light) {
-               if (cmos_handle) {
-                       rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
-                                       (status)?
-                                               TP_CMOS_THINKLIGHT_ON :
-                                               TP_CMOS_THINKLIGHT_OFF);
-               } else {
-                       rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
-                                       (status)? 1 : 0);
-               }
-               return (rc)? 0 : -EIO;
-       }
-
-       return -ENXIO;
-}
-
-static void light_set_status_worker(struct work_struct *work)
-{
-       struct tpacpi_led_classdev *data =
-                       container_of(work, struct tpacpi_led_classdev, work);
-
-       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               light_set_status((data->new_brightness != LED_OFF));
-}
-
-static void light_sysfs_set(struct led_classdev *led_cdev,
-                       enum led_brightness brightness)
-{
-       struct tpacpi_led_classdev *data =
-               container_of(led_cdev,
-                            struct tpacpi_led_classdev,
-                            led_classdev);
-       data->new_brightness = brightness;
-       queue_work(tpacpi_wq, &data->work);
-}
-
-static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
-{
-       return (light_get_status() == 1)? LED_FULL : LED_OFF;
-}
-
-static struct tpacpi_led_classdev tpacpi_led_thinklight = {
-       .led_classdev = {
-               .name           = "tpacpi::thinklight",
-               .brightness_set = &light_sysfs_set,
-               .brightness_get = &light_sysfs_get,
-       }
-};
-
-static int __init light_init(struct ibm_init_struct *iibm)
-{
-       int rc;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(ledb);
-       TPACPI_ACPIHANDLE_INIT(lght);
-       TPACPI_ACPIHANDLE_INIT(cmos);
-       INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
-
-       /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
-       tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
-
-       if (tp_features.light)
-               /* light status not supported on
-                  570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
-               tp_features.light_status =
-                       acpi_evalf(ec_handle, NULL, "KBLT", "qv");
-
-       vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
-               str_supported(tp_features.light),
-               str_supported(tp_features.light_status));
-
-       if (!tp_features.light)
-               return 1;
-
-       rc = led_classdev_register(&tpacpi_pdev->dev,
-                                  &tpacpi_led_thinklight.led_classdev);
-
-       if (rc < 0) {
-               tp_features.light = 0;
-               tp_features.light_status = 0;
-       } else  {
-               rc = 0;
-       }
-
-       return rc;
-}
-
-static void light_exit(void)
-{
-       led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
-       if (work_pending(&tpacpi_led_thinklight.work))
-               flush_workqueue(tpacpi_wq);
-}
-
-static int light_read(char *p)
-{
-       int len = 0;
-       int status;
-
-       if (!tp_features.light) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       } else if (!tp_features.light_status) {
-               len += sprintf(p + len, "status:\t\tunknown\n");
-               len += sprintf(p + len, "commands:\ton, off\n");
-       } else {
-               status = light_get_status();
-               if (status < 0)
-                       return status;
-               len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
-               len += sprintf(p + len, "commands:\ton, off\n");
-       }
-
-       return len;
-}
-
-static int light_write(char *buf)
-{
-       char *cmd;
-       int newstatus = 0;
-
-       if (!tp_features.light)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "on") == 0) {
-                       newstatus = 1;
-               } else if (strlencmp(cmd, "off") == 0) {
-                       newstatus = 0;
-               } else
-                       return -EINVAL;
-       }
-
-       return light_set_status(newstatus);
-}
-
-static struct ibm_struct light_driver_data = {
-       .name = "light",
-       .read = light_read,
-       .write = light_write,
-       .exit = light_exit,
-};
-
-/*************************************************************************
- * Dock subdriver
- */
-
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
-
-static void dock_notify(struct ibm_struct *ibm, u32 event);
-static int dock_read(char *p);
-static int dock_write(char *buf);
-
-TPACPI_HANDLE(dock, root, "\\_SB.GDCK",        /* X30, X31, X40 */
-          "\\_SB.PCI0.DOCK",   /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
-          "\\_SB.PCI0.PCI1.DOCK",      /* all others */
-          "\\_SB.PCI.ISA.SLCE",        /* 570 */
-    );                         /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
-
-/* don't list other alternatives as we install a notify handler on the 570 */
-TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
-
-static const struct acpi_device_id ibm_pci_device_ids[] = {
-       {PCI_ROOT_HID_STRING, 0},
-       {"", 0},
-};
-
-static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
-       {
-        .notify = dock_notify,
-        .handle = &dock_handle,
-        .type = ACPI_SYSTEM_NOTIFY,
-       },
-       {
-       /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
-        * We just use it to get notifications of dock hotplug
-        * in very old thinkpads */
-        .hid = ibm_pci_device_ids,
-        .notify = dock_notify,
-        .handle = &pci_handle,
-        .type = ACPI_SYSTEM_NOTIFY,
-       },
-};
-
-static struct ibm_struct dock_driver_data[2] = {
-       {
-        .name = "dock",
-        .read = dock_read,
-        .write = dock_write,
-        .acpi = &ibm_dock_acpidriver[0],
-       },
-       {
-        .name = "dock",
-        .acpi = &ibm_dock_acpidriver[1],
-       },
-};
-
-#define dock_docked() (_sta(dock_handle) & 1)
-
-static int __init dock_init(struct ibm_init_struct *iibm)
-{
-       vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(dock);
-
-       vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
-               str_supported(dock_handle != NULL));
-
-       return (dock_handle)? 0 : 1;
-}
-
-static int __init dock_init2(struct ibm_init_struct *iibm)
-{
-       int dock2_needed;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
-
-       if (dock_driver_data[0].flags.acpi_driver_registered &&
-           dock_driver_data[0].flags.acpi_notify_installed) {
-               TPACPI_ACPIHANDLE_INIT(pci);
-               dock2_needed = (pci_handle != NULL);
-               vdbg_printk(TPACPI_DBG_INIT,
-                           "dock PCI handler for the TP 570 is %s\n",
-                           str_supported(dock2_needed));
-       } else {
-               vdbg_printk(TPACPI_DBG_INIT,
-               "dock subdriver part 2 not required\n");
-               dock2_needed = 0;
-       }
-
-       return (dock2_needed)? 0 : 1;
-}
-
-static void dock_notify(struct ibm_struct *ibm, u32 event)
-{
-       int docked = dock_docked();
-       int pci = ibm->acpi->hid && ibm->acpi->device &&
-               acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
-       int data;
-
-       if (event == 1 && !pci) /* 570 */
-               data = 1;       /* button */
-       else if (event == 1 && pci)     /* 570 */
-               data = 3;       /* dock */
-       else if (event == 3 && docked)
-               data = 1;       /* button */
-       else if (event == 3 && !docked)
-               data = 2;       /* undock */
-       else if (event == 0 && docked)
-               data = 3;       /* dock */
-       else {
-               printk(TPACPI_ERR "unknown dock event %d, status %d\n",
-                      event, _sta(dock_handle));
-               data = 0;       /* unknown */
-       }
-       acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
-       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
-                                         ibm->acpi->device->dev.bus_id,
-                                         event, data);
-}
-
-static int dock_read(char *p)
-{
-       int len = 0;
-       int docked = dock_docked();
-
-       if (!dock_handle)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       else if (!docked)
-               len += sprintf(p + len, "status:\t\tundocked\n");
-       else {
-               len += sprintf(p + len, "status:\t\tdocked\n");
-               len += sprintf(p + len, "commands:\tdock, undock\n");
-       }
-
-       return len;
-}
-
-static int dock_write(char *buf)
-{
-       char *cmd;
-
-       if (!dock_docked())
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "undock") == 0) {
-                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
-                           !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
-                               return -EIO;
-               } else if (strlencmp(cmd, "dock") == 0) {
-                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
-                               return -EIO;
-               } else
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-#endif /* CONFIG_THINKPAD_ACPI_DOCK */
-
-/*************************************************************************
- * Bay subdriver
- */
-
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-
-TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST",    /* 570 */
-          "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
-          "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
-          "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
-          );                           /* A21e, R30, R31 */
-TPACPI_HANDLE(bay_ej, bay, "_EJ3",     /* 600e/x, A2xm/p, A3x */
-          "_EJ0",              /* all others */
-          );                   /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
-TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
-          "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
-          );                           /* all others */
-TPACPI_HANDLE(bay2_ej, bay2, "_EJ3",   /* 600e/x, 770e, A3x */
-          "_EJ0",                      /* 770x */
-          );                           /* all others */
-
-static int __init bay_init(struct ibm_init_struct *iibm)
-{
-       vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(bay);
-       if (bay_handle)
-               TPACPI_ACPIHANDLE_INIT(bay_ej);
-       TPACPI_ACPIHANDLE_INIT(bay2);
-       if (bay2_handle)
-               TPACPI_ACPIHANDLE_INIT(bay2_ej);
-
-       tp_features.bay_status = bay_handle &&
-               acpi_evalf(bay_handle, NULL, "_STA", "qv");
-       tp_features.bay_status2 = bay2_handle &&
-               acpi_evalf(bay2_handle, NULL, "_STA", "qv");
-
-       tp_features.bay_eject = bay_handle && bay_ej_handle &&
-               (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
-       tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
-               (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
-
-       vdbg_printk(TPACPI_DBG_INIT,
-               "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
-               str_supported(tp_features.bay_status),
-               str_supported(tp_features.bay_eject),
-               str_supported(tp_features.bay_status2),
-               str_supported(tp_features.bay_eject2));
-
-       return (tp_features.bay_status || tp_features.bay_eject ||
-               tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
-}
-
-static void bay_notify(struct ibm_struct *ibm, u32 event)
-{
-       acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
-       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
-                                         ibm->acpi->device->dev.bus_id,
-                                         event, 0);
-}
-
-#define bay_occupied(b) (_sta(b##_handle) & 1)
-
-static int bay_read(char *p)
-{
-       int len = 0;
-       int occupied = bay_occupied(bay);
-       int occupied2 = bay_occupied(bay2);
-       int eject, eject2;
-
-       len += sprintf(p + len, "status:\t\t%s\n",
-               tp_features.bay_status ?
-                       (occupied ? "occupied" : "unoccupied") :
-                               "not supported");
-       if (tp_features.bay_status2)
-               len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
-                              "occupied" : "unoccupied");
-
-       eject = tp_features.bay_eject && occupied;
-       eject2 = tp_features.bay_eject2 && occupied2;
-
-       if (eject && eject2)
-               len += sprintf(p + len, "commands:\teject, eject2\n");
-       else if (eject)
-               len += sprintf(p + len, "commands:\teject\n");
-       else if (eject2)
-               len += sprintf(p + len, "commands:\teject2\n");
-
-       return len;
-}
-
-static int bay_write(char *buf)
-{
-       char *cmd;
-
-       if (!tp_features.bay_eject && !tp_features.bay_eject2)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
-                       if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
-                               return -EIO;
-               } else if (tp_features.bay_eject2 &&
-                          strlencmp(cmd, "eject2") == 0) {
-                       if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
-                               return -EIO;
-               } else
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
-       .notify = bay_notify,
-       .handle = &bay_handle,
-       .type = ACPI_SYSTEM_NOTIFY,
-};
-
-static struct ibm_struct bay_driver_data = {
-       .name = "bay",
-       .read = bay_read,
-       .write = bay_write,
-       .acpi = &ibm_bay_acpidriver,
-};
-
-#endif /* CONFIG_THINKPAD_ACPI_BAY */
-
-/*************************************************************************
- * CMOS subdriver
- */
-
-/* sysfs cmos_command -------------------------------------------------- */
-static ssize_t cmos_command_store(struct device *dev,
-                           struct device_attribute *attr,
-                           const char *buf, size_t count)
-{
-       unsigned long cmos_cmd;
-       int res;
-
-       if (parse_strtoul(buf, 21, &cmos_cmd))
-               return -EINVAL;
-
-       res = issue_thinkpad_cmos_command(cmos_cmd);
-       return (res)? res : count;
-}
-
-static struct device_attribute dev_attr_cmos_command =
-       __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
-
-/* --------------------------------------------------------------------- */
-
-static int __init cmos_init(struct ibm_init_struct *iibm)
-{
-       int res;
-
-       vdbg_printk(TPACPI_DBG_INIT,
-               "initializing cmos commands subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(cmos);
-
-       vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
-               str_supported(cmos_handle != NULL));
-
-       res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
-       if (res)
-               return res;
-
-       return (cmos_handle)? 0 : 1;
-}
-
-static void cmos_exit(void)
-{
-       device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
-}
-
-static int cmos_read(char *p)
-{
-       int len = 0;
-
-       /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
-          R30, R31, T20-22, X20-21 */
-       if (!cmos_handle)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       else {
-               len += sprintf(p + len, "status:\t\tsupported\n");
-               len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
-       }
-
-       return len;
-}
-
-static int cmos_write(char *buf)
-{
-       char *cmd;
-       int cmos_cmd, res;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
-                   cmos_cmd >= 0 && cmos_cmd <= 21) {
-                       /* cmos_cmd set */
-               } else
-                       return -EINVAL;
-
-               res = issue_thinkpad_cmos_command(cmos_cmd);
-               if (res)
-                       return res;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct cmos_driver_data = {
-       .name = "cmos",
-       .read = cmos_read,
-       .write = cmos_write,
-       .exit = cmos_exit,
-};
-
-/*************************************************************************
- * LED subdriver
- */
-
-enum led_access_mode {
-       TPACPI_LED_NONE = 0,
-       TPACPI_LED_570, /* 570 */
-       TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
-       TPACPI_LED_NEW, /* all others */
-};
-
-enum { /* For TPACPI_LED_OLD */
-       TPACPI_LED_EC_HLCL = 0x0c,      /* EC reg to get led to power on */
-       TPACPI_LED_EC_HLBL = 0x0d,      /* EC reg to blink a lit led */
-       TPACPI_LED_EC_HLMS = 0x0e,      /* EC reg to select led to command */
-};
-
-enum led_status_t {
-       TPACPI_LED_OFF = 0,
-       TPACPI_LED_ON,
-       TPACPI_LED_BLINK,
-};
-
-static enum led_access_mode led_supported;
-
-TPACPI_HANDLE(led, ec, "SLED", /* 570 */
-          "SYSL",              /* 600e/x, 770e, 770x, A21e, A2xm/p, */
-                               /* T20-22, X20-21 */
-          "LED",               /* all others */
-          );                   /* R30, R31 */
-
-#define TPACPI_LED_NUMLEDS 8
-static struct tpacpi_led_classdev *tpacpi_leds;
-static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
-static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
-       /* there's a limit of 19 chars + NULL before 2.6.26 */
-       "tpacpi::power",
-       "tpacpi:orange:batt",
-       "tpacpi:green:batt",
-       "tpacpi::dock_active",
-       "tpacpi::bay_active",
-       "tpacpi::dock_batt",
-       "tpacpi::unknown_led",
-       "tpacpi::standby",
-};
-
-static int led_get_status(const unsigned int led)
-{
-       int status;
-       enum led_status_t led_s;
-
-       switch (led_supported) {
-       case TPACPI_LED_570:
-               if (!acpi_evalf(ec_handle,
-                               &status, "GLED", "dd", 1 << led))
-                       return -EIO;
-               led_s = (status == 0)?
-                               TPACPI_LED_OFF :
-                               ((status == 1)?
-                                       TPACPI_LED_ON :
-                                       TPACPI_LED_BLINK);
-               tpacpi_led_state_cache[led] = led_s;
-               return led_s;
-       default:
-               return -ENXIO;
-       }
-
-       /* not reached */
-}
-
-static int led_set_status(const unsigned int led,
-                         const enum led_status_t ledstatus)
-{
-       /* off, on, blink. Index is led_status_t */
-       static const unsigned int led_sled_arg1[] = { 0, 1, 3 };
-       static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 };
-
-       int rc = 0;
-
-       switch (led_supported) {
-       case TPACPI_LED_570:
-               /* 570 */
-               if (led > 7)
-                       return -EINVAL;
-               if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
-                               (1 << led), led_sled_arg1[ledstatus]))
-                       rc = -EIO;
-               break;
-       case TPACPI_LED_OLD:
-               /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
-               if (led > 7)
-                       return -EINVAL;
-               rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
-               if (rc >= 0)
-                       rc = ec_write(TPACPI_LED_EC_HLBL,
-                                     (ledstatus == TPACPI_LED_BLINK) << led);
-               if (rc >= 0)
-                       rc = ec_write(TPACPI_LED_EC_HLCL,
-                                     (ledstatus != TPACPI_LED_OFF) << led);
-               break;
-       case TPACPI_LED_NEW:
-               /* all others */
-               if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
-                               led, led_led_arg1[ledstatus]))
-                       rc = -EIO;
-               break;
-       default:
-               rc = -ENXIO;
-       }
-
-       if (!rc)
-               tpacpi_led_state_cache[led] = ledstatus;
-
-       return rc;
-}
-
-static void led_sysfs_set_status(unsigned int led,
-                                enum led_brightness brightness)
-{
-       led_set_status(led,
-                       (brightness == LED_OFF) ?
-                       TPACPI_LED_OFF :
-                       (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
-                               TPACPI_LED_BLINK : TPACPI_LED_ON);
-}
-
-static void led_set_status_worker(struct work_struct *work)
-{
-       struct tpacpi_led_classdev *data =
-               container_of(work, struct tpacpi_led_classdev, work);
-
-       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
-               led_sysfs_set_status(data->led, data->new_brightness);
-}
-
-static void led_sysfs_set(struct led_classdev *led_cdev,
-                       enum led_brightness brightness)
-{
-       struct tpacpi_led_classdev *data = container_of(led_cdev,
-                            struct tpacpi_led_classdev, led_classdev);
-
-       data->new_brightness = brightness;
-       queue_work(tpacpi_wq, &data->work);
-}
-
-static int led_sysfs_blink_set(struct led_classdev *led_cdev,
-                       unsigned long *delay_on, unsigned long *delay_off)
-{
-       struct tpacpi_led_classdev *data = container_of(led_cdev,
-                            struct tpacpi_led_classdev, led_classdev);
-
-       /* Can we choose the flash rate? */
-       if (*delay_on == 0 && *delay_off == 0) {
-               /* yes. set them to the hardware blink rate (1 Hz) */
-               *delay_on = 500; /* ms */
-               *delay_off = 500; /* ms */
-       } else if ((*delay_on != 500) || (*delay_off != 500))
-               return -EINVAL;
-
-       data->new_brightness = TPACPI_LED_BLINK;
-       queue_work(tpacpi_wq, &data->work);
-
-       return 0;
-}
-
-static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
-{
-       int rc;
-
-       struct tpacpi_led_classdev *data = container_of(led_cdev,
-                            struct tpacpi_led_classdev, led_classdev);
-
-       rc = led_get_status(data->led);
-
-       if (rc == TPACPI_LED_OFF || rc < 0)
-               rc = LED_OFF;   /* no error handling in led class :( */
-       else
-               rc = LED_FULL;
-
-       return rc;
-}
-
-static void led_exit(void)
-{
-       unsigned int i;
-
-       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
-               if (tpacpi_leds[i].led_classdev.name)
-                       led_classdev_unregister(&tpacpi_leds[i].led_classdev);
-       }
-
-       kfree(tpacpi_leds);
-}
-
-static int __init led_init(struct ibm_init_struct *iibm)
-{
-       unsigned int i;
-       int rc;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(led);
-
-       if (!led_handle)
-               /* led not supported on R30, R31 */
-               led_supported = TPACPI_LED_NONE;
-       else if (strlencmp(led_path, "SLED") == 0)
-               /* 570 */
-               led_supported = TPACPI_LED_570;
-       else if (strlencmp(led_path, "SYSL") == 0)
-               /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
-               led_supported = TPACPI_LED_OLD;
-       else
-               /* all others */
-               led_supported = TPACPI_LED_NEW;
-
-       vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
-               str_supported(led_supported), led_supported);
-
-       tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
-                             GFP_KERNEL);
-       if (!tpacpi_leds) {
-               printk(TPACPI_ERR "Out of memory for LED data\n");
-               return -ENOMEM;
-       }
-
-       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
-               tpacpi_leds[i].led = i;
-
-               tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
-               tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
-               if (led_supported == TPACPI_LED_570)
-                       tpacpi_leds[i].led_classdev.brightness_get =
-                                                       &led_sysfs_get;
-
-               tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
-
-               INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
-
-               rc = led_classdev_register(&tpacpi_pdev->dev,
-                                          &tpacpi_leds[i].led_classdev);
-               if (rc < 0) {
-                       tpacpi_leds[i].led_classdev.name = NULL;
-                       led_exit();
-                       return rc;
-               }
-       }
-
-       return (led_supported != TPACPI_LED_NONE)? 0 : 1;
-}
-
-#define str_led_status(s) \
-       ((s) == TPACPI_LED_OFF ? "off" : \
-               ((s) == TPACPI_LED_ON ? "on" : "blinking"))
-
-static int led_read(char *p)
-{
-       int len = 0;
-
-       if (!led_supported) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
-       }
-       len += sprintf(p + len, "status:\t\tsupported\n");
-
-       if (led_supported == TPACPI_LED_570) {
-               /* 570 */
-               int i, status;
-               for (i = 0; i < 8; i++) {
-                       status = led_get_status(i);
-                       if (status < 0)
-                               return -EIO;
-                       len += sprintf(p + len, "%d:\t\t%s\n",
-                                      i, str_led_status(status));
-               }
-       }
-
-       len += sprintf(p + len, "commands:\t"
-                      "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
-
-       return len;
-}
-
-static int led_write(char *buf)
-{
-       char *cmd;
-       int led, rc;
-       enum led_status_t s;
-
-       if (!led_supported)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
-                       return -EINVAL;
-
-               if (strstr(cmd, "off")) {
-                       s = TPACPI_LED_OFF;
-               } else if (strstr(cmd, "on")) {
-                       s = TPACPI_LED_ON;
-               } else if (strstr(cmd, "blink")) {
-                       s = TPACPI_LED_BLINK;
-               } else {
-                       return -EINVAL;
-               }
-
-               rc = led_set_status(led, s);
-               if (rc < 0)
-                       return rc;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct led_driver_data = {
-       .name = "led",
-       .read = led_read,
-       .write = led_write,
-       .exit = led_exit,
-};
-
-/*************************************************************************
- * Beep subdriver
- */
-
-TPACPI_HANDLE(beep, ec, "BEEP");       /* all except R30, R31 */
-
-static int __init beep_init(struct ibm_init_struct *iibm)
-{
-       vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
-
-       TPACPI_ACPIHANDLE_INIT(beep);
-
-       vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
-               str_supported(beep_handle != NULL));
-
-       return (beep_handle)? 0 : 1;
-}
-
-static int beep_read(char *p)
-{
-       int len = 0;
-
-       if (!beep_handle)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       else {
-               len += sprintf(p + len, "status:\t\tsupported\n");
-               len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
-       }
-
-       return len;
-}
-
-static int beep_write(char *buf)
-{
-       char *cmd;
-       int beep_cmd;
-
-       if (!beep_handle)
-               return -ENODEV;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
-                   beep_cmd >= 0 && beep_cmd <= 17) {
-                       /* beep_cmd set */
-               } else
-                       return -EINVAL;
-               if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
-                       return -EIO;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct beep_driver_data = {
-       .name = "beep",
-       .read = beep_read,
-       .write = beep_write,
-};
-
-/*************************************************************************
- * Thermal subdriver
- */
-
-enum thermal_access_mode {
-       TPACPI_THERMAL_NONE = 0,        /* No thermal support */
-       TPACPI_THERMAL_ACPI_TMP07,      /* Use ACPI TMP0-7 */
-       TPACPI_THERMAL_ACPI_UPDT,       /* Use ACPI TMP0-7 with UPDT */
-       TPACPI_THERMAL_TPEC_8,          /* Use ACPI EC regs, 8 sensors */
-       TPACPI_THERMAL_TPEC_16,         /* Use ACPI EC regs, 16 sensors */
-};
-
-enum { /* TPACPI_THERMAL_TPEC_* */
-       TP_EC_THERMAL_TMP0 = 0x78,      /* ACPI EC regs TMP 0..7 */
-       TP_EC_THERMAL_TMP8 = 0xC0,      /* ACPI EC regs TMP 8..15 */
-       TP_EC_THERMAL_TMP_NA = -128,    /* ACPI EC sensor not available */
-};
-
-#define TPACPI_MAX_THERMAL_SENSORS 16  /* Max thermal sensors supported */
-struct ibm_thermal_sensors_struct {
-       s32 temp[TPACPI_MAX_THERMAL_SENSORS];
-};
-
-static enum thermal_access_mode thermal_read_mode;
-
-/* idx is zero-based */
-static int thermal_get_sensor(int idx, s32 *value)
-{
-       int t;
-       s8 tmp;
-       char tmpi[5];
-
-       t = TP_EC_THERMAL_TMP0;
-
-       switch (thermal_read_mode) {
-#if TPACPI_MAX_THERMAL_SENSORS >= 16
-       case TPACPI_THERMAL_TPEC_16:
-               if (idx >= 8 && idx <= 15) {
-                       t = TP_EC_THERMAL_TMP8;
-                       idx -= 8;
-               }
-               /* fallthrough */
-#endif
-       case TPACPI_THERMAL_TPEC_8:
-               if (idx <= 7) {
-                       if (!acpi_ec_read(t + idx, &tmp))
-                               return -EIO;
-                       *value = tmp * 1000;
-                       return 0;
-               }
-               break;
-
-       case TPACPI_THERMAL_ACPI_UPDT:
-               if (idx <= 7) {
-                       snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
-                       if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
-                               return -EIO;
-                       if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
-                               return -EIO;
-                       *value = (t - 2732) * 100;
-                       return 0;
-               }
-               break;
-
-       case TPACPI_THERMAL_ACPI_TMP07:
-               if (idx <= 7) {
-                       snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
-                       if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
-                               return -EIO;
-                       if (t > 127 || t < -127)
-                               t = TP_EC_THERMAL_TMP_NA;
-                       *value = t * 1000;
-                       return 0;
-               }
-               break;
-
-       case TPACPI_THERMAL_NONE:
-       default:
-               return -ENOSYS;
-       }
-
-       return -EINVAL;
-}
-
-static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
-{
-       int res, i;
-       int n;
-
-       n = 8;
-       i = 0;
-
-       if (!s)
-               return -EINVAL;
-
-       if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
-               n = 16;
-
-       for (i = 0 ; i < n; i++) {
-               res = thermal_get_sensor(i, &s->temp[i]);
-               if (res)
-                       return res;
-       }
-
-       return n;
-}
-
-/* sysfs temp##_input -------------------------------------------------- */
-
-static ssize_t thermal_temp_input_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       struct sensor_device_attribute *sensor_attr =
-                                       to_sensor_dev_attr(attr);
-       int idx = sensor_attr->index;
-       s32 value;
-       int res;
-
-       res = thermal_get_sensor(idx, &value);
-       if (res)
-               return res;
-       if (value == TP_EC_THERMAL_TMP_NA * 1000)
-               return -ENXIO;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
-}
-
-#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
-        SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, \
-                    thermal_temp_input_show, NULL, _idxB)
-
-static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
-       THERMAL_SENSOR_ATTR_TEMP(1, 0),
-       THERMAL_SENSOR_ATTR_TEMP(2, 1),
-       THERMAL_SENSOR_ATTR_TEMP(3, 2),
-       THERMAL_SENSOR_ATTR_TEMP(4, 3),
-       THERMAL_SENSOR_ATTR_TEMP(5, 4),
-       THERMAL_SENSOR_ATTR_TEMP(6, 5),
-       THERMAL_SENSOR_ATTR_TEMP(7, 6),
-       THERMAL_SENSOR_ATTR_TEMP(8, 7),
-       THERMAL_SENSOR_ATTR_TEMP(9, 8),
-       THERMAL_SENSOR_ATTR_TEMP(10, 9),
-       THERMAL_SENSOR_ATTR_TEMP(11, 10),
-       THERMAL_SENSOR_ATTR_TEMP(12, 11),
-       THERMAL_SENSOR_ATTR_TEMP(13, 12),
-       THERMAL_SENSOR_ATTR_TEMP(14, 13),
-       THERMAL_SENSOR_ATTR_TEMP(15, 14),
-       THERMAL_SENSOR_ATTR_TEMP(16, 15),
-};
-
-#define THERMAL_ATTRS(X) \
-       &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
-
-static struct attribute *thermal_temp_input_attr[] = {
-       THERMAL_ATTRS(8),
-       THERMAL_ATTRS(9),
-       THERMAL_ATTRS(10),
-       THERMAL_ATTRS(11),
-       THERMAL_ATTRS(12),
-       THERMAL_ATTRS(13),
-       THERMAL_ATTRS(14),
-       THERMAL_ATTRS(15),
-       THERMAL_ATTRS(0),
-       THERMAL_ATTRS(1),
-       THERMAL_ATTRS(2),
-       THERMAL_ATTRS(3),
-       THERMAL_ATTRS(4),
-       THERMAL_ATTRS(5),
-       THERMAL_ATTRS(6),
-       THERMAL_ATTRS(7),
-       NULL
-};
-
-static const struct attribute_group thermal_temp_input16_group = {
-       .attrs = thermal_temp_input_attr
-};
-
-static const struct attribute_group thermal_temp_input8_group = {
-       .attrs = &thermal_temp_input_attr[8]
-};
-
-#undef THERMAL_SENSOR_ATTR_TEMP
-#undef THERMAL_ATTRS
-
-/* --------------------------------------------------------------------- */
-
-static int __init thermal_init(struct ibm_init_struct *iibm)
-{
-       u8 t, ta1, ta2;
-       int i;
-       int acpi_tmp7;
-       int res;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
-
-       acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
-
-       if (thinkpad_id.ec_model) {
-               /*
-                * Direct EC access mode: sensors at registers
-                * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
-                * non-implemented, thermal sensors return 0x80 when
-                * not available
-                */
-
-               ta1 = ta2 = 0;
-               for (i = 0; i < 8; i++) {
-                       if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
-                               ta1 |= t;
-                       } else {
-                               ta1 = 0;
-                               break;
-                       }
-                       if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
-                               ta2 |= t;
-                       } else {
-                               ta1 = 0;
-                               break;
-                       }
-               }
-               if (ta1 == 0) {
-                       /* This is sheer paranoia, but we handle it anyway */
-                       if (acpi_tmp7) {
-                               printk(TPACPI_ERR
-                                      "ThinkPad ACPI EC access misbehaving, "
-                                      "falling back to ACPI TMPx access "
-                                      "mode\n");
-                               thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
-                       } else {
-                               printk(TPACPI_ERR
-                                      "ThinkPad ACPI EC access misbehaving, "
-                                      "disabling thermal sensors access\n");
-                               thermal_read_mode = TPACPI_THERMAL_NONE;
-                       }
-               } else {
-                       thermal_read_mode =
-                           (ta2 != 0) ?
-                           TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
-               }
-       } else if (acpi_tmp7) {
-               if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
-                       /* 600e/x, 770e, 770x */
-                       thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
-               } else {
-                       /* Standard ACPI TMPx access, max 8 sensors */
-                       thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
-               }
-       } else {
-               /* temperatures not supported on 570, G4x, R30, R31, R32 */
-               thermal_read_mode = TPACPI_THERMAL_NONE;
-       }
-
-       vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
-               str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
-               thermal_read_mode);
-
-       switch (thermal_read_mode) {
-       case TPACPI_THERMAL_TPEC_16:
-               res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
-                               &thermal_temp_input16_group);
-               if (res)
-                       return res;
-               break;
-       case TPACPI_THERMAL_TPEC_8:
-       case TPACPI_THERMAL_ACPI_TMP07:
-       case TPACPI_THERMAL_ACPI_UPDT:
-               res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
-                               &thermal_temp_input8_group);
-               if (res)
-                       return res;
-               break;
-       case TPACPI_THERMAL_NONE:
-       default:
-               return 1;
-       }
-
-       return 0;
-}
-
-static void thermal_exit(void)
-{
-       switch (thermal_read_mode) {
-       case TPACPI_THERMAL_TPEC_16:
-               sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
-                                  &thermal_temp_input16_group);
-               break;
-       case TPACPI_THERMAL_TPEC_8:
-       case TPACPI_THERMAL_ACPI_TMP07:
-       case TPACPI_THERMAL_ACPI_UPDT:
-               sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
-                                  &thermal_temp_input16_group);
-               break;
-       case TPACPI_THERMAL_NONE:
-       default:
-               break;
-       }
-}
-
-static int thermal_read(char *p)
-{
-       int len = 0;
-       int n, i;
-       struct ibm_thermal_sensors_struct t;
-
-       n = thermal_get_sensors(&t);
-       if (unlikely(n < 0))
-               return n;
-
-       len += sprintf(p + len, "temperatures:\t");
-
-       if (n > 0) {
-               for (i = 0; i < (n - 1); i++)
-                       len += sprintf(p + len, "%d ", t.temp[i] / 1000);
-               len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
-       } else
-               len += sprintf(p + len, "not supported\n");
-
-       return len;
-}
-
-static struct ibm_struct thermal_driver_data = {
-       .name = "thermal",
-       .read = thermal_read,
-       .exit = thermal_exit,
-};
-
-/*************************************************************************
- * EC Dump subdriver
- */
-
-static u8 ecdump_regs[256];
-
-static int ecdump_read(char *p)
-{
-       int len = 0;
-       int i, j;
-       u8 v;
-
-       len += sprintf(p + len, "EC      "
-                      " +00 +01 +02 +03 +04 +05 +06 +07"
-                      " +08 +09 +0a +0b +0c +0d +0e +0f\n");
-       for (i = 0; i < 256; i += 16) {
-               len += sprintf(p + len, "EC 0x%02x:", i);
-               for (j = 0; j < 16; j++) {
-                       if (!acpi_ec_read(i + j, &v))
-                               break;
-                       if (v != ecdump_regs[i + j])
-                               len += sprintf(p + len, " *%02x", v);
-                       else
-                               len += sprintf(p + len, "  %02x", v);
-                       ecdump_regs[i + j] = v;
-               }
-               len += sprintf(p + len, "\n");
-               if (j != 16)
-                       break;
-       }
-
-       /* These are way too dangerous to advertise openly... */
-#if 0
-       len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
-                      " (<offset> is 00-ff, <value> is 00-ff)\n");
-       len += sprintf(p + len, "commands:\t0x<offset> <value>  "
-                      " (<offset> is 00-ff, <value> is 0-255)\n");
-#endif
-       return len;
-}
-
-static int ecdump_write(char *buf)
-{
-       char *cmd;
-       int i, v;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
-                       /* i and v set */
-               } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
-                       /* i and v set */
-               } else
-                       return -EINVAL;
-               if (i >= 0 && i < 256 && v >= 0 && v < 256) {
-                       if (!acpi_ec_write(i, v))
-                               return -EIO;
-               } else
-                       return -EINVAL;
-       }
-
-       return 0;
-}
-
-static struct ibm_struct ecdump_driver_data = {
-       .name = "ecdump",
-       .read = ecdump_read,
-       .write = ecdump_write,
-       .flags.experimental = 1,
-};
-
-/*************************************************************************
- * Backlight/brightness subdriver
- */
-
-#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
-
-enum {
-       TP_EC_BACKLIGHT = 0x31,
-
-       /* TP_EC_BACKLIGHT bitmasks */
-       TP_EC_BACKLIGHT_LVLMSK = 0x1F,
-       TP_EC_BACKLIGHT_CMDMSK = 0xE0,
-       TP_EC_BACKLIGHT_MAPSW = 0x20,
-};
-
-static struct backlight_device *ibm_backlight_device;
-static int brightness_mode;
-static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
-
-static struct mutex brightness_mutex;
-
-/*
- * ThinkPads can read brightness from two places: EC 0x31, or
- * CMOS NVRAM byte 0x5E, bits 0-3.
- *
- * EC 0x31 has the following layout
- *   Bit 7: unknown function
- *   Bit 6: unknown function
- *   Bit 5: Z: honour scale changes, NZ: ignore scale changes
- *   Bit 4: must be set to zero to avoid problems
- *   Bit 3-0: backlight brightness level
- *
- * brightness_get_raw returns status data in the EC 0x31 layout
- */
-static int brightness_get_raw(int *status)
-{
-       u8 lec = 0, lcmos = 0, level = 0;
-
-       if (brightness_mode & 1) {
-               if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
-                       return -EIO;
-               level = lec & TP_EC_BACKLIGHT_LVLMSK;
-       };
-       if (brightness_mode & 2) {
-               lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
-                        & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
-                       >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
-               lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
-               level = lcmos;
-       }
-
-       if (brightness_mode == 3) {
-               *status = lec;  /* Prefer EC, CMOS is just a backing store */
-               lec &= TP_EC_BACKLIGHT_LVLMSK;
-               if (lec == lcmos)
-                       tp_warned.bright_cmos_ec_unsync = 0;
-               else {
-                       if (!tp_warned.bright_cmos_ec_unsync) {
-                               printk(TPACPI_ERR
-                                       "CMOS NVRAM (%u) and EC (%u) do not "
-                                       "agree on display brightness level\n",
-                                       (unsigned int) lcmos,
-                                       (unsigned int) lec);
-                               tp_warned.bright_cmos_ec_unsync = 1;
-                       }
-                       return -EIO;
-               }
-       } else {
-               *status = level;
-       }
-
-       return 0;
-}
-
-/* May return EINTR which can always be mapped to ERESTARTSYS */
-static int brightness_set(int value)
-{
-       int cmos_cmd, inc, i, res;
-       int current_value;
-       int command_bits;
-
-       if (value > ((tp_features.bright_16levels)? 15 : 7) ||
-           value < 0)
-               return -EINVAL;
-
-       res = mutex_lock_interruptible(&brightness_mutex);
-       if (res < 0)
-               return res;
-
-       res = brightness_get_raw(&current_value);
-       if (res < 0)
-               goto errout;
-
-       command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
-       current_value &= TP_EC_BACKLIGHT_LVLMSK;
-
-       cmos_cmd = value > current_value ?
-                       TP_CMOS_BRIGHTNESS_UP :
-                       TP_CMOS_BRIGHTNESS_DOWN;
-       inc = (value > current_value)? 1 : -1;
-
-       res = 0;
-       for (i = current_value; i != value; i += inc) {
-               if ((brightness_mode & 2) &&
-                   issue_thinkpad_cmos_command(cmos_cmd)) {
-                       res = -EIO;
-                       goto errout;
-               }
-               if ((brightness_mode & 1) &&
-                   !acpi_ec_write(TP_EC_BACKLIGHT,
-                                  (i + inc) | command_bits)) {
-                       res = -EIO;
-                       goto errout;;
-               }
-       }
-
-errout:
-       mutex_unlock(&brightness_mutex);
-       return res;
-}
-
-/* sysfs backlight class ----------------------------------------------- */
-
-static int brightness_update_status(struct backlight_device *bd)
-{
-       /* it is the backlight class's job (caller) to handle
-        * EINTR and other errors properly */
-       return brightness_set(
-               (bd->props.fb_blank == FB_BLANK_UNBLANK &&
-                bd->props.power == FB_BLANK_UNBLANK) ?
-                               bd->props.brightness : 0);
-}
-
-static int brightness_get(struct backlight_device *bd)
-{
-       int status, res;
-
-       res = brightness_get_raw(&status);
-       if (res < 0)
-               return 0; /* FIXME: teach backlight about error handling */
-
-       return status & TP_EC_BACKLIGHT_LVLMSK;
-}
-
-static struct backlight_ops ibm_backlight_data = {
-       .get_brightness = brightness_get,
-       .update_status  = brightness_update_status,
-};
-
-/* --------------------------------------------------------------------- */
-
-static int __init brightness_init(struct ibm_init_struct *iibm)
-{
-       int b;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
-
-       mutex_init(&brightness_mutex);
-
-       /*
-        * We always attempt to detect acpi support, so as to switch
-        * Lenovo Vista BIOS to ACPI brightness mode even if we are not
-        * going to publish a backlight interface
-        */
-       b = tpacpi_check_std_acpi_brightness_support();
-       if (b > 0) {
-
-               if (acpi_video_backlight_support()) {
-                       if (brightness_enable > 1) {
-                               printk(TPACPI_NOTICE
-                                      "Standard ACPI backlight interface "
-                                      "available, not loading native one.\n");
-                               return 1;
-                       } else if (brightness_enable == 1) {
-                               printk(TPACPI_NOTICE
-                                      "Backlight control force enabled, even if standard "
-                                      "ACPI backlight interface is available\n");
-                       }
-               } else {
-                       if (brightness_enable > 1) {
-                               printk(TPACPI_NOTICE
-                                      "Standard ACPI backlight interface not "
-                                      "available, thinkpad_acpi native "
-                                      "brightness control enabled\n");
-                       }
-               }
-       }
-
-       if (!brightness_enable) {
-               dbg_printk(TPACPI_DBG_INIT,
-                          "brightness support disabled by "
-                          "module parameter\n");
-               return 1;
-       }
-
-       if (b > 16) {
-               printk(TPACPI_ERR
-                      "Unsupported brightness interface, "
-                      "please contact %s\n", TPACPI_MAIL);
-               return 1;
-       }
-       if (b == 16)
-               tp_features.bright_16levels = 1;
-
-       if (!brightness_mode) {
-               if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
-                       brightness_mode = 2;
-               else
-                       brightness_mode = 3;
-
-               dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
-                       brightness_mode);
-       }
-
-       if (brightness_mode > 3)
-               return -EINVAL;
-
-       if (brightness_get_raw(&b) < 0)
-               return 1;
-
-       if (tp_features.bright_16levels)
-               printk(TPACPI_INFO
-                      "detected a 16-level brightness capable ThinkPad\n");
-
-       ibm_backlight_device = backlight_device_register(
-                                       TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
-                                       &ibm_backlight_data);
-       if (IS_ERR(ibm_backlight_device)) {
-               printk(TPACPI_ERR "Could not register backlight device\n");
-               return PTR_ERR(ibm_backlight_device);
-       }
-       vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
-
-       ibm_backlight_device->props.max_brightness =
-                               (tp_features.bright_16levels)? 15 : 7;
-       ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
-       backlight_update_status(ibm_backlight_device);
-
-       return 0;
-}
-
-static void brightness_exit(void)
-{
-       if (ibm_backlight_device) {
-               vdbg_printk(TPACPI_DBG_EXIT,
-                           "calling backlight_device_unregister()\n");
-               backlight_device_unregister(ibm_backlight_device);
-       }
-}
-
-static int brightness_read(char *p)
-{
-       int len = 0;
-       int level;
-
-       level = brightness_get(NULL);
-       if (level < 0) {
-               len += sprintf(p + len, "level:\t\tunreadable\n");
-       } else {
-               len += sprintf(p + len, "level:\t\t%d\n", level);
-               len += sprintf(p + len, "commands:\tup, down\n");
-               len += sprintf(p + len, "commands:\tlevel <level>"
-                              " (<level> is 0-%d)\n",
-                              (tp_features.bright_16levels) ? 15 : 7);
-       }
-
-       return len;
-}
-
-static int brightness_write(char *buf)
-{
-       int level;
-       int rc;
-       char *cmd;
-       int max_level = (tp_features.bright_16levels) ? 15 : 7;
-
-       level = brightness_get(NULL);
-       if (level < 0)
-               return level;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (strlencmp(cmd, "up") == 0) {
-                       if (level < max_level)
-                               level++;
-               } else if (strlencmp(cmd, "down") == 0) {
-                       if (level > 0)
-                               level--;
-               } else if (sscanf(cmd, "level %d", &level) == 1 &&
-                          level >= 0 && level <= max_level) {
-                       /* new level set */
-               } else
-                       return -EINVAL;
-       }
-
-       /*
-        * Now we know what the final level should be, so we try to set it.
-        * Doing it this way makes the syscall restartable in case of EINTR
-        */
-       rc = brightness_set(level);
-       return (rc == -EINTR)? ERESTARTSYS : rc;
-}
-
-static struct ibm_struct brightness_driver_data = {
-       .name = "brightness",
-       .read = brightness_read,
-       .write = brightness_write,
-       .exit = brightness_exit,
-};
-
-/*************************************************************************
- * Volume subdriver
- */
-
-static int volume_offset = 0x30;
-
-static int volume_read(char *p)
-{
-       int len = 0;
-       u8 level;
-
-       if (!acpi_ec_read(volume_offset, &level)) {
-               len += sprintf(p + len, "level:\t\tunreadable\n");
-       } else {
-               len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
-               len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
-               len += sprintf(p + len, "commands:\tup, down, mute\n");
-               len += sprintf(p + len, "commands:\tlevel <level>"
-                              " (<level> is 0-15)\n");
-       }
-
-       return len;
-}
-
-static int volume_write(char *buf)
-{
-       int cmos_cmd, inc, i;
-       u8 level, mute;
-       int new_level, new_mute;
-       char *cmd;
-
-       while ((cmd = next_cmd(&buf))) {
-               if (!acpi_ec_read(volume_offset, &level))
-                       return -EIO;
-               new_mute = mute = level & 0x40;
-               new_level = level = level & 0xf;
-
-               if (strlencmp(cmd, "up") == 0) {
-                       if (mute)
-                               new_mute = 0;
-                       else
-                               new_level = level == 15 ? 15 : level + 1;
-               } else if (strlencmp(cmd, "down") == 0) {
-                       if (mute)
-                               new_mute = 0;
-                       else
-                               new_level = level == 0 ? 0 : level - 1;
-               } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
-                          new_level >= 0 && new_level <= 15) {
-                       /* new_level set */
-               } else if (strlencmp(cmd, "mute") == 0) {
-                       new_mute = 0x40;
-               } else
-                       return -EINVAL;
-
-               if (new_level != level) {
-                       /* mute doesn't change */
-
-                       cmos_cmd = (new_level > level) ?
-                                       TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
-                       inc = new_level > level ? 1 : -1;
-
-                       if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
-                                    !acpi_ec_write(volume_offset, level)))
-                               return -EIO;
-
-                       for (i = level; i != new_level; i += inc)
-                               if (issue_thinkpad_cmos_command(cmos_cmd) ||
-                                   !acpi_ec_write(volume_offset, i + inc))
-                                       return -EIO;
-
-                       if (mute &&
-                           (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
-                            !acpi_ec_write(volume_offset, new_level + mute))) {
-                               return -EIO;
-                       }
-               }
-
-               if (new_mute != mute) {
-                       /* level doesn't change */
-
-                       cmos_cmd = (new_mute) ?
-                                  TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
-
-                       if (issue_thinkpad_cmos_command(cmos_cmd) ||
-                           !acpi_ec_write(volume_offset, level + new_mute))
-                               return -EIO;
-               }
-       }
-
-       return 0;
-}
-
-static struct ibm_struct volume_driver_data = {
-       .name = "volume",
-       .read = volume_read,
-       .write = volume_write,
-};
-
-/*************************************************************************
- * Fan subdriver
- */
-
-/*
- * FAN ACCESS MODES
- *
- * TPACPI_FAN_RD_ACPI_GFAN:
- *     ACPI GFAN method: returns fan level
- *
- *     see TPACPI_FAN_WR_ACPI_SFAN
- *     EC 0x2f (HFSP) not available if GFAN exists
- *
- * TPACPI_FAN_WR_ACPI_SFAN:
- *     ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
- *
- *     EC 0x2f (HFSP) might be available *for reading*, but do not use
- *     it for writing.
- *
- * TPACPI_FAN_WR_TPEC:
- *     ThinkPad EC register 0x2f (HFSP): fan control loop mode
- *     Supported on almost all ThinkPads
- *
- *     Fan speed changes of any sort (including those caused by the
- *     disengaged mode) are usually done slowly by the firmware as the
- *     maximum ammount of fan duty cycle change per second seems to be
- *     limited.
- *
- *     Reading is not available if GFAN exists.
- *     Writing is not available if SFAN exists.
- *
- *     Bits
- *      7      automatic mode engaged;
- *             (default operation mode of the ThinkPad)
- *             fan level is ignored in this mode.
- *      6      full speed mode (takes precedence over bit 7);
- *             not available on all thinkpads.  May disable
- *             the tachometer while the fan controller ramps up
- *             the speed (which can take up to a few *minutes*).
- *             Speeds up fan to 100% duty-cycle, which is far above
- *             the standard RPM levels.  It is not impossible that
- *             it could cause hardware damage.
- *     5-3     unused in some models.  Extra bits for fan level
- *             in others, but still useless as all values above
- *             7 map to the same speed as level 7 in these models.
- *     2-0     fan level (0..7 usually)
- *                     0x00 = stop
- *                     0x07 = max (set when temperatures critical)
- *             Some ThinkPads may have other levels, see
- *             TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
- *
- *     FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
- *     boot. Apparently the EC does not intialize it, so unless ACPI DSDT
- *     does so, its initial value is meaningless (0x07).
- *
- *     For firmware bugs, refer to:
- *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- *     ----
- *
- *     ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
- *     Main fan tachometer reading (in RPM)
- *
- *     This register is present on all ThinkPads with a new-style EC, and
- *     it is known not to be present on the A21m/e, and T22, as there is
- *     something else in offset 0x84 according to the ACPI DSDT.  Other
- *     ThinkPads from this same time period (and earlier) probably lack the
- *     tachometer as well.
- *
- *     Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
- *     was never fixed by IBM to report the EC firmware version string
- *     probably support the tachometer (like the early X models), so
- *     detecting it is quite hard.  We need more data to know for sure.
- *
- *     FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
- *     might result.
- *
- *     FIRMWARE BUG: may go stale while the EC is switching to full speed
- *     mode.
- *
- *     For firmware bugs, refer to:
- *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
- *
- * TPACPI_FAN_WR_ACPI_FANS:
- *     ThinkPad X31, X40, X41.  Not available in the X60.
- *
- *     FANS ACPI handle: takes three arguments: low speed, medium speed,
- *     high speed.  ACPI DSDT seems to map these three speeds to levels
- *     as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
- *     (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
- *
- *     The speeds are stored on handles
- *     (FANA:FAN9), (FANC:FANB), (FANE:FAND).
- *
- *     There are three default speed sets, acessible as handles:
- *     FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
- *
- *     ACPI DSDT switches which set is in use depending on various
- *     factors.
- *
- *     TPACPI_FAN_WR_TPEC is also available and should be used to
- *     command the fan.  The X31/X40/X41 seems to have 8 fan levels,
- *     but the ACPI tables just mention level 7.
- */
-
-enum {                                 /* Fan control constants */
-       fan_status_offset = 0x2f,       /* EC register 0x2f */
-       fan_rpm_offset = 0x84,          /* EC register 0x84: LSB, 0x85 MSB (RPM)
-                                        * 0x84 must be read before 0x85 */
-
-       TP_EC_FAN_FULLSPEED = 0x40,     /* EC fan mode: full speed */
-       TP_EC_FAN_AUTO      = 0x80,     /* EC fan mode: auto fan control */
-
-       TPACPI_FAN_LAST_LEVEL = 0x100,  /* Use cached last-seen fan level */
-};
-
-enum fan_status_access_mode {
-       TPACPI_FAN_NONE = 0,            /* No fan status or control */
-       TPACPI_FAN_RD_ACPI_GFAN,        /* Use ACPI GFAN */
-       TPACPI_FAN_RD_TPEC,             /* Use ACPI EC regs 0x2f, 0x84-0x85 */
-};
-
-enum fan_control_access_mode {
-       TPACPI_FAN_WR_NONE = 0,         /* No fan control */
-       TPACPI_FAN_WR_ACPI_SFAN,        /* Use ACPI SFAN */
-       TPACPI_FAN_WR_TPEC,             /* Use ACPI EC reg 0x2f */
-       TPACPI_FAN_WR_ACPI_FANS,        /* Use ACPI FANS and EC reg 0x2f */
-};
-
-enum fan_control_commands {
-       TPACPI_FAN_CMD_SPEED    = 0x0001,       /* speed command */
-       TPACPI_FAN_CMD_LEVEL    = 0x0002,       /* level command  */
-       TPACPI_FAN_CMD_ENABLE   = 0x0004,       /* enable/disable cmd,
-                                                * and also watchdog cmd */
-};
-
-static int fan_control_allowed;
-
-static enum fan_status_access_mode fan_status_access_mode;
-static enum fan_control_access_mode fan_control_access_mode;
-static enum fan_control_commands fan_control_commands;
-
-static u8 fan_control_initial_status;
-static u8 fan_control_desired_level;
-static u8 fan_control_resume_level;
-static int fan_watchdog_maxinterval;
-
-static struct mutex fan_mutex;
-
-static void fan_watchdog_fire(struct work_struct *ignored);
-static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
-
-TPACPI_HANDLE(fans, ec, "FANS");       /* X31, X40, X41 */
-TPACPI_HANDLE(gfan, ec, "GFAN",        /* 570 */
-          "\\FSPD",            /* 600e/x, 770e, 770x */
-          );                   /* all others */
-TPACPI_HANDLE(sfan, ec, "SFAN",        /* 570 */
-          "JFNS",              /* 770x-JL */
-          );                   /* all others */
-
-/*
- * Call with fan_mutex held
- */
-static void fan_update_desired_level(u8 status)
-{
-       if ((status &
-            (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
-               if (status > 7)
-                       fan_control_desired_level = 7;
-               else
-                       fan_control_desired_level = status;
-       }
-}
-
-static int fan_get_status(u8 *status)
-{
-       u8 s;
-
-       /* TODO:
-        * Add TPACPI_FAN_RD_ACPI_FANS ? */
-
-       switch (fan_status_access_mode) {
-       case TPACPI_FAN_RD_ACPI_GFAN:
-               /* 570, 600e/x, 770e, 770x */
-
-               if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
-                       return -EIO;
-
-               if (likely(status))
-                       *status = s & 0x07;
-
-               break;
-
-       case TPACPI_FAN_RD_TPEC:
-               /* all except 570, 600e/x, 770e, 770x */
-               if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
-                       return -EIO;
-
-               if (likely(status))
-                       *status = s;
-
-               break;
-
-       default:
-               return -ENXIO;
-       }
-
-       return 0;
-}
-
-static int fan_get_status_safe(u8 *status)
-{
-       int rc;
-       u8 s;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-       rc = fan_get_status(&s);
-       if (!rc)
-               fan_update_desired_level(s);
-       mutex_unlock(&fan_mutex);
-
-       if (status)
-               *status = s;
-
-       return rc;
-}
-
-static int fan_get_speed(unsigned int *speed)
-{
-       u8 hi, lo;
-
-       switch (fan_status_access_mode) {
-       case TPACPI_FAN_RD_TPEC:
-               /* all except 570, 600e/x, 770e, 770x */
-               if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
-                            !acpi_ec_read(fan_rpm_offset + 1, &hi)))
-                       return -EIO;
-
-               if (likely(speed))
-                       *speed = (hi << 8) | lo;
-
-               break;
-
-       default:
-               return -ENXIO;
-       }
-
-       return 0;
-}
-
-static int fan_set_level(int level)
-{
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       switch (fan_control_access_mode) {
-       case TPACPI_FAN_WR_ACPI_SFAN:
-               if (level >= 0 && level <= 7) {
-                       if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
-                               return -EIO;
-               } else
-                       return -EINVAL;
-               break;
-
-       case TPACPI_FAN_WR_ACPI_FANS:
-       case TPACPI_FAN_WR_TPEC:
-               if (!(level & TP_EC_FAN_AUTO) &&
-                   !(level & TP_EC_FAN_FULLSPEED) &&
-                   ((level < 0) || (level > 7)))
-                       return -EINVAL;
-
-               /* safety net should the EC not support AUTO
-                * or FULLSPEED mode bits and just ignore them */
-               if (level & TP_EC_FAN_FULLSPEED)
-                       level |= 7;     /* safety min speed 7 */
-               else if (level & TP_EC_FAN_AUTO)
-                       level |= 4;     /* safety min speed 4 */
-
-               if (!acpi_ec_write(fan_status_offset, level))
-                       return -EIO;
-               else
-                       tp_features.fan_ctrl_status_undef = 0;
-               break;
-
-       default:
-               return -ENXIO;
-       }
-       return 0;
-}
-
-static int fan_set_level_safe(int level)
-{
-       int rc;
-
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-
-       if (level == TPACPI_FAN_LAST_LEVEL)
-               level = fan_control_desired_level;
-
-       rc = fan_set_level(level);
-       if (!rc)
-               fan_update_desired_level(level);
-
-       mutex_unlock(&fan_mutex);
-       return rc;
-}
-
-static int fan_set_enable(void)
-{
-       u8 s;
-       int rc;
-
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-
-       switch (fan_control_access_mode) {
-       case TPACPI_FAN_WR_ACPI_FANS:
-       case TPACPI_FAN_WR_TPEC:
-               rc = fan_get_status(&s);
-               if (rc < 0)
-                       break;
-
-               /* Don't go out of emergency fan mode */
-               if (s != 7) {
-                       s &= 0x07;
-                       s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
-               }
-
-               if (!acpi_ec_write(fan_status_offset, s))
-                       rc = -EIO;
-               else {
-                       tp_features.fan_ctrl_status_undef = 0;
-                       rc = 0;
-               }
-               break;
-
-       case TPACPI_FAN_WR_ACPI_SFAN:
-               rc = fan_get_status(&s);
-               if (rc < 0)
-                       break;
-
-               s &= 0x07;
-
-               /* Set fan to at least level 4 */
-               s |= 4;
-
-               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
-                       rc = -EIO;
-               else
-                       rc = 0;
-               break;
-
-       default:
-               rc = -ENXIO;
-       }
-
-       mutex_unlock(&fan_mutex);
-       return rc;
-}
-
-static int fan_set_disable(void)
-{
-       int rc;
-
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-
-       rc = 0;
-       switch (fan_control_access_mode) {
-       case TPACPI_FAN_WR_ACPI_FANS:
-       case TPACPI_FAN_WR_TPEC:
-               if (!acpi_ec_write(fan_status_offset, 0x00))
-                       rc = -EIO;
-               else {
-                       fan_control_desired_level = 0;
-                       tp_features.fan_ctrl_status_undef = 0;
-               }
-               break;
-
-       case TPACPI_FAN_WR_ACPI_SFAN:
-               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
-                       rc = -EIO;
-               else
-                       fan_control_desired_level = 0;
-               break;
-
-       default:
-               rc = -ENXIO;
-       }
-
-
-       mutex_unlock(&fan_mutex);
-       return rc;
-}
-
-static int fan_set_speed(int speed)
-{
-       int rc;
-
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-
-       rc = 0;
-       switch (fan_control_access_mode) {
-       case TPACPI_FAN_WR_ACPI_FANS:
-               if (speed >= 0 && speed <= 65535) {
-                       if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
-                                       speed, speed, speed))
-                               rc = -EIO;
-               } else
-                       rc = -EINVAL;
-               break;
-
-       default:
-               rc = -ENXIO;
-       }
-
-       mutex_unlock(&fan_mutex);
-       return rc;
-}
-
-static void fan_watchdog_reset(void)
-{
-       static int fan_watchdog_active;
-
-       if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
-               return;
-
-       if (fan_watchdog_active)
-               cancel_delayed_work(&fan_watchdog_task);
-
-       if (fan_watchdog_maxinterval > 0 &&
-           tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
-               fan_watchdog_active = 1;
-               if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
-                               msecs_to_jiffies(fan_watchdog_maxinterval
-                                                * 1000))) {
-                       printk(TPACPI_ERR
-                              "failed to queue the fan watchdog, "
-                              "watchdog will not trigger\n");
-               }
-       } else
-               fan_watchdog_active = 0;
-}
-
-static void fan_watchdog_fire(struct work_struct *ignored)
-{
-       int rc;
-
-       if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
-               return;
-
-       printk(TPACPI_NOTICE "fan watchdog: enabling fan\n");
-       rc = fan_set_enable();
-       if (rc < 0) {
-               printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, "
-                       "will try again later...\n", -rc);
-               /* reschedule for later */
-               fan_watchdog_reset();
-       }
-}
-
-/*
- * SYSFS fan layout: hwmon compatible (device)
- *
- * pwm*_enable:
- *     0: "disengaged" mode
- *     1: manual mode
- *     2: native EC "auto" mode (recommended, hardware default)
- *
- * pwm*: set speed in manual mode, ignored otherwise.
- *     0 is level 0; 255 is level 7. Intermediate points done with linear
- *     interpolation.
- *
- * fan*_input: tachometer reading, RPM
- *
- *
- * SYSFS fan layout: extensions
- *
- * fan_watchdog (driver):
- *     fan watchdog interval in seconds, 0 disables (default), max 120
- */
-
-/* sysfs fan pwm1_enable ----------------------------------------------- */
-static ssize_t fan_pwm1_enable_show(struct device *dev,
-                                   struct device_attribute *attr,
-                                   char *buf)
-{
-       int res, mode;
-       u8 status;
-
-       res = fan_get_status_safe(&status);
-       if (res)
-               return res;
-
-       if (unlikely(tp_features.fan_ctrl_status_undef)) {
-               if (status != fan_control_initial_status) {
-                       tp_features.fan_ctrl_status_undef = 0;
-               } else {
-                       /* Return most likely status. In fact, it
-                        * might be the only possible status */
-                       status = TP_EC_FAN_AUTO;
-               }
-       }
-
-       if (status & TP_EC_FAN_FULLSPEED) {
-               mode = 0;
-       } else if (status & TP_EC_FAN_AUTO) {
-               mode = 2;
-       } else
-               mode = 1;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", mode);
-}
-
-static ssize_t fan_pwm1_enable_store(struct device *dev,
-                                    struct device_attribute *attr,
-                                    const char *buf, size_t count)
-{
-       unsigned long t;
-       int res, level;
-
-       if (parse_strtoul(buf, 2, &t))
-               return -EINVAL;
-
-       switch (t) {
-       case 0:
-               level = TP_EC_FAN_FULLSPEED;
-               break;
-       case 1:
-               level = TPACPI_FAN_LAST_LEVEL;
-               break;
-       case 2:
-               level = TP_EC_FAN_AUTO;
-               break;
-       case 3:
-               /* reserved for software-controlled auto mode */
-               return -ENOSYS;
-       default:
-               return -EINVAL;
-       }
-
-       res = fan_set_level_safe(level);
-       if (res == -ENXIO)
-               return -EINVAL;
-       else if (res < 0)
-               return res;
-
-       fan_watchdog_reset();
-
-       return count;
-}
-
-static struct device_attribute dev_attr_fan_pwm1_enable =
-       __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
-               fan_pwm1_enable_show, fan_pwm1_enable_store);
-
-/* sysfs fan pwm1 ------------------------------------------------------ */
-static ssize_t fan_pwm1_show(struct device *dev,
-                            struct device_attribute *attr,
-                            char *buf)
-{
-       int res;
-       u8 status;
-
-       res = fan_get_status_safe(&status);
-       if (res)
-               return res;
-
-       if (unlikely(tp_features.fan_ctrl_status_undef)) {
-               if (status != fan_control_initial_status) {
-                       tp_features.fan_ctrl_status_undef = 0;
-               } else {
-                       status = TP_EC_FAN_AUTO;
-               }
-       }
-
-       if ((status &
-            (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
-               status = fan_control_desired_level;
-
-       if (status > 7)
-               status = 7;
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
-}
-
-static ssize_t fan_pwm1_store(struct device *dev,
-                             struct device_attribute *attr,
-                             const char *buf, size_t count)
-{
-       unsigned long s;
-       int rc;
-       u8 status, newlevel;
-
-       if (parse_strtoul(buf, 255, &s))
-               return -EINVAL;
-
-       /* scale down from 0-255 to 0-7 */
-       newlevel = (s >> 5) & 0x07;
-
-       if (mutex_lock_interruptible(&fan_mutex))
-               return -ERESTARTSYS;
-
-       rc = fan_get_status(&status);
-       if (!rc && (status &
-                   (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
-               rc = fan_set_level(newlevel);
-               if (rc == -ENXIO)
-                       rc = -EINVAL;
-               else if (!rc) {
-                       fan_update_desired_level(newlevel);
-                       fan_watchdog_reset();
-               }
-       }
-
-       mutex_unlock(&fan_mutex);
-       return (rc)? rc : count;
-}
-
-static struct device_attribute dev_attr_fan_pwm1 =
-       __ATTR(pwm1, S_IWUSR | S_IRUGO,
-               fan_pwm1_show, fan_pwm1_store);
-
-/* sysfs fan fan1_input ------------------------------------------------ */
-static ssize_t fan_fan1_input_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       int res;
-       unsigned int speed;
-
-       res = fan_get_speed(&speed);
-       if (res < 0)
-               return res;
-
-       return snprintf(buf, PAGE_SIZE, "%u\n", speed);
-}
-
-static struct device_attribute dev_attr_fan_fan1_input =
-       __ATTR(fan1_input, S_IRUGO,
-               fan_fan1_input_show, NULL);
-
-/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
-static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
-                                    char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
-}
-
-static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
-                                     const char *buf, size_t count)
-{
-       unsigned long t;
-
-       if (parse_strtoul(buf, 120, &t))
-               return -EINVAL;
-
-       if (!fan_control_allowed)
-               return -EPERM;
-
-       fan_watchdog_maxinterval = t;
-       fan_watchdog_reset();
-
-       return count;
-}
-
-static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
-               fan_fan_watchdog_show, fan_fan_watchdog_store);
-
-/* --------------------------------------------------------------------- */
-static struct attribute *fan_attributes[] = {
-       &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
-       &dev_attr_fan_fan1_input.attr,
-       NULL
-};
-
-static const struct attribute_group fan_attr_group = {
-       .attrs = fan_attributes,
-};
-
-static int __init fan_init(struct ibm_init_struct *iibm)
-{
-       int rc;
-
-       vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
-
-       mutex_init(&fan_mutex);
-       fan_status_access_mode = TPACPI_FAN_NONE;
-       fan_control_access_mode = TPACPI_FAN_WR_NONE;
-       fan_control_commands = 0;
-       fan_watchdog_maxinterval = 0;
-       tp_features.fan_ctrl_status_undef = 0;
-       fan_control_desired_level = 7;
-
-       TPACPI_ACPIHANDLE_INIT(fans);
-       TPACPI_ACPIHANDLE_INIT(gfan);
-       TPACPI_ACPIHANDLE_INIT(sfan);
-
-       if (gfan_handle) {
-               /* 570, 600e/x, 770e, 770x */
-               fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
-       } else {
-               /* all other ThinkPads: note that even old-style
-                * ThinkPad ECs supports the fan control register */
-               if (likely(acpi_ec_read(fan_status_offset,
-                                       &fan_control_initial_status))) {
-                       fan_status_access_mode = TPACPI_FAN_RD_TPEC;
-
-                       /* In some ThinkPads, neither the EC nor the ACPI
-                        * DSDT initialize the fan status, and it ends up
-                        * being set to 0x07 when it *could* be either
-                        * 0x07 or 0x80.
-                        *
-                        * Enable for TP-1Y (T43), TP-78 (R51e),
-                        * TP-76 (R52), TP-70 (T43, R52), which are known
-                        * to be buggy. */
-                       if (fan_control_initial_status == 0x07) {
-                               switch (thinkpad_id.ec_model) {
-                               case 0x5931: /* TP-1Y */
-                               case 0x3837: /* TP-78 */
-                               case 0x3637: /* TP-76 */
-                               case 0x3037: /* TP-70 */
-                                       printk(TPACPI_NOTICE
-                                              "fan_init: initial fan status "
-                                              "is unknown, assuming it is "
-                                              "in auto mode\n");
-                                       tp_features.fan_ctrl_status_undef = 1;
-                                       ;;
-                               }
-                       }
-               } else {
-                       printk(TPACPI_ERR
-                              "ThinkPad ACPI EC access misbehaving, "
-                              "fan status and control unavailable\n");
-                       return 1;
-               }
-       }
-
-       if (sfan_handle) {
-               /* 570, 770x-JL */
-               fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
-               fan_control_commands |=
-                   TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
-       } else {
-               if (!gfan_handle) {
-                       /* gfan without sfan means no fan control */
-                       /* all other models implement TP EC 0x2f control */
-
-                       if (fans_handle) {
-                               /* X31, X40, X41 */
-                               fan_control_access_mode =
-                                   TPACPI_FAN_WR_ACPI_FANS;
-                               fan_control_commands |=
-                                   TPACPI_FAN_CMD_SPEED |
-                                   TPACPI_FAN_CMD_LEVEL |
-                                   TPACPI_FAN_CMD_ENABLE;
-                       } else {
-                               fan_control_access_mode = TPACPI_FAN_WR_TPEC;
-                               fan_control_commands |=
-                                   TPACPI_FAN_CMD_LEVEL |
-                                   TPACPI_FAN_CMD_ENABLE;
-                       }
-               }
-       }
-
-       vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
-               str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
-                 fan_control_access_mode != TPACPI_FAN_WR_NONE),
-               fan_status_access_mode, fan_control_access_mode);
-
-       /* fan control master switch */
-       if (!fan_control_allowed) {
-               fan_control_access_mode = TPACPI_FAN_WR_NONE;
-               fan_control_commands = 0;
-               dbg_printk(TPACPI_DBG_INIT,
-                          "fan control features disabled by parameter\n");
-       }
-
-       /* update fan_control_desired_level */
-       if (fan_status_access_mode != TPACPI_FAN_NONE)
-               fan_get_status_safe(NULL);
-
-       if (fan_status_access_mode != TPACPI_FAN_NONE ||
-           fan_control_access_mode != TPACPI_FAN_WR_NONE) {
-               rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
-                                        &fan_attr_group);
-               if (rc < 0)
-                       return rc;
-
-               rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
-                                       &driver_attr_fan_watchdog);
-               if (rc < 0) {
-                       sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
-                                       &fan_attr_group);
-                       return rc;
-               }
-               return 0;
-       } else
-               return 1;
-}
-
-static void fan_exit(void)
-{
-       vdbg_printk(TPACPI_DBG_EXIT,
-                   "cancelling any pending fan watchdog tasks\n");
-
-       /* FIXME: can we really do this unconditionally? */
-       sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
-       driver_remove_file(&tpacpi_hwmon_pdriver.driver,
-                          &driver_attr_fan_watchdog);
-
-       cancel_delayed_work(&fan_watchdog_task);
-       flush_workqueue(tpacpi_wq);
-}
-
-static void fan_suspend(pm_message_t state)
-{
-       int rc;
-
-       if (!fan_control_allowed)
-               return;
-
-       /* Store fan status in cache */
-       fan_control_resume_level = 0;
-       rc = fan_get_status_safe(&fan_control_resume_level);
-       if (rc < 0)
-               printk(TPACPI_NOTICE
-                       "failed to read fan level for later "
-                       "restore during resume: %d\n", rc);
-
-       /* if it is undefined, don't attempt to restore it.
-        * KEEP THIS LAST */
-       if (tp_features.fan_ctrl_status_undef)
-               fan_control_resume_level = 0;
-}
-
-static void fan_resume(void)
-{
-       u8 current_level = 7;
-       bool do_set = false;
-       int rc;
-
-       /* DSDT *always* updates status on resume */
-       tp_features.fan_ctrl_status_undef = 0;
-
-       if (!fan_control_allowed ||
-           !fan_control_resume_level ||
-           (fan_get_status_safe(&current_level) < 0))
-               return;
-
-       switch (fan_control_access_mode) {
-       case TPACPI_FAN_WR_ACPI_SFAN:
-               /* never decrease fan level */
-               do_set = (fan_control_resume_level > current_level);
-               break;
-       case TPACPI_FAN_WR_ACPI_FANS:
-       case TPACPI_FAN_WR_TPEC:
-               /* never decrease fan level, scale is:
-                * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO
-                *
-                * We expect the firmware to set either 7 or AUTO, but we
-                * handle FULLSPEED out of paranoia.
-                *
-                * So, we can safely only restore FULLSPEED or 7, anything
-                * else could slow the fan.  Restoring AUTO is useless, at
-                * best that's exactly what the DSDT already set (it is the
-                * slower it uses).
-                *
-                * Always keep in mind that the DSDT *will* have set the
-                * fans to what the vendor supposes is the best level.  We
-                * muck with it only to speed the fan up.
-                */
-               if (fan_control_resume_level != 7 &&
-                   !(fan_control_resume_level & TP_EC_FAN_FULLSPEED))
-                       return;
-               else
-                       do_set = !(current_level & TP_EC_FAN_FULLSPEED) &&
-                                (current_level != fan_control_resume_level);
-               break;
-       default:
-               return;
-       }
-       if (do_set) {
-               printk(TPACPI_NOTICE
-                       "restoring fan level to 0x%02x\n",
-                       fan_control_resume_level);
-               rc = fan_set_level_safe(fan_control_resume_level);
-               if (rc < 0)
-                       printk(TPACPI_NOTICE
-                               "failed to restore fan level: %d\n", rc);
-       }
-}
-
-static int fan_read(char *p)
-{
-       int len = 0;
-       int rc;
-       u8 status;
-       unsigned int speed = 0;
-
-       switch (fan_status_access_mode) {
-       case TPACPI_FAN_RD_ACPI_GFAN:
-               /* 570, 600e/x, 770e, 770x */
-               rc = fan_get_status_safe(&status);
-               if (rc < 0)
-                       return rc;
-
-               len += sprintf(p + len, "status:\t\t%s\n"
-                              "level:\t\t%d\n",
-                              (status != 0) ? "enabled" : "disabled", status);
-               break;
-
-       case TPACPI_FAN_RD_TPEC:
-               /* all except 570, 600e/x, 770e, 770x */
-               rc = fan_get_status_safe(&status);
-               if (rc < 0)
-                       return rc;
-
-               if (unlikely(tp_features.fan_ctrl_status_undef)) {
-                       if (status != fan_control_initial_status)
-                               tp_features.fan_ctrl_status_undef = 0;
-                       else
-                               /* Return most likely status. In fact, it
-                                * might be the only possible status */
-                               status = TP_EC_FAN_AUTO;
-               }
-
-               len += sprintf(p + len, "status:\t\t%s\n",
-                              (status != 0) ? "enabled" : "disabled");
-
-               rc = fan_get_speed(&speed);
-               if (rc < 0)
-                       return rc;
-
-               len += sprintf(p + len, "speed:\t\t%d\n", speed);
-
-               if (status & TP_EC_FAN_FULLSPEED)
-                       /* Disengaged mode takes precedence */
-                       len += sprintf(p + len, "level:\t\tdisengaged\n");
-               else if (status & TP_EC_FAN_AUTO)
-                       len += sprintf(p + len, "level:\t\tauto\n");
-               else
-                       len += sprintf(p + len, "level:\t\t%d\n", status);
-               break;
-
-       case TPACPI_FAN_NONE:
-       default:
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-       }
-
-       if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
-               len += sprintf(p + len, "commands:\tlevel <level>");
-
-               switch (fan_control_access_mode) {
-               case TPACPI_FAN_WR_ACPI_SFAN:
-                       len += sprintf(p + len, " (<level> is 0-7)\n");
-                       break;
-
-               default:
-                       len += sprintf(p + len, " (<level> is 0-7, "
-                                      "auto, disengaged, full-speed)\n");
-                       break;
-               }
-       }
-
-       if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
-               len += sprintf(p + len, "commands:\tenable, disable\n"
-                              "commands:\twatchdog <timeout> (<timeout> "
-                              "is 0 (off), 1-120 (seconds))\n");
-
-       if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
-               len += sprintf(p + len, "commands:\tspeed <speed>"
-                              " (<speed> is 0-65535)\n");
-
-       return len;
-}
-
-static int fan_write_cmd_level(const char *cmd, int *rc)
-{
-       int level;
-
-       if (strlencmp(cmd, "level auto") == 0)
-               level = TP_EC_FAN_AUTO;
-       else if ((strlencmp(cmd, "level disengaged") == 0) |
-                       (strlencmp(cmd, "level full-speed") == 0))
-               level = TP_EC_FAN_FULLSPEED;
-       else if (sscanf(cmd, "level %d", &level) != 1)
-               return 0;
-
-       *rc = fan_set_level_safe(level);
-       if (*rc == -ENXIO)
-               printk(TPACPI_ERR "level command accepted for unsupported "
-                      "access mode %d", fan_control_access_mode);
-
-       return 1;
-}
-
-static int fan_write_cmd_enable(const char *cmd, int *rc)
-{
-       if (strlencmp(cmd, "enable") != 0)
-               return 0;
-
-       *rc = fan_set_enable();
-       if (*rc == -ENXIO)
-               printk(TPACPI_ERR "enable command accepted for unsupported "
-                      "access mode %d", fan_control_access_mode);
-
-       return 1;
-}
-
-static int fan_write_cmd_disable(const char *cmd, int *rc)
-{
-       if (strlencmp(cmd, "disable") != 0)
-               return 0;
-
-       *rc = fan_set_disable();
-       if (*rc == -ENXIO)
-               printk(TPACPI_ERR "disable command accepted for unsupported "
-                      "access mode %d", fan_control_access_mode);
-
-       return 1;
-}
-
-static int fan_write_cmd_speed(const char *cmd, int *rc)
-{
-       int speed;
-
-       /* TODO:
-        * Support speed <low> <medium> <high> ? */
-
-       if (sscanf(cmd, "speed %d", &speed) != 1)
-               return 0;
-
-       *rc = fan_set_speed(speed);
-       if (*rc == -ENXIO)
-               printk(TPACPI_ERR "speed command accepted for unsupported "
-                      "access mode %d", fan_control_access_mode);
-
-       return 1;
-}
-
-static int fan_write_cmd_watchdog(const char *cmd, int *rc)
-{
-       int interval;
-
-       if (sscanf(cmd, "watchdog %d", &interval) != 1)
-               return 0;
-
-       if (interval < 0 || interval > 120)
-               *rc = -EINVAL;
-       else
-               fan_watchdog_maxinterval = interval;
-
-       return 1;
-}
-
-static int fan_write(char *buf)
-{
-       char *cmd;
-       int rc = 0;
-
-       while (!rc && (cmd = next_cmd(&buf))) {
-               if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
-                     fan_write_cmd_level(cmd, &rc)) &&
-                   !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
-                     (fan_write_cmd_enable(cmd, &rc) ||
-                      fan_write_cmd_disable(cmd, &rc) ||
-                      fan_write_cmd_watchdog(cmd, &rc))) &&
-                   !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
-                     fan_write_cmd_speed(cmd, &rc))
-                   )
-                       rc = -EINVAL;
-               else if (!rc)
-                       fan_watchdog_reset();
-       }
-
-       return rc;
-}
-
-static struct ibm_struct fan_driver_data = {
-       .name = "fan",
-       .read = fan_read,
-       .write = fan_write,
-       .exit = fan_exit,
-       .suspend = fan_suspend,
-       .resume = fan_resume,
-};
-
-/****************************************************************************
- ****************************************************************************
- *
- * Infrastructure
- *
- ****************************************************************************
- ****************************************************************************/
-
-/* sysfs name ---------------------------------------------------------- */
-static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
-                          struct device_attribute *attr,
-                          char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
-}
-
-static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
-       __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
-
-/* --------------------------------------------------------------------- */
-
-/* /proc support */
-static struct proc_dir_entry *proc_dir;
-
-/*
- * Module and infrastructure proble, init and exit handling
- */
-
-static int force_load;
-
-#ifdef CONFIG_THINKPAD_ACPI_DEBUG
-static const char * __init str_supported(int is_supported)
-{
-       static char text_unsupported[] __initdata = "not supported";
-
-       return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
-}
-#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
-
-static void ibm_exit(struct ibm_struct *ibm)
-{
-       dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
-
-       list_del_init(&ibm->all_drivers);
-
-       if (ibm->flags.acpi_notify_installed) {
-               dbg_printk(TPACPI_DBG_EXIT,
-                       "%s: acpi_remove_notify_handler\n", ibm->name);
-               BUG_ON(!ibm->acpi);
-               acpi_remove_notify_handler(*ibm->acpi->handle,
-                                          ibm->acpi->type,
-                                          dispatch_acpi_notify);
-               ibm->flags.acpi_notify_installed = 0;
-               ibm->flags.acpi_notify_installed = 0;
-       }
-
-       if (ibm->flags.proc_created) {
-               dbg_printk(TPACPI_DBG_EXIT,
-                       "%s: remove_proc_entry\n", ibm->name);
-               remove_proc_entry(ibm->name, proc_dir);
-               ibm->flags.proc_created = 0;
-       }
-
-       if (ibm->flags.acpi_driver_registered) {
-               dbg_printk(TPACPI_DBG_EXIT,
-                       "%s: acpi_bus_unregister_driver\n", ibm->name);
-               BUG_ON(!ibm->acpi);
-               acpi_bus_unregister_driver(ibm->acpi->driver);
-               kfree(ibm->acpi->driver);
-               ibm->acpi->driver = NULL;
-               ibm->flags.acpi_driver_registered = 0;
-       }
-
-       if (ibm->flags.init_called && ibm->exit) {
-               ibm->exit();
-               ibm->flags.init_called = 0;
-       }
-
-       dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
-}
-
-static int __init ibm_init(struct ibm_init_struct *iibm)
-{
-       int ret;
-       struct ibm_struct *ibm = iibm->data;
-       struct proc_dir_entry *entry;
-
-       BUG_ON(ibm == NULL);
-
-       INIT_LIST_HEAD(&ibm->all_drivers);
-
-       if (ibm->flags.experimental && !experimental)
-               return 0;
-
-       dbg_printk(TPACPI_DBG_INIT,
-               "probing for %s\n", ibm->name);
-
-       if (iibm->init) {
-               ret = iibm->init(iibm);
-               if (ret > 0)
-                       return 0;       /* probe failed */
-               if (ret)
-                       return ret;
-
-               ibm->flags.init_called = 1;
-       }
-
-       if (ibm->acpi) {
-               if (ibm->acpi->hid) {
-                       ret = register_tpacpi_subdriver(ibm);
-                       if (ret)
-                               goto err_out;
-               }
-
-               if (ibm->acpi->notify) {
-                       ret = setup_acpi_notify(ibm);
-                       if (ret == -ENODEV) {
-                               printk(TPACPI_NOTICE "disabling subdriver %s\n",
-                                       ibm->name);
-                               ret = 0;
-                               goto err_out;
-                       }
-                       if (ret < 0)
-                               goto err_out;
-               }
-       }
-
-       dbg_printk(TPACPI_DBG_INIT,
-               "%s installed\n", ibm->name);
-
-       if (ibm->read) {
-               entry = create_proc_entry(ibm->name,
-                                         S_IFREG | S_IRUGO | S_IWUSR,
-                                         proc_dir);
-               if (!entry) {
-                       printk(TPACPI_ERR "unable to create proc entry %s\n",
-                              ibm->name);
-                       ret = -ENODEV;
-                       goto err_out;
-               }
-               entry->owner = THIS_MODULE;
-               entry->data = ibm;
-               entry->read_proc = &dispatch_procfs_read;
-               if (ibm->write)
-                       entry->write_proc = &dispatch_procfs_write;
-               ibm->flags.proc_created = 1;
-       }
-
-       list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
-
-       return 0;
-
-err_out:
-       dbg_printk(TPACPI_DBG_INIT,
-               "%s: at error exit path with result %d\n",
-               ibm->name, ret);
-
-       ibm_exit(ibm);
-       return (ret < 0)? ret : 0;
-}
-
-/* Probing */
-
-/* returns 0 - probe ok, or < 0 - probe error.
- * Probe ok doesn't mean thinkpad found.
- * On error, kfree() cleanup on tp->* is not performed, caller must do it */
-static int __must_check __init get_thinkpad_model_data(
-                                               struct thinkpad_id_data *tp)
-{
-       const struct dmi_device *dev = NULL;
-       char ec_fw_string[18];
-       char const *s;
-
-       if (!tp)
-               return -EINVAL;
-
-       memset(tp, 0, sizeof(*tp));
-
-       if (dmi_name_in_vendors("IBM"))
-               tp->vendor = PCI_VENDOR_ID_IBM;
-       else if (dmi_name_in_vendors("LENOVO"))
-               tp->vendor = PCI_VENDOR_ID_LENOVO;
-       else
-               return 0;
-
-       s = dmi_get_system_info(DMI_BIOS_VERSION);
-       tp->bios_version_str = kstrdup(s, GFP_KERNEL);
-       if (s && !tp->bios_version_str)
-               return -ENOMEM;
-       if (!tp->bios_version_str)
-               return 0;
-       tp->bios_model = tp->bios_version_str[0]
-                        | (tp->bios_version_str[1] << 8);
-
-       /*
-        * ThinkPad T23 or newer, A31 or newer, R50e or newer,
-        * X32 or newer, all Z series;  Some models must have an
-        * up-to-date BIOS or they will not be detected.
-        *
-        * See http://thinkwiki.org/wiki/List_of_DMI_IDs
-        */
-       while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
-               if (sscanf(dev->name,
-                          "IBM ThinkPad Embedded Controller -[%17c",
-                          ec_fw_string) == 1) {
-                       ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
-                       ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
-
-                       tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
-                       if (!tp->ec_version_str)
-                               return -ENOMEM;
-                       tp->ec_model = ec_fw_string[0]
-                                       | (ec_fw_string[1] << 8);
-                       break;
-               }
-       }
-
-       s = dmi_get_system_info(DMI_PRODUCT_VERSION);
-       if (s && !strnicmp(s, "ThinkPad", 8)) {
-               tp->model_str = kstrdup(s, GFP_KERNEL);
-               if (!tp->model_str)
-                       return -ENOMEM;
-       }
-
-       s = dmi_get_system_info(DMI_PRODUCT_NAME);
-       tp->nummodel_str = kstrdup(s, GFP_KERNEL);
-       if (s && !tp->nummodel_str)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static int __init probe_for_thinkpad(void)
-{
-       int is_thinkpad;
-
-       if (acpi_disabled)
-               return -ENODEV;
-
-       /*
-        * Non-ancient models have better DMI tagging, but very old models
-        * don't.
-        */
-       is_thinkpad = (thinkpad_id.model_str != NULL);
-
-       /* ec is required because many other handles are relative to it */
-       TPACPI_ACPIHANDLE_INIT(ec);
-       if (!ec_handle) {
-               if (is_thinkpad)
-                       printk(TPACPI_ERR
-                               "Not yet supported ThinkPad detected!\n");
-               return -ENODEV;
-       }
-
-       /*
-        * Risks a regression on very old machines, but reduces potential
-        * false positives a damn great deal
-        */
-       if (!is_thinkpad)
-               is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
-
-       if (!is_thinkpad && !force_load)
-               return -ENODEV;
-
-       return 0;
-}
-
-
-/* Module init, exit, parameters */
-
-static struct ibm_init_struct ibms_init[] __initdata = {
-       {
-               .init = thinkpad_acpi_driver_init,
-               .data = &thinkpad_acpi_driver_data,
-       },
-       {
-               .init = hotkey_init,
-               .data = &hotkey_driver_data,
-       },
-       {
-               .init = bluetooth_init,
-               .data = &bluetooth_driver_data,
-       },
-       {
-               .init = wan_init,
-               .data = &wan_driver_data,
-       },
-#ifdef CONFIG_THINKPAD_ACPI_VIDEO
-       {
-               .init = video_init,
-               .data = &video_driver_data,
-       },
-#endif
-       {
-               .init = light_init,
-               .data = &light_driver_data,
-       },
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
-       {
-               .init = dock_init,
-               .data = &dock_driver_data[0],
-       },
-       {
-               .init = dock_init2,
-               .data = &dock_driver_data[1],
-       },
-#endif
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-       {
-               .init = bay_init,
-               .data = &bay_driver_data,
-       },
-#endif
-       {
-               .init = cmos_init,
-               .data = &cmos_driver_data,
-       },
-       {
-               .init = led_init,
-               .data = &led_driver_data,
-       },
-       {
-               .init = beep_init,
-               .data = &beep_driver_data,
-       },
-       {
-               .init = thermal_init,
-               .data = &thermal_driver_data,
-       },
-       {
-               .data = &ecdump_driver_data,
-       },
-       {
-               .init = brightness_init,
-               .data = &brightness_driver_data,
-       },
-       {
-               .data = &volume_driver_data,
-       },
-       {
-               .init = fan_init,
-               .data = &fan_driver_data,
-       },
-};
-
-static int __init set_ibm_param(const char *val, struct kernel_param *kp)
-{
-       unsigned int i;
-       struct ibm_struct *ibm;
-
-       if (!kp || !kp->name || !val)
-               return -EINVAL;
-
-       for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
-               ibm = ibms_init[i].data;
-               WARN_ON(ibm == NULL);
-
-               if (!ibm || !ibm->name)
-                       continue;
-
-               if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
-                       if (strlen(val) > sizeof(ibms_init[i].param) - 2)
-                               return -ENOSPC;
-                       strcpy(ibms_init[i].param, val);
-                       strcat(ibms_init[i].param, ",");
-                       return 0;
-               }
-       }
-
-       return -EINVAL;
-}
-
-module_param(experimental, int, 0);
-MODULE_PARM_DESC(experimental,
-                "Enables experimental features when non-zero");
-
-module_param_named(debug, dbg_level, uint, 0);
-MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
-
-module_param(force_load, bool, 0);
-MODULE_PARM_DESC(force_load,
-                "Attempts to load the driver even on a "
-                "mis-identified ThinkPad when true");
-
-module_param_named(fan_control, fan_control_allowed, bool, 0);
-MODULE_PARM_DESC(fan_control,
-                "Enables setting fan parameters features when true");
-
-module_param_named(brightness_mode, brightness_mode, int, 0);
-MODULE_PARM_DESC(brightness_mode,
-                "Selects brightness control strategy: "
-                "0=auto, 1=EC, 2=CMOS, 3=both");
-
-module_param(brightness_enable, uint, 0);
-MODULE_PARM_DESC(brightness_enable,
-                "Enables backlight control when 1, disables when 0");
-
-module_param(hotkey_report_mode, uint, 0);
-MODULE_PARM_DESC(hotkey_report_mode,
-                "used for backwards compatibility with userspace, "
-                "see documentation");
-
-#define TPACPI_PARAM(feature) \
-       module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
-       MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
-                        "at module load, see documentation")
-
-TPACPI_PARAM(hotkey);
-TPACPI_PARAM(bluetooth);
-TPACPI_PARAM(video);
-TPACPI_PARAM(light);
-#ifdef CONFIG_THINKPAD_ACPI_DOCK
-TPACPI_PARAM(dock);
-#endif
-#ifdef CONFIG_THINKPAD_ACPI_BAY
-TPACPI_PARAM(bay);
-#endif /* CONFIG_THINKPAD_ACPI_BAY */
-TPACPI_PARAM(cmos);
-TPACPI_PARAM(led);
-TPACPI_PARAM(beep);
-TPACPI_PARAM(ecdump);
-TPACPI_PARAM(brightness);
-TPACPI_PARAM(volume);
-TPACPI_PARAM(fan);
-
-static void thinkpad_acpi_module_exit(void)
-{
-       struct ibm_struct *ibm, *itmp;
-
-       tpacpi_lifecycle = TPACPI_LIFE_EXITING;
-
-       list_for_each_entry_safe_reverse(ibm, itmp,
-                                        &tpacpi_all_drivers,
-                                        all_drivers) {
-               ibm_exit(ibm);
-       }
-
-       dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
-
-       if (tpacpi_inputdev) {
-               if (tp_features.input_device_registered)
-                       input_unregister_device(tpacpi_inputdev);
-               else
-                       input_free_device(tpacpi_inputdev);
-       }
-
-       if (tpacpi_hwmon)
-               hwmon_device_unregister(tpacpi_hwmon);
-
-       if (tp_features.sensors_pdev_attrs_registered)
-               device_remove_file(&tpacpi_sensors_pdev->dev,
-                                  &dev_attr_thinkpad_acpi_pdev_name);
-       if (tpacpi_sensors_pdev)
-               platform_device_unregister(tpacpi_sensors_pdev);
-       if (tpacpi_pdev)
-               platform_device_unregister(tpacpi_pdev);
-
-       if (tp_features.sensors_pdrv_attrs_registered)
-               tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
-       if (tp_features.platform_drv_attrs_registered)
-               tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
-
-       if (tp_features.sensors_pdrv_registered)
-               platform_driver_unregister(&tpacpi_hwmon_pdriver);
-
-       if (tp_features.platform_drv_registered)
-               platform_driver_unregister(&tpacpi_pdriver);
-
-       if (proc_dir)
-               remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
-
-       if (tpacpi_wq)
-               destroy_workqueue(tpacpi_wq);
-
-       kfree(thinkpad_id.bios_version_str);
-       kfree(thinkpad_id.ec_version_str);
-       kfree(thinkpad_id.model_str);
-}
-
-
-static int __init thinkpad_acpi_module_init(void)
-{
-       int ret, i;
-
-       tpacpi_lifecycle = TPACPI_LIFE_INIT;
-
-       /* Parameter checking */
-       if (hotkey_report_mode > 2)
-               return -EINVAL;
-
-       /* Driver-level probe */
-
-       ret = get_thinkpad_model_data(&thinkpad_id);
-       if (ret) {
-               printk(TPACPI_ERR
-                       "unable to get DMI data: %d\n", ret);
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       ret = probe_for_thinkpad();
-       if (ret) {
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-
-       /* Driver initialization */
-
-       TPACPI_ACPIHANDLE_INIT(ecrd);
-       TPACPI_ACPIHANDLE_INIT(ecwr);
-
-       tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
-       if (!tpacpi_wq) {
-               thinkpad_acpi_module_exit();
-               return -ENOMEM;
-       }
-
-       proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
-       if (!proc_dir) {
-               printk(TPACPI_ERR
-                      "unable to create proc dir " TPACPI_PROC_DIR);
-               thinkpad_acpi_module_exit();
-               return -ENODEV;
-       }
-       proc_dir->owner = THIS_MODULE;
-
-       ret = platform_driver_register(&tpacpi_pdriver);
-       if (ret) {
-               printk(TPACPI_ERR
-                      "unable to register main platform driver\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tp_features.platform_drv_registered = 1;
-
-       ret = platform_driver_register(&tpacpi_hwmon_pdriver);
-       if (ret) {
-               printk(TPACPI_ERR
-                      "unable to register hwmon platform driver\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tp_features.sensors_pdrv_registered = 1;
-
-       ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
-       if (!ret) {
-               tp_features.platform_drv_attrs_registered = 1;
-               ret = tpacpi_create_driver_attributes(
-                                       &tpacpi_hwmon_pdriver.driver);
-       }
-       if (ret) {
-               printk(TPACPI_ERR
-                      "unable to create sysfs driver attributes\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tp_features.sensors_pdrv_attrs_registered = 1;
-
-
-       /* Device initialization */
-       tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
-                                                       NULL, 0);
-       if (IS_ERR(tpacpi_pdev)) {
-               ret = PTR_ERR(tpacpi_pdev);
-               tpacpi_pdev = NULL;
-               printk(TPACPI_ERR "unable to register platform device\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tpacpi_sensors_pdev = platform_device_register_simple(
-                                               TPACPI_HWMON_DRVR_NAME,
-                                               -1, NULL, 0);
-       if (IS_ERR(tpacpi_sensors_pdev)) {
-               ret = PTR_ERR(tpacpi_sensors_pdev);
-               tpacpi_sensors_pdev = NULL;
-               printk(TPACPI_ERR
-                      "unable to register hwmon platform device\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       ret = device_create_file(&tpacpi_sensors_pdev->dev,
-                                &dev_attr_thinkpad_acpi_pdev_name);
-       if (ret) {
-               printk(TPACPI_ERR
-                      "unable to create sysfs hwmon device attributes\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       tp_features.sensors_pdev_attrs_registered = 1;
-       tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
-       if (IS_ERR(tpacpi_hwmon)) {
-               ret = PTR_ERR(tpacpi_hwmon);
-               tpacpi_hwmon = NULL;
-               printk(TPACPI_ERR "unable to register hwmon device\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       }
-       mutex_init(&tpacpi_inputdev_send_mutex);
-       tpacpi_inputdev = input_allocate_device();
-       if (!tpacpi_inputdev) {
-               printk(TPACPI_ERR "unable to allocate input device\n");
-               thinkpad_acpi_module_exit();
-               return -ENOMEM;
-       } else {
-               /* Prepare input device, but don't register */
-               tpacpi_inputdev->name = "ThinkPad Extra Buttons";
-               tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
-               tpacpi_inputdev->id.bustype = BUS_HOST;
-               tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
-                                               thinkpad_id.vendor :
-                                               PCI_VENDOR_ID_IBM;
-               tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
-               tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
-       }
-       for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
-               ret = ibm_init(&ibms_init[i]);
-               if (ret >= 0 && *ibms_init[i].param)
-                       ret = ibms_init[i].data->write(ibms_init[i].param);
-               if (ret < 0) {
-                       thinkpad_acpi_module_exit();
-                       return ret;
-               }
-       }
-       ret = input_register_device(tpacpi_inputdev);
-       if (ret < 0) {
-               printk(TPACPI_ERR "unable to register input device\n");
-               thinkpad_acpi_module_exit();
-               return ret;
-       } else {
-               tp_features.input_device_registered = 1;
-       }
-
-       tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
-       return 0;
-}
-
-/* Please remove this in year 2009 */
-MODULE_ALIAS("ibm_acpi");
-
-MODULE_ALIAS(TPACPI_DRVR_SHORTNAME);
-
-/*
- * DMI matching for module autoloading
- *
- * See http://thinkwiki.org/wiki/List_of_DMI_IDs
- * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
- *
- * Only models listed in thinkwiki will be supported, so add yours
- * if it is not there yet.
- */
-#define IBM_BIOS_MODULE_ALIAS(__type) \
-       MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
-
-/* Non-ancient thinkpads */
-MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");
-MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");
-
-/* Ancient thinkpad BIOSes have to be identified by
- * BIOS type or model number, and there are far less
- * BIOS types than model numbers... */
-IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");
-IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");
-IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
-
-MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
-MODULE_DESCRIPTION(TPACPI_DESC);
-MODULE_VERSION(TPACPI_VERSION);
-MODULE_LICENSE("GPL");
-
-module_init(thinkpad_acpi_module_init);
-module_exit(thinkpad_acpi_module_exit);
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
new file mode 100644 (file)
index 0000000..9652c3f
--- /dev/null
@@ -0,0 +1,5 @@
+# drivers/platform/Kconfig
+
+if X86
+source "drivers/platform/x86/Kconfig"
+endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
new file mode 100644 (file)
index 0000000..782953a
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for linux/drivers/platform
+#
+
+obj-$(CONFIG_X86)              += x86/
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
new file mode 100644 (file)
index 0000000..0a9a5b9
--- /dev/null
@@ -0,0 +1,290 @@
+#
+# X86 Platform Specific Drivers
+#
+
+menuconfig X86_PLATFORM_DEVICES
+       bool "X86 Platform Specific Device Drivers"
+       default y
+       ---help---
+         Say Y here to get to see options for device drivers for various
+         x86 platforms, including vendor-specific laptop extension drivers.
+         This option alone does not add any kernel code.
+
+         If you say N, all options in this submenu will be skipped and disabled.
+
+if X86_PLATFORM_DEVICES
+
+config ACER_WMI
+       tristate "Acer WMI Laptop Extras (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       depends on ACPI
+       depends on LEDS_CLASS
+       depends on NEW_LEDS
+       depends on BACKLIGHT_CLASS_DEVICE
+       depends on SERIO_I8042
+       depends on RFKILL
+       select ACPI_WMI
+       ---help---
+         This is a driver for newer Acer (and Wistron) laptops. It adds
+         wireless radio and bluetooth control, and on some laptops,
+         exposes the mail LED and LCD backlight.
+
+         For more information about this driver see
+         <file:Documentation/laptops/acer-wmi.txt>
+
+         If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
+         here.
+
+config ASUS_LAPTOP
+       tristate "Asus Laptop Extras (EXPERIMENTAL)"
+       depends on ACPI
+       depends on EXPERIMENTAL && !ACPI_ASUS
+       depends on LEDS_CLASS
+       depends on NEW_LEDS
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This is the new Linux driver for Asus laptops. It may also support some
+         MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
+         standard ACPI events that go through /proc/acpi/events. It also adds
+         support for video output switching, LCD backlight control, Bluetooth and
+         Wlan control, and most importantly, allows you to blink those fancy LEDs.
+
+         For more information and a userspace daemon for handling the extra
+         buttons see <http://acpi4asus.sf.net/>.
+
+         If you have an ACPI-compatible ASUS laptop, say Y or M here.
+
+config FUJITSU_LAPTOP
+       tristate "Fujitsu Laptop Extras"
+       depends on ACPI
+       depends on INPUT
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This is a driver for laptops built by Fujitsu:
+
+           * P2xxx/P5xxx/S6xxx/S7xxx series Lifebooks
+           * Possibly other Fujitsu laptop models
+           * Tested with S6410 and S7020
+
+         It adds support for LCD brightness control and some hotkeys.
+
+         If you have a Fujitsu laptop, say Y or M here.
+
+config FUJITSU_LAPTOP_DEBUG
+       bool "Verbose debug mode for Fujitsu Laptop Extras"
+       depends on FUJITSU_LAPTOP
+       default n
+       ---help---
+         Enables extra debug output from the fujitsu extras driver, at the
+         expense of a slight increase in driver size.
+
+         If you are not sure, say N here.
+
+config TC1100_WMI
+       tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
+       depends on !X86_64
+       depends on EXPERIMENTAL
+       depends on ACPI
+       select ACPI_WMI
+       ---help---
+         This is a driver for the WMI extensions (wireless and bluetooth power
+         control) of the HP Compaq TC1100 tablet.
+
+config HP_WMI
+       tristate "HP WMI extras"
+       depends on ACPI_WMI
+       depends on INPUT
+       depends on RFKILL
+       help
+        Say Y here if you want to support WMI-based hotkeys on HP laptops and
+        to read data from WMI such as docking or ambient light sensor state.
+
+        To compile this driver as a module, choose M here: the module will
+        be called hp-wmi.
+
+config MSI_LAPTOP
+       tristate "MSI Laptop Extras"
+       depends on ACPI
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This is a driver for laptops built by MSI (MICRO-STAR
+         INTERNATIONAL):
+
+         MSI MegaBook S270 (MS-1013)
+         Cytron/TCM/Medion/Tchibo MD96100/SAM2000
+
+         It adds support for Bluetooth, WLAN and LCD brightness control.
+
+         More information about this driver is available at
+         <http://0pointer.de/lennart/tchibo.html>.
+
+         If you have an MSI S270 laptop, say Y or M here.
+
+config PANASONIC_LAPTOP
+       tristate "Panasonic Laptop Extras"
+       depends on INPUT && ACPI
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This driver adds support for access to backlight control and hotkeys
+         on Panasonic Let's Note laptops.
+
+         If you have a Panasonic Let's note laptop (such as the R1(N variant),
+         R2, R3, R5, T2, W2 and Y2 series), say Y.
+
+config COMPAL_LAPTOP
+       tristate "Compal Laptop Extras"
+       depends on ACPI
+       depends on BACKLIGHT_CLASS_DEVICE
+       ---help---
+         This is a driver for laptops built by Compal:
+
+         Compal FL90/IFL90
+         Compal FL91/IFL91
+         Compal FL92/JFL92
+         Compal FT00/IFT00
+
+         It adds support for Bluetooth, WLAN and LCD brightness control.
+
+         If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
+
+config SONY_LAPTOP
+       tristate "Sony Laptop Extras"
+       depends on ACPI
+       select BACKLIGHT_CLASS_DEVICE
+       depends on INPUT
+         ---help---
+         This mini-driver drives the SNC and SPIC devices present in the ACPI
+         BIOS of the Sony Vaio laptops.
+
+         It gives access to some extra laptop functionalities like Bluetooth,
+         screen brightness control, Fn keys and allows powering on/off some
+         devices.
+
+         Read <file:Documentation/laptops/sony-laptop.txt> for more information.
+
+config SONYPI_COMPAT
+       bool "Sonypi compatibility"
+       depends on SONY_LAPTOP
+         ---help---
+         Build the sonypi driver compatibility code into the sony-laptop driver.
+
+config THINKPAD_ACPI
+       tristate "ThinkPad ACPI Laptop Extras"
+       depends on ACPI
+       select BACKLIGHT_LCD_SUPPORT
+       select BACKLIGHT_CLASS_DEVICE
+       select HWMON
+       select NVRAM
+       select INPUT
+       select NEW_LEDS
+       select LEDS_CLASS
+       select NET
+       select RFKILL
+       ---help---
+         This is a driver for the IBM and Lenovo ThinkPad laptops. It adds
+         support for Fn-Fx key combinations, Bluetooth control, video
+         output switching, ThinkLight control, UltraBay eject and more.
+         For more information about this driver see
+         <file:Documentation/laptops/thinkpad-acpi.txt> and
+         <http://ibm-acpi.sf.net/> .
+
+         This driver was formerly known as ibm-acpi.
+
+         If you have an IBM or Lenovo ThinkPad laptop, say Y or M here.
+
+config THINKPAD_ACPI_DEBUG
+       bool "Verbose debug mode"
+       depends on THINKPAD_ACPI
+       default n
+       ---help---
+         Enables extra debugging information, at the expense of a slightly
+         increase in driver size.
+
+         If you are not sure, say N here.
+
+config THINKPAD_ACPI_DOCK
+       bool "Legacy Docking Station Support"
+       depends on THINKPAD_ACPI
+       depends on ACPI_DOCK=n
+       default n
+       ---help---
+         Allows the thinkpad_acpi driver to handle docking station events.
+         This support was made obsolete by the generic ACPI docking station
+         support (CONFIG_ACPI_DOCK).  It will allow locking and removing the
+         laptop from the docking station, but will not properly connect PCI
+         devices.
+
+         If you are not sure, say N here.
+
+config THINKPAD_ACPI_BAY
+       bool "Legacy Removable Bay Support"
+       depends on THINKPAD_ACPI
+       default y
+       ---help---
+         Allows the thinkpad_acpi driver to handle removable bays.  It will
+         electrically disable the device in the bay, and also generate
+         notifications when the bay lever is ejected or inserted.
+
+         If you are not sure, say Y here.
+
+config THINKPAD_ACPI_VIDEO
+       bool "Video output control support"
+       depends on THINKPAD_ACPI
+       default y
+       ---help---
+         Allows the thinkpad_acpi driver to provide an interface to control
+         the various video output ports.
+
+         This feature often won't work well, depending on ThinkPad model,
+         display state, video output devices in use, whether there is a X
+         server running, phase of the moon, and the current mood of
+         Schroedinger's cat.  If you can use X.org's RandR to control
+         your ThinkPad's video output ports instead of this feature,
+         don't think twice: do it and say N here to save some memory.
+
+         If you are not sure, say Y here.
+
+config THINKPAD_ACPI_HOTKEY_POLL
+       bool "Support NVRAM polling for hot keys"
+       depends on THINKPAD_ACPI
+       default y
+       ---help---
+         Some thinkpad models benefit from NVRAM polling to detect a few of
+         the hot key press events.  If you know your ThinkPad model does not
+         need to do NVRAM polling to support any of the hot keys you use,
+         unselecting this option will save about 1kB of memory.
+
+         ThinkPads T40 and newer, R52 and newer, and X31 and newer are
+         unlikely to need NVRAM polling in their latest BIOS versions.
+
+         NVRAM polling can detect at most the following keys: ThinkPad/Access
+         IBM, Zoom, Switch Display (fn+F7), ThinkLight, Volume up/down/mute,
+         Brightness up/down, Display Expand (fn+F8), Hibernate (fn+F12).
+
+         If you are not sure, say Y here.  The driver enables polling only if
+         it is strictly necessary to do so.
+
+config INTEL_MENLOW
+       tristate "Thermal Management driver for Intel menlow platform"
+       depends on ACPI_THERMAL
+       select THERMAL
+       ---help---
+         ACPI thermal management enhancement driver on
+         Intel Menlow platform.
+
+         If unsure, say N.
+
+config EEEPC_LAPTOP
+       tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
+       depends on ACPI
+       depends on BACKLIGHT_CLASS_DEVICE
+       depends on HWMON
+       depends on EXPERIMENTAL
+       depends on RFKILL
+       ---help---
+         This driver supports the Fn-Fx keys on Eee PC laptops.
+         It also adds the ability to switch camera/wlan on/off.
+
+         If you have an Eee PC laptop, say Y or M here.
+
+endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
new file mode 100644 (file)
index 0000000..4d26b1b
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# Makefile for linux/drivers/platform/x86
+# x86 Platform-Specific Drivers
+#
+obj-$(CONFIG_ASUS_LAPTOP)      += asus-laptop.o
+obj-$(CONFIG_EEEPC_LAPTOP)     += eeepc-laptop.o
+obj-$(CONFIG_MSI_LAPTOP)       += msi-laptop.o
+obj-$(CONFIG_COMPAL_LAPTOP)    += compal-laptop.o
+obj-$(CONFIG_ACER_WMI)         += acer-wmi.o
+obj-$(CONFIG_HP_WMI)           += hp-wmi.o
+obj-$(CONFIG_TC1100_WMI)       += tc1100-wmi.o
+obj-$(CONFIG_SONY_LAPTOP)      += sony-laptop.o
+obj-$(CONFIG_THINKPAD_ACPI)    += thinkpad_acpi.o
+obj-$(CONFIG_FUJITSU_LAPTOP)   += fujitsu-laptop.o
+obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
+obj-$(CONFIG_INTEL_MENLOW)     += intel_menlow.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
new file mode 100644 (file)
index 0000000..94c9f91
--- /dev/null
@@ -0,0 +1,1345 @@
+/*
+ *  Acer WMI Laptop Extras
+ *
+ *  Copyright (C) 2007-2008    Carlos Corbacho <carlos@strangeworlds.co.uk>
+ *
+ *  Based on acer_acpi:
+ *    Copyright (C) 2005-2007  E.M. Smith
+ *    Copyright (C) 2007-2008  Carlos Corbacho <cathectic@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/i8042.h>
+#include <linux/rfkill.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Carlos Corbacho");
+MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
+MODULE_LICENSE("GPL");
+
+#define ACER_LOGPREFIX "acer-wmi: "
+#define ACER_ERR KERN_ERR ACER_LOGPREFIX
+#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
+#define ACER_INFO KERN_INFO ACER_LOGPREFIX
+
+/*
+ * The following defines quirks to get some specific functions to work
+ * which are known to not be supported over ACPI-WMI (such as the mail LED
+ * on WMID based Acer's)
+ */
+struct acer_quirks {
+       const char *vendor;
+       const char *model;
+       u16 quirks;
+};
+
+/*
+ * Magic Number
+ * Meaning is unknown - this number is required for writing to ACPI for AMW0
+ * (it's also used in acerhk when directly accessing the BIOS)
+ */
+#define ACER_AMW0_WRITE        0x9610
+
+/*
+ * Bit masks for the AMW0 interface
+ */
+#define ACER_AMW0_WIRELESS_MASK  0x35
+#define ACER_AMW0_BLUETOOTH_MASK 0x34
+#define ACER_AMW0_MAILLED_MASK   0x31
+
+/*
+ * Method IDs for WMID interface
+ */
+#define ACER_WMID_GET_WIRELESS_METHODID                1
+#define ACER_WMID_GET_BLUETOOTH_METHODID       2
+#define ACER_WMID_GET_BRIGHTNESS_METHODID      3
+#define ACER_WMID_SET_WIRELESS_METHODID                4
+#define ACER_WMID_SET_BLUETOOTH_METHODID       5
+#define ACER_WMID_SET_BRIGHTNESS_METHODID      6
+#define ACER_WMID_GET_THREEG_METHODID          10
+#define ACER_WMID_SET_THREEG_METHODID          11
+
+/*
+ * Acer ACPI method GUIDs
+ */
+#define AMW0_GUID1             "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
+#define AMW0_GUID2             "431F16ED-0C2B-444C-B267-27DEB140CF9C"
+#define WMID_GUID1             "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
+#define WMID_GUID2             "95764E09-FB56-4e83-B31A-37761F60994A"
+
+MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
+MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
+
+/* Temporary workaround until the WMI sysfs interface goes in */
+MODULE_ALIAS("dmi:*:*Acer*:*:");
+
+/*
+ * Interface capability flags
+ */
+#define ACER_CAP_MAILLED               (1<<0)
+#define ACER_CAP_WIRELESS              (1<<1)
+#define ACER_CAP_BLUETOOTH             (1<<2)
+#define ACER_CAP_BRIGHTNESS            (1<<3)
+#define ACER_CAP_THREEG                        (1<<4)
+#define ACER_CAP_ANY                   (0xFFFFFFFF)
+
+/*
+ * Interface type flags
+ */
+enum interface_flags {
+       ACER_AMW0,
+       ACER_AMW0_V2,
+       ACER_WMID,
+};
+
+#define ACER_DEFAULT_WIRELESS  0
+#define ACER_DEFAULT_BLUETOOTH 0
+#define ACER_DEFAULT_MAILLED   0
+#define ACER_DEFAULT_THREEG    0
+
+static int max_brightness = 0xF;
+
+static int mailled = -1;
+static int brightness = -1;
+static int threeg = -1;
+static int force_series;
+
+module_param(mailled, int, 0444);
+module_param(brightness, int, 0444);
+module_param(threeg, int, 0444);
+module_param(force_series, int, 0444);
+MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
+MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
+MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
+MODULE_PARM_DESC(force_series, "Force a different laptop series");
+
+struct acer_data {
+       int mailled;
+       int threeg;
+       int brightness;
+};
+
+struct acer_debug {
+       struct dentry *root;
+       struct dentry *devices;
+       u32 wmid_devices;
+};
+
+static struct rfkill *wireless_rfkill;
+static struct rfkill *bluetooth_rfkill;
+
+/* Each low-level interface must define at least some of the following */
+struct wmi_interface {
+       /* The WMI device type */
+       u32 type;
+
+       /* The capabilities this interface provides */
+       u32 capability;
+
+       /* Private data for the current interface */
+       struct acer_data data;
+
+       /* debugfs entries associated with this interface */
+       struct acer_debug debug;
+};
+
+/* The static interface pointer, points to the currently detected interface */
+static struct wmi_interface *interface;
+
+/*
+ * Embedded Controller quirks
+ * Some laptops require us to directly access the EC to either enable or query
+ * features that are not available through WMI.
+ */
+
+struct quirk_entry {
+       u8 wireless;
+       u8 mailled;
+       s8 brightness;
+       u8 bluetooth;
+};
+
+static struct quirk_entry *quirks;
+
+static void set_quirks(void)
+{
+       if (!interface)
+               return;
+
+       if (quirks->mailled)
+               interface->capability |= ACER_CAP_MAILLED;
+
+       if (quirks->brightness)
+               interface->capability |= ACER_CAP_BRIGHTNESS;
+}
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+       quirks = dmi->driver_data;
+       return 0;
+}
+
+static struct quirk_entry quirk_unknown = {
+};
+
+static struct quirk_entry quirk_acer_aspire_1520 = {
+       .brightness = -1,
+};
+
+static struct quirk_entry quirk_acer_travelmate_2490 = {
+       .mailled = 1,
+};
+
+/* This AMW0 laptop has no bluetooth */
+static struct quirk_entry quirk_medion_md_98300 = {
+       .wireless = 1,
+};
+
+static struct quirk_entry quirk_fujitsu_amilo_li_1718 = {
+       .wireless = 2,
+};
+
+static struct dmi_system_id acer_quirks[] = {
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 1360",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
+               },
+               .driver_data = &quirk_acer_aspire_1520,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 1520",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
+               },
+               .driver_data = &quirk_acer_aspire_1520,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 3100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 3610",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5610",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5630",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5650",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 5680",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer Aspire 9110",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer TravelMate 2490",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Acer TravelMate 4200",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
+               },
+               .driver_data = &quirk_acer_travelmate_2490,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Fujitsu Siemens Amilo Li 1718",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Li 1718"),
+               },
+               .driver_data = &quirk_fujitsu_amilo_li_1718,
+       },
+       {
+               .callback = dmi_matched,
+               .ident = "Medion MD 98300",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
+               },
+               .driver_data = &quirk_medion_md_98300,
+       },
+       {}
+};
+
+/* Find which quirks are needed for a particular vendor/ model pair */
+static void find_quirks(void)
+{
+       if (!force_series) {
+               dmi_check_system(acer_quirks);
+       } else if (force_series == 2490) {
+               quirks = &quirk_acer_travelmate_2490;
+       }
+
+       if (quirks == NULL)
+               quirks = &quirk_unknown;
+
+       set_quirks();
+}
+
+/*
+ * General interface convenience methods
+ */
+
+static bool has_cap(u32 cap)
+{
+       if ((interface->capability & cap) != 0)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * AMW0 (V1) interface
+ */
+struct wmab_args {
+       u32 eax;
+       u32 ebx;
+       u32 ecx;
+       u32 edx;
+};
+
+struct wmab_ret {
+       u32 eax;
+       u32 ebx;
+       u32 ecx;
+       u32 edx;
+       u32 eex;
+};
+
+static acpi_status wmab_execute(struct wmab_args *regbuf,
+struct acpi_buffer *result)
+{
+       struct acpi_buffer input;
+       acpi_status status;
+       input.length = sizeof(struct wmab_args);
+       input.pointer = (u8 *)regbuf;
+
+       status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
+
+       return status;
+}
+
+static acpi_status AMW0_get_u32(u32 *value, u32 cap,
+struct wmi_interface *iface)
+{
+       int err;
+       u8 result;
+
+       switch (cap) {
+       case ACER_CAP_MAILLED:
+               switch (quirks->mailled) {
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 7) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_WIRELESS:
+               switch (quirks->wireless) {
+               case 1:
+                       err = ec_read(0x7B, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result & 0x1;
+                       return AE_OK;
+               case 2:
+                       err = ec_read(0x71, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result & 0x1;
+                       return AE_OK;
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 2) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_BLUETOOTH:
+               switch (quirks->bluetooth) {
+               default:
+                       err = ec_read(0xA, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = (result >> 4) & 0x1;
+                       return AE_OK;
+               }
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               switch (quirks->brightness) {
+               default:
+                       err = ec_read(0x83, &result);
+                       if (err)
+                               return AE_ERROR;
+                       *value = result;
+                       return AE_OK;
+               }
+               break;
+       default:
+               return AE_ERROR;
+       }
+       return AE_OK;
+}
+
+static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+{
+       struct wmab_args args;
+
+       args.eax = ACER_AMW0_WRITE;
+       args.ebx = value ? (1<<8) : 0;
+       args.ecx = args.edx = 0;
+
+       switch (cap) {
+       case ACER_CAP_MAILLED:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_MAILLED_MASK;
+               break;
+       case ACER_CAP_WIRELESS:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_WIRELESS_MASK;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               if (value > max_brightness)
+                       return AE_BAD_PARAMETER;
+               switch (quirks->brightness) {
+               default:
+                       return ec_write(0x83, value);
+                       break;
+               }
+       default:
+               return AE_ERROR;
+       }
+
+       /* Actually do the set */
+       return wmab_execute(&args, NULL);
+}
+
+static acpi_status AMW0_find_mailled(void)
+{
+       struct wmab_args args;
+       struct wmab_ret ret;
+       acpi_status status = AE_OK;
+       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+
+       args.eax = 0x86;
+       args.ebx = args.ecx = args.edx = 0;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+       obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eex & 0x1)
+               interface->capability |= ACER_CAP_MAILLED;
+
+       kfree(out.pointer);
+
+       return AE_OK;
+}
+
+static acpi_status AMW0_set_capabilities(void)
+{
+       struct wmab_args args;
+       struct wmab_ret ret;
+       acpi_status status = AE_OK;
+       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+
+       /*
+        * On laptops with this strange GUID (non Acer), normal probing doesn't
+        * work.
+        */
+       if (wmi_has_guid(AMW0_GUID2)) {
+               interface->capability |= ACER_CAP_WIRELESS;
+               return AE_OK;
+       }
+
+       args.eax = ACER_AMW0_WRITE;
+       args.ecx = args.edx = 0;
+
+       args.ebx = 0xa2 << 8;
+       args.ebx |= ACER_AMW0_WIRELESS_MASK;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+       obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eax & 0x1)
+               interface->capability |= ACER_CAP_WIRELESS;
+
+       args.ebx = 2 << 8;
+       args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
+
+       status = wmab_execute(&args, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER
+       && obj->buffer.length == sizeof(struct wmab_ret)) {
+               ret = *((struct wmab_ret *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       if (ret.eax & 0x1)
+               interface->capability |= ACER_CAP_BLUETOOTH;
+
+       kfree(out.pointer);
+
+       /*
+        * This appears to be safe to enable, since all Wistron based laptops
+        * appear to use the same EC register for brightness, even if they
+        * differ for wireless, etc
+        */
+       if (quirks->brightness >= 0)
+               interface->capability |= ACER_CAP_BRIGHTNESS;
+
+       return AE_OK;
+}
+
+static struct wmi_interface AMW0_interface = {
+       .type = ACER_AMW0,
+};
+
+static struct wmi_interface AMW0_V2_interface = {
+       .type = ACER_AMW0_V2,
+};
+
+/*
+ * New interface (The WMID interface)
+ */
+static acpi_status
+WMI_execute_u32(u32 method_id, u32 in, u32 *out)
+{
+       struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
+       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       u32 tmp;
+       acpi_status status;
+
+       status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
+
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) result.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+               obj->buffer.length == sizeof(u32)) {
+               tmp = *((u32 *) obj->buffer.pointer);
+       } else {
+               tmp = 0;
+       }
+
+       if (out)
+               *out = tmp;
+
+       kfree(result.pointer);
+
+       return status;
+}
+
+static acpi_status WMID_get_u32(u32 *value, u32 cap,
+struct wmi_interface *iface)
+{
+       acpi_status status;
+       u8 tmp;
+       u32 result, method_id = 0;
+
+       switch (cap) {
+       case ACER_CAP_WIRELESS:
+               method_id = ACER_WMID_GET_WIRELESS_METHODID;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
+               break;
+       case ACER_CAP_BRIGHTNESS:
+               method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
+               break;
+       case ACER_CAP_THREEG:
+               method_id = ACER_WMID_GET_THREEG_METHODID;
+               break;
+       case ACER_CAP_MAILLED:
+               if (quirks->mailled == 1) {
+                       ec_read(0x9f, &tmp);
+                       *value = tmp & 0x1;
+                       return 0;
+               }
+       default:
+               return AE_ERROR;
+       }
+       status = WMI_execute_u32(method_id, 0, &result);
+
+       if (ACPI_SUCCESS(status))
+               *value = (u8)result;
+
+       return status;
+}
+
+static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+{
+       u32 method_id = 0;
+       char param;
+
+       switch (cap) {
+       case ACER_CAP_BRIGHTNESS:
+               if (value > max_brightness)
+                       return AE_BAD_PARAMETER;
+               method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
+               break;
+       case ACER_CAP_WIRELESS:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               method_id = ACER_WMID_SET_WIRELESS_METHODID;
+               break;
+       case ACER_CAP_BLUETOOTH:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
+               break;
+       case ACER_CAP_THREEG:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               method_id = ACER_WMID_SET_THREEG_METHODID;
+               break;
+       case ACER_CAP_MAILLED:
+               if (value > 1)
+                       return AE_BAD_PARAMETER;
+               if (quirks->mailled == 1) {
+                       param = value ? 0x92 : 0x93;
+                       i8042_command(&param, 0x1059);
+                       return 0;
+               }
+               break;
+       default:
+               return AE_ERROR;
+       }
+       return WMI_execute_u32(method_id, (u32)value, NULL);
+}
+
+static acpi_status WMID_set_capabilities(void)
+{
+       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *obj;
+       acpi_status status;
+       u32 devices;
+
+       status = wmi_query_block(WMID_GUID2, 1, &out);
+       if (ACPI_FAILURE(status))
+               return status;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+               obj->buffer.length == sizeof(u32)) {
+               devices = *((u32 *) obj->buffer.pointer);
+       } else {
+               return AE_ERROR;
+       }
+
+       /* Not sure on the meaning of the relevant bits yet to detect these */
+       interface->capability |= ACER_CAP_WIRELESS;
+       interface->capability |= ACER_CAP_THREEG;
+
+       /* WMID always provides brightness methods */
+       interface->capability |= ACER_CAP_BRIGHTNESS;
+
+       if (devices & 0x10)
+               interface->capability |= ACER_CAP_BLUETOOTH;
+
+       if (!(devices & 0x20))
+               max_brightness = 0x9;
+
+       return status;
+}
+
+static struct wmi_interface wmid_interface = {
+       .type = ACER_WMID,
+};
+
+/*
+ * Generic Device (interface-independent)
+ */
+
+static acpi_status get_u32(u32 *value, u32 cap)
+{
+       acpi_status status = AE_ERROR;
+
+       switch (interface->type) {
+       case ACER_AMW0:
+               status = AMW0_get_u32(value, cap, interface);
+               break;
+       case ACER_AMW0_V2:
+               if (cap == ACER_CAP_MAILLED) {
+                       status = AMW0_get_u32(value, cap, interface);
+                       break;
+               }
+       case ACER_WMID:
+               status = WMID_get_u32(value, cap, interface);
+               break;
+       }
+
+       return status;
+}
+
+static acpi_status set_u32(u32 value, u32 cap)
+{
+       acpi_status status;
+
+       if (interface->capability & cap) {
+               switch (interface->type) {
+               case ACER_AMW0:
+                       return AMW0_set_u32(value, cap, interface);
+               case ACER_AMW0_V2:
+                       if (cap == ACER_CAP_MAILLED)
+                               return AMW0_set_u32(value, cap, interface);
+
+                       /*
+                        * On some models, some WMID methods don't toggle
+                        * properly. For those cases, we want to run the AMW0
+                        * method afterwards to be certain we've really toggled
+                        * the device state.
+                        */
+                       if (cap == ACER_CAP_WIRELESS ||
+                               cap == ACER_CAP_BLUETOOTH) {
+                               status = WMID_set_u32(value, cap, interface);
+                               if (ACPI_FAILURE(status))
+                                       return status;
+
+                               return AMW0_set_u32(value, cap, interface);
+                       }
+               case ACER_WMID:
+                       return WMID_set_u32(value, cap, interface);
+               default:
+                       return AE_BAD_PARAMETER;
+               }
+       }
+       return AE_BAD_PARAMETER;
+}
+
+static void __init acer_commandline_init(void)
+{
+       /*
+        * These will all fail silently if the value given is invalid, or the
+        * capability isn't available on the given interface
+        */
+       set_u32(mailled, ACER_CAP_MAILLED);
+       set_u32(threeg, ACER_CAP_THREEG);
+       set_u32(brightness, ACER_CAP_BRIGHTNESS);
+}
+
+/*
+ * LED device (Mail LED only, no other LEDs known yet)
+ */
+static void mail_led_set(struct led_classdev *led_cdev,
+enum led_brightness value)
+{
+       set_u32(value, ACER_CAP_MAILLED);
+}
+
+static struct led_classdev mail_led = {
+       .name = "acer-wmi::mail",
+       .brightness_set = mail_led_set,
+};
+
+static int __devinit acer_led_init(struct device *dev)
+{
+       return led_classdev_register(dev, &mail_led);
+}
+
+static void acer_led_exit(void)
+{
+       led_classdev_unregister(&mail_led);
+}
+
+/*
+ * Backlight device
+ */
+static struct backlight_device *acer_backlight_device;
+
+static int read_brightness(struct backlight_device *bd)
+{
+       u32 value;
+       get_u32(&value, ACER_CAP_BRIGHTNESS);
+       return value;
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+       int intensity = bd->props.brightness;
+
+       if (bd->props.power != FB_BLANK_UNBLANK)
+               intensity = 0;
+       if (bd->props.fb_blank != FB_BLANK_UNBLANK)
+               intensity = 0;
+
+       set_u32(intensity, ACER_CAP_BRIGHTNESS);
+
+       return 0;
+}
+
+static struct backlight_ops acer_bl_ops = {
+       .get_brightness = read_brightness,
+       .update_status = update_bl_status,
+};
+
+static int __devinit acer_backlight_init(struct device *dev)
+{
+       struct backlight_device *bd;
+
+       bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
+       if (IS_ERR(bd)) {
+               printk(ACER_ERR "Could not register Acer backlight device\n");
+               acer_backlight_device = NULL;
+               return PTR_ERR(bd);
+       }
+
+       acer_backlight_device = bd;
+
+       bd->props.power = FB_BLANK_UNBLANK;
+       bd->props.brightness = max_brightness;
+       bd->props.max_brightness = max_brightness;
+       backlight_update_status(bd);
+       return 0;
+}
+
+static void acer_backlight_exit(void)
+{
+       backlight_device_unregister(acer_backlight_device);
+}
+
+/*
+ * Rfkill devices
+ */
+static void acer_rfkill_update(struct work_struct *ignored);
+static DECLARE_DELAYED_WORK(acer_rfkill_work, acer_rfkill_update);
+static void acer_rfkill_update(struct work_struct *ignored)
+{
+       u32 state;
+       acpi_status status;
+
+       status = get_u32(&state, ACER_CAP_WIRELESS);
+       if (ACPI_SUCCESS(status))
+               rfkill_force_state(wireless_rfkill, state ?
+                       RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED);
+
+       if (has_cap(ACER_CAP_BLUETOOTH)) {
+               status = get_u32(&state, ACER_CAP_BLUETOOTH);
+               if (ACPI_SUCCESS(status))
+                       rfkill_force_state(bluetooth_rfkill, state ?
+                               RFKILL_STATE_UNBLOCKED :
+                               RFKILL_STATE_SOFT_BLOCKED);
+       }
+
+       schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
+}
+
+static int acer_rfkill_set(void *data, enum rfkill_state state)
+{
+       acpi_status status;
+       u32 *cap = data;
+       status = set_u32((u32) (state == RFKILL_STATE_UNBLOCKED), *cap);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+       return 0;
+}
+
+static struct rfkill * acer_rfkill_register(struct device *dev,
+enum rfkill_type type, char *name, u32 cap)
+{
+       int err;
+       u32 state;
+       u32 *data;
+       struct rfkill *rfkill_dev;
+
+       rfkill_dev = rfkill_allocate(dev, type);
+       if (!rfkill_dev)
+               return ERR_PTR(-ENOMEM);
+       rfkill_dev->name = name;
+       get_u32(&state, cap);
+       rfkill_dev->state = state ? RFKILL_STATE_UNBLOCKED :
+               RFKILL_STATE_SOFT_BLOCKED;
+       data = kzalloc(sizeof(u32), GFP_KERNEL);
+       if (!data) {
+               rfkill_free(rfkill_dev);
+               return ERR_PTR(-ENOMEM);
+       }
+       *data = cap;
+       rfkill_dev->data = data;
+       rfkill_dev->toggle_radio = acer_rfkill_set;
+       rfkill_dev->user_claim_unsupported = 1;
+
+       err = rfkill_register(rfkill_dev);
+       if (err) {
+               kfree(rfkill_dev->data);
+               rfkill_free(rfkill_dev);
+               return ERR_PTR(err);
+       }
+       return rfkill_dev;
+}
+
+static int acer_rfkill_init(struct device *dev)
+{
+       wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
+               "acer-wireless", ACER_CAP_WIRELESS);
+       if (IS_ERR(wireless_rfkill))
+               return PTR_ERR(wireless_rfkill);
+
+       if (has_cap(ACER_CAP_BLUETOOTH)) {
+               bluetooth_rfkill = acer_rfkill_register(dev,
+                       RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
+                       ACER_CAP_BLUETOOTH);
+               if (IS_ERR(bluetooth_rfkill)) {
+                       kfree(wireless_rfkill->data);
+                       rfkill_unregister(wireless_rfkill);
+                       return PTR_ERR(bluetooth_rfkill);
+               }
+       }
+
+       schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
+
+       return 0;
+}
+
+static void acer_rfkill_exit(void)
+{
+       cancel_delayed_work_sync(&acer_rfkill_work);
+       kfree(wireless_rfkill->data);
+       rfkill_unregister(wireless_rfkill);
+       if (has_cap(ACER_CAP_BLUETOOTH)) {
+               kfree(wireless_rfkill->data);
+               rfkill_unregister(bluetooth_rfkill);
+       }
+       return;
+}
+
+/*
+ * sysfs interface
+ */
+static ssize_t show_bool_threeg(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       u32 result; \
+       acpi_status status = get_u32(&result, ACER_CAP_THREEG);
+       if (ACPI_SUCCESS(status))
+               return sprintf(buf, "%u\n", result);
+       return sprintf(buf, "Read error\n");
+}
+
+static ssize_t set_bool_threeg(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       u32 tmp = simple_strtoul(buf, NULL, 10);
+       acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
+               if (ACPI_FAILURE(status))
+                       return -EINVAL;
+       return count;
+}
+static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg,
+       set_bool_threeg);
+
+static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
+       char *buf)
+{
+       switch (interface->type) {
+       case ACER_AMW0:
+               return sprintf(buf, "AMW0\n");
+       case ACER_AMW0_V2:
+               return sprintf(buf, "AMW0 v2\n");
+       case ACER_WMID:
+               return sprintf(buf, "WMID\n");
+       default:
+               return sprintf(buf, "Error!\n");
+       }
+}
+
+static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
+       show_interface, NULL);
+
+/*
+ * debugfs functions
+ */
+static u32 get_wmid_devices(void)
+{
+       struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *obj;
+       acpi_status status;
+
+       status = wmi_query_block(WMID_GUID2, 1, &out);
+       if (ACPI_FAILURE(status))
+               return 0;
+
+       obj = (union acpi_object *) out.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+               obj->buffer.length == sizeof(u32)) {
+               return *((u32 *) obj->buffer.pointer);
+       } else {
+               return 0;
+       }
+}
+
+/*
+ * Platform device
+ */
+static int __devinit acer_platform_probe(struct platform_device *device)
+{
+       int err;
+
+       if (has_cap(ACER_CAP_MAILLED)) {
+               err = acer_led_init(&device->dev);
+               if (err)
+                       goto error_mailled;
+       }
+
+       if (has_cap(ACER_CAP_BRIGHTNESS)) {
+               err = acer_backlight_init(&device->dev);
+               if (err)
+                       goto error_brightness;
+       }
+
+       err = acer_rfkill_init(&device->dev);
+
+       return err;
+
+error_brightness:
+       acer_led_exit();
+error_mailled:
+       return err;
+}
+
+static int acer_platform_remove(struct platform_device *device)
+{
+       if (has_cap(ACER_CAP_MAILLED))
+               acer_led_exit();
+       if (has_cap(ACER_CAP_BRIGHTNESS))
+               acer_backlight_exit();
+
+       acer_rfkill_exit();
+       return 0;
+}
+
+static int acer_platform_suspend(struct platform_device *dev,
+pm_message_t state)
+{
+       u32 value;
+       struct acer_data *data = &interface->data;
+
+       if (!data)
+               return -ENOMEM;
+
+       if (has_cap(ACER_CAP_MAILLED)) {
+               get_u32(&value, ACER_CAP_MAILLED);
+               data->mailled = value;
+       }
+
+       if (has_cap(ACER_CAP_BRIGHTNESS)) {
+               get_u32(&value, ACER_CAP_BRIGHTNESS);
+               data->brightness = value;
+       }
+
+       return 0;
+}
+
+static int acer_platform_resume(struct platform_device *device)
+{
+       struct acer_data *data = &interface->data;
+
+       if (!data)
+               return -ENOMEM;
+
+       if (has_cap(ACER_CAP_MAILLED))
+               set_u32(data->mailled, ACER_CAP_MAILLED);
+
+       if (has_cap(ACER_CAP_BRIGHTNESS))
+               set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
+
+       return 0;
+}
+
+static struct platform_driver acer_platform_driver = {
+       .driver = {
+               .name = "acer-wmi",
+               .owner = THIS_MODULE,
+       },
+       .probe = acer_platform_probe,
+       .remove = acer_platform_remove,
+       .suspend = acer_platform_suspend,
+       .resume = acer_platform_resume,
+};
+
+static struct platform_device *acer_platform_device;
+
+static int remove_sysfs(struct platform_device *device)
+{
+       if (has_cap(ACER_CAP_THREEG))
+               device_remove_file(&device->dev, &dev_attr_threeg);
+
+       device_remove_file(&device->dev, &dev_attr_interface);
+
+       return 0;
+}
+
+static int create_sysfs(void)
+{
+       int retval = -ENOMEM;
+
+       if (has_cap(ACER_CAP_THREEG)) {
+               retval = device_create_file(&acer_platform_device->dev,
+                       &dev_attr_threeg);
+               if (retval)
+                       goto error_sysfs;
+       }
+
+       retval = device_create_file(&acer_platform_device->dev,
+               &dev_attr_interface);
+       if (retval)
+               goto error_sysfs;
+
+       return 0;
+
+error_sysfs:
+               remove_sysfs(acer_platform_device);
+       return retval;
+}
+
+static void remove_debugfs(void)
+{
+       debugfs_remove(interface->debug.devices);
+       debugfs_remove(interface->debug.root);
+}
+
+static int create_debugfs(void)
+{
+       interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
+       if (!interface->debug.root) {
+               printk(ACER_ERR "Failed to create debugfs directory");
+               return -ENOMEM;
+       }
+
+       interface->debug.devices = debugfs_create_u32("devices", S_IRUGO,
+                                       interface->debug.root,
+                                       &interface->debug.wmid_devices);
+       if (!interface->debug.devices)
+               goto error_debugfs;
+
+       return 0;
+
+error_debugfs:
+       remove_debugfs();
+       return -ENOMEM;
+}
+
+static int __init acer_wmi_init(void)
+{
+       int err;
+
+       printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
+
+       find_quirks();
+
+       /*
+        * Detect which ACPI-WMI interface we're using.
+        */
+       if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
+               interface = &AMW0_V2_interface;
+
+       if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
+               interface = &wmid_interface;
+
+       if (wmi_has_guid(WMID_GUID2) && interface) {
+               if (ACPI_FAILURE(WMID_set_capabilities())) {
+                       printk(ACER_ERR "Unable to detect available WMID "
+                                       "devices\n");
+                       return -ENODEV;
+               }
+       } else if (!wmi_has_guid(WMID_GUID2) && interface) {
+               printk(ACER_ERR "No WMID device detection method found\n");
+               return -ENODEV;
+       }
+
+       if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
+               interface = &AMW0_interface;
+
+               if (ACPI_FAILURE(AMW0_set_capabilities())) {
+                       printk(ACER_ERR "Unable to detect available AMW0 "
+                                       "devices\n");
+                       return -ENODEV;
+               }
+       }
+
+       if (wmi_has_guid(AMW0_GUID1))
+               AMW0_find_mailled();
+
+       if (!interface) {
+               printk(ACER_ERR "No or unsupported WMI interface, unable to "
+                               "load\n");
+               return -ENODEV;
+       }
+
+       set_quirks();
+
+       if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
+               interface->capability &= ~ACER_CAP_BRIGHTNESS;
+               printk(ACER_INFO "Brightness must be controlled by "
+                      "generic video driver\n");
+       }
+
+       if (platform_driver_register(&acer_platform_driver)) {
+               printk(ACER_ERR "Unable to register platform driver.\n");
+               goto error_platform_register;
+       }
+       acer_platform_device = platform_device_alloc("acer-wmi", -1);
+       platform_device_add(acer_platform_device);
+
+       err = create_sysfs();
+       if (err)
+               return err;
+
+       if (wmi_has_guid(WMID_GUID2)) {
+               interface->debug.wmid_devices = get_wmid_devices();
+               err = create_debugfs();
+               if (err)
+                       return err;
+       }
+
+       /* Override any initial settings with values from the commandline */
+       acer_commandline_init();
+
+       return 0;
+
+error_platform_register:
+       return -ENODEV;
+}
+
+static void __exit acer_wmi_exit(void)
+{
+       remove_sysfs(acer_platform_device);
+       remove_debugfs();
+       platform_device_del(acer_platform_device);
+       platform_driver_unregister(&acer_platform_driver);
+
+       printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
+       return;
+}
+
+module_init(acer_wmi_init);
+module_exit(acer_wmi_exit);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
new file mode 100644 (file)
index 0000000..8fb8b35
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ *  asus-laptop.c - Asus Laptop Support
+ *
+ *
+ *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
+ *  Copyright (C) 2006-2007 Corentin Chary
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *  The development page for this driver is located at
+ *  http://sourceforge.net/projects/acpi4asus/
+ *
+ *  Credits:
+ *  Pontus Fuchs   - Helper functions, cleanup
+ *  Johann Wiesner - Small compile fixes
+ *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
+ *  Eric Burghard  - LED display support for W1N
+ *  Josh Green     - Light Sens support
+ *  Thomas Tuttle  - His first patch for led support was very helpfull
+ *  Sam Lin        - GPS support
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/proc_fs.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+
+#define ASUS_LAPTOP_VERSION "0.42"
+
+#define ASUS_HOTK_NAME          "Asus Laptop Support"
+#define ASUS_HOTK_CLASS         "hotkey"
+#define ASUS_HOTK_DEVICE_NAME   "Hotkey"
+#define ASUS_HOTK_FILE          "asus-laptop"
+#define ASUS_HOTK_PREFIX        "\\_SB.ATKD."
+
+/*
+ * Some events we use, same for all Asus
+ */
+#define ATKD_BR_UP       0x10
+#define ATKD_BR_DOWN     0x20
+#define ATKD_LCD_ON      0x33
+#define ATKD_LCD_OFF     0x34
+
+/*
+ * Known bits returned by \_SB.ATKD.HWRS
+ */
+#define WL_HWRS     0x80
+#define BT_HWRS     0x100
+
+/*
+ * Flags for hotk status
+ * WL_ON and BT_ON are also used for wireless_status()
+ */
+#define WL_ON       0x01       //internal Wifi
+#define BT_ON       0x02       //internal Bluetooth
+#define MLED_ON     0x04       //mail LED
+#define TLED_ON     0x08       //touchpad LED
+#define RLED_ON     0x10       //Record LED
+#define PLED_ON     0x20       //Phone LED
+#define GLED_ON     0x40       //Gaming LED
+#define LCD_ON      0x80       //LCD backlight
+#define GPS_ON      0x100      //GPS
+
+#define ASUS_LOG    ASUS_HOTK_FILE ": "
+#define ASUS_ERR    KERN_ERR    ASUS_LOG
+#define ASUS_WARNING    KERN_WARNING    ASUS_LOG
+#define ASUS_NOTICE KERN_NOTICE ASUS_LOG
+#define ASUS_INFO   KERN_INFO   ASUS_LOG
+#define ASUS_DEBUG  KERN_DEBUG  ASUS_LOG
+
+MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary");
+MODULE_DESCRIPTION(ASUS_HOTK_NAME);
+MODULE_LICENSE("GPL");
+
+/* WAPF defines the behavior of the Fn+Fx wlan key
+ * The significance of values is yet to be found, but
+ * most of the time:
+ * 0x0 will do nothing
+ * 0x1 will allow to control the device with Fn+Fx key.
+ * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key
+ * 0x5 like 0x1 or 0x4
+ * So, if something doesn't work as you want, just try other values =)
+ */
+static uint wapf = 1;
+module_param(wapf, uint, 0644);
+MODULE_PARM_DESC(wapf, "WAPF value");
+
+#define ASUS_HANDLE(object, paths...)                                  \
+       static acpi_handle  object##_handle = NULL;                     \
+       static char *object##_paths[] = { paths }
+
+/* LED */
+ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED");
+ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED");
+ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED");        /* W1JC */
+ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED");        /* A7J */
+ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED");        /* G1, G2 (probably) */
+
+/* LEDD */
+ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM");
+
+/* Bluetooth and WLAN
+ * WLED and BLED are not handled like other XLED, because in some dsdt
+ * they also control the WLAN/Bluetooth device.
+ */
+ASUS_HANDLE(wl_switch, ASUS_HOTK_PREFIX "WLED");
+ASUS_HANDLE(bt_switch, ASUS_HOTK_PREFIX "BLED");
+ASUS_HANDLE(wireless_status, ASUS_HOTK_PREFIX "RSTS"); /* All new models */
+
+/* Brightness */
+ASUS_HANDLE(brightness_set, ASUS_HOTK_PREFIX "SPLV");
+ASUS_HANDLE(brightness_get, ASUS_HOTK_PREFIX "GPLV");
+
+/* Backlight */
+ASUS_HANDLE(lcd_switch, "\\_SB.PCI0.SBRG.EC0._Q10",    /* All new models */
+           "\\_SB.PCI0.ISA.EC0._Q10",  /* A1x */
+           "\\_SB.PCI0.PX40.ECD0._Q10",        /* L3C */
+           "\\_SB.PCI0.PX40.EC0.Q10",  /* M1A */
+           "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
+           "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
+           "\\_SB.PCI0.PX40.Q10",      /* S1x */
+           "\\Q10");           /* A2x, L2D, L3D, M2E */
+
+/* Display */
+ASUS_HANDLE(display_set, ASUS_HOTK_PREFIX "SDSP");
+ASUS_HANDLE(display_get, "\\_SB.PCI0.P0P1.VGA.GETD",   /*  A6B, A6K A6R A7D F3JM L4R M6R A3G
+                                                          M6A M6V VX-1 V6J V6V W3Z */
+           "\\_SB.PCI0.P0P2.VGA.GETD", /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V
+                                          S5A M5A z33A W1Jc W2V G1 */
+           "\\_SB.PCI0.P0P3.VGA.GETD", /* A6V A6Q */
+           "\\_SB.PCI0.P0PA.VGA.GETD", /* A6T, A6M */
+           "\\_SB.PCI0.PCI1.VGAC.NMAP",        /* L3C */
+           "\\_SB.PCI0.VGA.GETD",      /* Z96F */
+           "\\ACTD",           /* A2D */
+           "\\ADVG",           /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
+           "\\DNXT",           /* P30 */
+           "\\INFB",           /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
+           "\\SSTE");          /* A3F A6F A3N A3L M6N W3N W6A */
+
+ASUS_HANDLE(ls_switch, ASUS_HOTK_PREFIX "ALSC");       /* Z71A Z71V */
+ASUS_HANDLE(ls_level, ASUS_HOTK_PREFIX "ALSL");        /* Z71A Z71V */
+
+/* GPS */
+/* R2H use different handle for GPS on/off */
+ASUS_HANDLE(gps_on, ASUS_HOTK_PREFIX "SDON");  /* R2H */
+ASUS_HANDLE(gps_off, ASUS_HOTK_PREFIX "SDOF"); /* R2H */
+ASUS_HANDLE(gps_status, ASUS_HOTK_PREFIX "GPST");
+
+/*
+ * This is the main structure, we can use it to store anything interesting
+ * about the hotk device
+ */
+struct asus_hotk {
+       char *name;             //laptop name
+       struct acpi_device *device;     //the device we are in
+       acpi_handle handle;     //the handle of the hotk device
+       char status;            //status of the hotk, for LEDs, ...
+       u32 ledd_status;        //status of the LED display
+       u8 light_level;         //light sensor level
+       u8 light_switch;        //light sensor switch value
+       u16 event_count[128];   //count for each event TODO make this better
+};
+
+/*
+ * This header is made available to allow proper configuration given model,
+ * revision number , ... this info cannot go in struct asus_hotk because it is
+ * available before the hotk
+ */
+static struct acpi_table_header *asus_info;
+
+/* The actual device the driver binds to */
+static struct asus_hotk *hotk;
+
+/*
+ * The hotkey driver declaration
+ */
+static const struct acpi_device_id asus_device_ids[] = {
+       {"ATK0100", 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, asus_device_ids);
+
+static int asus_hotk_add(struct acpi_device *device);
+static int asus_hotk_remove(struct acpi_device *device, int type);
+static struct acpi_driver asus_hotk_driver = {
+       .name = ASUS_HOTK_NAME,
+       .class = ASUS_HOTK_CLASS,
+       .ids = asus_device_ids,
+       .ops = {
+               .add = asus_hotk_add,
+               .remove = asus_hotk_remove,
+               },
+};
+
+/* The backlight device /sys/class/backlight */
+static struct backlight_device *asus_backlight_device;
+
+/*
+ * The backlight class declaration
+ */
+static int read_brightness(struct backlight_device *bd);
+static int update_bl_status(struct backlight_device *bd);
+static struct backlight_ops asusbl_ops = {
+       .get_brightness = read_brightness,
+       .update_status = update_bl_status,
+};
+
+/* These functions actually update the LED's, and are called from a
+ * workqueue. By doing this as separate work rather than when the LED
+ * subsystem asks, we avoid messing with the Asus ACPI stuff during a
+ * potentially bad time, such as a timer interrupt. */
+static struct workqueue_struct *led_workqueue;
+
+#define ASUS_LED(object, ledname)                                      \
+       static void object##_led_set(struct led_classdev *led_cdev,     \
+                                    enum led_brightness value);        \
+       static void object##_led_update(struct work_struct *ignored);   \
+       static int object##_led_wk;                                     \
+       static DECLARE_WORK(object##_led_work, object##_led_update);    \
+       static struct led_classdev object##_led = {                     \
+               .name           = "asus::" ledname,                     \
+               .brightness_set = object##_led_set,                     \
+       }
+
+ASUS_LED(mled, "mail");
+ASUS_LED(tled, "touchpad");
+ASUS_LED(rled, "record");
+ASUS_LED(pled, "phone");
+ASUS_LED(gled, "gaming");
+
+/*
+ * This function evaluates an ACPI method, given an int as parameter, the
+ * method is searched within the scope of the handle, can be NULL. The output
+ * of the method is written is output, which can also be NULL
+ *
+ * returns 0 if write is successful, -1 else.
+ */
+static int write_acpi_int(acpi_handle handle, const char *method, int val,
+                         struct acpi_buffer *output)
+{
+       struct acpi_object_list params; //list of input parameters (an int here)
+       union acpi_object in_obj;       //the only param we use
+       acpi_status status;
+
+       if (!handle)
+               return 0;
+
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = val;
+
+       status = acpi_evaluate_object(handle, (char *)method, &params, output);
+       if (status == AE_OK)
+               return 0;
+       else
+               return -1;
+}
+
+static int read_wireless_status(int mask)
+{
+       unsigned long long status;
+       acpi_status rv = AE_OK;
+
+       if (!wireless_status_handle)
+               return (hotk->status & mask) ? 1 : 0;
+
+       rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
+       if (ACPI_FAILURE(rv))
+               printk(ASUS_WARNING "Error reading Wireless status\n");
+       else
+               return (status & mask) ? 1 : 0;
+
+       return (hotk->status & mask) ? 1 : 0;
+}
+
+static int read_gps_status(void)
+{
+       unsigned long long status;
+       acpi_status rv = AE_OK;
+
+       rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
+       if (ACPI_FAILURE(rv))
+               printk(ASUS_WARNING "Error reading GPS status\n");
+       else
+               return status ? 1 : 0;
+
+       return (hotk->status & GPS_ON) ? 1 : 0;
+}
+
+/* Generic LED functions */
+static int read_status(int mask)
+{
+       /* There is a special method for both wireless devices */
+       if (mask == BT_ON || mask == WL_ON)
+               return read_wireless_status(mask);
+       else if (mask == GPS_ON)
+               return read_gps_status();
+
+       return (hotk->status & mask) ? 1 : 0;
+}
+
+static void write_status(acpi_handle handle, int out, int mask)
+{
+       hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask);
+
+       switch (mask) {
+       case MLED_ON:
+               out = !(out & 0x1);
+               break;
+       case GLED_ON:
+               out = (out & 0x1) + 1;
+               break;
+       case GPS_ON:
+               handle = (out) ? gps_on_handle : gps_off_handle;
+               out = 0x02;
+               break;
+       default:
+               out &= 0x1;
+               break;
+       }
+
+       if (write_acpi_int(handle, NULL, out, NULL))
+               printk(ASUS_WARNING " write failed %x\n", mask);
+}
+
+/* /sys/class/led handlers */
+#define ASUS_LED_HANDLER(object, mask)                                 \
+       static void object##_led_set(struct led_classdev *led_cdev,     \
+                                    enum led_brightness value)         \
+       {                                                               \
+               object##_led_wk = (value > 0) ? 1 : 0;                  \
+               queue_work(led_workqueue, &object##_led_work);          \
+       }                                                               \
+       static void object##_led_update(struct work_struct *ignored)    \
+       {                                                               \
+               int value = object##_led_wk;                            \
+               write_status(object##_set_handle, value, (mask));       \
+       }
+
+ASUS_LED_HANDLER(mled, MLED_ON);
+ASUS_LED_HANDLER(pled, PLED_ON);
+ASUS_LED_HANDLER(rled, RLED_ON);
+ASUS_LED_HANDLER(tled, TLED_ON);
+ASUS_LED_HANDLER(gled, GLED_ON);
+
+static int get_lcd_state(void)
+{
+       return read_status(LCD_ON);
+}
+
+static int set_lcd_state(int value)
+{
+       int lcd = 0;
+       acpi_status status = 0;
+
+       lcd = value ? 1 : 0;
+
+       if (lcd == get_lcd_state())
+               return 0;
+
+       if (lcd_switch_handle) {
+               status = acpi_evaluate_object(lcd_switch_handle,
+                                             NULL, NULL, NULL);
+
+               if (ACPI_FAILURE(status))
+                       printk(ASUS_WARNING "Error switching LCD\n");
+       }
+
+       write_status(NULL, lcd, LCD_ON);
+       return 0;
+}
+
+static void lcd_blank(int blank)
+{
+       struct backlight_device *bd = asus_backlight_device;
+
+       if (bd) {
+               bd->props.power = blank;
+               backlight_update_status(bd);
+       }
+}
+
+static int read_brightness(struct backlight_device *bd)
+{
+       unsigned long long value;
+       acpi_status rv = AE_OK;
+
+       rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
+       if (ACPI_FAILURE(rv))
+               printk(ASUS_WARNING "Error reading brightness\n");
+
+       return value;
+}
+
+static int set_brightness(struct backlight_device *bd, int value)
+{
+       int ret = 0;
+
+       value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
+       /* 0 <= value <= 15 */
+
+       if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
+               printk(ASUS_WARNING "Error changing brightness\n");
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+       int rv;
+       int value = bd->props.brightness;
+
+       rv = set_brightness(bd, value);
+       if (rv)
+               return rv;
+
+       value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
+       return set_lcd_state(value);
+}
+
+/*
+ * Platform device handlers
+ */
+
+/*
+ * We write our info in page, we begin at offset off and cannot write more
+ * than count bytes. We set eof to 1 if we handle those 2 values. We return the
+ * number of bytes written in page
+ */
+static ssize_t show_infos(struct device *dev,
+                         struct device_attribute *attr, char *page)
+{
+       int len = 0;
+       unsigned long long temp;
+       char buf[16];           //enough for all info
+       acpi_status rv = AE_OK;
+
+       /*
+        * We use the easy way, we don't care of off and count, so we don't set eof
+        * to 1
+        */
+
+       len += sprintf(page, ASUS_HOTK_NAME " " ASUS_LAPTOP_VERSION "\n");
+       len += sprintf(page + len, "Model reference    : %s\n", hotk->name);
+       /*
+        * The SFUN method probably allows the original driver to get the list
+        * of features supported by a given model. For now, 0x0100 or 0x0800
+        * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
+        * The significance of others is yet to be found.
+        */
+       rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp);
+       if (!ACPI_FAILURE(rv))
+               len += sprintf(page + len, "SFUN value         : 0x%04x\n",
+                              (uint) temp);
+       /*
+        * Another value for userspace: the ASYM method returns 0x02 for
+        * battery low and 0x04 for battery critical, its readings tend to be
+        * more accurate than those provided by _BST.
+        * Note: since not all the laptops provide this method, errors are
+        * silently ignored.
+        */
+       rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp);
+       if (!ACPI_FAILURE(rv))
+               len += sprintf(page + len, "ASYM value         : 0x%04x\n",
+                              (uint) temp);
+       if (asus_info) {
+               snprintf(buf, 16, "%d", asus_info->length);
+               len += sprintf(page + len, "DSDT length        : %s\n", buf);
+               snprintf(buf, 16, "%d", asus_info->checksum);
+               len += sprintf(page + len, "DSDT checksum      : %s\n", buf);
+               snprintf(buf, 16, "%d", asus_info->revision);
+               len += sprintf(page + len, "DSDT revision      : %s\n", buf);
+               snprintf(buf, 7, "%s", asus_info->oem_id);
+               len += sprintf(page + len, "OEM id             : %s\n", buf);
+               snprintf(buf, 9, "%s", asus_info->oem_table_id);
+               len += sprintf(page + len, "OEM table id       : %s\n", buf);
+               snprintf(buf, 16, "%x", asus_info->oem_revision);
+               len += sprintf(page + len, "OEM revision       : 0x%s\n", buf);
+               snprintf(buf, 5, "%s", asus_info->asl_compiler_id);
+               len += sprintf(page + len, "ASL comp vendor id : %s\n", buf);
+               snprintf(buf, 16, "%x", asus_info->asl_compiler_revision);
+               len += sprintf(page + len, "ASL comp revision  : 0x%s\n", buf);
+       }
+
+       return len;
+}
+
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+       if (!count)
+               return 0;
+       if (count > 31)
+               return -EINVAL;
+       if (sscanf(buf, "%i", val) != 1)
+               return -EINVAL;
+       return count;
+}
+
+static ssize_t store_status(const char *buf, size_t count,
+                           acpi_handle handle, int mask)
+{
+       int rv, value;
+       int out = 0;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0)
+               out = value ? 1 : 0;
+
+       write_status(handle, out, mask);
+
+       return rv;
+}
+
+/*
+ * LEDD display
+ */
+static ssize_t show_ledd(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "0x%08x\n", hotk->ledd_status);
+}
+
+static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0) {
+               if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
+                       printk(ASUS_WARNING "LED display write failed\n");
+               else
+                       hotk->ledd_status = (u32) value;
+       }
+       return rv;
+}
+
+/*
+ * WLAN
+ */
+static ssize_t show_wlan(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", read_status(WL_ON));
+}
+
+static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       return store_status(buf, count, wl_switch_handle, WL_ON);
+}
+
+/*
+ * Bluetooth
+ */
+static ssize_t show_bluetooth(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", read_status(BT_ON));
+}
+
+static ssize_t store_bluetooth(struct device *dev,
+                              struct device_attribute *attr, const char *buf,
+                              size_t count)
+{
+       return store_status(buf, count, bt_switch_handle, BT_ON);
+}
+
+/*
+ * Display
+ */
+static void set_display(int value)
+{
+       /* no sanity check needed for now */
+       if (write_acpi_int(display_set_handle, NULL, value, NULL))
+               printk(ASUS_WARNING "Error setting display\n");
+       return;
+}
+
+static int read_display(void)
+{
+       unsigned long long value = 0;
+       acpi_status rv = AE_OK;
+
+       /* In most of the case, we know how to set the display, but sometime
+          we can't read it */
+       if (display_get_handle) {
+               rv = acpi_evaluate_integer(display_get_handle, NULL,
+                                          NULL, &value);
+               if (ACPI_FAILURE(rv))
+                       printk(ASUS_WARNING "Error reading display status\n");
+       }
+
+       value &= 0x0F;          /* needed for some models, shouldn't hurt others */
+
+       return value;
+}
+
+/*
+ * Now, *this* one could be more user-friendly, but so far, no-one has
+ * complained. The significance of bits is the same as in store_disp()
+ */
+static ssize_t show_disp(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", read_display());
+}
+
+/*
+ * Experimental support for display switching. As of now: 1 should activate
+ * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
+ * Any combination (bitwise) of these will suffice. I never actually tested 4
+ * displays hooked up simultaneously, so be warned. See the acpi4asus README
+ * for more info.
+ */
+static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0)
+               set_display(value);
+       return rv;
+}
+
+/*
+ * Light Sens
+ */
+static void set_light_sens_switch(int value)
+{
+       if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
+               printk(ASUS_WARNING "Error setting light sensor switch\n");
+       hotk->light_switch = value;
+}
+
+static ssize_t show_lssw(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", hotk->light_switch);
+}
+
+static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0)
+               set_light_sens_switch(value ? 1 : 0);
+
+       return rv;
+}
+
+static void set_light_sens_level(int value)
+{
+       if (write_acpi_int(ls_level_handle, NULL, value, NULL))
+               printk(ASUS_WARNING "Error setting light sensor level\n");
+       hotk->light_level = value;
+}
+
+static ssize_t show_lslvl(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", hotk->light_level);
+}
+
+static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
+                          const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0) {
+               value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
+               /* 0 <= value <= 15 */
+               set_light_sens_level(value);
+       }
+
+       return rv;
+}
+
+/*
+ * GPS
+ */
+static ssize_t show_gps(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", read_status(GPS_ON));
+}
+
+static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
+                        const char *buf, size_t count)
+{
+       return store_status(buf, count, NULL, GPS_ON);
+}
+
+static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
+{
+       /* TODO Find a better way to handle events count. */
+       if (!hotk)
+               return;
+
+       /*
+        * We need to tell the backlight device when the backlight power is
+        * switched
+        */
+       if (event == ATKD_LCD_ON) {
+               write_status(NULL, 1, LCD_ON);
+               lcd_blank(FB_BLANK_UNBLANK);
+       } else if (event == ATKD_LCD_OFF) {
+               write_status(NULL, 0, LCD_ON);
+               lcd_blank(FB_BLANK_POWERDOWN);
+       }
+
+       acpi_bus_generate_proc_event(hotk->device, event,
+                               hotk->event_count[event % 128]++);
+
+       return;
+}
+
+#define ASUS_CREATE_DEVICE_ATTR(_name)                                 \
+       struct device_attribute dev_attr_##_name = {                    \
+               .attr = {                                               \
+                       .name = __stringify(_name),                     \
+                       .mode = 0 },                                    \
+               .show   = NULL,                                         \
+               .store  = NULL,                                         \
+       }
+
+#define ASUS_SET_DEVICE_ATTR(_name, _mode, _show, _store)              \
+       do {                                                            \
+               dev_attr_##_name.attr.mode = _mode;                     \
+               dev_attr_##_name.show = _show;                          \
+               dev_attr_##_name.store = _store;                        \
+       } while(0)
+
+static ASUS_CREATE_DEVICE_ATTR(infos);
+static ASUS_CREATE_DEVICE_ATTR(wlan);
+static ASUS_CREATE_DEVICE_ATTR(bluetooth);
+static ASUS_CREATE_DEVICE_ATTR(display);
+static ASUS_CREATE_DEVICE_ATTR(ledd);
+static ASUS_CREATE_DEVICE_ATTR(ls_switch);
+static ASUS_CREATE_DEVICE_ATTR(ls_level);
+static ASUS_CREATE_DEVICE_ATTR(gps);
+
+static struct attribute *asuspf_attributes[] = {
+       &dev_attr_infos.attr,
+       &dev_attr_wlan.attr,
+       &dev_attr_bluetooth.attr,
+       &dev_attr_display.attr,
+       &dev_attr_ledd.attr,
+       &dev_attr_ls_switch.attr,
+       &dev_attr_ls_level.attr,
+       &dev_attr_gps.attr,
+       NULL
+};
+
+static struct attribute_group asuspf_attribute_group = {
+       .attrs = asuspf_attributes
+};
+
+static struct platform_driver asuspf_driver = {
+       .driver = {
+                  .name = ASUS_HOTK_FILE,
+                  .owner = THIS_MODULE,
+                  }
+};
+
+static struct platform_device *asuspf_device;
+
+static void asus_hotk_add_fs(void)
+{
+       ASUS_SET_DEVICE_ATTR(infos, 0444, show_infos, NULL);
+
+       if (wl_switch_handle)
+               ASUS_SET_DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan);
+
+       if (bt_switch_handle)
+               ASUS_SET_DEVICE_ATTR(bluetooth, 0644,
+                                    show_bluetooth, store_bluetooth);
+
+       if (display_set_handle && display_get_handle)
+               ASUS_SET_DEVICE_ATTR(display, 0644, show_disp, store_disp);
+       else if (display_set_handle)
+               ASUS_SET_DEVICE_ATTR(display, 0200, NULL, store_disp);
+
+       if (ledd_set_handle)
+               ASUS_SET_DEVICE_ATTR(ledd, 0644, show_ledd, store_ledd);
+
+       if (ls_switch_handle && ls_level_handle) {
+               ASUS_SET_DEVICE_ATTR(ls_level, 0644, show_lslvl, store_lslvl);
+               ASUS_SET_DEVICE_ATTR(ls_switch, 0644, show_lssw, store_lssw);
+       }
+
+       if (gps_status_handle && gps_on_handle && gps_off_handle)
+               ASUS_SET_DEVICE_ATTR(gps, 0644, show_gps, store_gps);
+}
+
+static int asus_handle_init(char *name, acpi_handle * handle,
+                           char **paths, int num_paths)
+{
+       int i;
+       acpi_status status;
+
+       for (i = 0; i < num_paths; i++) {
+               status = acpi_get_handle(NULL, paths[i], handle);
+               if (ACPI_SUCCESS(status))
+                       return 0;
+       }
+
+       *handle = NULL;
+       return -ENODEV;
+}
+
+#define ASUS_HANDLE_INIT(object)                                       \
+       asus_handle_init(#object, &object##_handle, object##_paths,     \
+                        ARRAY_SIZE(object##_paths))
+
+/*
+ * This function is used to initialize the hotk with right values. In this
+ * method, we can make all the detection we want, and modify the hotk struct
+ */
+static int asus_hotk_get_info(void)
+{
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *model = NULL;
+       unsigned long long bsts_result, hwrs_result;
+       char *string = NULL;
+       acpi_status status;
+
+       /*
+        * Get DSDT headers early enough to allow for differentiating between
+        * models, but late enough to allow acpi_bus_register_driver() to fail
+        * before doing anything ACPI-specific. Should we encounter a machine,
+        * which needs special handling (i.e. its hotkey device has a different
+        * HID), this bit will be moved. A global variable asus_info contains
+        * the DSDT header.
+        */
+       status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
+       if (ACPI_FAILURE(status))
+               printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
+
+       /* We have to write 0 on init this far for all ASUS models */
+       if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
+               printk(ASUS_ERR "Hotkey initialization failed\n");
+               return -ENODEV;
+       }
+
+       /* This needs to be called for some laptops to init properly */
+       status =
+           acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
+       if (ACPI_FAILURE(status))
+               printk(ASUS_WARNING "Error calling BSTS\n");
+       else if (bsts_result)
+               printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
+                      (uint) bsts_result);
+
+       /* This too ... */
+       write_acpi_int(hotk->handle, "CWAP", wapf, NULL);
+
+       /*
+        * Try to match the object returned by INIT to the specific model.
+        * Handle every possible object (or the lack of thereof) the DSDT
+        * writers might throw at us. When in trouble, we pass NULL to
+        * asus_model_match() and try something completely different.
+        */
+       if (buffer.pointer) {
+               model = buffer.pointer;
+               switch (model->type) {
+               case ACPI_TYPE_STRING:
+                       string = model->string.pointer;
+                       break;
+               case ACPI_TYPE_BUFFER:
+                       string = model->buffer.pointer;
+                       break;
+               default:
+                       string = "";
+                       break;
+               }
+       }
+       hotk->name = kstrdup(string, GFP_KERNEL);
+       if (!hotk->name)
+               return -ENOMEM;
+
+       if (*string)
+               printk(ASUS_NOTICE "  %s model detected\n", string);
+
+       ASUS_HANDLE_INIT(mled_set);
+       ASUS_HANDLE_INIT(tled_set);
+       ASUS_HANDLE_INIT(rled_set);
+       ASUS_HANDLE_INIT(pled_set);
+       ASUS_HANDLE_INIT(gled_set);
+
+       ASUS_HANDLE_INIT(ledd_set);
+
+       /*
+        * The HWRS method return informations about the hardware.
+        * 0x80 bit is for WLAN, 0x100 for Bluetooth.
+        * The significance of others is yet to be found.
+        * If we don't find the method, we assume the device are present.
+        */
+       status =
+           acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result);
+       if (ACPI_FAILURE(status))
+               hwrs_result = WL_HWRS | BT_HWRS;
+
+       if (hwrs_result & WL_HWRS)
+               ASUS_HANDLE_INIT(wl_switch);
+       if (hwrs_result & BT_HWRS)
+               ASUS_HANDLE_INIT(bt_switch);
+
+       ASUS_HANDLE_INIT(wireless_status);
+
+       ASUS_HANDLE_INIT(brightness_set);
+       ASUS_HANDLE_INIT(brightness_get);
+
+       ASUS_HANDLE_INIT(lcd_switch);
+
+       ASUS_HANDLE_INIT(display_set);
+       ASUS_HANDLE_INIT(display_get);
+
+       /* There is a lot of models with "ALSL", but a few get
+          a real light sens, so we need to check it. */
+       if (!ASUS_HANDLE_INIT(ls_switch))
+               ASUS_HANDLE_INIT(ls_level);
+
+       ASUS_HANDLE_INIT(gps_on);
+       ASUS_HANDLE_INIT(gps_off);
+       ASUS_HANDLE_INIT(gps_status);
+
+       kfree(model);
+
+       return AE_OK;
+}
+
+static int asus_hotk_check(void)
+{
+       int result = 0;
+
+       result = acpi_bus_get_status(hotk->device);
+       if (result)
+               return result;
+
+       if (hotk->device->status.present) {
+               result = asus_hotk_get_info();
+       } else {
+               printk(ASUS_ERR "Hotkey device not present, aborting\n");
+               return -EINVAL;
+       }
+
+       return result;
+}
+
+static int asus_hotk_found;
+
+static int asus_hotk_add(struct acpi_device *device)
+{
+       acpi_status status = AE_OK;
+       int result;
+
+       if (!device)
+               return -EINVAL;
+
+       printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
+              ASUS_LAPTOP_VERSION);
+
+       hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
+       if (!hotk)
+               return -ENOMEM;
+
+       hotk->handle = device->handle;
+       strcpy(acpi_device_name(device), ASUS_HOTK_DEVICE_NAME);
+       strcpy(acpi_device_class(device), ASUS_HOTK_CLASS);
+       device->driver_data = hotk;
+       hotk->device = device;
+
+       result = asus_hotk_check();
+       if (result)
+               goto end;
+
+       asus_hotk_add_fs();
+
+       /*
+        * We install the handler, it will receive the hotk in parameter, so, we
+        * could add other data to the hotk struct
+        */
+       status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
+                                            asus_hotk_notify, hotk);
+       if (ACPI_FAILURE(status))
+               printk(ASUS_ERR "Error installing notify handler\n");
+
+       asus_hotk_found = 1;
+
+       /* WLED and BLED are on by default */
+       write_status(bt_switch_handle, 1, BT_ON);
+       write_status(wl_switch_handle, 1, WL_ON);
+
+       /* If the h/w switch is off, we need to check the real status */
+       write_status(NULL, read_status(BT_ON), BT_ON);
+       write_status(NULL, read_status(WL_ON), WL_ON);
+
+       /* LCD Backlight is on by default */
+       write_status(NULL, 1, LCD_ON);
+
+       /* LED display is off by default */
+       hotk->ledd_status = 0xFFF;
+
+       /* Set initial values of light sensor and level */
+       hotk->light_switch = 1; /* Default to light sensor disabled */
+       hotk->light_level = 0;  /* level 5 for sensor sensitivity */
+
+       if (ls_switch_handle)
+               set_light_sens_switch(hotk->light_switch);
+
+       if (ls_level_handle)
+               set_light_sens_level(hotk->light_level);
+
+       /* GPS is on by default */
+       write_status(NULL, 1, GPS_ON);
+
+      end:
+       if (result) {
+               kfree(hotk->name);
+               kfree(hotk);
+       }
+
+       return result;
+}
+
+static int asus_hotk_remove(struct acpi_device *device, int type)
+{
+       acpi_status status = 0;
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
+                                           asus_hotk_notify);
+       if (ACPI_FAILURE(status))
+               printk(ASUS_ERR "Error removing notify handler\n");
+
+       kfree(hotk->name);
+       kfree(hotk);
+
+       return 0;
+}
+
+static void asus_backlight_exit(void)
+{
+       if (asus_backlight_device)
+               backlight_device_unregister(asus_backlight_device);
+}
+
+#define  ASUS_LED_UNREGISTER(object)                           \
+       if (object##_led.dev)                                   \
+               led_classdev_unregister(&object##_led)
+
+static void asus_led_exit(void)
+{
+       destroy_workqueue(led_workqueue);
+       ASUS_LED_UNREGISTER(mled);
+       ASUS_LED_UNREGISTER(tled);
+       ASUS_LED_UNREGISTER(pled);
+       ASUS_LED_UNREGISTER(rled);
+       ASUS_LED_UNREGISTER(gled);
+}
+
+static void __exit asus_laptop_exit(void)
+{
+       asus_backlight_exit();
+       asus_led_exit();
+
+       acpi_bus_unregister_driver(&asus_hotk_driver);
+       sysfs_remove_group(&asuspf_device->dev.kobj, &asuspf_attribute_group);
+       platform_device_unregister(asuspf_device);
+       platform_driver_unregister(&asuspf_driver);
+}
+
+static int asus_backlight_init(struct device *dev)
+{
+       struct backlight_device *bd;
+
+       if (brightness_set_handle && lcd_switch_handle) {
+               bd = backlight_device_register(ASUS_HOTK_FILE, dev,
+                                              NULL, &asusbl_ops);
+               if (IS_ERR(bd)) {
+                       printk(ASUS_ERR
+                              "Could not register asus backlight device\n");
+                       asus_backlight_device = NULL;
+                       return PTR_ERR(bd);
+               }
+
+               asus_backlight_device = bd;
+
+               bd->props.max_brightness = 15;
+               bd->props.brightness = read_brightness(NULL);
+               bd->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(bd);
+       }
+       return 0;
+}
+
+static int asus_led_register(acpi_handle handle,
+                            struct led_classdev *ldev, struct device *dev)
+{
+       if (!handle)
+               return 0;
+
+       return led_classdev_register(dev, ldev);
+}
+
+#define ASUS_LED_REGISTER(object, device)                              \
+       asus_led_register(object##_set_handle, &object##_led, device)
+
+static int asus_led_init(struct device *dev)
+{
+       int rv;
+
+       rv = ASUS_LED_REGISTER(mled, dev);
+       if (rv)
+               goto out;
+
+       rv = ASUS_LED_REGISTER(tled, dev);
+       if (rv)
+               goto out1;
+
+       rv = ASUS_LED_REGISTER(rled, dev);
+       if (rv)
+               goto out2;
+
+       rv = ASUS_LED_REGISTER(pled, dev);
+       if (rv)
+               goto out3;
+
+       rv = ASUS_LED_REGISTER(gled, dev);
+       if (rv)
+               goto out4;
+
+       led_workqueue = create_singlethread_workqueue("led_workqueue");
+       if (!led_workqueue)
+               goto out5;
+
+       return 0;
+out5:
+       rv = -ENOMEM;
+       ASUS_LED_UNREGISTER(gled);
+out4:
+       ASUS_LED_UNREGISTER(pled);
+out3:
+       ASUS_LED_UNREGISTER(rled);
+out2:
+       ASUS_LED_UNREGISTER(tled);
+out1:
+       ASUS_LED_UNREGISTER(mled);
+out:
+       return rv;
+}
+
+static int __init asus_laptop_init(void)
+{
+       struct device *dev;
+       int result;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       result = acpi_bus_register_driver(&asus_hotk_driver);
+       if (result < 0)
+               return result;
+
+       /*
+        * This is a bit of a kludge.  We only want this module loaded
+        * for ASUS systems, but there's currently no way to probe the
+        * ACPI namespace for ASUS HIDs.  So we just return failure if
+        * we didn't find one, which will cause the module to be
+        * unloaded.
+        */
+       if (!asus_hotk_found) {
+               acpi_bus_unregister_driver(&asus_hotk_driver);
+               return -ENODEV;
+       }
+
+       dev = acpi_get_physical_device(hotk->device->handle);
+
+       if (!acpi_video_backlight_support()) {
+               result = asus_backlight_init(dev);
+               if (result)
+                       goto fail_backlight;
+       } else
+               printk(ASUS_INFO "Brightness ignored, must be controlled by "
+                      "ACPI video driver\n");
+
+       result = asus_led_init(dev);
+       if (result)
+               goto fail_led;
+
+       /* Register platform stuff */
+       result = platform_driver_register(&asuspf_driver);
+       if (result)
+               goto fail_platform_driver;
+
+       asuspf_device = platform_device_alloc(ASUS_HOTK_FILE, -1);
+       if (!asuspf_device) {
+               result = -ENOMEM;
+               goto fail_platform_device1;
+       }
+
+       result = platform_device_add(asuspf_device);
+       if (result)
+               goto fail_platform_device2;
+
+       result = sysfs_create_group(&asuspf_device->dev.kobj,
+                                   &asuspf_attribute_group);
+       if (result)
+               goto fail_sysfs;
+
+       return 0;
+
+      fail_sysfs:
+       platform_device_del(asuspf_device);
+
+      fail_platform_device2:
+       platform_device_put(asuspf_device);
+
+      fail_platform_device1:
+       platform_driver_unregister(&asuspf_driver);
+
+      fail_platform_driver:
+       asus_led_exit();
+
+      fail_led:
+       asus_backlight_exit();
+
+      fail_backlight:
+
+       return result;
+}
+
+module_init(asus_laptop_init);
+module_exit(asus_laptop_exit);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
new file mode 100644 (file)
index 0000000..11003bb
--- /dev/null
@@ -0,0 +1,406 @@
+/*-*-linux-c-*-*/
+
+/*
+  Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
+
+  based on MSI driver
+
+  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+ */
+
+/*
+ * comapl-laptop.c - Compal laptop support.
+ *
+ * This driver exports a few files in /sys/devices/platform/compal-laptop/:
+ *
+ *   wlan - wlan subsystem state: contains 0 or 1 (rw)
+ *
+ *   bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
+ *
+ *   raw - raw value taken from embedded controller register (ro)
+ *
+ * In addition to these platform device attributes the driver
+ * registers itself in the Linux backlight control subsystem and is
+ * available to userspace under /sys/class/backlight/compal-laptop/.
+ *
+ * This driver might work on other laptops produced by Compal. If you
+ * want to try it you can pass force=1 as argument to the module which
+ * will force it to load even when the DMI data doesn't identify the
+ * laptop as FL9x.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+#include <linux/autoconf.h>
+
+#define COMPAL_DRIVER_VERSION "0.2.6"
+
+#define COMPAL_LCD_LEVEL_MAX 8
+
+#define COMPAL_EC_COMMAND_WIRELESS 0xBB
+#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
+
+#define KILLSWITCH_MASK 0x10
+#define WLAN_MASK      0x01
+#define BT_MASK        0x02
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+/* Hardware access */
+
+static int set_lcd_level(int level)
+{
+       if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
+               return -EINVAL;
+
+       ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
+
+       return 0;
+}
+
+static int get_lcd_level(void)
+{
+       u8 result;
+
+       ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
+
+       return (int) result;
+}
+
+static int set_wlan_state(int state)
+{
+       u8 result, value;
+
+       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+       if ((result & KILLSWITCH_MASK) == 0)
+               return -EINVAL;
+       else {
+               if (state)
+                       value = (u8) (result | WLAN_MASK);
+               else
+                       value = (u8) (result & ~WLAN_MASK);
+               ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
+       }
+
+       return 0;
+}
+
+static int set_bluetooth_state(int state)
+{
+       u8 result, value;
+
+       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+       if ((result & KILLSWITCH_MASK) == 0)
+               return -EINVAL;
+       else {
+               if (state)
+                       value = (u8) (result | BT_MASK);
+               else
+                       value = (u8) (result & ~BT_MASK);
+               ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
+       }
+
+       return 0;
+}
+
+static int get_wireless_state(int *wlan, int *bluetooth)
+{
+       u8 result;
+
+       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+       if (wlan) {
+               if ((result & KILLSWITCH_MASK) == 0)
+                       *wlan = 0;
+               else
+                       *wlan = result & WLAN_MASK;
+       }
+
+       if (bluetooth) {
+               if ((result & KILLSWITCH_MASK) == 0)
+                       *bluetooth = 0;
+               else
+                       *bluetooth = (result & BT_MASK) >> 1;
+       }
+
+       return 0;
+}
+
+/* Backlight device stuff */
+
+static int bl_get_brightness(struct backlight_device *b)
+{
+       return get_lcd_level();
+}
+
+
+static int bl_update_status(struct backlight_device *b)
+{
+       return set_lcd_level(b->props.brightness);
+}
+
+static struct backlight_ops compalbl_ops = {
+       .get_brightness = bl_get_brightness,
+       .update_status  = bl_update_status,
+};
+
+static struct backlight_device *compalbl_device;
+
+/* Platform device */
+
+static ssize_t show_wlan(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret, enabled;
+
+       ret = get_wireless_state(&enabled, NULL);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t show_raw(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       u8 result;
+
+       ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+       return sprintf(buf, "%i\n", result);
+}
+
+static ssize_t show_bluetooth(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+       int ret, enabled;
+
+       ret = get_wireless_state(NULL, &enabled);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t store_wlan_state(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int state, ret;
+
+       if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
+               return -EINVAL;
+
+       ret = set_wlan_state(state);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t store_bluetooth_state(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+       int state, ret;
+
+       if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
+               return -EINVAL;
+
+       ret = set_bluetooth_state(state);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
+static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
+static DEVICE_ATTR(raw, 0444, show_raw, NULL);
+
+static struct attribute *compal_attributes[] = {
+       &dev_attr_bluetooth.attr,
+       &dev_attr_wlan.attr,
+       &dev_attr_raw.attr,
+       NULL
+};
+
+static struct attribute_group compal_attribute_group = {
+       .attrs = compal_attributes
+};
+
+static struct platform_driver compal_driver = {
+       .driver = {
+               .name = "compal-laptop",
+               .owner = THIS_MODULE,
+       }
+};
+
+static struct platform_device *compal_device;
+
+/* Initialization */
+
+static int dmi_check_cb(const struct dmi_system_id *id)
+{
+       printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
+               id->ident);
+
+       return 0;
+}
+
+static struct dmi_system_id __initdata compal_dmi_table[] = {
+       {
+               .ident = "FL90/IFL90",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "FL90/IFL90",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "FL91/IFL91",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "FL92/JFL92",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "FT00/IFT00",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
+                       DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+               },
+               .callback = dmi_check_cb
+       },
+       { }
+};
+
+static int __init compal_init(void)
+{
+       int ret;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       if (!force && !dmi_check_system(compal_dmi_table))
+               return -ENODEV;
+
+       /* Register backlight stuff */
+
+       if (!acpi_video_backlight_support()) {
+               compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
+                                                           &compalbl_ops);
+               if (IS_ERR(compalbl_device))
+                       return PTR_ERR(compalbl_device);
+
+               compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
+       }
+
+       ret = platform_driver_register(&compal_driver);
+       if (ret)
+               goto fail_backlight;
+
+       /* Register platform stuff */
+
+       compal_device = platform_device_alloc("compal-laptop", -1);
+       if (!compal_device) {
+               ret = -ENOMEM;
+               goto fail_platform_driver;
+       }
+
+       ret = platform_device_add(compal_device);
+       if (ret)
+               goto fail_platform_device1;
+
+       ret = sysfs_create_group(&compal_device->dev.kobj,
+               &compal_attribute_group);
+       if (ret)
+               goto fail_platform_device2;
+
+       printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
+               " successfully loaded.\n");
+
+       return 0;
+
+fail_platform_device2:
+
+       platform_device_del(compal_device);
+
+fail_platform_device1:
+
+       platform_device_put(compal_device);
+
+fail_platform_driver:
+
+       platform_driver_unregister(&compal_driver);
+
+fail_backlight:
+
+       backlight_device_unregister(compalbl_device);
+
+       return ret;
+}
+
+static void __exit compal_cleanup(void)
+{
+
+       sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
+       platform_device_unregister(compal_device);
+       platform_driver_unregister(&compal_driver);
+       backlight_device_unregister(compalbl_device);
+
+       printk(KERN_INFO "compal-laptop: driver unloaded.\n");
+}
+
+module_init(compal_init);
+module_exit(compal_cleanup);
+
+MODULE_AUTHOR("Cezary Jackiewicz");
+MODULE_DESCRIPTION("Compal Laptop Support");
+MODULE_VERSION(COMPAL_DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
+MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
new file mode 100644 (file)
index 0000000..02fe2b8
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ *  eepc-laptop.c - Asus Eee PC extras
+ *
+ *  Based on asus_acpi.c as patched for the Eee PC by Asus:
+ *  ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
+ *  Based on eee.c from eeepc-linux
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <linux/uaccess.h>
+#include <linux/input.h>
+#include <linux/rfkill.h>
+
+#define EEEPC_LAPTOP_VERSION   "0.1"
+
+#define EEEPC_HOTK_NAME                "Eee PC Hotkey Driver"
+#define EEEPC_HOTK_FILE                "eeepc"
+#define EEEPC_HOTK_CLASS       "hotkey"
+#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
+#define EEEPC_HOTK_HID         "ASUS010"
+
+#define EEEPC_LOG      EEEPC_HOTK_FILE ": "
+#define EEEPC_ERR      KERN_ERR        EEEPC_LOG
+#define EEEPC_WARNING  KERN_WARNING    EEEPC_LOG
+#define EEEPC_NOTICE   KERN_NOTICE     EEEPC_LOG
+#define EEEPC_INFO     KERN_INFO       EEEPC_LOG
+
+/*
+ * Definitions for Asus EeePC
+ */
+#define        NOTIFY_WLAN_ON  0x10
+#define NOTIFY_BRN_MIN 0x20
+#define NOTIFY_BRN_MAX 0x2f
+
+enum {
+       DISABLE_ASL_WLAN = 0x0001,
+       DISABLE_ASL_BLUETOOTH = 0x0002,
+       DISABLE_ASL_IRDA = 0x0004,
+       DISABLE_ASL_CAMERA = 0x0008,
+       DISABLE_ASL_TV = 0x0010,
+       DISABLE_ASL_GPS = 0x0020,
+       DISABLE_ASL_DISPLAYSWITCH = 0x0040,
+       DISABLE_ASL_MODEM = 0x0080,
+       DISABLE_ASL_CARDREADER = 0x0100
+};
+
+enum {
+       CM_ASL_WLAN = 0,
+       CM_ASL_BLUETOOTH,
+       CM_ASL_IRDA,
+       CM_ASL_1394,
+       CM_ASL_CAMERA,
+       CM_ASL_TV,
+       CM_ASL_GPS,
+       CM_ASL_DVDROM,
+       CM_ASL_DISPLAYSWITCH,
+       CM_ASL_PANELBRIGHT,
+       CM_ASL_BIOSFLASH,
+       CM_ASL_ACPIFLASH,
+       CM_ASL_CPUFV,
+       CM_ASL_CPUTEMPERATURE,
+       CM_ASL_FANCPU,
+       CM_ASL_FANCHASSIS,
+       CM_ASL_USBPORT1,
+       CM_ASL_USBPORT2,
+       CM_ASL_USBPORT3,
+       CM_ASL_MODEM,
+       CM_ASL_CARDREADER,
+       CM_ASL_LID
+};
+
+static const char *cm_getv[] = {
+       "WLDG", NULL, NULL, NULL,
+       "CAMG", NULL, NULL, NULL,
+       NULL, "PBLG", NULL, NULL,
+       "CFVG", NULL, NULL, NULL,
+       "USBG", NULL, NULL, "MODG",
+       "CRDG", "LIDG"
+};
+
+static const char *cm_setv[] = {
+       "WLDS", NULL, NULL, NULL,
+       "CAMS", NULL, NULL, NULL,
+       "SDSP", "PBLS", "HDPS", NULL,
+       "CFVS", NULL, NULL, NULL,
+       "USBG", NULL, NULL, "MODS",
+       "CRDS", NULL
+};
+
+#define EEEPC_EC       "\\_SB.PCI0.SBRG.EC0."
+
+#define EEEPC_EC_FAN_PWM       EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
+#define EEEPC_EC_SC02          0x63
+#define EEEPC_EC_FAN_HRPM      EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_LRPM      EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_CTRL      EEEPC_EC "SFB3" /* Byte containing SF25  */
+#define EEEPC_EC_SFB3          0xD3
+
+/*
+ * This is the main structure, we can use it to store useful information
+ * about the hotk device
+ */
+struct eeepc_hotk {
+       struct acpi_device *device;     /* the device we are in */
+       acpi_handle handle;             /* the handle of the hotk device */
+       u32 cm_supported;               /* the control methods supported
+                                          by this BIOS */
+       uint init_flag;                 /* Init flags */
+       u16 event_count[128];           /* count for each event */
+       struct input_dev *inputdev;
+       u16 *keycode_map;
+       struct rfkill *eeepc_wlan_rfkill;
+       struct rfkill *eeepc_bluetooth_rfkill;
+};
+
+/* The actual device the driver binds to */
+static struct eeepc_hotk *ehotk;
+
+/* Platform device/driver */
+static struct platform_driver platform_driver = {
+       .driver = {
+               .name = EEEPC_HOTK_FILE,
+               .owner = THIS_MODULE,
+       }
+};
+
+static struct platform_device *platform_device;
+
+struct key_entry {
+       char type;
+       u8 code;
+       u16 keycode;
+};
+
+enum { KE_KEY, KE_END };
+
+static struct key_entry eeepc_keymap[] = {
+       /* Sleep already handled via generic ACPI code */
+       {KE_KEY, 0x10, KEY_WLAN },
+       {KE_KEY, 0x12, KEY_PROG1 },
+       {KE_KEY, 0x13, KEY_MUTE },
+       {KE_KEY, 0x14, KEY_VOLUMEDOWN },
+       {KE_KEY, 0x15, KEY_VOLUMEUP },
+       {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
+       {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
+       {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
+       {KE_END, 0},
+};
+
+/*
+ * The hotkey driver declaration
+ */
+static int eeepc_hotk_add(struct acpi_device *device);
+static int eeepc_hotk_remove(struct acpi_device *device, int type);
+
+static const struct acpi_device_id eeepc_device_ids[] = {
+       {EEEPC_HOTK_HID, 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
+
+static struct acpi_driver eeepc_hotk_driver = {
+       .name = EEEPC_HOTK_NAME,
+       .class = EEEPC_HOTK_CLASS,
+       .ids = eeepc_device_ids,
+       .ops = {
+               .add = eeepc_hotk_add,
+               .remove = eeepc_hotk_remove,
+       },
+};
+
+/* The backlight device /sys/class/backlight */
+static struct backlight_device *eeepc_backlight_device;
+
+/* The hwmon device */
+static struct device *eeepc_hwmon_device;
+
+/*
+ * The backlight class declaration
+ */
+static int read_brightness(struct backlight_device *bd);
+static int update_bl_status(struct backlight_device *bd);
+static struct backlight_ops eeepcbl_ops = {
+       .get_brightness = read_brightness,
+       .update_status = update_bl_status,
+};
+
+MODULE_AUTHOR("Corentin Chary, Eric Cooper");
+MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
+MODULE_LICENSE("GPL");
+
+/*
+ * ACPI Helpers
+ */
+static int write_acpi_int(acpi_handle handle, const char *method, int val,
+                         struct acpi_buffer *output)
+{
+       struct acpi_object_list params;
+       union acpi_object in_obj;
+       acpi_status status;
+
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = val;
+
+       status = acpi_evaluate_object(handle, (char *)method, &params, output);
+       return (status == AE_OK ? 0 : -1);
+}
+
+static int read_acpi_int(acpi_handle handle, const char *method, int *val)
+{
+       acpi_status status;
+       unsigned long long result;
+
+       status = acpi_evaluate_integer(handle, (char *)method, NULL, &result);
+       if (ACPI_FAILURE(status)) {
+               *val = -1;
+               return -1;
+       } else {
+               *val = result;
+               return 0;
+       }
+}
+
+static int set_acpi(int cm, int value)
+{
+       if (ehotk->cm_supported & (0x1 << cm)) {
+               const char *method = cm_setv[cm];
+               if (method == NULL)
+                       return -ENODEV;
+               if (write_acpi_int(ehotk->handle, method, value, NULL))
+                       printk(EEEPC_WARNING "Error writing %s\n", method);
+       }
+       return 0;
+}
+
+static int get_acpi(int cm)
+{
+       int value = -1;
+       if ((ehotk->cm_supported & (0x1 << cm))) {
+               const char *method = cm_getv[cm];
+               if (method == NULL)
+                       return -ENODEV;
+               if (read_acpi_int(ehotk->handle, method, &value))
+                       printk(EEEPC_WARNING "Error reading %s\n", method);
+       }
+       return value;
+}
+
+/*
+ * Backlight
+ */
+static int read_brightness(struct backlight_device *bd)
+{
+       return get_acpi(CM_ASL_PANELBRIGHT);
+}
+
+static int set_brightness(struct backlight_device *bd, int value)
+{
+       value = max(0, min(15, value));
+       return set_acpi(CM_ASL_PANELBRIGHT, value);
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+       return set_brightness(bd, bd->props.brightness);
+}
+
+/*
+ * Rfkill helpers
+ */
+
+static int eeepc_wlan_rfkill_set(void *data, enum rfkill_state state)
+{
+       if (state == RFKILL_STATE_SOFT_BLOCKED)
+               return set_acpi(CM_ASL_WLAN, 0);
+       else
+               return set_acpi(CM_ASL_WLAN, 1);
+}
+
+static int eeepc_wlan_rfkill_state(void *data, enum rfkill_state *state)
+{
+       if (get_acpi(CM_ASL_WLAN) == 1)
+               *state = RFKILL_STATE_UNBLOCKED;
+       else
+               *state = RFKILL_STATE_SOFT_BLOCKED;
+       return 0;
+}
+
+static int eeepc_bluetooth_rfkill_set(void *data, enum rfkill_state state)
+{
+       if (state == RFKILL_STATE_SOFT_BLOCKED)
+               return set_acpi(CM_ASL_BLUETOOTH, 0);
+       else
+               return set_acpi(CM_ASL_BLUETOOTH, 1);
+}
+
+static int eeepc_bluetooth_rfkill_state(void *data, enum rfkill_state *state)
+{
+       if (get_acpi(CM_ASL_BLUETOOTH) == 1)
+               *state = RFKILL_STATE_UNBLOCKED;
+       else
+               *state = RFKILL_STATE_SOFT_BLOCKED;
+       return 0;
+}
+
+/*
+ * Sys helpers
+ */
+static int parse_arg(const char *buf, unsigned long count, int *val)
+{
+       if (!count)
+               return 0;
+       if (sscanf(buf, "%i", val) != 1)
+               return -EINVAL;
+       return count;
+}
+
+static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0)
+               set_acpi(cm, value);
+       return rv;
+}
+
+static ssize_t show_sys_acpi(int cm, char *buf)
+{
+       return sprintf(buf, "%d\n", get_acpi(cm));
+}
+
+#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm)                           \
+       static ssize_t show_##_name(struct device *dev,                 \
+                                   struct device_attribute *attr,      \
+                                   char *buf)                          \
+       {                                                               \
+               return show_sys_acpi(_cm, buf);                         \
+       }                                                               \
+       static ssize_t store_##_name(struct device *dev,                \
+                                    struct device_attribute *attr,     \
+                                    const char *buf, size_t count)     \
+       {                                                               \
+               return store_sys_acpi(_cm, buf, count);                 \
+       }                                                               \
+       static struct device_attribute dev_attr_##_name = {             \
+               .attr = {                                               \
+                       .name = __stringify(_name),                     \
+                       .mode = 0644 },                                 \
+               .show   = show_##_name,                                 \
+               .store  = store_##_name,                                \
+       }
+
+EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
+EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
+EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
+
+static struct attribute *platform_attributes[] = {
+       &dev_attr_camera.attr,
+       &dev_attr_cardr.attr,
+       &dev_attr_disp.attr,
+       NULL
+};
+
+static struct attribute_group platform_attribute_group = {
+       .attrs = platform_attributes
+};
+
+/*
+ * Hotkey functions
+ */
+static struct key_entry *eepc_get_entry_by_scancode(int code)
+{
+       struct key_entry *key;
+
+       for (key = eeepc_keymap; key->type != KE_END; key++)
+               if (code == key->code)
+                       return key;
+
+       return NULL;
+}
+
+static struct key_entry *eepc_get_entry_by_keycode(int code)
+{
+       struct key_entry *key;
+
+       for (key = eeepc_keymap; key->type != KE_END; key++)
+               if (code == key->keycode && key->type == KE_KEY)
+                       return key;
+
+       return NULL;
+}
+
+static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+       struct key_entry *key = eepc_get_entry_by_scancode(scancode);
+
+       if (key && key->type == KE_KEY) {
+               *keycode = key->keycode;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+       struct key_entry *key;
+       int old_keycode;
+
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       key = eepc_get_entry_by_scancode(scancode);
+       if (key && key->type == KE_KEY) {
+               old_keycode = key->keycode;
+               key->keycode = keycode;
+               set_bit(keycode, dev->keybit);
+               if (!eepc_get_entry_by_keycode(old_keycode))
+                       clear_bit(old_keycode, dev->keybit);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int eeepc_hotk_check(void)
+{
+       const struct key_entry *key;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       int result;
+
+       result = acpi_bus_get_status(ehotk->device);
+       if (result)
+               return result;
+       if (ehotk->device->status.present) {
+               if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
+                                   &buffer)) {
+                       printk(EEEPC_ERR "Hotkey initialization failed\n");
+                       return -ENODEV;
+               } else {
+                       printk(EEEPC_NOTICE "Hotkey init flags 0x%x\n",
+                              ehotk->init_flag);
+               }
+               /* get control methods supported */
+               if (read_acpi_int(ehotk->handle, "CMSG"
+                                  , &ehotk->cm_supported)) {
+                       printk(EEEPC_ERR
+                              "Get control methods supported failed\n");
+                       return -ENODEV;
+               } else {
+                       printk(EEEPC_INFO
+                              "Get control methods supported: 0x%x\n",
+                              ehotk->cm_supported);
+               }
+               ehotk->inputdev = input_allocate_device();
+               if (!ehotk->inputdev) {
+                       printk(EEEPC_INFO "Unable to allocate input device\n");
+                       return 0;
+               }
+               ehotk->inputdev->name = "Asus EeePC extra buttons";
+               ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
+               ehotk->inputdev->id.bustype = BUS_HOST;
+               ehotk->inputdev->getkeycode = eeepc_getkeycode;
+               ehotk->inputdev->setkeycode = eeepc_setkeycode;
+
+               for (key = eeepc_keymap; key->type != KE_END; key++) {
+                       switch (key->type) {
+                       case KE_KEY:
+                               set_bit(EV_KEY, ehotk->inputdev->evbit);
+                               set_bit(key->keycode, ehotk->inputdev->keybit);
+                               break;
+                       }
+               }
+               result = input_register_device(ehotk->inputdev);
+               if (result) {
+                       printk(EEEPC_INFO "Unable to register input device\n");
+                       input_free_device(ehotk->inputdev);
+                       return 0;
+               }
+       } else {
+               printk(EEEPC_ERR "Hotkey device not present, aborting\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void notify_brn(void)
+{
+       struct backlight_device *bd = eeepc_backlight_device;
+       bd->props.brightness = read_brightness(bd);
+}
+
+static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
+{
+       static struct key_entry *key;
+       if (!ehotk)
+               return;
+       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
+               notify_brn();
+       acpi_bus_generate_proc_event(ehotk->device, event,
+                                    ehotk->event_count[event % 128]++);
+       if (ehotk->inputdev) {
+               key = eepc_get_entry_by_scancode(event);
+               if (key) {
+                       switch (key->type) {
+                       case KE_KEY:
+                               input_report_key(ehotk->inputdev, key->keycode,
+                                                1);
+                               input_sync(ehotk->inputdev);
+                               input_report_key(ehotk->inputdev, key->keycode,
+                                                0);
+                               input_sync(ehotk->inputdev);
+                               break;
+                       }
+               }
+       }
+}
+
+static int eeepc_hotk_add(struct acpi_device *device)
+{
+       acpi_status status = AE_OK;
+       int result;
+
+       if (!device)
+                return -EINVAL;
+       printk(EEEPC_NOTICE EEEPC_HOTK_NAME "\n");
+       ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
+       if (!ehotk)
+               return -ENOMEM;
+       ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
+       ehotk->handle = device->handle;
+       strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
+       strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
+       device->driver_data = ehotk;
+       ehotk->device = device;
+       result = eeepc_hotk_check();
+       if (result)
+               goto end;
+       status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
+                                            eeepc_hotk_notify, ehotk);
+       if (ACPI_FAILURE(status))
+               printk(EEEPC_ERR "Error installing notify handler\n");
+
+       if (get_acpi(CM_ASL_WLAN) != -1) {
+               ehotk->eeepc_wlan_rfkill = rfkill_allocate(&device->dev,
+                                                          RFKILL_TYPE_WLAN);
+
+               if (!ehotk->eeepc_wlan_rfkill)
+                       goto end;
+
+               ehotk->eeepc_wlan_rfkill->name = "eeepc-wlan";
+               ehotk->eeepc_wlan_rfkill->toggle_radio = eeepc_wlan_rfkill_set;
+               ehotk->eeepc_wlan_rfkill->get_state = eeepc_wlan_rfkill_state;
+               if (get_acpi(CM_ASL_WLAN) == 1)
+                       ehotk->eeepc_wlan_rfkill->state =
+                               RFKILL_STATE_UNBLOCKED;
+               else
+                       ehotk->eeepc_wlan_rfkill->state =
+                               RFKILL_STATE_SOFT_BLOCKED;
+               rfkill_register(ehotk->eeepc_wlan_rfkill);
+       }
+
+       if (get_acpi(CM_ASL_BLUETOOTH) != -1) {
+               ehotk->eeepc_bluetooth_rfkill =
+                       rfkill_allocate(&device->dev, RFKILL_TYPE_BLUETOOTH);
+
+               if (!ehotk->eeepc_bluetooth_rfkill)
+                       goto end;
+
+               ehotk->eeepc_bluetooth_rfkill->name = "eeepc-bluetooth";
+               ehotk->eeepc_bluetooth_rfkill->toggle_radio =
+                       eeepc_bluetooth_rfkill_set;
+               ehotk->eeepc_bluetooth_rfkill->get_state =
+                       eeepc_bluetooth_rfkill_state;
+               if (get_acpi(CM_ASL_BLUETOOTH) == 1)
+                       ehotk->eeepc_bluetooth_rfkill->state =
+                               RFKILL_STATE_UNBLOCKED;
+               else
+                       ehotk->eeepc_bluetooth_rfkill->state =
+                               RFKILL_STATE_SOFT_BLOCKED;
+               rfkill_register(ehotk->eeepc_bluetooth_rfkill);
+       }
+
+ end:
+       if (result) {
+               kfree(ehotk);
+               ehotk = NULL;
+       }
+       return result;
+}
+
+static int eeepc_hotk_remove(struct acpi_device *device, int type)
+{
+       acpi_status status = 0;
+
+       if (!device || !acpi_driver_data(device))
+                return -EINVAL;
+       status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
+                                           eeepc_hotk_notify);
+       if (ACPI_FAILURE(status))
+               printk(EEEPC_ERR "Error removing notify handler\n");
+       kfree(ehotk);
+       return 0;
+}
+
+/*
+ * Hwmon
+ */
+static int eeepc_get_fan_pwm(void)
+{
+       int value = 0;
+
+       read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
+       value = value * 255 / 100;
+       return (value);
+}
+
+static void eeepc_set_fan_pwm(int value)
+{
+       value = SENSORS_LIMIT(value, 0, 255);
+       value = value * 100 / 255;
+       ec_write(EEEPC_EC_SC02, value);
+}
+
+static int eeepc_get_fan_rpm(void)
+{
+       int high = 0;
+       int low = 0;
+
+       read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
+       read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
+       return (high << 8 | low);
+}
+
+static int eeepc_get_fan_ctrl(void)
+{
+       int value = 0;
+
+       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
+       return ((value & 0x02 ? 1 : 0));
+}
+
+static void eeepc_set_fan_ctrl(int manual)
+{
+       int value = 0;
+
+       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
+       if (manual)
+               value |= 0x02;
+       else
+               value &= ~0x02;
+       ec_write(EEEPC_EC_SFB3, value);
+}
+
+static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
+{
+       int rv, value;
+
+       rv = parse_arg(buf, count, &value);
+       if (rv > 0)
+               set(value);
+       return rv;
+}
+
+static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
+{
+       return sprintf(buf, "%d\n", get());
+}
+
+#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _set, _get)             \
+       static ssize_t show_##_name(struct device *dev,                 \
+                                   struct device_attribute *attr,      \
+                                   char *buf)                          \
+       {                                                               \
+               return show_sys_hwmon(_set, buf);                       \
+       }                                                               \
+       static ssize_t store_##_name(struct device *dev,                \
+                                    struct device_attribute *attr,     \
+                                    const char *buf, size_t count)     \
+       {                                                               \
+               return store_sys_hwmon(_get, buf, count);               \
+       }                                                               \
+       static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
+
+EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
+EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
+                        eeepc_get_fan_pwm, eeepc_set_fan_pwm);
+EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
+                        eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
+
+static ssize_t
+show_name(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "eeepc\n");
+}
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+static struct attribute *hwmon_attributes[] = {
+       &sensor_dev_attr_pwm1.dev_attr.attr,
+       &sensor_dev_attr_fan1_input.dev_attr.attr,
+       &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+       &sensor_dev_attr_name.dev_attr.attr,
+       NULL
+};
+
+static struct attribute_group hwmon_attribute_group = {
+       .attrs = hwmon_attributes
+};
+
+/*
+ * exit/init
+ */
+static void eeepc_backlight_exit(void)
+{
+       if (eeepc_backlight_device)
+               backlight_device_unregister(eeepc_backlight_device);
+       if (ehotk->inputdev)
+               input_unregister_device(ehotk->inputdev);
+       if (ehotk->eeepc_wlan_rfkill)
+               rfkill_unregister(ehotk->eeepc_wlan_rfkill);
+       if (ehotk->eeepc_bluetooth_rfkill)
+               rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
+       eeepc_backlight_device = NULL;
+}
+
+static void eeepc_hwmon_exit(void)
+{
+       struct device *hwmon;
+
+       hwmon = eeepc_hwmon_device;
+       if (!hwmon)
+               return ;
+       sysfs_remove_group(&hwmon->kobj,
+                          &hwmon_attribute_group);
+       hwmon_device_unregister(hwmon);
+       eeepc_hwmon_device = NULL;
+}
+
+static void __exit eeepc_laptop_exit(void)
+{
+       eeepc_backlight_exit();
+       eeepc_hwmon_exit();
+       acpi_bus_unregister_driver(&eeepc_hotk_driver);
+       sysfs_remove_group(&platform_device->dev.kobj,
+                          &platform_attribute_group);
+       platform_device_unregister(platform_device);
+       platform_driver_unregister(&platform_driver);
+}
+
+static int eeepc_backlight_init(struct device *dev)
+{
+       struct backlight_device *bd;
+
+       bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
+                                      NULL, &eeepcbl_ops);
+       if (IS_ERR(bd)) {
+               printk(EEEPC_ERR
+                      "Could not register eeepc backlight device\n");
+               eeepc_backlight_device = NULL;
+               return PTR_ERR(bd);
+       }
+       eeepc_backlight_device = bd;
+       bd->props.max_brightness = 15;
+       bd->props.brightness = read_brightness(NULL);
+       bd->props.power = FB_BLANK_UNBLANK;
+       backlight_update_status(bd);
+       return 0;
+}
+
+static int eeepc_hwmon_init(struct device *dev)
+{
+       struct device *hwmon;
+       int result;
+
+       hwmon = hwmon_device_register(dev);
+       if (IS_ERR(hwmon)) {
+               printk(EEEPC_ERR
+                      "Could not register eeepc hwmon device\n");
+               eeepc_hwmon_device = NULL;
+               return PTR_ERR(hwmon);
+       }
+       eeepc_hwmon_device = hwmon;
+       result = sysfs_create_group(&hwmon->kobj,
+                                   &hwmon_attribute_group);
+       if (result)
+               eeepc_hwmon_exit();
+       return result;
+}
+
+static int __init eeepc_laptop_init(void)
+{
+       struct device *dev;
+       int result;
+
+       if (acpi_disabled)
+               return -ENODEV;
+       result = acpi_bus_register_driver(&eeepc_hotk_driver);
+       if (result < 0)
+               return result;
+       if (!ehotk) {
+               acpi_bus_unregister_driver(&eeepc_hotk_driver);
+               return -ENODEV;
+       }
+       dev = acpi_get_physical_device(ehotk->device->handle);
+
+       if (!acpi_video_backlight_support()) {
+               result = eeepc_backlight_init(dev);
+               if (result)
+                       goto fail_backlight;
+       } else
+               printk(EEEPC_INFO "Backlight controlled by ACPI video "
+                      "driver\n");
+
+       result = eeepc_hwmon_init(dev);
+       if (result)
+               goto fail_hwmon;
+       /* Register platform stuff */
+       result = platform_driver_register(&platform_driver);
+       if (result)
+               goto fail_platform_driver;
+       platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
+       if (!platform_device) {
+               result = -ENOMEM;
+               goto fail_platform_device1;
+       }
+       result = platform_device_add(platform_device);
+       if (result)
+               goto fail_platform_device2;
+       result = sysfs_create_group(&platform_device->dev.kobj,
+                                   &platform_attribute_group);
+       if (result)
+               goto fail_sysfs;
+       return 0;
+fail_sysfs:
+       platform_device_del(platform_device);
+fail_platform_device2:
+       platform_device_put(platform_device);
+fail_platform_device1:
+       platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+       eeepc_hwmon_exit();
+fail_hwmon:
+       eeepc_backlight_exit();
+fail_backlight:
+       return result;
+}
+
+module_init(eeepc_laptop_init);
+module_exit(eeepc_laptop_exit);
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
new file mode 100644 (file)
index 0000000..a7dd3e9
--- /dev/null
@@ -0,0 +1,1126 @@
+/*-*-linux-c-*-*/
+
+/*
+  Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au>
+  Copyright (C) 2008 Peter Gruber <nokos@gmx.net>
+  Based on earlier work:
+    Copyright (C) 2003 Shane Spencer <shane@bogomip.com>
+    Adrian Yee <brewt-fujitsu@brewt.org>
+
+  Templated from msi-laptop.c and thinkpad_acpi.c which is copyright
+  by its respective authors.
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+ */
+
+/*
+ * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional
+ * features made available on a range of Fujitsu laptops including the
+ * P2xxx/P5xxx/S6xxx/S7xxx series.
+ *
+ * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/;
+ * others may be added at a later date.
+ *
+ *   lcd_level - Screen brightness: contains a single integer in the
+ *   range 0..7. (rw)
+ *
+ * In addition to these platform device attributes the driver
+ * registers itself in the Linux backlight control subsystem and is
+ * available to userspace under /sys/class/backlight/fujitsu-laptop/.
+ *
+ * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are
+ * also supported by this driver.
+ *
+ * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and
+ * P8010.  It should work on most P-series and S-series Lifebooks, but
+ * YMMV.
+ *
+ * The module parameter use_alt_lcd_levels switches between different ACPI
+ * brightness controls which are used by different Fujitsu laptops.  In most
+ * cases the correct method is automatically detected. "use_alt_lcd_levels=1"
+ * is applicable for a Fujitsu Lifebook S6410 if autodetection fails.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/backlight.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/video_output.h>
+#include <linux/platform_device.h>
+
+#define FUJITSU_DRIVER_VERSION "0.4.3"
+
+#define FUJITSU_LCD_N_LEVELS 8
+
+#define ACPI_FUJITSU_CLASS              "fujitsu"
+#define ACPI_FUJITSU_HID                "FUJ02B1"
+#define ACPI_FUJITSU_DRIVER_NAME       "Fujitsu laptop FUJ02B1 ACPI brightness driver"
+#define ACPI_FUJITSU_DEVICE_NAME        "Fujitsu FUJ02B1"
+#define ACPI_FUJITSU_HOTKEY_HID        "FUJ02E3"
+#define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
+#define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3"
+
+#define ACPI_FUJITSU_NOTIFY_CODE1     0x80
+
+#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS     0x86
+#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS     0x87
+
+/* Hotkey details */
+#define KEY1_CODE      0x410   /* codes for the keys in the GIRB register */
+#define KEY2_CODE      0x411
+#define KEY3_CODE      0x412
+#define KEY4_CODE      0x413
+
+#define MAX_HOTKEY_RINGBUFFER_SIZE 100
+#define RINGBUFFERSIZE 40
+
+/* Debugging */
+#define FUJLAPTOP_LOG     ACPI_FUJITSU_HID ": "
+#define FUJLAPTOP_ERR     KERN_ERR FUJLAPTOP_LOG
+#define FUJLAPTOP_NOTICE   KERN_NOTICE FUJLAPTOP_LOG
+#define FUJLAPTOP_INFO    KERN_INFO FUJLAPTOP_LOG
+#define FUJLAPTOP_DEBUG    KERN_DEBUG FUJLAPTOP_LOG
+
+#define FUJLAPTOP_DBG_ALL        0xffff
+#define FUJLAPTOP_DBG_ERROR      0x0001
+#define FUJLAPTOP_DBG_WARN       0x0002
+#define FUJLAPTOP_DBG_INFO       0x0004
+#define FUJLAPTOP_DBG_TRACE      0x0008
+
+#define dbg_printk(a_dbg_level, format, arg...) \
+       do { if (dbg_level & a_dbg_level) \
+               printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \
+       } while (0)
+#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
+#define vdbg_printk(a_dbg_level, format, arg...) \
+       dbg_printk(a_dbg_level, format, ## arg)
+#else
+#define vdbg_printk(a_dbg_level, format, arg...)
+#endif
+
+/* Device controlling the backlight and associated keys */
+struct fujitsu_t {
+       acpi_handle acpi_handle;
+       struct acpi_device *dev;
+       struct input_dev *input;
+       char phys[32];
+       struct backlight_device *bl_device;
+       struct platform_device *pf_device;
+       int keycode1, keycode2, keycode3, keycode4;
+
+       unsigned int max_brightness;
+       unsigned int brightness_changed;
+       unsigned int brightness_level;
+};
+
+static struct fujitsu_t *fujitsu;
+static int use_alt_lcd_levels = -1;
+static int disable_brightness_keys = -1;
+static int disable_brightness_adjust = -1;
+
+/* Device used to access other hotkeys on the laptop */
+struct fujitsu_hotkey_t {
+       acpi_handle acpi_handle;
+       struct acpi_device *dev;
+       struct input_dev *input;
+       char phys[32];
+       struct platform_device *pf_device;
+       struct kfifo *fifo;
+       spinlock_t fifo_lock;
+
+       unsigned int irb;       /* info about the pressed buttons */
+};
+
+static struct fujitsu_hotkey_t *fujitsu_hotkey;
+
+static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
+                                      void *data);
+
+#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
+static u32 dbg_level = 0x03;
+#endif
+
+static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data);
+
+/* Hardware access for LCD brightness control */
+
+static int set_lcd_level(int level)
+{
+       acpi_status status = AE_OK;
+       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+       struct acpi_object_list arg_list = { 1, &arg0 };
+       acpi_handle handle = NULL;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
+                   level);
+
+       if (level < 0 || level >= fujitsu->max_brightness)
+               return -EINVAL;
+
+       if (!fujitsu)
+               return -EINVAL;
+
+       status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle);
+       if (ACPI_FAILURE(status)) {
+               vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n");
+               return -ENODEV;
+       }
+
+       arg0.integer.value = level;
+
+       status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return 0;
+}
+
+static int set_lcd_level_alt(int level)
+{
+       acpi_status status = AE_OK;
+       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+       struct acpi_object_list arg_list = { 1, &arg0 };
+       acpi_handle handle = NULL;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
+                   level);
+
+       if (level < 0 || level >= fujitsu->max_brightness)
+               return -EINVAL;
+
+       if (!fujitsu)
+               return -EINVAL;
+
+       status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle);
+       if (ACPI_FAILURE(status)) {
+               vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n");
+               return -ENODEV;
+       }
+
+       arg0.integer.value = level;
+
+       status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return 0;
+}
+
+static int get_lcd_level(void)
+{
+       unsigned long long state = 0;
+       acpi_status status = AE_OK;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
+
+       status =
+           acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state);
+       if (status < 0)
+               return status;
+
+       fujitsu->brightness_level = state & 0x0fffffff;
+
+       if (state & 0x80000000)
+               fujitsu->brightness_changed = 1;
+       else
+               fujitsu->brightness_changed = 0;
+
+       return fujitsu->brightness_level;
+}
+
+static int get_max_brightness(void)
+{
+       unsigned long long state = 0;
+       acpi_status status = AE_OK;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
+
+       status =
+           acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state);
+       if (status < 0)
+               return status;
+
+       fujitsu->max_brightness = state;
+
+       return fujitsu->max_brightness;
+}
+
+static int get_lcd_level_alt(void)
+{
+       unsigned long long state = 0;
+       acpi_status status = AE_OK;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLS\n");
+
+       status =
+           acpi_evaluate_integer(fujitsu->acpi_handle, "GBLS", NULL, &state);
+       if (status < 0)
+               return status;
+
+       fujitsu->brightness_level = state & 0x0fffffff;
+
+       if (state & 0x80000000)
+               fujitsu->brightness_changed = 1;
+       else
+               fujitsu->brightness_changed = 0;
+
+       return fujitsu->brightness_level;
+}
+
+/* Backlight device stuff */
+
+static int bl_get_brightness(struct backlight_device *b)
+{
+       if (use_alt_lcd_levels)
+               return get_lcd_level_alt();
+       else
+               return get_lcd_level();
+}
+
+static int bl_update_status(struct backlight_device *b)
+{
+       if (use_alt_lcd_levels)
+               return set_lcd_level_alt(b->props.brightness);
+       else
+               return set_lcd_level(b->props.brightness);
+}
+
+static struct backlight_ops fujitsubl_ops = {
+       .get_brightness = bl_get_brightness,
+       .update_status = bl_update_status,
+};
+
+/* Platform LCD brightness device */
+
+static ssize_t
+show_max_brightness(struct device *dev,
+                   struct device_attribute *attr, char *buf)
+{
+
+       int ret;
+
+       ret = get_max_brightness();
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t
+show_brightness_changed(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+
+       int ret;
+
+       ret = fujitsu->brightness_changed;
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t show_lcd_level(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+
+       int ret;
+
+       if (use_alt_lcd_levels)
+               ret = get_lcd_level_alt();
+       else
+               ret = get_lcd_level();
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t store_lcd_level(struct device *dev,
+                              struct device_attribute *attr, const char *buf,
+                              size_t count)
+{
+
+       int level, ret;
+
+       if (sscanf(buf, "%i", &level) != 1
+           || (level < 0 || level >= fujitsu->max_brightness))
+               return -EINVAL;
+
+       if (use_alt_lcd_levels)
+               ret = set_lcd_level_alt(level);
+       else
+               ret = set_lcd_level(level);
+       if (ret < 0)
+               return ret;
+
+       if (use_alt_lcd_levels)
+               ret = get_lcd_level_alt();
+       else
+               ret = get_lcd_level();
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+/* Hardware access for hotkey device */
+
+static int get_irb(void)
+{
+       unsigned long long state = 0;
+       acpi_status status = AE_OK;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "Get irb\n");
+
+       status =
+           acpi_evaluate_integer(fujitsu_hotkey->acpi_handle, "GIRB", NULL,
+                                 &state);
+       if (status < 0)
+               return status;
+
+       fujitsu_hotkey->irb = state;
+
+       return fujitsu_hotkey->irb;
+}
+
+static ssize_t
+ignore_store(struct device *dev,
+            struct device_attribute *attr, const char *buf, size_t count)
+{
+       return count;
+}
+
+static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store);
+static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed,
+                  ignore_store);
+static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
+
+static struct attribute *fujitsupf_attributes[] = {
+       &dev_attr_brightness_changed.attr,
+       &dev_attr_max_brightness.attr,
+       &dev_attr_lcd_level.attr,
+       NULL
+};
+
+static struct attribute_group fujitsupf_attribute_group = {
+       .attrs = fujitsupf_attributes
+};
+
+static struct platform_driver fujitsupf_driver = {
+       .driver = {
+                  .name = "fujitsu-laptop",
+                  .owner = THIS_MODULE,
+                  }
+};
+
+static void dmi_check_cb_common(const struct dmi_system_id *id)
+{
+       acpi_handle handle;
+       int have_blnf;
+       printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n",
+              id->ident);
+       have_blnf = ACPI_SUCCESS
+           (acpi_get_handle(NULL, "\\_SB.PCI0.GFX0.LCD.BLNF", &handle));
+       if (use_alt_lcd_levels == -1) {
+               vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detecting usealt\n");
+               use_alt_lcd_levels = 1;
+       }
+       if (disable_brightness_keys == -1) {
+               vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                           "auto-detecting disable_keys\n");
+               disable_brightness_keys = have_blnf ? 1 : 0;
+       }
+       if (disable_brightness_adjust == -1) {
+               vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                           "auto-detecting disable_adjust\n");
+               disable_brightness_adjust = have_blnf ? 0 : 1;
+       }
+}
+
+static int dmi_check_cb_s6410(const struct dmi_system_id *id)
+{
+       dmi_check_cb_common(id);
+       fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
+       fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
+       return 0;
+}
+
+static int dmi_check_cb_s6420(const struct dmi_system_id *id)
+{
+       dmi_check_cb_common(id);
+       fujitsu->keycode1 = KEY_SCREENLOCK;     /* "Lock" */
+       fujitsu->keycode2 = KEY_HELP;   /* "Mobility Center" */
+       return 0;
+}
+
+static int dmi_check_cb_p8010(const struct dmi_system_id *id)
+{
+       dmi_check_cb_common(id);
+       fujitsu->keycode1 = KEY_HELP;   /* "Support" */
+       fujitsu->keycode3 = KEY_SWITCHVIDEOMODE;        /* "Presentation" */
+       fujitsu->keycode4 = KEY_WWW;    /* "Internet" */
+       return 0;
+}
+
+static struct dmi_system_id fujitsu_dmi_table[] = {
+       {
+        .ident = "Fujitsu Siemens S6410",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"),
+                    },
+        .callback = dmi_check_cb_s6410},
+       {
+        .ident = "Fujitsu Siemens S6420",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"),
+                    },
+        .callback = dmi_check_cb_s6420},
+       {
+        .ident = "Fujitsu LifeBook P8010",
+        .matches = {
+                    DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                    DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"),
+                    },
+        .callback = dmi_check_cb_p8010},
+       {}
+};
+
+/* ACPI device for LCD brightness control */
+
+static int acpi_fujitsu_add(struct acpi_device *device)
+{
+       acpi_status status;
+       acpi_handle handle;
+       int result = 0;
+       int state = 0;
+       struct input_dev *input;
+       int error;
+
+       if (!device)
+               return -EINVAL;
+
+       fujitsu->acpi_handle = device->handle;
+       sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
+       device->driver_data = fujitsu;
+
+       status = acpi_install_notify_handler(device->handle,
+                                            ACPI_DEVICE_NOTIFY,
+                                            acpi_fujitsu_notify, fujitsu);
+
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR "Error installing notify handler\n");
+               error = -ENODEV;
+               goto err_stop;
+       }
+
+       fujitsu->input = input = input_allocate_device();
+       if (!input) {
+               error = -ENOMEM;
+               goto err_uninstall_notify;
+       }
+
+       snprintf(fujitsu->phys, sizeof(fujitsu->phys),
+                "%s/video/input0", acpi_device_hid(device));
+
+       input->name = acpi_device_name(device);
+       input->phys = fujitsu->phys;
+       input->id.bustype = BUS_HOST;
+       input->id.product = 0x06;
+       input->dev.parent = &device->dev;
+       input->evbit[0] = BIT(EV_KEY);
+       set_bit(KEY_BRIGHTNESSUP, input->keybit);
+       set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+       set_bit(KEY_UNKNOWN, input->keybit);
+
+       error = input_register_device(input);
+       if (error)
+               goto err_free_input_dev;
+
+       result = acpi_bus_get_power(fujitsu->acpi_handle, &state);
+       if (result) {
+               printk(KERN_ERR "Error reading power state\n");
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+              acpi_device_name(device), acpi_device_bid(device),
+              !device->power.state ? "on" : "off");
+
+       fujitsu->dev = device;
+
+       if (ACPI_SUCCESS
+           (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
+               vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
+               if (ACPI_FAILURE
+                   (acpi_evaluate_object
+                    (device->handle, METHOD_NAME__INI, NULL, NULL)))
+                       printk(KERN_ERR "_INI Method failed\n");
+       }
+
+       /* do config (detect defaults) */
+       use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0;
+       disable_brightness_keys = disable_brightness_keys == 1 ? 1 : 0;
+       disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0;
+       vdbg_printk(FUJLAPTOP_DBG_INFO,
+                   "config: [alt interface: %d], [key disable: %d], [adjust disable: %d]\n",
+                   use_alt_lcd_levels, disable_brightness_keys,
+                   disable_brightness_adjust);
+
+       if (get_max_brightness() <= 0)
+               fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
+       if (use_alt_lcd_levels)
+               get_lcd_level_alt();
+       else
+               get_lcd_level();
+
+       return result;
+
+end:
+err_free_input_dev:
+       input_free_device(input);
+err_uninstall_notify:
+       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+                                  acpi_fujitsu_notify);
+err_stop:
+
+       return result;
+}
+
+static int acpi_fujitsu_remove(struct acpi_device *device, int type)
+{
+       acpi_status status;
+       struct fujitsu_t *fujitsu = NULL;
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       fujitsu = acpi_driver_data(device);
+
+       status = acpi_remove_notify_handler(fujitsu->acpi_handle,
+                                           ACPI_DEVICE_NOTIFY,
+                                           acpi_fujitsu_notify);
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       fujitsu->acpi_handle = NULL;
+
+       return 0;
+}
+
+/* Brightness notify */
+
+static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct input_dev *input;
+       int keycode;
+       int oldb, newb;
+
+       input = fujitsu->input;
+
+       switch (event) {
+       case ACPI_FUJITSU_NOTIFY_CODE1:
+               keycode = 0;
+               oldb = fujitsu->brightness_level;
+               get_lcd_level();  /* the alt version always yields changed */
+               newb = fujitsu->brightness_level;
+
+               vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                           "brightness button event [%i -> %i (%i)]\n",
+                           oldb, newb, fujitsu->brightness_changed);
+
+               if (oldb == newb && fujitsu->brightness_changed) {
+                       keycode = 0;
+                       if (disable_brightness_keys != 1) {
+                               if (oldb == 0) {
+                                       acpi_bus_generate_proc_event
+                                           (fujitsu->dev,
+                                            ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS,
+                                            0);
+                                       keycode = KEY_BRIGHTNESSDOWN;
+                               } else if (oldb ==
+                                          (fujitsu->max_brightness) - 1) {
+                                       acpi_bus_generate_proc_event
+                                           (fujitsu->dev,
+                                            ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS,
+                                            0);
+                                       keycode = KEY_BRIGHTNESSUP;
+                               }
+                       }
+               } else if (oldb < newb) {
+                       if (disable_brightness_adjust != 1) {
+                               if (use_alt_lcd_levels)
+                                       set_lcd_level_alt(newb);
+                               else
+                                       set_lcd_level(newb);
+                       }
+                       if (disable_brightness_keys != 1) {
+                               acpi_bus_generate_proc_event(fujitsu->dev,
+                                       ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
+                               keycode = KEY_BRIGHTNESSUP;
+                       }
+               } else if (oldb > newb) {
+                       if (disable_brightness_adjust != 1) {
+                               if (use_alt_lcd_levels)
+                                       set_lcd_level_alt(newb);
+                               else
+                                       set_lcd_level(newb);
+                       }
+                       if (disable_brightness_keys != 1) {
+                               acpi_bus_generate_proc_event(fujitsu->dev,
+                                       ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
+                               keycode = KEY_BRIGHTNESSDOWN;
+                       }
+               } else {
+                       keycode = KEY_UNKNOWN;
+               }
+               break;
+       default:
+               keycode = KEY_UNKNOWN;
+               vdbg_printk(FUJLAPTOP_DBG_WARN,
+                           "unsupported event [0x%x]\n", event);
+               break;
+       }
+
+       if (keycode != 0) {
+               input_report_key(input, keycode, 1);
+               input_sync(input);
+               input_report_key(input, keycode, 0);
+               input_sync(input);
+       }
+
+       return;
+}
+
+/* ACPI device for hotkey handling */
+
+static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
+{
+       acpi_status status;
+       acpi_handle handle;
+       int result = 0;
+       int state = 0;
+       struct input_dev *input;
+       int error;
+       int i;
+
+       if (!device)
+               return -EINVAL;
+
+       fujitsu_hotkey->acpi_handle = device->handle;
+       sprintf(acpi_device_name(device), "%s",
+               ACPI_FUJITSU_HOTKEY_DEVICE_NAME);
+       sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
+       device->driver_data = fujitsu_hotkey;
+
+       status = acpi_install_notify_handler(device->handle,
+                                            ACPI_DEVICE_NOTIFY,
+                                            acpi_fujitsu_hotkey_notify,
+                                            fujitsu_hotkey);
+
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR "Error installing notify handler\n");
+               error = -ENODEV;
+               goto err_stop;
+       }
+
+       /* kfifo */
+       spin_lock_init(&fujitsu_hotkey->fifo_lock);
+       fujitsu_hotkey->fifo =
+           kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL,
+                       &fujitsu_hotkey->fifo_lock);
+       if (IS_ERR(fujitsu_hotkey->fifo)) {
+               printk(KERN_ERR "kfifo_alloc failed\n");
+               error = PTR_ERR(fujitsu_hotkey->fifo);
+               goto err_stop;
+       }
+
+       fujitsu_hotkey->input = input = input_allocate_device();
+       if (!input) {
+               error = -ENOMEM;
+               goto err_uninstall_notify;
+       }
+
+       snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys),
+                "%s/video/input0", acpi_device_hid(device));
+
+       input->name = acpi_device_name(device);
+       input->phys = fujitsu_hotkey->phys;
+       input->id.bustype = BUS_HOST;
+       input->id.product = 0x06;
+       input->dev.parent = &device->dev;
+       input->evbit[0] = BIT(EV_KEY);
+       set_bit(fujitsu->keycode1, input->keybit);
+       set_bit(fujitsu->keycode2, input->keybit);
+       set_bit(fujitsu->keycode3, input->keybit);
+       set_bit(fujitsu->keycode4, input->keybit);
+       set_bit(KEY_UNKNOWN, input->keybit);
+
+       error = input_register_device(input);
+       if (error)
+               goto err_free_input_dev;
+
+       result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state);
+       if (result) {
+               printk(KERN_ERR "Error reading power state\n");
+               goto end;
+       }
+
+       printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+              acpi_device_name(device), acpi_device_bid(device),
+              !device->power.state ? "on" : "off");
+
+       fujitsu_hotkey->dev = device;
+
+       if (ACPI_SUCCESS
+           (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
+               vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
+               if (ACPI_FAILURE
+                   (acpi_evaluate_object
+                    (device->handle, METHOD_NAME__INI, NULL, NULL)))
+                       printk(KERN_ERR "_INI Method failed\n");
+       }
+
+       i = 0;                  /* Discard hotkey ringbuffer */
+       while (get_irb() != 0 && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) ;
+       vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
+
+       return result;
+
+end:
+err_free_input_dev:
+       input_free_device(input);
+err_uninstall_notify:
+       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
+                                  acpi_fujitsu_hotkey_notify);
+       kfifo_free(fujitsu_hotkey->fifo);
+err_stop:
+
+       return result;
+}
+
+static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
+{
+       acpi_status status;
+       struct fujitsu_hotkey_t *fujitsu_hotkey = NULL;
+
+       if (!device || !acpi_driver_data(device))
+               return -EINVAL;
+
+       fujitsu_hotkey = acpi_driver_data(device);
+
+       status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle,
+                                           ACPI_DEVICE_NOTIFY,
+                                           acpi_fujitsu_hotkey_notify);
+
+       fujitsu_hotkey->acpi_handle = NULL;
+
+       kfifo_free(fujitsu_hotkey->fifo);
+
+       return 0;
+}
+
+static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event,
+                                      void *data)
+{
+       struct input_dev *input;
+       int keycode, keycode_r;
+       unsigned int irb = 1;
+       int i, status;
+
+       input = fujitsu_hotkey->input;
+
+       vdbg_printk(FUJLAPTOP_DBG_TRACE, "Hotkey event\n");
+
+       switch (event) {
+       case ACPI_FUJITSU_NOTIFY_CODE1:
+               i = 0;
+               while ((irb = get_irb()) != 0
+                      && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) {
+                       vdbg_printk(FUJLAPTOP_DBG_TRACE, "GIRB result [%x]\n",
+                                   irb);
+
+                       switch (irb & 0x4ff) {
+                       case KEY1_CODE:
+                               keycode = fujitsu->keycode1;
+                               break;
+                       case KEY2_CODE:
+                               keycode = fujitsu->keycode2;
+                               break;
+                       case KEY3_CODE:
+                               keycode = fujitsu->keycode3;
+                               break;
+                       case KEY4_CODE:
+                               keycode = fujitsu->keycode4;
+                               break;
+                       case 0:
+                               keycode = 0;
+                               break;
+                       default:
+                               vdbg_printk(FUJLAPTOP_DBG_WARN,
+                                           "Unknown GIRB result [%x]\n", irb);
+                               keycode = -1;
+                               break;
+                       }
+                       if (keycode > 0) {
+                               vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                                       "Push keycode into ringbuffer [%d]\n",
+                                       keycode);
+                               status = kfifo_put(fujitsu_hotkey->fifo,
+                                                  (unsigned char *)&keycode,
+                                                  sizeof(keycode));
+                               if (status != sizeof(keycode)) {
+                                       vdbg_printk(FUJLAPTOP_DBG_WARN,
+                                           "Could not push keycode [0x%x]\n",
+                                           keycode);
+                               } else {
+                                       input_report_key(input, keycode, 1);
+                                       input_sync(input);
+                               }
+                       } else if (keycode == 0) {
+                               while ((status =
+                                       kfifo_get
+                                       (fujitsu_hotkey->fifo, (unsigned char *)
+                                        &keycode_r,
+                                        sizeof
+                                        (keycode_r))) == sizeof(keycode_r)) {
+                                       input_report_key(input, keycode_r, 0);
+                                       input_sync(input);
+                                       vdbg_printk(FUJLAPTOP_DBG_TRACE,
+                                         "Pop keycode from ringbuffer [%d]\n",
+                                         keycode_r);
+                               }
+                       }
+               }
+
+               break;
+       default:
+               keycode = KEY_UNKNOWN;
+               vdbg_printk(FUJLAPTOP_DBG_WARN,
+                           "Unsupported event [0x%x]\n", event);
+               input_report_key(input, keycode, 1);
+               input_sync(input);
+               input_report_key(input, keycode, 0);
+               input_sync(input);
+               break;
+       }
+
+       return;
+}
+
+/* Initialization */
+
+static const struct acpi_device_id fujitsu_device_ids[] = {
+       {ACPI_FUJITSU_HID, 0},
+       {"", 0},
+};
+
+static struct acpi_driver acpi_fujitsu_driver = {
+       .name = ACPI_FUJITSU_DRIVER_NAME,
+       .class = ACPI_FUJITSU_CLASS,
+       .ids = fujitsu_device_ids,
+       .ops = {
+               .add = acpi_fujitsu_add,
+               .remove = acpi_fujitsu_remove,
+               },
+};
+
+static const struct acpi_device_id fujitsu_hotkey_device_ids[] = {
+       {ACPI_FUJITSU_HOTKEY_HID, 0},
+       {"", 0},
+};
+
+static struct acpi_driver acpi_fujitsu_hotkey_driver = {
+       .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME,
+       .class = ACPI_FUJITSU_CLASS,
+       .ids = fujitsu_hotkey_device_ids,
+       .ops = {
+               .add = acpi_fujitsu_hotkey_add,
+               .remove = acpi_fujitsu_hotkey_remove,
+               },
+};
+
+static int __init fujitsu_init(void)
+{
+       int ret, result, max_brightness;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL);
+       if (!fujitsu)
+               return -ENOMEM;
+       memset(fujitsu, 0, sizeof(struct fujitsu_t));
+       fujitsu->keycode1 = KEY_PROG1;
+       fujitsu->keycode2 = KEY_PROG2;
+       fujitsu->keycode3 = KEY_PROG3;
+       fujitsu->keycode4 = KEY_PROG4;
+       dmi_check_system(fujitsu_dmi_table);
+
+       result = acpi_bus_register_driver(&acpi_fujitsu_driver);
+       if (result < 0) {
+               ret = -ENODEV;
+               goto fail_acpi;
+       }
+
+       /* Register platform stuff */
+
+       fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1);
+       if (!fujitsu->pf_device) {
+               ret = -ENOMEM;
+               goto fail_platform_driver;
+       }
+
+       ret = platform_device_add(fujitsu->pf_device);
+       if (ret)
+               goto fail_platform_device1;
+
+       ret =
+           sysfs_create_group(&fujitsu->pf_device->dev.kobj,
+                              &fujitsupf_attribute_group);
+       if (ret)
+               goto fail_platform_device2;
+
+       /* Register backlight stuff */
+
+       if (!acpi_video_backlight_support()) {
+               fujitsu->bl_device =
+                       backlight_device_register("fujitsu-laptop", NULL, NULL,
+                                                 &fujitsubl_ops);
+               if (IS_ERR(fujitsu->bl_device))
+                       return PTR_ERR(fujitsu->bl_device);
+               max_brightness = fujitsu->max_brightness;
+               fujitsu->bl_device->props.max_brightness = max_brightness - 1;
+               fujitsu->bl_device->props.brightness = fujitsu->brightness_level;
+       }
+
+       ret = platform_driver_register(&fujitsupf_driver);
+       if (ret)
+               goto fail_backlight;
+
+       /* Register hotkey driver */
+
+       fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL);
+       if (!fujitsu_hotkey) {
+               ret = -ENOMEM;
+               goto fail_hotkey;
+       }
+       memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t));
+
+       result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver);
+       if (result < 0) {
+               ret = -ENODEV;
+               goto fail_hotkey1;
+       }
+
+       printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION
+              " successfully loaded.\n");
+
+       return 0;
+
+fail_hotkey1:
+
+       kfree(fujitsu_hotkey);
+
+fail_hotkey:
+
+       platform_driver_unregister(&fujitsupf_driver);
+
+fail_backlight:
+
+       if (fujitsu->bl_device)
+               backlight_device_unregister(fujitsu->bl_device);
+
+fail_platform_device2:
+
+       platform_device_del(fujitsu->pf_device);
+
+fail_platform_device1:
+
+       platform_device_put(fujitsu->pf_device);
+
+fail_platform_driver:
+
+       acpi_bus_unregister_driver(&acpi_fujitsu_driver);
+
+fail_acpi:
+
+       kfree(fujitsu);
+
+       return ret;
+}
+
+static void __exit fujitsu_cleanup(void)
+{
+       sysfs_remove_group(&fujitsu->pf_device->dev.kobj,
+                          &fujitsupf_attribute_group);
+       platform_device_unregister(fujitsu->pf_device);
+       platform_driver_unregister(&fujitsupf_driver);
+       if (fujitsu->bl_device)
+               backlight_device_unregister(fujitsu->bl_device);
+
+       acpi_bus_unregister_driver(&acpi_fujitsu_driver);
+
+       kfree(fujitsu);
+
+       acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver);
+
+       kfree(fujitsu_hotkey);
+
+       printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n");
+}
+
+module_init(fujitsu_init);
+module_exit(fujitsu_cleanup);
+
+module_param(use_alt_lcd_levels, uint, 0644);
+MODULE_PARM_DESC(use_alt_lcd_levels,
+                "Use alternative interface for lcd_levels (needed for Lifebook s6410).");
+module_param(disable_brightness_keys, uint, 0644);
+MODULE_PARM_DESC(disable_brightness_keys,
+                "Disable brightness keys (eg. if they are already handled by the generic ACPI_VIDEO device).");
+module_param(disable_brightness_adjust, uint, 0644);
+MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment .");
+#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
+module_param_named(debug, dbg_level, uint, 0644);
+MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
+#endif
+
+MODULE_AUTHOR("Jonathan Woithe, Peter Gruber");
+MODULE_DESCRIPTION("Fujitsu laptop extras support");
+MODULE_VERSION(FUJITSU_DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*");
+
+static struct pnp_device_id pnp_ids[] = {
+       {.id = "FUJ02bf"},
+       {.id = "FUJ02B1"},
+       {.id = "FUJ02E3"},
+       {.id = ""}
+};
+
+MODULE_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 (file)
index 0000000..4b7c24c
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * HP WMI hotkeys
+ *
+ * Copyright (C) 2008 Red Hat <mjg@redhat.com>
+ *
+ * Portions based on wistron_btns.c:
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/rfkill.h>
+#include <linux/string.h>
+
+MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>");
+MODULE_DESCRIPTION("HP laptop WMI hotkeys driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("wmi:95F24279-4D7B-4334-9387-ACCDC67EF61C");
+MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
+
+#define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C"
+#define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4"
+
+#define HPWMI_DISPLAY_QUERY 0x1
+#define HPWMI_HDDTEMP_QUERY 0x2
+#define HPWMI_ALS_QUERY 0x3
+#define HPWMI_DOCK_QUERY 0x4
+#define HPWMI_WIRELESS_QUERY 0x5
+#define HPWMI_HOTKEY_QUERY 0xc
+
+static int __init hp_wmi_bios_setup(struct platform_device *device);
+static int __exit hp_wmi_bios_remove(struct platform_device *device);
+
+struct bios_args {
+       u32 signature;
+       u32 command;
+       u32 commandtype;
+       u32 datasize;
+       u32 data;
+};
+
+struct bios_return {
+       u32 sigpass;
+       u32 return_code;
+       u32 value;
+};
+
+struct key_entry {
+       char type;              /* See KE_* below */
+       u16 code;
+       u16 keycode;
+};
+
+enum { KE_KEY, KE_SW, KE_END };
+
+static struct key_entry hp_wmi_keymap[] = {
+       {KE_SW, 0x01, SW_DOCK},
+       {KE_KEY, 0x02, KEY_BRIGHTNESSUP},
+       {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
+       {KE_KEY, 0x20e6, KEY_PROG1},
+       {KE_KEY, 0x2142, KEY_MEDIA},
+       {KE_KEY, 0x213b, KEY_INFO},
+       {KE_KEY, 0x231b, KEY_HELP},
+       {KE_END, 0}
+};
+
+static struct input_dev *hp_wmi_input_dev;
+static struct platform_device *hp_wmi_platform_dev;
+
+static struct rfkill *wifi_rfkill;
+static struct rfkill *bluetooth_rfkill;
+static struct rfkill *wwan_rfkill;
+
+static struct platform_driver hp_wmi_driver = {
+       .driver = {
+                  .name = "hp-wmi",
+                  .owner = THIS_MODULE,
+       },
+       .probe = hp_wmi_bios_setup,
+       .remove = hp_wmi_bios_remove,
+};
+
+static int hp_wmi_perform_query(int query, int write, int value)
+{
+       struct bios_return bios_return;
+       acpi_status status;
+       union acpi_object *obj;
+       struct bios_args args = {
+               .signature = 0x55434553,
+               .command = write ? 0x2 : 0x1,
+               .commandtype = query,
+               .datasize = write ? 0x4 : 0,
+               .data = value,
+       };
+       struct acpi_buffer input = { sizeof(struct bios_args), &args };
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+       status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
+
+       obj = output.pointer;
+
+       if (!obj || obj->type != ACPI_TYPE_BUFFER)
+               return -EINVAL;
+
+       bios_return = *((struct bios_return *)obj->buffer.pointer);
+       if (bios_return.return_code > 0)
+               return bios_return.return_code * -1;
+       else
+               return bios_return.value;
+}
+
+static int hp_wmi_display_state(void)
+{
+       return hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, 0);
+}
+
+static int hp_wmi_hddtemp_state(void)
+{
+       return hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, 0);
+}
+
+static int hp_wmi_als_state(void)
+{
+       return hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, 0);
+}
+
+static int hp_wmi_dock_state(void)
+{
+       return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
+}
+
+static int hp_wmi_wifi_set(void *data, enum rfkill_state state)
+{
+       if (state)
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x101);
+       else
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x100);
+}
+
+static int hp_wmi_bluetooth_set(void *data, enum rfkill_state state)
+{
+       if (state)
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x202);
+       else
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x200);
+}
+
+static int hp_wmi_wwan_set(void *data, enum rfkill_state state)
+{
+       if (state)
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x404);
+       else
+               return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, 0x400);
+}
+
+static int hp_wmi_wifi_state(void)
+{
+       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+       if (wireless & 0x100)
+               return RFKILL_STATE_UNBLOCKED;
+       else
+               return RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static int hp_wmi_bluetooth_state(void)
+{
+       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+       if (wireless & 0x10000)
+               return RFKILL_STATE_UNBLOCKED;
+       else
+               return RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static int hp_wmi_wwan_state(void)
+{
+       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+       if (wireless & 0x1000000)
+               return RFKILL_STATE_UNBLOCKED;
+       else
+               return RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static ssize_t show_display(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       int value = hp_wmi_display_state();
+       if (value < 0)
+               return -EINVAL;
+       return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_hddtemp(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       int value = hp_wmi_hddtemp_state();
+       if (value < 0)
+               return -EINVAL;
+       return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_als(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       int value = hp_wmi_als_state();
+       if (value < 0)
+               return -EINVAL;
+       return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       int value = hp_wmi_dock_state();
+       if (value < 0)
+               return -EINVAL;
+       return sprintf(buf, "%d\n", value);
+}
+
+static ssize_t set_als(struct device *dev, struct device_attribute *attr,
+                      const char *buf, size_t count)
+{
+       u32 tmp = simple_strtoul(buf, NULL, 10);
+       hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, tmp);
+       return count;
+}
+
+static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
+static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
+static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
+static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
+
+static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
+{
+       struct key_entry *key;
+
+       for (key = hp_wmi_keymap; key->type != KE_END; key++)
+               if (code == key->code)
+                       return key;
+
+       return NULL;
+}
+
+static struct key_entry *hp_wmi_get_entry_by_keycode(int keycode)
+{
+       struct key_entry *key;
+
+       for (key = hp_wmi_keymap; key->type != KE_END; key++)
+               if (key->type == KE_KEY && keycode == key->keycode)
+                       return key;
+
+       return NULL;
+}
+
+static int hp_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+       struct key_entry *key = hp_wmi_get_entry_by_scancode(scancode);
+
+       if (key && key->type == KE_KEY) {
+               *keycode = key->keycode;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static int hp_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+       struct key_entry *key;
+       int old_keycode;
+
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       key = hp_wmi_get_entry_by_scancode(scancode);
+       if (key && key->type == KE_KEY) {
+               old_keycode = key->keycode;
+               key->keycode = keycode;
+               set_bit(keycode, dev->keybit);
+               if (!hp_wmi_get_entry_by_keycode(old_keycode))
+                       clear_bit(old_keycode, dev->keybit);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static void hp_wmi_notify(u32 value, void *context)
+{
+       struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+       static struct key_entry *key;
+       union acpi_object *obj;
+
+       wmi_get_event_data(value, &response);
+
+       obj = (union acpi_object *)response.pointer;
+
+       if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
+               int eventcode = *((u8 *) obj->buffer.pointer);
+               if (eventcode == 0x4)
+                       eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
+                                                        0);
+               key = hp_wmi_get_entry_by_scancode(eventcode);
+               if (key) {
+                       switch (key->type) {
+                       case KE_KEY:
+                               input_report_key(hp_wmi_input_dev,
+                                                key->keycode, 1);
+                               input_sync(hp_wmi_input_dev);
+                               input_report_key(hp_wmi_input_dev,
+                                                key->keycode, 0);
+                               input_sync(hp_wmi_input_dev);
+                               break;
+                       case KE_SW:
+                               input_report_switch(hp_wmi_input_dev,
+                                                   key->keycode,
+                                                   hp_wmi_dock_state());
+                               input_sync(hp_wmi_input_dev);
+                               break;
+                       }
+               } else if (eventcode == 0x5) {
+                       if (wifi_rfkill)
+                               rfkill_force_state(wifi_rfkill,
+                                                  hp_wmi_wifi_state());
+                       if (bluetooth_rfkill)
+                               rfkill_force_state(bluetooth_rfkill,
+                                                  hp_wmi_bluetooth_state());
+                       if (wwan_rfkill)
+                               rfkill_force_state(wwan_rfkill,
+                                                  hp_wmi_wwan_state());
+               } else
+                       printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
+                              eventcode);
+       } else
+               printk(KERN_INFO "HP WMI: Unknown response received\n");
+}
+
+static int __init hp_wmi_input_setup(void)
+{
+       struct key_entry *key;
+       int err;
+
+       hp_wmi_input_dev = input_allocate_device();
+
+       hp_wmi_input_dev->name = "HP WMI hotkeys";
+       hp_wmi_input_dev->phys = "wmi/input0";
+       hp_wmi_input_dev->id.bustype = BUS_HOST;
+       hp_wmi_input_dev->getkeycode = hp_wmi_getkeycode;
+       hp_wmi_input_dev->setkeycode = hp_wmi_setkeycode;
+
+       for (key = hp_wmi_keymap; key->type != KE_END; key++) {
+               switch (key->type) {
+               case KE_KEY:
+                       set_bit(EV_KEY, hp_wmi_input_dev->evbit);
+                       set_bit(key->keycode, hp_wmi_input_dev->keybit);
+                       break;
+               case KE_SW:
+                       set_bit(EV_SW, hp_wmi_input_dev->evbit);
+                       set_bit(key->keycode, hp_wmi_input_dev->swbit);
+                       break;
+               }
+       }
+
+       err = input_register_device(hp_wmi_input_dev);
+
+       if (err) {
+               input_free_device(hp_wmi_input_dev);
+               return err;
+       }
+
+       return 0;
+}
+
+static void cleanup_sysfs(struct platform_device *device)
+{
+       device_remove_file(&device->dev, &dev_attr_display);
+       device_remove_file(&device->dev, &dev_attr_hddtemp);
+       device_remove_file(&device->dev, &dev_attr_als);
+       device_remove_file(&device->dev, &dev_attr_dock);
+}
+
+static int __init hp_wmi_bios_setup(struct platform_device *device)
+{
+       int err;
+       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+
+       err = device_create_file(&device->dev, &dev_attr_display);
+       if (err)
+               goto add_sysfs_error;
+       err = device_create_file(&device->dev, &dev_attr_hddtemp);
+       if (err)
+               goto add_sysfs_error;
+       err = device_create_file(&device->dev, &dev_attr_als);
+       if (err)
+               goto add_sysfs_error;
+       err = device_create_file(&device->dev, &dev_attr_dock);
+       if (err)
+               goto add_sysfs_error;
+
+       if (wireless & 0x1) {
+               wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN);
+               wifi_rfkill->name = "hp-wifi";
+               wifi_rfkill->state = hp_wmi_wifi_state();
+               wifi_rfkill->toggle_radio = hp_wmi_wifi_set;
+               wifi_rfkill->user_claim_unsupported = 1;
+               rfkill_register(wifi_rfkill);
+       }
+
+       if (wireless & 0x2) {
+               bluetooth_rfkill = rfkill_allocate(&device->dev,
+                                                  RFKILL_TYPE_BLUETOOTH);
+               bluetooth_rfkill->name = "hp-bluetooth";
+               bluetooth_rfkill->state = hp_wmi_bluetooth_state();
+               bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set;
+               bluetooth_rfkill->user_claim_unsupported = 1;
+               rfkill_register(bluetooth_rfkill);
+       }
+
+       if (wireless & 0x4) {
+               wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN);
+               wwan_rfkill->name = "hp-wwan";
+               wwan_rfkill->state = hp_wmi_wwan_state();
+               wwan_rfkill->toggle_radio = hp_wmi_wwan_set;
+               wwan_rfkill->user_claim_unsupported = 1;
+               rfkill_register(wwan_rfkill);
+       }
+
+       return 0;
+add_sysfs_error:
+       cleanup_sysfs(device);
+       return err;
+}
+
+static int __exit hp_wmi_bios_remove(struct platform_device *device)
+{
+       cleanup_sysfs(device);
+
+       if (wifi_rfkill)
+               rfkill_unregister(wifi_rfkill);
+       if (bluetooth_rfkill)
+               rfkill_unregister(bluetooth_rfkill);
+       if (wwan_rfkill)
+               rfkill_unregister(wwan_rfkill);
+
+       return 0;
+}
+
+static int __init hp_wmi_init(void)
+{
+       int err;
+
+       if (wmi_has_guid(HPWMI_EVENT_GUID)) {
+               err = wmi_install_notify_handler(HPWMI_EVENT_GUID,
+                                                hp_wmi_notify, NULL);
+               if (!err)
+                       hp_wmi_input_setup();
+       }
+
+       if (wmi_has_guid(HPWMI_BIOS_GUID)) {
+               err = platform_driver_register(&hp_wmi_driver);
+               if (err)
+                       return 0;
+               hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
+               if (!hp_wmi_platform_dev) {
+                       platform_driver_unregister(&hp_wmi_driver);
+                       return 0;
+               }
+               platform_device_add(hp_wmi_platform_dev);
+       }
+
+       return 0;
+}
+
+static void __exit hp_wmi_exit(void)
+{
+       if (wmi_has_guid(HPWMI_EVENT_GUID)) {
+               wmi_remove_notify_handler(HPWMI_EVENT_GUID);
+               input_unregister_device(hp_wmi_input_dev);
+       }
+       if (hp_wmi_platform_dev) {
+               platform_device_del(hp_wmi_platform_dev);
+               platform_driver_unregister(&hp_wmi_driver);
+       }
+}
+
+module_init(hp_wmi_init);
+module_exit(hp_wmi_exit);
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
new file mode 100644 (file)
index 0000000..27b7662
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ *  intel_menlow.c - Intel menlow Driver for thermal management extension
+ *
+ *  Copyright (C) 2008 Intel Corp
+ *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
+ *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This driver creates the sys I/F for programming the sensors.
+ *  It also implements the driver for intel menlow memory controller (hardware
+ *  id is INT0002) which makes use of the platform specific ACPI methods
+ *  to get/set bandwidth.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/pm.h>
+
+#include <linux/thermal.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Thomas Sujith");
+MODULE_AUTHOR("Zhang Rui");
+MODULE_DESCRIPTION("Intel Menlow platform specific driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Memory controller device control
+ */
+
+#define MEMORY_GET_BANDWIDTH "GTHS"
+#define MEMORY_SET_BANDWIDTH "STHS"
+#define MEMORY_ARG_CUR_BANDWIDTH 1
+#define MEMORY_ARG_MAX_BANDWIDTH 0
+
+/*
+ * GTHS returning 'n' would mean that [0,n-1] states are supported
+ * In that case max_cstate would be n-1
+ * GTHS returning '0' would mean that no bandwidth control states are supported
+ */
+static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
+                                       unsigned long *max_state)
+{
+       struct acpi_device *device = cdev->devdata;
+       acpi_handle handle = device->handle;
+       unsigned long long value;
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       acpi_status status = AE_OK;
+
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
+       status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
+                                      &arg_list, &value);
+       if (ACPI_FAILURE(status))
+               return -EFAULT;
+
+       if (!value)
+               return -EINVAL;
+
+       *max_state = value - 1;
+       return 0;
+}
+
+static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
+                                   char *buf)
+{
+       unsigned long value;
+       if (memory_get_int_max_bandwidth(cdev, &value))
+               return -EINVAL;
+
+       return sprintf(buf, "%ld\n", value);
+}
+
+static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
+                                   char *buf)
+{
+       struct acpi_device *device = cdev->devdata;
+       acpi_handle handle = device->handle;
+       unsigned long long value;
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       acpi_status status = AE_OK;
+
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
+       status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
+                                      &arg_list, &value);
+       if (ACPI_FAILURE(status))
+               return -EFAULT;
+
+       return sprintf(buf, "%llu\n", value);
+}
+
+static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
+                                   unsigned int state)
+{
+       struct acpi_device *device = cdev->devdata;
+       acpi_handle handle = device->handle;
+       struct acpi_object_list arg_list;
+       union acpi_object arg;
+       acpi_status status;
+       unsigned long long temp;
+       unsigned long max_state;
+
+       if (memory_get_int_max_bandwidth(cdev, &max_state))
+               return -EFAULT;
+
+       if (state > max_state)
+               return -EINVAL;
+
+       arg_list.count = 1;
+       arg_list.pointer = &arg;
+       arg.type = ACPI_TYPE_INTEGER;
+       arg.integer.value = state;
+
+       status =
+           acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
+                                 &temp);
+
+       printk(KERN_INFO
+              "Bandwidth value was %d: status is %d\n", state, status);
+       if (ACPI_FAILURE(status))
+               return -EFAULT;
+
+       return 0;
+}
+
+static struct thermal_cooling_device_ops memory_cooling_ops = {
+       .get_max_state = memory_get_max_bandwidth,
+       .get_cur_state = memory_get_cur_bandwidth,
+       .set_cur_state = memory_set_cur_bandwidth,
+};
+
+/*
+ * Memory Device Management
+ */
+static int intel_menlow_memory_add(struct acpi_device *device)
+{
+       int result = -ENODEV;
+       acpi_status status = AE_OK;
+       acpi_handle dummy;
+       struct thermal_cooling_device *cdev;
+
+       if (!device)
+               return -EINVAL;
+
+       status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
+       if (ACPI_FAILURE(status))
+               goto end;
+
+       status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
+       if (ACPI_FAILURE(status))
+               goto end;
+
+       cdev = thermal_cooling_device_register("Memory controller", device,
+                                              &memory_cooling_ops);
+       if (IS_ERR(cdev)) {
+               result = PTR_ERR(cdev);
+               goto end;
+       }
+
+       device->driver_data = cdev;
+       result = sysfs_create_link(&device->dev.kobj,
+                               &cdev->device.kobj, "thermal_cooling");
+       if (result)
+               goto unregister;
+
+       result = sysfs_create_link(&cdev->device.kobj,
+                               &device->dev.kobj, "device");
+       if (result) {
+               sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+               goto unregister;
+       }
+
+ end:
+       return result;
+
+ unregister:
+       thermal_cooling_device_unregister(cdev);
+       return result;
+
+}
+
+static int intel_menlow_memory_remove(struct acpi_device *device, int type)
+{
+       struct thermal_cooling_device *cdev = acpi_driver_data(device);
+
+       if (!device || !cdev)
+               return -EINVAL;
+
+       sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+       sysfs_remove_link(&cdev->device.kobj, "device");
+       thermal_cooling_device_unregister(cdev);
+
+       return 0;
+}
+
+static const struct acpi_device_id intel_menlow_memory_ids[] = {
+       {"INT0002", 0},
+       {"", 0},
+};
+
+static struct acpi_driver intel_menlow_memory_driver = {
+       .name = "intel_menlow_thermal_control",
+       .ids = intel_menlow_memory_ids,
+       .ops = {
+               .add = intel_menlow_memory_add,
+               .remove = intel_menlow_memory_remove,
+               },
+};
+
+/*
+ * Sensor control on menlow platform
+ */
+
+#define THERMAL_AUX0 0
+#define THERMAL_AUX1 1
+#define GET_AUX0 "GAX0"
+#define GET_AUX1 "GAX1"
+#define SET_AUX0 "SAX0"
+#define SET_AUX1 "SAX1"
+
+struct intel_menlow_attribute {
+       struct device_attribute attr;
+       struct device *device;
+       acpi_handle handle;
+       struct list_head node;
+};
+
+static LIST_HEAD(intel_menlow_attr_list);
+static DEFINE_MUTEX(intel_menlow_attr_lock);
+
+/*
+ * sensor_get_auxtrip - get the current auxtrip value from sensor
+ * @name: Thermalzone name
+ * @auxtype : AUX0/AUX1
+ * @buf: syfs buffer
+ */
+static int sensor_get_auxtrip(acpi_handle handle, int index,
+                                                       unsigned long long *value)
+{
+       acpi_status status;
+
+       if ((index != 0 && index != 1) || !value)
+               return -EINVAL;
+
+       status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
+                                      NULL, value);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * sensor_set_auxtrip - set the new auxtrip value to sensor
+ * @name: Thermalzone name
+ * @auxtype : AUX0/AUX1
+ * @buf: syfs buffer
+ */
+static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
+{
+       acpi_status status;
+       union acpi_object arg = {
+               ACPI_TYPE_INTEGER
+       };
+       struct acpi_object_list args = {
+               1, &arg
+       };
+       unsigned long long temp;
+
+       if (index != 0 && index != 1)
+               return -EINVAL;
+
+       status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
+                                      NULL, &temp);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+       if ((index && value < temp) || (!index && value > temp))
+               return -EINVAL;
+
+       arg.integer.value = value;
+       status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
+                                      &args, &temp);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+
+       /* do we need to check the return value of SAX0/SAX1 ? */
+
+       return 0;
+}
+
+#define to_intel_menlow_attr(_attr)    \
+       container_of(_attr, struct intel_menlow_attribute, attr)
+
+static ssize_t aux0_show(struct device *dev,
+                        struct device_attribute *dev_attr, char *buf)
+{
+       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
+       unsigned long long value;
+       int result;
+
+       result = sensor_get_auxtrip(attr->handle, 0, &value);
+
+       return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
+}
+
+static ssize_t aux1_show(struct device *dev,
+                        struct device_attribute *dev_attr, char *buf)
+{
+       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
+       unsigned long long value;
+       int result;
+
+       result = sensor_get_auxtrip(attr->handle, 1, &value);
+
+       return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
+}
+
+static ssize_t aux0_store(struct device *dev,
+                         struct device_attribute *dev_attr,
+                         const char *buf, size_t count)
+{
+       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
+       int value;
+       int result;
+
+       /*Sanity check; should be a positive integer */
+       if (!sscanf(buf, "%d", &value))
+               return -EINVAL;
+
+       if (value < 0)
+               return -EINVAL;
+
+       result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
+       return result ? result : count;
+}
+
+static ssize_t aux1_store(struct device *dev,
+                         struct device_attribute *dev_attr,
+                         const char *buf, size_t count)
+{
+       struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
+       int value;
+       int result;
+
+       /*Sanity check; should be a positive integer */
+       if (!sscanf(buf, "%d", &value))
+               return -EINVAL;
+
+       if (value < 0)
+               return -EINVAL;
+
+       result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
+       return result ? result : count;
+}
+
+/* BIOS can enable/disable the thermal user application in dabney platform */
+#define BIOS_ENABLED "\\_TZ.GSTS"
+static ssize_t bios_enabled_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       acpi_status status;
+       unsigned long long bios_enabled;
+
+       status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
+}
+
+static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
+                                         void *store, struct device *dev,
+                                         acpi_handle handle)
+{
+       struct intel_menlow_attribute *attr;
+       int result;
+
+       attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
+       if (!attr)
+               return -ENOMEM;
+
+       attr->attr.attr.name = name;
+       attr->attr.attr.mode = mode;
+       attr->attr.show = show;
+       attr->attr.store = store;
+       attr->device = dev;
+       attr->handle = handle;
+
+       result = device_create_file(dev, &attr->attr);
+       if (result)
+               return result;
+
+       mutex_lock(&intel_menlow_attr_lock);
+       list_add_tail(&attr->node, &intel_menlow_attr_list);
+       mutex_unlock(&intel_menlow_attr_lock);
+
+       return 0;
+}
+
+static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
+                                               void *context, void **rv)
+{
+       acpi_status status;
+       acpi_handle dummy;
+       struct thermal_zone_device *thermal;
+       int result;
+
+       result = acpi_bus_get_private_data(handle, (void **)&thermal);
+       if (result)
+               return 0;
+
+       /* _TZ must have the AUX0/1 methods */
+       status = acpi_get_handle(handle, GET_AUX0, &dummy);
+       if (ACPI_FAILURE(status))
+               goto not_found;
+
+       status = acpi_get_handle(handle, SET_AUX0, &dummy);
+       if (ACPI_FAILURE(status))
+               goto not_found;
+
+       result = intel_menlow_add_one_attribute("aux0", 0644,
+                                               aux0_show, aux0_store,
+                                               &thermal->device, handle);
+       if (result)
+               return AE_ERROR;
+
+       status = acpi_get_handle(handle, GET_AUX1, &dummy);
+       if (ACPI_FAILURE(status))
+               goto not_found;
+
+       status = acpi_get_handle(handle, SET_AUX1, &dummy);
+       if (ACPI_FAILURE(status))
+               goto not_found;
+
+       result = intel_menlow_add_one_attribute("aux1", 0644,
+                                               aux1_show, aux1_store,
+                                               &thermal->device, handle);
+       if (result)
+               return AE_ERROR;
+
+       /*
+        * create the "dabney_enabled" attribute which means the user app
+        * should be loaded or not
+        */
+
+       result = intel_menlow_add_one_attribute("bios_enabled", 0444,
+                                               bios_enabled_show, NULL,
+                                               &thermal->device, handle);
+       if (result)
+               return AE_ERROR;
+
+ not_found:
+       if (status == AE_NOT_FOUND)
+               return AE_OK;
+       else
+               return status;
+}
+
+static void intel_menlow_unregister_sensor(void)
+{
+       struct intel_menlow_attribute *pos, *next;
+
+       mutex_lock(&intel_menlow_attr_lock);
+       list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
+               list_del(&pos->node);
+               device_remove_file(pos->device, &pos->attr);
+               kfree(pos);
+       }
+       mutex_unlock(&intel_menlow_attr_lock);
+
+       return;
+}
+
+static int __init intel_menlow_module_init(void)
+{
+       int result = -ENODEV;
+       acpi_status status;
+       unsigned long long enable;
+
+       if (acpi_disabled)
+               return result;
+
+       /* Looking for the \_TZ.GSTS method */
+       status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
+       if (ACPI_FAILURE(status) || !enable)
+               return -ENODEV;
+
+       /* Looking for ACPI device MEM0 with hardware id INT0002 */
+       result = acpi_bus_register_driver(&intel_menlow_memory_driver);
+       if (result)
+               return result;
+
+       /* Looking for sensors in each ACPI thermal zone */
+       status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX,
+                                    intel_menlow_register_sensor, NULL, NULL);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return 0;
+}
+
+static void __exit intel_menlow_module_exit(void)
+{
+       acpi_bus_unregister_driver(&intel_menlow_memory_driver);
+       intel_menlow_unregister_sensor();
+}
+
+module_init(intel_menlow_module_init);
+module_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 (file)
index 0000000..759763d
--- /dev/null
@@ -0,0 +1,437 @@
+/*-*-linux-c-*-*/
+
+/*
+  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+  02110-1301, USA.
+ */
+
+/*
+ * msi-laptop.c - MSI S270 laptop support. This laptop is sold under
+ * various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
+ *
+ * Driver also supports S271, S420 models.
+ *
+ * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
+ *
+ *   lcd_level - Screen brightness: contains a single integer in the
+ *   range 0..8. (rw)
+ *
+ *   auto_brightness - Enable automatic brightness control: contains
+ *   either 0 or 1. If set to 1 the hardware adjusts the screen
+ *   brightness automatically when the power cord is
+ *   plugged/unplugged. (rw)
+ *
+ *   wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
+ *
+ *   bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
+ *   Please note that this file is constantly 0 if no Bluetooth
+ *   hardware is available. (ro)
+ *
+ * In addition to these platform device attributes the driver
+ * registers itself in the Linux backlight control subsystem and is
+ * available to userspace under /sys/class/backlight/msi-laptop-bl/.
+ *
+ * This driver might work on other laptops produced by MSI. If you
+ * want to try it you can pass force=1 as argument to the module which
+ * will force it to load even when the DMI data doesn't identify the
+ * laptop as MSI S270. YMMV.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+
+#define MSI_DRIVER_VERSION "0.5"
+
+#define MSI_LCD_LEVEL_MAX 9
+
+#define MSI_EC_COMMAND_WIRELESS 0x10
+#define MSI_EC_COMMAND_LCD_LEVEL 0x11
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+static int auto_brightness;
+module_param(auto_brightness, int, 0);
+MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
+
+/* Hardware access */
+
+static int set_lcd_level(int level)
+{
+       u8 buf[2];
+
+       if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
+               return -EINVAL;
+
+       buf[0] = 0x80;
+       buf[1] = (u8) (level*31);
+
+       return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1);
+}
+
+static int get_lcd_level(void)
+{
+       u8 wdata = 0, rdata;
+       int result;
+
+       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
+       if (result < 0)
+               return result;
+
+       return (int) rdata / 31;
+}
+
+static int get_auto_brightness(void)
+{
+       u8 wdata = 4, rdata;
+       int result;
+
+       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1);
+       if (result < 0)
+               return result;
+
+       return !!(rdata & 8);
+}
+
+static int set_auto_brightness(int enable)
+{
+       u8 wdata[2], rdata;
+       int result;
+
+       wdata[0] = 4;
+
+       result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1);
+       if (result < 0)
+               return result;
+
+       wdata[0] = 0x84;
+       wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
+
+       return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1);
+}
+
+static int get_wireless_state(int *wlan, int *bluetooth)
+{
+       u8 wdata = 0, rdata;
+       int result;
+
+       result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1);
+       if (result < 0)
+               return -1;
+
+       if (wlan)
+               *wlan = !!(rdata & 8);
+
+       if (bluetooth)
+               *bluetooth = !!(rdata & 128);
+
+       return 0;
+}
+
+/* Backlight device stuff */
+
+static int bl_get_brightness(struct backlight_device *b)
+{
+       return get_lcd_level();
+}
+
+
+static int bl_update_status(struct backlight_device *b)
+{
+       return set_lcd_level(b->props.brightness);
+}
+
+static struct backlight_ops msibl_ops = {
+       .get_brightness = bl_get_brightness,
+       .update_status  = bl_update_status,
+};
+
+static struct backlight_device *msibl_device;
+
+/* Platform device */
+
+static ssize_t show_wlan(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+
+       int ret, enabled;
+
+       ret = get_wireless_state(&enabled, NULL);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t show_bluetooth(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+
+       int ret, enabled;
+
+       ret = get_wireless_state(NULL, &enabled);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t show_lcd_level(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+
+       int ret;
+
+       ret = get_lcd_level();
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t store_lcd_level(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+
+       int level, ret;
+
+       if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
+               return -EINVAL;
+
+       ret = set_lcd_level(level);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static ssize_t show_auto_brightness(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+
+       int ret;
+
+       ret = get_auto_brightness();
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t store_auto_brightness(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+
+       int enable, ret;
+
+       if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
+               return -EINVAL;
+
+       ret = set_auto_brightness(enable);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
+static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
+static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
+static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
+
+static struct attribute *msipf_attributes[] = {
+       &dev_attr_lcd_level.attr,
+       &dev_attr_auto_brightness.attr,
+       &dev_attr_bluetooth.attr,
+       &dev_attr_wlan.attr,
+       NULL
+};
+
+static struct attribute_group msipf_attribute_group = {
+       .attrs = msipf_attributes
+};
+
+static struct platform_driver msipf_driver = {
+       .driver = {
+               .name = "msi-laptop-pf",
+               .owner = THIS_MODULE,
+       }
+};
+
+static struct platform_device *msipf_device;
+
+/* Initialization */
+
+static int dmi_check_cb(const struct dmi_system_id *id)
+{
+        printk("msi-laptop: Identified laptop model '%s'.\n", id->ident);
+        return 0;
+}
+
+static struct dmi_system_id __initdata msi_dmi_table[] = {
+       {
+               .ident = "MSI S270",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
+                       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "MSI S271",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
+                       DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "MSI S420",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"),
+                       DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+                       DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
+               },
+               .callback = dmi_check_cb
+       },
+       {
+               .ident = "Medion MD96100",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "0131"),
+                       DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD")
+               },
+               .callback = dmi_check_cb
+       },
+       { }
+};
+
+static int __init msi_init(void)
+{
+       int ret;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       if (!force && !dmi_check_system(msi_dmi_table))
+               return -ENODEV;
+
+       if (auto_brightness < 0 || auto_brightness > 2)
+               return -EINVAL;
+
+       /* Register backlight stuff */
+
+       if (acpi_video_backlight_support()) {
+               printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
+                      "by ACPI video driver\n");
+       } else {
+               msibl_device = backlight_device_register("msi-laptop-bl", NULL,
+                                                        NULL, &msibl_ops);
+               if (IS_ERR(msibl_device))
+                       return PTR_ERR(msibl_device);
+               msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1;
+       }
+
+       ret = platform_driver_register(&msipf_driver);
+       if (ret)
+               goto fail_backlight;
+
+       /* Register platform stuff */
+
+       msipf_device = platform_device_alloc("msi-laptop-pf", -1);
+       if (!msipf_device) {
+               ret = -ENOMEM;
+               goto fail_platform_driver;
+       }
+
+       ret = platform_device_add(msipf_device);
+       if (ret)
+               goto fail_platform_device1;
+
+       ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
+       if (ret)
+               goto fail_platform_device2;
+
+       /* Disable automatic brightness control by default because
+        * this module was probably loaded to do brightness control in
+        * software. */
+
+       if (auto_brightness != 2)
+               set_auto_brightness(auto_brightness);
+
+       printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
+
+       return 0;
+
+fail_platform_device2:
+
+       platform_device_del(msipf_device);
+
+fail_platform_device1:
+
+       platform_device_put(msipf_device);
+
+fail_platform_driver:
+
+       platform_driver_unregister(&msipf_driver);
+
+fail_backlight:
+
+       backlight_device_unregister(msibl_device);
+
+       return ret;
+}
+
+static void __exit msi_cleanup(void)
+{
+
+       sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
+       platform_device_unregister(msipf_device);
+       platform_driver_unregister(&msipf_driver);
+       backlight_device_unregister(msibl_device);
+
+       /* Enable automatic brightness control again */
+       if (auto_brightness != 2)
+               set_auto_brightness(1);
+
+       printk(KERN_INFO "msi-laptop: driver unloaded.\n");
+}
+
+module_init(msi_init);
+module_exit(msi_cleanup);
+
+MODULE_AUTHOR("Lennart Poettering");
+MODULE_DESCRIPTION("MSI Laptop Support");
+MODULE_VERSION(MSI_DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*");
+MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*");
+MODULE_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 (file)
index 0000000..4a1bc64
--- /dev/null
@@ -0,0 +1,766 @@
+/*
+ *  Panasonic HotKey and LCD brightness control driver
+ *  (C) 2004 Hiroshi Miura <miura@da-cha.org>
+ *  (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/
+ *  (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp>
+ *  (C) 2004 David Bronaugh <dbronaugh>
+ *  (C) 2006-2008 Harald Welte <laforge@gnumonks.org>
+ *
+ *  derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  publicshed by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ *---------------------------------------------------------------------------
+ *
+ * ChangeLog:
+ *     Sep.23, 2008    Harald Welte <laforge@gnumonks.org>
+ *             -v0.95  rename driver from drivers/acpi/pcc_acpi.c to
+ *                     drivers/misc/panasonic-laptop.c
+ *
+ *     Jul.04, 2008    Harald Welte <laforge@gnumonks.org>
+ *             -v0.94  replace /proc interface with device attributes
+ *                     support {set,get}keycode on th input device
+ *
+ *      Jun.27, 2008   Harald Welte <laforge@gnumonks.org>
+ *             -v0.92  merge with 2.6.26-rc6 input API changes
+ *                     remove broken <= 2.6.15 kernel support
+ *                     resolve all compiler warnings
+ *                     various coding style fixes (checkpatch.pl)
+ *                     add support for backlight api
+ *                     major code restructuring
+ *
+ *     Dac.28, 2007    Harald Welte <laforge@gnumonks.org>
+ *             -v0.91  merge with 2.6.24-rc6 ACPI changes
+ *
+ *     Nov.04, 2006    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.9   remove warning about section reference.
+ *                     remove acpi_os_free
+ *                     add /proc/acpi/pcc/brightness interface for HAL access
+ *                     merge dbronaugh's enhancement
+ *                     Aug.17, 2004 David Bronaugh (dbronaugh)
+ *                             - Added screen brightness setting interface
+ *                               Thanks to FreeBSD crew (acpi_panasonic.c)
+ *                               for the ideas I needed to accomplish it
+ *
+ *     May.29, 2006    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.8.4 follow to change keyinput structure
+ *                     thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>,
+ *                     Jacob Bower <jacob.bower@ic.ac.uk> and
+ *                     Hiroshi Yokota for providing solutions.
+ *
+ *     Oct.02, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.8.2 merge code of YOKOTA Hiroshi
+ *                                     <yokota@netlab.is.tsukuba.ac.jp>.
+ *                     Add sticky key mode interface.
+ *                     Refactoring acpi_pcc_generate_keyinput().
+ *
+ *     Sep.15, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.8   Generate key input event on input subsystem.
+ *                     This is based on yet another driver written by
+ *                                                     Ryuta Nakanishi.
+ *
+ *     Sep.10, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.7   Change proc interface functions using seq_file
+ *                     facility as same as other ACPI drivers.
+ *
+ *     Aug.28, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.6.4 Fix a silly error with status checking
+ *
+ *     Aug.25, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             -v0.6.3 replace read_acpi_int by standard function
+ *                                                     acpi_evaluate_integer
+ *                     some clean up and make smart copyright notice.
+ *                     fix return value of pcc_acpi_get_key()
+ *                     fix checking return value of acpi_bus_register_driver()
+ *
+ *      Aug.22, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.2 Add check on ACPI data (num_sifr)
+ *                      Coding style cleanups, better error messages/handling
+ *                     Fixed an off-by-one error in memory allocation
+ *
+ *      Aug.21, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              -v0.6.1 Fix a silly error with status checking
+ *
+ *      Aug.20, 2004    David Bronaugh <dbronaugh@linuxboxen.org>
+ *              - v0.6  Correct brightness controls to reflect reality
+ *                      based on information gleaned by Hiroshi Miura
+ *                      and discussions with Hiroshi Miura
+ *
+ *     Aug.10, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             - v0.5  support LCD brightness control
+ *                     based on the disclosed information by MEI.
+ *
+ *     Jul.25, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             - v0.4  first post version
+ *                     add function to retrive SIFR
+ *
+ *     Jul.24, 2004    Hiroshi Miura <miura@da-cha.org>
+ *             - v0.3  get proper status of hotkey
+ *
+ *      Jul.22, 2004   Hiroshi Miura <miura@da-cha.org>
+ *             - v0.2  add HotKey handler
+ *
+ *      Jul.17, 2004   Hiroshi Miura <miura@da-cha.org>
+ *             - v0.1  start from toshiba_acpi driver written by John Belmonte
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/backlight.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/input.h>
+
+
+#ifndef ACPI_HOTKEY_COMPONENT
+#define ACPI_HOTKEY_COMPONENT  0x10000000
+#endif
+
+#define _COMPONENT             ACPI_HOTKEY_COMPONENT
+
+MODULE_AUTHOR("Hiroshi Miura, David Bronaugh and Harald Welte");
+MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Let's Note laptops");
+MODULE_LICENSE("GPL");
+
+#define LOGPREFIX "pcc_acpi: "
+
+/* Define ACPI PATHs */
+/* Lets note hotkeys */
+#define METHOD_HKEY_QUERY      "HINF"
+#define METHOD_HKEY_SQTY       "SQTY"
+#define METHOD_HKEY_SINF       "SINF"
+#define METHOD_HKEY_SSET       "SSET"
+#define HKEY_NOTIFY             0x80
+
+#define ACPI_PCC_DRIVER_NAME   "Panasonic Laptop Support"
+#define ACPI_PCC_DEVICE_NAME   "Hotkey"
+#define ACPI_PCC_CLASS         "pcc"
+
+#define ACPI_PCC_INPUT_PHYS    "panasonic/hkey0"
+
+/* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent
+   ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00
+*/
+enum SINF_BITS { SINF_NUM_BATTERIES = 0,
+                SINF_LCD_TYPE,
+                SINF_AC_MAX_BRIGHT,
+                SINF_AC_MIN_BRIGHT,
+                SINF_AC_CUR_BRIGHT,
+                SINF_DC_MAX_BRIGHT,
+                SINF_DC_MIN_BRIGHT,
+                SINF_DC_CUR_BRIGHT,
+                SINF_MUTE,
+                SINF_RESERVED,
+                SINF_ENV_STATE,
+                SINF_STICKY_KEY = 0x80,
+       };
+/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
+static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+
+static const struct acpi_device_id pcc_device_ids[] = {
+       { "MAT0012", 0},
+       { "MAT0013", 0},
+       { "MAT0018", 0},
+       { "MAT0019", 0},
+       { "", 0},
+};
+
+static struct acpi_driver acpi_pcc_driver = {
+       .name =         ACPI_PCC_DRIVER_NAME,
+       .class =        ACPI_PCC_CLASS,
+       .ids =          pcc_device_ids,
+       .ops =          {
+                               .add =          acpi_pcc_hotkey_add,
+                               .remove =       acpi_pcc_hotkey_remove,
+                               .resume =       acpi_pcc_hotkey_resume,
+                       },
+};
+
+#define KEYMAP_SIZE            11
+static const int initial_keymap[KEYMAP_SIZE] = {
+       /*  0 */ KEY_RESERVED,
+       /*  1 */ KEY_BRIGHTNESSDOWN,
+       /*  2 */ KEY_BRIGHTNESSUP,
+       /*  3 */ KEY_DISPLAYTOGGLE,
+       /*  4 */ KEY_MUTE,
+       /*  5 */ KEY_VOLUMEDOWN,
+       /*  6 */ KEY_VOLUMEUP,
+       /*  7 */ KEY_SLEEP,
+       /*  8 */ KEY_PROG1, /* Change CPU boost */
+       /*  9 */ KEY_BATTERY,
+       /* 10 */ KEY_SUSPEND,
+};
+
+struct pcc_acpi {
+       acpi_handle             handle;
+       unsigned long           num_sifr;
+       int                     sticky_mode;
+       u32                     *sinf;
+       struct acpi_device      *device;
+       struct input_dev        *input_dev;
+       struct backlight_device *backlight;
+       int                     keymap[KEYMAP_SIZE];
+};
+
+struct pcc_keyinput {
+       struct acpi_hotkey      *hotkey;
+};
+
+/* method access functions */
+static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
+{
+       union acpi_object in_objs[] = {
+               { .integer.type  = ACPI_TYPE_INTEGER,
+                 .integer.value = func, },
+               { .integer.type  = ACPI_TYPE_INTEGER,
+                 .integer.value = val, },
+       };
+       struct acpi_object_list params = {
+               .count   = ARRAY_SIZE(in_objs),
+               .pointer = in_objs,
+       };
+       acpi_status status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_write_sset");
+
+       status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SSET,
+                                     &params, NULL);
+
+       return status == AE_OK;
+}
+
+static inline int acpi_pcc_get_sqty(struct acpi_device *device)
+{
+       unsigned long long s;
+       acpi_status status;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty");
+
+       status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY,
+                                      NULL, &s);
+       if (ACPI_SUCCESS(status))
+               return s;
+       else {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "evaluation error HKEY.SQTY\n"));
+               return -EINVAL;
+       }
+}
+
+static int acpi_pcc_retrieve_biosdata(struct pcc_acpi *pcc, u32 *sinf)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+       union acpi_object *hkey = NULL;
+       int i;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata");
+
+       status = acpi_evaluate_object(pcc->handle, METHOD_HKEY_SINF, 0,
+                                     &buffer);
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "evaluation error HKEY.SINF\n"));
+               return 0;
+       }
+
+       hkey = buffer.pointer;
+       if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n"));
+               goto end;
+       }
+
+       if (pcc->num_sifr < hkey->package.count) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                "SQTY reports bad SINF length\n"));
+               status = AE_ERROR;
+               goto end;
+       }
+
+       for (i = 0; i < hkey->package.count; i++) {
+               union acpi_object *element = &(hkey->package.elements[i]);
+               if (likely(element->type == ACPI_TYPE_INTEGER)) {
+                       sinf[i] = element->integer.value;
+               } else
+                       ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                        "Invalid HKEY.SINF data\n"));
+       }
+       sinf[hkey->package.count] = -1;
+
+end:
+       kfree(buffer.pointer);
+       return status == AE_OK;
+}
+
+/* backlight API interface functions */
+
+/* This driver currently treats AC and DC brightness identical,
+ * since we don't need to invent an interface to the core ACPI
+ * logic to receive events in case a power supply is plugged in
+ * or removed */
+
+static int bl_get(struct backlight_device *bd)
+{
+       struct pcc_acpi *pcc = bl_get_data(bd);
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       return pcc->sinf[SINF_AC_CUR_BRIGHT];
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+       struct pcc_acpi *pcc = bl_get_data(bd);
+       int bright = bd->props.brightness;
+       int rc;
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT])
+               bright = pcc->sinf[SINF_AC_MIN_BRIGHT];
+
+       if (bright < pcc->sinf[SINF_DC_MIN_BRIGHT])
+               bright = pcc->sinf[SINF_DC_MIN_BRIGHT];
+
+       if (bright < pcc->sinf[SINF_AC_MIN_BRIGHT] ||
+           bright > pcc->sinf[SINF_AC_MAX_BRIGHT])
+               return -EINVAL;
+
+       rc = acpi_pcc_write_sset(pcc, SINF_AC_CUR_BRIGHT, bright);
+       if (rc < 0)
+               return rc;
+
+       return acpi_pcc_write_sset(pcc, SINF_DC_CUR_BRIGHT, bright);
+}
+
+static struct backlight_ops pcc_backlight_ops = {
+       .get_brightness = bl_get,
+       .update_status  = bl_set_status,
+};
+
+
+/* sysfs user interface functions */
+
+static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]);
+}
+
+static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]);
+}
+
+static ssize_t show_mute(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]);
+}
+
+static ssize_t show_sticky(struct device *dev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf))
+               return -EIO;
+
+       return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]);
+}
+
+static ssize_t set_sticky(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct acpi_device *acpi = to_acpi_device(dev);
+       struct pcc_acpi *pcc = acpi_driver_data(acpi);
+       int val;
+
+       if (count && sscanf(buf, "%i", &val) == 1 &&
+           (val == 0 || val == 1)) {
+               acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, val);
+               pcc->sticky_mode = val;
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(numbatt, S_IRUGO, show_numbatt, NULL);
+static DEVICE_ATTR(lcdtype, S_IRUGO, show_lcdtype, NULL);
+static DEVICE_ATTR(mute, S_IRUGO, show_mute, NULL);
+static DEVICE_ATTR(sticky_key, S_IRUGO | S_IWUSR, show_sticky, set_sticky);
+
+static struct attribute *pcc_sysfs_entries[] = {
+       &dev_attr_numbatt.attr,
+       &dev_attr_lcdtype.attr,
+       &dev_attr_mute.attr,
+       &dev_attr_sticky_key.attr,
+       NULL,
+};
+
+static struct attribute_group pcc_attr_group = {
+       .name   = NULL,         /* put in device directory */
+       .attrs  = pcc_sysfs_entries,
+};
+
+
+/* hotkey input device driver */
+
+static int pcc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+{
+       struct pcc_acpi *pcc = input_get_drvdata(dev);
+
+       if (scancode >= ARRAY_SIZE(pcc->keymap))
+               return -EINVAL;
+
+       *keycode = pcc->keymap[scancode];
+
+       return 0;
+}
+
+static int keymap_get_by_keycode(struct pcc_acpi *pcc, int keycode)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++) {
+               if (pcc->keymap[i] == keycode)
+                       return i+1;
+       }
+
+       return 0;
+}
+
+static int pcc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+{
+       struct pcc_acpi *pcc = input_get_drvdata(dev);
+       int oldkeycode;
+
+       if (scancode >= ARRAY_SIZE(pcc->keymap))
+               return -EINVAL;
+
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       oldkeycode = pcc->keymap[scancode];
+       pcc->keymap[scancode] = keycode;
+
+       set_bit(keycode, dev->keybit);
+
+       if (!keymap_get_by_keycode(pcc, oldkeycode))
+               clear_bit(oldkeycode, dev->keybit);
+
+       return 0;
+}
+
+static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
+{
+       struct input_dev *hotk_input_dev = pcc->input_dev;
+       int rc;
+       int key_code, hkey_num;
+       unsigned long long result;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_generate_keyinput");
+
+       rc = acpi_evaluate_integer(pcc->handle, METHOD_HKEY_QUERY,
+                                  NULL, &result);
+       if (!ACPI_SUCCESS(rc)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                "error getting hotkey status\n"));
+               return;
+       }
+
+       acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
+
+       hkey_num = result & 0xf;
+
+       if (hkey_num < 0 || hkey_num > ARRAY_SIZE(pcc->keymap)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "hotkey number out of range: %d\n",
+                                 hkey_num));
+               return;
+       }
+
+       key_code = pcc->keymap[hkey_num];
+
+       if (key_code != KEY_RESERVED) {
+               int pushed = (result & 0x80) ? TRUE : FALSE;
+
+               input_report_key(hotk_input_dev, key_code, pushed);
+               input_sync(hotk_input_dev);
+       }
+
+       return;
+}
+
+static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct pcc_acpi *pcc = (struct pcc_acpi *) data;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify");
+
+       switch (event) {
+       case HKEY_NOTIFY:
+               acpi_pcc_generate_keyinput(pcc);
+               break;
+       default:
+               /* nothing to do */
+               break;
+       }
+}
+
+static int acpi_pcc_init_input(struct pcc_acpi *pcc)
+{
+       int i, rc;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_init_input");
+
+       pcc->input_dev = input_allocate_device();
+       if (!pcc->input_dev) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Couldn't allocate input device for hotkey"));
+               return -ENOMEM;
+       }
+
+       pcc->input_dev->evbit[0] = BIT(EV_KEY);
+
+       pcc->input_dev->name = ACPI_PCC_DRIVER_NAME;
+       pcc->input_dev->phys = ACPI_PCC_INPUT_PHYS;
+       pcc->input_dev->id.bustype = BUS_HOST;
+       pcc->input_dev->id.vendor = 0x0001;
+       pcc->input_dev->id.product = 0x0001;
+       pcc->input_dev->id.version = 0x0100;
+       pcc->input_dev->getkeycode = pcc_getkeycode;
+       pcc->input_dev->setkeycode = pcc_setkeycode;
+
+       /* load initial keymap */
+       memcpy(pcc->keymap, initial_keymap, sizeof(pcc->keymap));
+
+       for (i = 0; i < ARRAY_SIZE(pcc->keymap); i++)
+               __set_bit(pcc->keymap[i], pcc->input_dev->keybit);
+       __clear_bit(KEY_RESERVED, pcc->input_dev->keybit);
+
+       input_set_drvdata(pcc->input_dev, pcc);
+
+       rc = input_register_device(pcc->input_dev);
+       if (rc < 0)
+               input_free_device(pcc->input_dev);
+
+       return rc;
+}
+
+/* kernel module interface */
+
+static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+{
+       struct pcc_acpi *pcc = acpi_driver_data(device);
+       acpi_status status = AE_OK;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume");
+
+       if (device == NULL || pcc == NULL)
+               return -EINVAL;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
+                         pcc->sticky_mode));
+
+       status = acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
+
+       return status == AE_OK ? 0 : -EINVAL;
+}
+
+static int acpi_pcc_hotkey_add(struct acpi_device *device)
+{
+       acpi_status status;
+       struct pcc_acpi *pcc;
+       int num_sifr, result;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add");
+
+       if (!device)
+               return -EINVAL;
+
+       num_sifr = acpi_pcc_get_sqty(device);
+
+       if (num_sifr > 255) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+               return -ENODEV;
+       }
+
+       pcc = kzalloc(sizeof(struct pcc_acpi), GFP_KERNEL);
+       if (!pcc) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Couldn't allocate mem for pcc"));
+               return -ENOMEM;
+       }
+
+       pcc->sinf = kzalloc(sizeof(u32) * (num_sifr + 1), GFP_KERNEL);
+       if (!pcc->sinf) {
+               result = -ENOMEM;
+               goto out_hotkey;
+       }
+
+       pcc->device = device;
+       pcc->handle = device->handle;
+       pcc->num_sifr = num_sifr;
+       device->driver_data = pcc;
+       strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
+       strcpy(acpi_device_class(device), ACPI_PCC_CLASS);
+
+       result = acpi_pcc_init_input(pcc);
+       if (result) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Error installing keyinput handler\n"));
+               goto out_sinf;
+       }
+
+       /* initialize hotkey input device */
+       status = acpi_install_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+                                            acpi_pcc_hotkey_notify, pcc);
+
+       if (ACPI_FAILURE(status)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Error installing notify handler\n"));
+               result = -ENODEV;
+               goto out_input;
+       }
+
+       /* initialize backlight */
+       pcc->backlight = backlight_device_register("panasonic", NULL, pcc,
+                                                  &pcc_backlight_ops);
+       if (IS_ERR(pcc->backlight))
+               goto out_notify;
+
+       if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                "Couldn't retrieve BIOS data\n"));
+               goto out_backlight;
+       }
+
+       /* read the initial brightness setting from the hardware */
+       pcc->backlight->props.max_brightness =
+                                       pcc->sinf[SINF_AC_MAX_BRIGHT];
+       pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
+
+       /* read the initial sticky key mode from the hardware */
+       pcc->sticky_mode = pcc->sinf[SINF_STICKY_KEY];
+
+       /* add sysfs attributes */
+       result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
+       if (result)
+               goto out_backlight;
+
+       return 0;
+
+out_backlight:
+       backlight_device_unregister(pcc->backlight);
+out_notify:
+       acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+                                  acpi_pcc_hotkey_notify);
+out_input:
+       input_unregister_device(pcc->input_dev);
+       /* no need to input_free_device() since core input API refcount and
+        * free()s the device */
+out_sinf:
+       kfree(pcc->sinf);
+out_hotkey:
+       kfree(pcc);
+
+       return result;
+}
+
+static int __init acpi_pcc_init(void)
+{
+       int result = 0;
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_init");
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       result = acpi_bus_register_driver(&acpi_pcc_driver);
+       if (result < 0) {
+               ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+                                 "Error registering hotkey driver\n"));
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+{
+       struct pcc_acpi *pcc = acpi_driver_data(device);
+
+       ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove");
+
+       if (!device || !pcc)
+               return -EINVAL;
+
+       sysfs_remove_group(&device->dev.kobj, &pcc_attr_group);
+
+       backlight_device_unregister(pcc->backlight);
+
+       acpi_remove_notify_handler(pcc->handle, ACPI_DEVICE_NOTIFY,
+                                  acpi_pcc_hotkey_notify);
+
+       input_unregister_device(pcc->input_dev);
+       /* no need to input_free_device() since core input API refcount and
+        * free()s the device */
+
+       kfree(pcc->sinf);
+       kfree(pcc);
+
+       return 0;
+}
+
+static void __exit acpi_pcc_exit(void)
+{
+       ACPI_FUNCTION_TRACE("acpi_pcc_exit");
+
+       acpi_bus_unregister_driver(&acpi_pcc_driver);
+}
+
+module_init(acpi_pcc_init);
+module_exit(acpi_pcc_exit);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
new file mode 100644 (file)
index 0000000..571b211
--- /dev/null
@@ -0,0 +1,2781 @@
+/*
+ * ACPI Sony Notebook Control Driver (SNC and SPIC)
+ *
+ * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net>
+ * Copyright (C) 2007 Mattia Dongili <malattia@linux.it>
+ *
+ * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c
+ * which are copyrighted by their respective authors.
+ *
+ * The SNY6001 driver part is based on the sonypi driver which includes
+ * material from:
+ *
+ * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net>
+ *
+ * Copyright (C) 2005 Narayanan R S <nars@kadamba.org>
+ *
+ * Copyright (C) 2001-2002 Alcôve <www.alcove.com>
+ *
+ * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+ *
+ * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+ *
+ * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/kfifo.h>
+#include <linux/workqueue.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+#include <asm/uaccess.h>
+#include <linux/sonypi.h>
+#include <linux/sony-laptop.h>
+#ifdef CONFIG_SONYPI_COMPAT
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#endif
+
+#define DRV_PFX                        "sony-laptop: "
+#define dprintk(msg...)                do {                    \
+       if (debug) printk(KERN_WARNING DRV_PFX  msg);   \
+} while (0)
+
+#define SONY_LAPTOP_DRIVER_VERSION     "0.6"
+
+#define SONY_NC_CLASS          "sony-nc"
+#define SONY_NC_HID            "SNY5001"
+#define SONY_NC_DRIVER_NAME    "Sony Notebook Control Driver"
+
+#define SONY_PIC_CLASS         "sony-pic"
+#define SONY_PIC_HID           "SNY6001"
+#define SONY_PIC_DRIVER_NAME   "Sony Programmable IO Control Driver"
+
+MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
+MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help "
+                "the development of this driver");
+
+static int no_spic;            /* = 0 */
+module_param(no_spic, int, 0444);
+MODULE_PARM_DESC(no_spic,
+                "set this if you don't want to enable the SPIC device");
+
+static int compat;             /* = 0 */
+module_param(compat, int, 0444);
+MODULE_PARM_DESC(compat,
+                "set this if you want to enable backward compatibility mode");
+
+static unsigned long mask = 0xffffffff;
+module_param(mask, ulong, 0644);
+MODULE_PARM_DESC(mask,
+                "set this to the mask of event you want to enable (see doc)");
+
+static int camera;             /* = 0 */
+module_param(camera, int, 0444);
+MODULE_PARM_DESC(camera,
+                "set this to 1 to enable Motion Eye camera controls "
+                "(only use it if you have a C1VE or C1VN model)");
+
+#ifdef CONFIG_SONYPI_COMPAT
+static int minor = -1;
+module_param(minor, int, 0);
+MODULE_PARM_DESC(minor,
+                "minor number of the misc device for the SPIC compatibility code, "
+                "default is -1 (automatic)");
+#endif
+
+/*********** Input Devices ***********/
+
+#define SONY_LAPTOP_BUF_SIZE   128
+struct sony_laptop_input_s {
+       atomic_t                users;
+       struct input_dev        *jog_dev;
+       struct input_dev        *key_dev;
+       struct kfifo            *fifo;
+       spinlock_t              fifo_lock;
+       struct workqueue_struct *wq;
+};
+static struct sony_laptop_input_s sony_laptop_input = {
+       .users = ATOMIC_INIT(0),
+};
+
+struct sony_laptop_keypress {
+       struct input_dev *dev;
+       int key;
+};
+
+/* Correspondance table between sonypi events
+ * and input layer indexes in the keymap
+ */
+static int sony_laptop_input_index[] = {
+       -1,     /*  0 no event */
+       -1,     /*  1 SONYPI_EVENT_JOGDIAL_DOWN */
+       -1,     /*  2 SONYPI_EVENT_JOGDIAL_UP */
+       -1,     /*  3 SONYPI_EVENT_JOGDIAL_DOWN_PRESSED */
+       -1,     /*  4 SONYPI_EVENT_JOGDIAL_UP_PRESSED */
+       -1,     /*  5 SONYPI_EVENT_JOGDIAL_PRESSED */
+       -1,     /*  6 SONYPI_EVENT_JOGDIAL_RELEASED */
+        0,     /*  7 SONYPI_EVENT_CAPTURE_PRESSED */
+        1,     /*  8 SONYPI_EVENT_CAPTURE_RELEASED */
+        2,     /*  9 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
+        3,     /* 10 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
+        4,     /* 11 SONYPI_EVENT_FNKEY_ESC */
+        5,     /* 12 SONYPI_EVENT_FNKEY_F1 */
+        6,     /* 13 SONYPI_EVENT_FNKEY_F2 */
+        7,     /* 14 SONYPI_EVENT_FNKEY_F3 */
+        8,     /* 15 SONYPI_EVENT_FNKEY_F4 */
+        9,     /* 16 SONYPI_EVENT_FNKEY_F5 */
+       10,     /* 17 SONYPI_EVENT_FNKEY_F6 */
+       11,     /* 18 SONYPI_EVENT_FNKEY_F7 */
+       12,     /* 19 SONYPI_EVENT_FNKEY_F8 */
+       13,     /* 20 SONYPI_EVENT_FNKEY_F9 */
+       14,     /* 21 SONYPI_EVENT_FNKEY_F10 */
+       15,     /* 22 SONYPI_EVENT_FNKEY_F11 */
+       16,     /* 23 SONYPI_EVENT_FNKEY_F12 */
+       17,     /* 24 SONYPI_EVENT_FNKEY_1 */
+       18,     /* 25 SONYPI_EVENT_FNKEY_2 */
+       19,     /* 26 SONYPI_EVENT_FNKEY_D */
+       20,     /* 27 SONYPI_EVENT_FNKEY_E */
+       21,     /* 28 SONYPI_EVENT_FNKEY_F */
+       22,     /* 29 SONYPI_EVENT_FNKEY_S */
+       23,     /* 30 SONYPI_EVENT_FNKEY_B */
+       24,     /* 31 SONYPI_EVENT_BLUETOOTH_PRESSED */
+       25,     /* 32 SONYPI_EVENT_PKEY_P1 */
+       26,     /* 33 SONYPI_EVENT_PKEY_P2 */
+       27,     /* 34 SONYPI_EVENT_PKEY_P3 */
+       28,     /* 35 SONYPI_EVENT_BACK_PRESSED */
+       -1,     /* 36 SONYPI_EVENT_LID_CLOSED */
+       -1,     /* 37 SONYPI_EVENT_LID_OPENED */
+       29,     /* 38 SONYPI_EVENT_BLUETOOTH_ON */
+       30,     /* 39 SONYPI_EVENT_BLUETOOTH_OFF */
+       31,     /* 40 SONYPI_EVENT_HELP_PRESSED */
+       32,     /* 41 SONYPI_EVENT_FNKEY_ONLY */
+       33,     /* 42 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
+       34,     /* 43 SONYPI_EVENT_JOGDIAL_FAST_UP */
+       35,     /* 44 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
+       36,     /* 45 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
+       37,     /* 46 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
+       38,     /* 47 SONYPI_EVENT_JOGDIAL_VFAST_UP */
+       39,     /* 48 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
+       40,     /* 49 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
+       41,     /* 50 SONYPI_EVENT_ZOOM_PRESSED */
+       42,     /* 51 SONYPI_EVENT_THUMBPHRASE_PRESSED */
+       43,     /* 52 SONYPI_EVENT_MEYE_FACE */
+       44,     /* 53 SONYPI_EVENT_MEYE_OPPOSITE */
+       45,     /* 54 SONYPI_EVENT_MEMORYSTICK_INSERT */
+       46,     /* 55 SONYPI_EVENT_MEMORYSTICK_EJECT */
+       -1,     /* 56 SONYPI_EVENT_ANYBUTTON_RELEASED */
+       -1,     /* 57 SONYPI_EVENT_BATTERY_INSERT */
+       -1,     /* 58 SONYPI_EVENT_BATTERY_REMOVE */
+       -1,     /* 59 SONYPI_EVENT_FNKEY_RELEASED */
+       47,     /* 60 SONYPI_EVENT_WIRELESS_ON */
+       48,     /* 61 SONYPI_EVENT_WIRELESS_OFF */
+       49,     /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */
+       50,     /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */
+};
+
+static int sony_laptop_input_keycode_map[] = {
+       KEY_CAMERA,     /*  0 SONYPI_EVENT_CAPTURE_PRESSED */
+       KEY_RESERVED,   /*  1 SONYPI_EVENT_CAPTURE_RELEASED */
+       KEY_RESERVED,   /*  2 SONYPI_EVENT_CAPTURE_PARTIALPRESSED */
+       KEY_RESERVED,   /*  3 SONYPI_EVENT_CAPTURE_PARTIALRELEASED */
+       KEY_FN_ESC,     /*  4 SONYPI_EVENT_FNKEY_ESC */
+       KEY_FN_F1,      /*  5 SONYPI_EVENT_FNKEY_F1 */
+       KEY_FN_F2,      /*  6 SONYPI_EVENT_FNKEY_F2 */
+       KEY_FN_F3,      /*  7 SONYPI_EVENT_FNKEY_F3 */
+       KEY_FN_F4,      /*  8 SONYPI_EVENT_FNKEY_F4 */
+       KEY_FN_F5,      /*  9 SONYPI_EVENT_FNKEY_F5 */
+       KEY_FN_F6,      /* 10 SONYPI_EVENT_FNKEY_F6 */
+       KEY_FN_F7,      /* 11 SONYPI_EVENT_FNKEY_F7 */
+       KEY_FN_F8,      /* 12 SONYPI_EVENT_FNKEY_F8 */
+       KEY_FN_F9,      /* 13 SONYPI_EVENT_FNKEY_F9 */
+       KEY_FN_F10,     /* 14 SONYPI_EVENT_FNKEY_F10 */
+       KEY_FN_F11,     /* 15 SONYPI_EVENT_FNKEY_F11 */
+       KEY_FN_F12,     /* 16 SONYPI_EVENT_FNKEY_F12 */
+       KEY_FN_F1,      /* 17 SONYPI_EVENT_FNKEY_1 */
+       KEY_FN_F2,      /* 18 SONYPI_EVENT_FNKEY_2 */
+       KEY_FN_D,       /* 19 SONYPI_EVENT_FNKEY_D */
+       KEY_FN_E,       /* 20 SONYPI_EVENT_FNKEY_E */
+       KEY_FN_F,       /* 21 SONYPI_EVENT_FNKEY_F */
+       KEY_FN_S,       /* 22 SONYPI_EVENT_FNKEY_S */
+       KEY_FN_B,       /* 23 SONYPI_EVENT_FNKEY_B */
+       KEY_BLUETOOTH,  /* 24 SONYPI_EVENT_BLUETOOTH_PRESSED */
+       KEY_PROG1,      /* 25 SONYPI_EVENT_PKEY_P1 */
+       KEY_PROG2,      /* 26 SONYPI_EVENT_PKEY_P2 */
+       KEY_PROG3,      /* 27 SONYPI_EVENT_PKEY_P3 */
+       KEY_BACK,       /* 28 SONYPI_EVENT_BACK_PRESSED */
+       KEY_BLUETOOTH,  /* 29 SONYPI_EVENT_BLUETOOTH_ON */
+       KEY_BLUETOOTH,  /* 30 SONYPI_EVENT_BLUETOOTH_OFF */
+       KEY_HELP,       /* 31 SONYPI_EVENT_HELP_PRESSED */
+       KEY_FN,         /* 32 SONYPI_EVENT_FNKEY_ONLY */
+       KEY_RESERVED,   /* 33 SONYPI_EVENT_JOGDIAL_FAST_DOWN */
+       KEY_RESERVED,   /* 34 SONYPI_EVENT_JOGDIAL_FAST_UP */
+       KEY_RESERVED,   /* 35 SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED */
+       KEY_RESERVED,   /* 36 SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED */
+       KEY_RESERVED,   /* 37 SONYPI_EVENT_JOGDIAL_VFAST_DOWN */
+       KEY_RESERVED,   /* 38 SONYPI_EVENT_JOGDIAL_VFAST_UP */
+       KEY_RESERVED,   /* 39 SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED */
+       KEY_RESERVED,   /* 40 SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED */
+       KEY_ZOOM,       /* 41 SONYPI_EVENT_ZOOM_PRESSED */
+       BTN_THUMB,      /* 42 SONYPI_EVENT_THUMBPHRASE_PRESSED */
+       KEY_RESERVED,   /* 43 SONYPI_EVENT_MEYE_FACE */
+       KEY_RESERVED,   /* 44 SONYPI_EVENT_MEYE_OPPOSITE */
+       KEY_RESERVED,   /* 45 SONYPI_EVENT_MEMORYSTICK_INSERT */
+       KEY_RESERVED,   /* 46 SONYPI_EVENT_MEMORYSTICK_EJECT */
+       KEY_WLAN,       /* 47 SONYPI_EVENT_WIRELESS_ON */
+       KEY_WLAN,       /* 48 SONYPI_EVENT_WIRELESS_OFF */
+       KEY_ZOOMIN,     /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */
+       KEY_ZOOMOUT     /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */
+};
+
+/* release buttons after a short delay if pressed */
+static void do_sony_laptop_release_key(struct work_struct *work)
+{
+       struct sony_laptop_keypress kp;
+
+       while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp,
+                        sizeof(kp)) == sizeof(kp)) {
+               msleep(10);
+               input_report_key(kp.dev, kp.key, 0);
+               input_sync(kp.dev);
+       }
+}
+static DECLARE_WORK(sony_laptop_release_key_work,
+               do_sony_laptop_release_key);
+
+/* forward event to the input subsystem */
+static void sony_laptop_report_input_event(u8 event)
+{
+       struct input_dev *jog_dev = sony_laptop_input.jog_dev;
+       struct input_dev *key_dev = sony_laptop_input.key_dev;
+       struct sony_laptop_keypress kp = { NULL };
+
+       if (event == SONYPI_EVENT_FNKEY_RELEASED) {
+               /* Nothing, not all VAIOs generate this event */
+               return;
+       }
+
+       /* report events */
+       switch (event) {
+       /* jog_dev events */
+       case SONYPI_EVENT_JOGDIAL_UP:
+       case SONYPI_EVENT_JOGDIAL_UP_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, 1);
+               input_sync(jog_dev);
+               return;
+
+       case SONYPI_EVENT_JOGDIAL_DOWN:
+       case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED:
+               input_report_rel(jog_dev, REL_WHEEL, -1);
+               input_sync(jog_dev);
+               return;
+
+       /* key_dev events */
+       case SONYPI_EVENT_JOGDIAL_PRESSED:
+               kp.key = BTN_MIDDLE;
+               kp.dev = jog_dev;
+               break;
+
+       default:
+               if (event >= ARRAY_SIZE(sony_laptop_input_index)) {
+                       dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
+                       break;
+               }
+               if (sony_laptop_input_index[event] != -1) {
+                       kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
+                       if (kp.key != KEY_UNKNOWN)
+                               kp.dev = key_dev;
+               }
+               break;
+       }
+
+       if (kp.dev) {
+               input_report_key(kp.dev, kp.key, 1);
+               /* we emit the scancode so we can always remap the key */
+               input_event(kp.dev, EV_MSC, MSC_SCAN, event);
+               input_sync(kp.dev);
+               kfifo_put(sony_laptop_input.fifo,
+                         (unsigned char *)&kp, sizeof(kp));
+
+               if (!work_pending(&sony_laptop_release_key_work))
+                       queue_work(sony_laptop_input.wq,
+                                       &sony_laptop_release_key_work);
+       } else
+               dprintk("unknown input event %.2x\n", event);
+}
+
+static int sony_laptop_setup_input(struct acpi_device *acpi_device)
+{
+       struct input_dev *jog_dev;
+       struct input_dev *key_dev;
+       int i;
+       int error;
+
+       /* don't run again if already initialized */
+       if (atomic_add_return(1, &sony_laptop_input.users) > 1)
+               return 0;
+
+       /* kfifo */
+       spin_lock_init(&sony_laptop_input.fifo_lock);
+       sony_laptop_input.fifo =
+               kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+                           &sony_laptop_input.fifo_lock);
+       if (IS_ERR(sony_laptop_input.fifo)) {
+               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+               error = PTR_ERR(sony_laptop_input.fifo);
+               goto err_dec_users;
+       }
+
+       /* init workqueue */
+       sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop");
+       if (!sony_laptop_input.wq) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create workqueue.\n");
+               error = -ENXIO;
+               goto err_free_kfifo;
+       }
+
+       /* input keys */
+       key_dev = input_allocate_device();
+       if (!key_dev) {
+               error = -ENOMEM;
+               goto err_destroy_wq;
+       }
+
+       key_dev->name = "Sony Vaio Keys";
+       key_dev->id.bustype = BUS_ISA;
+       key_dev->id.vendor = PCI_VENDOR_ID_SONY;
+       key_dev->dev.parent = &acpi_device->dev;
+
+       /* Initialize the Input Drivers: special keys */
+       set_bit(EV_KEY, key_dev->evbit);
+       set_bit(EV_MSC, key_dev->evbit);
+       set_bit(MSC_SCAN, key_dev->mscbit);
+       key_dev->keycodesize = sizeof(sony_laptop_input_keycode_map[0]);
+       key_dev->keycodemax = ARRAY_SIZE(sony_laptop_input_keycode_map);
+       key_dev->keycode = &sony_laptop_input_keycode_map;
+       for (i = 0; i < ARRAY_SIZE(sony_laptop_input_keycode_map); i++) {
+               if (sony_laptop_input_keycode_map[i] != KEY_RESERVED) {
+                       set_bit(sony_laptop_input_keycode_map[i],
+                               key_dev->keybit);
+               }
+       }
+
+       error = input_register_device(key_dev);
+       if (error)
+               goto err_free_keydev;
+
+       sony_laptop_input.key_dev = key_dev;
+
+       /* jogdial */
+       jog_dev = input_allocate_device();
+       if (!jog_dev) {
+               error = -ENOMEM;
+               goto err_unregister_keydev;
+       }
+
+       jog_dev->name = "Sony Vaio Jogdial";
+       jog_dev->id.bustype = BUS_ISA;
+       jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
+       key_dev->dev.parent = &acpi_device->dev;
+
+       jog_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+       jog_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_MIDDLE);
+       jog_dev->relbit[0] = BIT_MASK(REL_WHEEL);
+
+       error = input_register_device(jog_dev);
+       if (error)
+               goto err_free_jogdev;
+
+       sony_laptop_input.jog_dev = jog_dev;
+
+       return 0;
+
+err_free_jogdev:
+       input_free_device(jog_dev);
+
+err_unregister_keydev:
+       input_unregister_device(key_dev);
+       /* to avoid kref underflow below at input_free_device */
+       key_dev = NULL;
+
+err_free_keydev:
+       input_free_device(key_dev);
+
+err_destroy_wq:
+       destroy_workqueue(sony_laptop_input.wq);
+
+err_free_kfifo:
+       kfifo_free(sony_laptop_input.fifo);
+
+err_dec_users:
+       atomic_dec(&sony_laptop_input.users);
+       return error;
+}
+
+static void sony_laptop_remove_input(void)
+{
+       /* cleanup only after the last user has gone */
+       if (!atomic_dec_and_test(&sony_laptop_input.users))
+               return;
+
+       /* flush workqueue first */
+       flush_workqueue(sony_laptop_input.wq);
+
+       /* destroy input devs */
+       input_unregister_device(sony_laptop_input.key_dev);
+       sony_laptop_input.key_dev = NULL;
+
+       if (sony_laptop_input.jog_dev) {
+               input_unregister_device(sony_laptop_input.jog_dev);
+               sony_laptop_input.jog_dev = NULL;
+       }
+
+       destroy_workqueue(sony_laptop_input.wq);
+       kfifo_free(sony_laptop_input.fifo);
+}
+
+/*********** Platform Device ***********/
+
+static atomic_t sony_pf_users = ATOMIC_INIT(0);
+static struct platform_driver sony_pf_driver = {
+       .driver = {
+                  .name = "sony-laptop",
+                  .owner = THIS_MODULE,
+                  }
+};
+static struct platform_device *sony_pf_device;
+
+static int sony_pf_add(void)
+{
+       int ret = 0;
+
+       /* don't run again if already initialized */
+       if (atomic_add_return(1, &sony_pf_users) > 1)
+               return 0;
+
+       ret = platform_driver_register(&sony_pf_driver);
+       if (ret)
+               goto out;
+
+       sony_pf_device = platform_device_alloc("sony-laptop", -1);
+       if (!sony_pf_device) {
+               ret = -ENOMEM;
+               goto out_platform_registered;
+       }
+
+       ret = platform_device_add(sony_pf_device);
+       if (ret)
+               goto out_platform_alloced;
+
+       return 0;
+
+      out_platform_alloced:
+       platform_device_put(sony_pf_device);
+       sony_pf_device = NULL;
+      out_platform_registered:
+       platform_driver_unregister(&sony_pf_driver);
+      out:
+       atomic_dec(&sony_pf_users);
+       return ret;
+}
+
+static void sony_pf_remove(void)
+{
+       /* deregister only after the last user has gone */
+       if (!atomic_dec_and_test(&sony_pf_users))
+               return;
+
+       platform_device_del(sony_pf_device);
+       platform_device_put(sony_pf_device);
+       platform_driver_unregister(&sony_pf_driver);
+}
+
+/*********** SNC (SNY5001) Device ***********/
+
+/* the device uses 1-based values, while the backlight subsystem uses
+   0-based values */
+#define SONY_MAX_BRIGHTNESS    8
+
+#define SNC_VALIDATE_IN                0
+#define SNC_VALIDATE_OUT       1
+
+static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *,
+                             char *);
+static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *,
+                              const char *, size_t);
+static int boolean_validate(const int, const int);
+static int brightness_default_validate(const int, const int);
+
+struct sony_nc_value {
+       char *name;             /* name of the entry */
+       char **acpiget;         /* names of the ACPI get function */
+       char **acpiset;         /* names of the ACPI set function */
+       int (*validate)(const int, const int);  /* input/output validation */
+       int value;              /* current setting */
+       int valid;              /* Has ever been set */
+       int debug;              /* active only in debug mode ? */
+       struct device_attribute devattr;        /* sysfs atribute */
+};
+
+#define SNC_HANDLE_NAMES(_name, _values...) \
+       static char *snc_##_name[] = { _values, NULL }
+
+#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \
+       { \
+               .name           = __stringify(_name), \
+               .acpiget        = _getters, \
+               .acpiset        = _setters, \
+               .validate       = _validate, \
+               .debug          = _debug, \
+               .devattr        = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \
+       }
+
+#define SNC_HANDLE_NULL        { .name = NULL }
+
+SNC_HANDLE_NAMES(fnkey_get, "GHKE");
+
+SNC_HANDLE_NAMES(brightness_def_get, "GPBR");
+SNC_HANDLE_NAMES(brightness_def_set, "SPBR");
+
+SNC_HANDLE_NAMES(cdpower_get, "GCDP");
+SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW");
+
+SNC_HANDLE_NAMES(audiopower_get, "GAZP");
+SNC_HANDLE_NAMES(audiopower_set, "AZPW");
+
+SNC_HANDLE_NAMES(lanpower_get, "GLNP");
+SNC_HANDLE_NAMES(lanpower_set, "LNPW");
+
+SNC_HANDLE_NAMES(lidstate_get, "GLID");
+
+SNC_HANDLE_NAMES(indicatorlamp_get, "GILS");
+SNC_HANDLE_NAMES(indicatorlamp_set, "SILS");
+
+SNC_HANDLE_NAMES(gainbass_get, "GMGB");
+SNC_HANDLE_NAMES(gainbass_set, "CMGB");
+
+SNC_HANDLE_NAMES(PID_get, "GPID");
+
+SNC_HANDLE_NAMES(CTR_get, "GCTR");
+SNC_HANDLE_NAMES(CTR_set, "SCTR");
+
+SNC_HANDLE_NAMES(PCR_get, "GPCR");
+SNC_HANDLE_NAMES(PCR_set, "SPCR");
+
+SNC_HANDLE_NAMES(CMI_get, "GCMI");
+SNC_HANDLE_NAMES(CMI_set, "SCMI");
+
+static struct sony_nc_value sony_nc_values[] = {
+       SNC_HANDLE(brightness_default, snc_brightness_def_get,
+                       snc_brightness_def_set, brightness_default_validate, 0),
+       SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0),
+       SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0),
+       SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set,
+                       boolean_validate, 0),
+       SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set,
+                       boolean_validate, 1),
+       SNC_HANDLE(lidstate, snc_lidstate_get, NULL,
+                       boolean_validate, 0),
+       SNC_HANDLE(indicatorlamp, snc_indicatorlamp_get, snc_indicatorlamp_set,
+                       boolean_validate, 0),
+       SNC_HANDLE(gainbass, snc_gainbass_get, snc_gainbass_set,
+                       boolean_validate, 0),
+       /* unknown methods */
+       SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1),
+       SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1),
+       SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1),
+       SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1),
+       SNC_HANDLE_NULL
+};
+
+static acpi_handle sony_nc_acpi_handle;
+static struct acpi_device *sony_nc_acpi_device = NULL;
+
+/*
+ * acpi_evaluate_object wrappers
+ */
+static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
+{
+       struct acpi_buffer output;
+       union acpi_object out_obj;
+       acpi_status status;
+
+       output.length = sizeof(out_obj);
+       output.pointer = &out_obj;
+
+       status = acpi_evaluate_object(handle, name, NULL, &output);
+       if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) {
+               *result = out_obj.integer.value;
+               return 0;
+       }
+
+       printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
+
+       return -1;
+}
+
+static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
+                           int *result)
+{
+       struct acpi_object_list params;
+       union acpi_object in_obj;
+       struct acpi_buffer output;
+       union acpi_object out_obj;
+       acpi_status status;
+
+       params.count = 1;
+       params.pointer = &in_obj;
+       in_obj.type = ACPI_TYPE_INTEGER;
+       in_obj.integer.value = value;
+
+       output.length = sizeof(out_obj);
+       output.pointer = &out_obj;
+
+       status = acpi_evaluate_object(handle, name, &params, &output);
+       if (status == AE_OK) {
+               if (result != NULL) {
+                       if (out_obj.type != ACPI_TYPE_INTEGER) {
+                               printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
+                                      "return type\n");
+                               return -1;
+                       }
+                       *result = out_obj.integer.value;
+               }
+               return 0;
+       }
+
+       printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
+
+       return -1;
+}
+
+/*
+ * sony_nc_values input/output validate functions
+ */
+
+/* brightness_default_validate:
+ *
+ * manipulate input output values to keep consistency with the
+ * backlight framework for which brightness values are 0-based.
+ */
+static int brightness_default_validate(const int direction, const int value)
+{
+       switch (direction) {
+               case SNC_VALIDATE_OUT:
+                       return value - 1;
+               case SNC_VALIDATE_IN:
+                       if (value >= 0 && value < SONY_MAX_BRIGHTNESS)
+                               return value + 1;
+       }
+       return -EINVAL;
+}
+
+/* boolean_validate:
+ *
+ * on input validate boolean values 0/1, on output just pass the
+ * received value.
+ */
+static int boolean_validate(const int direction, const int value)
+{
+       if (direction == SNC_VALIDATE_IN) {
+               if (value != 0 && value != 1)
+                       return -EINVAL;
+       }
+       return value;
+}
+
+/*
+ * Sysfs show/store common to all sony_nc_values
+ */
+static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr,
+                             char *buffer)
+{
+       int value;
+       struct sony_nc_value *item =
+           container_of(attr, struct sony_nc_value, devattr);
+
+       if (!*item->acpiget)
+               return -EIO;
+
+       if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0)
+               return -EIO;
+
+       if (item->validate)
+               value = item->validate(SNC_VALIDATE_OUT, value);
+
+       return snprintf(buffer, PAGE_SIZE, "%d\n", value);
+}
+
+static ssize_t sony_nc_sysfs_store(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buffer, size_t count)
+{
+       int value;
+       struct sony_nc_value *item =
+           container_of(attr, struct sony_nc_value, devattr);
+
+       if (!item->acpiset)
+               return -EIO;
+
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+
+       if (item->validate)
+               value = item->validate(SNC_VALIDATE_IN, value);
+
+       if (value < 0)
+               return value;
+
+       if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0)
+               return -EIO;
+       item->value = value;
+       item->valid = 1;
+       return count;
+}
+
+
+/*
+ * Backlight device
+ */
+static int sony_backlight_update_status(struct backlight_device *bd)
+{
+       return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
+                               bd->props.brightness + 1, NULL);
+}
+
+static int sony_backlight_get_brightness(struct backlight_device *bd)
+{
+       int value;
+
+       if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value))
+               return 0;
+       /* brightness levels are 1-based, while backlight ones are 0-based */
+       return value - 1;
+}
+
+static struct backlight_device *sony_backlight_device;
+static struct backlight_ops sony_backlight_ops = {
+       .update_status = sony_backlight_update_status,
+       .get_brightness = sony_backlight_get_brightness,
+};
+
+/*
+ * New SNC-only Vaios event mapping to driver known keys
+ */
+struct sony_nc_event {
+       u8      data;
+       u8      event;
+};
+
+static struct sony_nc_event *sony_nc_events;
+
+/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence
+ * for Fn keys
+ */
+static int sony_nc_C_enable(const struct dmi_system_id *id)
+{
+       int result = 0;
+
+       printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident);
+
+       sony_nc_events = id->driver_data;
+
+       if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0
+                       || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) {
+               printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some "
+                               "functionalities may be missing\n");
+               return 1;
+       }
+       return 0;
+}
+
+static struct sony_nc_event sony_C_events[] = {
+       { 0x81, SONYPI_EVENT_FNKEY_F1 },
+       { 0x01, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x85, SONYPI_EVENT_FNKEY_F5 },
+       { 0x05, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x86, SONYPI_EVENT_FNKEY_F6 },
+       { 0x06, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x87, SONYPI_EVENT_FNKEY_F7 },
+       { 0x07, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x8A, SONYPI_EVENT_FNKEY_F10 },
+       { 0x0A, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x8C, SONYPI_EVENT_FNKEY_F12 },
+       { 0x0C, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0, 0 },
+};
+
+/* SNC-only model map */
+static const struct dmi_system_id sony_nc_ids[] = {
+               {
+                       .ident = "Sony Vaio FE Series",
+                       .callback = sony_nc_C_enable,
+                       .driver_data = sony_C_events,
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"),
+                       },
+               },
+               {
+                       .ident = "Sony Vaio FZ Series",
+                       .callback = sony_nc_C_enable,
+                       .driver_data = sony_C_events,
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"),
+                       },
+               },
+               {
+                       .ident = "Sony Vaio C Series",
+                       .callback = sony_nc_C_enable,
+                       .driver_data = sony_C_events,
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"),
+                       },
+               },
+               {
+                       .ident = "Sony Vaio N Series",
+                       .callback = sony_nc_C_enable,
+                       .driver_data = sony_C_events,
+                       .matches = {
+                               DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                               DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"),
+                       },
+               },
+               { }
+};
+
+/*
+ * ACPI callbacks
+ */
+static void sony_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct sony_nc_event *evmap;
+       u32 ev = event;
+       int result;
+
+       if (ev == 0x92) {
+               /* read the key pressed from EC.GECR
+                * A call to SN07 with 0x0202 will do it as well respecting
+                * the current protocol on different OSes
+                *
+                * Note: the path for GECR may be
+                *   \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends)
+                *   \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR)
+                *
+                * TODO: we may want to do the same for the older GHKE -need
+                *       dmi list- so this snippet may become one more callback.
+                */
+               if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0)
+                       dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev);
+               else
+                       ev = result & 0xFF;
+       }
+
+       if (sony_nc_events)
+               for (evmap = sony_nc_events; evmap->event; evmap++) {
+                       if (evmap->data == ev) {
+                               ev = evmap->event;
+                               break;
+                       }
+               }
+
+       dprintk("sony_acpi_notify, event: 0x%.2x\n", ev);
+       sony_laptop_report_input_event(ev);
+       acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev);
+}
+
+static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
+                                     void *context, void **return_value)
+{
+       struct acpi_namespace_node *node;
+       union acpi_operand_object *operand;
+
+       node = (struct acpi_namespace_node *)handle;
+       operand = (union acpi_operand_object *)node->object;
+
+       printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii,
+              (u32) operand->method.param_count);
+
+       return AE_OK;
+}
+
+/*
+ * ACPI device
+ */
+static int sony_nc_resume(struct acpi_device *device)
+{
+       struct sony_nc_value *item;
+
+       for (item = sony_nc_values; item->name; item++) {
+               int ret;
+
+               if (!item->valid)
+                       continue;
+               ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
+                                      item->value, NULL);
+               if (ret < 0) {
+                       printk("%s: %d\n", __func__, ret);
+                       break;
+               }
+       }
+
+       /* set the last requested brightness level */
+       if (sony_backlight_device &&
+                       !sony_backlight_update_status(sony_backlight_device))
+               printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
+
+       /* re-initialize models with specific requirements */
+       dmi_check_system(sony_nc_ids);
+
+       return 0;
+}
+
+static int sony_nc_add(struct acpi_device *device)
+{
+       acpi_status status;
+       int result = 0;
+       acpi_handle handle;
+       struct sony_nc_value *item;
+
+       printk(KERN_INFO DRV_PFX "%s v%s.\n",
+               SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
+
+       sony_nc_acpi_device = device;
+       strcpy(acpi_device_class(device), "sony/hotkey");
+
+       sony_nc_acpi_handle = device->handle;
+
+       /* read device status */
+       result = acpi_bus_get_status(device);
+       /* bail IFF the above call was successful and the device is not present */
+       if (!result && !device->status.present) {
+               dprintk("Device not present\n");
+               result = -ENODEV;
+               goto outwalk;
+       }
+
+       if (debug) {
+               status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
+                                            1, sony_walk_callback, NULL, NULL);
+               if (ACPI_FAILURE(status)) {
+                       printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
+                       result = -ENODEV;
+                       goto outwalk;
+               }
+       }
+
+       /* try to _INI the device if such method exists (ACPI spec 3.0-6.5.1
+        * should be respected as we already checked for the device presence above */
+       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, METHOD_NAME__INI, &handle))) {
+               dprintk("Invoking _INI\n");
+               if (ACPI_FAILURE(acpi_evaluate_object(sony_nc_acpi_handle, METHOD_NAME__INI,
+                                               NULL, NULL)))
+                       dprintk("_INI Method failed\n");
+       }
+
+       /* setup input devices and helper fifo */
+       result = sony_laptop_setup_input(device);
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create input devices.\n");
+               goto outwalk;
+       }
+
+       status = acpi_install_notify_handler(sony_nc_acpi_handle,
+                                            ACPI_DEVICE_NOTIFY,
+                                            sony_acpi_notify, NULL);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_WARNING DRV_PFX "unable to install notify handler (%u)\n", status);
+               result = -ENODEV;
+               goto outinput;
+       }
+
+       if (acpi_video_backlight_support()) {
+               printk(KERN_INFO DRV_PFX "brightness ignored, must be "
+                      "controlled by ACPI video driver\n");
+       } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
+                                               &handle))) {
+               sony_backlight_device = backlight_device_register("sony", NULL,
+                                                                 NULL,
+                                                                 &sony_backlight_ops);
+
+               if (IS_ERR(sony_backlight_device)) {
+                       printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
+                       sony_backlight_device = NULL;
+               } else {
+                       sony_backlight_device->props.brightness =
+                           sony_backlight_get_brightness
+                           (sony_backlight_device);
+                       sony_backlight_device->props.max_brightness =
+                           SONY_MAX_BRIGHTNESS - 1;
+               }
+
+       }
+
+       /* initialize models with specific requirements */
+       dmi_check_system(sony_nc_ids);
+
+       result = sony_pf_add();
+       if (result)
+               goto outbacklight;
+
+       /* create sony_pf sysfs attributes related to the SNC device */
+       for (item = sony_nc_values; item->name; ++item) {
+
+               if (!debug && item->debug)
+                       continue;
+
+               /* find the available acpiget as described in the DSDT */
+               for (; item->acpiget && *item->acpiget; ++item->acpiget) {
+                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+                                                        *item->acpiget,
+                                                        &handle))) {
+                               dprintk("Found %s getter: %s\n",
+                                               item->name, *item->acpiget);
+                               item->devattr.attr.mode |= S_IRUGO;
+                               break;
+                       }
+               }
+
+               /* find the available acpiset as described in the DSDT */
+               for (; item->acpiset && *item->acpiset; ++item->acpiset) {
+                       if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
+                                                        *item->acpiset,
+                                                        &handle))) {
+                               dprintk("Found %s setter: %s\n",
+                                               item->name, *item->acpiset);
+                               item->devattr.attr.mode |= S_IWUSR;
+                               break;
+                       }
+               }
+
+               if (item->devattr.attr.mode != 0) {
+                       result =
+                           device_create_file(&sony_pf_device->dev,
+                                              &item->devattr);
+                       if (result)
+                               goto out_sysfs;
+               }
+       }
+
+       return 0;
+
+      out_sysfs:
+       for (item = sony_nc_values; item->name; ++item) {
+               device_remove_file(&sony_pf_device->dev, &item->devattr);
+       }
+       sony_pf_remove();
+
+      outbacklight:
+       if (sony_backlight_device)
+               backlight_device_unregister(sony_backlight_device);
+
+       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
+                                           ACPI_DEVICE_NOTIFY,
+                                           sony_acpi_notify);
+       if (ACPI_FAILURE(status))
+               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
+
+      outinput:
+       sony_laptop_remove_input();
+
+      outwalk:
+       return result;
+}
+
+static int sony_nc_remove(struct acpi_device *device, int type)
+{
+       acpi_status status;
+       struct sony_nc_value *item;
+
+       if (sony_backlight_device)
+               backlight_device_unregister(sony_backlight_device);
+
+       sony_nc_acpi_device = NULL;
+
+       status = acpi_remove_notify_handler(sony_nc_acpi_handle,
+                                           ACPI_DEVICE_NOTIFY,
+                                           sony_acpi_notify);
+       if (ACPI_FAILURE(status))
+               printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n");
+
+       for (item = sony_nc_values; item->name; ++item) {
+               device_remove_file(&sony_pf_device->dev, &item->devattr);
+       }
+
+       sony_pf_remove();
+       sony_laptop_remove_input();
+       dprintk(SONY_NC_DRIVER_NAME " removed.\n");
+
+       return 0;
+}
+
+static const struct acpi_device_id sony_device_ids[] = {
+       {SONY_NC_HID, 0},
+       {SONY_PIC_HID, 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, sony_device_ids);
+
+static const struct acpi_device_id sony_nc_device_ids[] = {
+       {SONY_NC_HID, 0},
+       {"", 0},
+};
+
+static struct acpi_driver sony_nc_driver = {
+       .name = SONY_NC_DRIVER_NAME,
+       .class = SONY_NC_CLASS,
+       .ids = sony_nc_device_ids,
+       .owner = THIS_MODULE,
+       .ops = {
+               .add = sony_nc_add,
+               .remove = sony_nc_remove,
+               .resume = sony_nc_resume,
+               },
+};
+
+/*********** SPIC (SNY6001) Device ***********/
+
+#define SONYPI_DEVICE_TYPE1    0x00000001
+#define SONYPI_DEVICE_TYPE2    0x00000002
+#define SONYPI_DEVICE_TYPE3    0x00000004
+#define SONYPI_DEVICE_TYPE4    0x00000008
+
+#define SONYPI_TYPE1_OFFSET    0x04
+#define SONYPI_TYPE2_OFFSET    0x12
+#define SONYPI_TYPE3_OFFSET    0x12
+#define SONYPI_TYPE4_OFFSET    0x12
+
+struct sony_pic_ioport {
+       struct acpi_resource_io io1;
+       struct acpi_resource_io io2;
+       struct list_head        list;
+};
+
+struct sony_pic_irq {
+       struct acpi_resource_irq        irq;
+       struct list_head                list;
+};
+
+struct sonypi_eventtypes {
+       u8                      data;
+       unsigned long           mask;
+       struct sonypi_event     *events;
+};
+
+struct device_ctrl {
+       int                             model;
+       int                             (*handle_irq)(const u8, const u8);
+       u16                             evport_offset;
+       u8                              has_camera;
+       u8                              has_bluetooth;
+       u8                              has_wwan;
+       struct sonypi_eventtypes        *event_types;
+};
+
+struct sony_pic_dev {
+       struct device_ctrl      *control;
+       struct acpi_device      *acpi_dev;
+       struct sony_pic_irq     *cur_irq;
+       struct sony_pic_ioport  *cur_ioport;
+       struct list_head        interrupts;
+       struct list_head        ioports;
+       struct mutex            lock;
+       u8                      camera_power;
+       u8                      bluetooth_power;
+       u8                      wwan_power;
+};
+
+static struct sony_pic_dev spic_dev = {
+       .interrupts     = LIST_HEAD_INIT(spic_dev.interrupts),
+       .ioports        = LIST_HEAD_INIT(spic_dev.ioports),
+};
+
+/* Event masks */
+#define SONYPI_JOGGER_MASK                     0x00000001
+#define SONYPI_CAPTURE_MASK                    0x00000002
+#define SONYPI_FNKEY_MASK                      0x00000004
+#define SONYPI_BLUETOOTH_MASK                  0x00000008
+#define SONYPI_PKEY_MASK                       0x00000010
+#define SONYPI_BACK_MASK                       0x00000020
+#define SONYPI_HELP_MASK                       0x00000040
+#define SONYPI_LID_MASK                                0x00000080
+#define SONYPI_ZOOM_MASK                       0x00000100
+#define SONYPI_THUMBPHRASE_MASK                        0x00000200
+#define SONYPI_MEYE_MASK                       0x00000400
+#define SONYPI_MEMORYSTICK_MASK                        0x00000800
+#define SONYPI_BATTERY_MASK                    0x00001000
+#define SONYPI_WIRELESS_MASK                   0x00002000
+
+struct sonypi_event {
+       u8      data;
+       u8      event;
+};
+
+/* The set of possible button release events */
+static struct sonypi_event sonypi_releaseev[] = {
+       { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED },
+       { 0, 0 }
+};
+
+/* The set of possible jogger events  */
+static struct sonypi_event sonypi_joggerev[] = {
+       { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
+       { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
+       { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
+       { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
+       { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP },
+       { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN },
+       { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED },
+       { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED },
+       { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP },
+       { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN },
+       { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED },
+       { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED },
+       { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible capture button events */
+static struct sonypi_event sonypi_captureev[] = {
+       { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
+       { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
+       { 0x40, SONYPI_EVENT_CAPTURE_PRESSED },
+       { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
+       { 0, 0 }
+};
+
+/* The set of possible fnkeys events */
+static struct sonypi_event sonypi_fnkeyev[] = {
+       { 0x10, SONYPI_EVENT_FNKEY_ESC },
+       { 0x11, SONYPI_EVENT_FNKEY_F1 },
+       { 0x12, SONYPI_EVENT_FNKEY_F2 },
+       { 0x13, SONYPI_EVENT_FNKEY_F3 },
+       { 0x14, SONYPI_EVENT_FNKEY_F4 },
+       { 0x15, SONYPI_EVENT_FNKEY_F5 },
+       { 0x16, SONYPI_EVENT_FNKEY_F6 },
+       { 0x17, SONYPI_EVENT_FNKEY_F7 },
+       { 0x18, SONYPI_EVENT_FNKEY_F8 },
+       { 0x19, SONYPI_EVENT_FNKEY_F9 },
+       { 0x1a, SONYPI_EVENT_FNKEY_F10 },
+       { 0x1b, SONYPI_EVENT_FNKEY_F11 },
+       { 0x1c, SONYPI_EVENT_FNKEY_F12 },
+       { 0x1f, SONYPI_EVENT_FNKEY_RELEASED },
+       { 0x21, SONYPI_EVENT_FNKEY_1 },
+       { 0x22, SONYPI_EVENT_FNKEY_2 },
+       { 0x31, SONYPI_EVENT_FNKEY_D },
+       { 0x32, SONYPI_EVENT_FNKEY_E },
+       { 0x33, SONYPI_EVENT_FNKEY_F },
+       { 0x34, SONYPI_EVENT_FNKEY_S },
+       { 0x35, SONYPI_EVENT_FNKEY_B },
+       { 0x36, SONYPI_EVENT_FNKEY_ONLY },
+       { 0, 0 }
+};
+
+/* The set of possible program key events */
+static struct sonypi_event sonypi_pkeyev[] = {
+       { 0x01, SONYPI_EVENT_PKEY_P1 },
+       { 0x02, SONYPI_EVENT_PKEY_P2 },
+       { 0x04, SONYPI_EVENT_PKEY_P3 },
+       { 0, 0 }
+};
+
+/* The set of possible bluetooth events */
+static struct sonypi_event sonypi_blueev[] = {
+       { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
+       { 0x59, SONYPI_EVENT_BLUETOOTH_ON },
+       { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
+       { 0, 0 }
+};
+
+/* The set of possible wireless events */
+static struct sonypi_event sonypi_wlessev[] = {
+       { 0x59, SONYPI_EVENT_WIRELESS_ON },
+       { 0x5a, SONYPI_EVENT_WIRELESS_OFF },
+       { 0, 0 }
+};
+
+/* The set of possible back button events */
+static struct sonypi_event sonypi_backev[] = {
+       { 0x20, SONYPI_EVENT_BACK_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible help button events */
+static struct sonypi_event sonypi_helpev[] = {
+       { 0x3b, SONYPI_EVENT_HELP_PRESSED },
+       { 0, 0 }
+};
+
+
+/* The set of possible lid events */
+static struct sonypi_event sonypi_lidev[] = {
+       { 0x51, SONYPI_EVENT_LID_CLOSED },
+       { 0x50, SONYPI_EVENT_LID_OPENED },
+       { 0, 0 }
+};
+
+/* The set of possible zoom events */
+static struct sonypi_event sonypi_zoomev[] = {
+       { 0x39, SONYPI_EVENT_ZOOM_PRESSED },
+       { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED },
+       { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible thumbphrase events */
+static struct sonypi_event sonypi_thumbphraseev[] = {
+       { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED },
+       { 0, 0 }
+};
+
+/* The set of possible motioneye camera events */
+static struct sonypi_event sonypi_meyeev[] = {
+       { 0x00, SONYPI_EVENT_MEYE_FACE },
+       { 0x01, SONYPI_EVENT_MEYE_OPPOSITE },
+       { 0, 0 }
+};
+
+/* The set of possible memorystick events */
+static struct sonypi_event sonypi_memorystickev[] = {
+       { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT },
+       { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT },
+       { 0, 0 }
+};
+
+/* The set of possible battery events */
+static struct sonypi_event sonypi_batteryev[] = {
+       { 0x20, SONYPI_EVENT_BATTERY_INSERT },
+       { 0x30, SONYPI_EVENT_BATTERY_REMOVE },
+       { 0, 0 }
+};
+
+static struct sonypi_eventtypes type1_events[] = {
+       { 0, 0xffffffff, sonypi_releaseev },
+       { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev },
+       { 0x30, SONYPI_LID_MASK, sonypi_lidev },
+       { 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev },
+       { 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+       { 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { 0 },
+};
+static struct sonypi_eventtypes type2_events[] = {
+       { 0, 0xffffffff, sonypi_releaseev },
+       { 0x38, SONYPI_LID_MASK, sonypi_lidev },
+       { 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev },
+       { 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev },
+       { 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0x11, SONYPI_BACK_MASK, sonypi_backev },
+       { 0x21, SONYPI_HELP_MASK, sonypi_helpev },
+       { 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev },
+       { 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev },
+       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0 },
+};
+static struct sonypi_eventtypes type3_events[] = {
+       { 0, 0xffffffff, sonypi_releaseev },
+       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0 },
+};
+static struct sonypi_eventtypes type4_events[] = {
+       { 0, 0xffffffff, sonypi_releaseev },
+       { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev },
+       { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev },
+       { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev },
+       { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev },
+       { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev },
+       { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev },
+       { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev },
+       { 0 },
+};
+
+/* low level spic calls */
+#define ITERATIONS_LONG                10000
+#define ITERATIONS_SHORT       10
+#define wait_on_command(command, iterations) {                         \
+       unsigned int n = iterations;                                    \
+       while (--n && (command))                                        \
+               udelay(1);                                              \
+       if (!n)                                                         \
+               dprintk("command failed at %s : %s (line %d)\n",        \
+                               __FILE__, __func__, __LINE__);  \
+}
+
+static u8 sony_pic_call1(u8 dev)
+{
+       u8 v1, v2;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+       v1 = inb_p(spic_dev.cur_ioport->io1.minimum + 4);
+       v2 = inb_p(spic_dev.cur_ioport->io1.minimum);
+       dprintk("sony_pic_call1(0x%.2x): 0x%.4x\n", dev, (v2 << 8) | v1);
+       return v2;
+}
+
+static u8 sony_pic_call2(u8 dev, u8 fn)
+{
+       u8 v1;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2,
+                       ITERATIONS_LONG);
+       outb(fn, spic_dev.cur_ioport->io1.minimum);
+       v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
+       dprintk("sony_pic_call2(0x%.2x - 0x%.2x): 0x%.4x\n", dev, fn, v1);
+       return v1;
+}
+
+static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
+{
+       u8 v1;
+
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(dev, spic_dev.cur_ioport->io1.minimum + 4);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(fn, spic_dev.cur_ioport->io1.minimum);
+       wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG);
+       outb(v, spic_dev.cur_ioport->io1.minimum);
+       v1 = inb_p(spic_dev.cur_ioport->io1.minimum);
+       dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n",
+                       dev, fn, v, v1);
+       return v1;
+}
+
+/*
+ * minidrivers for SPIC models
+ */
+static int type4_handle_irq(const u8 data_mask, const u8 ev)
+{
+       /*
+        * 0x31 could mean we have to take some extra action and wait for
+        * the next irq for some Type4 models, it will generate a new
+        * irq and we can read new data from the device:
+        *  - 0x5c and 0x5f requires 0xA0
+        *  - 0x61 requires 0xB3
+        */
+       if (data_mask == 0x31) {
+               if (ev == 0x5c || ev == 0x5f)
+                       sony_pic_call1(0xA0);
+               else if (ev == 0x61)
+                       sony_pic_call1(0xB3);
+               return 0;
+       }
+       return 1;
+}
+
+static struct device_ctrl spic_types[] = {
+       {
+               .model = SONYPI_DEVICE_TYPE1,
+               .handle_irq = NULL,
+               .evport_offset = SONYPI_TYPE1_OFFSET,
+               .event_types = type1_events,
+       },
+       {
+               .model = SONYPI_DEVICE_TYPE2,
+               .handle_irq = NULL,
+               .evport_offset = SONYPI_TYPE2_OFFSET,
+               .event_types = type2_events,
+       },
+       {
+               .model = SONYPI_DEVICE_TYPE3,
+               .handle_irq = NULL,
+               .evport_offset = SONYPI_TYPE3_OFFSET,
+               .event_types = type3_events,
+       },
+       {
+               .model = SONYPI_DEVICE_TYPE4,
+               .handle_irq = type4_handle_irq,
+               .evport_offset = SONYPI_TYPE4_OFFSET,
+               .event_types = type4_events,
+       },
+};
+
+static void sony_pic_detect_device_type(struct sony_pic_dev *dev)
+{
+       struct pci_dev *pcidev;
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
+       if (pcidev) {
+               dev->control = &spic_types[0];
+               goto out;
+       }
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_ICH6_1, NULL);
+       if (pcidev) {
+               dev->control = &spic_types[2];
+               goto out;
+       }
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_ICH7_1, NULL);
+       if (pcidev) {
+               dev->control = &spic_types[3];
+               goto out;
+       }
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
+                       PCI_DEVICE_ID_INTEL_ICH8_4, NULL);
+       if (pcidev) {
+               dev->control = &spic_types[3];
+               goto out;
+       }
+
+       /* default */
+       dev->control = &spic_types[1];
+
+out:
+       if (pcidev)
+               pci_dev_put(pcidev);
+
+       printk(KERN_INFO DRV_PFX "detected Type%d model\n",
+                       dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 :
+                       dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 :
+                       dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4);
+}
+
+/* camera tests and poweron/poweroff */
+#define SONYPI_CAMERA_PICTURE          5
+#define SONYPI_CAMERA_CONTROL          0x10
+
+#define SONYPI_CAMERA_BRIGHTNESS               0
+#define SONYPI_CAMERA_CONTRAST                 1
+#define SONYPI_CAMERA_HUE                      2
+#define SONYPI_CAMERA_COLOR                    3
+#define SONYPI_CAMERA_SHARPNESS                        4
+
+#define SONYPI_CAMERA_EXPOSURE_MASK            0xC
+#define SONYPI_CAMERA_WHITE_BALANCE_MASK       0x3
+#define SONYPI_CAMERA_PICTURE_MODE_MASK                0x30
+#define SONYPI_CAMERA_MUTE_MASK                        0x40
+
+/* the rest don't need a loop until not 0xff */
+#define SONYPI_CAMERA_AGC                      6
+#define SONYPI_CAMERA_AGC_MASK                 0x30
+#define SONYPI_CAMERA_SHUTTER_MASK             0x7
+
+#define SONYPI_CAMERA_SHUTDOWN_REQUEST         7
+#define SONYPI_CAMERA_CONTROL                  0x10
+
+#define SONYPI_CAMERA_STATUS                   7
+#define SONYPI_CAMERA_STATUS_READY             0x2
+#define SONYPI_CAMERA_STATUS_POSITION          0x4
+
+#define SONYPI_DIRECTION_BACKWARDS             0x4
+
+#define SONYPI_CAMERA_REVISION                         8
+#define SONYPI_CAMERA_ROMVERSION               9
+
+static int __sony_pic_camera_ready(void)
+{
+       u8 v;
+
+       v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
+       return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
+}
+
+static int __sony_pic_camera_off(void)
+{
+       if (!camera) {
+               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+               return -ENODEV;
+       }
+
+       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
+                               SONYPI_CAMERA_MUTE_MASK),
+                       ITERATIONS_SHORT);
+
+       if (spic_dev.camera_power) {
+               sony_pic_call2(0x91, 0);
+               spic_dev.camera_power = 0;
+       }
+       return 0;
+}
+
+static int __sony_pic_camera_on(void)
+{
+       int i, j, x;
+
+       if (!camera) {
+               printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
+               return -ENODEV;
+       }
+
+       if (spic_dev.camera_power)
+               return 0;
+
+       for (j = 5; j > 0; j--) {
+
+               for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++)
+                       msleep(10);
+               sony_pic_call1(0x93);
+
+               for (i = 400; i > 0; i--) {
+                       if (__sony_pic_camera_ready())
+                               break;
+                       msleep(10);
+               }
+               if (i)
+                       break;
+       }
+
+       if (j == 0) {
+               printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
+               return -ENODEV;
+       }
+
+       wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
+                               0x5a),
+                       ITERATIONS_SHORT);
+
+       spic_dev.camera_power = 1;
+       return 0;
+}
+
+/* External camera command (exported to the motion eye v4l driver) */
+int sony_pic_camera_command(int command, u8 value)
+{
+       if (!camera)
+               return -EIO;
+
+       mutex_lock(&spic_dev.lock);
+
+       switch (command) {
+       case SONY_PIC_COMMAND_SETCAMERA:
+               if (value)
+                       __sony_pic_camera_on();
+               else
+                       __sony_pic_camera_off();
+               break;
+       case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACONTRAST:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAHUE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACOLOR:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAPICTURE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAAGC:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
+                               ITERATIONS_SHORT);
+               break;
+       default:
+               printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
+                      command);
+               break;
+       }
+       mutex_unlock(&spic_dev.lock);
+       return 0;
+}
+EXPORT_SYMBOL(sony_pic_camera_command);
+
+/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
+static void sony_pic_set_wwanpower(u8 state)
+{
+       state = !!state;
+       mutex_lock(&spic_dev.lock);
+       if (spic_dev.wwan_power == state) {
+               mutex_unlock(&spic_dev.lock);
+               return;
+       }
+       sony_pic_call2(0xB0, state);
+       spic_dev.wwan_power = state;
+       mutex_unlock(&spic_dev.lock);
+}
+
+static ssize_t sony_pic_wwanpower_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       sony_pic_set_wwanpower(value);
+
+       return count;
+}
+
+static ssize_t sony_pic_wwanpower_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       ssize_t count;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
+}
+
+/* bluetooth subsystem power state */
+static void __sony_pic_set_bluetoothpower(u8 state)
+{
+       state = !!state;
+       if (spic_dev.bluetooth_power == state)
+               return;
+       sony_pic_call2(0x96, state);
+       sony_pic_call1(0x82);
+       spic_dev.bluetooth_power = state;
+}
+
+static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       mutex_lock(&spic_dev.lock);
+       __sony_pic_set_bluetoothpower(value);
+       mutex_unlock(&spic_dev.lock);
+
+       return count;
+}
+
+static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       ssize_t count = 0;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
+}
+
+/* fan speed */
+/* FAN0 information (reverse engineered from ACPI tables) */
+#define SONY_PIC_FAN0_STATUS   0x93
+static int sony_pic_set_fanspeed(unsigned long value)
+{
+       return ec_write(SONY_PIC_FAN0_STATUS, value);
+}
+
+static int sony_pic_get_fanspeed(u8 *value)
+{
+       return ec_read(SONY_PIC_FAN0_STATUS, value);
+}
+
+static ssize_t sony_pic_fanspeed_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buffer, size_t count)
+{
+       unsigned long value;
+       if (count > 31)
+               return -EINVAL;
+
+       value = simple_strtoul(buffer, NULL, 10);
+       if (sony_pic_set_fanspeed(value))
+               return -EIO;
+
+       return count;
+}
+
+static ssize_t sony_pic_fanspeed_show(struct device *dev,
+               struct device_attribute *attr, char *buffer)
+{
+       u8 value = 0;
+       if (sony_pic_get_fanspeed(&value))
+               return -EIO;
+
+       return snprintf(buffer, PAGE_SIZE, "%d\n", value);
+}
+
+#define SPIC_ATTR(_name, _mode)                                        \
+struct device_attribute spic_attr_##_name = __ATTR(_name,      \
+               _mode, sony_pic_## _name ##_show,               \
+               sony_pic_## _name ##_store)
+
+static SPIC_ATTR(bluetoothpower, 0644);
+static SPIC_ATTR(wwanpower, 0644);
+static SPIC_ATTR(fanspeed, 0644);
+
+static struct attribute *spic_attributes[] = {
+       &spic_attr_bluetoothpower.attr,
+       &spic_attr_wwanpower.attr,
+       &spic_attr_fanspeed.attr,
+       NULL
+};
+
+static struct attribute_group spic_attribute_group = {
+       .attrs = spic_attributes
+};
+
+/******** SONYPI compatibility **********/
+#ifdef CONFIG_SONYPI_COMPAT
+
+/* battery / brightness / temperature  addresses */
+#define SONYPI_BAT_FLAGS       0x81
+#define SONYPI_LCD_LIGHT       0x96
+#define SONYPI_BAT1_PCTRM      0xa0
+#define SONYPI_BAT1_LEFT       0xa2
+#define SONYPI_BAT1_MAXRT      0xa4
+#define SONYPI_BAT2_PCTRM      0xa8
+#define SONYPI_BAT2_LEFT       0xaa
+#define SONYPI_BAT2_MAXRT      0xac
+#define SONYPI_BAT1_MAXTK      0xb0
+#define SONYPI_BAT1_FULL       0xb2
+#define SONYPI_BAT2_MAXTK      0xb8
+#define SONYPI_BAT2_FULL       0xba
+#define SONYPI_TEMP_STATUS     0xC1
+
+struct sonypi_compat_s {
+       struct fasync_struct    *fifo_async;
+       struct kfifo            *fifo;
+       spinlock_t              fifo_lock;
+       wait_queue_head_t       fifo_proc_list;
+       atomic_t                open_count;
+};
+static struct sonypi_compat_s sonypi_compat = {
+       .open_count = ATOMIC_INIT(0),
+};
+
+static int sonypi_misc_fasync(int fd, struct file *filp, int on)
+{
+       int retval;
+
+       retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+static int sonypi_misc_release(struct inode *inode, struct file *file)
+{
+       atomic_dec(&sonypi_compat.open_count);
+       return 0;
+}
+
+static int sonypi_misc_open(struct inode *inode, struct file *file)
+{
+       /* Flush input queue on first open */
+       lock_kernel();
+       if (atomic_inc_return(&sonypi_compat.open_count) == 1)
+               kfifo_reset(sonypi_compat.fifo);
+       unlock_kernel();
+       return 0;
+}
+
+static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
+                               size_t count, loff_t *pos)
+{
+       ssize_t ret;
+       unsigned char c;
+
+       if ((kfifo_len(sonypi_compat.fifo) == 0) &&
+           (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+
+       ret = wait_event_interruptible(sonypi_compat.fifo_proc_list,
+                                      kfifo_len(sonypi_compat.fifo) != 0);
+       if (ret)
+               return ret;
+
+       while (ret < count &&
+              (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) {
+               if (put_user(c, buf++))
+                       return -EFAULT;
+               ret++;
+       }
+
+       if (ret > 0) {
+               struct inode *inode = file->f_path.dentry->d_inode;
+               inode->i_atime = current_fs_time(inode->i_sb);
+       }
+
+       return ret;
+}
+
+static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait)
+{
+       poll_wait(file, &sonypi_compat.fifo_proc_list, wait);
+       if (kfifo_len(sonypi_compat.fifo))
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+static int ec_read16(u8 addr, u16 *value)
+{
+       u8 val_lb, val_hb;
+       if (ec_read(addr, &val_lb))
+               return -1;
+       if (ec_read(addr + 1, &val_hb))
+               return -1;
+       *value = val_lb | (val_hb << 8);
+       return 0;
+}
+
+static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
+                            unsigned int cmd, unsigned long arg)
+{
+       int ret = 0;
+       void __user *argp = (void __user *)arg;
+       u8 val8;
+       u16 val16;
+       int value;
+
+       mutex_lock(&spic_dev.lock);
+       switch (cmd) {
+       case SONYPI_IOCGBRT:
+               if (sony_backlight_device == NULL) {
+                       ret = -EIO;
+                       break;
+               }
+               if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) {
+                       ret = -EIO;
+                       break;
+               }
+               val8 = ((value & 0xff) - 1) << 5;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                               ret = -EFAULT;
+               break;
+       case SONYPI_IOCSBRT:
+               if (sony_backlight_device == NULL) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT",
+                               (val8 >> 5) + 1, NULL)) {
+                       ret = -EIO;
+                       break;
+               }
+               /* sync the backlight device status */
+               sony_backlight_device->props.brightness =
+                   sony_backlight_get_brightness(sony_backlight_device);
+               break;
+       case SONYPI_IOCGBAT1CAP:
+               if (ec_read16(SONYPI_BAT1_FULL, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT1REM:
+               if (ec_read16(SONYPI_BAT1_LEFT, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT2CAP:
+               if (ec_read16(SONYPI_BAT2_FULL, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBAT2REM:
+               if (ec_read16(SONYPI_BAT2_LEFT, &val16)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val16, sizeof(val16)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBATFLAGS:
+               if (ec_read(SONYPI_BAT_FLAGS, &val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               val8 &= 0x07;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCGBLUE:
+               val8 = spic_dev.bluetooth_power;
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCSBLUE:
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               __sony_pic_set_bluetoothpower(val8);
+               break;
+       /* FAN Controls */
+       case SONYPI_IOCGFAN:
+               if (sony_pic_get_fanspeed(&val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       case SONYPI_IOCSFAN:
+               if (copy_from_user(&val8, argp, sizeof(val8))) {
+                       ret = -EFAULT;
+                       break;
+               }
+               if (sony_pic_set_fanspeed(val8))
+                       ret = -EIO;
+               break;
+       /* GET Temperature (useful under APM) */
+       case SONYPI_IOCGTEMP:
+               if (ec_read(SONYPI_TEMP_STATUS, &val8)) {
+                       ret = -EIO;
+                       break;
+               }
+               if (copy_to_user(argp, &val8, sizeof(val8)))
+                       ret = -EFAULT;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       mutex_unlock(&spic_dev.lock);
+       return ret;
+}
+
+static const struct file_operations sonypi_misc_fops = {
+       .owner          = THIS_MODULE,
+       .read           = sonypi_misc_read,
+       .poll           = sonypi_misc_poll,
+       .open           = sonypi_misc_open,
+       .release        = sonypi_misc_release,
+       .fasync         = sonypi_misc_fasync,
+       .ioctl          = sonypi_misc_ioctl,
+};
+
+static struct miscdevice sonypi_misc_device = {
+       .minor          = MISC_DYNAMIC_MINOR,
+       .name           = "sonypi",
+       .fops           = &sonypi_misc_fops,
+};
+
+static void sonypi_compat_report_event(u8 event)
+{
+       kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event));
+       kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN);
+       wake_up_interruptible(&sonypi_compat.fifo_proc_list);
+}
+
+static int sonypi_compat_init(void)
+{
+       int error;
+
+       spin_lock_init(&sonypi_compat.fifo_lock);
+       sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL,
+                                        &sonypi_compat.fifo_lock);
+       if (IS_ERR(sonypi_compat.fifo)) {
+               printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
+               return PTR_ERR(sonypi_compat.fifo);
+       }
+
+       init_waitqueue_head(&sonypi_compat.fifo_proc_list);
+
+       if (minor != -1)
+               sonypi_misc_device.minor = minor;
+       error = misc_register(&sonypi_misc_device);
+       if (error) {
+               printk(KERN_ERR DRV_PFX "misc_register failed\n");
+               goto err_free_kfifo;
+       }
+       if (minor == -1)
+               printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
+                      sonypi_misc_device.minor);
+
+       return 0;
+
+err_free_kfifo:
+       kfifo_free(sonypi_compat.fifo);
+       return error;
+}
+
+static void sonypi_compat_exit(void)
+{
+       misc_deregister(&sonypi_misc_device);
+       kfifo_free(sonypi_compat.fifo);
+}
+#else
+static int sonypi_compat_init(void) { return 0; }
+static void sonypi_compat_exit(void) { }
+static void sonypi_compat_report_event(u8 event) { }
+#endif /* CONFIG_SONYPI_COMPAT */
+
+/*
+ * ACPI callbacks
+ */
+static acpi_status
+sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
+{
+       u32 i;
+       struct sony_pic_dev *dev = (struct sony_pic_dev *)context;
+
+       switch (resource->type) {
+       case ACPI_RESOURCE_TYPE_START_DEPENDENT:
+               {
+                       /* start IO enumeration */
+                       struct sony_pic_ioport *ioport = kzalloc(sizeof(*ioport), GFP_KERNEL);
+                       if (!ioport)
+                               return AE_ERROR;
+
+                       list_add(&ioport->list, &dev->ioports);
+                       return AE_OK;
+               }
+
+       case ACPI_RESOURCE_TYPE_END_DEPENDENT:
+               /* end IO enumeration */
+               return AE_OK;
+
+       case ACPI_RESOURCE_TYPE_IRQ:
+               {
+                       struct acpi_resource_irq *p = &resource->data.irq;
+                       struct sony_pic_irq *interrupt = NULL;
+                       if (!p || !p->interrupt_count) {
+                               /*
+                                * IRQ descriptors may have no IRQ# bits set,
+                                * particularly those those w/ _STA disabled
+                                */
+                               dprintk("Blank IRQ resource\n");
+                               return AE_OK;
+                       }
+                       for (i = 0; i < p->interrupt_count; i++) {
+                               if (!p->interrupts[i]) {
+                                       printk(KERN_WARNING DRV_PFX
+                                                       "Invalid IRQ %d\n",
+                                                       p->interrupts[i]);
+                                       continue;
+                               }
+                               interrupt = kzalloc(sizeof(*interrupt),
+                                               GFP_KERNEL);
+                               if (!interrupt)
+                                       return AE_ERROR;
+
+                               list_add(&interrupt->list, &dev->interrupts);
+                               interrupt->irq.triggering = p->triggering;
+                               interrupt->irq.polarity = p->polarity;
+                               interrupt->irq.sharable = p->sharable;
+                               interrupt->irq.interrupt_count = 1;
+                               interrupt->irq.interrupts[0] = p->interrupts[i];
+                       }
+                       return AE_OK;
+               }
+       case ACPI_RESOURCE_TYPE_IO:
+               {
+                       struct acpi_resource_io *io = &resource->data.io;
+                       struct sony_pic_ioport *ioport =
+                               list_first_entry(&dev->ioports, struct sony_pic_ioport, list);
+                       if (!io) {
+                               dprintk("Blank IO resource\n");
+                               return AE_OK;
+                       }
+
+                       if (!ioport->io1.minimum) {
+                               memcpy(&ioport->io1, io, sizeof(*io));
+                               dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum,
+                                               ioport->io1.address_length);
+                       }
+                       else if (!ioport->io2.minimum) {
+                               memcpy(&ioport->io2, io, sizeof(*io));
+                               dprintk("IO2 at 0x%.4x (0x%.2x)\n", ioport->io2.minimum,
+                                               ioport->io2.address_length);
+                       }
+                       else {
+                               printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
+                               return AE_ERROR;
+                       }
+                       return AE_OK;
+               }
+       default:
+               dprintk("Resource %d isn't an IRQ nor an IO port\n",
+                               resource->type);
+
+       case ACPI_RESOURCE_TYPE_END_TAG:
+               return AE_OK;
+       }
+       return AE_CTRL_TERMINATE;
+}
+
+static int sony_pic_possible_resources(struct acpi_device *device)
+{
+       int result = 0;
+       acpi_status status = AE_OK;
+
+       if (!device)
+               return -EINVAL;
+
+       /* get device status */
+       /* see acpi_pci_link_get_current acpi_pci_link_get_possible */
+       dprintk("Evaluating _STA\n");
+       result = acpi_bus_get_status(device);
+       if (result) {
+               printk(KERN_WARNING DRV_PFX "Unable to read status\n");
+               goto end;
+       }
+
+       if (!device->status.enabled)
+               dprintk("Device disabled\n");
+       else
+               dprintk("Device enabled\n");
+
+       /*
+        * Query and parse 'method'
+        */
+       dprintk("Evaluating %s\n", METHOD_NAME__PRS);
+       status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
+                       sony_pic_read_possible_resource, &spic_dev);
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_WARNING DRV_PFX
+                               "Failure evaluating %s\n",
+                               METHOD_NAME__PRS);
+               result = -ENODEV;
+       }
+end:
+       return result;
+}
+
+/*
+ *  Disable the spic device by calling its _DIS method
+ */
+static int sony_pic_disable(struct acpi_device *device)
+{
+       acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL,
+                                              NULL);
+
+       if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND)
+               return -ENXIO;
+
+       dprintk("Device disabled\n");
+       return 0;
+}
+
+
+/*
+ *  Based on drivers/acpi/pci_link.c:acpi_pci_link_set
+ *
+ *  Call _SRS to set current resources
+ */
+static int sony_pic_enable(struct acpi_device *device,
+               struct sony_pic_ioport *ioport, struct sony_pic_irq *irq)
+{
+       acpi_status status;
+       int result = 0;
+       /* Type 1 resource layout is:
+        *    IO
+        *    IO
+        *    IRQNoFlags
+        *    End
+        *
+        * Type 2 and 3 resource layout is:
+        *    IO
+        *    IRQNoFlags
+        *    End
+        */
+       struct {
+               struct acpi_resource res1;
+               struct acpi_resource res2;
+               struct acpi_resource res3;
+               struct acpi_resource res4;
+       } *resource;
+       struct acpi_buffer buffer = { 0, NULL };
+
+       if (!ioport || !irq)
+               return -EINVAL;
+
+       /* init acpi_buffer */
+       resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL);
+       if (!resource)
+               return -ENOMEM;
+
+       buffer.length = sizeof(*resource) + 1;
+       buffer.pointer = resource;
+
+       /* setup Type 1 resources */
+       if (spic_dev.control->model == SONYPI_DEVICE_TYPE1) {
+
+               /* setup io resources */
+               resource->res1.type = ACPI_RESOURCE_TYPE_IO;
+               resource->res1.length = sizeof(struct acpi_resource);
+               memcpy(&resource->res1.data.io, &ioport->io1,
+                               sizeof(struct acpi_resource_io));
+
+               resource->res2.type = ACPI_RESOURCE_TYPE_IO;
+               resource->res2.length = sizeof(struct acpi_resource);
+               memcpy(&resource->res2.data.io, &ioport->io2,
+                               sizeof(struct acpi_resource_io));
+
+               /* setup irq resource */
+               resource->res3.type = ACPI_RESOURCE_TYPE_IRQ;
+               resource->res3.length = sizeof(struct acpi_resource);
+               memcpy(&resource->res3.data.irq, &irq->irq,
+                               sizeof(struct acpi_resource_irq));
+               /* we requested a shared irq */
+               resource->res3.data.irq.sharable = ACPI_SHARED;
+
+               resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
+
+       }
+       /* setup Type 2/3 resources */
+       else {
+               /* setup io resource */
+               resource->res1.type = ACPI_RESOURCE_TYPE_IO;
+               resource->res1.length = sizeof(struct acpi_resource);
+               memcpy(&resource->res1.data.io, &ioport->io1,
+                               sizeof(struct acpi_resource_io));
+
+               /* setup irq resource */
+               resource->res2.type = ACPI_RESOURCE_TYPE_IRQ;
+               resource->res2.length = sizeof(struct acpi_resource);
+               memcpy(&resource->res2.data.irq, &irq->irq,
+                               sizeof(struct acpi_resource_irq));
+               /* we requested a shared irq */
+               resource->res2.data.irq.sharable = ACPI_SHARED;
+
+               resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
+       }
+
+       /* Attempt to set the resource */
+       dprintk("Evaluating _SRS\n");
+       status = acpi_set_current_resources(device->handle, &buffer);
+
+       /* check for total failure */
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
+               result = -ENODEV;
+               goto end;
+       }
+
+       /* Necessary device initializations calls (from sonypi) */
+       sony_pic_call1(0x82);
+       sony_pic_call2(0x81, 0xff);
+       sony_pic_call1(compat ? 0x92 : 0x82);
+
+end:
+       kfree(resource);
+       return result;
+}
+
+/*****************
+ *
+ * ISR: some event is available
+ *
+ *****************/
+static irqreturn_t sony_pic_irq(int irq, void *dev_id)
+{
+       int i, j;
+       u8 ev = 0;
+       u8 data_mask = 0;
+       u8 device_event = 0;
+
+       struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id;
+
+       ev = inb_p(dev->cur_ioport->io1.minimum);
+       if (dev->cur_ioport->io2.minimum)
+               data_mask = inb_p(dev->cur_ioport->io2.minimum);
+       else
+               data_mask = inb_p(dev->cur_ioport->io1.minimum +
+                               dev->control->evport_offset);
+
+       dprintk("event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
+                       ev, data_mask, dev->cur_ioport->io1.minimum,
+                       dev->control->evport_offset);
+
+       if (ev == 0x00 || ev == 0xff)
+               return IRQ_HANDLED;
+
+       for (i = 0; dev->control->event_types[i].mask; i++) {
+
+               if ((data_mask & dev->control->event_types[i].data) !=
+                   dev->control->event_types[i].data)
+                       continue;
+
+               if (!(mask & dev->control->event_types[i].mask))
+                       continue;
+
+               for (j = 0; dev->control->event_types[i].events[j].event; j++) {
+                       if (ev == dev->control->event_types[i].events[j].data) {
+                               device_event =
+                                       dev->control->
+                                               event_types[i].events[j].event;
+                               goto found;
+                       }
+               }
+       }
+       /* Still not able to decode the event try to pass
+        * it over to the minidriver
+        */
+       if (dev->control->handle_irq &&
+                       dev->control->handle_irq(data_mask, ev) == 0)
+               return IRQ_HANDLED;
+
+       dprintk("unknown event ([%.2x] [%.2x]) at port 0x%.4x(+0x%.2x)\n",
+                       ev, data_mask, dev->cur_ioport->io1.minimum,
+                       dev->control->evport_offset);
+       return IRQ_HANDLED;
+
+found:
+       sony_laptop_report_input_event(device_event);
+       acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
+       sonypi_compat_report_event(device_event);
+
+       return IRQ_HANDLED;
+}
+
+/*****************
+ *
+ *  ACPI driver
+ *
+ *****************/
+static int sony_pic_remove(struct acpi_device *device, int type)
+{
+       struct sony_pic_ioport *io, *tmp_io;
+       struct sony_pic_irq *irq, *tmp_irq;
+
+       if (sony_pic_disable(device)) {
+               printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
+               return -ENXIO;
+       }
+
+       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+       release_region(spic_dev.cur_ioport->io1.minimum,
+                       spic_dev.cur_ioport->io1.address_length);
+       if (spic_dev.cur_ioport->io2.minimum)
+               release_region(spic_dev.cur_ioport->io2.minimum,
+                               spic_dev.cur_ioport->io2.address_length);
+
+       sonypi_compat_exit();
+
+       sony_laptop_remove_input();
+
+       /* pf attrs */
+       sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+       sony_pf_remove();
+
+       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+               list_del(&io->list);
+               kfree(io);
+       }
+       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+               list_del(&irq->list);
+               kfree(irq);
+       }
+       spic_dev.cur_ioport = NULL;
+       spic_dev.cur_irq = NULL;
+
+       dprintk(SONY_PIC_DRIVER_NAME " removed.\n");
+       return 0;
+}
+
+static int sony_pic_add(struct acpi_device *device)
+{
+       int result;
+       struct sony_pic_ioport *io, *tmp_io;
+       struct sony_pic_irq *irq, *tmp_irq;
+
+       printk(KERN_INFO DRV_PFX "%s v%s.\n",
+               SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
+
+       spic_dev.acpi_dev = device;
+       strcpy(acpi_device_class(device), "sony/hotkey");
+       sony_pic_detect_device_type(&spic_dev);
+       mutex_init(&spic_dev.lock);
+
+       /* read _PRS resources */
+       result = sony_pic_possible_resources(device);
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to read possible resources.\n");
+               goto err_free_resources;
+       }
+
+       /* setup input devices and helper fifo */
+       result = sony_laptop_setup_input(device);
+       if (result) {
+               printk(KERN_ERR DRV_PFX
+                               "Unabe to create input devices.\n");
+               goto err_free_resources;
+       }
+
+       if (sonypi_compat_init())
+               goto err_remove_input;
+
+       /* request io port */
+       list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
+               if (request_region(io->io1.minimum, io->io1.address_length,
+                                       "Sony Programable I/O Device")) {
+                       dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
+                                       io->io1.minimum, io->io1.maximum,
+                                       io->io1.address_length);
+                       /* Type 1 have 2 ioports */
+                       if (io->io2.minimum) {
+                               if (request_region(io->io2.minimum,
+                                               io->io2.address_length,
+                                               "Sony Programable I/O Device")) {
+                                       dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
+                                                       io->io2.minimum, io->io2.maximum,
+                                                       io->io2.address_length);
+                                       spic_dev.cur_ioport = io;
+                                       break;
+                               }
+                               else {
+                                       dprintk("Unable to get I/O port2: "
+                                                       "0x%.4x (0x%.4x) + 0x%.2x\n",
+                                                       io->io2.minimum, io->io2.maximum,
+                                                       io->io2.address_length);
+                                       release_region(io->io1.minimum,
+                                                       io->io1.address_length);
+                               }
+                       }
+                       else {
+                               spic_dev.cur_ioport = io;
+                               break;
+                       }
+               }
+       }
+       if (!spic_dev.cur_ioport) {
+               printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
+               result = -ENODEV;
+               goto err_remove_compat;
+       }
+
+       /* request IRQ */
+       list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
+               if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
+                                       IRQF_SHARED, "sony-laptop", &spic_dev)) {
+                       dprintk("IRQ: %d - triggering: %d - "
+                                       "polarity: %d - shr: %d\n",
+                                       irq->irq.interrupts[0],
+                                       irq->irq.triggering,
+                                       irq->irq.polarity,
+                                       irq->irq.sharable);
+                       spic_dev.cur_irq = irq;
+                       break;
+               }
+       }
+       if (!spic_dev.cur_irq) {
+               printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
+               result = -ENODEV;
+               goto err_release_region;
+       }
+
+       /* set resource status _SRS */
+       result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+       if (result) {
+               printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
+               goto err_free_irq;
+       }
+
+       spic_dev.bluetooth_power = -1;
+       /* create device attributes */
+       result = sony_pf_add();
+       if (result)
+               goto err_disable_device;
+
+       result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+       if (result)
+               goto err_remove_pf;
+
+       return 0;
+
+err_remove_pf:
+       sony_pf_remove();
+
+err_disable_device:
+       sony_pic_disable(device);
+
+err_free_irq:
+       free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);
+
+err_release_region:
+       release_region(spic_dev.cur_ioport->io1.minimum,
+                       spic_dev.cur_ioport->io1.address_length);
+       if (spic_dev.cur_ioport->io2.minimum)
+               release_region(spic_dev.cur_ioport->io2.minimum,
+                               spic_dev.cur_ioport->io2.address_length);
+
+err_remove_compat:
+       sonypi_compat_exit();
+
+err_remove_input:
+       sony_laptop_remove_input();
+
+err_free_resources:
+       list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
+               list_del(&io->list);
+               kfree(io);
+       }
+       list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) {
+               list_del(&irq->list);
+               kfree(irq);
+       }
+       spic_dev.cur_ioport = NULL;
+       spic_dev.cur_irq = NULL;
+
+       return result;
+}
+
+static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
+{
+       if (sony_pic_disable(device))
+               return -ENXIO;
+       return 0;
+}
+
+static int sony_pic_resume(struct acpi_device *device)
+{
+       sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+       return 0;
+}
+
+static const struct acpi_device_id sony_pic_device_ids[] = {
+       {SONY_PIC_HID, 0},
+       {"", 0},
+};
+
+static struct acpi_driver sony_pic_driver = {
+       .name = SONY_PIC_DRIVER_NAME,
+       .class = SONY_PIC_CLASS,
+       .ids = sony_pic_device_ids,
+       .owner = THIS_MODULE,
+       .ops = {
+               .add = sony_pic_add,
+               .remove = sony_pic_remove,
+               .suspend = sony_pic_suspend,
+               .resume = sony_pic_resume,
+               },
+};
+
+static struct dmi_system_id __initdata sonypi_dmi_table[] = {
+       {
+               .ident = "Sony Vaio",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"),
+               },
+       },
+       {
+               .ident = "Sony Vaio",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"),
+               },
+       },
+       { }
+};
+
+static int __init sony_laptop_init(void)
+{
+       int result;
+
+       if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
+               result = acpi_bus_register_driver(&sony_pic_driver);
+               if (result) {
+                       printk(KERN_ERR DRV_PFX
+                                       "Unable to register SPIC driver.");
+                       goto out;
+               }
+       }
+
+       result = acpi_bus_register_driver(&sony_nc_driver);
+       if (result) {
+               printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
+               goto out_unregister_pic;
+       }
+
+       return 0;
+
+out_unregister_pic:
+       if (!no_spic)
+               acpi_bus_unregister_driver(&sony_pic_driver);
+out:
+       return result;
+}
+
+static void __exit sony_laptop_exit(void)
+{
+       acpi_bus_unregister_driver(&sony_nc_driver);
+       if (!no_spic)
+               acpi_bus_unregister_driver(&sony_pic_driver);
+}
+
+module_init(sony_laptop_init);
+module_exit(sony_laptop_exit);
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c
new file mode 100644 (file)
index 0000000..f25e4c9
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ *  HP Compaq TC1100 Tablet WMI Extras Driver
+ *
+ *  Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk>
+ *  Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com>
+ *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
+ *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/platform_device.h>
+
+#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
+
+#define TC1100_INSTANCE_WIRELESS               1
+#define TC1100_INSTANCE_JOGDIAL                2
+
+#define TC1100_LOGPREFIX "tc1100-wmi: "
+#define TC1100_INFO KERN_INFO TC1100_LOGPREFIX
+
+MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho");
+MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505");
+
+static int tc1100_probe(struct platform_device *device);
+static int tc1100_remove(struct platform_device *device);
+static int tc1100_suspend(struct platform_device *device, pm_message_t state);
+static int tc1100_resume(struct platform_device *device);
+
+static struct platform_driver tc1100_driver = {
+       .driver = {
+               .name = "tc1100-wmi",
+               .owner = THIS_MODULE,
+       },
+       .probe = tc1100_probe,
+       .remove = tc1100_remove,
+       .suspend = tc1100_suspend,
+       .resume = tc1100_resume,
+};
+
+static struct platform_device *tc1100_device;
+
+struct tc1100_data {
+       u32 wireless;
+       u32 jogdial;
+};
+
+static struct tc1100_data suspend_data;
+
+/* --------------------------------------------------------------------------
+                               Device Management
+   -------------------------------------------------------------------------- */
+
+static int get_state(u32 *out, u8 instance)
+{
+       u32 tmp;
+       acpi_status status;
+       struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+
+       if (!out)
+               return -EINVAL;
+
+       if (instance > 2)
+               return -ENODEV;
+
+       status = wmi_query_block(GUID, instance, &result);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       obj = (union acpi_object *) result.pointer;
+       if (obj && obj->type == ACPI_TYPE_BUFFER &&
+               obj->buffer.length == sizeof(u32)) {
+               tmp = *((u32 *) obj->buffer.pointer);
+       } else {
+               tmp = 0;
+       }
+
+       if (result.length > 0 && result.pointer)
+               kfree(result.pointer);
+
+       switch (instance) {
+       case TC1100_INSTANCE_WIRELESS:
+               *out = (tmp == 3) ? 1 : 0;
+               return 0;
+       case TC1100_INSTANCE_JOGDIAL:
+               *out = (tmp == 1) ? 1 : 0;
+               return 0;
+       default:
+               return -ENODEV;
+       }
+}
+
+static int set_state(u32 *in, u8 instance)
+{
+       u32 value;
+       acpi_status status;
+       struct acpi_buffer input;
+
+       if (!in)
+               return -EINVAL;
+
+       if (instance > 2)
+               return -ENODEV;
+
+       switch (instance) {
+       case TC1100_INSTANCE_WIRELESS:
+               value = (*in) ? 1 : 2;
+               break;
+       case TC1100_INSTANCE_JOGDIAL:
+               value = (*in) ? 0 : 1;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       input.length = sizeof(u32);
+       input.pointer = &value;
+
+       status = wmi_set_block(GUID, instance, &input);
+       if (ACPI_FAILURE(status))
+               return -ENODEV;
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------------
+                               FS Interface (/sys)
+   -------------------------------------------------------------------------- */
+
+/*
+ * Read/ write bool sysfs macro
+ */
+#define show_set_bool(value, instance) \
+static ssize_t \
+show_bool_##value(struct device *dev, struct device_attribute *attr, \
+       char *buf) \
+{ \
+       u32 result; \
+       acpi_status status = get_state(&result, instance); \
+       if (ACPI_SUCCESS(status)) \
+               return sprintf(buf, "%d\n", result); \
+       return sprintf(buf, "Read error\n"); \
+} \
+\
+static ssize_t \
+set_bool_##value(struct device *dev, struct device_attribute *attr, \
+       const char *buf, size_t count) \
+{ \
+       u32 tmp = simple_strtoul(buf, NULL, 10); \
+       acpi_status status = set_state(&tmp, instance); \
+               if (ACPI_FAILURE(status)) \
+                       return -EINVAL; \
+       return count; \
+} \
+static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
+       show_bool_##value, set_bool_##value);
+
+show_set_bool(wireless, TC1100_INSTANCE_WIRELESS);
+show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL);
+
+static void remove_fs(void)
+{
+       device_remove_file(&tc1100_device->dev, &dev_attr_wireless);
+       device_remove_file(&tc1100_device->dev, &dev_attr_jogdial);
+}
+
+static int add_fs(void)
+{
+       int ret;
+
+       ret = device_create_file(&tc1100_device->dev, &dev_attr_wireless);
+       if (ret)
+               goto add_sysfs_error;
+
+       ret = device_create_file(&tc1100_device->dev, &dev_attr_jogdial);
+       if (ret)
+               goto add_sysfs_error;
+
+       return ret;
+
+add_sysfs_error:
+       remove_fs();
+       return ret;
+}
+
+/* --------------------------------------------------------------------------
+                               Driver Model
+   -------------------------------------------------------------------------- */
+
+static int tc1100_probe(struct platform_device *device)
+{
+       int result = 0;
+
+       result = add_fs();
+       return result;
+}
+
+
+static int tc1100_remove(struct platform_device *device)
+{
+       remove_fs();
+       return 0;
+}
+
+static int tc1100_suspend(struct platform_device *dev, pm_message_t state)
+{
+       int ret;
+
+       ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
+       if (ret)
+               return ret;
+
+       ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int tc1100_resume(struct platform_device *dev)
+{
+       int ret;
+
+       ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS);
+       if (ret)
+               return ret;
+
+       ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int __init tc1100_init(void)
+{
+       int result = 0;
+
+       if (!wmi_has_guid(GUID))
+               return -ENODEV;
+
+       result = platform_driver_register(&tc1100_driver);
+       if (result)
+               return result;
+
+       tc1100_device = platform_device_alloc("tc1100-wmi", -1);
+       platform_device_add(tc1100_device);
+
+       printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras loaded\n");
+
+       return result;
+}
+
+static void __exit tc1100_exit(void)
+{
+       platform_device_del(tc1100_device);
+       platform_driver_unregister(&tc1100_driver);
+
+       printk(TC1100_INFO "HP Compaq TC1100 Tablet WMI Extras unloaded\n");
+}
+
+module_init(tc1100_init);
+module_exit(tc1100_exit);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
new file mode 100644 (file)
index 0000000..899766e
--- /dev/null
@@ -0,0 +1,6949 @@
+/*
+ *  thinkpad_acpi.c - ThinkPad ACPI Extras
+ *
+ *
+ *  Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
+ *  Copyright (C) 2006-2008 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ *  02110-1301, USA.
+ */
+
+#define TPACPI_VERSION "0.21"
+#define TPACPI_SYSFS_VERSION 0x020200
+
+/*
+ *  Changelog:
+ *  2007-10-20         changelog trimmed down
+ *
+ *  2007-03-27  0.14   renamed to thinkpad_acpi and moved to
+ *                     drivers/misc.
+ *
+ *  2006-11-22 0.13    new maintainer
+ *                     changelog now lives in git commit history, and will
+ *                     not be updated further in-file.
+ *
+ *  2005-03-17 0.11    support for 600e, 770x
+ *                         thanks to Jamie Lentin <lentinj@dial.pipex.com>
+ *
+ *  2005-01-16 0.9     use MODULE_VERSION
+ *                         thanks to Henrik Brix Andersen <brix@gentoo.org>
+ *                     fix parameter passing on module loading
+ *                         thanks to Rusty Russell <rusty@rustcorp.com.au>
+ *                         thanks to Jim Radford <radford@blackbean.org>
+ *  2004-11-08 0.8     fix init error case, don't return from a macro
+ *                         thanks to Chris Wright <chrisw@osdl.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/delay.h>
+
+#include <linux/nvram.h>
+#include <linux/proc_fs.h>
+#include <linux/sysfs.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/rfkill.h>
+#include <asm/uaccess.h>
+
+#include <linux/dmi.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+
+#include <acpi/acpi_drivers.h>
+#include <acpi/acnamesp.h>
+
+#include <linux/pci_ids.h>
+
+
+/* ThinkPad CMOS commands */
+#define TP_CMOS_VOLUME_DOWN    0
+#define TP_CMOS_VOLUME_UP      1
+#define TP_CMOS_VOLUME_MUTE    2
+#define TP_CMOS_BRIGHTNESS_UP  4
+#define TP_CMOS_BRIGHTNESS_DOWN        5
+#define TP_CMOS_THINKLIGHT_ON  12
+#define TP_CMOS_THINKLIGHT_OFF 13
+
+/* NVRAM Addresses */
+enum tp_nvram_addr {
+       TP_NVRAM_ADDR_HK2               = 0x57,
+       TP_NVRAM_ADDR_THINKLIGHT        = 0x58,
+       TP_NVRAM_ADDR_VIDEO             = 0x59,
+       TP_NVRAM_ADDR_BRIGHTNESS        = 0x5e,
+       TP_NVRAM_ADDR_MIXER             = 0x60,
+};
+
+/* NVRAM bit masks */
+enum {
+       TP_NVRAM_MASK_HKT_THINKPAD      = 0x08,
+       TP_NVRAM_MASK_HKT_ZOOM          = 0x20,
+       TP_NVRAM_MASK_HKT_DISPLAY       = 0x40,
+       TP_NVRAM_MASK_HKT_HIBERNATE     = 0x80,
+       TP_NVRAM_MASK_THINKLIGHT        = 0x10,
+       TP_NVRAM_MASK_HKT_DISPEXPND     = 0x30,
+       TP_NVRAM_MASK_HKT_BRIGHTNESS    = 0x20,
+       TP_NVRAM_MASK_LEVEL_BRIGHTNESS  = 0x0f,
+       TP_NVRAM_POS_LEVEL_BRIGHTNESS   = 0,
+       TP_NVRAM_MASK_MUTE              = 0x40,
+       TP_NVRAM_MASK_HKT_VOLUME        = 0x80,
+       TP_NVRAM_MASK_LEVEL_VOLUME      = 0x0f,
+       TP_NVRAM_POS_LEVEL_VOLUME       = 0,
+};
+
+/* ACPI HIDs */
+#define TPACPI_ACPI_HKEY_HID           "IBM0068"
+
+/* Input IDs */
+#define TPACPI_HKEY_INPUT_PRODUCT      0x5054 /* "TP" */
+#define TPACPI_HKEY_INPUT_VERSION      0x4101
+
+
+/****************************************************************************
+ * Main driver
+ */
+
+#define TPACPI_NAME "thinkpad"
+#define TPACPI_DESC "ThinkPad ACPI Extras"
+#define TPACPI_FILE TPACPI_NAME "_acpi"
+#define TPACPI_URL "http://ibm-acpi.sf.net/"
+#define TPACPI_MAIL "ibm-acpi-devel@lists.sourceforge.net"
+
+#define TPACPI_PROC_DIR "ibm"
+#define TPACPI_ACPI_EVENT_PREFIX "ibm"
+#define TPACPI_DRVR_NAME TPACPI_FILE
+#define TPACPI_DRVR_SHORTNAME "tpacpi"
+#define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon"
+
+#define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd"
+#define TPACPI_WORKQUEUE_NAME "ktpacpid"
+
+#define TPACPI_MAX_ACPI_ARGS 3
+
+/* rfkill switches */
+enum {
+       TPACPI_RFK_BLUETOOTH_SW_ID = 0,
+       TPACPI_RFK_WWAN_SW_ID,
+};
+
+/* Debugging */
+#define TPACPI_LOG TPACPI_FILE ": "
+#define TPACPI_ERR        KERN_ERR    TPACPI_LOG
+#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG
+#define TPACPI_INFO   KERN_INFO   TPACPI_LOG
+#define TPACPI_DEBUG  KERN_DEBUG  TPACPI_LOG
+
+#define TPACPI_DBG_ALL         0xffff
+#define TPACPI_DBG_INIT                0x0001
+#define TPACPI_DBG_EXIT                0x0002
+#define dbg_printk(a_dbg_level, format, arg...) \
+       do { if (dbg_level & a_dbg_level) \
+               printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \
+       } while (0)
+#ifdef CONFIG_THINKPAD_ACPI_DEBUG
+#define vdbg_printk(a_dbg_level, format, arg...) \
+       dbg_printk(a_dbg_level, format, ## arg)
+static const char *str_supported(int is_supported);
+#else
+#define vdbg_printk(a_dbg_level, format, arg...)
+#endif
+
+#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
+#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
+#define strlencmp(a, b) (strncmp((a), (b), strlen(b)))
+
+
+/****************************************************************************
+ * Driver-wide structs and misc. variables
+ */
+
+struct ibm_struct;
+
+struct tp_acpi_drv_struct {
+       const struct acpi_device_id *hid;
+       struct acpi_driver *driver;
+
+       void (*notify) (struct ibm_struct *, u32);
+       acpi_handle *handle;
+       u32 type;
+       struct acpi_device *device;
+};
+
+struct ibm_struct {
+       char *name;
+
+       int (*read) (char *);
+       int (*write) (char *);
+       void (*exit) (void);
+       void (*resume) (void);
+       void (*suspend) (pm_message_t state);
+
+       struct list_head all_drivers;
+
+       struct tp_acpi_drv_struct *acpi;
+
+       struct {
+               u8 acpi_driver_registered:1;
+               u8 acpi_notify_installed:1;
+               u8 proc_created:1;
+               u8 init_called:1;
+               u8 experimental:1;
+       } flags;
+};
+
+struct ibm_init_struct {
+       char param[32];
+
+       int (*init) (struct ibm_init_struct *);
+       struct ibm_struct *data;
+};
+
+static struct {
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+       u32 bay_status:1;
+       u32 bay_eject:1;
+       u32 bay_status2:1;
+       u32 bay_eject2:1;
+#endif
+       u32 bluetooth:1;
+       u32 hotkey:1;
+       u32 hotkey_mask:1;
+       u32 hotkey_wlsw:1;
+       u32 hotkey_tablet:1;
+       u32 light:1;
+       u32 light_status:1;
+       u32 bright_16levels:1;
+       u32 bright_acpimode:1;
+       u32 wan:1;
+       u32 fan_ctrl_status_undef:1;
+       u32 input_device_registered:1;
+       u32 platform_drv_registered:1;
+       u32 platform_drv_attrs_registered:1;
+       u32 sensors_pdrv_registered:1;
+       u32 sensors_pdrv_attrs_registered:1;
+       u32 sensors_pdev_attrs_registered:1;
+       u32 hotkey_poll_active:1;
+} tp_features;
+
+static struct {
+       u16 hotkey_mask_ff:1;
+       u16 bright_cmos_ec_unsync:1;
+} tp_warned;
+
+struct thinkpad_id_data {
+       unsigned int vendor;    /* ThinkPad vendor:
+                                * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
+
+       char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
+       char *ec_version_str;   /* Something like 1ZHT51WW-1.04a */
+
+       u16 bios_model;         /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
+       u16 ec_model;
+
+       char *model_str;        /* ThinkPad T43 */
+       char *nummodel_str;     /* 9384A9C for a 9384-A9C model */
+};
+static struct thinkpad_id_data thinkpad_id;
+
+static enum {
+       TPACPI_LIFE_INIT = 0,
+       TPACPI_LIFE_RUNNING,
+       TPACPI_LIFE_EXITING,
+} tpacpi_lifecycle;
+
+static int experimental;
+static u32 dbg_level;
+
+static struct workqueue_struct *tpacpi_wq;
+
+/* Special LED class that can defer work */
+struct tpacpi_led_classdev {
+       struct led_classdev led_classdev;
+       struct work_struct work;
+       enum led_brightness new_brightness;
+       unsigned int led;
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * ACPI Helpers and device model
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * ACPI basic handles
+ */
+
+static acpi_handle root_handle;
+
+#define TPACPI_HANDLE(object, parent, paths...)                        \
+       static acpi_handle  object##_handle;                    \
+       static acpi_handle *object##_parent = &parent##_handle; \
+       static char        *object##_path;                      \
+       static char        *object##_paths[] = { paths }
+
+TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0",  /* 240, 240x */
+          "\\_SB.PCI.ISA.EC",  /* 570 */
+          "\\_SB.PCI0.ISA0.EC0",       /* 600e/x, 770e, 770x */
+          "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
+          "\\_SB.PCI0.AD4S.EC0",       /* i1400, R30 */
+          "\\_SB.PCI0.ICH3.EC0",       /* R31 */
+          "\\_SB.PCI0.LPC.EC", /* all others */
+          );
+
+TPACPI_HANDLE(ecrd, ec, "ECRD");       /* 570 */
+TPACPI_HANDLE(ecwr, ec, "ECWR");       /* 570 */
+
+TPACPI_HANDLE(cmos, root, "\\UCMS",    /* R50, R50e, R50p, R51, */
+                                       /* T4x, X31, X40 */
+          "\\CMOS",            /* A3x, G4x, R32, T23, T30, X22-24, X30 */
+          "\\CMS",             /* R40, R40e */
+          );                   /* all others */
+
+TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY",  /* 600e/x, 770e, 770x */
+          "^HKEY",             /* R30, R31 */
+          "HKEY",              /* all others */
+          );                   /* 570 */
+
+TPACPI_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA",  /* 570 */
+          "\\_SB.PCI0.AGP0.VID0",      /* 600e/x, 770x */
+          "\\_SB.PCI0.VID0",   /* 770e */
+          "\\_SB.PCI0.VID",    /* A21e, G4x, R50e, X30, X40 */
+          "\\_SB.PCI0.AGP.VID",        /* all others */
+          );                           /* R30, R31 */
+
+
+/*************************************************************************
+ * ACPI helpers
+ */
+
+static int acpi_evalf(acpi_handle handle,
+                     void *res, char *method, char *fmt, ...)
+{
+       char *fmt0 = fmt;
+       struct acpi_object_list params;
+       union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS];
+       struct acpi_buffer result, *resultp;
+       union acpi_object out_obj;
+       acpi_status status;
+       va_list ap;
+       char res_type;
+       int success;
+       int quiet;
+
+       if (!*fmt) {
+               printk(TPACPI_ERR "acpi_evalf() called with empty format\n");
+               return 0;
+       }
+
+       if (*fmt == 'q') {
+               quiet = 1;
+               fmt++;
+       } else
+               quiet = 0;
+
+       res_type = *(fmt++);
+
+       params.count = 0;
+       params.pointer = &in_objs[0];
+
+       va_start(ap, fmt);
+       while (*fmt) {
+               char c = *(fmt++);
+               switch (c) {
+               case 'd':       /* int */
+                       in_objs[params.count].integer.value = va_arg(ap, int);
+                       in_objs[params.count++].type = ACPI_TYPE_INTEGER;
+                       break;
+                       /* add more types as needed */
+               default:
+                       printk(TPACPI_ERR "acpi_evalf() called "
+                              "with invalid format character '%c'\n", c);
+                       return 0;
+               }
+       }
+       va_end(ap);
+
+       if (res_type != 'v') {
+               result.length = sizeof(out_obj);
+               result.pointer = &out_obj;
+               resultp = &result;
+       } else
+               resultp = NULL;
+
+       status = acpi_evaluate_object(handle, method, &params, resultp);
+
+       switch (res_type) {
+       case 'd':               /* int */
+               if (res)
+                       *(int *)res = out_obj.integer.value;
+               success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
+               break;
+       case 'v':               /* void */
+               success = status == AE_OK;
+               break;
+               /* add more types as needed */
+       default:
+               printk(TPACPI_ERR "acpi_evalf() called "
+                      "with invalid format character '%c'\n", res_type);
+               return 0;
+       }
+
+       if (!success && !quiet)
+               printk(TPACPI_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
+                      method, fmt0, status);
+
+       return success;
+}
+
+static int acpi_ec_read(int i, u8 *p)
+{
+       int v;
+
+       if (ecrd_handle) {
+               if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
+                       return 0;
+               *p = v;
+       } else {
+               if (ec_read(i, p) < 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int acpi_ec_write(int i, u8 v)
+{
+       if (ecwr_handle) {
+               if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
+                       return 0;
+       } else {
+               if (ec_write(i, v) < 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+#if defined(CONFIG_THINKPAD_ACPI_DOCK) || defined(CONFIG_THINKPAD_ACPI_BAY)
+static int _sta(acpi_handle handle)
+{
+       int status;
+
+       if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
+               status = 0;
+
+       return status;
+}
+#endif
+
+static int issue_thinkpad_cmos_command(int cmos_cmd)
+{
+       if (!cmos_handle)
+               return -ENXIO;
+
+       if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
+               return -EIO;
+
+       return 0;
+}
+
+/*************************************************************************
+ * ACPI device model
+ */
+
+#define TPACPI_ACPIHANDLE_INIT(object) \
+       drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \
+               object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
+
+static void drv_acpi_handle_init(char *name,
+                          acpi_handle *handle, acpi_handle parent,
+                          char **paths, int num_paths, char **path)
+{
+       int i;
+       acpi_status status;
+
+       vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n",
+               name);
+
+       for (i = 0; i < num_paths; i++) {
+               status = acpi_get_handle(parent, paths[i], handle);
+               if (ACPI_SUCCESS(status)) {
+                       *path = paths[i];
+                       dbg_printk(TPACPI_DBG_INIT,
+                                  "Found ACPI handle %s for %s\n",
+                                  *path, name);
+                       return;
+               }
+       }
+
+       vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n",
+                   name);
+       *handle = NULL;
+}
+
+static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+       struct ibm_struct *ibm = data;
+
+       if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
+               return;
+
+       if (!ibm || !ibm->acpi || !ibm->acpi->notify)
+               return;
+
+       ibm->acpi->notify(ibm, event);
+}
+
+static int __init setup_acpi_notify(struct ibm_struct *ibm)
+{
+       acpi_status status;
+       int rc;
+
+       BUG_ON(!ibm->acpi);
+
+       if (!*ibm->acpi->handle)
+               return 0;
+
+       vdbg_printk(TPACPI_DBG_INIT,
+               "setting up ACPI notify for %s\n", ibm->name);
+
+       rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
+       if (rc < 0) {
+               printk(TPACPI_ERR "acpi_bus_get_device(%s) failed: %d\n",
+                       ibm->name, rc);
+               return -ENODEV;
+       }
+
+       ibm->acpi->device->driver_data = ibm;
+       sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
+               TPACPI_ACPI_EVENT_PREFIX,
+               ibm->name);
+
+       status = acpi_install_notify_handler(*ibm->acpi->handle,
+                       ibm->acpi->type, dispatch_acpi_notify, ibm);
+       if (ACPI_FAILURE(status)) {
+               if (status == AE_ALREADY_EXISTS) {
+                       printk(TPACPI_NOTICE
+                              "another device driver is already "
+                              "handling %s events\n", ibm->name);
+               } else {
+                       printk(TPACPI_ERR
+                              "acpi_install_notify_handler(%s) failed: %d\n",
+                              ibm->name, status);
+               }
+               return -ENODEV;
+       }
+       ibm->flags.acpi_notify_installed = 1;
+       return 0;
+}
+
+static int __init tpacpi_device_add(struct acpi_device *device)
+{
+       return 0;
+}
+
+static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
+{
+       int rc;
+
+       dbg_printk(TPACPI_DBG_INIT,
+               "registering %s as an ACPI driver\n", ibm->name);
+
+       BUG_ON(!ibm->acpi);
+
+       ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
+       if (!ibm->acpi->driver) {
+               printk(TPACPI_ERR
+                      "failed to allocate memory for ibm->acpi->driver\n");
+               return -ENOMEM;
+       }
+
+       sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name);
+       ibm->acpi->driver->ids = ibm->acpi->hid;
+
+       ibm->acpi->driver->ops.add = &tpacpi_device_add;
+
+       rc = acpi_bus_register_driver(ibm->acpi->driver);
+       if (rc < 0) {
+               printk(TPACPI_ERR "acpi_bus_register_driver(%s) failed: %d\n",
+                      ibm->name, rc);
+               kfree(ibm->acpi->driver);
+               ibm->acpi->driver = NULL;
+       } else if (!rc)
+               ibm->flags.acpi_driver_registered = 1;
+
+       return rc;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Procfs Helpers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static int dispatch_procfs_read(char *page, char **start, off_t off,
+                       int count, int *eof, void *data)
+{
+       struct ibm_struct *ibm = data;
+       int len;
+
+       if (!ibm || !ibm->read)
+               return -EINVAL;
+
+       len = ibm->read(page);
+       if (len < 0)
+               return len;
+
+       if (len <= off + count)
+               *eof = 1;
+       *start = page + off;
+       len -= off;
+       if (len > count)
+               len = count;
+       if (len < 0)
+               len = 0;
+
+       return len;
+}
+
+static int dispatch_procfs_write(struct file *file,
+                       const char __user *userbuf,
+                       unsigned long count, void *data)
+{
+       struct ibm_struct *ibm = data;
+       char *kernbuf;
+       int ret;
+
+       if (!ibm || !ibm->write)
+               return -EINVAL;
+
+       kernbuf = kmalloc(count + 2, GFP_KERNEL);
+       if (!kernbuf)
+               return -ENOMEM;
+
+       if (copy_from_user(kernbuf, userbuf, count)) {
+               kfree(kernbuf);
+               return -EFAULT;
+       }
+
+       kernbuf[count] = 0;
+       strcat(kernbuf, ",");
+       ret = ibm->write(kernbuf);
+       if (ret == 0)
+               ret = count;
+
+       kfree(kernbuf);
+
+       return ret;
+}
+
+static char *next_cmd(char **cmds)
+{
+       char *start = *cmds;
+       char *end;
+
+       while ((end = strchr(start, ',')) && end == start)
+               start = end + 1;
+
+       if (!end)
+               return NULL;
+
+       *end = 0;
+       *cmds = end + 1;
+       return start;
+}
+
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Device model: input, hwmon and platform
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+static struct platform_device *tpacpi_pdev;
+static struct platform_device *tpacpi_sensors_pdev;
+static struct device *tpacpi_hwmon;
+static struct input_dev *tpacpi_inputdev;
+static struct mutex tpacpi_inputdev_send_mutex;
+static LIST_HEAD(tpacpi_all_drivers);
+
+static int tpacpi_suspend_handler(struct platform_device *pdev,
+                                 pm_message_t state)
+{
+       struct ibm_struct *ibm, *itmp;
+
+       list_for_each_entry_safe(ibm, itmp,
+                                &tpacpi_all_drivers,
+                                all_drivers) {
+               if (ibm->suspend)
+                       (ibm->suspend)(state);
+       }
+
+       return 0;
+}
+
+static int tpacpi_resume_handler(struct platform_device *pdev)
+{
+       struct ibm_struct *ibm, *itmp;
+
+       list_for_each_entry_safe(ibm, itmp,
+                                &tpacpi_all_drivers,
+                                all_drivers) {
+               if (ibm->resume)
+                       (ibm->resume)();
+       }
+
+       return 0;
+}
+
+static struct platform_driver tpacpi_pdriver = {
+       .driver = {
+               .name = TPACPI_DRVR_NAME,
+               .owner = THIS_MODULE,
+       },
+       .suspend = tpacpi_suspend_handler,
+       .resume = tpacpi_resume_handler,
+};
+
+static struct platform_driver tpacpi_hwmon_pdriver = {
+       .driver = {
+               .name = TPACPI_HWMON_DRVR_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+/*************************************************************************
+ * sysfs support helpers
+ */
+
+struct attribute_set {
+       unsigned int members, max_members;
+       struct attribute_group group;
+};
+
+struct attribute_set_obj {
+       struct attribute_set s;
+       struct attribute *a;
+} __attribute__((packed));
+
+static struct attribute_set *create_attr_set(unsigned int max_members,
+                                               const char *name)
+{
+       struct attribute_set_obj *sobj;
+
+       if (max_members == 0)
+               return NULL;
+
+       /* Allocates space for implicit NULL at the end too */
+       sobj = kzalloc(sizeof(struct attribute_set_obj) +
+                   max_members * sizeof(struct attribute *),
+                   GFP_KERNEL);
+       if (!sobj)
+               return NULL;
+       sobj->s.max_members = max_members;
+       sobj->s.group.attrs = &sobj->a;
+       sobj->s.group.name = name;
+
+       return &sobj->s;
+}
+
+#define destroy_attr_set(_set) \
+       kfree(_set);
+
+/* not multi-threaded safe, use it in a single thread per set */
+static int add_to_attr_set(struct attribute_set *s, struct attribute *attr)
+{
+       if (!s || !attr)
+               return -EINVAL;
+
+       if (s->members >= s->max_members)
+               return -ENOMEM;
+
+       s->group.attrs[s->members] = attr;
+       s->members++;
+
+       return 0;
+}
+
+static int add_many_to_attr_set(struct attribute_set *s,
+                       struct attribute **attr,
+                       unsigned int count)
+{
+       int i, res;
+
+       for (i = 0; i < count; i++) {
+               res = add_to_attr_set(s, attr[i]);
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
+
+static void delete_attr_set(struct attribute_set *s, struct kobject *kobj)
+{
+       sysfs_remove_group(kobj, &s->group);
+       destroy_attr_set(s);
+}
+
+#define register_attr_set_with_sysfs(_attr_set, _kobj) \
+       sysfs_create_group(_kobj, &_attr_set->group)
+
+static int parse_strtoul(const char *buf,
+               unsigned long max, unsigned long *value)
+{
+       char *endp;
+
+       while (*buf && isspace(*buf))
+               buf++;
+       *value = simple_strtoul(buf, &endp, 0);
+       while (*endp && isspace(*endp))
+               endp++;
+       if (*endp || *value > max)
+               return -EINVAL;
+
+       return 0;
+}
+
+static void tpacpi_disable_brightness_delay(void)
+{
+       if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
+               printk(TPACPI_NOTICE
+                       "ACPI backlight control delay disabled\n");
+}
+
+static int __init tpacpi_query_bcl_levels(acpi_handle handle)
+{
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       int rc;
+
+       if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) {
+               obj = (union acpi_object *)buffer.pointer;
+               if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
+                       printk(TPACPI_ERR "Unknown _BCL data, "
+                              "please report this to %s\n", TPACPI_MAIL);
+                       rc = 0;
+               } else {
+                       rc = obj->package.count;
+               }
+       } else {
+               return 0;
+       }
+
+       kfree(buffer.pointer);
+       return rc;
+}
+
+static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle,
+                                       u32 lvl, void *context, void **rv)
+{
+       char name[ACPI_PATH_SEGMENT_LENGTH];
+       struct acpi_buffer buffer = { sizeof(name), &name };
+
+       if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) &&
+           !strncmp("_BCL", name, sizeof(name) - 1)) {
+               BUG_ON(!rv || !*rv);
+               **(int **)rv = tpacpi_query_bcl_levels(handle);
+               return AE_CTRL_TERMINATE;
+       } else {
+               return AE_OK;
+       }
+}
+
+/*
+ * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map
+ */
+static int __init tpacpi_check_std_acpi_brightness_support(void)
+{
+       int status;
+       int bcl_levels = 0;
+       void *bcl_ptr = &bcl_levels;
+
+       if (!vid_handle) {
+               TPACPI_ACPIHANDLE_INIT(vid);
+       }
+       if (!vid_handle)
+               return 0;
+
+       /*
+        * Search for a _BCL method, and execute it.  This is safe on all
+        * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista
+        * BIOS in ACPI backlight control mode.  We do NOT have to care
+        * about calling the _BCL method in an enabled video device, any
+        * will do for our purposes.
+        */
+
+       status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3,
+                                    tpacpi_acpi_walk_find_bcl, NULL,
+                                    &bcl_ptr);
+
+       if (ACPI_SUCCESS(status) && bcl_levels > 2) {
+               tp_features.bright_acpimode = 1;
+               return (bcl_levels - 2);
+       }
+
+       return 0;
+}
+
+static int __init tpacpi_new_rfkill(const unsigned int id,
+                       struct rfkill **rfk,
+                       const enum rfkill_type rfktype,
+                       const char *name,
+                       int (*toggle_radio)(void *, enum rfkill_state),
+                       int (*get_state)(void *, enum rfkill_state *))
+{
+       int res;
+       enum rfkill_state initial_state;
+
+       *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
+       if (!*rfk) {
+               printk(TPACPI_ERR
+                       "failed to allocate memory for rfkill class\n");
+               return -ENOMEM;
+       }
+
+       (*rfk)->name = name;
+       (*rfk)->get_state = get_state;
+       (*rfk)->toggle_radio = toggle_radio;
+
+       if (!get_state(NULL, &initial_state))
+               (*rfk)->state = initial_state;
+
+       res = rfkill_register(*rfk);
+       if (res < 0) {
+               printk(TPACPI_ERR
+                       "failed to register %s rfkill switch: %d\n",
+                       name, res);
+               rfkill_free(*rfk);
+               *rfk = NULL;
+               return res;
+       }
+
+       return 0;
+}
+
+/*************************************************************************
+ * thinkpad-acpi driver attributes
+ */
+
+/* interface_version --------------------------------------------------- */
+static ssize_t tpacpi_driver_interface_version_show(
+                               struct device_driver *drv,
+                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
+}
+
+static DRIVER_ATTR(interface_version, S_IRUGO,
+               tpacpi_driver_interface_version_show, NULL);
+
+/* debug_level --------------------------------------------------------- */
+static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
+                                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
+}
+
+static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
+                                               const char *buf, size_t count)
+{
+       unsigned long t;
+
+       if (parse_strtoul(buf, 0xffff, &t))
+               return -EINVAL;
+
+       dbg_level = t;
+
+       return count;
+}
+
+static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
+               tpacpi_driver_debug_show, tpacpi_driver_debug_store);
+
+/* version ------------------------------------------------------------- */
+static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
+                                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s v%s\n",
+                       TPACPI_DESC, TPACPI_VERSION);
+}
+
+static DRIVER_ATTR(version, S_IRUGO,
+               tpacpi_driver_version_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+static struct driver_attribute *tpacpi_driver_attributes[] = {
+       &driver_attr_debug_level, &driver_attr_version,
+       &driver_attr_interface_version,
+};
+
+static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
+{
+       int i, res;
+
+       i = 0;
+       res = 0;
+       while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
+               res = driver_create_file(drv, tpacpi_driver_attributes[i]);
+               i++;
+       }
+
+       return res;
+}
+
+static void tpacpi_remove_driver_attributes(struct device_driver *drv)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
+               driver_remove_file(drv, tpacpi_driver_attributes[i]);
+}
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Subdrivers
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/*************************************************************************
+ * thinkpad-acpi init subdriver
+ */
+
+static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
+{
+       printk(TPACPI_INFO "%s v%s\n", TPACPI_DESC, TPACPI_VERSION);
+       printk(TPACPI_INFO "%s\n", TPACPI_URL);
+
+       printk(TPACPI_INFO "ThinkPad BIOS %s, EC %s\n",
+               (thinkpad_id.bios_version_str) ?
+                       thinkpad_id.bios_version_str : "unknown",
+               (thinkpad_id.ec_version_str) ?
+                       thinkpad_id.ec_version_str : "unknown");
+
+       if (thinkpad_id.vendor && thinkpad_id.model_str)
+               printk(TPACPI_INFO "%s %s, model %s\n",
+                       (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
+                               "IBM" : ((thinkpad_id.vendor ==
+                                               PCI_VENDOR_ID_LENOVO) ?
+                                       "Lenovo" : "Unknown vendor"),
+                       thinkpad_id.model_str,
+                       (thinkpad_id.nummodel_str) ?
+                               thinkpad_id.nummodel_str : "unknown");
+
+       return 0;
+}
+
+static int thinkpad_acpi_driver_read(char *p)
+{
+       int len = 0;
+
+       len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC);
+       len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION);
+
+       return len;
+}
+
+static struct ibm_struct thinkpad_acpi_driver_data = {
+       .name = "driver",
+       .read = thinkpad_acpi_driver_read,
+};
+
+/*************************************************************************
+ * Hotkey subdriver
+ */
+
+enum { /* hot key scan codes (derived from ACPI DSDT) */
+       TP_ACPI_HOTKEYSCAN_FNF1         = 0,
+       TP_ACPI_HOTKEYSCAN_FNF2,
+       TP_ACPI_HOTKEYSCAN_FNF3,
+       TP_ACPI_HOTKEYSCAN_FNF4,
+       TP_ACPI_HOTKEYSCAN_FNF5,
+       TP_ACPI_HOTKEYSCAN_FNF6,
+       TP_ACPI_HOTKEYSCAN_FNF7,
+       TP_ACPI_HOTKEYSCAN_FNF8,
+       TP_ACPI_HOTKEYSCAN_FNF9,
+       TP_ACPI_HOTKEYSCAN_FNF10,
+       TP_ACPI_HOTKEYSCAN_FNF11,
+       TP_ACPI_HOTKEYSCAN_FNF12,
+       TP_ACPI_HOTKEYSCAN_FNBACKSPACE,
+       TP_ACPI_HOTKEYSCAN_FNINSERT,
+       TP_ACPI_HOTKEYSCAN_FNDELETE,
+       TP_ACPI_HOTKEYSCAN_FNHOME,
+       TP_ACPI_HOTKEYSCAN_FNEND,
+       TP_ACPI_HOTKEYSCAN_FNPAGEUP,
+       TP_ACPI_HOTKEYSCAN_FNPAGEDOWN,
+       TP_ACPI_HOTKEYSCAN_FNSPACE,
+       TP_ACPI_HOTKEYSCAN_VOLUMEUP,
+       TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
+       TP_ACPI_HOTKEYSCAN_MUTE,
+       TP_ACPI_HOTKEYSCAN_THINKPAD,
+};
+
+enum { /* Keys available through NVRAM polling */
+       TPACPI_HKEY_NVRAM_KNOWN_MASK = 0x00fb88c0U,
+       TPACPI_HKEY_NVRAM_GOOD_MASK  = 0x00fb8000U,
+};
+
+enum { /* Positions of some of the keys in hotkey masks */
+       TP_ACPI_HKEY_DISPSWTCH_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF7,
+       TP_ACPI_HKEY_DISPXPAND_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF8,
+       TP_ACPI_HKEY_HIBERNATE_MASK     = 1 << TP_ACPI_HOTKEYSCAN_FNF12,
+       TP_ACPI_HKEY_BRGHTUP_MASK       = 1 << TP_ACPI_HOTKEYSCAN_FNHOME,
+       TP_ACPI_HKEY_BRGHTDWN_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNEND,
+       TP_ACPI_HKEY_THNKLGHT_MASK      = 1 << TP_ACPI_HOTKEYSCAN_FNPAGEUP,
+       TP_ACPI_HKEY_ZOOM_MASK          = 1 << TP_ACPI_HOTKEYSCAN_FNSPACE,
+       TP_ACPI_HKEY_VOLUP_MASK         = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEUP,
+       TP_ACPI_HKEY_VOLDWN_MASK        = 1 << TP_ACPI_HOTKEYSCAN_VOLUMEDOWN,
+       TP_ACPI_HKEY_MUTE_MASK          = 1 << TP_ACPI_HOTKEYSCAN_MUTE,
+       TP_ACPI_HKEY_THINKPAD_MASK      = 1 << TP_ACPI_HOTKEYSCAN_THINKPAD,
+};
+
+enum { /* NVRAM to ACPI HKEY group map */
+       TP_NVRAM_HKEY_GROUP_HK2         = TP_ACPI_HKEY_THINKPAD_MASK |
+                                         TP_ACPI_HKEY_ZOOM_MASK |
+                                         TP_ACPI_HKEY_DISPSWTCH_MASK |
+                                         TP_ACPI_HKEY_HIBERNATE_MASK,
+       TP_NVRAM_HKEY_GROUP_BRIGHTNESS  = TP_ACPI_HKEY_BRGHTUP_MASK |
+                                         TP_ACPI_HKEY_BRGHTDWN_MASK,
+       TP_NVRAM_HKEY_GROUP_VOLUME      = TP_ACPI_HKEY_VOLUP_MASK |
+                                         TP_ACPI_HKEY_VOLDWN_MASK |
+                                         TP_ACPI_HKEY_MUTE_MASK,
+};
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+struct tp_nvram_state {
+       u16 thinkpad_toggle:1;
+       u16 zoom_toggle:1;
+       u16 display_toggle:1;
+       u16 thinklight_toggle:1;
+       u16 hibernate_toggle:1;
+       u16 displayexp_toggle:1;
+       u16 display_state:1;
+       u16 brightness_toggle:1;
+       u16 volume_toggle:1;
+       u16 mute:1;
+
+       u8 brightness_level;
+       u8 volume_level;
+};
+
+static struct task_struct *tpacpi_hotkey_task;
+static u32 hotkey_source_mask;         /* bit mask 0=ACPI,1=NVRAM */
+static int hotkey_poll_freq = 10;      /* Hz */
+static struct mutex hotkey_thread_mutex;
+static struct mutex hotkey_thread_data_mutex;
+static unsigned int hotkey_config_change;
+
+#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+#define hotkey_source_mask 0U
+
+#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+static struct mutex hotkey_mutex;
+
+static enum {  /* Reasons for waking up */
+       TP_ACPI_WAKEUP_NONE = 0,        /* None or unknown */
+       TP_ACPI_WAKEUP_BAYEJ,           /* Bay ejection request */
+       TP_ACPI_WAKEUP_UNDOCK,          /* Undock request */
+} hotkey_wakeup_reason;
+
+static int hotkey_autosleep_ack;
+
+static int hotkey_orig_status;
+static u32 hotkey_orig_mask;
+static u32 hotkey_all_mask;
+static u32 hotkey_reserved_mask;
+static u32 hotkey_mask;
+
+static unsigned int hotkey_report_mode;
+
+static u16 *hotkey_keycode_map;
+
+static struct attribute_set *hotkey_dev_attributes;
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+#define HOTKEY_CONFIG_CRITICAL_START \
+       do { \
+               mutex_lock(&hotkey_thread_data_mutex); \
+               hotkey_config_change++; \
+       } while (0);
+#define HOTKEY_CONFIG_CRITICAL_END \
+       mutex_unlock(&hotkey_thread_data_mutex);
+#else
+#define HOTKEY_CONFIG_CRITICAL_START
+#define HOTKEY_CONFIG_CRITICAL_END
+#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+/* HKEY.MHKG() return bits */
+#define TP_HOTKEY_TABLET_MASK (1 << 3)
+
+static int hotkey_get_wlsw(int *status)
+{
+       if (!acpi_evalf(hkey_handle, status, "WLSW", "d"))
+               return -EIO;
+       return 0;
+}
+
+static int hotkey_get_tablet_mode(int *status)
+{
+       int s;
+
+       if (!acpi_evalf(hkey_handle, &s, "MHKG", "d"))
+               return -EIO;
+
+       *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
+       return 0;
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_mask_get(void)
+{
+       u32 m = 0;
+
+       if (tp_features.hotkey_mask) {
+               if (!acpi_evalf(hkey_handle, &m, "DHKN", "d"))
+                       return -EIO;
+       }
+       hotkey_mask = m | (hotkey_source_mask & hotkey_mask);
+
+       return 0;
+}
+
+/*
+ * Call with hotkey_mutex held
+ */
+static int hotkey_mask_set(u32 mask)
+{
+       int i;
+       int rc = 0;
+
+       if (tp_features.hotkey_mask) {
+               if (!tp_warned.hotkey_mask_ff &&
+                   (mask == 0xffff || mask == 0xffffff ||
+                    mask == 0xffffffff)) {
+                       tp_warned.hotkey_mask_ff = 1;
+                       printk(TPACPI_NOTICE
+                              "setting the hotkey mask to 0x%08x is likely "
+                              "not the best way to go about it\n", mask);
+                       printk(TPACPI_NOTICE
+                              "please consider using the driver defaults, "
+                              "and refer to up-to-date thinkpad-acpi "
+                              "documentation\n");
+               }
+
+               HOTKEY_CONFIG_CRITICAL_START
+               for (i = 0; i < 32; i++) {
+                       u32 m = 1 << i;
+                       /* enable in firmware mask only keys not in NVRAM
+                        * mode, but enable the key in the cached hotkey_mask
+                        * regardless of mode, or the key will end up
+                        * disabled by hotkey_mask_get() */
+                       if (!acpi_evalf(hkey_handle,
+                                       NULL, "MHKM", "vdd", i + 1,
+                                       !!((mask & ~hotkey_source_mask) & m))) {
+                               rc = -EIO;
+                               break;
+                       } else {
+                               hotkey_mask = (hotkey_mask & ~m) | (mask & m);
+                       }
+               }
+               HOTKEY_CONFIG_CRITICAL_END
+
+               /* hotkey_mask_get must be called unconditionally below */
+               if (!hotkey_mask_get() && !rc &&
+                   (hotkey_mask & ~hotkey_source_mask) !=
+                    (mask & ~hotkey_source_mask)) {
+                       printk(TPACPI_NOTICE
+                              "requested hot key mask 0x%08x, but "
+                              "firmware forced it to 0x%08x\n",
+                              mask, hotkey_mask);
+               }
+       } else {
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+               HOTKEY_CONFIG_CRITICAL_START
+               hotkey_mask = mask & hotkey_source_mask;
+               HOTKEY_CONFIG_CRITICAL_END
+               hotkey_mask_get();
+               if (hotkey_mask != mask) {
+                       printk(TPACPI_NOTICE
+                              "requested hot key mask 0x%08x, "
+                              "forced to 0x%08x (NVRAM poll mask is "
+                              "0x%08x): no firmware mask support\n",
+                              mask, hotkey_mask, hotkey_source_mask);
+               }
+#else
+               hotkey_mask_get();
+               rc = -ENXIO;
+#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+       }
+
+       return rc;
+}
+
+static int hotkey_status_get(int *status)
+{
+       if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
+               return -EIO;
+
+       return 0;
+}
+
+static int hotkey_status_set(int status)
+{
+       if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
+               return -EIO;
+
+       return 0;
+}
+
+static void tpacpi_input_send_tabletsw(void)
+{
+       int state;
+
+       if (tp_features.hotkey_tablet &&
+           !hotkey_get_tablet_mode(&state)) {
+               mutex_lock(&tpacpi_inputdev_send_mutex);
+
+               input_report_switch(tpacpi_inputdev,
+                                   SW_TABLET_MODE, !!state);
+               input_sync(tpacpi_inputdev);
+
+               mutex_unlock(&tpacpi_inputdev_send_mutex);
+       }
+}
+
+static void tpacpi_input_send_key(unsigned int scancode)
+{
+       unsigned int keycode;
+
+       keycode = hotkey_keycode_map[scancode];
+
+       if (keycode != KEY_RESERVED) {
+               mutex_lock(&tpacpi_inputdev_send_mutex);
+
+               input_report_key(tpacpi_inputdev, keycode, 1);
+               if (keycode == KEY_UNKNOWN)
+                       input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
+                                   scancode);
+               input_sync(tpacpi_inputdev);
+
+               input_report_key(tpacpi_inputdev, keycode, 0);
+               if (keycode == KEY_UNKNOWN)
+                       input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,
+                                   scancode);
+               input_sync(tpacpi_inputdev);
+
+               mutex_unlock(&tpacpi_inputdev_send_mutex);
+       }
+}
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
+
+static void tpacpi_hotkey_send_key(unsigned int scancode)
+{
+       tpacpi_input_send_key(scancode);
+       if (hotkey_report_mode < 2) {
+               acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
+                                               0x80, 0x1001 + scancode);
+       }
+}
+
+static void hotkey_read_nvram(struct tp_nvram_state *n, u32 m)
+{
+       u8 d;
+
+       if (m & TP_NVRAM_HKEY_GROUP_HK2) {
+               d = nvram_read_byte(TP_NVRAM_ADDR_HK2);
+               n->thinkpad_toggle = !!(d & TP_NVRAM_MASK_HKT_THINKPAD);
+               n->zoom_toggle = !!(d & TP_NVRAM_MASK_HKT_ZOOM);
+               n->display_toggle = !!(d & TP_NVRAM_MASK_HKT_DISPLAY);
+               n->hibernate_toggle = !!(d & TP_NVRAM_MASK_HKT_HIBERNATE);
+       }
+       if (m & TP_ACPI_HKEY_THNKLGHT_MASK) {
+               d = nvram_read_byte(TP_NVRAM_ADDR_THINKLIGHT);
+               n->thinklight_toggle = !!(d & TP_NVRAM_MASK_THINKLIGHT);
+       }
+       if (m & TP_ACPI_HKEY_DISPXPAND_MASK) {
+               d = nvram_read_byte(TP_NVRAM_ADDR_VIDEO);
+               n->displayexp_toggle =
+                               !!(d & TP_NVRAM_MASK_HKT_DISPEXPND);
+       }
+       if (m & TP_NVRAM_HKEY_GROUP_BRIGHTNESS) {
+               d = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS);
+               n->brightness_level = (d & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
+                               >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+               n->brightness_toggle =
+                               !!(d & TP_NVRAM_MASK_HKT_BRIGHTNESS);
+       }
+       if (m & TP_NVRAM_HKEY_GROUP_VOLUME) {
+               d = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
+               n->volume_level = (d & TP_NVRAM_MASK_LEVEL_VOLUME)
+                               >> TP_NVRAM_POS_LEVEL_VOLUME;
+               n->mute = !!(d & TP_NVRAM_MASK_MUTE);
+               n->volume_toggle = !!(d & TP_NVRAM_MASK_HKT_VOLUME);
+       }
+}
+
+#define TPACPI_COMPARE_KEY(__scancode, __member) \
+       do { \
+               if ((mask & (1 << __scancode)) && \
+                   oldn->__member != newn->__member) \
+               tpacpi_hotkey_send_key(__scancode); \
+       } while (0)
+
+#define TPACPI_MAY_SEND_KEY(__scancode) \
+       do { if (mask & (1 << __scancode)) \
+               tpacpi_hotkey_send_key(__scancode); } while (0)
+
+static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
+                                          struct tp_nvram_state *newn,
+                                          u32 mask)
+{
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF12, hibernate_toggle);
+
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNPAGEUP, thinklight_toggle);
+
+       TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF8, displayexp_toggle);
+
+       /* handle volume */
+       if (oldn->volume_toggle != newn->volume_toggle) {
+               if (oldn->mute != newn->mute) {
+                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
+               }
+               if (oldn->volume_level > newn->volume_level) {
+                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+               } else if (oldn->volume_level < newn->volume_level) {
+                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+               } else if (oldn->mute == newn->mute) {
+                       /* repeated key presses that didn't change state */
+                       if (newn->mute) {
+                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
+                       } else if (newn->volume_level != 0) {
+                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+                       } else {
+                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+                       }
+               }
+       }
+
+       /* handle brightness */
+       if (oldn->brightness_toggle != newn->brightness_toggle) {
+               if (oldn->brightness_level < newn->brightness_level) {
+                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+               } else if (oldn->brightness_level > newn->brightness_level) {
+                       TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+               } else {
+                       /* repeated key presses that didn't change state */
+                       if (newn->brightness_level != 0) {
+                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+                       } else {
+                               TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+                       }
+               }
+       }
+}
+
+#undef TPACPI_COMPARE_KEY
+#undef TPACPI_MAY_SEND_KEY
+
+static int hotkey_kthread(void *data)
+{
+       struct tp_nvram_state s[2];
+       u32 mask;
+       unsigned int si, so;
+       unsigned long t;
+       unsigned int change_detector, must_reset;
+
+       mutex_lock(&hotkey_thread_mutex);
+
+       if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
+               goto exit;
+
+       set_freezable();
+
+       so = 0;
+       si = 1;
+       t = 0;
+
+       /* Initial state for compares */
+       mutex_lock(&hotkey_thread_data_mutex);
+       change_detector = hotkey_config_change;
+       mask = hotkey_source_mask & hotkey_mask;
+       mutex_unlock(&hotkey_thread_data_mutex);
+       hotkey_read_nvram(&s[so], mask);
+
+       while (!kthread_should_stop() && hotkey_poll_freq) {
+               if (t == 0)
+                       t = 1000/hotkey_poll_freq;
+               t = msleep_interruptible(t);
+               if (unlikely(kthread_should_stop()))
+                       break;
+               must_reset = try_to_freeze();
+               if (t > 0 && !must_reset)
+                       continue;
+
+               mutex_lock(&hotkey_thread_data_mutex);
+               if (must_reset || hotkey_config_change != change_detector) {
+                       /* forget old state on thaw or config change */
+                       si = so;
+                       t = 0;
+                       change_detector = hotkey_config_change;
+               }
+               mask = hotkey_source_mask & hotkey_mask;
+               mutex_unlock(&hotkey_thread_data_mutex);
+
+               if (likely(mask)) {
+                       hotkey_read_nvram(&s[si], mask);
+                       if (likely(si != so)) {
+                               hotkey_compare_and_issue_event(&s[so], &s[si],
+                                                               mask);
+                       }
+               }
+
+               so = si;
+               si ^= 1;
+       }
+
+exit:
+       mutex_unlock(&hotkey_thread_mutex);
+       return 0;
+}
+
+static void hotkey_poll_stop_sync(void)
+{
+       if (tpacpi_hotkey_task) {
+               if (frozen(tpacpi_hotkey_task) ||
+                   freezing(tpacpi_hotkey_task))
+                       thaw_process(tpacpi_hotkey_task);
+
+               kthread_stop(tpacpi_hotkey_task);
+               tpacpi_hotkey_task = NULL;
+               mutex_lock(&hotkey_thread_mutex);
+               /* at this point, the thread did exit */
+               mutex_unlock(&hotkey_thread_mutex);
+       }
+}
+
+/* call with hotkey_mutex held */
+static void hotkey_poll_setup(int may_warn)
+{
+       if ((hotkey_source_mask & hotkey_mask) != 0 &&
+           hotkey_poll_freq > 0 &&
+           (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) {
+               if (!tpacpi_hotkey_task) {
+                       tpacpi_hotkey_task = kthread_run(hotkey_kthread,
+                                       NULL, TPACPI_NVRAM_KTHREAD_NAME);
+                       if (IS_ERR(tpacpi_hotkey_task)) {
+                               tpacpi_hotkey_task = NULL;
+                               printk(TPACPI_ERR
+                                      "could not create kernel thread "
+                                      "for hotkey polling\n");
+                       }
+               }
+       } else {
+               hotkey_poll_stop_sync();
+               if (may_warn &&
+                   hotkey_source_mask != 0 && hotkey_poll_freq == 0) {
+                       printk(TPACPI_NOTICE
+                               "hot keys 0x%08x require polling, "
+                               "which is currently disabled\n",
+                               hotkey_source_mask);
+               }
+       }
+}
+
+static void hotkey_poll_setup_safe(int may_warn)
+{
+       mutex_lock(&hotkey_mutex);
+       hotkey_poll_setup(may_warn);
+       mutex_unlock(&hotkey_mutex);
+}
+
+#else /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+static void hotkey_poll_setup_safe(int __unused)
+{
+}
+
+#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+static int hotkey_inputdev_open(struct input_dev *dev)
+{
+       switch (tpacpi_lifecycle) {
+       case TPACPI_LIFE_INIT:
+               /*
+                * hotkey_init will call hotkey_poll_setup_safe
+                * at the appropriate moment
+                */
+               return 0;
+       case TPACPI_LIFE_EXITING:
+               return -EBUSY;
+       case TPACPI_LIFE_RUNNING:
+               hotkey_poll_setup_safe(0);
+               return 0;
+       }
+
+       /* Should only happen if tpacpi_lifecycle is corrupt */
+       BUG();
+       return -EBUSY;
+}
+
+static void hotkey_inputdev_close(struct input_dev *dev)
+{
+       /* disable hotkey polling when possible */
+       if (tpacpi_lifecycle == TPACPI_LIFE_RUNNING)
+               hotkey_poll_setup_safe(0);
+}
+
+/* sysfs hotkey enable ------------------------------------------------- */
+static ssize_t hotkey_enable_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int res, status;
+
+       res = hotkey_status_get(&status);
+       if (res)
+               return res;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", status);
+}
+
+static ssize_t hotkey_enable_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+       int res;
+
+       if (parse_strtoul(buf, 1, &t))
+               return -EINVAL;
+
+       res = hotkey_status_set(t);
+
+       return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_enable =
+       __ATTR(hotkey_enable, S_IWUSR | S_IRUGO,
+               hotkey_enable_show, hotkey_enable_store);
+
+/* sysfs hotkey mask --------------------------------------------------- */
+static ssize_t hotkey_mask_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int res;
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+       res = hotkey_mask_get();
+       mutex_unlock(&hotkey_mutex);
+
+       return (res)?
+               res : snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_mask);
+}
+
+static ssize_t hotkey_mask_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+       int res;
+
+       if (parse_strtoul(buf, 0xffffffffUL, &t))
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+
+       res = hotkey_mask_set(t);
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       hotkey_poll_setup(1);
+#endif
+
+       mutex_unlock(&hotkey_mutex);
+
+       return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_hotkey_mask =
+       __ATTR(hotkey_mask, S_IWUSR | S_IRUGO,
+               hotkey_mask_show, hotkey_mask_store);
+
+/* sysfs hotkey bios_enabled ------------------------------------------- */
+static ssize_t hotkey_bios_enabled_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_enabled =
+       __ATTR(hotkey_bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL);
+
+/* sysfs hotkey bios_mask ---------------------------------------------- */
+static ssize_t hotkey_bios_mask_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_bios_mask =
+       __ATTR(hotkey_bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL);
+
+/* sysfs hotkey all_mask ----------------------------------------------- */
+static ssize_t hotkey_all_mask_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n",
+                               hotkey_all_mask | hotkey_source_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_all_mask =
+       __ATTR(hotkey_all_mask, S_IRUGO, hotkey_all_mask_show, NULL);
+
+/* sysfs hotkey recommended_mask --------------------------------------- */
+static ssize_t hotkey_recommended_mask_show(struct device *dev,
+                                           struct device_attribute *attr,
+                                           char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n",
+                       (hotkey_all_mask | hotkey_source_mask)
+                       & ~hotkey_reserved_mask);
+}
+
+static struct device_attribute dev_attr_hotkey_recommended_mask =
+       __ATTR(hotkey_recommended_mask, S_IRUGO,
+               hotkey_recommended_mask_show, NULL);
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+
+/* sysfs hotkey hotkey_source_mask ------------------------------------- */
+static ssize_t hotkey_source_mask_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask);
+}
+
+static ssize_t hotkey_source_mask_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+
+       if (parse_strtoul(buf, 0xffffffffUL, &t) ||
+               ((t & ~TPACPI_HKEY_NVRAM_KNOWN_MASK) != 0))
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+
+       HOTKEY_CONFIG_CRITICAL_START
+       hotkey_source_mask = t;
+       HOTKEY_CONFIG_CRITICAL_END
+
+       hotkey_poll_setup(1);
+
+       mutex_unlock(&hotkey_mutex);
+
+       return count;
+}
+
+static struct device_attribute dev_attr_hotkey_source_mask =
+       __ATTR(hotkey_source_mask, S_IWUSR | S_IRUGO,
+               hotkey_source_mask_show, hotkey_source_mask_store);
+
+/* sysfs hotkey hotkey_poll_freq --------------------------------------- */
+static ssize_t hotkey_poll_freq_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq);
+}
+
+static ssize_t hotkey_poll_freq_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+
+       if (parse_strtoul(buf, 25, &t))
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+
+       hotkey_poll_freq = t;
+
+       hotkey_poll_setup(1);
+       mutex_unlock(&hotkey_mutex);
+
+       return count;
+}
+
+static struct device_attribute dev_attr_hotkey_poll_freq =
+       __ATTR(hotkey_poll_freq, S_IWUSR | S_IRUGO,
+               hotkey_poll_freq_show, hotkey_poll_freq_store);
+
+#endif /* CONFIG_THINKPAD_ACPI_HOTKEY_POLL */
+
+/* sysfs hotkey radio_sw (pollable) ------------------------------------ */
+static ssize_t hotkey_radio_sw_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int res, s;
+       res = hotkey_get_wlsw(&s);
+       if (res < 0)
+               return res;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
+}
+
+static struct device_attribute dev_attr_hotkey_radio_sw =
+       __ATTR(hotkey_radio_sw, S_IRUGO, hotkey_radio_sw_show, NULL);
+
+static void hotkey_radio_sw_notify_change(void)
+{
+       if (tp_features.hotkey_wlsw)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "hotkey_radio_sw");
+}
+
+/* sysfs hotkey tablet mode (pollable) --------------------------------- */
+static ssize_t hotkey_tablet_mode_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int res, s;
+       res = hotkey_get_tablet_mode(&s);
+       if (res < 0)
+               return res;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", !!s);
+}
+
+static struct device_attribute dev_attr_hotkey_tablet_mode =
+       __ATTR(hotkey_tablet_mode, S_IRUGO, hotkey_tablet_mode_show, NULL);
+
+static void hotkey_tablet_mode_notify_change(void)
+{
+       if (tp_features.hotkey_tablet)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "hotkey_tablet_mode");
+}
+
+/* sysfs hotkey report_mode -------------------------------------------- */
+static ssize_t hotkey_report_mode_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+               (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
+}
+
+static struct device_attribute dev_attr_hotkey_report_mode =
+       __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
+
+/* sysfs wakeup reason (pollable) -------------------------------------- */
+static ssize_t hotkey_wakeup_reason_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason);
+}
+
+static struct device_attribute dev_attr_hotkey_wakeup_reason =
+       __ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL);
+
+static void hotkey_wakeup_reason_notify_change(void)
+{
+       if (tp_features.hotkey_mask)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "wakeup_reason");
+}
+
+/* sysfs wakeup hotunplug_complete (pollable) -------------------------- */
+static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack);
+}
+
+static struct device_attribute dev_attr_hotkey_wakeup_hotunplug_complete =
+       __ATTR(wakeup_hotunplug_complete, S_IRUGO,
+              hotkey_wakeup_hotunplug_complete_show, NULL);
+
+static void hotkey_wakeup_hotunplug_complete_notify_change(void)
+{
+       if (tp_features.hotkey_mask)
+               sysfs_notify(&tpacpi_pdev->dev.kobj, NULL,
+                            "wakeup_hotunplug_complete");
+}
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *hotkey_attributes[] __initdata = {
+       &dev_attr_hotkey_enable.attr,
+       &dev_attr_hotkey_bios_enabled.attr,
+       &dev_attr_hotkey_report_mode.attr,
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       &dev_attr_hotkey_mask.attr,
+       &dev_attr_hotkey_all_mask.attr,
+       &dev_attr_hotkey_recommended_mask.attr,
+       &dev_attr_hotkey_source_mask.attr,
+       &dev_attr_hotkey_poll_freq.attr,
+#endif
+};
+
+static struct attribute *hotkey_mask_attributes[] __initdata = {
+       &dev_attr_hotkey_bios_mask.attr,
+#ifndef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       &dev_attr_hotkey_mask.attr,
+       &dev_attr_hotkey_all_mask.attr,
+       &dev_attr_hotkey_recommended_mask.attr,
+#endif
+       &dev_attr_hotkey_wakeup_reason.attr,
+       &dev_attr_hotkey_wakeup_hotunplug_complete.attr,
+};
+
+static void bluetooth_update_rfk(void);
+static void wan_update_rfk(void);
+static void tpacpi_send_radiosw_update(void)
+{
+       int wlsw;
+
+       /* Sync these BEFORE sending any rfkill events */
+       if (tp_features.bluetooth)
+               bluetooth_update_rfk();
+       if (tp_features.wan)
+               wan_update_rfk();
+
+       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
+               mutex_lock(&tpacpi_inputdev_send_mutex);
+
+               input_report_switch(tpacpi_inputdev,
+                                   SW_RFKILL_ALL, !!wlsw);
+               input_sync(tpacpi_inputdev);
+
+               mutex_unlock(&tpacpi_inputdev_send_mutex);
+       }
+       hotkey_radio_sw_notify_change();
+}
+
+static void hotkey_exit(void)
+{
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       hotkey_poll_stop_sync();
+#endif
+
+       if (hotkey_dev_attributes)
+               delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+
+       kfree(hotkey_keycode_map);
+
+       if (tp_features.hotkey) {
+               dbg_printk(TPACPI_DBG_EXIT,
+                          "restoring original hot key mask\n");
+               /* no short-circuit boolean operator below! */
+               if ((hotkey_mask_set(hotkey_orig_mask) |
+                    hotkey_status_set(hotkey_orig_status)) != 0)
+                       printk(TPACPI_ERR
+                              "failed to restore hot key mask "
+                              "to BIOS defaults\n");
+       }
+}
+
+static int __init hotkey_init(struct ibm_init_struct *iibm)
+{
+       /* Requirements for changing the default keymaps:
+        *
+        * 1. Many of the keys are mapped to KEY_RESERVED for very
+        *    good reasons.  Do not change them unless you have deep
+        *    knowledge on the IBM and Lenovo ThinkPad firmware for
+        *    the various ThinkPad models.  The driver behaves
+        *    differently for KEY_RESERVED: such keys have their
+        *    hot key mask *unset* in mask_recommended, and also
+        *    in the initial hot key mask programmed into the
+        *    firmware at driver load time, which means the firm-
+        *    ware may react very differently if you change them to
+        *    something else;
+        *
+        * 2. You must be subscribed to the linux-thinkpad and
+        *    ibm-acpi-devel mailing lists, and you should read the
+        *    list archives since 2007 if you want to change the
+        *    keymaps.  This requirement exists so that you will
+        *    know the past history of problems with the thinkpad-
+        *    acpi driver keymaps, and also that you will be
+        *    listening to any bug reports;
+        *
+        * 3. Do not send thinkpad-acpi specific patches directly to
+        *    for merging, *ever*.  Send them to the linux-acpi
+        *    mailinglist for comments.  Merging is to be done only
+        *    through acpi-test and the ACPI maintainer.
+        *
+        * If the above is too much to ask, don't change the keymap.
+        * Ask the thinkpad-acpi maintainer to do it, instead.
+        */
+       static u16 ibm_keycode_map[] __initdata = {
+               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+               KEY_FN_F1,      KEY_FN_F2,      KEY_COFFEE,     KEY_SLEEP,
+               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
+
+               /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
+               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
+               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
+               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
+
+               /* brightness: firmware always reacts to them, unless
+                * X.org did some tricks in the radeon BIOS scratch
+                * registers of *some* models */
+               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
+               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
+
+               /* Thinklight: firmware always react to it */
+               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
+
+               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
+               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
+
+               /* Volume: firmware always react to it and reprograms
+                * the built-in *extra* mixer.  Never map it to control
+                * another mixer by default. */
+               KEY_RESERVED,   /* 0x14: VOLUME UP */
+               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
+               KEY_RESERVED,   /* 0x16: MUTE */
+
+               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
+
+               /* (assignments unknown, please report if found) */
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+       };
+       static u16 lenovo_keycode_map[] __initdata = {
+               /* Scan Codes 0x00 to 0x0B: ACPI HKEY FN+F1..F12 */
+               KEY_FN_F1,      KEY_COFFEE,     KEY_BATTERY,    KEY_SLEEP,
+               KEY_WLAN,       KEY_FN_F6, KEY_SWITCHVIDEOMODE, KEY_FN_F8,
+               KEY_FN_F9,      KEY_FN_F10,     KEY_FN_F11,     KEY_SUSPEND,
+
+               /* Scan codes 0x0C to 0x1F: Other ACPI HKEY hot keys */
+               KEY_UNKNOWN,    /* 0x0C: FN+BACKSPACE */
+               KEY_UNKNOWN,    /* 0x0D: FN+INSERT */
+               KEY_UNKNOWN,    /* 0x0E: FN+DELETE */
+
+               /* These either have to go through ACPI video, or
+                * act like in the IBM ThinkPads, so don't ever
+                * enable them by default */
+               KEY_RESERVED,   /* 0x0F: FN+HOME (brightness up) */
+               KEY_RESERVED,   /* 0x10: FN+END (brightness down) */
+
+               KEY_RESERVED,   /* 0x11: FN+PGUP (thinklight toggle) */
+
+               KEY_UNKNOWN,    /* 0x12: FN+PGDOWN */
+               KEY_ZOOM,       /* 0x13: FN+SPACE (zoom) */
+
+               /* Volume: z60/z61, T60 (BIOS version?): firmware always
+                * react to it and reprograms the built-in *extra* mixer.
+                * Never map it to control another mixer by default.
+                *
+                * T60?, T61, R60?, R61: firmware and EC tries to send
+                * these over the regular keyboard, so these are no-ops,
+                * but there are still weird bugs re. MUTE, so do not
+                * change unless you get test reports from all Lenovo
+                * models.  May cause the BIOS to interfere with the
+                * HDA mixer.
+                */
+               KEY_RESERVED,   /* 0x14: VOLUME UP */
+               KEY_RESERVED,   /* 0x15: VOLUME DOWN */
+               KEY_RESERVED,   /* 0x16: MUTE */
+
+               KEY_VENDOR,     /* 0x17: Thinkpad/AccessIBM/Lenovo */
+
+               /* (assignments unknown, please report if found) */
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+               KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+       };
+
+#define TPACPI_HOTKEY_MAP_LEN          ARRAY_SIZE(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_SIZE         sizeof(ibm_keycode_map)
+#define TPACPI_HOTKEY_MAP_TYPESIZE     sizeof(ibm_keycode_map[0])
+
+       int res, i;
+       int status;
+       int hkeyv;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
+
+       BUG_ON(!tpacpi_inputdev);
+       BUG_ON(tpacpi_inputdev->open != NULL ||
+              tpacpi_inputdev->close != NULL);
+
+       TPACPI_ACPIHANDLE_INIT(hkey);
+       mutex_init(&hotkey_mutex);
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       mutex_init(&hotkey_thread_mutex);
+       mutex_init(&hotkey_thread_data_mutex);
+#endif
+
+       /* hotkey not supported on 570 */
+       tp_features.hotkey = hkey_handle != NULL;
+
+       vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
+               str_supported(tp_features.hotkey));
+
+       if (!tp_features.hotkey)
+               return 1;
+
+       tpacpi_disable_brightness_delay();
+
+       hotkey_dev_attributes = create_attr_set(13, NULL);
+       if (!hotkey_dev_attributes)
+               return -ENOMEM;
+       res = add_many_to_attr_set(hotkey_dev_attributes,
+                       hotkey_attributes,
+                       ARRAY_SIZE(hotkey_attributes));
+       if (res)
+               goto err_exit;
+
+       /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+          A30, R30, R31, T20-22, X20-21, X22-24.  Detected by checking
+          for HKEY interface version 0x100 */
+       if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+               if ((hkeyv >> 8) != 1) {
+                       printk(TPACPI_ERR "unknown version of the "
+                              "HKEY interface: 0x%x\n", hkeyv);
+                       printk(TPACPI_ERR "please report this to %s\n",
+                              TPACPI_MAIL);
+               } else {
+                       /*
+                        * MHKV 0x100 in A31, R40, R40e,
+                        * T4x, X31, and later
+                        */
+                       tp_features.hotkey_mask = 1;
+               }
+       }
+
+       vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
+               str_supported(tp_features.hotkey_mask));
+
+       if (tp_features.hotkey_mask) {
+               if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
+                               "MHKA", "qd")) {
+                       printk(TPACPI_ERR
+                              "missing MHKA handler, "
+                              "please report this to %s\n",
+                              TPACPI_MAIL);
+                       /* FN+F12, FN+F4, FN+F3 */
+                       hotkey_all_mask = 0x080cU;
+               }
+       }
+
+       /* hotkey_source_mask *must* be zero for
+        * the first hotkey_mask_get */
+       res = hotkey_status_get(&hotkey_orig_status);
+       if (res)
+               goto err_exit;
+
+       if (tp_features.hotkey_mask) {
+               res = hotkey_mask_get();
+               if (res)
+                       goto err_exit;
+
+               hotkey_orig_mask = hotkey_mask;
+               res = add_many_to_attr_set(
+                               hotkey_dev_attributes,
+                               hotkey_mask_attributes,
+                               ARRAY_SIZE(hotkey_mask_attributes));
+               if (res)
+                       goto err_exit;
+       }
+
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+       if (tp_features.hotkey_mask) {
+               hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
+                                       & ~hotkey_all_mask;
+       } else {
+               hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
+       }
+
+       vdbg_printk(TPACPI_DBG_INIT,
+                   "hotkey source mask 0x%08x, polling freq %d\n",
+                   hotkey_source_mask, hotkey_poll_freq);
+#endif
+
+       /* Not all thinkpads have a hardware radio switch */
+       if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
+               tp_features.hotkey_wlsw = 1;
+               printk(TPACPI_INFO
+                       "radio switch found; radios are %s\n",
+                       enabled(status, 0));
+       }
+       if (tp_features.hotkey_wlsw)
+               res = add_to_attr_set(hotkey_dev_attributes,
+                               &dev_attr_hotkey_radio_sw.attr);
+
+       /* For X41t, X60t, X61t Tablets... */
+       if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
+               tp_features.hotkey_tablet = 1;
+               printk(TPACPI_INFO
+                       "possible tablet mode switch found; "
+                       "ThinkPad in %s mode\n",
+                       (status & TP_HOTKEY_TABLET_MASK)?
+                               "tablet" : "laptop");
+               res = add_to_attr_set(hotkey_dev_attributes,
+                               &dev_attr_hotkey_tablet_mode.attr);
+       }
+
+       if (!res)
+               res = register_attr_set_with_sysfs(
+                               hotkey_dev_attributes,
+                               &tpacpi_pdev->dev.kobj);
+       if (res)
+               goto err_exit;
+
+       /* Set up key map */
+
+       hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
+                                       GFP_KERNEL);
+       if (!hotkey_keycode_map) {
+               printk(TPACPI_ERR
+                       "failed to allocate memory for key map\n");
+               res = -ENOMEM;
+               goto err_exit;
+       }
+
+       if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
+               dbg_printk(TPACPI_DBG_INIT,
+                          "using Lenovo default hot key map\n");
+               memcpy(hotkey_keycode_map, &lenovo_keycode_map,
+                       TPACPI_HOTKEY_MAP_SIZE);
+       } else {
+               dbg_printk(TPACPI_DBG_INIT,
+                          "using IBM default hot key map\n");
+               memcpy(hotkey_keycode_map, &ibm_keycode_map,
+                       TPACPI_HOTKEY_MAP_SIZE);
+       }
+
+       set_bit(EV_KEY, tpacpi_inputdev->evbit);
+       set_bit(EV_MSC, tpacpi_inputdev->evbit);
+       set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
+       tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
+       tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
+       tpacpi_inputdev->keycode = hotkey_keycode_map;
+       for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
+               if (hotkey_keycode_map[i] != KEY_RESERVED) {
+                       set_bit(hotkey_keycode_map[i],
+                               tpacpi_inputdev->keybit);
+               } else {
+                       if (i < sizeof(hotkey_reserved_mask)*8)
+                               hotkey_reserved_mask |= 1 << i;
+               }
+       }
+
+       if (tp_features.hotkey_wlsw) {
+               set_bit(EV_SW, tpacpi_inputdev->evbit);
+               set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
+       }
+       if (tp_features.hotkey_tablet) {
+               set_bit(EV_SW, tpacpi_inputdev->evbit);
+               set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
+       }
+
+       /* Do not issue duplicate brightness change events to
+        * userspace */
+       if (!tp_features.bright_acpimode)
+               /* update bright_acpimode... */
+               tpacpi_check_std_acpi_brightness_support();
+
+       if (tp_features.bright_acpimode) {
+               printk(TPACPI_INFO
+                      "This ThinkPad has standard ACPI backlight "
+                      "brightness control, supported by the ACPI "
+                      "video driver\n");
+               printk(TPACPI_NOTICE
+                      "Disabling thinkpad-acpi brightness events "
+                      "by default...\n");
+
+               /* The hotkey_reserved_mask change below is not
+                * necessary while the keys are at KEY_RESERVED in the
+                * default map, but better safe than sorry, leave it
+                * here as a marker of what we have to do, especially
+                * when we finally become able to set this at runtime
+                * on response to X.org requests */
+               hotkey_reserved_mask |=
+                       (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
+                       | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
+       }
+
+       dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
+       res = hotkey_status_set(1);
+       if (res) {
+               hotkey_exit();
+               return res;
+       }
+       res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
+                               & ~hotkey_reserved_mask)
+                               | hotkey_orig_mask);
+       if (res < 0 && res != -ENXIO) {
+               hotkey_exit();
+               return res;
+       }
+
+       dbg_printk(TPACPI_DBG_INIT,
+                       "legacy hot key reporting over procfs %s\n",
+                       (hotkey_report_mode < 2) ?
+                               "enabled" : "disabled");
+
+       tpacpi_inputdev->open = &hotkey_inputdev_open;
+       tpacpi_inputdev->close = &hotkey_inputdev_close;
+
+       hotkey_poll_setup_safe(1);
+       tpacpi_send_radiosw_update();
+       tpacpi_input_send_tabletsw();
+
+       return 0;
+
+err_exit:
+       delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+       hotkey_dev_attributes = NULL;
+
+       return (res < 0)? res : 1;
+}
+
+static void hotkey_notify(struct ibm_struct *ibm, u32 event)
+{
+       u32 hkey;
+       unsigned int scancode;
+       int send_acpi_ev;
+       int ignore_acpi_ev;
+       int unk_ev;
+
+       if (event != 0x80) {
+               printk(TPACPI_ERR
+                      "unknown HKEY notification event %d\n", event);
+               /* forward it to userspace, maybe it knows how to handle it */
+               acpi_bus_generate_netlink_event(
+                                       ibm->acpi->device->pnp.device_class,
+                                       ibm->acpi->device->dev.bus_id,
+                                       event, 0);
+               return;
+       }
+
+       while (1) {
+               if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {
+                       printk(TPACPI_ERR "failed to retrieve HKEY event\n");
+                       return;
+               }
+
+               if (hkey == 0) {
+                       /* queue empty */
+                       return;
+               }
+
+               send_acpi_ev = 1;
+               ignore_acpi_ev = 0;
+               unk_ev = 0;
+
+               switch (hkey >> 12) {
+               case 1:
+                       /* 0x1000-0x1FFF: key presses */
+                       scancode = hkey & 0xfff;
+                       if (scancode > 0 && scancode < 0x21) {
+                               scancode--;
+                               if (!(hotkey_source_mask & (1 << scancode))) {
+                                       tpacpi_input_send_key(scancode);
+                                       send_acpi_ev = 0;
+                               } else {
+                                       ignore_acpi_ev = 1;
+                               }
+                       } else {
+                               unk_ev = 1;
+                       }
+                       break;
+               case 2:
+                       /* Wakeup reason */
+                       switch (hkey) {
+                       case 0x2304: /* suspend, undock */
+                       case 0x2404: /* hibernation, undock */
+                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_UNDOCK;
+                               ignore_acpi_ev = 1;
+                               break;
+                       case 0x2305: /* suspend, bay eject */
+                       case 0x2405: /* hibernation, bay eject */
+                               hotkey_wakeup_reason = TP_ACPI_WAKEUP_BAYEJ;
+                               ignore_acpi_ev = 1;
+                               break;
+                       default:
+                               unk_ev = 1;
+                       }
+                       if (hotkey_wakeup_reason != TP_ACPI_WAKEUP_NONE) {
+                               printk(TPACPI_INFO
+                                      "woke up due to a hot-unplug "
+                                      "request...\n");
+                               hotkey_wakeup_reason_notify_change();
+                       }
+                       break;
+               case 3:
+                       /* bay-related wakeups */
+                       if (hkey == 0x3003) {
+                               hotkey_autosleep_ack = 1;
+                               printk(TPACPI_INFO
+                                      "bay ejected\n");
+                               hotkey_wakeup_hotunplug_complete_notify_change();
+                       } else {
+                               unk_ev = 1;
+                       }
+                       break;
+               case 4:
+                       /* dock-related wakeups */
+                       if (hkey == 0x4003) {
+                               hotkey_autosleep_ack = 1;
+                               printk(TPACPI_INFO
+                                      "undocked\n");
+                               hotkey_wakeup_hotunplug_complete_notify_change();
+                       } else {
+                               unk_ev = 1;
+                       }
+                       break;
+               case 5:
+                       /* 0x5000-0x5FFF: human interface helpers */
+                       switch (hkey) {
+                       case 0x5010: /* Lenovo new BIOS: brightness changed */
+                       case 0x500b: /* X61t: tablet pen inserted into bay */
+                       case 0x500c: /* X61t: tablet pen removed from bay */
+                               break;
+                       case 0x5009: /* X41t-X61t: swivel up (tablet mode) */
+                       case 0x500a: /* X41t-X61t: swivel down (normal mode) */
+                               tpacpi_input_send_tabletsw();
+                               hotkey_tablet_mode_notify_change();
+                               send_acpi_ev = 0;
+                               break;
+                       case 0x5001:
+                       case 0x5002:
+                               /* LID switch events.  Do not propagate */
+                               ignore_acpi_ev = 1;
+                               break;
+                       default:
+                               unk_ev = 1;
+                       }
+                       break;
+               case 7:
+                       /* 0x7000-0x7FFF: misc */
+                       if (tp_features.hotkey_wlsw && hkey == 0x7000) {
+                               tpacpi_send_radiosw_update();
+                               send_acpi_ev = 0;
+                               break;
+                       }
+                       /* fallthrough to default */
+               default:
+                       unk_ev = 1;
+               }
+               if (unk_ev) {
+                       printk(TPACPI_NOTICE
+                              "unhandled HKEY event 0x%04x\n", hkey);
+               }
+
+               /* Legacy events */
+               if (!ignore_acpi_ev &&
+                   (send_acpi_ev || hotkey_report_mode < 2)) {
+                       acpi_bus_generate_proc_event(ibm->acpi->device,
+                                                    event, hkey);
+               }
+
+               /* netlink events */
+               if (!ignore_acpi_ev && send_acpi_ev) {
+                       acpi_bus_generate_netlink_event(
+                                       ibm->acpi->device->pnp.device_class,
+                                       ibm->acpi->device->dev.bus_id,
+                                       event, hkey);
+               }
+       }
+}
+
+static void hotkey_suspend(pm_message_t state)
+{
+       /* Do these on suspend, we get the events on early resume! */
+       hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
+       hotkey_autosleep_ack = 0;
+}
+
+static void hotkey_resume(void)
+{
+       tpacpi_disable_brightness_delay();
+
+       if (hotkey_mask_get())
+               printk(TPACPI_ERR
+                      "error while trying to read hot key mask "
+                      "from firmware\n");
+       tpacpi_send_radiosw_update();
+       hotkey_tablet_mode_notify_change();
+       hotkey_wakeup_reason_notify_change();
+       hotkey_wakeup_hotunplug_complete_notify_change();
+       hotkey_poll_setup_safe(0);
+}
+
+/* procfs -------------------------------------------------------------- */
+static int hotkey_read(char *p)
+{
+       int res, status;
+       int len = 0;
+
+       if (!tp_features.hotkey) {
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+               return len;
+       }
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+       res = hotkey_status_get(&status);
+       if (!res)
+               res = hotkey_mask_get();
+       mutex_unlock(&hotkey_mutex);
+       if (res)
+               return res;
+
+       len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+       if (tp_features.hotkey_mask) {
+               len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_mask);
+               len += sprintf(p + len,
+                              "commands:\tenable, disable, reset, <mask>\n");
+       } else {
+               len += sprintf(p + len, "mask:\t\tnot supported\n");
+               len += sprintf(p + len, "commands:\tenable, disable, reset\n");
+       }
+
+       return len;
+}
+
+static int hotkey_write(char *buf)
+{
+       int res, status;
+       u32 mask;
+       char *cmd;
+
+       if (!tp_features.hotkey)
+               return -ENODEV;
+
+       if (mutex_lock_interruptible(&hotkey_mutex))
+               return -ERESTARTSYS;
+
+       status = -1;
+       mask = hotkey_mask;
+
+       res = 0;
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "enable") == 0) {
+                       status = 1;
+               } else if (strlencmp(cmd, "disable") == 0) {
+                       status = 0;
+               } else if (strlencmp(cmd, "reset") == 0) {
+                       status = hotkey_orig_status;
+                       mask = hotkey_orig_mask;
+               } else if (sscanf(cmd, "0x%x", &mask) == 1) {
+                       /* mask set */
+               } else if (sscanf(cmd, "%x", &mask) == 1) {
+                       /* mask set */
+               } else {
+                       res = -EINVAL;
+                       goto errexit;
+               }
+       }
+       if (status != -1)
+               res = hotkey_status_set(status);
+
+       if (!res && mask != hotkey_mask)
+               res = hotkey_mask_set(mask);
+
+errexit:
+       mutex_unlock(&hotkey_mutex);
+       return res;
+}
+
+static const struct acpi_device_id ibm_htk_device_ids[] = {
+       {TPACPI_ACPI_HKEY_HID, 0},
+       {"", 0},
+};
+
+static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
+       .hid = ibm_htk_device_ids,
+       .notify = hotkey_notify,
+       .handle = &hkey_handle,
+       .type = ACPI_DEVICE_NOTIFY,
+};
+
+static struct ibm_struct hotkey_driver_data = {
+       .name = "hotkey",
+       .read = hotkey_read,
+       .write = hotkey_write,
+       .exit = hotkey_exit,
+       .resume = hotkey_resume,
+       .suspend = hotkey_suspend,
+       .acpi = &ibm_hotkey_acpidriver,
+};
+
+/*************************************************************************
+ * Bluetooth subdriver
+ */
+
+enum {
+       /* ACPI GBDC/SBDC bits */
+       TP_ACPI_BLUETOOTH_HWPRESENT     = 0x01, /* Bluetooth hw available */
+       TP_ACPI_BLUETOOTH_RADIOSSW      = 0x02, /* Bluetooth radio enabled */
+       TP_ACPI_BLUETOOTH_UNK           = 0x04, /* unknown function */
+};
+
+static struct rfkill *tpacpi_bluetooth_rfkill;
+
+static int bluetooth_get_radiosw(void)
+{
+       int status;
+
+       if (!tp_features.bluetooth)
+               return -ENODEV;
+
+       /* WLSW overrides bluetooth in firmware/hardware, reflect that */
+       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+               return RFKILL_STATE_HARD_BLOCKED;
+
+       if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+               return -EIO;
+
+       return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
+               RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void bluetooth_update_rfk(void)
+{
+       int status;
+
+       if (!tpacpi_bluetooth_rfkill)
+               return;
+
+       status = bluetooth_get_radiosw();
+       if (status < 0)
+               return;
+       rfkill_force_state(tpacpi_bluetooth_rfkill, status);
+}
+
+static int bluetooth_set_radiosw(int radio_on, int update_rfk)
+{
+       int status;
+
+       if (!tp_features.bluetooth)
+               return -ENODEV;
+
+       /* WLSW overrides bluetooth in firmware/hardware, but there is no
+        * reason to risk weird behaviour. */
+       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+           && radio_on)
+               return -EPERM;
+
+       if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+               return -EIO;
+       if (radio_on)
+               status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+       else
+               status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+       if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+               return -EIO;
+
+       if (update_rfk)
+               bluetooth_update_rfk();
+
+       return 0;
+}
+
+/* sysfs bluetooth enable ---------------------------------------------- */
+static ssize_t bluetooth_enable_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int status;
+
+       status = bluetooth_get_radiosw();
+       if (status < 0)
+               return status;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
+}
+
+static ssize_t bluetooth_enable_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+       int res;
+
+       if (parse_strtoul(buf, 1, &t))
+               return -EINVAL;
+
+       res = bluetooth_set_radiosw(t, 1);
+
+       return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_bluetooth_enable =
+       __ATTR(bluetooth_enable, S_IWUSR | S_IRUGO,
+               bluetooth_enable_show, bluetooth_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *bluetooth_attributes[] = {
+       &dev_attr_bluetooth_enable.attr,
+       NULL
+};
+
+static const struct attribute_group bluetooth_attr_group = {
+       .attrs = bluetooth_attributes,
+};
+
+static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
+{
+       int bts = bluetooth_get_radiosw();
+
+       if (bts < 0)
+               return bts;
+
+       *state = bts;
+       return 0;
+}
+
+static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
+{
+       return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void bluetooth_exit(void)
+{
+       if (tpacpi_bluetooth_rfkill)
+               rfkill_unregister(tpacpi_bluetooth_rfkill);
+
+       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+                       &bluetooth_attr_group);
+}
+
+static int __init bluetooth_init(struct ibm_init_struct *iibm)
+{
+       int res;
+       int status = 0;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(hkey);
+
+       /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+          G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
+       tp_features.bluetooth = hkey_handle &&
+           acpi_evalf(hkey_handle, &status, "GBDC", "qd");
+
+       vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
+               str_supported(tp_features.bluetooth),
+               status);
+
+       if (tp_features.bluetooth &&
+           !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
+               /* no bluetooth hardware present in system */
+               tp_features.bluetooth = 0;
+               dbg_printk(TPACPI_DBG_INIT,
+                          "bluetooth hardware not installed\n");
+       }
+
+       if (!tp_features.bluetooth)
+               return 1;
+
+       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+                               &bluetooth_attr_group);
+       if (res)
+               return res;
+
+       res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
+                               &tpacpi_bluetooth_rfkill,
+                               RFKILL_TYPE_BLUETOOTH,
+                               "tpacpi_bluetooth_sw",
+                               tpacpi_bluetooth_rfk_set,
+                               tpacpi_bluetooth_rfk_get);
+       if (res) {
+               bluetooth_exit();
+               return res;
+       }
+
+       return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int bluetooth_read(char *p)
+{
+       int len = 0;
+       int status = bluetooth_get_radiosw();
+
+       if (!tp_features.bluetooth)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else {
+               len += sprintf(p + len, "status:\t\t%s\n",
+                               (status == RFKILL_STATE_UNBLOCKED) ?
+                                       "enabled" : "disabled");
+               len += sprintf(p + len, "commands:\tenable, disable\n");
+       }
+
+       return len;
+}
+
+static int bluetooth_write(char *buf)
+{
+       char *cmd;
+
+       if (!tp_features.bluetooth)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "enable") == 0) {
+                       bluetooth_set_radiosw(1, 1);
+               } else if (strlencmp(cmd, "disable") == 0) {
+                       bluetooth_set_radiosw(0, 1);
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct bluetooth_driver_data = {
+       .name = "bluetooth",
+       .read = bluetooth_read,
+       .write = bluetooth_write,
+       .exit = bluetooth_exit,
+};
+
+/*************************************************************************
+ * Wan subdriver
+ */
+
+enum {
+       /* ACPI GWAN/SWAN bits */
+       TP_ACPI_WANCARD_HWPRESENT       = 0x01, /* Wan hw available */
+       TP_ACPI_WANCARD_RADIOSSW        = 0x02, /* Wan radio enabled */
+       TP_ACPI_WANCARD_UNK             = 0x04, /* unknown function */
+};
+
+static struct rfkill *tpacpi_wan_rfkill;
+
+static int wan_get_radiosw(void)
+{
+       int status;
+
+       if (!tp_features.wan)
+               return -ENODEV;
+
+       /* WLSW overrides WWAN in firmware/hardware, reflect that */
+       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+               return RFKILL_STATE_HARD_BLOCKED;
+
+       if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+               return -EIO;
+
+       return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
+               RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void wan_update_rfk(void)
+{
+       int status;
+
+       if (!tpacpi_wan_rfkill)
+               return;
+
+       status = wan_get_radiosw();
+       if (status < 0)
+               return;
+       rfkill_force_state(tpacpi_wan_rfkill, status);
+}
+
+static int wan_set_radiosw(int radio_on, int update_rfk)
+{
+       int status;
+
+       if (!tp_features.wan)
+               return -ENODEV;
+
+       /* WLSW overrides bluetooth in firmware/hardware, but there is no
+        * reason to risk weird behaviour. */
+       if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+           && radio_on)
+               return -EPERM;
+
+       if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+               return -EIO;
+       if (radio_on)
+               status |= TP_ACPI_WANCARD_RADIOSSW;
+       else
+               status &= ~TP_ACPI_WANCARD_RADIOSSW;
+       if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
+               return -EIO;
+
+       if (update_rfk)
+               wan_update_rfk();
+
+       return 0;
+}
+
+/* sysfs wan enable ---------------------------------------------------- */
+static ssize_t wan_enable_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int status;
+
+       status = wan_get_radiosw();
+       if (status < 0)
+               return status;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
+}
+
+static ssize_t wan_enable_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long t;
+       int res;
+
+       if (parse_strtoul(buf, 1, &t))
+               return -EINVAL;
+
+       res = wan_set_radiosw(t, 1);
+
+       return (res) ? res : count;
+}
+
+static struct device_attribute dev_attr_wan_enable =
+       __ATTR(wwan_enable, S_IWUSR | S_IRUGO,
+               wan_enable_show, wan_enable_store);
+
+/* --------------------------------------------------------------------- */
+
+static struct attribute *wan_attributes[] = {
+       &dev_attr_wan_enable.attr,
+       NULL
+};
+
+static const struct attribute_group wan_attr_group = {
+       .attrs = wan_attributes,
+};
+
+static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
+{
+       int wans = wan_get_radiosw();
+
+       if (wans < 0)
+               return wans;
+
+       *state = wans;
+       return 0;
+}
+
+static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
+{
+       return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void wan_exit(void)
+{
+       if (tpacpi_wan_rfkill)
+               rfkill_unregister(tpacpi_wan_rfkill);
+
+       sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+               &wan_attr_group);
+}
+
+static int __init wan_init(struct ibm_init_struct *iibm)
+{
+       int res;
+       int status = 0;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(hkey);
+
+       tp_features.wan = hkey_handle &&
+           acpi_evalf(hkey_handle, &status, "GWAN", "qd");
+
+       vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
+               str_supported(tp_features.wan),
+               status);
+
+       if (tp_features.wan &&
+           !(status & TP_ACPI_WANCARD_HWPRESENT)) {
+               /* no wan hardware present in system */
+               tp_features.wan = 0;
+               dbg_printk(TPACPI_DBG_INIT,
+                          "wan hardware not installed\n");
+       }
+
+       if (!tp_features.wan)
+               return 1;
+
+       res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+                               &wan_attr_group);
+       if (res)
+               return res;
+
+       res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
+                               &tpacpi_wan_rfkill,
+                               RFKILL_TYPE_WWAN,
+                               "tpacpi_wwan_sw",
+                               tpacpi_wan_rfk_set,
+                               tpacpi_wan_rfk_get);
+       if (res) {
+               wan_exit();
+               return res;
+       }
+
+       return 0;
+}
+
+/* procfs -------------------------------------------------------------- */
+static int wan_read(char *p)
+{
+       int len = 0;
+       int status = wan_get_radiosw();
+
+       if (!tp_features.wan)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else {
+               len += sprintf(p + len, "status:\t\t%s\n",
+                               (status == RFKILL_STATE_UNBLOCKED) ?
+                                       "enabled" : "disabled");
+               len += sprintf(p + len, "commands:\tenable, disable\n");
+       }
+
+       return len;
+}
+
+static int wan_write(char *buf)
+{
+       char *cmd;
+
+       if (!tp_features.wan)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "enable") == 0) {
+                       wan_set_radiosw(1, 1);
+               } else if (strlencmp(cmd, "disable") == 0) {
+                       wan_set_radiosw(0, 1);
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct wan_driver_data = {
+       .name = "wan",
+       .read = wan_read,
+       .write = wan_write,
+       .exit = wan_exit,
+};
+
+/*************************************************************************
+ * Video subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_VIDEO
+
+enum video_access_mode {
+       TPACPI_VIDEO_NONE = 0,
+       TPACPI_VIDEO_570,       /* 570 */
+       TPACPI_VIDEO_770,       /* 600e/x, 770e, 770x */
+       TPACPI_VIDEO_NEW,       /* all others */
+};
+
+enum { /* video status flags, based on VIDEO_570 */
+       TP_ACPI_VIDEO_S_LCD = 0x01,     /* LCD output enabled */
+       TP_ACPI_VIDEO_S_CRT = 0x02,     /* CRT output enabled */
+       TP_ACPI_VIDEO_S_DVI = 0x08,     /* DVI output enabled */
+};
+
+enum {  /* TPACPI_VIDEO_570 constants */
+       TP_ACPI_VIDEO_570_PHSCMD = 0x87,        /* unknown magic constant :( */
+       TP_ACPI_VIDEO_570_PHSMASK = 0x03,       /* PHS bits that map to
+                                                * video_status_flags */
+       TP_ACPI_VIDEO_570_PHS2CMD = 0x8b,       /* unknown magic constant :( */
+       TP_ACPI_VIDEO_570_PHS2SET = 0x80,       /* unknown magic constant :( */
+};
+
+static enum video_access_mode video_supported;
+static int video_orig_autosw;
+
+static int video_autosw_get(void);
+static int video_autosw_set(int enable);
+
+TPACPI_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID");      /* G41 */
+
+static int __init video_init(struct ibm_init_struct *iibm)
+{
+       int ivga;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(vid);
+       TPACPI_ACPIHANDLE_INIT(vid2);
+
+       if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
+               /* G41, assume IVGA doesn't change */
+               vid_handle = vid2_handle;
+
+       if (!vid_handle)
+               /* video switching not supported on R30, R31 */
+               video_supported = TPACPI_VIDEO_NONE;
+       else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
+               /* 570 */
+               video_supported = TPACPI_VIDEO_570;
+       else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
+               /* 600e/x, 770e, 770x */
+               video_supported = TPACPI_VIDEO_770;
+       else
+               /* all others */
+               video_supported = TPACPI_VIDEO_NEW;
+
+       vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
+               str_supported(video_supported != TPACPI_VIDEO_NONE),
+               video_supported);
+
+       return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
+}
+
+static void video_exit(void)
+{
+       dbg_printk(TPACPI_DBG_EXIT,
+                  "restoring original video autoswitch mode\n");
+       if (video_autosw_set(video_orig_autosw))
+               printk(TPACPI_ERR "error while trying to restore original "
+                       "video autoswitch mode\n");
+}
+
+static int video_outputsw_get(void)
+{
+       int status = 0;
+       int i;
+
+       switch (video_supported) {
+       case TPACPI_VIDEO_570:
+               if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
+                                TP_ACPI_VIDEO_570_PHSCMD))
+                       return -EIO;
+               status = i & TP_ACPI_VIDEO_570_PHSMASK;
+               break;
+       case TPACPI_VIDEO_770:
+               if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
+                       return -EIO;
+               if (i)
+                       status |= TP_ACPI_VIDEO_S_LCD;
+               if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
+                       return -EIO;
+               if (i)
+                       status |= TP_ACPI_VIDEO_S_CRT;
+               break;
+       case TPACPI_VIDEO_NEW:
+               if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
+                   !acpi_evalf(NULL, &i, "\\VCDC", "d"))
+                       return -EIO;
+               if (i)
+                       status |= TP_ACPI_VIDEO_S_CRT;
+
+               if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
+                   !acpi_evalf(NULL, &i, "\\VCDL", "d"))
+                       return -EIO;
+               if (i)
+                       status |= TP_ACPI_VIDEO_S_LCD;
+               if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
+                       return -EIO;
+               if (i)
+                       status |= TP_ACPI_VIDEO_S_DVI;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return status;
+}
+
+static int video_outputsw_set(int status)
+{
+       int autosw;
+       int res = 0;
+
+       switch (video_supported) {
+       case TPACPI_VIDEO_570:
+               res = acpi_evalf(NULL, NULL,
+                                "\\_SB.PHS2", "vdd",
+                                TP_ACPI_VIDEO_570_PHS2CMD,
+                                status | TP_ACPI_VIDEO_570_PHS2SET);
+               break;
+       case TPACPI_VIDEO_770:
+               autosw = video_autosw_get();
+               if (autosw < 0)
+                       return autosw;
+
+               res = video_autosw_set(1);
+               if (res)
+                       return res;
+               res = acpi_evalf(vid_handle, NULL,
+                                "ASWT", "vdd", status * 0x100, 0);
+               if (!autosw && video_autosw_set(autosw)) {
+                       printk(TPACPI_ERR
+                              "video auto-switch left enabled due to error\n");
+                       return -EIO;
+               }
+               break;
+       case TPACPI_VIDEO_NEW:
+               res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
+                     acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return (res)? 0 : -EIO;
+}
+
+static int video_autosw_get(void)
+{
+       int autosw = 0;
+
+       switch (video_supported) {
+       case TPACPI_VIDEO_570:
+               if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
+                       return -EIO;
+               break;
+       case TPACPI_VIDEO_770:
+       case TPACPI_VIDEO_NEW:
+               if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
+                       return -EIO;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return autosw & 1;
+}
+
+static int video_autosw_set(int enable)
+{
+       if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
+               return -EIO;
+       return 0;
+}
+
+static int video_outputsw_cycle(void)
+{
+       int autosw = video_autosw_get();
+       int res;
+
+       if (autosw < 0)
+               return autosw;
+
+       switch (video_supported) {
+       case TPACPI_VIDEO_570:
+               res = video_autosw_set(1);
+               if (res)
+                       return res;
+               res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
+               break;
+       case TPACPI_VIDEO_770:
+       case TPACPI_VIDEO_NEW:
+               res = video_autosw_set(1);
+               if (res)
+                       return res;
+               res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
+               break;
+       default:
+               return -ENOSYS;
+       }
+       if (!autosw && video_autosw_set(autosw)) {
+               printk(TPACPI_ERR
+                      "video auto-switch left enabled due to error\n");
+               return -EIO;
+       }
+
+       return (res)? 0 : -EIO;
+}
+
+static int video_expand_toggle(void)
+{
+       switch (video_supported) {
+       case TPACPI_VIDEO_570:
+               return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
+                       0 : -EIO;
+       case TPACPI_VIDEO_770:
+               return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
+                       0 : -EIO;
+       case TPACPI_VIDEO_NEW:
+               return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
+                       0 : -EIO;
+       default:
+               return -ENOSYS;
+       }
+       /* not reached */
+}
+
+static int video_read(char *p)
+{
+       int status, autosw;
+       int len = 0;
+
+       if (video_supported == TPACPI_VIDEO_NONE) {
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+               return len;
+       }
+
+       status = video_outputsw_get();
+       if (status < 0)
+               return status;
+
+       autosw = video_autosw_get();
+       if (autosw < 0)
+               return autosw;
+
+       len += sprintf(p + len, "status:\t\tsupported\n");
+       len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
+       len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
+       if (video_supported == TPACPI_VIDEO_NEW)
+               len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
+       len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
+       len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
+       len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
+       if (video_supported == TPACPI_VIDEO_NEW)
+               len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
+       len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
+       len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
+
+       return len;
+}
+
+static int video_write(char *buf)
+{
+       char *cmd;
+       int enable, disable, status;
+       int res;
+
+       if (video_supported == TPACPI_VIDEO_NONE)
+               return -ENODEV;
+
+       enable = 0;
+       disable = 0;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "lcd_enable") == 0) {
+                       enable |= TP_ACPI_VIDEO_S_LCD;
+               } else if (strlencmp(cmd, "lcd_disable") == 0) {
+                       disable |= TP_ACPI_VIDEO_S_LCD;
+               } else if (strlencmp(cmd, "crt_enable") == 0) {
+                       enable |= TP_ACPI_VIDEO_S_CRT;
+               } else if (strlencmp(cmd, "crt_disable") == 0) {
+                       disable |= TP_ACPI_VIDEO_S_CRT;
+               } else if (video_supported == TPACPI_VIDEO_NEW &&
+                          strlencmp(cmd, "dvi_enable") == 0) {
+                       enable |= TP_ACPI_VIDEO_S_DVI;
+               } else if (video_supported == TPACPI_VIDEO_NEW &&
+                          strlencmp(cmd, "dvi_disable") == 0) {
+                       disable |= TP_ACPI_VIDEO_S_DVI;
+               } else if (strlencmp(cmd, "auto_enable") == 0) {
+                       res = video_autosw_set(1);
+                       if (res)
+                               return res;
+               } else if (strlencmp(cmd, "auto_disable") == 0) {
+                       res = video_autosw_set(0);
+                       if (res)
+                               return res;
+               } else if (strlencmp(cmd, "video_switch") == 0) {
+                       res = video_outputsw_cycle();
+                       if (res)
+                               return res;
+               } else if (strlencmp(cmd, "expand_toggle") == 0) {
+                       res = video_expand_toggle();
+                       if (res)
+                               return res;
+               } else
+                       return -EINVAL;
+       }
+
+       if (enable || disable) {
+               status = video_outputsw_get();
+               if (status < 0)
+                       return status;
+               res = video_outputsw_set((status & ~disable) | enable);
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct video_driver_data = {
+       .name = "video",
+       .read = video_read,
+       .write = video_write,
+       .exit = video_exit,
+};
+
+#endif /* CONFIG_THINKPAD_ACPI_VIDEO */
+
+/*************************************************************************
+ * Light (thinklight) subdriver
+ */
+
+TPACPI_HANDLE(lght, root, "\\LGHT");   /* A21e, A2xm/p, T20-22, X20-21 */
+TPACPI_HANDLE(ledb, ec, "LEDB");               /* G4x */
+
+static int light_get_status(void)
+{
+       int status = 0;
+
+       if (tp_features.light_status) {
+               if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
+                       return -EIO;
+               return (!!status);
+       }
+
+       return -ENXIO;
+}
+
+static int light_set_status(int status)
+{
+       int rc;
+
+       if (tp_features.light) {
+               if (cmos_handle) {
+                       rc = acpi_evalf(cmos_handle, NULL, NULL, "vd",
+                                       (status)?
+                                               TP_CMOS_THINKLIGHT_ON :
+                                               TP_CMOS_THINKLIGHT_OFF);
+               } else {
+                       rc = acpi_evalf(lght_handle, NULL, NULL, "vd",
+                                       (status)? 1 : 0);
+               }
+               return (rc)? 0 : -EIO;
+       }
+
+       return -ENXIO;
+}
+
+static void light_set_status_worker(struct work_struct *work)
+{
+       struct tpacpi_led_classdev *data =
+                       container_of(work, struct tpacpi_led_classdev, work);
+
+       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+               light_set_status((data->new_brightness != LED_OFF));
+}
+
+static void light_sysfs_set(struct led_classdev *led_cdev,
+                       enum led_brightness brightness)
+{
+       struct tpacpi_led_classdev *data =
+               container_of(led_cdev,
+                            struct tpacpi_led_classdev,
+                            led_classdev);
+       data->new_brightness = brightness;
+       queue_work(tpacpi_wq, &data->work);
+}
+
+static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
+{
+       return (light_get_status() == 1)? LED_FULL : LED_OFF;
+}
+
+static struct tpacpi_led_classdev tpacpi_led_thinklight = {
+       .led_classdev = {
+               .name           = "tpacpi::thinklight",
+               .brightness_set = &light_sysfs_set,
+               .brightness_get = &light_sysfs_get,
+       }
+};
+
+static int __init light_init(struct ibm_init_struct *iibm)
+{
+       int rc;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(ledb);
+       TPACPI_ACPIHANDLE_INIT(lght);
+       TPACPI_ACPIHANDLE_INIT(cmos);
+       INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker);
+
+       /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
+       tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
+
+       if (tp_features.light)
+               /* light status not supported on
+                  570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
+               tp_features.light_status =
+                       acpi_evalf(ec_handle, NULL, "KBLT", "qv");
+
+       vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
+               str_supported(tp_features.light),
+               str_supported(tp_features.light_status));
+
+       if (!tp_features.light)
+               return 1;
+
+       rc = led_classdev_register(&tpacpi_pdev->dev,
+                                  &tpacpi_led_thinklight.led_classdev);
+
+       if (rc < 0) {
+               tp_features.light = 0;
+               tp_features.light_status = 0;
+       } else  {
+               rc = 0;
+       }
+
+       return rc;
+}
+
+static void light_exit(void)
+{
+       led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
+       if (work_pending(&tpacpi_led_thinklight.work))
+               flush_workqueue(tpacpi_wq);
+}
+
+static int light_read(char *p)
+{
+       int len = 0;
+       int status;
+
+       if (!tp_features.light) {
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       } else if (!tp_features.light_status) {
+               len += sprintf(p + len, "status:\t\tunknown\n");
+               len += sprintf(p + len, "commands:\ton, off\n");
+       } else {
+               status = light_get_status();
+               if (status < 0)
+                       return status;
+               len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
+               len += sprintf(p + len, "commands:\ton, off\n");
+       }
+
+       return len;
+}
+
+static int light_write(char *buf)
+{
+       char *cmd;
+       int newstatus = 0;
+
+       if (!tp_features.light)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "on") == 0) {
+                       newstatus = 1;
+               } else if (strlencmp(cmd, "off") == 0) {
+                       newstatus = 0;
+               } else
+                       return -EINVAL;
+       }
+
+       return light_set_status(newstatus);
+}
+
+static struct ibm_struct light_driver_data = {
+       .name = "light",
+       .read = light_read,
+       .write = light_write,
+       .exit = light_exit,
+};
+
+/*************************************************************************
+ * Dock subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+
+static void dock_notify(struct ibm_struct *ibm, u32 event);
+static int dock_read(char *p);
+static int dock_write(char *buf);
+
+TPACPI_HANDLE(dock, root, "\\_SB.GDCK",        /* X30, X31, X40 */
+          "\\_SB.PCI0.DOCK",   /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
+          "\\_SB.PCI0.PCI1.DOCK",      /* all others */
+          "\\_SB.PCI.ISA.SLCE",        /* 570 */
+    );                         /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
+
+/* don't list other alternatives as we install a notify handler on the 570 */
+TPACPI_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
+
+static const struct acpi_device_id ibm_pci_device_ids[] = {
+       {PCI_ROOT_HID_STRING, 0},
+       {"", 0},
+};
+
+static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
+       {
+        .notify = dock_notify,
+        .handle = &dock_handle,
+        .type = ACPI_SYSTEM_NOTIFY,
+       },
+       {
+       /* THIS ONE MUST NEVER BE USED FOR DRIVER AUTOLOADING.
+        * We just use it to get notifications of dock hotplug
+        * in very old thinkpads */
+        .hid = ibm_pci_device_ids,
+        .notify = dock_notify,
+        .handle = &pci_handle,
+        .type = ACPI_SYSTEM_NOTIFY,
+       },
+};
+
+static struct ibm_struct dock_driver_data[2] = {
+       {
+        .name = "dock",
+        .read = dock_read,
+        .write = dock_write,
+        .acpi = &ibm_dock_acpidriver[0],
+       },
+       {
+        .name = "dock",
+        .acpi = &ibm_dock_acpidriver[1],
+       },
+};
+
+#define dock_docked() (_sta(dock_handle) & 1)
+
+static int __init dock_init(struct ibm_init_struct *iibm)
+{
+       vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(dock);
+
+       vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
+               str_supported(dock_handle != NULL));
+
+       return (dock_handle)? 0 : 1;
+}
+
+static int __init dock_init2(struct ibm_init_struct *iibm)
+{
+       int dock2_needed;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n");
+
+       if (dock_driver_data[0].flags.acpi_driver_registered &&
+           dock_driver_data[0].flags.acpi_notify_installed) {
+               TPACPI_ACPIHANDLE_INIT(pci);
+               dock2_needed = (pci_handle != NULL);
+               vdbg_printk(TPACPI_DBG_INIT,
+                           "dock PCI handler for the TP 570 is %s\n",
+                           str_supported(dock2_needed));
+       } else {
+               vdbg_printk(TPACPI_DBG_INIT,
+               "dock subdriver part 2 not required\n");
+               dock2_needed = 0;
+       }
+
+       return (dock2_needed)? 0 : 1;
+}
+
+static void dock_notify(struct ibm_struct *ibm, u32 event)
+{
+       int docked = dock_docked();
+       int pci = ibm->acpi->hid && ibm->acpi->device &&
+               acpi_match_device_ids(ibm->acpi->device, ibm_pci_device_ids);
+       int data;
+
+       if (event == 1 && !pci) /* 570 */
+               data = 1;       /* button */
+       else if (event == 1 && pci)     /* 570 */
+               data = 3;       /* dock */
+       else if (event == 3 && docked)
+               data = 1;       /* button */
+       else if (event == 3 && !docked)
+               data = 2;       /* undock */
+       else if (event == 0 && docked)
+               data = 3;       /* dock */
+       else {
+               printk(TPACPI_ERR "unknown dock event %d, status %d\n",
+                      event, _sta(dock_handle));
+               data = 0;       /* unknown */
+       }
+       acpi_bus_generate_proc_event(ibm->acpi->device, event, data);
+       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+                                         ibm->acpi->device->dev.bus_id,
+                                         event, data);
+}
+
+static int dock_read(char *p)
+{
+       int len = 0;
+       int docked = dock_docked();
+
+       if (!dock_handle)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else if (!docked)
+               len += sprintf(p + len, "status:\t\tundocked\n");
+       else {
+               len += sprintf(p + len, "status:\t\tdocked\n");
+               len += sprintf(p + len, "commands:\tdock, undock\n");
+       }
+
+       return len;
+}
+
+static int dock_write(char *buf)
+{
+       char *cmd;
+
+       if (!dock_docked())
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "undock") == 0) {
+                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
+                           !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
+                               return -EIO;
+               } else if (strlencmp(cmd, "dock") == 0) {
+                       if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+#endif /* CONFIG_THINKPAD_ACPI_DOCK */
+
+/*************************************************************************
+ * Bay subdriver
+ */
+
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+
+TPACPI_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST",    /* 570 */
+          "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
+          "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
+          "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
+          );                           /* A21e, R30, R31 */
+TPACPI_HANDLE(bay_ej, bay, "_EJ3",     /* 600e/x, A2xm/p, A3x */
+          "_EJ0",              /* all others */
+          );                   /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
+TPACPI_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
+          "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
+          );                           /* all others */
+TPACPI_HANDLE(bay2_ej, bay2, "_EJ3",   /* 600e/x, 770e, A3x */
+          "_EJ0",                      /* 770x */
+          );                           /* all others */
+
+static int __init bay_init(struct ibm_init_struct *iibm)
+{
+       vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(bay);
+       if (bay_handle)
+               TPACPI_ACPIHANDLE_INIT(bay_ej);
+       TPACPI_ACPIHANDLE_INIT(bay2);
+       if (bay2_handle)
+               TPACPI_ACPIHANDLE_INIT(bay2_ej);
+
+       tp_features.bay_status = bay_handle &&
+               acpi_evalf(bay_handle, NULL, "_STA", "qv");
+       tp_features.bay_status2 = bay2_handle &&
+               acpi_evalf(bay2_handle, NULL, "_STA", "qv");
+
+       tp_features.bay_eject = bay_handle && bay_ej_handle &&
+               (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
+       tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
+               (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
+
+       vdbg_printk(TPACPI_DBG_INIT,
+               "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
+               str_supported(tp_features.bay_status),
+               str_supported(tp_features.bay_eject),
+               str_supported(tp_features.bay_status2),
+               str_supported(tp_features.bay_eject2));
+
+       return (tp_features.bay_status || tp_features.bay_eject ||
+               tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
+}
+
+static void bay_notify(struct ibm_struct *ibm, u32 event)
+{
+       acpi_bus_generate_proc_event(ibm->acpi->device, event, 0);
+       acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,
+                                         ibm->acpi->device->dev.bus_id,
+                                         event, 0);
+}
+
+#define bay_occupied(b) (_sta(b##_handle) & 1)
+
+static int bay_read(char *p)
+{
+       int len = 0;
+       int occupied = bay_occupied(bay);
+       int occupied2 = bay_occupied(bay2);
+       int eject, eject2;
+
+       len += sprintf(p + len, "status:\t\t%s\n",
+               tp_features.bay_status ?
+                       (occupied ? "occupied" : "unoccupied") :
+                               "not supported");
+       if (tp_features.bay_status2)
+               len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
+                              "occupied" : "unoccupied");
+
+       eject = tp_features.bay_eject && occupied;
+       eject2 = tp_features.bay_eject2 && occupied2;
+
+       if (eject && eject2)
+               len += sprintf(p + len, "commands:\teject, eject2\n");
+       else if (eject)
+               len += sprintf(p + len, "commands:\teject\n");
+       else if (eject2)
+               len += sprintf(p + len, "commands:\teject2\n");
+
+       return len;
+}
+
+static int bay_write(char *buf)
+{
+       char *cmd;
+
+       if (!tp_features.bay_eject && !tp_features.bay_eject2)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
+                       if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
+                               return -EIO;
+               } else if (tp_features.bay_eject2 &&
+                          strlencmp(cmd, "eject2") == 0) {
+                       if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
+       .notify = bay_notify,
+       .handle = &bay_handle,
+       .type = ACPI_SYSTEM_NOTIFY,
+};
+
+static struct ibm_struct bay_driver_data = {
+       .name = "bay",
+       .read = bay_read,
+       .write = bay_write,
+       .acpi = &ibm_bay_acpidriver,
+};
+
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+
+/*************************************************************************
+ * CMOS subdriver
+ */
+
+/* sysfs cmos_command -------------------------------------------------- */
+static ssize_t cmos_command_store(struct device *dev,
+                           struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       unsigned long cmos_cmd;
+       int res;
+
+       if (parse_strtoul(buf, 21, &cmos_cmd))
+               return -EINVAL;
+
+       res = issue_thinkpad_cmos_command(cmos_cmd);
+       return (res)? res : count;
+}
+
+static struct device_attribute dev_attr_cmos_command =
+       __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
+
+/* --------------------------------------------------------------------- */
+
+static int __init cmos_init(struct ibm_init_struct *iibm)
+{
+       int res;
+
+       vdbg_printk(TPACPI_DBG_INIT,
+               "initializing cmos commands subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(cmos);
+
+       vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
+               str_supported(cmos_handle != NULL));
+
+       res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+       if (res)
+               return res;
+
+       return (cmos_handle)? 0 : 1;
+}
+
+static void cmos_exit(void)
+{
+       device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
+}
+
+static int cmos_read(char *p)
+{
+       int len = 0;
+
+       /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+          R30, R31, T20-22, X20-21 */
+       if (!cmos_handle)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else {
+               len += sprintf(p + len, "status:\t\tsupported\n");
+               len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
+       }
+
+       return len;
+}
+
+static int cmos_write(char *buf)
+{
+       char *cmd;
+       int cmos_cmd, res;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
+                   cmos_cmd >= 0 && cmos_cmd <= 21) {
+                       /* cmos_cmd set */
+               } else
+                       return -EINVAL;
+
+               res = issue_thinkpad_cmos_command(cmos_cmd);
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct cmos_driver_data = {
+       .name = "cmos",
+       .read = cmos_read,
+       .write = cmos_write,
+       .exit = cmos_exit,
+};
+
+/*************************************************************************
+ * LED subdriver
+ */
+
+enum led_access_mode {
+       TPACPI_LED_NONE = 0,
+       TPACPI_LED_570, /* 570 */
+       TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+       TPACPI_LED_NEW, /* all others */
+};
+
+enum { /* For TPACPI_LED_OLD */
+       TPACPI_LED_EC_HLCL = 0x0c,      /* EC reg to get led to power on */
+       TPACPI_LED_EC_HLBL = 0x0d,      /* EC reg to blink a lit led */
+       TPACPI_LED_EC_HLMS = 0x0e,      /* EC reg to select led to command */
+};
+
+enum led_status_t {
+       TPACPI_LED_OFF = 0,
+       TPACPI_LED_ON,
+       TPACPI_LED_BLINK,
+};
+
+static enum led_access_mode led_supported;
+
+TPACPI_HANDLE(led, ec, "SLED", /* 570 */
+          "SYSL",              /* 600e/x, 770e, 770x, A21e, A2xm/p, */
+                               /* T20-22, X20-21 */
+          "LED",               /* all others */
+          );                   /* R30, R31 */
+
+#define TPACPI_LED_NUMLEDS 8
+static struct tpacpi_led_classdev *tpacpi_leds;
+static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
+static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
+       /* there's a limit of 19 chars + NULL before 2.6.26 */
+       "tpacpi::power",
+       "tpacpi:orange:batt",
+       "tpacpi:green:batt",
+       "tpacpi::dock_active",
+       "tpacpi::bay_active",
+       "tpacpi::dock_batt",
+       "tpacpi::unknown_led",
+       "tpacpi::standby",
+};
+
+static int led_get_status(const unsigned int led)
+{
+       int status;
+       enum led_status_t led_s;
+
+       switch (led_supported) {
+       case TPACPI_LED_570:
+               if (!acpi_evalf(ec_handle,
+                               &status, "GLED", "dd", 1 << led))
+                       return -EIO;
+               led_s = (status == 0)?
+                               TPACPI_LED_OFF :
+                               ((status == 1)?
+                                       TPACPI_LED_ON :
+                                       TPACPI_LED_BLINK);
+               tpacpi_led_state_cache[led] = led_s;
+               return led_s;
+       default:
+               return -ENXIO;
+       }
+
+       /* not reached */
+}
+
+static int led_set_status(const unsigned int led,
+                         const enum led_status_t ledstatus)
+{
+       /* off, on, blink. Index is led_status_t */
+       static const unsigned int led_sled_arg1[] = { 0, 1, 3 };
+       static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 };
+
+       int rc = 0;
+
+       switch (led_supported) {
+       case TPACPI_LED_570:
+               /* 570 */
+               if (led > 7)
+                       return -EINVAL;
+               if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+                               (1 << led), led_sled_arg1[ledstatus]))
+                       rc = -EIO;
+               break;
+       case TPACPI_LED_OLD:
+               /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
+               if (led > 7)
+                       return -EINVAL;
+               rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
+               if (rc >= 0)
+                       rc = ec_write(TPACPI_LED_EC_HLBL,
+                                     (ledstatus == TPACPI_LED_BLINK) << led);
+               if (rc >= 0)
+                       rc = ec_write(TPACPI_LED_EC_HLCL,
+                                     (ledstatus != TPACPI_LED_OFF) << led);
+               break;
+       case TPACPI_LED_NEW:
+               /* all others */
+               if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+                               led, led_led_arg1[ledstatus]))
+                       rc = -EIO;
+               break;
+       default:
+               rc = -ENXIO;
+       }
+
+       if (!rc)
+               tpacpi_led_state_cache[led] = ledstatus;
+
+       return rc;
+}
+
+static void led_sysfs_set_status(unsigned int led,
+                                enum led_brightness brightness)
+{
+       led_set_status(led,
+                       (brightness == LED_OFF) ?
+                       TPACPI_LED_OFF :
+                       (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
+                               TPACPI_LED_BLINK : TPACPI_LED_ON);
+}
+
+static void led_set_status_worker(struct work_struct *work)
+{
+       struct tpacpi_led_classdev *data =
+               container_of(work, struct tpacpi_led_classdev, work);
+
+       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+               led_sysfs_set_status(data->led, data->new_brightness);
+}
+
+static void led_sysfs_set(struct led_classdev *led_cdev,
+                       enum led_brightness brightness)
+{
+       struct tpacpi_led_classdev *data = container_of(led_cdev,
+                            struct tpacpi_led_classdev, led_classdev);
+
+       data->new_brightness = brightness;
+       queue_work(tpacpi_wq, &data->work);
+}
+
+static int led_sysfs_blink_set(struct led_classdev *led_cdev,
+                       unsigned long *delay_on, unsigned long *delay_off)
+{
+       struct tpacpi_led_classdev *data = container_of(led_cdev,
+                            struct tpacpi_led_classdev, led_classdev);
+
+       /* Can we choose the flash rate? */
+       if (*delay_on == 0 && *delay_off == 0) {
+               /* yes. set them to the hardware blink rate (1 Hz) */
+               *delay_on = 500; /* ms */
+               *delay_off = 500; /* ms */
+       } else if ((*delay_on != 500) || (*delay_off != 500))
+               return -EINVAL;
+
+       data->new_brightness = TPACPI_LED_BLINK;
+       queue_work(tpacpi_wq, &data->work);
+
+       return 0;
+}
+
+static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev)
+{
+       int rc;
+
+       struct tpacpi_led_classdev *data = container_of(led_cdev,
+                            struct tpacpi_led_classdev, led_classdev);
+
+       rc = led_get_status(data->led);
+
+       if (rc == TPACPI_LED_OFF || rc < 0)
+               rc = LED_OFF;   /* no error handling in led class :( */
+       else
+               rc = LED_FULL;
+
+       return rc;
+}
+
+static void led_exit(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+               if (tpacpi_leds[i].led_classdev.name)
+                       led_classdev_unregister(&tpacpi_leds[i].led_classdev);
+       }
+
+       kfree(tpacpi_leds);
+}
+
+static int __init led_init(struct ibm_init_struct *iibm)
+{
+       unsigned int i;
+       int rc;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(led);
+
+       if (!led_handle)
+               /* led not supported on R30, R31 */
+               led_supported = TPACPI_LED_NONE;
+       else if (strlencmp(led_path, "SLED") == 0)
+               /* 570 */
+               led_supported = TPACPI_LED_570;
+       else if (strlencmp(led_path, "SYSL") == 0)
+               /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
+               led_supported = TPACPI_LED_OLD;
+       else
+               /* all others */
+               led_supported = TPACPI_LED_NEW;
+
+       vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
+               str_supported(led_supported), led_supported);
+
+       tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
+                             GFP_KERNEL);
+       if (!tpacpi_leds) {
+               printk(TPACPI_ERR "Out of memory for LED data\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+               tpacpi_leds[i].led = i;
+
+               tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set;
+               tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set;
+               if (led_supported == TPACPI_LED_570)
+                       tpacpi_leds[i].led_classdev.brightness_get =
+                                                       &led_sysfs_get;
+
+               tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
+
+               INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
+
+               rc = led_classdev_register(&tpacpi_pdev->dev,
+                                          &tpacpi_leds[i].led_classdev);
+               if (rc < 0) {
+                       tpacpi_leds[i].led_classdev.name = NULL;
+                       led_exit();
+                       return rc;
+               }
+       }
+
+       return (led_supported != TPACPI_LED_NONE)? 0 : 1;
+}
+
+#define str_led_status(s) \
+       ((s) == TPACPI_LED_OFF ? "off" : \
+               ((s) == TPACPI_LED_ON ? "on" : "blinking"))
+
+static int led_read(char *p)
+{
+       int len = 0;
+
+       if (!led_supported) {
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+               return len;
+       }
+       len += sprintf(p + len, "status:\t\tsupported\n");
+
+       if (led_supported == TPACPI_LED_570) {
+               /* 570 */
+               int i, status;
+               for (i = 0; i < 8; i++) {
+                       status = led_get_status(i);
+                       if (status < 0)
+                               return -EIO;
+                       len += sprintf(p + len, "%d:\t\t%s\n",
+                                      i, str_led_status(status));
+               }
+       }
+
+       len += sprintf(p + len, "commands:\t"
+                      "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
+
+       return len;
+}
+
+static int led_write(char *buf)
+{
+       char *cmd;
+       int led, rc;
+       enum led_status_t s;
+
+       if (!led_supported)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
+                       return -EINVAL;
+
+               if (strstr(cmd, "off")) {
+                       s = TPACPI_LED_OFF;
+               } else if (strstr(cmd, "on")) {
+                       s = TPACPI_LED_ON;
+               } else if (strstr(cmd, "blink")) {
+                       s = TPACPI_LED_BLINK;
+               } else {
+                       return -EINVAL;
+               }
+
+               rc = led_set_status(led, s);
+               if (rc < 0)
+                       return rc;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct led_driver_data = {
+       .name = "led",
+       .read = led_read,
+       .write = led_write,
+       .exit = led_exit,
+};
+
+/*************************************************************************
+ * Beep subdriver
+ */
+
+TPACPI_HANDLE(beep, ec, "BEEP");       /* all except R30, R31 */
+
+static int __init beep_init(struct ibm_init_struct *iibm)
+{
+       vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
+
+       TPACPI_ACPIHANDLE_INIT(beep);
+
+       vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
+               str_supported(beep_handle != NULL));
+
+       return (beep_handle)? 0 : 1;
+}
+
+static int beep_read(char *p)
+{
+       int len = 0;
+
+       if (!beep_handle)
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       else {
+               len += sprintf(p + len, "status:\t\tsupported\n");
+               len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
+       }
+
+       return len;
+}
+
+static int beep_write(char *buf)
+{
+       char *cmd;
+       int beep_cmd;
+
+       if (!beep_handle)
+               return -ENODEV;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
+                   beep_cmd >= 0 && beep_cmd <= 17) {
+                       /* beep_cmd set */
+               } else
+                       return -EINVAL;
+               if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct beep_driver_data = {
+       .name = "beep",
+       .read = beep_read,
+       .write = beep_write,
+};
+
+/*************************************************************************
+ * Thermal subdriver
+ */
+
+enum thermal_access_mode {
+       TPACPI_THERMAL_NONE = 0,        /* No thermal support */
+       TPACPI_THERMAL_ACPI_TMP07,      /* Use ACPI TMP0-7 */
+       TPACPI_THERMAL_ACPI_UPDT,       /* Use ACPI TMP0-7 with UPDT */
+       TPACPI_THERMAL_TPEC_8,          /* Use ACPI EC regs, 8 sensors */
+       TPACPI_THERMAL_TPEC_16,         /* Use ACPI EC regs, 16 sensors */
+};
+
+enum { /* TPACPI_THERMAL_TPEC_* */
+       TP_EC_THERMAL_TMP0 = 0x78,      /* ACPI EC regs TMP 0..7 */
+       TP_EC_THERMAL_TMP8 = 0xC0,      /* ACPI EC regs TMP 8..15 */
+       TP_EC_THERMAL_TMP_NA = -128,    /* ACPI EC sensor not available */
+};
+
+#define TPACPI_MAX_THERMAL_SENSORS 16  /* Max thermal sensors supported */
+struct ibm_thermal_sensors_struct {
+       s32 temp[TPACPI_MAX_THERMAL_SENSORS];
+};
+
+static enum thermal_access_mode thermal_read_mode;
+
+/* idx is zero-based */
+static int thermal_get_sensor(int idx, s32 *value)
+{
+       int t;
+       s8 tmp;
+       char tmpi[5];
+
+       t = TP_EC_THERMAL_TMP0;
+
+       switch (thermal_read_mode) {
+#if TPACPI_MAX_THERMAL_SENSORS >= 16
+       case TPACPI_THERMAL_TPEC_16:
+               if (idx >= 8 && idx <= 15) {
+                       t = TP_EC_THERMAL_TMP8;
+                       idx -= 8;
+               }
+               /* fallthrough */
+#endif
+       case TPACPI_THERMAL_TPEC_8:
+               if (idx <= 7) {
+                       if (!acpi_ec_read(t + idx, &tmp))
+                               return -EIO;
+                       *value = tmp * 1000;
+                       return 0;
+               }
+               break;
+
+       case TPACPI_THERMAL_ACPI_UPDT:
+               if (idx <= 7) {
+                       snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+                       if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
+                               return -EIO;
+                       if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+                               return -EIO;
+                       *value = (t - 2732) * 100;
+                       return 0;
+               }
+               break;
+
+       case TPACPI_THERMAL_ACPI_TMP07:
+               if (idx <= 7) {
+                       snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
+                       if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
+                               return -EIO;
+                       if (t > 127 || t < -127)
+                               t = TP_EC_THERMAL_TMP_NA;
+                       *value = t * 1000;
+                       return 0;
+               }
+               break;
+
+       case TPACPI_THERMAL_NONE:
+       default:
+               return -ENOSYS;
+       }
+
+       return -EINVAL;
+}
+
+static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
+{
+       int res, i;
+       int n;
+
+       n = 8;
+       i = 0;
+
+       if (!s)
+               return -EINVAL;
+
+       if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
+               n = 16;
+
+       for (i = 0 ; i < n; i++) {
+               res = thermal_get_sensor(i, &s->temp[i]);
+               if (res)
+                       return res;
+       }
+
+       return n;
+}
+
+/* sysfs temp##_input -------------------------------------------------- */
+
+static ssize_t thermal_temp_input_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       struct sensor_device_attribute *sensor_attr =
+                                       to_sensor_dev_attr(attr);
+       int idx = sensor_attr->index;
+       s32 value;
+       int res;
+
+       res = thermal_get_sensor(idx, &value);
+       if (res)
+               return res;
+       if (value == TP_EC_THERMAL_TMP_NA * 1000)
+               return -ENXIO;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+}
+
+#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
+        SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, \
+                    thermal_temp_input_show, NULL, _idxB)
+
+static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
+       THERMAL_SENSOR_ATTR_TEMP(1, 0),
+       THERMAL_SENSOR_ATTR_TEMP(2, 1),
+       THERMAL_SENSOR_ATTR_TEMP(3, 2),
+       THERMAL_SENSOR_ATTR_TEMP(4, 3),
+       THERMAL_SENSOR_ATTR_TEMP(5, 4),
+       THERMAL_SENSOR_ATTR_TEMP(6, 5),
+       THERMAL_SENSOR_ATTR_TEMP(7, 6),
+       THERMAL_SENSOR_ATTR_TEMP(8, 7),
+       THERMAL_SENSOR_ATTR_TEMP(9, 8),
+       THERMAL_SENSOR_ATTR_TEMP(10, 9),
+       THERMAL_SENSOR_ATTR_TEMP(11, 10),
+       THERMAL_SENSOR_ATTR_TEMP(12, 11),
+       THERMAL_SENSOR_ATTR_TEMP(13, 12),
+       THERMAL_SENSOR_ATTR_TEMP(14, 13),
+       THERMAL_SENSOR_ATTR_TEMP(15, 14),
+       THERMAL_SENSOR_ATTR_TEMP(16, 15),
+};
+
+#define THERMAL_ATTRS(X) \
+       &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
+
+static struct attribute *thermal_temp_input_attr[] = {
+       THERMAL_ATTRS(8),
+       THERMAL_ATTRS(9),
+       THERMAL_ATTRS(10),
+       THERMAL_ATTRS(11),
+       THERMAL_ATTRS(12),
+       THERMAL_ATTRS(13),
+       THERMAL_ATTRS(14),
+       THERMAL_ATTRS(15),
+       THERMAL_ATTRS(0),
+       THERMAL_ATTRS(1),
+       THERMAL_ATTRS(2),
+       THERMAL_ATTRS(3),
+       THERMAL_ATTRS(4),
+       THERMAL_ATTRS(5),
+       THERMAL_ATTRS(6),
+       THERMAL_ATTRS(7),
+       NULL
+};
+
+static const struct attribute_group thermal_temp_input16_group = {
+       .attrs = thermal_temp_input_attr
+};
+
+static const struct attribute_group thermal_temp_input8_group = {
+       .attrs = &thermal_temp_input_attr[8]
+};
+
+#undef THERMAL_SENSOR_ATTR_TEMP
+#undef THERMAL_ATTRS
+
+/* --------------------------------------------------------------------- */
+
+static int __init thermal_init(struct ibm_init_struct *iibm)
+{
+       u8 t, ta1, ta2;
+       int i;
+       int acpi_tmp7;
+       int res;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
+
+       acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
+
+       if (thinkpad_id.ec_model) {
+               /*
+                * Direct EC access mode: sensors at registers
+                * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
+                * non-implemented, thermal sensors return 0x80 when
+                * not available
+                */
+
+               ta1 = ta2 = 0;
+               for (i = 0; i < 8; i++) {
+                       if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
+                               ta1 |= t;
+                       } else {
+                               ta1 = 0;
+                               break;
+                       }
+                       if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
+                               ta2 |= t;
+                       } else {
+                               ta1 = 0;
+                               break;
+                       }
+               }
+               if (ta1 == 0) {
+                       /* This is sheer paranoia, but we handle it anyway */
+                       if (acpi_tmp7) {
+                               printk(TPACPI_ERR
+                                      "ThinkPad ACPI EC access misbehaving, "
+                                      "falling back to ACPI TMPx access "
+                                      "mode\n");
+                               thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+                       } else {
+                               printk(TPACPI_ERR
+                                      "ThinkPad ACPI EC access misbehaving, "
+                                      "disabling thermal sensors access\n");
+                               thermal_read_mode = TPACPI_THERMAL_NONE;
+                       }
+               } else {
+                       thermal_read_mode =
+                           (ta2 != 0) ?
+                           TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
+               }
+       } else if (acpi_tmp7) {
+               if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
+                       /* 600e/x, 770e, 770x */
+                       thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
+               } else {
+                       /* Standard ACPI TMPx access, max 8 sensors */
+                       thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
+               }
+       } else {
+               /* temperatures not supported on 570, G4x, R30, R31, R32 */
+               thermal_read_mode = TPACPI_THERMAL_NONE;
+       }
+
+       vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
+               str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
+               thermal_read_mode);
+
+       switch (thermal_read_mode) {
+       case TPACPI_THERMAL_TPEC_16:
+               res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+                               &thermal_temp_input16_group);
+               if (res)
+                       return res;
+               break;
+       case TPACPI_THERMAL_TPEC_8:
+       case TPACPI_THERMAL_ACPI_TMP07:
+       case TPACPI_THERMAL_ACPI_UPDT:
+               res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+                               &thermal_temp_input8_group);
+               if (res)
+                       return res;
+               break;
+       case TPACPI_THERMAL_NONE:
+       default:
+               return 1;
+       }
+
+       return 0;
+}
+
+static void thermal_exit(void)
+{
+       switch (thermal_read_mode) {
+       case TPACPI_THERMAL_TPEC_16:
+               sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+                                  &thermal_temp_input16_group);
+               break;
+       case TPACPI_THERMAL_TPEC_8:
+       case TPACPI_THERMAL_ACPI_TMP07:
+       case TPACPI_THERMAL_ACPI_UPDT:
+               sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+                                  &thermal_temp_input16_group);
+               break;
+       case TPACPI_THERMAL_NONE:
+       default:
+               break;
+       }
+}
+
+static int thermal_read(char *p)
+{
+       int len = 0;
+       int n, i;
+       struct ibm_thermal_sensors_struct t;
+
+       n = thermal_get_sensors(&t);
+       if (unlikely(n < 0))
+               return n;
+
+       len += sprintf(p + len, "temperatures:\t");
+
+       if (n > 0) {
+               for (i = 0; i < (n - 1); i++)
+                       len += sprintf(p + len, "%d ", t.temp[i] / 1000);
+               len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
+       } else
+               len += sprintf(p + len, "not supported\n");
+
+       return len;
+}
+
+static struct ibm_struct thermal_driver_data = {
+       .name = "thermal",
+       .read = thermal_read,
+       .exit = thermal_exit,
+};
+
+/*************************************************************************
+ * EC Dump subdriver
+ */
+
+static u8 ecdump_regs[256];
+
+static int ecdump_read(char *p)
+{
+       int len = 0;
+       int i, j;
+       u8 v;
+
+       len += sprintf(p + len, "EC      "
+                      " +00 +01 +02 +03 +04 +05 +06 +07"
+                      " +08 +09 +0a +0b +0c +0d +0e +0f\n");
+       for (i = 0; i < 256; i += 16) {
+               len += sprintf(p + len, "EC 0x%02x:", i);
+               for (j = 0; j < 16; j++) {
+                       if (!acpi_ec_read(i + j, &v))
+                               break;
+                       if (v != ecdump_regs[i + j])
+                               len += sprintf(p + len, " *%02x", v);
+                       else
+                               len += sprintf(p + len, "  %02x", v);
+                       ecdump_regs[i + j] = v;
+               }
+               len += sprintf(p + len, "\n");
+               if (j != 16)
+                       break;
+       }
+
+       /* These are way too dangerous to advertise openly... */
+#if 0
+       len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
+                      " (<offset> is 00-ff, <value> is 00-ff)\n");
+       len += sprintf(p + len, "commands:\t0x<offset> <value>  "
+                      " (<offset> is 00-ff, <value> is 0-255)\n");
+#endif
+       return len;
+}
+
+static int ecdump_write(char *buf)
+{
+       char *cmd;
+       int i, v;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
+                       /* i and v set */
+               } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
+                       /* i and v set */
+               } else
+                       return -EINVAL;
+               if (i >= 0 && i < 256 && v >= 0 && v < 256) {
+                       if (!acpi_ec_write(i, v))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct ibm_struct ecdump_driver_data = {
+       .name = "ecdump",
+       .read = ecdump_read,
+       .write = ecdump_write,
+       .flags.experimental = 1,
+};
+
+/*************************************************************************
+ * Backlight/brightness subdriver
+ */
+
+#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen"
+
+enum {
+       TP_EC_BACKLIGHT = 0x31,
+
+       /* TP_EC_BACKLIGHT bitmasks */
+       TP_EC_BACKLIGHT_LVLMSK = 0x1F,
+       TP_EC_BACKLIGHT_CMDMSK = 0xE0,
+       TP_EC_BACKLIGHT_MAPSW = 0x20,
+};
+
+static struct backlight_device *ibm_backlight_device;
+static int brightness_mode;
+static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */
+
+static struct mutex brightness_mutex;
+
+/*
+ * ThinkPads can read brightness from two places: EC 0x31, or
+ * CMOS NVRAM byte 0x5E, bits 0-3.
+ *
+ * EC 0x31 has the following layout
+ *   Bit 7: unknown function
+ *   Bit 6: unknown function
+ *   Bit 5: Z: honour scale changes, NZ: ignore scale changes
+ *   Bit 4: must be set to zero to avoid problems
+ *   Bit 3-0: backlight brightness level
+ *
+ * brightness_get_raw returns status data in the EC 0x31 layout
+ */
+static int brightness_get_raw(int *status)
+{
+       u8 lec = 0, lcmos = 0, level = 0;
+
+       if (brightness_mode & 1) {
+               if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec))
+                       return -EIO;
+               level = lec & TP_EC_BACKLIGHT_LVLMSK;
+       };
+       if (brightness_mode & 2) {
+               lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
+                        & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
+                       >> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+               lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07;
+               level = lcmos;
+       }
+
+       if (brightness_mode == 3) {
+               *status = lec;  /* Prefer EC, CMOS is just a backing store */
+               lec &= TP_EC_BACKLIGHT_LVLMSK;
+               if (lec == lcmos)
+                       tp_warned.bright_cmos_ec_unsync = 0;
+               else {
+                       if (!tp_warned.bright_cmos_ec_unsync) {
+                               printk(TPACPI_ERR
+                                       "CMOS NVRAM (%u) and EC (%u) do not "
+                                       "agree on display brightness level\n",
+                                       (unsigned int) lcmos,
+                                       (unsigned int) lec);
+                               tp_warned.bright_cmos_ec_unsync = 1;
+                       }
+                       return -EIO;
+               }
+       } else {
+               *status = level;
+       }
+
+       return 0;
+}
+
+/* May return EINTR which can always be mapped to ERESTARTSYS */
+static int brightness_set(int value)
+{
+       int cmos_cmd, inc, i, res;
+       int current_value;
+       int command_bits;
+
+       if (value > ((tp_features.bright_16levels)? 15 : 7) ||
+           value < 0)
+               return -EINVAL;
+
+       res = mutex_lock_interruptible(&brightness_mutex);
+       if (res < 0)
+               return res;
+
+       res = brightness_get_raw(&current_value);
+       if (res < 0)
+               goto errout;
+
+       command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK;
+       current_value &= TP_EC_BACKLIGHT_LVLMSK;
+
+       cmos_cmd = value > current_value ?
+                       TP_CMOS_BRIGHTNESS_UP :
+                       TP_CMOS_BRIGHTNESS_DOWN;
+       inc = (value > current_value)? 1 : -1;
+
+       res = 0;
+       for (i = current_value; i != value; i += inc) {
+               if ((brightness_mode & 2) &&
+                   issue_thinkpad_cmos_command(cmos_cmd)) {
+                       res = -EIO;
+                       goto errout;
+               }
+               if ((brightness_mode & 1) &&
+                   !acpi_ec_write(TP_EC_BACKLIGHT,
+                                  (i + inc) | command_bits)) {
+                       res = -EIO;
+                       goto errout;;
+               }
+       }
+
+errout:
+       mutex_unlock(&brightness_mutex);
+       return res;
+}
+
+/* sysfs backlight class ----------------------------------------------- */
+
+static int brightness_update_status(struct backlight_device *bd)
+{
+       /* it is the backlight class's job (caller) to handle
+        * EINTR and other errors properly */
+       return brightness_set(
+               (bd->props.fb_blank == FB_BLANK_UNBLANK &&
+                bd->props.power == FB_BLANK_UNBLANK) ?
+                               bd->props.brightness : 0);
+}
+
+static int brightness_get(struct backlight_device *bd)
+{
+       int status, res;
+
+       res = brightness_get_raw(&status);
+       if (res < 0)
+               return 0; /* FIXME: teach backlight about error handling */
+
+       return status & TP_EC_BACKLIGHT_LVLMSK;
+}
+
+static struct backlight_ops ibm_backlight_data = {
+       .get_brightness = brightness_get,
+       .update_status  = brightness_update_status,
+};
+
+/* --------------------------------------------------------------------- */
+
+static int __init brightness_init(struct ibm_init_struct *iibm)
+{
+       int b;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
+
+       mutex_init(&brightness_mutex);
+
+       /*
+        * We always attempt to detect acpi support, so as to switch
+        * Lenovo Vista BIOS to ACPI brightness mode even if we are not
+        * going to publish a backlight interface
+        */
+       b = tpacpi_check_std_acpi_brightness_support();
+       if (b > 0) {
+
+               if (acpi_video_backlight_support()) {
+                       if (brightness_enable > 1) {
+                               printk(TPACPI_NOTICE
+                                      "Standard ACPI backlight interface "
+                                      "available, not loading native one.\n");
+                               return 1;
+                       } else if (brightness_enable == 1) {
+                               printk(TPACPI_NOTICE
+                                      "Backlight control force enabled, even if standard "
+                                      "ACPI backlight interface is available\n");
+                       }
+               } else {
+                       if (brightness_enable > 1) {
+                               printk(TPACPI_NOTICE
+                                      "Standard ACPI backlight interface not "
+                                      "available, thinkpad_acpi native "
+                                      "brightness control enabled\n");
+                       }
+               }
+       }
+
+       if (!brightness_enable) {
+               dbg_printk(TPACPI_DBG_INIT,
+                          "brightness support disabled by "
+                          "module parameter\n");
+               return 1;
+       }
+
+       if (b > 16) {
+               printk(TPACPI_ERR
+                      "Unsupported brightness interface, "
+                      "please contact %s\n", TPACPI_MAIL);
+               return 1;
+       }
+       if (b == 16)
+               tp_features.bright_16levels = 1;
+
+       if (!brightness_mode) {
+               if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
+                       brightness_mode = 2;
+               else
+                       brightness_mode = 3;
+
+               dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
+                       brightness_mode);
+       }
+
+       if (brightness_mode > 3)
+               return -EINVAL;
+
+       if (brightness_get_raw(&b) < 0)
+               return 1;
+
+       if (tp_features.bright_16levels)
+               printk(TPACPI_INFO
+                      "detected a 16-level brightness capable ThinkPad\n");
+
+       ibm_backlight_device = backlight_device_register(
+                                       TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
+                                       &ibm_backlight_data);
+       if (IS_ERR(ibm_backlight_device)) {
+               printk(TPACPI_ERR "Could not register backlight device\n");
+               return PTR_ERR(ibm_backlight_device);
+       }
+       vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
+
+       ibm_backlight_device->props.max_brightness =
+                               (tp_features.bright_16levels)? 15 : 7;
+       ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
+       backlight_update_status(ibm_backlight_device);
+
+       return 0;
+}
+
+static void brightness_exit(void)
+{
+       if (ibm_backlight_device) {
+               vdbg_printk(TPACPI_DBG_EXIT,
+                           "calling backlight_device_unregister()\n");
+               backlight_device_unregister(ibm_backlight_device);
+       }
+}
+
+static int brightness_read(char *p)
+{
+       int len = 0;
+       int level;
+
+       level = brightness_get(NULL);
+       if (level < 0) {
+               len += sprintf(p + len, "level:\t\tunreadable\n");
+       } else {
+               len += sprintf(p + len, "level:\t\t%d\n", level);
+               len += sprintf(p + len, "commands:\tup, down\n");
+               len += sprintf(p + len, "commands:\tlevel <level>"
+                              " (<level> is 0-%d)\n",
+                              (tp_features.bright_16levels) ? 15 : 7);
+       }
+
+       return len;
+}
+
+static int brightness_write(char *buf)
+{
+       int level;
+       int rc;
+       char *cmd;
+       int max_level = (tp_features.bright_16levels) ? 15 : 7;
+
+       level = brightness_get(NULL);
+       if (level < 0)
+               return level;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (strlencmp(cmd, "up") == 0) {
+                       if (level < max_level)
+                               level++;
+               } else if (strlencmp(cmd, "down") == 0) {
+                       if (level > 0)
+                               level--;
+               } else if (sscanf(cmd, "level %d", &level) == 1 &&
+                          level >= 0 && level <= max_level) {
+                       /* new level set */
+               } else
+                       return -EINVAL;
+       }
+
+       /*
+        * Now we know what the final level should be, so we try to set it.
+        * Doing it this way makes the syscall restartable in case of EINTR
+        */
+       rc = brightness_set(level);
+       return (rc == -EINTR)? ERESTARTSYS : rc;
+}
+
+static struct ibm_struct brightness_driver_data = {
+       .name = "brightness",
+       .read = brightness_read,
+       .write = brightness_write,
+       .exit = brightness_exit,
+};
+
+/*************************************************************************
+ * Volume subdriver
+ */
+
+static int volume_offset = 0x30;
+
+static int volume_read(char *p)
+{
+       int len = 0;
+       u8 level;
+
+       if (!acpi_ec_read(volume_offset, &level)) {
+               len += sprintf(p + len, "level:\t\tunreadable\n");
+       } else {
+               len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
+               len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
+               len += sprintf(p + len, "commands:\tup, down, mute\n");
+               len += sprintf(p + len, "commands:\tlevel <level>"
+                              " (<level> is 0-15)\n");
+       }
+
+       return len;
+}
+
+static int volume_write(char *buf)
+{
+       int cmos_cmd, inc, i;
+       u8 level, mute;
+       int new_level, new_mute;
+       char *cmd;
+
+       while ((cmd = next_cmd(&buf))) {
+               if (!acpi_ec_read(volume_offset, &level))
+                       return -EIO;
+               new_mute = mute = level & 0x40;
+               new_level = level = level & 0xf;
+
+               if (strlencmp(cmd, "up") == 0) {
+                       if (mute)
+                               new_mute = 0;
+                       else
+                               new_level = level == 15 ? 15 : level + 1;
+               } else if (strlencmp(cmd, "down") == 0) {
+                       if (mute)
+                               new_mute = 0;
+                       else
+                               new_level = level == 0 ? 0 : level - 1;
+               } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
+                          new_level >= 0 && new_level <= 15) {
+                       /* new_level set */
+               } else if (strlencmp(cmd, "mute") == 0) {
+                       new_mute = 0x40;
+               } else
+                       return -EINVAL;
+
+               if (new_level != level) {
+                       /* mute doesn't change */
+
+                       cmos_cmd = (new_level > level) ?
+                                       TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
+                       inc = new_level > level ? 1 : -1;
+
+                       if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
+                                    !acpi_ec_write(volume_offset, level)))
+                               return -EIO;
+
+                       for (i = level; i != new_level; i += inc)
+                               if (issue_thinkpad_cmos_command(cmos_cmd) ||
+                                   !acpi_ec_write(volume_offset, i + inc))
+                                       return -EIO;
+
+                       if (mute &&
+                           (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
+                            !acpi_ec_write(volume_offset, new_level + mute))) {
+                               return -EIO;
+                       }
+               }
+
+               if (new_mute != mute) {
+                       /* level doesn't change */
+
+                       cmos_cmd = (new_mute) ?
+                                  TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
+
+                       if (issue_thinkpad_cmos_command(cmos_cmd) ||
+                           !acpi_ec_write(volume_offset, level + new_mute))
+                               return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+static struct ibm_struct volume_driver_data = {
+       .name = "volume",
+       .read = volume_read,
+       .write = volume_write,
+};
+
+/*************************************************************************
+ * Fan subdriver
+ */
+
+/*
+ * FAN ACCESS MODES
+ *
+ * TPACPI_FAN_RD_ACPI_GFAN:
+ *     ACPI GFAN method: returns fan level
+ *
+ *     see TPACPI_FAN_WR_ACPI_SFAN
+ *     EC 0x2f (HFSP) not available if GFAN exists
+ *
+ * TPACPI_FAN_WR_ACPI_SFAN:
+ *     ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
+ *
+ *     EC 0x2f (HFSP) might be available *for reading*, but do not use
+ *     it for writing.
+ *
+ * TPACPI_FAN_WR_TPEC:
+ *     ThinkPad EC register 0x2f (HFSP): fan control loop mode
+ *     Supported on almost all ThinkPads
+ *
+ *     Fan speed changes of any sort (including those caused by the
+ *     disengaged mode) are usually done slowly by the firmware as the
+ *     maximum ammount of fan duty cycle change per second seems to be
+ *     limited.
+ *
+ *     Reading is not available if GFAN exists.
+ *     Writing is not available if SFAN exists.
+ *
+ *     Bits
+ *      7      automatic mode engaged;
+ *             (default operation mode of the ThinkPad)
+ *             fan level is ignored in this mode.
+ *      6      full speed mode (takes precedence over bit 7);
+ *             not available on all thinkpads.  May disable
+ *             the tachometer while the fan controller ramps up
+ *             the speed (which can take up to a few *minutes*).
+ *             Speeds up fan to 100% duty-cycle, which is far above
+ *             the standard RPM levels.  It is not impossible that
+ *             it could cause hardware damage.
+ *     5-3     unused in some models.  Extra bits for fan level
+ *             in others, but still useless as all values above
+ *             7 map to the same speed as level 7 in these models.
+ *     2-0     fan level (0..7 usually)
+ *                     0x00 = stop
+ *                     0x07 = max (set when temperatures critical)
+ *             Some ThinkPads may have other levels, see
+ *             TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
+ *
+ *     FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
+ *     boot. Apparently the EC does not intialize it, so unless ACPI DSDT
+ *     does so, its initial value is meaningless (0x07).
+ *
+ *     For firmware bugs, refer to:
+ *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ *     ----
+ *
+ *     ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
+ *     Main fan tachometer reading (in RPM)
+ *
+ *     This register is present on all ThinkPads with a new-style EC, and
+ *     it is known not to be present on the A21m/e, and T22, as there is
+ *     something else in offset 0x84 according to the ACPI DSDT.  Other
+ *     ThinkPads from this same time period (and earlier) probably lack the
+ *     tachometer as well.
+ *
+ *     Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
+ *     was never fixed by IBM to report the EC firmware version string
+ *     probably support the tachometer (like the early X models), so
+ *     detecting it is quite hard.  We need more data to know for sure.
+ *
+ *     FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
+ *     might result.
+ *
+ *     FIRMWARE BUG: may go stale while the EC is switching to full speed
+ *     mode.
+ *
+ *     For firmware bugs, refer to:
+ *     http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
+ *
+ * TPACPI_FAN_WR_ACPI_FANS:
+ *     ThinkPad X31, X40, X41.  Not available in the X60.
+ *
+ *     FANS ACPI handle: takes three arguments: low speed, medium speed,
+ *     high speed.  ACPI DSDT seems to map these three speeds to levels
+ *     as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
+ *     (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
+ *
+ *     The speeds are stored on handles
+ *     (FANA:FAN9), (FANC:FANB), (FANE:FAND).
+ *
+ *     There are three default speed sets, acessible as handles:
+ *     FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
+ *
+ *     ACPI DSDT switches which set is in use depending on various
+ *     factors.
+ *
+ *     TPACPI_FAN_WR_TPEC is also available and should be used to
+ *     command the fan.  The X31/X40/X41 seems to have 8 fan levels,
+ *     but the ACPI tables just mention level 7.
+ */
+
+enum {                                 /* Fan control constants */
+       fan_status_offset = 0x2f,       /* EC register 0x2f */
+       fan_rpm_offset = 0x84,          /* EC register 0x84: LSB, 0x85 MSB (RPM)
+                                        * 0x84 must be read before 0x85 */
+
+       TP_EC_FAN_FULLSPEED = 0x40,     /* EC fan mode: full speed */
+       TP_EC_FAN_AUTO      = 0x80,     /* EC fan mode: auto fan control */
+
+       TPACPI_FAN_LAST_LEVEL = 0x100,  /* Use cached last-seen fan level */
+};
+
+enum fan_status_access_mode {
+       TPACPI_FAN_NONE = 0,            /* No fan status or control */
+       TPACPI_FAN_RD_ACPI_GFAN,        /* Use ACPI GFAN */
+       TPACPI_FAN_RD_TPEC,             /* Use ACPI EC regs 0x2f, 0x84-0x85 */
+};
+
+enum fan_control_access_mode {
+       TPACPI_FAN_WR_NONE = 0,         /* No fan control */
+       TPACPI_FAN_WR_ACPI_SFAN,        /* Use ACPI SFAN */
+       TPACPI_FAN_WR_TPEC,             /* Use ACPI EC reg 0x2f */
+       TPACPI_FAN_WR_ACPI_FANS,        /* Use ACPI FANS and EC reg 0x2f */
+};
+
+enum fan_control_commands {
+       TPACPI_FAN_CMD_SPEED    = 0x0001,       /* speed command */
+       TPACPI_FAN_CMD_LEVEL    = 0x0002,       /* level command  */
+       TPACPI_FAN_CMD_ENABLE   = 0x0004,       /* enable/disable cmd,
+                                                * and also watchdog cmd */
+};
+
+static int fan_control_allowed;
+
+static enum fan_status_access_mode fan_status_access_mode;
+static enum fan_control_access_mode fan_control_access_mode;
+static enum fan_control_commands fan_control_commands;
+
+static u8 fan_control_initial_status;
+static u8 fan_control_desired_level;
+static u8 fan_control_resume_level;
+static int fan_watchdog_maxinterval;
+
+static struct mutex fan_mutex;
+
+static void fan_watchdog_fire(struct work_struct *ignored);
+static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
+
+TPACPI_HANDLE(fans, ec, "FANS");       /* X31, X40, X41 */
+TPACPI_HANDLE(gfan, ec, "GFAN",        /* 570 */
+          "\\FSPD",            /* 600e/x, 770e, 770x */
+          );                   /* all others */
+TPACPI_HANDLE(sfan, ec, "SFAN",        /* 570 */
+          "JFNS",              /* 770x-JL */
+          );                   /* all others */
+
+/*
+ * Call with fan_mutex held
+ */
+static void fan_update_desired_level(u8 status)
+{
+       if ((status &
+            (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+               if (status > 7)
+                       fan_control_desired_level = 7;
+               else
+                       fan_control_desired_level = status;
+       }
+}
+
+static int fan_get_status(u8 *status)
+{
+       u8 s;
+
+       /* TODO:
+        * Add TPACPI_FAN_RD_ACPI_FANS ? */
+
+       switch (fan_status_access_mode) {
+       case TPACPI_FAN_RD_ACPI_GFAN:
+               /* 570, 600e/x, 770e, 770x */
+
+               if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+                       return -EIO;
+
+               if (likely(status))
+                       *status = s & 0x07;
+
+               break;
+
+       case TPACPI_FAN_RD_TPEC:
+               /* all except 570, 600e/x, 770e, 770x */
+               if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
+                       return -EIO;
+
+               if (likely(status))
+                       *status = s;
+
+               break;
+
+       default:
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static int fan_get_status_safe(u8 *status)
+{
+       int rc;
+       u8 s;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+       rc = fan_get_status(&s);
+       if (!rc)
+               fan_update_desired_level(s);
+       mutex_unlock(&fan_mutex);
+
+       if (status)
+               *status = s;
+
+       return rc;
+}
+
+static int fan_get_speed(unsigned int *speed)
+{
+       u8 hi, lo;
+
+       switch (fan_status_access_mode) {
+       case TPACPI_FAN_RD_TPEC:
+               /* all except 570, 600e/x, 770e, 770x */
+               if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
+                            !acpi_ec_read(fan_rpm_offset + 1, &hi)))
+                       return -EIO;
+
+               if (likely(speed))
+                       *speed = (hi << 8) | lo;
+
+               break;
+
+       default:
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static int fan_set_level(int level)
+{
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       switch (fan_control_access_mode) {
+       case TPACPI_FAN_WR_ACPI_SFAN:
+               if (level >= 0 && level <= 7) {
+                       if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
+                               return -EIO;
+               } else
+                       return -EINVAL;
+               break;
+
+       case TPACPI_FAN_WR_ACPI_FANS:
+       case TPACPI_FAN_WR_TPEC:
+               if (!(level & TP_EC_FAN_AUTO) &&
+                   !(level & TP_EC_FAN_FULLSPEED) &&
+                   ((level < 0) || (level > 7)))
+                       return -EINVAL;
+
+               /* safety net should the EC not support AUTO
+                * or FULLSPEED mode bits and just ignore them */
+               if (level & TP_EC_FAN_FULLSPEED)
+                       level |= 7;     /* safety min speed 7 */
+               else if (level & TP_EC_FAN_AUTO)
+                       level |= 4;     /* safety min speed 4 */
+
+               if (!acpi_ec_write(fan_status_offset, level))
+                       return -EIO;
+               else
+                       tp_features.fan_ctrl_status_undef = 0;
+               break;
+
+       default:
+               return -ENXIO;
+       }
+       return 0;
+}
+
+static int fan_set_level_safe(int level)
+{
+       int rc;
+
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+
+       if (level == TPACPI_FAN_LAST_LEVEL)
+               level = fan_control_desired_level;
+
+       rc = fan_set_level(level);
+       if (!rc)
+               fan_update_desired_level(level);
+
+       mutex_unlock(&fan_mutex);
+       return rc;
+}
+
+static int fan_set_enable(void)
+{
+       u8 s;
+       int rc;
+
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+
+       switch (fan_control_access_mode) {
+       case TPACPI_FAN_WR_ACPI_FANS:
+       case TPACPI_FAN_WR_TPEC:
+               rc = fan_get_status(&s);
+               if (rc < 0)
+                       break;
+
+               /* Don't go out of emergency fan mode */
+               if (s != 7) {
+                       s &= 0x07;
+                       s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
+               }
+
+               if (!acpi_ec_write(fan_status_offset, s))
+                       rc = -EIO;
+               else {
+                       tp_features.fan_ctrl_status_undef = 0;
+                       rc = 0;
+               }
+               break;
+
+       case TPACPI_FAN_WR_ACPI_SFAN:
+               rc = fan_get_status(&s);
+               if (rc < 0)
+                       break;
+
+               s &= 0x07;
+
+               /* Set fan to at least level 4 */
+               s |= 4;
+
+               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
+                       rc = -EIO;
+               else
+                       rc = 0;
+               break;
+
+       default:
+               rc = -ENXIO;
+       }
+
+       mutex_unlock(&fan_mutex);
+       return rc;
+}
+
+static int fan_set_disable(void)
+{
+       int rc;
+
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+
+       rc = 0;
+       switch (fan_control_access_mode) {
+       case TPACPI_FAN_WR_ACPI_FANS:
+       case TPACPI_FAN_WR_TPEC:
+               if (!acpi_ec_write(fan_status_offset, 0x00))
+                       rc = -EIO;
+               else {
+                       fan_control_desired_level = 0;
+                       tp_features.fan_ctrl_status_undef = 0;
+               }
+               break;
+
+       case TPACPI_FAN_WR_ACPI_SFAN:
+               if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
+                       rc = -EIO;
+               else
+                       fan_control_desired_level = 0;
+               break;
+
+       default:
+               rc = -ENXIO;
+       }
+
+
+       mutex_unlock(&fan_mutex);
+       return rc;
+}
+
+static int fan_set_speed(int speed)
+{
+       int rc;
+
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+
+       rc = 0;
+       switch (fan_control_access_mode) {
+       case TPACPI_FAN_WR_ACPI_FANS:
+               if (speed >= 0 && speed <= 65535) {
+                       if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
+                                       speed, speed, speed))
+                               rc = -EIO;
+               } else
+                       rc = -EINVAL;
+               break;
+
+       default:
+               rc = -ENXIO;
+       }
+
+       mutex_unlock(&fan_mutex);
+       return rc;
+}
+
+static void fan_watchdog_reset(void)
+{
+       static int fan_watchdog_active;
+
+       if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
+               return;
+
+       if (fan_watchdog_active)
+               cancel_delayed_work(&fan_watchdog_task);
+
+       if (fan_watchdog_maxinterval > 0 &&
+           tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
+               fan_watchdog_active = 1;
+               if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
+                               msecs_to_jiffies(fan_watchdog_maxinterval
+                                                * 1000))) {
+                       printk(TPACPI_ERR
+                              "failed to queue the fan watchdog, "
+                              "watchdog will not trigger\n");
+               }
+       } else
+               fan_watchdog_active = 0;
+}
+
+static void fan_watchdog_fire(struct work_struct *ignored)
+{
+       int rc;
+
+       if (tpacpi_lifecycle != TPACPI_LIFE_RUNNING)
+               return;
+
+       printk(TPACPI_NOTICE "fan watchdog: enabling fan\n");
+       rc = fan_set_enable();
+       if (rc < 0) {
+               printk(TPACPI_ERR "fan watchdog: error %d while enabling fan, "
+                       "will try again later...\n", -rc);
+               /* reschedule for later */
+               fan_watchdog_reset();
+       }
+}
+
+/*
+ * SYSFS fan layout: hwmon compatible (device)
+ *
+ * pwm*_enable:
+ *     0: "disengaged" mode
+ *     1: manual mode
+ *     2: native EC "auto" mode (recommended, hardware default)
+ *
+ * pwm*: set speed in manual mode, ignored otherwise.
+ *     0 is level 0; 255 is level 7. Intermediate points done with linear
+ *     interpolation.
+ *
+ * fan*_input: tachometer reading, RPM
+ *
+ *
+ * SYSFS fan layout: extensions
+ *
+ * fan_watchdog (driver):
+ *     fan watchdog interval in seconds, 0 disables (default), max 120
+ */
+
+/* sysfs fan pwm1_enable ----------------------------------------------- */
+static ssize_t fan_pwm1_enable_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int res, mode;
+       u8 status;
+
+       res = fan_get_status_safe(&status);
+       if (res)
+               return res;
+
+       if (unlikely(tp_features.fan_ctrl_status_undef)) {
+               if (status != fan_control_initial_status) {
+                       tp_features.fan_ctrl_status_undef = 0;
+               } else {
+                       /* Return most likely status. In fact, it
+                        * might be the only possible status */
+                       status = TP_EC_FAN_AUTO;
+               }
+       }
+
+       if (status & TP_EC_FAN_FULLSPEED) {
+               mode = 0;
+       } else if (status & TP_EC_FAN_AUTO) {
+               mode = 2;
+       } else
+               mode = 1;
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", mode);
+}
+
+static ssize_t fan_pwm1_enable_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       unsigned long t;
+       int res, level;
+
+       if (parse_strtoul(buf, 2, &t))
+               return -EINVAL;
+
+       switch (t) {
+       case 0:
+               level = TP_EC_FAN_FULLSPEED;
+               break;
+       case 1:
+               level = TPACPI_FAN_LAST_LEVEL;
+               break;
+       case 2:
+               level = TP_EC_FAN_AUTO;
+               break;
+       case 3:
+               /* reserved for software-controlled auto mode */
+               return -ENOSYS;
+       default:
+               return -EINVAL;
+       }
+
+       res = fan_set_level_safe(level);
+       if (res == -ENXIO)
+               return -EINVAL;
+       else if (res < 0)
+               return res;
+
+       fan_watchdog_reset();
+
+       return count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1_enable =
+       __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+               fan_pwm1_enable_show, fan_pwm1_enable_store);
+
+/* sysfs fan pwm1 ------------------------------------------------------ */
+static ssize_t fan_pwm1_show(struct device *dev,
+                            struct device_attribute *attr,
+                            char *buf)
+{
+       int res;
+       u8 status;
+
+       res = fan_get_status_safe(&status);
+       if (res)
+               return res;
+
+       if (unlikely(tp_features.fan_ctrl_status_undef)) {
+               if (status != fan_control_initial_status) {
+                       tp_features.fan_ctrl_status_undef = 0;
+               } else {
+                       status = TP_EC_FAN_AUTO;
+               }
+       }
+
+       if ((status &
+            (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
+               status = fan_control_desired_level;
+
+       if (status > 7)
+               status = 7;
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
+}
+
+static ssize_t fan_pwm1_store(struct device *dev,
+                             struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       unsigned long s;
+       int rc;
+       u8 status, newlevel;
+
+       if (parse_strtoul(buf, 255, &s))
+               return -EINVAL;
+
+       /* scale down from 0-255 to 0-7 */
+       newlevel = (s >> 5) & 0x07;
+
+       if (mutex_lock_interruptible(&fan_mutex))
+               return -ERESTARTSYS;
+
+       rc = fan_get_status(&status);
+       if (!rc && (status &
+                   (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
+               rc = fan_set_level(newlevel);
+               if (rc == -ENXIO)
+                       rc = -EINVAL;
+               else if (!rc) {
+                       fan_update_desired_level(newlevel);
+                       fan_watchdog_reset();
+               }
+       }
+
+       mutex_unlock(&fan_mutex);
+       return (rc)? rc : count;
+}
+
+static struct device_attribute dev_attr_fan_pwm1 =
+       __ATTR(pwm1, S_IWUSR | S_IRUGO,
+               fan_pwm1_show, fan_pwm1_store);
+
+/* sysfs fan fan1_input ------------------------------------------------ */
+static ssize_t fan_fan1_input_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       int res;
+       unsigned int speed;
+
+       res = fan_get_speed(&speed);
+       if (res < 0)
+               return res;
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", speed);
+}
+
+static struct device_attribute dev_attr_fan_fan1_input =
+       __ATTR(fan1_input, S_IRUGO,
+               fan_fan1_input_show, NULL);
+
+/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
+static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
+                                    char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
+}
+
+static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
+                                     const char *buf, size_t count)
+{
+       unsigned long t;
+
+       if (parse_strtoul(buf, 120, &t))
+               return -EINVAL;
+
+       if (!fan_control_allowed)
+               return -EPERM;
+
+       fan_watchdog_maxinterval = t;
+       fan_watchdog_reset();
+
+       return count;
+}
+
+static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
+               fan_fan_watchdog_show, fan_fan_watchdog_store);
+
+/* --------------------------------------------------------------------- */
+static struct attribute *fan_attributes[] = {
+       &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
+       &dev_attr_fan_fan1_input.attr,
+       NULL
+};
+
+static const struct attribute_group fan_attr_group = {
+       .attrs = fan_attributes,
+};
+
+static int __init fan_init(struct ibm_init_struct *iibm)
+{
+       int rc;
+
+       vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
+
+       mutex_init(&fan_mutex);
+       fan_status_access_mode = TPACPI_FAN_NONE;
+       fan_control_access_mode = TPACPI_FAN_WR_NONE;
+       fan_control_commands = 0;
+       fan_watchdog_maxinterval = 0;
+       tp_features.fan_ctrl_status_undef = 0;
+       fan_control_desired_level = 7;
+
+       TPACPI_ACPIHANDLE_INIT(fans);
+       TPACPI_ACPIHANDLE_INIT(gfan);
+       TPACPI_ACPIHANDLE_INIT(sfan);
+
+       if (gfan_handle) {
+               /* 570, 600e/x, 770e, 770x */
+               fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
+       } else {
+               /* all other ThinkPads: note that even old-style
+                * ThinkPad ECs supports the fan control register */
+               if (likely(acpi_ec_read(fan_status_offset,
+                                       &fan_control_initial_status))) {
+                       fan_status_access_mode = TPACPI_FAN_RD_TPEC;
+
+                       /* In some ThinkPads, neither the EC nor the ACPI
+                        * DSDT initialize the fan status, and it ends up
+                        * being set to 0x07 when it *could* be either
+                        * 0x07 or 0x80.
+                        *
+                        * Enable for TP-1Y (T43), TP-78 (R51e),
+                        * TP-76 (R52), TP-70 (T43, R52), which are known
+                        * to be buggy. */
+                       if (fan_control_initial_status == 0x07) {
+                               switch (thinkpad_id.ec_model) {
+                               case 0x5931: /* TP-1Y */
+                               case 0x3837: /* TP-78 */
+                               case 0x3637: /* TP-76 */
+                               case 0x3037: /* TP-70 */
+                                       printk(TPACPI_NOTICE
+                                              "fan_init: initial fan status "
+                                              "is unknown, assuming it is "
+                                              "in auto mode\n");
+                                       tp_features.fan_ctrl_status_undef = 1;
+                                       ;;
+                               }
+                       }
+               } else {
+                       printk(TPACPI_ERR
+                              "ThinkPad ACPI EC access misbehaving, "
+                              "fan status and control unavailable\n");
+                       return 1;
+               }
+       }
+
+       if (sfan_handle) {
+               /* 570, 770x-JL */
+               fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
+               fan_control_commands |=
+                   TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
+       } else {
+               if (!gfan_handle) {
+                       /* gfan without sfan means no fan control */
+                       /* all other models implement TP EC 0x2f control */
+
+                       if (fans_handle) {
+                               /* X31, X40, X41 */
+                               fan_control_access_mode =
+                                   TPACPI_FAN_WR_ACPI_FANS;
+                               fan_control_commands |=
+                                   TPACPI_FAN_CMD_SPEED |
+                                   TPACPI_FAN_CMD_LEVEL |
+                                   TPACPI_FAN_CMD_ENABLE;
+                       } else {
+                               fan_control_access_mode = TPACPI_FAN_WR_TPEC;
+                               fan_control_commands |=
+                                   TPACPI_FAN_CMD_LEVEL |
+                                   TPACPI_FAN_CMD_ENABLE;
+                       }
+               }
+       }
+
+       vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
+               str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
+                 fan_control_access_mode != TPACPI_FAN_WR_NONE),
+               fan_status_access_mode, fan_control_access_mode);
+
+       /* fan control master switch */
+       if (!fan_control_allowed) {
+               fan_control_access_mode = TPACPI_FAN_WR_NONE;
+               fan_control_commands = 0;
+               dbg_printk(TPACPI_DBG_INIT,
+                          "fan control features disabled by parameter\n");
+       }
+
+       /* update fan_control_desired_level */
+       if (fan_status_access_mode != TPACPI_FAN_NONE)
+               fan_get_status_safe(NULL);
+
+       if (fan_status_access_mode != TPACPI_FAN_NONE ||
+           fan_control_access_mode != TPACPI_FAN_WR_NONE) {
+               rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+                                        &fan_attr_group);
+               if (rc < 0)
+                       return rc;
+
+               rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
+                                       &driver_attr_fan_watchdog);
+               if (rc < 0) {
+                       sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+                                       &fan_attr_group);
+                       return rc;
+               }
+               return 0;
+       } else
+               return 1;
+}
+
+static void fan_exit(void)
+{
+       vdbg_printk(TPACPI_DBG_EXIT,
+                   "cancelling any pending fan watchdog tasks\n");
+
+       /* FIXME: can we really do this unconditionally? */
+       sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
+       driver_remove_file(&tpacpi_hwmon_pdriver.driver,
+                          &driver_attr_fan_watchdog);
+
+       cancel_delayed_work(&fan_watchdog_task);
+       flush_workqueue(tpacpi_wq);
+}
+
+static void fan_suspend(pm_message_t state)
+{
+       int rc;
+
+       if (!fan_control_allowed)
+               return;
+
+       /* Store fan status in cache */
+       fan_control_resume_level = 0;
+       rc = fan_get_status_safe(&fan_control_resume_level);
+       if (rc < 0)
+               printk(TPACPI_NOTICE
+                       "failed to read fan level for later "
+                       "restore during resume: %d\n", rc);
+
+       /* if it is undefined, don't attempt to restore it.
+        * KEEP THIS LAST */
+       if (tp_features.fan_ctrl_status_undef)
+               fan_control_resume_level = 0;
+}
+
+static void fan_resume(void)
+{
+       u8 current_level = 7;
+       bool do_set = false;
+       int rc;
+
+       /* DSDT *always* updates status on resume */
+       tp_features.fan_ctrl_status_undef = 0;
+
+       if (!fan_control_allowed ||
+           !fan_control_resume_level ||
+           (fan_get_status_safe(&current_level) < 0))
+               return;
+
+       switch (fan_control_access_mode) {
+       case TPACPI_FAN_WR_ACPI_SFAN:
+               /* never decrease fan level */
+               do_set = (fan_control_resume_level > current_level);
+               break;
+       case TPACPI_FAN_WR_ACPI_FANS:
+       case TPACPI_FAN_WR_TPEC:
+               /* never decrease fan level, scale is:
+                * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO
+                *
+                * We expect the firmware to set either 7 or AUTO, but we
+                * handle FULLSPEED out of paranoia.
+                *
+                * So, we can safely only restore FULLSPEED or 7, anything
+                * else could slow the fan.  Restoring AUTO is useless, at
+                * best that's exactly what the DSDT already set (it is the
+                * slower it uses).
+                *
+                * Always keep in mind that the DSDT *will* have set the
+                * fans to what the vendor supposes is the best level.  We
+                * muck with it only to speed the fan up.
+                */
+               if (fan_control_resume_level != 7 &&
+                   !(fan_control_resume_level & TP_EC_FAN_FULLSPEED))
+                       return;
+               else
+                       do_set = !(current_level & TP_EC_FAN_FULLSPEED) &&
+                                (current_level != fan_control_resume_level);
+               break;
+       default:
+               return;
+       }
+       if (do_set) {
+               printk(TPACPI_NOTICE
+                       "restoring fan level to 0x%02x\n",
+                       fan_control_resume_level);
+               rc = fan_set_level_safe(fan_control_resume_level);
+               if (rc < 0)
+                       printk(TPACPI_NOTICE
+                               "failed to restore fan level: %d\n", rc);
+       }
+}
+
+static int fan_read(char *p)
+{
+       int len = 0;
+       int rc;
+       u8 status;
+       unsigned int speed = 0;
+
+       switch (fan_status_access_mode) {
+       case TPACPI_FAN_RD_ACPI_GFAN:
+               /* 570, 600e/x, 770e, 770x */
+               rc = fan_get_status_safe(&status);
+               if (rc < 0)
+                       return rc;
+
+               len += sprintf(p + len, "status:\t\t%s\n"
+                              "level:\t\t%d\n",
+                              (status != 0) ? "enabled" : "disabled", status);
+               break;
+
+       case TPACPI_FAN_RD_TPEC:
+               /* all except 570, 600e/x, 770e, 770x */
+               rc = fan_get_status_safe(&status);
+               if (rc < 0)
+                       return rc;
+
+               if (unlikely(tp_features.fan_ctrl_status_undef)) {
+                       if (status != fan_control_initial_status)
+                               tp_features.fan_ctrl_status_undef = 0;
+                       else
+                               /* Return most likely status. In fact, it
+                                * might be the only possible status */
+                               status = TP_EC_FAN_AUTO;
+               }
+
+               len += sprintf(p + len, "status:\t\t%s\n",
+                              (status != 0) ? "enabled" : "disabled");
+
+               rc = fan_get_speed(&speed);
+               if (rc < 0)
+                       return rc;
+
+               len += sprintf(p + len, "speed:\t\t%d\n", speed);
+
+               if (status & TP_EC_FAN_FULLSPEED)
+                       /* Disengaged mode takes precedence */
+                       len += sprintf(p + len, "level:\t\tdisengaged\n");
+               else if (status & TP_EC_FAN_AUTO)
+                       len += sprintf(p + len, "level:\t\tauto\n");
+               else
+                       len += sprintf(p + len, "level:\t\t%d\n", status);
+               break;
+
+       case TPACPI_FAN_NONE:
+       default:
+               len += sprintf(p + len, "status:\t\tnot supported\n");
+       }
+
+       if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
+               len += sprintf(p + len, "commands:\tlevel <level>");
+
+               switch (fan_control_access_mode) {
+               case TPACPI_FAN_WR_ACPI_SFAN:
+                       len += sprintf(p + len, " (<level> is 0-7)\n");
+                       break;
+
+               default:
+                       len += sprintf(p + len, " (<level> is 0-7, "
+                                      "auto, disengaged, full-speed)\n");
+                       break;
+               }
+       }
+
+       if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
+               len += sprintf(p + len, "commands:\tenable, disable\n"
+                              "commands:\twatchdog <timeout> (<timeout> "
+                              "is 0 (off), 1-120 (seconds))\n");
+
+       if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
+               len += sprintf(p + len, "commands:\tspeed <speed>"
+                              " (<speed> is 0-65535)\n");
+
+       return len;
+}
+
+static int fan_write_cmd_level(const char *cmd, int *rc)
+{
+       int level;
+
+       if (strlencmp(cmd, "level auto") == 0)
+               level = TP_EC_FAN_AUTO;
+       else if ((strlencmp(cmd, "level disengaged") == 0) |
+                       (strlencmp(cmd, "level full-speed") == 0))
+               level = TP_EC_FAN_FULLSPEED;
+       else if (sscanf(cmd, "level %d", &level) != 1)
+               return 0;
+
+       *rc = fan_set_level_safe(level);
+       if (*rc == -ENXIO)
+               printk(TPACPI_ERR "level command accepted for unsupported "
+                      "access mode %d", fan_control_access_mode);
+
+       return 1;
+}
+
+static int fan_write_cmd_enable(const char *cmd, int *rc)
+{
+       if (strlencmp(cmd, "enable") != 0)
+               return 0;
+
+       *rc = fan_set_enable();
+       if (*rc == -ENXIO)
+               printk(TPACPI_ERR "enable command accepted for unsupported "
+                      "access mode %d", fan_control_access_mode);
+
+       return 1;
+}
+
+static int fan_write_cmd_disable(const char *cmd, int *rc)
+{
+       if (strlencmp(cmd, "disable") != 0)
+               return 0;
+
+       *rc = fan_set_disable();
+       if (*rc == -ENXIO)
+               printk(TPACPI_ERR "disable command accepted for unsupported "
+                      "access mode %d", fan_control_access_mode);
+
+       return 1;
+}
+
+static int fan_write_cmd_speed(const char *cmd, int *rc)
+{
+       int speed;
+
+       /* TODO:
+        * Support speed <low> <medium> <high> ? */
+
+       if (sscanf(cmd, "speed %d", &speed) != 1)
+               return 0;
+
+       *rc = fan_set_speed(speed);
+       if (*rc == -ENXIO)
+               printk(TPACPI_ERR "speed command accepted for unsupported "
+                      "access mode %d", fan_control_access_mode);
+
+       return 1;
+}
+
+static int fan_write_cmd_watchdog(const char *cmd, int *rc)
+{
+       int interval;
+
+       if (sscanf(cmd, "watchdog %d", &interval) != 1)
+               return 0;
+
+       if (interval < 0 || interval > 120)
+               *rc = -EINVAL;
+       else
+               fan_watchdog_maxinterval = interval;
+
+       return 1;
+}
+
+static int fan_write(char *buf)
+{
+       char *cmd;
+       int rc = 0;
+
+       while (!rc && (cmd = next_cmd(&buf))) {
+               if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
+                     fan_write_cmd_level(cmd, &rc)) &&
+                   !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
+                     (fan_write_cmd_enable(cmd, &rc) ||
+                      fan_write_cmd_disable(cmd, &rc) ||
+                      fan_write_cmd_watchdog(cmd, &rc))) &&
+                   !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
+                     fan_write_cmd_speed(cmd, &rc))
+                   )
+                       rc = -EINVAL;
+               else if (!rc)
+                       fan_watchdog_reset();
+       }
+
+       return rc;
+}
+
+static struct ibm_struct fan_driver_data = {
+       .name = "fan",
+       .read = fan_read,
+       .write = fan_write,
+       .exit = fan_exit,
+       .suspend = fan_suspend,
+       .resume = fan_resume,
+};
+
+/****************************************************************************
+ ****************************************************************************
+ *
+ * Infrastructure
+ *
+ ****************************************************************************
+ ****************************************************************************/
+
+/* sysfs name ---------------------------------------------------------- */
+static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
+                          struct device_attribute *attr,
+                          char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
+}
+
+static struct device_attribute dev_attr_thinkpad_acpi_pdev_name =
+       __ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
+
+/* --------------------------------------------------------------------- */
+
+/* /proc support */
+static struct proc_dir_entry *proc_dir;
+
+/*
+ * Module and infrastructure proble, init and exit handling
+ */
+
+static int force_load;
+
+#ifdef CONFIG_THINKPAD_ACPI_DEBUG
+static const char * __init str_supported(int is_supported)
+{
+       static char text_unsupported[] __initdata = "not supported";
+
+       return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
+}
+#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
+
+static void ibm_exit(struct ibm_struct *ibm)
+{
+       dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
+
+       list_del_init(&ibm->all_drivers);
+
+       if (ibm->flags.acpi_notify_installed) {
+               dbg_printk(TPACPI_DBG_EXIT,
+                       "%s: acpi_remove_notify_handler\n", ibm->name);
+               BUG_ON(!ibm->acpi);
+               acpi_remove_notify_handler(*ibm->acpi->handle,
+                                          ibm->acpi->type,
+                                          dispatch_acpi_notify);
+               ibm->flags.acpi_notify_installed = 0;
+               ibm->flags.acpi_notify_installed = 0;
+       }
+
+       if (ibm->flags.proc_created) {
+               dbg_printk(TPACPI_DBG_EXIT,
+                       "%s: remove_proc_entry\n", ibm->name);
+               remove_proc_entry(ibm->name, proc_dir);
+               ibm->flags.proc_created = 0;
+       }
+
+       if (ibm->flags.acpi_driver_registered) {
+               dbg_printk(TPACPI_DBG_EXIT,
+                       "%s: acpi_bus_unregister_driver\n", ibm->name);
+               BUG_ON(!ibm->acpi);
+               acpi_bus_unregister_driver(ibm->acpi->driver);
+               kfree(ibm->acpi->driver);
+               ibm->acpi->driver = NULL;
+               ibm->flags.acpi_driver_registered = 0;
+       }
+
+       if (ibm->flags.init_called && ibm->exit) {
+               ibm->exit();
+               ibm->flags.init_called = 0;
+       }
+
+       dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
+}
+
+static int __init ibm_init(struct ibm_init_struct *iibm)
+{
+       int ret;
+       struct ibm_struct *ibm = iibm->data;
+       struct proc_dir_entry *entry;
+
+       BUG_ON(ibm == NULL);
+
+       INIT_LIST_HEAD(&ibm->all_drivers);
+
+       if (ibm->flags.experimental && !experimental)
+               return 0;
+
+       dbg_printk(TPACPI_DBG_INIT,
+               "probing for %s\n", ibm->name);
+
+       if (iibm->init) {
+               ret = iibm->init(iibm);
+               if (ret > 0)
+                       return 0;       /* probe failed */
+               if (ret)
+                       return ret;
+
+               ibm->flags.init_called = 1;
+       }
+
+       if (ibm->acpi) {
+               if (ibm->acpi->hid) {
+                       ret = register_tpacpi_subdriver(ibm);
+                       if (ret)
+                               goto err_out;
+               }
+
+               if (ibm->acpi->notify) {
+                       ret = setup_acpi_notify(ibm);
+                       if (ret == -ENODEV) {
+                               printk(TPACPI_NOTICE "disabling subdriver %s\n",
+                                       ibm->name);
+                               ret = 0;
+                               goto err_out;
+                       }
+                       if (ret < 0)
+                               goto err_out;
+               }
+       }
+
+       dbg_printk(TPACPI_DBG_INIT,
+               "%s installed\n", ibm->name);
+
+       if (ibm->read) {
+               entry = create_proc_entry(ibm->name,
+                                         S_IFREG | S_IRUGO | S_IWUSR,
+                                         proc_dir);
+               if (!entry) {
+                       printk(TPACPI_ERR "unable to create proc entry %s\n",
+                              ibm->name);
+                       ret = -ENODEV;
+                       goto err_out;
+               }
+               entry->owner = THIS_MODULE;
+               entry->data = ibm;
+               entry->read_proc = &dispatch_procfs_read;
+               if (ibm->write)
+                       entry->write_proc = &dispatch_procfs_write;
+               ibm->flags.proc_created = 1;
+       }
+
+       list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
+
+       return 0;
+
+err_out:
+       dbg_printk(TPACPI_DBG_INIT,
+               "%s: at error exit path with result %d\n",
+               ibm->name, ret);
+
+       ibm_exit(ibm);
+       return (ret < 0)? ret : 0;
+}
+
+/* Probing */
+
+/* returns 0 - probe ok, or < 0 - probe error.
+ * Probe ok doesn't mean thinkpad found.
+ * On error, kfree() cleanup on tp->* is not performed, caller must do it */
+static int __must_check __init get_thinkpad_model_data(
+                                               struct thinkpad_id_data *tp)
+{
+       const struct dmi_device *dev = NULL;
+       char ec_fw_string[18];
+       char const *s;
+
+       if (!tp)
+               return -EINVAL;
+
+       memset(tp, 0, sizeof(*tp));
+
+       if (dmi_name_in_vendors("IBM"))
+               tp->vendor = PCI_VENDOR_ID_IBM;
+       else if (dmi_name_in_vendors("LENOVO"))
+               tp->vendor = PCI_VENDOR_ID_LENOVO;
+       else
+               return 0;
+
+       s = dmi_get_system_info(DMI_BIOS_VERSION);
+       tp->bios_version_str = kstrdup(s, GFP_KERNEL);
+       if (s && !tp->bios_version_str)
+               return -ENOMEM;
+       if (!tp->bios_version_str)
+               return 0;
+       tp->bios_model = tp->bios_version_str[0]
+                        | (tp->bios_version_str[1] << 8);
+
+       /*
+        * ThinkPad T23 or newer, A31 or newer, R50e or newer,
+        * X32 or newer, all Z series;  Some models must have an
+        * up-to-date BIOS or they will not be detected.
+        *
+        * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+        */
+       while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+               if (sscanf(dev->name,
+                          "IBM ThinkPad Embedded Controller -[%17c",
+                          ec_fw_string) == 1) {
+                       ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
+                       ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
+
+                       tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+                       if (!tp->ec_version_str)
+                               return -ENOMEM;
+                       tp->ec_model = ec_fw_string[0]
+                                       | (ec_fw_string[1] << 8);
+                       break;
+               }
+       }
+
+       s = dmi_get_system_info(DMI_PRODUCT_VERSION);
+       if (s && !strnicmp(s, "ThinkPad", 8)) {
+               tp->model_str = kstrdup(s, GFP_KERNEL);
+               if (!tp->model_str)
+                       return -ENOMEM;
+       }
+
+       s = dmi_get_system_info(DMI_PRODUCT_NAME);
+       tp->nummodel_str = kstrdup(s, GFP_KERNEL);
+       if (s && !tp->nummodel_str)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int __init probe_for_thinkpad(void)
+{
+       int is_thinkpad;
+
+       if (acpi_disabled)
+               return -ENODEV;
+
+       /*
+        * Non-ancient models have better DMI tagging, but very old models
+        * don't.
+        */
+       is_thinkpad = (thinkpad_id.model_str != NULL);
+
+       /* ec is required because many other handles are relative to it */
+       TPACPI_ACPIHANDLE_INIT(ec);
+       if (!ec_handle) {
+               if (is_thinkpad)
+                       printk(TPACPI_ERR
+                               "Not yet supported ThinkPad detected!\n");
+               return -ENODEV;
+       }
+
+       /*
+        * Risks a regression on very old machines, but reduces potential
+        * false positives a damn great deal
+        */
+       if (!is_thinkpad)
+               is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
+
+       if (!is_thinkpad && !force_load)
+               return -ENODEV;
+
+       return 0;
+}
+
+
+/* Module init, exit, parameters */
+
+static struct ibm_init_struct ibms_init[] __initdata = {
+       {
+               .init = thinkpad_acpi_driver_init,
+               .data = &thinkpad_acpi_driver_data,
+       },
+       {
+               .init = hotkey_init,
+               .data = &hotkey_driver_data,
+       },
+       {
+               .init = bluetooth_init,
+               .data = &bluetooth_driver_data,
+       },
+       {
+               .init = wan_init,
+               .data = &wan_driver_data,
+       },
+#ifdef CONFIG_THINKPAD_ACPI_VIDEO
+       {
+               .init = video_init,
+               .data = &video_driver_data,
+       },
+#endif
+       {
+               .init = light_init,
+               .data = &light_driver_data,
+       },
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+       {
+               .init = dock_init,
+               .data = &dock_driver_data[0],
+       },
+       {
+               .init = dock_init2,
+               .data = &dock_driver_data[1],
+       },
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+       {
+               .init = bay_init,
+               .data = &bay_driver_data,
+       },
+#endif
+       {
+               .init = cmos_init,
+               .data = &cmos_driver_data,
+       },
+       {
+               .init = led_init,
+               .data = &led_driver_data,
+       },
+       {
+               .init = beep_init,
+               .data = &beep_driver_data,
+       },
+       {
+               .init = thermal_init,
+               .data = &thermal_driver_data,
+       },
+       {
+               .data = &ecdump_driver_data,
+       },
+       {
+               .init = brightness_init,
+               .data = &brightness_driver_data,
+       },
+       {
+               .data = &volume_driver_data,
+       },
+       {
+               .init = fan_init,
+               .data = &fan_driver_data,
+       },
+};
+
+static int __init set_ibm_param(const char *val, struct kernel_param *kp)
+{
+       unsigned int i;
+       struct ibm_struct *ibm;
+
+       if (!kp || !kp->name || !val)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+               ibm = ibms_init[i].data;
+               WARN_ON(ibm == NULL);
+
+               if (!ibm || !ibm->name)
+                       continue;
+
+               if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
+                       if (strlen(val) > sizeof(ibms_init[i].param) - 2)
+                               return -ENOSPC;
+                       strcpy(ibms_init[i].param, val);
+                       strcat(ibms_init[i].param, ",");
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+module_param(experimental, int, 0);
+MODULE_PARM_DESC(experimental,
+                "Enables experimental features when non-zero");
+
+module_param_named(debug, dbg_level, uint, 0);
+MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
+
+module_param(force_load, bool, 0);
+MODULE_PARM_DESC(force_load,
+                "Attempts to load the driver even on a "
+                "mis-identified ThinkPad when true");
+
+module_param_named(fan_control, fan_control_allowed, bool, 0);
+MODULE_PARM_DESC(fan_control,
+                "Enables setting fan parameters features when true");
+
+module_param_named(brightness_mode, brightness_mode, int, 0);
+MODULE_PARM_DESC(brightness_mode,
+                "Selects brightness control strategy: "
+                "0=auto, 1=EC, 2=CMOS, 3=both");
+
+module_param(brightness_enable, uint, 0);
+MODULE_PARM_DESC(brightness_enable,
+                "Enables backlight control when 1, disables when 0");
+
+module_param(hotkey_report_mode, uint, 0);
+MODULE_PARM_DESC(hotkey_report_mode,
+                "used for backwards compatibility with userspace, "
+                "see documentation");
+
+#define TPACPI_PARAM(feature) \
+       module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
+       MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
+                        "at module load, see documentation")
+
+TPACPI_PARAM(hotkey);
+TPACPI_PARAM(bluetooth);
+TPACPI_PARAM(video);
+TPACPI_PARAM(light);
+#ifdef CONFIG_THINKPAD_ACPI_DOCK
+TPACPI_PARAM(dock);
+#endif
+#ifdef CONFIG_THINKPAD_ACPI_BAY
+TPACPI_PARAM(bay);
+#endif /* CONFIG_THINKPAD_ACPI_BAY */
+TPACPI_PARAM(cmos);
+TPACPI_PARAM(led);
+TPACPI_PARAM(beep);
+TPACPI_PARAM(ecdump);
+TPACPI_PARAM(brightness);
+TPACPI_PARAM(volume);
+TPACPI_PARAM(fan);
+
+static void thinkpad_acpi_module_exit(void)
+{
+       struct ibm_struct *ibm, *itmp;
+
+       tpacpi_lifecycle = TPACPI_LIFE_EXITING;
+
+       list_for_each_entry_safe_reverse(ibm, itmp,
+                                        &tpacpi_all_drivers,
+                                        all_drivers) {
+               ibm_exit(ibm);
+       }
+
+       dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
+
+       if (tpacpi_inputdev) {
+               if (tp_features.input_device_registered)
+                       input_unregister_device(tpacpi_inputdev);
+               else
+                       input_free_device(tpacpi_inputdev);
+       }
+
+       if (tpacpi_hwmon)
+               hwmon_device_unregister(tpacpi_hwmon);
+
+       if (tp_features.sensors_pdev_attrs_registered)
+               device_remove_file(&tpacpi_sensors_pdev->dev,
+                                  &dev_attr_thinkpad_acpi_pdev_name);
+       if (tpacpi_sensors_pdev)
+               platform_device_unregister(tpacpi_sensors_pdev);
+       if (tpacpi_pdev)
+               platform_device_unregister(tpacpi_pdev);
+
+       if (tp_features.sensors_pdrv_attrs_registered)
+               tpacpi_remove_driver_attributes(&tpacpi_hwmon_pdriver.driver);
+       if (tp_features.platform_drv_attrs_registered)
+               tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
+
+       if (tp_features.sensors_pdrv_registered)
+               platform_driver_unregister(&tpacpi_hwmon_pdriver);
+
+       if (tp_features.platform_drv_registered)
+               platform_driver_unregister(&tpacpi_pdriver);
+
+       if (proc_dir)
+               remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir);
+
+       if (tpacpi_wq)
+               destroy_workqueue(tpacpi_wq);
+
+       kfree(thinkpad_id.bios_version_str);
+       kfree(thinkpad_id.ec_version_str);
+       kfree(thinkpad_id.model_str);
+}
+
+
+static int __init thinkpad_acpi_module_init(void)
+{
+       int ret, i;
+
+       tpacpi_lifecycle = TPACPI_LIFE_INIT;
+
+       /* Parameter checking */
+       if (hotkey_report_mode > 2)
+               return -EINVAL;
+
+       /* Driver-level probe */
+
+       ret = get_thinkpad_model_data(&thinkpad_id);
+       if (ret) {
+               printk(TPACPI_ERR
+                       "unable to get DMI data: %d\n", ret);
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       ret = probe_for_thinkpad();
+       if (ret) {
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+
+       /* Driver initialization */
+
+       TPACPI_ACPIHANDLE_INIT(ecrd);
+       TPACPI_ACPIHANDLE_INIT(ecwr);
+
+       tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME);
+       if (!tpacpi_wq) {
+               thinkpad_acpi_module_exit();
+               return -ENOMEM;
+       }
+
+       proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir);
+       if (!proc_dir) {
+               printk(TPACPI_ERR
+                      "unable to create proc dir " TPACPI_PROC_DIR);
+               thinkpad_acpi_module_exit();
+               return -ENODEV;
+       }
+       proc_dir->owner = THIS_MODULE;
+
+       ret = platform_driver_register(&tpacpi_pdriver);
+       if (ret) {
+               printk(TPACPI_ERR
+                      "unable to register main platform driver\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tp_features.platform_drv_registered = 1;
+
+       ret = platform_driver_register(&tpacpi_hwmon_pdriver);
+       if (ret) {
+               printk(TPACPI_ERR
+                      "unable to register hwmon platform driver\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tp_features.sensors_pdrv_registered = 1;
+
+       ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
+       if (!ret) {
+               tp_features.platform_drv_attrs_registered = 1;
+               ret = tpacpi_create_driver_attributes(
+                                       &tpacpi_hwmon_pdriver.driver);
+       }
+       if (ret) {
+               printk(TPACPI_ERR
+                      "unable to create sysfs driver attributes\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tp_features.sensors_pdrv_attrs_registered = 1;
+
+
+       /* Device initialization */
+       tpacpi_pdev = platform_device_register_simple(TPACPI_DRVR_NAME, -1,
+                                                       NULL, 0);
+       if (IS_ERR(tpacpi_pdev)) {
+               ret = PTR_ERR(tpacpi_pdev);
+               tpacpi_pdev = NULL;
+               printk(TPACPI_ERR "unable to register platform device\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tpacpi_sensors_pdev = platform_device_register_simple(
+                                               TPACPI_HWMON_DRVR_NAME,
+                                               -1, NULL, 0);
+       if (IS_ERR(tpacpi_sensors_pdev)) {
+               ret = PTR_ERR(tpacpi_sensors_pdev);
+               tpacpi_sensors_pdev = NULL;
+               printk(TPACPI_ERR
+                      "unable to register hwmon platform device\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       ret = device_create_file(&tpacpi_sensors_pdev->dev,
+                                &dev_attr_thinkpad_acpi_pdev_name);
+       if (ret) {
+               printk(TPACPI_ERR
+                      "unable to create sysfs hwmon device attributes\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       tp_features.sensors_pdev_attrs_registered = 1;
+       tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
+       if (IS_ERR(tpacpi_hwmon)) {
+               ret = PTR_ERR(tpacpi_hwmon);
+               tpacpi_hwmon = NULL;
+               printk(TPACPI_ERR "unable to register hwmon device\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       }
+       mutex_init(&tpacpi_inputdev_send_mutex);
+       tpacpi_inputdev = input_allocate_device();
+       if (!tpacpi_inputdev) {
+               printk(TPACPI_ERR "unable to allocate input device\n");
+               thinkpad_acpi_module_exit();
+               return -ENOMEM;
+       } else {
+               /* Prepare input device, but don't register */
+               tpacpi_inputdev->name = "ThinkPad Extra Buttons";
+               tpacpi_inputdev->phys = TPACPI_DRVR_NAME "/input0";
+               tpacpi_inputdev->id.bustype = BUS_HOST;
+               tpacpi_inputdev->id.vendor = (thinkpad_id.vendor) ?
+                                               thinkpad_id.vendor :
+                                               PCI_VENDOR_ID_IBM;
+               tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
+               tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
+       }
+       for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
+               ret = ibm_init(&ibms_init[i]);
+               if (ret >= 0 && *ibms_init[i].param)
+                       ret = ibms_init[i].data->write(ibms_init[i].param);
+               if (ret < 0) {
+                       thinkpad_acpi_module_exit();
+                       return ret;
+               }
+       }
+       ret = input_register_device(tpacpi_inputdev);
+       if (ret < 0) {
+               printk(TPACPI_ERR "unable to register input device\n");
+               thinkpad_acpi_module_exit();
+               return ret;
+       } else {
+               tp_features.input_device_registered = 1;
+       }
+
+       tpacpi_lifecycle = TPACPI_LIFE_RUNNING;
+       return 0;
+}
+
+/* Please remove this in year 2009 */
+MODULE_ALIAS("ibm_acpi");
+
+MODULE_ALIAS(TPACPI_DRVR_SHORTNAME);
+
+/*
+ * DMI matching for module autoloading
+ *
+ * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+ * See http://thinkwiki.org/wiki/BIOS_Upgrade_Downloads
+ *
+ * Only models listed in thinkwiki will be supported, so add yours
+ * if it is not there yet.
+ */
+#define IBM_BIOS_MODULE_ALIAS(__type) \
+       MODULE_ALIAS("dmi:bvnIBM:bvr" __type "ET??WW")
+
+/* Non-ancient thinkpads */
+MODULE_ALIAS("dmi:bvnIBM:*:svnIBM:*:pvrThinkPad*:rvnIBM:*");
+MODULE_ALIAS("dmi:bvnLENOVO:*:svnLENOVO:*:pvrThinkPad*:rvnLENOVO:*");
+
+/* Ancient thinkpad BIOSes have to be identified by
+ * BIOS type or model number, and there are far less
+ * BIOS types than model numbers... */
+IBM_BIOS_MODULE_ALIAS("I[B,D,H,I,M,N,O,T,W,V,Y,Z]");
+IBM_BIOS_MODULE_ALIAS("1[0,3,6,8,A-G,I,K,M-P,S,T]");
+IBM_BIOS_MODULE_ALIAS("K[U,X-Z]");
+
+MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
+MODULE_DESCRIPTION(TPACPI_DESC);
+MODULE_VERSION(TPACPI_VERSION);
+MODULE_LICENSE("GPL");
+
+module_init(thinkpad_acpi_module_init);
+module_exit(thinkpad_acpi_module_exit);