Merge branch 'msi-wmi' into release
authorLen Brown <len.brown@intel.com>
Wed, 16 Dec 2009 18:57:12 +0000 (13:57 -0500)
committerLen Brown <len.brown@intel.com>
Wed, 16 Dec 2009 18:57:12 +0000 (13:57 -0500)
53 files changed:
Documentation/acpi/method-customizing.txt [new file with mode: 0644]
Documentation/feature-removal-schedule.txt
Documentation/laptops/thinkpad-acpi.txt
Documentation/thermal/sysfs-api.txt
arch/x86/kernel/acpi/cstate.c
drivers/acpi/acpica/acnamesp.h
drivers/acpi/acpica/acobject.h
drivers/acpi/acpica/dsmethod.c
drivers/acpi/acpica/dswload.c
drivers/acpi/acpica/evregion.c
drivers/acpi/acpica/evrgnini.c
drivers/acpi/acpica/evxface.c
drivers/acpi/acpica/evxfevnt.c
drivers/acpi/acpica/evxfregn.c
drivers/acpi/acpica/exmutex.c
drivers/acpi/acpica/nsaccess.c
drivers/acpi/acpica/nsdump.c
drivers/acpi/acpica/nseval.c
drivers/acpi/acpica/nsnames.c
drivers/acpi/acpica/nspredef.c
drivers/acpi/acpica/nsrepair.c
drivers/acpi/acpica/nsrepair2.c
drivers/acpi/acpica/nsutils.c
drivers/acpi/acpica/nsxfeval.c
drivers/acpi/acpica/nsxfname.c
drivers/acpi/acpica/nsxfobj.c
drivers/acpi/acpica/psxface.c
drivers/acpi/acpica/rsxface.c
drivers/acpi/acpica/utcopy.c
drivers/acpi/button.c
drivers/acpi/debug.c
drivers/acpi/dock.c
drivers/acpi/processor_core.c
drivers/acpi/processor_perflib.c
drivers/acpi/thermal.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/misc/Kconfig
drivers/platform/x86/Kconfig
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus_acpi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/eeepc-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/wmi.c
drivers/pnp/pnpacpi/core.c
drivers/pnp/pnpacpi/rsparser.c
drivers/thermal/thermal_sys.c
include/acpi/acoutput.h
include/acpi/acpixf.h
include/acpi/processor.h
include/linux/pnp.h

diff --git a/Documentation/acpi/method-customizing.txt b/Documentation/acpi/method-customizing.txt
new file mode 100644 (file)
index 0000000..e628cd2
--- /dev/null
@@ -0,0 +1,66 @@
+Linux ACPI Custom Control Method How To
+=======================================
+
+Written by Zhang Rui <rui.zhang@intel.com>
+
+
+Linux supports customizing ACPI control methods at runtime.
+
+Users can use this to
+1. override an existing method which may not work correctly,
+   or just for debugging purposes.
+2. insert a completely new method in order to create a missing
+   method such as _OFF, _ON, _STA, _INI, etc.
+For these cases, it is far simpler to dynamically install a single
+control method rather than override the entire DSDT, because kernel
+rebuild/reboot is not needed and test result can be got in minutes.
+
+Note: Only ACPI METHOD can be overridden, any other object types like
+      "Device", "OperationRegion", are not recognized.
+Note: The same ACPI control method can be overridden for many times,
+      and it's always the latest one that used by Linux/kernel.
+
+1. override an existing method
+   a) get the ACPI table via ACPI sysfs I/F. e.g. to get the DSDT,
+      just run "cat /sys/firmware/acpi/tables/DSDT > /tmp/dsdt.dat"
+   b) disassemble the table by running "iasl -d dsdt.dat".
+   c) rewrite the ASL code of the method and save it in a new file,
+   d) package the new file (psr.asl) to an ACPI table format.
+      Here is an example of a customized \_SB._AC._PSR method,
+
+      DefinitionBlock ("", "SSDT", 1, "", "", 0x20080715)
+      {
+       External (ACON)
+
+       Method (\_SB_.AC._PSR, 0, NotSerialized)
+       {
+               Store ("In AC _PSR", Debug)
+               Return (ACON)
+       }
+      }
+      Note that the full pathname of the method in ACPI namespace
+      should be used.
+      And remember to use "External" to declare external objects.
+   e) assemble the file to generate the AML code of the method.
+      e.g. "iasl psr.asl" (psr.aml is generated as a result)
+   f) mount debugfs by "mount -t debugfs none /sys/kernel/debug"
+   g) override the old method via the debugfs by running
+      "cat /tmp/psr.aml > /sys/kernel/debug/acpi/custom_method"
+
+2. insert a new method
+   This is easier than overriding an existing method.
+   We just need to create the ASL code of the method we want to
+   insert and then follow the step c) ~ g) in section 1.
+
+3. undo your changes
+   The "undo" operation is not supported for a new inserted method
+   right now, i.e. we can not remove a method currently.
+   For an overrided method, in order to undo your changes, please
+   save a copy of the method original ASL code in step c) section 1,
+   and redo step c) ~ g) to override the method with the original one.
+
+
+Note: We can use a kernel with multiple custom ACPI method running,
+      But each individual write to debugfs can implement a SINGLE
+      method override. i.e. if we want to insert/override multiple
+      ACPI methods, we need to redo step c) ~ g) for multiple times.
index 21ab9357326d77ce3b007d5f5c34c92f0e41c174..870d190fe6178676067b4f2adadbfd07b56beacb 100644 (file)
@@ -474,3 +474,22 @@ Why:       Obsoleted by the adt7475 driver.
 Who:   Jean Delvare <khali@linux-fr.org>
 
 ---------------------------
+What:  Support for lcd_switch and display_get in asus-laptop driver
+When:  March 2010
+Why:   These two features use non-standard interfaces. There are the
+       only features that really need multiple path to guess what's
+       the right method name on a specific laptop.
+
+       Removing them will allow to remove a lot of code an significantly
+       clean the drivers.
+
+       This will affect the backlight code which won't be able to know
+       if the backlight is on or off. The platform display file will also be
+       write only (like the one in eeepc-laptop).
+
+       This should'nt affect a lot of user because they usually know
+       when their display is on or off.
+
+Who:   Corentin Chary <corentin.chary@gmail.com>
+
+----------------------------
index aafcaa6341915bb001c27d1bd05006add6606224..169091f75e6d2122d1b7cb9d2bf99e5a055af00e 100644 (file)
@@ -1,7 +1,7 @@
                     ThinkPad ACPI Extras Driver
 
-                            Version 0.23
-                          April 10th, 2009
+                            Version 0.24
+                        December 11th,  2009
 
                Borislav Deianov <borislav@users.sf.net>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@@ -460,6 +460,8 @@ event       code    Key             Notes
                                For Lenovo ThinkPads with a new
                                BIOS, it has to be handled either
                                by the ACPI OSI, or by userspace.
+                               The driver does the right thing,
+                               never mess with this.
 0x1011 0x10    FN+END          Brightness down.  See brightness
                                up for details.
 
@@ -582,46 +584,15 @@ with hotkey_report_mode.
 
 Brightness hotkey notes:
 
-These are the current sane choices for brightness key mapping in
-thinkpad-acpi:
+Don't mess with the brightness hotkeys in a Thinkpad.  If you want
+notifications for OSD, use the sysfs backlight class event support.
 
-For IBM and Lenovo models *without* ACPI backlight control (the ones on
-which thinkpad-acpi will autoload its backlight interface by default,
-and on which ACPI video does not export a backlight interface):
-
-1. Don't enable or map the brightness hotkeys in thinkpad-acpi, as
-   these older firmware versions unfortunately won't respect the hotkey
-   mask for brightness keys anyway, and always reacts to them.  This
-   usually work fine, unless X.org drivers are doing something to block
-   the BIOS.  In that case, use (3) below.  This is the default mode of
-   operation.
-
-2. Enable the hotkeys, but map them to something else that is NOT
-   KEY_BRIGHTNESS_UP/DOWN or any other keycode that would cause
-   userspace to try to change the backlight level, and use that as an
-   on-screen-display hint.
-
-3. IF AND ONLY IF X.org drivers find a way to block the firmware from
-   automatically changing the brightness, enable the hotkeys and map
-   them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN, and feed that to
-   something that calls xbacklight.  thinkpad-acpi will not be able to
-   change brightness in that case either, so you should disable its
-   backlight interface.
-
-For Lenovo models *with* ACPI backlight control:
-
-1. Load up ACPI video and use that.  ACPI video will report ACPI
-   events for brightness change keys.  Do not mess with thinkpad-acpi
-   defaults in this case.  thinkpad-acpi should not have anything to do
-   with backlight events in a scenario where ACPI video is loaded:
-   brightness hotkeys must be disabled, and the backlight interface is
-   to be kept disabled as well.  This is the default mode of operation.
-
-2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi,
-   and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN.  Process
-   these keys on userspace somehow (e.g. by calling xbacklight).
-   The driver will do this automatically if it detects that ACPI video
-   has been disabled.
+The driver will issue KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN events
+automatically for the cases were userspace has to do something to
+implement brightness changes.  When you override these events, you will
+either fail to handle properly the ThinkPads that require explicit
+action to change backlight brightness, or the ThinkPads that require
+that no action be taken to work properly.
 
 
 Bluetooth
@@ -1121,25 +1092,61 @@ WARNING:
     its level up and down at every change.
 
 
-Volume control -- /proc/acpi/ibm/volume
----------------------------------------
+Volume control
+--------------
+
+procfs: /proc/acpi/ibm/volume
+ALSA: "ThinkPad Console Audio Control", default ID: "ThinkPadEC"
+
+NOTE: by default, the volume control interface operates in read-only
+mode, as it is supposed to be used for on-screen-display purposes.
+The read/write mode can be enabled through the use of the
+"volume_control=1" module parameter.
 
-This feature allows volume control on ThinkPad models which don't have
-a hardware volume knob. The available commands are:
+NOTE: distros are urged to not enable volume_control by default, this
+should be done by the local admin only.  The ThinkPad UI is for the
+console audio control to be done through the volume keys only, and for
+the desktop environment to just provide on-screen-display feedback.
+Software volume control should be done only in the main AC97/HDA
+mixer.
+
+This feature allows volume control on ThinkPad models with a digital
+volume knob (when available, not all models have it), as well as
+mute/unmute control.  The available commands are:
 
        echo up   >/proc/acpi/ibm/volume
        echo down >/proc/acpi/ibm/volume
        echo mute >/proc/acpi/ibm/volume
+       echo unmute >/proc/acpi/ibm/volume
        echo 'level <level>' >/proc/acpi/ibm/volume
 
-The <level> number range is 0 to 15 although not all of them may be
+The <level> number range is 0 to 14 although not all of them may be
 distinct. The unmute the volume after the mute command, use either the
-up or down command (the level command will not unmute the volume).
+up or down command (the level command will not unmute the volume), or
+the unmute command.
+
 The current volume level and mute state is shown in the file.
 
-The ALSA mixer interface to this feature is still missing, but patches
-to add it exist.  That problem should be addressed in the not so
-distant future.
+You can use the volume_capabilities parameter to tell the driver
+whether your thinkpad has volume control or mute-only control:
+volume_capabilities=1 for mixers with mute and volume control,
+volume_capabilities=2 for mixers with only mute control.
+
+If the driver misdetects the capabilities for your ThinkPad model,
+please report this to ibm-acpi-devel@lists.sourceforge.net, so that we
+can update the driver.
+
+There are two strategies for volume control.  To select which one
+should be used, use the volume_mode module parameter: volume_mode=1
+selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing
+(so that volume/mute changes are remembered across shutdown/reboot).
+
+The driver will operate in volume_mode=3 by default. If that does not
+work well on your ThinkPad model, please report this to
+ibm-acpi-devel@lists.sourceforge.net.
+
+The driver supports the standard ALSA module parameters.  If the ALSA
+mixer is disabled, the driver will disable all volume functionality.
 
 
 Fan control and monitoring: fan speed, fan enable/disable
@@ -1405,6 +1412,7 @@ to enable more than one output class, just add their values.
        0x0008                  HKEY event interface, hotkeys
        0x0010                  Fan control
        0x0020                  Backlight brightness
+       0x0040                  Audio mixer/volume control
 
 There is also a kernel build option to enable more debugging
 information, which may be necessary to debug driver problems.
@@ -1465,3 +1473,9 @@ Sysfs interface changelog:
                and it is always able to disable hot keys.  Very old
                thinkpads are properly supported.  hotkey_bios_mask
                is deprecated and marked for removal.
+
+0x020600:      Marker for backlight change event support.
+
+0x020700:      Support for mute-only mixers.
+               Volume control in read-only mode by default.
+               Marker for ALSA mixer support.
index a87dc277a5ca4937bd3ebff2b7eeb829b6b9ee1a..cb3d15bc1aeb47f2d05f00394a176c01430568e8 100644 (file)
@@ -206,6 +206,7 @@ passive
        passive trip point for the zone. Activation is done by polling with
        an interval of 1 second.
        Unit: millidegrees Celsius
+       Valid values: 0 (disabled) or greater than 1000
        RW, Optional
 
 *****************************
index 59cdfa4686b2bf4ad7ceae1ab680265effe901aa..2e837f5080fe56d3ad0c87d308564cf157147624 100644 (file)
@@ -48,7 +48,7 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
         * P4, Core and beyond CPUs
         */
        if (c->x86_vendor == X86_VENDOR_INTEL &&
-           (c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 14)))
+           (c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 0x0f)))
                        flags->bm_control = 0;
 }
 EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
index ab83919dda61d78b9fb42b182347111a20ae1da6..61edb156e8d088e7e320b6af4bb5a72e350aba66 100644 (file)
@@ -296,6 +296,11 @@ acpi_ns_complex_repairs(struct acpi_predefined_data *data,
                        acpi_status validate_status,
                        union acpi_operand_object **return_object_ptr);
 
+void
+acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
+                            u8 package_type,
+                            union acpi_operand_object *obj_desc);
+
 /*
  * nssearch - Namespace searching and entry
  */
@@ -354,9 +359,7 @@ acpi_ns_externalize_name(u32 internal_name_length,
                         const char *internal_name,
                         u32 * converted_name_length, char **converted_name);
 
-struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle);
-
-acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node);
+struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle);
 
 void acpi_ns_terminate(void);
 
index b39d682a2140291509423ef6624e3d785d66f639..64062b1be3eee472be38220e18fa36ecab1917ad 100644 (file)
@@ -180,7 +180,11 @@ struct acpi_object_method {
        u8 sync_level;
        union acpi_operand_object *mutex;
        u8 *aml_start;
-       ACPI_INTERNAL_METHOD implementation;
+       union {
+               ACPI_INTERNAL_METHOD implementation;
+               union acpi_operand_object *handler;
+       } extra;
+
        u32 aml_length;
        u8 thread_count;
        acpi_owner_id owner_id;
index 567a4899a0186a2667295bba73a0918ecbb33b1b..e786f9fd767f3dc0a87715915573b52fd0ddd3bf 100644 (file)
@@ -414,7 +414,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
        /* Invoke an internal method if necessary */
 
        if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
-               status = obj_desc->method.implementation(next_walk_state);
+               status = obj_desc->method.extra.implementation(next_walk_state);
                if (status == AE_OK) {
                        status = AE_CTRL_TERMINATE;
                }
index 10fc785178437155f31b6c469ad1029e70dadb65..b40513dd6a6a90884c16678b4541dd8f84e867d5 100644 (file)
@@ -212,18 +212,19 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state,
                case ACPI_TYPE_BUFFER:
 
                        /*
-                        * These types we will allow, but we will change the type. This
-                        * enables some existing code of the form:
+                        * These types we will allow, but we will change the type.
+                        * This enables some existing code of the form:
                         *
                         *  Name (DEB, 0)
                         *  Scope (DEB) { ... }
                         *
-                        * Note: silently change the type here. On the second pass, we will report
-                        * a warning
+                        * Note: silently change the type here. On the second pass,
+                        * we will report a warning
                         */
                        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-                                         "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)\n",
-                                         path,
+                                         "Type override - [%4.4s] had invalid type (%s) "
+                                         "for Scope operator, changed to type ANY\n",
+                                         acpi_ut_get_node_name(node),
                                          acpi_ut_get_type_name(node->type)));
 
                        node->type = ACPI_TYPE_ANY;
@@ -235,8 +236,10 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state,
                        /* All other types are an error */
 
                        ACPI_ERROR((AE_INFO,
-                                   "Invalid type (%s) for target of Scope operator [%4.4s] (Cannot override)",
-                                   acpi_ut_get_type_name(node->type), path));
+                                   "Invalid type (%s) for target of "
+                                   "Scope operator [%4.4s] (Cannot override)",
+                                   acpi_ut_get_type_name(node->type),
+                                   acpi_ut_get_node_name(node)));
 
                        return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
                }
@@ -697,15 +700,16 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
                case ACPI_TYPE_BUFFER:
 
                        /*
-                        * These types we will allow, but we will change the type. This
-                        * enables some existing code of the form:
+                        * These types we will allow, but we will change the type.
+                        * This enables some existing code of the form:
                         *
                         *  Name (DEB, 0)
                         *  Scope (DEB) { ... }
                         */
                        ACPI_WARNING((AE_INFO,
-                                     "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)",
-                                     buffer_ptr,
+                                     "Type override - [%4.4s] had invalid type (%s) "
+                                     "for Scope operator, changed to type ANY\n",
+                                     acpi_ut_get_node_name(node),
                                      acpi_ut_get_type_name(node->type)));
 
                        node->type = ACPI_TYPE_ANY;
@@ -717,9 +721,10 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
                        /* All other types are an error */
 
                        ACPI_ERROR((AE_INFO,
-                                   "Invalid type (%s) for target of Scope operator [%4.4s]",
+                                   "Invalid type (%s) for target of "
+                                   "Scope operator [%4.4s] (Cannot override)",
                                    acpi_ut_get_type_name(node->type),
-                                   buffer_ptr));
+                                   acpi_ut_get_node_name(node)));
 
                        return (AE_AML_OPERAND_TYPE);
                }
@@ -1047,9 +1052,22 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
                        }
 
                        /*
-                        * If we are executing a method, initialize the region
+                        * The op_region is not fully parsed at this time. The only valid
+                        * argument is the space_id. (We must save the address of the
+                        * AML of the address and length operands)
+                        *
+                        * If we have a valid region, initialize it. The namespace is
+                        * unlocked at this point.
+                        *
+                        * Need to unlock interpreter if it is locked (if we are running
+                        * a control method), in order to allow _REG methods to be run
+                        * during acpi_ev_initialize_region.
                         */
                        if (walk_state->method_node) {
+                               /*
+                                * Executing a method: initialize the region and unlock
+                                * the interpreter
+                                */
                                status =
                                    acpi_ex_create_region(op->named.data,
                                                          op->named.length,
@@ -1058,21 +1076,17 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
                                if (ACPI_FAILURE(status)) {
                                        return (status);
                                }
-                       }
 
-                       /*
-                        * The op_region is not fully parsed at this time. Only valid
-                        * argument is the space_id. (We must save the address of the
-                        * AML of the address and length operands)
-                        */
+                               acpi_ex_exit_interpreter();
+                       }
 
-                       /*
-                        * If we have a valid region, initialize it
-                        * Namespace is NOT locked at this point.
-                        */
                        status =
                            acpi_ev_initialize_region
                            (acpi_ns_get_attached_object(node), FALSE);
+                       if (walk_state->method_node) {
+                               acpi_ex_enter_interpreter();
+                       }
+
                        if (ACPI_FAILURE(status)) {
                                /*
                                 *  If AE_NOT_EXIST is returned, it is not fatal
index 0bc807c33a568b712cbb1ea27b4c37ca65ec772c..5336d911fbf07c7f6240d72a6691c766c9c1d5ad 100644 (file)
@@ -718,7 +718,7 @@ acpi_ev_install_handler(acpi_handle obj_handle,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        if (!node) {
                return (AE_BAD_PARAMETER);
        }
@@ -1087,7 +1087,7 @@ acpi_ev_reg_run(acpi_handle obj_handle,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        if (!node) {
                return (AE_BAD_PARAMETER);
        }
index cf29c4953028dbeaf2cd33a25c1a0c8dd184826b..ff168052a33261860675a4572c879bf754ab9b16 100644 (file)
@@ -575,6 +575,21 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
                                handler_obj = obj_desc->thermal_zone.handler;
                                break;
 
+                       case ACPI_TYPE_METHOD:
+                               /*
+                                * If we are executing module level code, the original
+                                * Node's object was replaced by this Method object and we
+                                * saved the handler in the method object.
+                                *
+                                * See acpi_ns_exec_module_code
+                                */
+                               if (obj_desc->method.
+                                   flags & AOPOBJ_MODULE_LEVEL) {
+                                       handler_obj =
+                                           obj_desc->method.extra.handler;
+                               }
+                               break;
+
                        default:
                                /* Ignore other objects */
                                break;
index 10b8543dd466e593b3d5f589af22cc7041843a24..2fe0809d4eb298162c99171073dcfa0162327b59 100644 (file)
@@ -259,7 +259,7 @@ acpi_install_notify_handler(acpi_handle device,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(device);
+       node = acpi_ns_validate_handle(device);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -425,7 +425,7 @@ acpi_remove_notify_handler(acpi_handle device,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(device);
+       node = acpi_ns_validate_handle(device);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
index 4721f58fe42c0653d6a6c14aee384ffbab2e708a..eed7a38d25f2cfdb2be2c5c712da9f8b4d6df5f8 100644 (file)
@@ -610,7 +610,7 @@ acpi_install_gpe_block(acpi_handle gpe_device,
                return (status);
        }
 
-       node = acpi_ns_map_handle_to_node(gpe_device);
+       node = acpi_ns_validate_handle(gpe_device);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -698,7 +698,7 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device)
                return (status);
        }
 
-       node = acpi_ns_map_handle_to_node(gpe_device);
+       node = acpi_ns_validate_handle(gpe_device);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
index 7c3d2d356ffba4877227cc2c340b7d3d1d62dc9c..c98aa7c2d67c6bbc492e9c842ce8270814e8b0a8 100644 (file)
@@ -89,7 +89,7 @@ acpi_install_address_space_handler(acpi_handle device,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(device);
+       node = acpi_ns_validate_handle(device);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -155,7 +155,7 @@ acpi_remove_address_space_handler(acpi_handle device,
 
        /* Convert and validate the device handle */
 
-       node = acpi_ns_map_handle_to_node(device);
+       node = acpi_ns_validate_handle(device);
        if (!node ||
            ((node->type != ACPI_TYPE_DEVICE) &&
             (node->type != ACPI_TYPE_PROCESSOR) &&
index 2f0114202b05dc8a86e77b31075d3e930e96718f..3c456bd575d0398224fe1ce600da19a000156c48 100644 (file)
@@ -375,6 +375,15 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(AE_AML_MUTEX_NOT_ACQUIRED);
        }
 
+       /* Must have a valid thread ID */
+
+       if (!walk_state->thread) {
+               ACPI_ERROR((AE_INFO,
+                           "Cannot release Mutex [%4.4s], null thread info",
+                           acpi_ut_get_node_name(obj_desc->mutex.node)));
+               return_ACPI_STATUS(AE_AML_INTERNAL);
+       }
+
        /*
         * The Mutex is owned, but this thread must be the owner.
         * Special case for Global Lock, any thread can release
@@ -392,15 +401,6 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(AE_AML_NOT_OWNER);
        }
 
-       /* Must have a valid thread ID */
-
-       if (!walk_state->thread) {
-               ACPI_ERROR((AE_INFO,
-                           "Cannot release Mutex [%4.4s], null thread info",
-                           acpi_ut_get_node_name(obj_desc->mutex.node)));
-               return_ACPI_STATUS(AE_AML_INTERNAL);
-       }
-
        /*
         * The sync level of the mutex must be equal to the current sync level. In
         * other words, the current level means that at least one mutex at that
index 9c3cdbe2d82a96e8b5ed8502432d057afa3e5856..d622ba770000f0e22e836c5d49f2edd6d96bd6b4 100644 (file)
@@ -165,7 +165,7 @@ acpi_status acpi_ns_root_initialize(void)
 
                                obj_desc->method.method_flags =
                                    AML_METHOD_INTERNAL_ONLY;
-                               obj_desc->method.implementation =
+                               obj_desc->method.extra.implementation =
                                    acpi_ut_osi_implementation;
 #endif
                                break;
index 2deb986861ca2d9ca9d376f92d737954200f5968..e37836e27e29eb3a2816696fbfb7c5da43b04999 100644 (file)
@@ -180,7 +180,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
                return (AE_OK);
        }
 
-       this_node = acpi_ns_map_handle_to_node(obj_handle);
+       this_node = acpi_ns_validate_handle(obj_handle);
        if (!this_node) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n",
                                  obj_handle));
index f771e978c40396cc405e5484e29344dd5176d9f8..af9fe91037340ff1e404ffdc521881da8e65bf58 100644 (file)
@@ -381,6 +381,18 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
                                    method_obj->method.next_object);
        type = acpi_ns_get_type(parent_node);
 
+       /*
+        * Get the region handler and save it in the method object. We may need
+        * this if an operation region declaration causes a _REG method to be run.
+        *
+        * We can't do this in acpi_ps_link_module_code because
+        * acpi_gbl_root_node->Object is NULL at PASS1.
+        */
+       if ((type == ACPI_TYPE_DEVICE) && parent_node->object) {
+               method_obj->method.extra.handler =
+                   parent_node->object->device.handler;
+       }
+
        /* Must clear next_object (acpi_ns_attach_object needs the field) */
 
        method_obj->method.next_object = NULL;
@@ -415,6 +427,12 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
        ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n",
                          method_obj->method.aml_start));
 
+       /* Delete a possible implicit return value (in slack mode) */
+
+       if (info->return_object) {
+               acpi_ut_remove_reference(info->return_object);
+       }
+
        /* Detach the temporary method object */
 
        acpi_ns_detach_object(parent_node);
index af8e6bcee07e5544112299df765c540a7b4c0602..8f9a4875ce26708ae06b17a5f637b43a52964537 100644 (file)
@@ -232,7 +232,7 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle,
 
        ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, target_handle);
 
-       node = acpi_ns_map_handle_to_node(target_handle);
+       node = acpi_ns_validate_handle(target_handle);
        if (!node) {
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
index b05f42903c869e4378b515be629b37b091f11ce5..d34fa59548f737cd38f1ebc3a242f3563fe3919d 100644 (file)
@@ -216,29 +216,38 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
        data->pathname = pathname;
 
        /*
-        * Check that the type of the return object is what is expected for
-        * this predefined name
+        * Check that the type of the main return object is what is expected
+        * for this predefined name
         */
        status = acpi_ns_check_object_type(data, return_object_ptr,
                                           predefined->info.expected_btypes,
                                           ACPI_NOT_PACKAGE_ELEMENT);
        if (ACPI_FAILURE(status)) {
-               goto check_validation_status;
+               goto exit;
        }
 
-       /* For returned Package objects, check the type of all sub-objects */
-
-       if (return_object->common.type == ACPI_TYPE_PACKAGE) {
+       /*
+        * For returned Package objects, check the type of all sub-objects.
+        * Note: Package may have been newly created by call above.
+        */
+       if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) {
                status = acpi_ns_check_package(data, return_object_ptr);
+               if (ACPI_FAILURE(status)) {
+                       goto exit;
+               }
        }
 
        /*
-        * Perform additional, more complicated repairs on a per-name
-        * basis.
+        * The return object was OK, or it was successfully repaired above.
+        * Now make some additional checks such as verifying that package
+        * objects are sorted correctly (if required) or buffer objects have
+        * the correct data width (bytes vs. dwords). These repairs are
+        * performed on a per-name basis, i.e., the code is specific to
+        * particular predefined names.
         */
        status = acpi_ns_complex_repairs(data, node, status, return_object_ptr);
 
-check_validation_status:
+exit:
        /*
         * If the object validation failed or if we successfully repaired one
         * or more objects, mark the parent node to suppress further warning
@@ -427,6 +436,13 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
                          data->pathname, package->ret_info.type,
                          return_object->package.count));
 
+       /*
+        * For variable-length Packages, we can safely remove all embedded
+        * and trailing NULL package elements
+        */
+       acpi_ns_remove_null_elements(data, package->ret_info.type,
+                                    return_object);
+
        /* Extract package count and elements array */
 
        elements = return_object->package.elements;
@@ -461,11 +477,11 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
                if (count < expected_count) {
                        goto package_too_small;
                } else if (count > expected_count) {
-                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
-                                             data->node_flags,
-                                             "Return Package is larger than needed - "
-                                             "found %u, expected %u", count,
-                                             expected_count));
+                       ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                         "%s: Return Package is larger than needed - "
+                                         "found %u, expected %u\n",
+                                         data->pathname, count,
+                                         expected_count));
                }
 
                /* Validate all elements of the returned package */
@@ -680,53 +696,18 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
        union acpi_operand_object *sub_package;
        union acpi_operand_object **sub_elements;
        acpi_status status;
-       u8 non_trailing_null = FALSE;
        u32 expected_count;
        u32 i;
        u32 j;
 
-       /* Validate each sub-Package in the parent Package */
-
+       /*
+        * Validate each sub-Package in the parent Package
+        *
+        * NOTE: assumes list of sub-packages contains no NULL elements.
+        * Any NULL elements should have been removed by earlier call
+        * to acpi_ns_remove_null_elements.
+        */
        for (i = 0; i < count; i++) {
-               /*
-                * Handling for NULL package elements. For now, we will simply allow
-                * a parent package with trailing NULL elements. This can happen if
-                * the package was defined to be longer than the initializer list.
-                * This is legal as per the ACPI specification. It is often used
-                * to allow for dynamic initialization of a Package.
-                *
-                * A future enhancement may be to simply truncate the package to
-                * remove the trailing NULL elements.
-                */
-               if (!(*elements)) {
-                       if (!non_trailing_null) {
-
-                               /* Ensure the remaining elements are all NULL */
-
-                               for (j = 1; j < (count - i + 1); j++) {
-                                       if (elements[j]) {
-                                               non_trailing_null = TRUE;
-                                       }
-                               }
-
-                               if (!non_trailing_null) {
-
-                                       /* Ignore the trailing NULL elements */
-
-                                       return (AE_OK);
-                               }
-                       }
-
-                       /* There are trailing non-null elements, issue warning */
-
-                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
-                                             data->node_flags,
-                                             "Found NULL element at package index %u",
-                                             i));
-                       elements++;
-                       continue;
-               }
-
                sub_package = *elements;
                sub_elements = sub_package->package.elements;
 
index d563f1a564a79ba4090e012158cda79c410b88a5..4fd1bdb056b2ffa496514b0a422c82aef36c35c7 100644 (file)
 #include "accommon.h"
 #include "acnamesp.h"
 #include "acinterp.h"
-#include "acpredef.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsrepair")
 
+/*******************************************************************************
+ *
+ * This module attempts to repair or convert objects returned by the
+ * predefined methods to an object type that is expected, as per the ACPI
+ * specification. The need for this code is dictated by the many machines that
+ * return incorrect types for the standard predefined methods. Performing these
+ * conversions here, in one place, eliminates the need for individual ACPI
+ * device drivers to do the same. Note: Most of these conversions are different
+ * than the internal object conversion routines used for implicit object
+ * conversion.
+ *
+ * The following conversions can be performed as necessary:
+ *
+ * Integer -> String
+ * Integer -> Buffer
+ * String  -> Integer
+ * String  -> Buffer
+ * Buffer  -> Integer
+ * Buffer  -> String
+ * Buffer  -> Package of Integers
+ * Package -> Package of one Package
+ *
+ ******************************************************************************/
+/* Local prototypes */
+static acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+                          union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+                         union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+                         union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_package(union acpi_operand_object *original_object,
+                          union acpi_operand_object **return_object);
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ns_repair_object
@@ -68,6 +107,7 @@ ACPI_MODULE_NAME("nsrepair")
  *              not expected.
  *
  ******************************************************************************/
+
 acpi_status
 acpi_ns_repair_object(struct acpi_predefined_data *data,
                      u32 expected_btypes,
@@ -76,32 +116,206 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
 {
        union acpi_operand_object *return_object = *return_object_ptr;
        union acpi_operand_object *new_object;
-       acpi_size length;
        acpi_status status;
 
+       ACPI_FUNCTION_NAME(ns_repair_object);
+
        /*
         * At this point, we know that the type of the returned object was not
         * one of the expected types for this predefined name. Attempt to
-        * repair the object. Only a limited number of repairs are possible.
+        * repair the object by converting it to one of the expected object
+        * types for this predefined name.
         */
-       switch (return_object->common.type) {
+       if (expected_btypes & ACPI_RTYPE_INTEGER) {
+               status = acpi_ns_convert_to_integer(return_object, &new_object);
+               if (ACPI_SUCCESS(status)) {
+                       goto object_repaired;
+               }
+       }
+       if (expected_btypes & ACPI_RTYPE_STRING) {
+               status = acpi_ns_convert_to_string(return_object, &new_object);
+               if (ACPI_SUCCESS(status)) {
+                       goto object_repaired;
+               }
+       }
+       if (expected_btypes & ACPI_RTYPE_BUFFER) {
+               status = acpi_ns_convert_to_buffer(return_object, &new_object);
+               if (ACPI_SUCCESS(status)) {
+                       goto object_repaired;
+               }
+       }
+       if (expected_btypes & ACPI_RTYPE_PACKAGE) {
+               status = acpi_ns_convert_to_package(return_object, &new_object);
+               if (ACPI_SUCCESS(status)) {
+                       goto object_repaired;
+               }
+       }
+
+       /* We cannot repair this object */
+
+       return (AE_AML_OPERAND_TYPE);
+
+      object_repaired:
+
+       /* Object was successfully repaired */
+
+       /*
+        * If the original object is a package element, we need to:
+        * 1. Set the reference count of the new object to match the
+        *    reference count of the old object.
+        * 2. Decrement the reference count of the original object.
+        */
+       if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+               new_object->common.reference_count =
+                   return_object->common.reference_count;
+
+               if (return_object->common.reference_count > 1) {
+                       return_object->common.reference_count--;
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                 "%s: Converted %s to expected %s at index %u\n",
+                                 data->pathname,
+                                 acpi_ut_get_object_type_name(return_object),
+                                 acpi_ut_get_object_type_name(new_object),
+                                 package_index));
+       } else {
+               ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                 "%s: Converted %s to expected %s\n",
+                                 data->pathname,
+                                 acpi_ut_get_object_type_name(return_object),
+                                 acpi_ut_get_object_type_name(new_object)));
+       }
+
+       /* Delete old object, install the new return object */
+
+       acpi_ut_remove_reference(return_object);
+       *return_object_ptr = new_object;
+       data->flags |= ACPI_OBJECT_REPAIRED;
+       return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_convert_to_integer
+ *
+ * PARAMETERS:  original_object     - Object to be converted
+ *              return_object       - Where the new converted object is returned
+ *
+ * RETURN:      Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+                          union acpi_operand_object **return_object)
+{
+       union acpi_operand_object *new_object;
+       acpi_status status;
+       u64 value = 0;
+       u32 i;
+
+       switch (original_object->common.type) {
+       case ACPI_TYPE_STRING:
+
+               /* String-to-Integer conversion */
+
+               status = acpi_ut_strtoul64(original_object->string.pointer,
+                                          ACPI_ANY_BASE, &value);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+               break;
+
        case ACPI_TYPE_BUFFER:
 
-               /* Does the method/object legally return a string? */
+               /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */
 
-               if (!(expected_btypes & ACPI_RTYPE_STRING)) {
+               if (original_object->buffer.length > 8) {
                        return (AE_AML_OPERAND_TYPE);
                }
 
+               /* Extract each buffer byte to create the integer */
+
+               for (i = 0; i < original_object->buffer.length; i++) {
+                       value |=
+                           ((u64) original_object->buffer.
+                            pointer[i] << (i * 8));
+               }
+               break;
+
+       default:
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       new_object = acpi_ut_create_integer_object(value);
+       if (!new_object) {
+               return (AE_NO_MEMORY);
+       }
+
+       *return_object = new_object;
+       return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_convert_to_string
+ *
+ * PARAMETERS:  original_object     - Object to be converted
+ *              return_object       - Where the new converted object is returned
+ *
+ * RETURN:      Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+                         union acpi_operand_object **return_object)
+{
+       union acpi_operand_object *new_object;
+       acpi_size length;
+       acpi_status status;
+
+       switch (original_object->common.type) {
+       case ACPI_TYPE_INTEGER:
+               /*
+                * Integer-to-String conversion. Commonly, convert
+                * an integer of value 0 to a NULL string. The last element of
+                * _BIF and _BIX packages occasionally need this fix.
+                */
+               if (original_object->integer.value == 0) {
+
+                       /* Allocate a new NULL string object */
+
+                       new_object = acpi_ut_create_string_object(0);
+                       if (!new_object) {
+                               return (AE_NO_MEMORY);
+                       }
+               } else {
+                       status =
+                           acpi_ex_convert_to_string(original_object,
+                                                     &new_object,
+                                                     ACPI_IMPLICIT_CONVERT_HEX);
+                       if (ACPI_FAILURE(status)) {
+                               return (status);
+                       }
+               }
+               break;
+
+       case ACPI_TYPE_BUFFER:
                /*
-                * Have a Buffer, expected a String, convert. Use a to_string
+                * Buffer-to-String conversion. Use a to_string
                 * conversion, no transform performed on the buffer data. The best
                 * example of this is the _BIF method, where the string data from
                 * the battery is often (incorrectly) returned as buffer object(s).
                 */
                length = 0;
-               while ((length < return_object->buffer.length) &&
-                      (return_object->buffer.pointer[length])) {
+               while ((length < original_object->buffer.length) &&
+                      (original_object->buffer.pointer[length])) {
                        length++;
                }
 
@@ -117,94 +331,176 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
                 * terminated at Length+1.
                 */
                ACPI_MEMCPY(new_object->string.pointer,
-                           return_object->buffer.pointer, length);
+                           original_object->buffer.pointer, length);
                break;
 
+       default:
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       *return_object = new_object;
+       return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_convert_to_buffer
+ *
+ * PARAMETERS:  original_object     - Object to be converted
+ *              return_object       - Where the new converted object is returned
+ *
+ * RETURN:      Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+                         union acpi_operand_object **return_object)
+{
+       union acpi_operand_object *new_object;
+       acpi_status status;
+       union acpi_operand_object **elements;
+       u32 *dword_buffer;
+       u32 count;
+       u32 i;
+
+       switch (original_object->common.type) {
        case ACPI_TYPE_INTEGER:
+               /*
+                * Integer-to-Buffer conversion.
+                * Convert the Integer to a packed-byte buffer. _MAT and other
+                * objects need this sometimes, if a read has been performed on a
+                * Field object that is less than or equal to the global integer
+                * size (32 or 64 bits).
+                */
+               status =
+                   acpi_ex_convert_to_buffer(original_object, &new_object);
+               if (ACPI_FAILURE(status)) {
+                       return (status);
+               }
+               break;
 
-               /* 1) Does the method/object legally return a buffer? */
+       case ACPI_TYPE_STRING:
 
-               if (expected_btypes & ACPI_RTYPE_BUFFER) {
-                       /*
-                        * Convert the Integer to a packed-byte buffer. _MAT needs
-                        * this sometimes, if a read has been performed on a Field
-                        * object that is less than or equal to the global integer
-                        * size (32 or 64 bits).
-                        */
-                       status =
-                           acpi_ex_convert_to_buffer(return_object,
-                                                     &new_object);
-                       if (ACPI_FAILURE(status)) {
-                               return (status);
-                       }
+               /* String-to-Buffer conversion. Simple data copy */
+
+               new_object =
+                   acpi_ut_create_buffer_object(original_object->string.
+                                                length);
+               if (!new_object) {
+                       return (AE_NO_MEMORY);
                }
 
-               /* 2) Does the method/object legally return a string? */
+               ACPI_MEMCPY(new_object->buffer.pointer,
+                           original_object->string.pointer,
+                           original_object->string.length);
+               break;
+
+       case ACPI_TYPE_PACKAGE:
+               /*
+                * This case is often seen for predefined names that must return a
+                * Buffer object with multiple DWORD integers within. For example,
+                * _FDE and _GTM. The Package can be converted to a Buffer.
+                */
+
+               /* All elements of the Package must be integers */
 
-               else if (expected_btypes & ACPI_RTYPE_STRING) {
-                       /*
-                        * The only supported Integer-to-String conversion is to convert
-                        * an integer of value 0 to a NULL string. The last element of
-                        * _BIF and _BIX packages occasionally need this fix.
-                        */
-                       if (return_object->integer.value != 0) {
+               elements = original_object->package.elements;
+               count = original_object->package.count;
+
+               for (i = 0; i < count; i++) {
+                       if ((!*elements) ||
+                           ((*elements)->common.type != ACPI_TYPE_INTEGER)) {
                                return (AE_AML_OPERAND_TYPE);
                        }
+                       elements++;
+               }
 
-                       /* Allocate a new NULL string object */
+               /* Create the new buffer object to replace the Package */
 
-                       new_object = acpi_ut_create_string_object(0);
-                       if (!new_object) {
-                               return (AE_NO_MEMORY);
-                       }
-               } else {
-                       return (AE_AML_OPERAND_TYPE);
+               new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count));
+               if (!new_object) {
+                       return (AE_NO_MEMORY);
                }
-               break;
 
-       default:
+               /* Copy the package elements (integers) to the buffer as DWORDs */
 
-               /* We cannot repair this object */
+               elements = original_object->package.elements;
+               dword_buffer = ACPI_CAST_PTR(u32, new_object->buffer.pointer);
+
+               for (i = 0; i < count; i++) {
+                       *dword_buffer = (u32) (*elements)->integer.value;
+                       dword_buffer++;
+                       elements++;
+               }
+               break;
 
+       default:
                return (AE_AML_OPERAND_TYPE);
        }
 
-       /* Object was successfully repaired */
+       *return_object = new_object;
+       return (AE_OK);
+}
 
-       /*
-        * If the original object is a package element, we need to:
-        * 1. Set the reference count of the new object to match the
-        *    reference count of the old object.
-        * 2. Decrement the reference count of the original object.
-        */
-       if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
-               new_object->common.reference_count =
-                   return_object->common.reference_count;
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_convert_to_package
+ *
+ * PARAMETERS:  original_object     - Object to be converted
+ *              return_object       - Where the new converted object is returned
+ *
+ * RETURN:      Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of
+ *              the buffer is converted to a single integer package element.
+ *
+ ******************************************************************************/
 
-               if (return_object->common.reference_count > 1) {
-                       return_object->common.reference_count--;
+static acpi_status
+acpi_ns_convert_to_package(union acpi_operand_object *original_object,
+                          union acpi_operand_object **return_object)
+{
+       union acpi_operand_object *new_object;
+       union acpi_operand_object **elements;
+       u32 length;
+       u8 *buffer;
+
+       switch (original_object->common.type) {
+       case ACPI_TYPE_BUFFER:
+
+               /* Buffer-to-Package conversion */
+
+               length = original_object->buffer.length;
+               new_object = acpi_ut_create_package_object(length);
+               if (!new_object) {
+                       return (AE_NO_MEMORY);
                }
 
-               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
-                                     "Converted %s to expected %s at index %u",
-                                     acpi_ut_get_object_type_name
-                                     (return_object),
-                                     acpi_ut_get_object_type_name(new_object),
-                                     package_index));
-       } else {
-               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
-                                     "Converted %s to expected %s",
-                                     acpi_ut_get_object_type_name
-                                     (return_object),
-                                     acpi_ut_get_object_type_name
-                                     (new_object)));
-       }
+               /* Convert each buffer byte to an integer package element */
 
-       /* Delete old object, install the new return object */
+               elements = new_object->package.elements;
+               buffer = original_object->buffer.pointer;
 
-       acpi_ut_remove_reference(return_object);
-       *return_object_ptr = new_object;
-       data->flags |= ACPI_OBJECT_REPAIRED;
+               while (length--) {
+                       *elements =
+                           acpi_ut_create_integer_object((u64) *buffer);
+                       if (!*elements) {
+                               acpi_ut_remove_reference(new_object);
+                               return (AE_NO_MEMORY);
+                       }
+                       elements++;
+                       buffer++;
+               }
+               break;
+
+       default:
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       *return_object = new_object;
        return (AE_OK);
 }
 
@@ -238,6 +534,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
 {
        union acpi_operand_object *pkg_obj_desc;
 
+       ACPI_FUNCTION_NAME(ns_repair_package_list);
+
        /*
         * Create the new outer package and populate it. The new package will
         * have a single element, the lone subpackage.
@@ -254,8 +552,9 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
        *obj_desc_ptr = pkg_obj_desc;
        data->flags |= ACPI_OBJECT_REPAIRED;
 
-       ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
-                             "Repaired Incorrectly formed Package"));
+       ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                         "%s: Repaired incorrectly formed Package\n",
+                         data->pathname));
 
        return (AE_OK);
 }
index d07b686138181ad9693dd72d9773bfc07480bbf9..f13691c1cca54996097e3ada48b792acf70708ce 100644 (file)
@@ -45,6 +45,7 @@
 #include <acpi/acpi.h>
 #include "accommon.h"
 #include "acnamesp.h"
+#include "acpredef.h"
 
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsrepair2")
@@ -73,6 +74,10 @@ static acpi_status
 acpi_ns_repair_ALR(struct acpi_predefined_data *data,
                   union acpi_operand_object **return_object_ptr);
 
+static acpi_status
+acpi_ns_repair_FDE(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr);
+
 static acpi_status
 acpi_ns_repair_PSS(struct acpi_predefined_data *data,
                   union acpi_operand_object **return_object_ptr);
@@ -88,9 +93,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
                          u32 sort_index,
                          u8 sort_direction, char *sort_key_name);
 
-static acpi_status
-acpi_ns_remove_null_elements(union acpi_operand_object *package);
-
 static acpi_status
 acpi_ns_sort_list(union acpi_operand_object **elements,
                  u32 count, u32 index, u8 sort_direction);
@@ -104,17 +106,27 @@ acpi_ns_sort_list(union acpi_operand_object **elements,
  * This table contains the names of the predefined methods for which we can
  * perform more complex repairs.
  *
- * _ALR: Sort the list ascending by ambient_illuminance if necessary
- * _PSS: Sort the list descending by Power if necessary
- * _TSS: Sort the list descending by Power if necessary
+ * As necessary:
+ *
+ * _ALR: Sort the list ascending by ambient_illuminance
+ * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _PSS: Sort the list descending by Power
+ * _TSS: Sort the list descending by Power
  */
 static const struct acpi_repair_info acpi_ns_repairable_names[] = {
        {"_ALR", acpi_ns_repair_ALR},
+       {"_FDE", acpi_ns_repair_FDE},
+       {"_GTM", acpi_ns_repair_FDE},   /* _GTM has same repair as _FDE */
        {"_PSS", acpi_ns_repair_PSS},
        {"_TSS", acpi_ns_repair_TSS},
        {{0, 0, 0, 0}, NULL}    /* Table terminator */
 };
 
+#define ACPI_FDE_FIELD_COUNT        5
+#define ACPI_FDE_BYTE_BUFFER_SIZE   5
+#define ACPI_FDE_DWORD_BUFFER_SIZE  (ACPI_FDE_FIELD_COUNT * sizeof (u32))
+
 /******************************************************************************
  *
  * FUNCTION:    acpi_ns_complex_repairs
@@ -213,6 +225,94 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
        return (status);
 }
 
+/******************************************************************************
+ *
+ * FUNCTION:    acpi_ns_repair_FDE
+ *
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              return_object_ptr   - Pointer to the object returned from the
+ *                                    evaluation of a method or object
+ *
+ * RETURN:      Status. AE_OK if object is OK or was repaired successfully
+ *
+ * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return
+ *              value is a Buffer of 5 DWORDs. This function repairs a common
+ *              problem where the return value is a Buffer of BYTEs, not
+ *              DWORDs.
+ *
+ *****************************************************************************/
+
+static acpi_status
+acpi_ns_repair_FDE(struct acpi_predefined_data *data,
+                  union acpi_operand_object **return_object_ptr)
+{
+       union acpi_operand_object *return_object = *return_object_ptr;
+       union acpi_operand_object *buffer_object;
+       u8 *byte_buffer;
+       u32 *dword_buffer;
+       u32 i;
+
+       ACPI_FUNCTION_NAME(ns_repair_FDE);
+
+       switch (return_object->common.type) {
+       case ACPI_TYPE_BUFFER:
+
+               /* This is the expected type. Length should be (at least) 5 DWORDs */
+
+               if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) {
+                       return (AE_OK);
+               }
+
+               /* We can only repair if we have exactly 5 BYTEs */
+
+               if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) {
+                       ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
+                                             data->node_flags,
+                                             "Incorrect return buffer length %u, expected %u",
+                                             return_object->buffer.length,
+                                             ACPI_FDE_DWORD_BUFFER_SIZE));
+
+                       return (AE_AML_OPERAND_TYPE);
+               }
+
+               /* Create the new (larger) buffer object */
+
+               buffer_object =
+                   acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE);
+               if (!buffer_object) {
+                       return (AE_NO_MEMORY);
+               }
+
+               /* Expand each byte to a DWORD */
+
+               byte_buffer = return_object->buffer.pointer;
+               dword_buffer =
+                   ACPI_CAST_PTR(u32, buffer_object->buffer.pointer);
+
+               for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) {
+                       *dword_buffer = (u32) *byte_buffer;
+                       dword_buffer++;
+                       byte_buffer++;
+               }
+
+               ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                 "%s Expanded Byte Buffer to expected DWord Buffer\n",
+                                 data->pathname));
+               break;
+
+       default:
+               return (AE_AML_OPERAND_TYPE);
+       }
+
+       /* Delete the original return object, return the new buffer object */
+
+       acpi_ut_remove_reference(return_object);
+       *return_object_ptr = buffer_object;
+
+       data->flags |= ACPI_OBJECT_REPAIRED;
+       return (AE_OK);
+}
+
 /******************************************************************************
  *
  * FUNCTION:    acpi_ns_repair_TSS
@@ -345,6 +445,8 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
        u32 previous_value;
        acpi_status status;
 
+       ACPI_FUNCTION_NAME(ns_check_sorted_list);
+
        /* The top-level object must be a package */
 
        if (return_object->common.type != ACPI_TYPE_PACKAGE) {
@@ -352,24 +454,10 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
        }
 
        /*
-        * Detect any NULL package elements and remove them from the
-        * package.
-        *
-        * TBD: We may want to do this for all predefined names that
-        * return a variable-length package of packages.
+        * NOTE: assumes list of sub-packages contains no NULL elements.
+        * Any NULL elements should have been removed by earlier call
+        * to acpi_ns_remove_null_elements.
         */
-       status = acpi_ns_remove_null_elements(return_object);
-       if (status == AE_NULL_ENTRY) {
-               ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
-                                     "NULL elements removed from package"));
-
-               /* Exit if package is now zero length */
-
-               if (!return_object->package.count) {
-                       return (AE_NULL_ENTRY);
-               }
-       }
-
        outer_elements = return_object->package.elements;
        outer_element_count = return_object->package.count;
        if (!outer_element_count) {
@@ -422,10 +510,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
 
                        data->flags |= ACPI_OBJECT_REPAIRED;
 
-                       ACPI_INFO_PREDEFINED((AE_INFO, data->pathname,
-                                             data->node_flags,
-                                             "Repaired unsorted list - now sorted by %s",
-                                             sort_key_name));
+                       ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                         "%s: Repaired unsorted list - now sorted by %s\n",
+                                         data->pathname, sort_key_name));
                        return (AE_OK);
                }
 
@@ -440,36 +527,63 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
  *
  * FUNCTION:    acpi_ns_remove_null_elements
  *
- * PARAMETERS:  obj_desc            - A Package object
+ * PARAMETERS:  Data                - Pointer to validation data structure
+ *              package_type        - An acpi_return_package_types value
+ *              obj_desc            - A Package object
  *
- * RETURN:      Status. AE_NULL_ENTRY means that one or more elements were
- *              removed.
+ * RETURN:      None.
  *
- * DESCRIPTION: Remove all NULL package elements and update the package count.
+ * DESCRIPTION: Remove all NULL package elements from packages that contain
+ *              a variable number of sub-packages.
  *
  *****************************************************************************/
 
-static acpi_status
-acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
+void
+acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
+                            u8 package_type,
+                            union acpi_operand_object *obj_desc)
 {
        union acpi_operand_object **source;
        union acpi_operand_object **dest;
-       acpi_status status = AE_OK;
        u32 count;
        u32 new_count;
        u32 i;
 
+       ACPI_FUNCTION_NAME(ns_remove_null_elements);
+
+       /*
+        * PTYPE1 packages contain no subpackages.
+        * PTYPE2 packages contain a variable number of sub-packages. We can
+        * safely remove all NULL elements from the PTYPE2 packages.
+        */
+       switch (package_type) {
+       case ACPI_PTYPE1_FIXED:
+       case ACPI_PTYPE1_VAR:
+       case ACPI_PTYPE1_OPTION:
+               return;
+
+       case ACPI_PTYPE2:
+       case ACPI_PTYPE2_COUNT:
+       case ACPI_PTYPE2_PKG_COUNT:
+       case ACPI_PTYPE2_FIXED:
+       case ACPI_PTYPE2_MIN:
+       case ACPI_PTYPE2_REV_FIXED:
+               break;
+
+       default:
+               return;
+       }
+
        count = obj_desc->package.count;
        new_count = count;
 
        source = obj_desc->package.elements;
        dest = source;
 
-       /* Examine all elements of the package object */
+       /* Examine all elements of the package object, remove nulls */
 
        for (i = 0; i < count; i++) {
                if (!*source) {
-                       status = AE_NULL_ENTRY;
                        new_count--;
                } else {
                        *dest = *source;
@@ -478,15 +592,18 @@ acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
                source++;
        }
 
-       if (status == AE_NULL_ENTRY) {
+       /* Update parent package if any null elements were removed */
+
+       if (new_count < count) {
+               ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+                                 "%s: Found and removed %u NULL elements\n",
+                                 data->pathname, (count - new_count)));
 
                /* NULL terminate list and update the package count */
 
                *dest = NULL;
                obj_desc->package.count = new_count;
        }
-
-       return (status);
 }
 
 /******************************************************************************
index ea55ab4f984924937feca72f00cb056d02bc1993..47d91e668a1bfc25436a0993b3e04b3397aed490 100644 (file)
@@ -671,24 +671,25 @@ acpi_ns_externalize_name(u32 internal_name_length,
 
 /*******************************************************************************
  *
- * FUNCTION:    acpi_ns_map_handle_to_node
+ * FUNCTION:    acpi_ns_validate_handle
  *
- * PARAMETERS:  Handle          - Handle to be converted to an Node
+ * PARAMETERS:  Handle          - Handle to be validated and typecast to a
+ *                                namespace node.
  *
- * RETURN:      A Name table entry pointer
+ * RETURN:      A pointer to a namespace node
  *
- * DESCRIPTION: Convert a namespace handle to a real Node
+ * DESCRIPTION: Convert a namespace handle to a namespace node. Handles special
+ *              cases for the root node.
  *
- * Note: Real integer handles would allow for more verification
+ * NOTE: Real integer handles would allow for more verification
  *       and keep all pointers within this subsystem - however this introduces
- *       more (and perhaps unnecessary) overhead.
- *
- * The current implemenation is basically a placeholder until such time comes
- * that it is needed.
+ *       more overhead and has not been necessary to this point. Drivers
+ *       holding handles are typically notified before a node becomes invalid
+ *       due to a table unload.
  *
  ******************************************************************************/
 
-struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle)
+struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle)
 {
 
        ACPI_FUNCTION_ENTRY();
@@ -708,42 +709,6 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle)
        return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
 }
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_ns_convert_entry_to_handle
- *
- * PARAMETERS:  Node          - Node to be converted to a Handle
- *
- * RETURN:      A user handle
- *
- * DESCRIPTION: Convert a real Node to a namespace handle
- *
- ******************************************************************************/
-
-acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node)
-{
-
-       /*
-        * Simple implementation for now;
-        */
-       return ((acpi_handle) node);
-
-/* Example future implementation ---------------------
-
-       if (!Node)
-       {
-               return (NULL);
-       }
-
-       if (Node == acpi_gbl_root_node)
-       {
-               return (ACPI_ROOT_OBJECT);
-       }
-
-       return ((acpi_handle) Node);
-------------------------------------------------------*/
-}
-
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ns_terminate
index f2bd1da770018dbab1da0b872d9b138abcdab268..f0c0892bc7e5c6babc8d46955d46d1d93f24bd82 100644 (file)
@@ -190,7 +190,7 @@ acpi_evaluate_object(acpi_handle handle,
 
        /* Convert and validate the device handle */
 
-       info->prefix_node = acpi_ns_map_handle_to_node(handle);
+       info->prefix_node = acpi_ns_validate_handle(handle);
        if (!info->prefix_node) {
                status = AE_BAD_PARAMETER;
                goto cleanup;
@@ -552,7 +552,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
                return (status);
        }
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
        if (ACPI_FAILURE(status)) {
                return (status);
@@ -729,7 +729,7 @@ acpi_attach_data(acpi_handle obj_handle,
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -775,7 +775,7 @@ acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler)
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -822,7 +822,7 @@ acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(obj_handle);
+       node = acpi_ns_validate_handle(obj_handle);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
index ddc84af6336e727d5eaa1ec4799e4c38e49a9333..e611dd961b200ba6f8afd6cebe23b282efe503f7 100644 (file)
@@ -93,7 +93,7 @@ acpi_get_handle(acpi_handle parent,
        /* Convert a parent handle to a prefix node */
 
        if (parent) {
-               prefix_node = acpi_ns_map_handle_to_node(parent);
+               prefix_node = acpi_ns_validate_handle(parent);
                if (!prefix_node) {
                        return (AE_BAD_PARAMETER);
                }
@@ -114,7 +114,7 @@ acpi_get_handle(acpi_handle parent,
 
                if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) {
                        *ret_handle =
-                           acpi_ns_convert_entry_to_handle(acpi_gbl_root_node);
+                           ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node);
                        return (AE_OK);
                }
        } else if (!prefix_node) {
@@ -129,7 +129,7 @@ acpi_get_handle(acpi_handle parent,
        status =
            acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node);
        if (ACPI_SUCCESS(status)) {
-               *ret_handle = acpi_ns_convert_entry_to_handle(node);
+               *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
        }
 
        return (status);
@@ -186,7 +186,7 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer)
                return (status);
        }
 
-       node = acpi_ns_map_handle_to_node(handle);
+       node = acpi_ns_validate_handle(handle);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -291,7 +291,7 @@ acpi_get_object_info(acpi_handle handle,
                goto cleanup;
        }
 
-       node = acpi_ns_map_handle_to_node(handle);
+       node = acpi_ns_validate_handle(handle);
        if (!node) {
                (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
                return (AE_BAD_PARAMETER);
index 4071bad4458ed7d229bc04e30cb02abcb8efe26b..0cc6ba01a49543c27eb669fdf662f8db31a587d5 100644 (file)
@@ -79,7 +79,7 @@ acpi_status acpi_get_id(acpi_handle handle, acpi_owner_id * ret_id)
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(handle);
+       node = acpi_ns_validate_handle(handle);
        if (!node) {
                (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
                return (AE_BAD_PARAMETER);
@@ -132,7 +132,7 @@ acpi_status acpi_get_type(acpi_handle handle, acpi_object_type * ret_type)
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(handle);
+       node = acpi_ns_validate_handle(handle);
        if (!node) {
                (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
                return (AE_BAD_PARAMETER);
@@ -182,7 +182,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
 
        /* Convert and validate the handle */
 
-       node = acpi_ns_map_handle_to_node(handle);
+       node = acpi_ns_validate_handle(handle);
        if (!node) {
                status = AE_BAD_PARAMETER;
                goto unlock_and_exit;
@@ -191,7 +191,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
        /* Get the parent entry */
 
        parent_node = acpi_ns_get_parent_node(node);
-       *ret_handle = acpi_ns_convert_entry_to_handle(parent_node);
+       *ret_handle = ACPI_CAST_PTR(acpi_handle, parent_node);
 
        /* Return exception if parent is null */
 
@@ -251,7 +251,7 @@ acpi_get_next_object(acpi_object_type type,
 
                /* Start search at the beginning of the specified scope */
 
-               parent_node = acpi_ns_map_handle_to_node(parent);
+               parent_node = acpi_ns_validate_handle(parent);
                if (!parent_node) {
                        status = AE_BAD_PARAMETER;
                        goto unlock_and_exit;
@@ -260,7 +260,7 @@ acpi_get_next_object(acpi_object_type type,
                /* Non-null handle, ignore the parent */
                /* Convert and validate the handle */
 
-               child_node = acpi_ns_map_handle_to_node(child);
+               child_node = acpi_ns_validate_handle(child);
                if (!child_node) {
                        status = AE_BAD_PARAMETER;
                        goto unlock_and_exit;
@@ -276,7 +276,7 @@ acpi_get_next_object(acpi_object_type type,
        }
 
        if (ret_handle) {
-               *ret_handle = acpi_ns_convert_entry_to_handle(node);
+               *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
        }
 
       unlock_and_exit:
index 12934ad6da8e3a1c2db36844d1a320f2a41ccc0d..d0c1b91eb8caa4731b81c71b94df969f17327127 100644 (file)
@@ -287,7 +287,8 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
        /* Invoke an internal method if necessary */
 
        if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
-               status = info->obj_desc->method.implementation(walk_state);
+               status =
+                   info->obj_desc->method.extra.implementation(walk_state);
                info->return_object = walk_state->return_desc;
 
                /* Cleanup states */
index 395212bcd19b470d756efa5328dfc9d21464c603..f27feb4772f6306b10a3a897f3f2759f5fa8c237 100644 (file)
@@ -104,7 +104,7 @@ acpi_rs_validate_parameters(acpi_handle device_handle,
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
-       node = acpi_ns_map_handle_to_node(device_handle);
+       node = acpi_ns_validate_handle(device_handle);
        if (!node) {
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
index 0f0c64bf8ac98d1cae2da0bdb073405d5f08d9e5..f857c5efb79f6d2cb2dc5d3bc9e9ae4376a51865 100644 (file)
@@ -323,11 +323,11 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type,
  * RETURN:      Status
  *
  * DESCRIPTION: This function is called to place a package object in a user
- *              buffer.  A package object by definition contains other objects.
+ *              buffer. A package object by definition contains other objects.
  *
  *              The buffer is assumed to have sufficient space for the object.
- *              The caller must have verified the buffer length needed using the
- *              acpi_ut_get_object_size function before calling this function.
+ *              The caller must have verified the buffer length needed using
+ *              the acpi_ut_get_object_size function before calling this function.
  *
  ******************************************************************************/
 
@@ -382,12 +382,12 @@ acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object,
  * FUNCTION:    acpi_ut_copy_iobject_to_eobject
  *
  * PARAMETERS:  internal_object     - The internal object to be converted
- *              buffer_ptr          - Where the object is returned
+ *              ret_buffer          - Where the object is returned
  *
  * RETURN:      Status
  *
- * DESCRIPTION: This function is called to build an API object to be returned to
- *              the caller.
+ * DESCRIPTION: This function is called to build an API object to be returned
+ *              to the caller.
  *
  ******************************************************************************/
 
@@ -626,7 +626,7 @@ acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object,
  * PARAMETERS:  external_object     - The external object to be converted
  *              internal_object     - Where the internal object is returned
  *
- * RETURN:      Status              - the status of the call
+ * RETURN:      Status
  *
  * DESCRIPTION: Converts an external object to an internal object.
  *
@@ -665,7 +665,7 @@ acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object,
  *
  * RETURN:      Status
  *
- * DESCRIPTION: Simple copy of one internal object to another.  Reference count
+ * DESCRIPTION: Simple copy of one internal object to another. Reference count
  *              of the destination object is preserved.
  *
  ******************************************************************************/
@@ -897,10 +897,11 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type,
  *
  * FUNCTION:    acpi_ut_copy_ipackage_to_ipackage
  *
- * PARAMETERS:  *source_obj     - Pointer to the source package object
- *              *dest_obj       - Where the internal object is returned
+ * PARAMETERS:  source_obj      - Pointer to the source package object
+ *              dest_obj        - Where the internal object is returned
+ *              walk_state      - Current Walk state descriptor
  *
- * RETURN:      Status          - the status of the call
+ * RETURN:      Status
  *
  * DESCRIPTION: This function is called to copy an internal package object
  *              into another internal package object.
@@ -953,9 +954,9 @@ acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj,
  *
  * FUNCTION:    acpi_ut_copy_iobject_to_iobject
  *
- * PARAMETERS:  walk_state          - Current walk state
- *              source_desc         - The internal object to be copied
+ * PARAMETERS:  source_desc         - The internal object to be copied
  *              dest_desc           - Where the copied object is returned
+ *              walk_state          - Current walk state
  *
  * RETURN:      Status
  *
index 0c9c6a9a002cb397867269e7bc44f56d4bd9f9bd..8a95e8329df732891b260efe67ad1c1855538be8 100644 (file)
@@ -282,6 +282,13 @@ static int acpi_lid_send_state(struct acpi_device *device)
        if (ret == NOTIFY_DONE)
                ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
                                                   device);
+       if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
+               /*
+                * It is also regarded as success if the notifier_chain
+                * returns NOTIFY_OK or NOTIFY_DONE.
+                */
+               ret = 0;
+       }
        return ret;
 }
 
index 8a690c3b8e234734df9ec63d1667c98671bc621a..cc421b7ae166c4ffe6a005c73b3f2714a36db2b9 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/moduleparam.h>
+#include <linux/debugfs.h>
 #include <asm/uaccess.h>
 #include <acpi/acpi_drivers.h>
 
@@ -195,6 +196,80 @@ static int param_get_trace_state(char *buffer, struct kernel_param *kp)
 module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
                  NULL, 0644);
 
+/* --------------------------------------------------------------------------
+                               DebugFS Interface
+   -------------------------------------------------------------------------- */
+
+static ssize_t cm_write(struct file *file, const char __user *user_buf,
+                       size_t count, loff_t *ppos)
+{
+       static char *buf;
+       static int uncopied_bytes;
+       struct acpi_table_header table;
+       acpi_status status;
+
+       if (!(*ppos)) {
+               /* parse the table header to get the table length */
+               if (count <= sizeof(struct acpi_table_header))
+                       return -EINVAL;
+               if (copy_from_user(&table, user_buf,
+                       sizeof(struct acpi_table_header)))
+                       return -EFAULT;
+               uncopied_bytes = table.length;
+               buf = kzalloc(uncopied_bytes, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+       }
+
+       if (uncopied_bytes < count) {
+               kfree(buf);
+               return -EINVAL;
+       }
+
+       if (copy_from_user(buf + (*ppos), user_buf, count)) {
+               kfree(buf);
+               return -EFAULT;
+       }
+
+       uncopied_bytes -= count;
+       *ppos += count;
+
+       if (!uncopied_bytes) {
+               status = acpi_install_method(buf);
+               kfree(buf);
+               if (ACPI_FAILURE(status))
+                       return -EINVAL;
+               add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+       }
+
+       return count;
+}
+
+static const struct file_operations cm_fops = {
+       .write = cm_write,
+};
+
+static int acpi_debugfs_init(void)
+{
+       struct dentry *acpi_dir, *cm_dentry;
+
+       acpi_dir = debugfs_create_dir("acpi", NULL);
+       if (!acpi_dir)
+               goto err;
+
+       cm_dentry = debugfs_create_file("custom_method", S_IWUGO,
+                                       acpi_dir, NULL, &cm_fops);
+       if (!cm_dentry)
+               goto err;
+
+       return 0;
+
+err:
+       if (acpi_dir)
+               debugfs_remove(acpi_dir);
+       return -EINVAL;
+}
+
 /* --------------------------------------------------------------------------
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
@@ -286,7 +361,7 @@ static const struct file_operations acpi_system_debug_proc_fops = {
 };
 #endif
 
-int __init acpi_debug_init(void)
+int __init acpi_procfs_init(void)
 {
 #ifdef CONFIG_ACPI_PROCFS
        struct proc_dir_entry *entry;
@@ -321,3 +396,10 @@ int __init acpi_debug_init(void)
        return 0;
 #endif
 }
+
+int __init acpi_debug_init(void)
+{
+       acpi_debugfs_init();
+       acpi_procfs_init();
+       return 0;
+}
index 30be3c148f7eadd29f7ab67338d3953054b9f323..bbc2c1315c473ca7c7e8ff3770506ba645f16f0f 100644 (file)
@@ -50,7 +50,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
        " before undocking");
 
 static struct atomic_notifier_head dock_notifier_list;
-static char dock_device_name[] = "dock";
 
 static const struct acpi_device_id dock_device_ids[] = {
        {"LNXDOCK", 0},
@@ -93,40 +92,30 @@ struct dock_dependent_device {
  *                         Dock Dependent device functions                   *
  *****************************************************************************/
 /**
- *  alloc_dock_dependent_device - allocate and init a dependent device
- *  @handle: the acpi_handle of the dependent device
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @handle: handle of the dependent device
  *
- *  Allocate memory for a dependent device structure for a device referenced
- *  by the acpi handle
+ * Add the dependent device to the dock's dependent device list.
  */
-static struct dock_dependent_device *
-alloc_dock_dependent_device(acpi_handle handle)
+static int
+add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
 {
        struct dock_dependent_device *dd;
 
        dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-       if (dd) {
-               dd->handle = handle;
-               INIT_LIST_HEAD(&dd->list);
-               INIT_LIST_HEAD(&dd->hotplug_list);
-       }
-       return dd;
-}
+       if (!dd)
+               return -ENOMEM;
+
+       dd->handle = handle;
+       INIT_LIST_HEAD(&dd->list);
+       INIT_LIST_HEAD(&dd->hotplug_list);
 
-/**
- * add_dock_dependent_device - associate a device with the dock station
- * @ds: The dock station
- * @dd: The dependent device
- *
- * Add the dependent device to the dock's dependent device list.
- */
-static void
-add_dock_dependent_device(struct dock_station *ds,
-                         struct dock_dependent_device *dd)
-{
        spin_lock(&ds->dd_lock);
        list_add_tail(&dd->list, &ds->dependent_devices);
        spin_unlock(&ds->dd_lock);
+
+       return 0;
 }
 
 /**
@@ -249,6 +238,7 @@ static int is_battery(acpi_handle handle)
 static int is_ejectable_bay(acpi_handle handle)
 {
        acpi_handle phandle;
+
        if (!is_ejectable(handle))
                return 0;
        if (is_battery(handle) || is_ata(handle))
@@ -275,14 +265,13 @@ int is_dock_device(acpi_handle handle)
 
        if (is_dock(handle))
                return 1;
-       list_for_each_entry(dock_station, &dock_stations, sibling) {
+
+       list_for_each_entry(dock_station, &dock_stations, sibling)
                if (find_dock_dependent_device(dock_station, handle))
                        return 1;
-       }
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(is_dock_device);
 
 /**
@@ -305,8 +294,6 @@ static int dock_present(struct dock_station *ds)
        return 0;
 }
 
-
-
 /**
  * dock_create_acpi_device - add new devices to acpi
  * @handle - handle of the device to add
@@ -320,7 +307,7 @@ static int dock_present(struct dock_station *ds)
  */
 static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
 {
-       struct acpi_device *device = NULL;
+       struct acpi_device *device;
        struct acpi_device *parent_device;
        acpi_handle parent;
        int ret;
@@ -337,8 +324,7 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
                ret = acpi_bus_add(&device, parent_device, handle,
                        ACPI_BUS_TYPE_DEVICE);
                if (ret) {
-                       pr_debug("error adding bus, %x\n",
-                               -ret);
+                       pr_debug("error adding bus, %x\n", -ret);
                        return NULL;
                }
        }
@@ -364,7 +350,6 @@ static void dock_remove_acpi_device(acpi_handle handle)
        }
 }
 
-
 /**
  * hotplug_dock_devices - insert or remove devices on the dock station
  * @ds: the dock station
@@ -384,10 +369,9 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
        /*
         * First call driver specific hotplug functions
         */
-       list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+       list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
                if (dd->ops && dd->ops->handler)
                        dd->ops->handler(dd->handle, event, dd->context);
-       }
 
        /*
         * Now make sure that an acpi_device is created for each
@@ -426,6 +410,7 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
        list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
                if (dd->ops && dd->ops->uevent)
                        dd->ops->uevent(dd->handle, event, dd->context);
+
        if (num != DOCK_EVENT)
                kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 }
@@ -456,8 +441,8 @@ static void eject_dock(struct dock_station *ds)
        arg.type = ACPI_TYPE_INTEGER;
        arg.integer.value = 1;
 
-       if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
-                                             &arg_list, NULL)))
+       status = acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL);
+       if (ACPI_FAILURE(status))
                pr_debug("Failed to evaluate _EJ0!\n");
 }
 
@@ -577,7 +562,6 @@ int register_dock_notifier(struct notifier_block *nb)
 
        return atomic_notifier_chain_register(&dock_notifier_list, nb);
 }
-
 EXPORT_SYMBOL_GPL(register_dock_notifier);
 
 /**
@@ -591,7 +575,6 @@ void unregister_dock_notifier(struct notifier_block *nb)
 
        atomic_notifier_chain_unregister(&dock_notifier_list, nb);
 }
-
 EXPORT_SYMBOL_GPL(unregister_dock_notifier);
 
 /**
@@ -636,7 +619,6 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
 
        return ret;
 }
-
 EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
 
 /**
@@ -657,7 +639,6 @@ void unregister_hotplug_dock_device(acpi_handle handle)
                        dock_del_hotplug_device(dock_station, dd);
        }
 }
-
 EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
 
 /**
@@ -772,7 +753,7 @@ struct dock_data {
 
 static void acpi_dock_deferred_cb(void *context)
 {
-       struct dock_data *data = (struct dock_data *)context;
+       struct dock_data *data = context;
 
        dock_notify(data->handle, data->event, data->ds);
        kfree(data);
@@ -782,23 +763,22 @@ static int acpi_dock_notifier_call(struct notifier_block *this,
        unsigned long event, void *data)
 {
        struct dock_station *dock_station;
-       acpi_handle handle = (acpi_handle)data;
+       acpi_handle handle = data;
 
        if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
           && event != ACPI_NOTIFY_EJECT_REQUEST)
                return 0;
        list_for_each_entry(dock_station, &dock_stations, sibling) {
                if (dock_station->handle == handle) {
-                       struct dock_data *dock_data;
+                       struct dock_data *dd;
 
-                       dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
-                       if (!dock_data)
+                       dd = kmalloc(sizeof(*dd), GFP_KERNEL);
+                       if (!dd)
                                return 0;
-                       dock_data->handle = handle;
-                       dock_data->event = event;
-                       dock_data->ds = dock_station;
-                       acpi_os_hotplug_execute(acpi_dock_deferred_cb,
-                               dock_data);
+                       dd->handle = handle;
+                       dd->event = event;
+                       dd->ds = dock_station;
+                       acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd);
                        return 0 ;
                }
        }
@@ -826,7 +806,6 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
        acpi_status status;
        acpi_handle tmp, parent;
        struct dock_station *ds = context;
-       struct dock_dependent_device *dd;
 
        status = acpi_bus_get_ejd(handle, &tmp);
        if (ACPI_FAILURE(status)) {
@@ -840,11 +819,9 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
                        goto fdd_out;
        }
 
-       if (tmp == ds->handle) {
-               dd = alloc_dock_dependent_device(handle);
-               if (dd)
-                       add_dock_dependent_device(ds, dd);
-       }
+       if (tmp == ds->handle)
+               add_dock_dependent_device(ds, handle);
+
 fdd_out:
        return AE_OK;
 }
@@ -857,8 +834,7 @@ static ssize_t show_docked(struct device *dev,
 {
        struct acpi_device *tmp;
 
-       struct dock_station *dock_station = *((struct dock_station **)
-               dev->platform_data);
+       struct dock_station *dock_station = dev->platform_data;
 
        if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
                return snprintf(buf, PAGE_SIZE, "1\n");
@@ -872,8 +848,7 @@ static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
 static ssize_t show_flags(struct device *dev,
                          struct device_attribute *attr, char *buf)
 {
-       struct dock_station *dock_station = *((struct dock_station **)
-               dev->platform_data);
+       struct dock_station *dock_station = dev->platform_data;
        return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
 
 }
@@ -886,8 +861,7 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
 {
        int ret;
-       struct dock_station *dock_station = *((struct dock_station **)
-               dev->platform_data);
+       struct dock_station *dock_station = dev->platform_data;
 
        if (!count)
                return -EINVAL;
@@ -905,8 +879,7 @@ static ssize_t show_dock_uid(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
        unsigned long long lbuf;
-       struct dock_station *dock_station = *((struct dock_station **)
-               dev->platform_data);
+       struct dock_station *dock_station = dev->platform_data;
        acpi_status status = acpi_evaluate_integer(dock_station->handle,
                                        "_UID", NULL, &lbuf);
        if (ACPI_FAILURE(status))
@@ -919,8 +892,7 @@ static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
 static ssize_t show_dock_type(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       struct dock_station *dock_station = *((struct dock_station **)
-               dev->platform_data);
+       struct dock_station *dock_station = dev->platform_data;
        char *type;
 
        if (dock_station->flags & DOCK_IS_DOCK)
@@ -936,6 +908,19 @@ static ssize_t show_dock_type(struct device *dev,
 }
 static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
 
+static struct attribute *dock_attributes[] = {
+       &dev_attr_docked.attr,
+       &dev_attr_flags.attr,
+       &dev_attr_undock.attr,
+       &dev_attr_uid.attr,
+       &dev_attr_type.attr,
+       NULL
+};
+
+static struct attribute_group dock_attribute_group = {
+       .attrs = dock_attributes
+};
+
 /**
  * dock_add - add a new dock station
  * @handle: the dock station handle
@@ -945,39 +930,30 @@ static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
  */
 static int dock_add(acpi_handle handle)
 {
-       int ret;
-       struct dock_dependent_device *dd;
-       struct dock_station *dock_station;
-       struct platform_device *dock_device;
+       int ret, id;
+       struct dock_station ds, *dock_station;
+       struct platform_device *dd;
+
+       id = dock_station_count;
+       dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
+       if (IS_ERR(dd))
+               return PTR_ERR(dd);
+
+       dock_station = dd->dev.platform_data;
 
-       /* allocate & initialize the dock_station private data */
-       dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
-       if (!dock_station)
-               return -ENOMEM;
        dock_station->handle = handle;
+       dock_station->dock_device = dd;
        dock_station->last_dock_time = jiffies - HZ;
-       INIT_LIST_HEAD(&dock_station->dependent_devices);
-       INIT_LIST_HEAD(&dock_station->hotplug_devices);
-       INIT_LIST_HEAD(&dock_station->sibling);
-       spin_lock_init(&dock_station->dd_lock);
+
        mutex_init(&dock_station->hp_lock);
+       spin_lock_init(&dock_station->dd_lock);
+       INIT_LIST_HEAD(&dock_station->sibling);
+       INIT_LIST_HEAD(&dock_station->hotplug_devices);
        ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
-
-       /* initialize platform device stuff */
-       dock_station->dock_device =
-               platform_device_register_simple(dock_device_name,
-                       dock_station_count, NULL, 0);
-       dock_device = dock_station->dock_device;
-       if (IS_ERR(dock_device)) {
-               kfree(dock_station);
-               dock_station = NULL;
-               return PTR_ERR(dock_device);
-       }
-       platform_device_add_data(dock_device, &dock_station,
-               sizeof(struct dock_station *));
+       INIT_LIST_HEAD(&dock_station->dependent_devices);
 
        /* we want the dock device to send uevents */
-       dev_set_uevent_suppress(&dock_device->dev, 0);
+       dev_set_uevent_suppress(&dd->dev, 0);
 
        if (is_dock(handle))
                dock_station->flags |= DOCK_IS_DOCK;
@@ -986,47 +962,9 @@ static int dock_add(acpi_handle handle)
        if (is_battery(handle))
                dock_station->flags |= DOCK_IS_BAT;
 
-       ret = device_create_file(&dock_device->dev, &dev_attr_docked);
-       if (ret) {
-               printk(KERN_ERR "Error %d adding sysfs file\n", ret);
-               platform_device_unregister(dock_device);
-               kfree(dock_station);
-               dock_station = NULL;
-               return ret;
-       }
-       ret = device_create_file(&dock_device->dev, &dev_attr_undock);
-       if (ret) {
-               printk(KERN_ERR "Error %d adding sysfs file\n", ret);
-               device_remove_file(&dock_device->dev, &dev_attr_docked);
-               platform_device_unregister(dock_device);
-               kfree(dock_station);
-               dock_station = NULL;
-               return ret;
-       }
-       ret = device_create_file(&dock_device->dev, &dev_attr_uid);
-       if (ret) {
-               printk(KERN_ERR "Error %d adding sysfs file\n", ret);
-               device_remove_file(&dock_device->dev, &dev_attr_docked);
-               device_remove_file(&dock_device->dev, &dev_attr_undock);
-               platform_device_unregister(dock_device);
-               kfree(dock_station);
-               dock_station = NULL;
-               return ret;
-       }
-       ret = device_create_file(&dock_device->dev, &dev_attr_flags);
-       if (ret) {
-               printk(KERN_ERR "Error %d adding sysfs file\n", ret);
-               device_remove_file(&dock_device->dev, &dev_attr_docked);
-               device_remove_file(&dock_device->dev, &dev_attr_undock);
-               device_remove_file(&dock_device->dev, &dev_attr_uid);
-               platform_device_unregister(dock_device);
-               kfree(dock_station);
-               dock_station = NULL;
-               return ret;
-       }
-       ret = device_create_file(&dock_device->dev, &dev_attr_type);
+       ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
        if (ret)
-               printk(KERN_ERR"Error %d adding sysfs file\n", ret);
+               goto err_unregister;
 
        /* Find dependent devices */
        acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
@@ -1034,58 +972,43 @@ static int dock_add(acpi_handle handle)
                            dock_station, NULL);
 
        /* add the dock station as a device dependent on itself */
-       dd = alloc_dock_dependent_device(handle);
-       if (!dd) {
-               kfree(dock_station);
-               dock_station = NULL;
-               ret = -ENOMEM;
-               goto dock_add_err_unregister;
-       }
-       add_dock_dependent_device(dock_station, dd);
+       ret = add_dock_dependent_device(dock_station, handle);
+       if (ret)
+               goto err_rmgroup;
 
        dock_station_count++;
        list_add(&dock_station->sibling, &dock_stations);
        return 0;
 
-dock_add_err_unregister:
-       device_remove_file(&dock_device->dev, &dev_attr_type);
-       device_remove_file(&dock_device->dev, &dev_attr_docked);
-       device_remove_file(&dock_device->dev, &dev_attr_undock);
-       device_remove_file(&dock_device->dev, &dev_attr_uid);
-       device_remove_file(&dock_device->dev, &dev_attr_flags);
-       platform_device_unregister(dock_device);
-       kfree(dock_station);
-       dock_station = NULL;
+err_rmgroup:
+       sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
+err_unregister:
+       platform_device_unregister(dd);
+       printk(KERN_ERR "%s encountered error %d\n", __func__, ret);
        return ret;
 }
 
 /**
  * dock_remove - free up resources related to the dock station
  */
-static int dock_remove(struct dock_station *dock_station)
+static int dock_remove(struct dock_station *ds)
 {
        struct dock_dependent_device *dd, *tmp;
-       struct platform_device *dock_device = dock_station->dock_device;
+       struct platform_device *dock_device = ds->dock_device;
 
        if (!dock_station_count)
                return 0;
 
        /* remove dependent devices */
-       list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
-                                list)
-           kfree(dd);
+       list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list)
+               kfree(dd);
+
+       list_del(&ds->sibling);
 
        /* cleanup sysfs */
-       device_remove_file(&dock_device->dev, &dev_attr_type);
-       device_remove_file(&dock_device->dev, &dev_attr_docked);
-       device_remove_file(&dock_device->dev, &dev_attr_undock);
-       device_remove_file(&dock_device->dev, &dev_attr_uid);
-       device_remove_file(&dock_device->dev, &dev_attr_flags);
+       sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
        platform_device_unregister(dock_device);
 
-       /* free dock station memory */
-       kfree(dock_station);
-       dock_station = NULL;
        return 0;
 }
 
@@ -1103,11 +1026,10 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
        acpi_status status = AE_OK;
 
-       if (is_dock(handle)) {
-               if (dock_add(handle) >= 0) {
+       if (is_dock(handle))
+               if (dock_add(handle) >= 0)
                        status = AE_CTRL_TERMINATE;
-               }
-       }
+
        return status;
 }
 
@@ -1145,8 +1067,7 @@ static int __init dock_init(void)
 
 static void __exit dock_exit(void)
 {
-       struct dock_station *dock_station;
-       struct dock_station *tmp;
+       struct dock_station *tmp, *dock_station;
 
        unregister_acpi_bus_notifier(&dock_acpi_notifier);
        list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
index cb4283f5a79d298280537fd471de3e3114c53f42..65b9f29ef10b2e66aefb14bec35f642630c226cc 100644 (file)
@@ -722,7 +722,7 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event)
        switch (event) {
        case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
                saved = pr->performance_platform_limit;
-               acpi_processor_ppc_has_changed(pr);
+               acpi_processor_ppc_has_changed(pr, 1);
                if (saved == pr->performance_platform_limit)
                        break;
                acpi_bus_generate_proc_event(device, event,
@@ -758,7 +758,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
        struct acpi_processor *pr = per_cpu(processors, cpu);
 
        if (action == CPU_ONLINE && pr) {
-               acpi_processor_ppc_has_changed(pr);
+               acpi_processor_ppc_has_changed(pr, 0);
                acpi_processor_cst_has_changed(pr);
                acpi_processor_tstate_has_changed(pr);
        }
@@ -830,7 +830,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
        arch_acpi_processor_cleanup_pdc(pr);
 
 #ifdef CONFIG_CPU_FREQ
-       acpi_processor_ppc_has_changed(pr);
+       acpi_processor_ppc_has_changed(pr, 0);
 #endif
        acpi_processor_get_throttling_info(pr);
        acpi_processor_get_limit_info(pr);
index 01e366d2b6fb14475e0b6074b886fd8b55877348..2cabadcc4d8c36806bc886080a6c0cae3d50dd04 100644 (file)
@@ -152,15 +152,59 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
        return 0;
 }
 
-int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE      0x80
+/*
+ * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status
+ * @handle: ACPI processor handle
+ * @status: the status code of _PPC evaluation
+ *     0: success. OSPM is now using the performance state specificed.
+ *     1: failure. OSPM has not changed the number of P-states in use
+ */
+static void acpi_processor_ppc_ost(acpi_handle handle, int status)
+{
+       union acpi_object params[2] = {
+               {.type = ACPI_TYPE_INTEGER,},
+               {.type = ACPI_TYPE_INTEGER,},
+       };
+       struct acpi_object_list arg_list = {2, params};
+       acpi_handle temp;
+
+       params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE;
+       params[1].integer.value =  status;
+
+       /* when there is no _OST , skip it */
+       if (ACPI_FAILURE(acpi_get_handle(handle, "_OST", &temp)))
+               return;
+
+       acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
+       return;
+}
+
+int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
 {
        int ret;
 
-       if (ignore_ppc)
+       if (ignore_ppc) {
+               /*
+                * Only when it is notification event, the _OST object
+                * will be evaluated. Otherwise it is skipped.
+                */
+               if (event_flag)
+                       acpi_processor_ppc_ost(pr->handle, 1);
                return 0;
+       }
 
        ret = acpi_processor_get_platform_limit(pr);
-
+       /*
+        * Only when it is notification event, the _OST object
+        * will be evaluated. Otherwise it is skipped.
+        */
+       if (event_flag) {
+               if (ret < 0)
+                       acpi_processor_ppc_ost(pr->handle, 1);
+               else
+                       acpi_processor_ppc_ost(pr->handle, 0);
+       }
        if (ret < 0)
                return (ret);
        else
index 65f67815902a2d18c0f1df09e48210af86002568..9073ada888354ac7cff6f0b8d38803495b5f763d 100644 (file)
@@ -1052,6 +1052,13 @@ static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
                                   acpi_device_bid(device));
                }
                seq_puts(seq, "\n");
+       } else {
+               seq_printf(seq, "passive (forced):");
+               if (tz->thermal_zone->forced_passive)
+                       seq_printf(seq, "        %i C\n",
+                                  tz->thermal_zone->forced_passive / 1000);
+               else
+                       seq_printf(seq, "<not set>\n");
        }
 
        for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {
index d2e698096ace182698152e2366d7c2dc2cf91342..679cd08b80b4444ed02d640793251aaa25806f6e 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/dmi.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
+#include <linux/pnp.h>
 
 #ifdef CONFIG_PPC_OF
 #include <linux/of_device.h>
@@ -1919,7 +1920,7 @@ struct SPMITable {
        s8      spmi_id[1]; /* A '\0' terminated array starts here. */
 };
 
-static __devinit int try_init_acpi(struct SPMITable *spmi)
+static __devinit int try_init_spmi(struct SPMITable *spmi)
 {
        struct smi_info  *info;
        u8               addr_space;
@@ -1940,7 +1941,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
                return -ENOMEM;
        }
 
-       info->addr_source = "ACPI";
+       info->addr_source = "SPMI";
 
        /* Figure out the interface type. */
        switch (spmi->InterfaceType) {
@@ -2002,7 +2003,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
        return 0;
 }
 
-static __devinit void acpi_find_bmc(void)
+static __devinit void spmi_find_bmc(void)
 {
        acpi_status      status;
        struct SPMITable *spmi;
@@ -2020,9 +2021,106 @@ static __devinit void acpi_find_bmc(void)
                if (status != AE_OK)
                        return;
 
-               try_init_acpi(spmi);
+               try_init_spmi(spmi);
        }
 }
+
+static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
+                                   const struct pnp_device_id *dev_id)
+{
+       struct acpi_device *acpi_dev;
+       struct smi_info *info;
+       acpi_handle handle;
+       acpi_status status;
+       unsigned long long tmp;
+
+       acpi_dev = pnp_acpi_device(dev);
+       if (!acpi_dev)
+               return -ENODEV;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->addr_source = "ACPI";
+
+       handle = acpi_dev->handle;
+
+       /* _IFT tells us the interface type: KCS, BT, etc */
+       status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
+       if (ACPI_FAILURE(status))
+               goto err_free;
+
+       switch (tmp) {
+       case 1:
+               info->si_type = SI_KCS;
+               break;
+       case 2:
+               info->si_type = SI_SMIC;
+               break;
+       case 3:
+               info->si_type = SI_BT;
+               break;
+       default:
+               dev_info(&dev->dev, "unknown interface type %lld\n", tmp);
+               goto err_free;
+       }
+
+       if (pnp_port_valid(dev, 0)) {
+               info->io_setup = port_setup;
+               info->io.addr_type = IPMI_IO_ADDR_SPACE;
+               info->io.addr_data = pnp_port_start(dev, 0);
+       } else if (pnp_mem_valid(dev, 0)) {
+               info->io_setup = mem_setup;
+               info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+               info->io.addr_data = pnp_mem_start(dev, 0);
+       } else {
+               dev_err(&dev->dev, "no I/O or memory address\n");
+               goto err_free;
+       }
+
+       info->io.regspacing = DEFAULT_REGSPACING;
+       info->io.regsize = DEFAULT_REGSPACING;
+       info->io.regshift = 0;
+
+       /* If _GPE exists, use it; otherwise use standard interrupts */
+       status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
+       if (ACPI_SUCCESS(status)) {
+               info->irq = tmp;
+               info->irq_setup = acpi_gpe_irq_setup;
+       } else if (pnp_irq_valid(dev, 0)) {
+               info->irq = pnp_irq(dev, 0);
+               info->irq_setup = std_irq_setup;
+       }
+
+       info->dev = &acpi_dev->dev;
+       pnp_set_drvdata(dev, info);
+
+       return try_smi_init(info);
+
+err_free:
+       kfree(info);
+       return -EINVAL;
+}
+
+static void __devexit ipmi_pnp_remove(struct pnp_dev *dev)
+{
+       struct smi_info *info = pnp_get_drvdata(dev);
+
+       cleanup_one_si(info);
+}
+
+static const struct pnp_device_id pnp_dev_table[] = {
+       {"IPI0001", 0},
+       {"", 0},
+};
+
+static struct pnp_driver ipmi_pnp_driver = {
+       .name           = DEVICE_NAME,
+       .probe          = ipmi_pnp_probe,
+       .remove         = __devexit_p(ipmi_pnp_remove),
+       .id_table       = pnp_dev_table,
+};
 #endif
 
 #ifdef CONFIG_DMI
@@ -2202,7 +2300,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        int rv;
        int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
        struct smi_info *info;
-       int first_reg_offset = 0;
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
@@ -2241,9 +2338,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
        info->addr_source_cleanup = ipmi_pci_cleanup;
        info->addr_source_data = pdev;
 
-       if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID)
-               first_reg_offset = 1;
-
        if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
                info->io_setup = port_setup;
                info->io.addr_type = IPMI_IO_ADDR_SPACE;
@@ -3108,7 +3202,10 @@ static __devinit int init_ipmi_si(void)
 #endif
 
 #ifdef CONFIG_ACPI
-       acpi_find_bmc();
+       spmi_find_bmc();
+#endif
+#ifdef CONFIG_PNP
+       pnp_register_driver(&ipmi_pnp_driver);
 #endif
 
 #ifdef CONFIG_PCI
@@ -3233,6 +3330,9 @@ static __exit void cleanup_ipmi_si(void)
 #ifdef CONFIG_PCI
        pci_unregister_driver(&ipmi_pci_driver);
 #endif
+#ifdef CONFIG_PNP
+       pnp_unregister_driver(&ipmi_pnp_driver);
+#endif
 
 #ifdef CONFIG_PPC_OF
        of_unregister_platform_driver(&ipmi_of_platform_driver);
index 59f4ba1b7034b8336969a37532066f399993e96f..1a7a9fc50ea1019fc74965daf6de40fff65b0f47 100644 (file)
@@ -248,19 +248,6 @@ config SGI_GRU_DEBUG
        This option enables addition debugging code for the SGI GRU driver. If
        you are unsure, say N.
 
-config DELL_LAPTOP
-       tristate "Dell Laptop Extras (EXPERIMENTAL)"
-       depends on X86
-       depends on DCDBAS
-       depends on EXPERIMENTAL
-       depends on BACKLIGHT_CLASS_DEVICE
-       depends on RFKILL
-       depends on POWER_SUPPLY
-       default n
-       ---help---
-       This driver adds support for rfkill and backlight control to Dell
-       laptops.
-
 config ISL29003
        tristate "Intersil ISL29003 ambient light sensor"
        depends on I2C && SYSFS
index a006dec3b3efd3850921759bde835bb58159778f..ba3638fdf0ceae9466585e4c3d64de5fd096e0c2 100644 (file)
@@ -334,6 +334,8 @@ config EEEPC_LAPTOP
        depends on HOTPLUG_PCI
        select BACKLIGHT_CLASS_DEVICE
        select HWMON
+       select LEDS_CLASS
+       select NEW_LEDS
        ---help---
          This driver supports the Fn-Fx keys on Eee PC laptops.
 
index b39d2bb3e75b897ddfc48e2e8856f118d93aa95e..61a1c75036582b2f2cb968a757b95c556f94082b 100644 (file)
@@ -221,6 +221,7 @@ static struct asus_hotk *hotk;
  */
 static const struct acpi_device_id asus_device_ids[] = {
        {"ATK0100", 0},
+       {"ATK0101", 0},
        {"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, asus_device_ids);
@@ -232,6 +233,7 @@ static void asus_hotk_notify(struct acpi_device *device, u32 event);
 static struct acpi_driver asus_hotk_driver = {
        .name = ASUS_HOTK_NAME,
        .class = ASUS_HOTK_CLASS,
+       .owner = THIS_MODULE,
        .ids = asus_device_ids,
        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
@@ -293,6 +295,11 @@ struct key_entry {
 enum { KE_KEY, KE_END };
 
 static struct key_entry asus_keymap[] = {
+       {KE_KEY, 0x02, KEY_SCREENLOCK},
+       {KE_KEY, 0x05, KEY_WLAN},
+       {KE_KEY, 0x08, KEY_F13},
+       {KE_KEY, 0x17, KEY_ZOOM},
+       {KE_KEY, 0x1f, KEY_BATTERY},
        {KE_KEY, 0x30, KEY_VOLUMEUP},
        {KE_KEY, 0x31, KEY_VOLUMEDOWN},
        {KE_KEY, 0x32, KEY_MUTE},
@@ -312,8 +319,11 @@ static struct key_entry asus_keymap[] = {
        {KE_KEY, 0x5F, KEY_WLAN},
        {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
        {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
-       {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
+       {KE_KEY, 0x62, KEY_SWITCHVIDEOMODE},
+       {KE_KEY, 0x63, KEY_SWITCHVIDEOMODE},
+       {KE_KEY, 0x6B, KEY_F13}, /* Lock Touchpad */
        {KE_KEY, 0x82, KEY_CAMERA},
+       {KE_KEY, 0x88, KEY_WLAN },
        {KE_KEY, 0x8A, KEY_PROG1},
        {KE_KEY, 0x95, KEY_MEDIA},
        {KE_KEY, 0x99, KEY_PHONE},
@@ -1240,9 +1250,6 @@ static int asus_hotk_add(struct acpi_device *device)
 {
        int result;
 
-       if (!device)
-               return -EINVAL;
-
        pr_notice("Asus Laptop Support version %s\n",
               ASUS_LAPTOP_VERSION);
 
@@ -1283,8 +1290,8 @@ static int asus_hotk_add(struct acpi_device *device)
        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 */
+       hotk->light_switch = 0; /* Default to light sensor disabled */
+       hotk->light_level = 5;  /* level 5 for sensor sensitivity */
 
        if (ls_switch_handle)
                set_light_sens_switch(hotk->light_switch);
@@ -1306,9 +1313,6 @@ end:
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
        kfree(hotk->name);
        kfree(hotk);
 
@@ -1444,9 +1448,6 @@ static int __init asus_laptop_init(void)
 {
        int result;
 
-       if (acpi_disabled)
-               return -ENODEV;
-
        result = acpi_bus_register_driver(&asus_hotk_driver);
        if (result < 0)
                return result;
index ddf5240ade8cc0e10358553382ffa68716933ce0..0c9c53111a22edaa9ede19af2b71c3c2aab6c408 100644 (file)
@@ -466,6 +466,7 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 static struct acpi_driver asus_hotk_driver = {
        .name = "asus_acpi",
        .class = ACPI_HOTK_CLASS,
+       .owner = THIS_MODULE,
        .ids = asus_device_ids,
        .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
        .ops = {
@@ -1334,9 +1335,6 @@ static int asus_hotk_add(struct acpi_device *device)
        acpi_status status = AE_OK;
        int result;
 
-       if (!device)
-               return -EINVAL;
-
        printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
               ASUS_ACPI_VERSION);
 
@@ -1392,9 +1390,6 @@ end:
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
-
        asus_hotk_remove_fs(device);
 
        kfree(hotk);
@@ -1422,21 +1417,17 @@ static int __init asus_acpi_init(void)
 {
        int result;
 
-       if (acpi_disabled)
-               return -ENODEV;
+       result = acpi_bus_register_driver(&asus_hotk_driver);
+       if (result < 0)
+               return result;
 
        asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
        if (!asus_proc_dir) {
                printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
+               acpi_bus_unregister_driver(&asus_hotk_driver);
                return -ENODEV;
        }
 
-       result = acpi_bus_register_driver(&asus_hotk_driver);
-       if (result < 0) {
-               remove_proc_entry(PROC_ASUS, acpi_root_dir);
-               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
index 74909c4aaeea8777bd8edd668dc10602705ff186..3780994dc8f2a533795397cce09aba310d950b0a 100644 (file)
@@ -58,6 +58,14 @@ static int da_command_code;
 static int da_num_tokens;
 static struct calling_interface_token *da_tokens;
 
+static struct platform_driver platform_driver = {
+       .driver = {
+               .name = "dell-laptop",
+               .owner = THIS_MODULE,
+       }
+};
+
+static struct platform_device *platform_device;
 static struct backlight_device *dell_backlight_device;
 static struct rfkill *wifi_rfkill;
 static struct rfkill *bluetooth_rfkill;
@@ -74,7 +82,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
        { }
 };
 
-static void parse_da_table(const struct dmi_header *dm)
+static void __init parse_da_table(const struct dmi_header *dm)
 {
        /* Final token is a terminator, so we don't want to copy it */
        int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
@@ -103,7 +111,7 @@ static void parse_da_table(const struct dmi_header *dm)
        da_num_tokens += tokens;
 }
 
-static void find_tokens(const struct dmi_header *dm, void *dummy)
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 {
        switch (dm->type) {
        case 0xd4: /* Indexed IO */
@@ -197,8 +205,8 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
        dell_send_request(&buffer, 17, 11);
        status = buffer.output[1];
 
-       if (status & BIT(bit))
-               rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
+       rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
+       rfkill_set_hw_state(rfkill, !(status & BIT(16)));
 }
 
 static const struct rfkill_ops dell_rfkill_ops = {
@@ -206,7 +214,7 @@ static const struct rfkill_ops dell_rfkill_ops = {
        .query = dell_rfkill_query,
 };
 
-static int dell_setup_rfkill(void)
+static int __init dell_setup_rfkill(void)
 {
        struct calling_interface_buffer buffer;
        int status;
@@ -217,7 +225,8 @@ static int dell_setup_rfkill(void)
        status = buffer.output[1];
 
        if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
-               wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
+               wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
+                                          RFKILL_TYPE_WLAN,
                                           &dell_rfkill_ops, (void *) 1);
                if (!wifi_rfkill) {
                        ret = -ENOMEM;
@@ -229,7 +238,8 @@ static int dell_setup_rfkill(void)
        }
 
        if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
-               bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
+               bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
+                                               &platform_device->dev,
                                                RFKILL_TYPE_BLUETOOTH,
                                                &dell_rfkill_ops, (void *) 2);
                if (!bluetooth_rfkill) {
@@ -242,7 +252,9 @@ static int dell_setup_rfkill(void)
        }
 
        if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
-               wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
+               wwan_rfkill = rfkill_alloc("dell-wwan",
+                                          &platform_device->dev,
+                                          RFKILL_TYPE_WWAN,
                                           &dell_rfkill_ops, (void *) 3);
                if (!wwan_rfkill) {
                        ret = -ENOMEM;
@@ -268,6 +280,22 @@ err_wifi:
        return ret;
 }
 
+static void dell_cleanup_rfkill(void)
+{
+       if (wifi_rfkill) {
+               rfkill_unregister(wifi_rfkill);
+               rfkill_destroy(wifi_rfkill);
+       }
+       if (bluetooth_rfkill) {
+               rfkill_unregister(bluetooth_rfkill);
+               rfkill_destroy(bluetooth_rfkill);
+       }
+       if (wwan_rfkill) {
+               rfkill_unregister(wwan_rfkill);
+               rfkill_destroy(wwan_rfkill);
+       }
+}
+
 static int dell_send_intensity(struct backlight_device *bd)
 {
        struct calling_interface_buffer buffer;
@@ -326,11 +354,23 @@ static int __init dell_init(void)
                return -ENODEV;
        }
 
+       ret = platform_driver_register(&platform_driver);
+       if (ret)
+               goto fail_platform_driver;
+       platform_device = platform_device_alloc("dell-laptop", -1);
+       if (!platform_device) {
+               ret = -ENOMEM;
+               goto fail_platform_device1;
+       }
+       ret = platform_device_add(platform_device);
+       if (ret)
+               goto fail_platform_device2;
+
        ret = dell_setup_rfkill();
 
        if (ret) {
                printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
-               goto out;
+               goto fail_rfkill;
        }
 
 #ifdef CONFIG_ACPI
@@ -352,13 +392,13 @@ static int __init dell_init(void)
        if (max_intensity) {
                dell_backlight_device = backlight_device_register(
                        "dell_backlight",
-                       NULL, NULL,
+                       &platform_device->dev, NULL,
                        &dell_ops);
 
                if (IS_ERR(dell_backlight_device)) {
                        ret = PTR_ERR(dell_backlight_device);
                        dell_backlight_device = NULL;
-                       goto out;
+                       goto fail_backlight;
                }
 
                dell_backlight_device->props.max_brightness = max_intensity;
@@ -368,13 +408,16 @@ static int __init dell_init(void)
        }
 
        return 0;
-out:
-       if (wifi_rfkill)
-               rfkill_unregister(wifi_rfkill);
-       if (bluetooth_rfkill)
-               rfkill_unregister(bluetooth_rfkill);
-       if (wwan_rfkill)
-               rfkill_unregister(wwan_rfkill);
+
+fail_backlight:
+       dell_cleanup_rfkill();
+fail_rfkill:
+       platform_device_del(platform_device);
+fail_platform_device2:
+       platform_device_put(platform_device);
+fail_platform_device1:
+       platform_driver_unregister(&platform_driver);
+fail_platform_driver:
        kfree(da_tokens);
        return ret;
 }
@@ -382,12 +425,7 @@ out:
 static void __exit dell_exit(void)
 {
        backlight_device_unregister(dell_backlight_device);
-       if (wifi_rfkill)
-               rfkill_unregister(wifi_rfkill);
-       if (bluetooth_rfkill)
-               rfkill_unregister(bluetooth_rfkill);
-       if (wwan_rfkill)
-               rfkill_unregister(wwan_rfkill);
+       dell_cleanup_rfkill();
 }
 
 module_init(dell_init);
index 0f900cc9fa7a5131283c14b2aae8e11d7667b949..67f3fe71c509e2f1e6ceab0646507087aa888eca 100644 (file)
@@ -31,6 +31,7 @@
 #include <acpi/acpi_drivers.h>
 #include <linux/acpi.h>
 #include <linux/string.h>
+#include <linux/dmi.h>
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -38,6 +39,8 @@ MODULE_LICENSE("GPL");
 
 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
 
+static int acpi_video;
+
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
 
 struct key_entry {
@@ -54,7 +57,7 @@ enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
  * via the keyboard controller so should not be sent again.
  */
 
-static struct key_entry dell_wmi_keymap[] = {
+static struct key_entry dell_legacy_wmi_keymap[] = {
        {KE_KEY, 0xe045, KEY_PROG1},
        {KE_KEY, 0xe009, KEY_EJECTCD},
 
@@ -72,7 +75,7 @@ static struct key_entry dell_wmi_keymap[] = {
 
        /* The next device is at offset 6, the active devices are at
           offset 8 and the attached devices at offset 10 */
-       {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
+       {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
 
        {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
 
@@ -96,6 +99,47 @@ static struct key_entry dell_wmi_keymap[] = {
        {KE_END, 0}
 };
 
+static bool dell_new_hk_type;
+
+struct dell_new_keymap_entry {
+       u16 scancode;
+       u16 keycode;
+};
+
+struct dell_hotkey_table {
+       struct dmi_header header;
+       struct dell_new_keymap_entry keymap[];
+
+};
+
+static struct key_entry *dell_new_wmi_keymap;
+
+static u16 bios_to_linux_keycode[256] = {
+
+       KEY_MEDIA,      KEY_NEXTSONG,   KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
+       KEY_STOPCD,     KEY_UNKNOWN,    KEY_UNKNOWN,    KEY_UNKNOWN,
+       KEY_WWW,        KEY_UNKNOWN,    KEY_VOLUMEDOWN, KEY_MUTE,
+       KEY_VOLUMEUP,   KEY_UNKNOWN,    KEY_BATTERY,    KEY_EJECTCD,
+       KEY_UNKNOWN,    KEY_SLEEP,      KEY_PROG1, KEY_BRIGHTNESSDOWN,
+       KEY_BRIGHTNESSUP,       KEY_UNKNOWN,    KEY_KBDILLUMTOGGLE,
+       KEY_UNKNOWN,    KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN, KEY_UNKNOWN,
+       KEY_SWITCHVIDEOMODE,    KEY_UNKNOWN,    KEY_UNKNOWN, KEY_PROG2,
+       KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       KEY_PROG3
+};
+
+
+static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
+
 static struct input_dev *dell_wmi_input_dev;
 
 static struct key_entry *dell_wmi_get_entry_by_scancode(int code)
@@ -164,24 +208,78 @@ static void dell_wmi_notify(u32 value, void *context)
        obj = (union acpi_object *)response.pointer;
 
        if (obj && obj->type == ACPI_TYPE_BUFFER) {
-               int *buffer = (int *)obj->buffer.pointer;
-               /*
-                *  The upper bytes of the event may contain
-                *  additional information, so mask them off for the
-                *  scancode lookup
-                */
-               key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF);
-               if (key) {
+               int reported_key;
+               u16 *buffer_entry = (u16 *)obj->buffer.pointer;
+               if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
+                       printk(KERN_INFO "dell-wmi: Received unknown WMI event"
+                                        " (0x%x)\n", buffer_entry[1]);
+                       return;
+               }
+
+               if (dell_new_hk_type)
+                       reported_key = (int)buffer_entry[2];
+               else
+                       reported_key = (int)buffer_entry[1] & 0xffff;
+
+               key = dell_wmi_get_entry_by_scancode(reported_key);
+
+               if (!key) {
+                       printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
+                               reported_key);
+               } else if ((key->keycode == KEY_BRIGHTNESSUP ||
+                           key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
+                       /* Don't report brightness notifications that will also
+                        * come via ACPI */
+                       return;
+               } else {
                        input_report_key(dell_wmi_input_dev, key->keycode, 1);
                        input_sync(dell_wmi_input_dev);
                        input_report_key(dell_wmi_input_dev, key->keycode, 0);
                        input_sync(dell_wmi_input_dev);
-               } else if (buffer[1] & 0xFFFF)
-                       printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
-                              buffer[1] & 0xFFFF);
+               }
        }
 }
 
+
+static void setup_new_hk_map(const struct dmi_header *dm)
+{
+
+       int i;
+       int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
+       struct dell_hotkey_table *table =
+               container_of(dm, struct dell_hotkey_table, header);
+
+       dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
+                                     sizeof(struct key_entry), GFP_KERNEL);
+
+       for (i = 0; i < hotkey_num; i++) {
+               dell_new_wmi_keymap[i].type = KE_KEY;
+               dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
+               dell_new_wmi_keymap[i].keycode =
+                       (table->keymap[i].keycode > 255) ? 0 :
+                       bios_to_linux_keycode[table->keymap[i].keycode];
+       }
+
+       dell_new_wmi_keymap[i].type = KE_END;
+       dell_new_wmi_keymap[i].code = 0;
+       dell_new_wmi_keymap[i].keycode = 0;
+
+       dell_wmi_keymap = dell_new_wmi_keymap;
+
+}
+
+
+static void find_hk_type(const struct dmi_header *dm, void *dummy)
+{
+
+       if ((dm->type == 0xb2) && (dm->length > 6)) {
+               dell_new_hk_type = true;
+               setup_new_hk_map(dm);
+       }
+
+}
+
+
 static int __init dell_wmi_input_setup(void)
 {
        struct key_entry *key;
@@ -226,6 +324,9 @@ static int __init dell_wmi_init(void)
        int err;
 
        if (wmi_has_guid(DELL_EVENT_GUID)) {
+
+               dmi_walk(find_hk_type, NULL);
+
                err = dell_wmi_input_setup();
 
                if (err)
@@ -240,6 +341,8 @@ static int __init dell_wmi_init(void)
                        return err;
                }
 
+               acpi_video = acpi_video_backlight_support();
+
        } else
                printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n");
 
index e647a856b9bf684d9e57a88d4b3321edb770232b..5838c69b2fb304937da89c53cab090bf6d9a65de 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  eepc-laptop.c - Asus Eee PC extras
+ *  eeepc-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
 #include <linux/rfkill.h>
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
+#include <linux/leds.h>
 
 #define EEEPC_LAPTOP_VERSION   "0.1"
+#define EEEPC_LAPTOP_NAME      "Eee PC Hotkey Driver"
+#define EEEPC_LAPTOP_FILE      "eeepc"
 
-#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_ACPI_CLASS       "hotkey"
+#define EEEPC_ACPI_DEVICE_NAME "Hotkey"
+#define EEEPC_ACPI_HID         "ASUS010"
 
+MODULE_AUTHOR("Corentin Chary, Eric Cooper");
+MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
+MODULE_LICENSE("GPL");
 
 /*
  * Definitions for Asus EeePC
  */
-#define        NOTIFY_WLAN_ON  0x10
 #define NOTIFY_BRN_MIN 0x20
 #define NOTIFY_BRN_MAX 0x2f
 
@@ -117,58 +120,6 @@ static const char *cm_setv[] = {
        NULL, NULL, "PBPS", "TPDS"
 };
 
-#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 *wlan_rfkill;
-       struct rfkill *bluetooth_rfkill;
-       struct rfkill *wwan3g_rfkill;
-       struct rfkill *wimax_rfkill;
-       struct hotplug_slot *hotplug_slot;
-       struct mutex hotplug_lock;
-};
-
-/* The actual device the driver binds to */
-static struct eeepc_hotk *ehotk;
-
-/* Platform device/driver */
-static int eeepc_hotk_thaw(struct device *device);
-static int eeepc_hotk_restore(struct device *device);
-
-static const struct dev_pm_ops eeepc_pm_ops = {
-       .thaw = eeepc_hotk_thaw,
-       .restore = eeepc_hotk_restore,
-};
-
-static struct platform_driver platform_driver = {
-       .driver = {
-               .name = EEEPC_HOTK_FILE,
-               .owner = THIS_MODULE,
-               .pm = &eeepc_pm_ops,
-       }
-};
-
-static struct platform_device *platform_device;
-
 struct key_entry {
        char type;
        u8 code;
@@ -177,7 +128,7 @@ struct key_entry {
 
 enum { KE_KEY, KE_END };
 
-static struct key_entry eeepc_keymap[] = {
+static const struct key_entry eeepc_keymap[] = {
        /* Sleep already handled via generic ACPI code */
        {KE_KEY, 0x10, KEY_WLAN },
        {KE_KEY, 0x11, KEY_WLAN },
@@ -185,77 +136,56 @@ static struct key_entry eeepc_keymap[] = {
        {KE_KEY, 0x13, KEY_MUTE },
        {KE_KEY, 0x14, KEY_VOLUMEDOWN },
        {KE_KEY, 0x15, KEY_VOLUMEUP },
+       {KE_KEY, 0x16, KEY_DISPLAY_OFF },
        {KE_KEY, 0x1a, KEY_COFFEE },
        {KE_KEY, 0x1b, KEY_ZOOM },
        {KE_KEY, 0x1c, KEY_PROG2 },
        {KE_KEY, 0x1d, KEY_PROG3 },
-       {KE_KEY, NOTIFY_BRN_MIN,     KEY_BRIGHTNESSDOWN },
-       {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
+       {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
+       {KE_KEY, NOTIFY_BRN_MAX, KEY_BRIGHTNESSUP },
        {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
        {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
        {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
+       {KE_KEY, 0x37, KEY_F13 }, /* Disable Touchpad */
+       {KE_KEY, 0x38, KEY_F14 },
        {KE_END, 0},
 };
 
+
 /*
- * The hotkey driver declaration
+ * This is the main structure, we can use it to store useful information
  */
-static int eeepc_hotk_add(struct acpi_device *device);
-static int eeepc_hotk_remove(struct acpi_device *device, int type);
-static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
-
-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,
-       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
-       .ops = {
-               .add = eeepc_hotk_add,
-               .remove = eeepc_hotk_remove,
-               .notify = eeepc_hotk_notify,
-       },
-};
+struct eeepc_laptop {
+       acpi_handle handle;             /* the handle of the acpi device */
+       u32 cm_supported;               /* the control methods supported
+                                          by this BIOS */
+       u16 event_count[128];           /* count for each event */
 
-/* PCI hotplug ops */
-static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
+       struct platform_device *platform_device;
+       struct device *hwmon_device;
+       struct backlight_device *backlight_device;
 
-static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
-       .owner = THIS_MODULE,
-       .get_adapter_status = eeepc_get_adapter_status,
-       .get_power_status = eeepc_get_adapter_status,
-};
+       struct input_dev *inputdev;
+       struct key_entry *keymap;
 
-/* The backlight device /sys/class/backlight */
-static struct backlight_device *eeepc_backlight_device;
+       struct rfkill *wlan_rfkill;
+       struct rfkill *bluetooth_rfkill;
+       struct rfkill *wwan3g_rfkill;
+       struct rfkill *wimax_rfkill;
 
-/* The hwmon device */
-static struct device *eeepc_hwmon_device;
+       struct hotplug_slot *hotplug_slot;
+       struct mutex hotplug_lock;
 
-/*
- * 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,
+       struct led_classdev tpd_led;
+       int tpd_led_wk;
+       struct workqueue_struct *led_workqueue;
+       struct work_struct tpd_led_work;
 };
 
-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)
+static int write_acpi_int(acpi_handle handle, const char *method, int val)
 {
        struct acpi_object_list params;
        union acpi_object in_obj;
@@ -266,7 +196,7 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,
        in_obj.type = ACPI_TYPE_INTEGER;
        in_obj.integer.value = val;
 
-       status = acpi_evaluate_object(handle, (char *)method, &params, output);
+       status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
        return (status == AE_OK ? 0 : -1);
 }
 
@@ -285,81 +215,56 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val)
        }
 }
 
-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))
-                       pr_warning("Error writing %s\n", method);
-       }
-       return 0;
-}
-
-static int get_acpi(int cm)
+static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value)
 {
-       int value = -ENODEV;
-       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))
-                       pr_warning("Error reading %s\n", method);
-       }
-       return value;
-}
+       const char *method = cm_setv[cm];
 
-/*
- * Backlight
- */
-static int read_brightness(struct backlight_device *bd)
-{
-       return get_acpi(CM_ASL_PANELBRIGHT);
-}
+       if (method == NULL)
+               return -ENODEV;
+       if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+               return -ENODEV;
 
-static int set_brightness(struct backlight_device *bd, int value)
-{
-       value = max(0, min(15, value));
-       return set_acpi(CM_ASL_PANELBRIGHT, value);
+       if (write_acpi_int(eeepc->handle, method, value))
+               pr_warning("Error writing %s\n", method);
+       return 0;
 }
 
-static int update_bl_status(struct backlight_device *bd)
+static int get_acpi(struct eeepc_laptop *eeepc, int cm)
 {
-       return set_brightness(bd, bd->props.brightness);
-}
+       const char *method = cm_getv[cm];
+       int value;
 
-/*
- * Rfkill helpers
- */
+       if (method == NULL)
+               return -ENODEV;
+       if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+               return -ENODEV;
 
-static bool eeepc_wlan_rfkill_blocked(void)
-{
-       if (get_acpi(CM_ASL_WLAN) == 1)
-               return false;
-       return true;
+       if (read_acpi_int(eeepc->handle, method, &value))
+               pr_warning("Error reading %s\n", method);
+       return value;
 }
 
-static int eeepc_rfkill_set(void *data, bool blocked)
+static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
+                             acpi_handle *handle)
 {
-       unsigned long asl = (unsigned long)data;
-       return set_acpi(asl, !blocked);
-}
+       const char *method = cm_setv[cm];
+       acpi_status status;
 
-static const struct rfkill_ops eeepc_rfkill_ops = {
-       .set_block = eeepc_rfkill_set,
-};
+       if (method == NULL)
+               return -ENODEV;
+       if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+               return -ENODEV;
 
-static void __devinit eeepc_enable_camera(void)
-{
-       /*
-        * If the following call to set_acpi() fails, it's because there's no
-        * camera so we can ignore the error.
-        */
-       if (get_acpi(CM_ASL_CAMERA) == 0)
-               set_acpi(CM_ASL_CAMERA, 1);
+       status = acpi_get_handle(eeepc->handle, (char *)method,
+                                handle);
+       if (status != AE_OK) {
+               pr_warning("Error finding %s\n", method);
+               return -ENODEV;
+       }
+       return 0;
 }
 
+
 /*
  * Sys helpers
  */
@@ -372,60 +277,63 @@ static int parse_arg(const char *buf, unsigned long count, int *val)
        return count;
 }
 
-static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
+static ssize_t store_sys_acpi(struct device *dev, int cm,
+                             const char *buf, size_t count)
 {
+       struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
        int rv, value;
 
        rv = parse_arg(buf, count, &value);
        if (rv > 0)
-               value = set_acpi(cm, value);
+               value = set_acpi(eeepc, cm, value);
        if (value < 0)
-               return value;
+               return -EIO;
        return rv;
 }
 
-static ssize_t show_sys_acpi(int cm, char *buf)
+static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
 {
-       int value = get_acpi(cm);
+       struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
+       int value = get_acpi(eeepc, cm);
 
        if (value < 0)
-               return value;
+               return -EIO;
        return sprintf(buf, "%d\n", value);
 }
 
-#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm)                           \
+#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm)                    \
        static ssize_t show_##_name(struct device *dev,                 \
                                    struct device_attribute *attr,      \
                                    char *buf)                          \
        {                                                               \
-               return show_sys_acpi(_cm, buf);                         \
+               return show_sys_acpi(dev, _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);                 \
+               return store_sys_acpi(dev, _cm, buf, count);            \
        }                                                               \
        static struct device_attribute dev_attr_##_name = {             \
                .attr = {                                               \
                        .name = __stringify(_name),                     \
-                       .mode = 0644 },                                 \
+                       .mode = _mode },                                \
                .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);
+EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
+EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
+EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
 
 struct eeepc_cpufv {
        int num;
        int cur;
 };
 
-static int get_cpufv(struct eeepc_cpufv *c)
+static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
 {
-       c->cur = get_acpi(CM_ASL_CPUFV);
+       c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
        c->num = (c->cur >> 8) & 0xff;
        c->cur &= 0xff;
        if (c->cur < 0 || c->num <= 0 || c->num > 12)
@@ -437,11 +345,12 @@ static ssize_t show_available_cpufv(struct device *dev,
                                    struct device_attribute *attr,
                                    char *buf)
 {
+       struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
        struct eeepc_cpufv c;
        int i;
        ssize_t len = 0;
 
-       if (get_cpufv(&c))
+       if (get_cpufv(eeepc, &c))
                return -ENODEV;
        for (i = 0; i < c.num; i++)
                len += sprintf(buf + len, "%d ", i);
@@ -453,9 +362,10 @@ static ssize_t show_cpufv(struct device *dev,
                          struct device_attribute *attr,
                          char *buf)
 {
+       struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
        struct eeepc_cpufv c;
 
-       if (get_cpufv(&c))
+       if (get_cpufv(eeepc, &c))
                return -ENODEV;
        return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
 }
@@ -464,17 +374,18 @@ static ssize_t store_cpufv(struct device *dev,
                           struct device_attribute *attr,
                           const char *buf, size_t count)
 {
+       struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
        struct eeepc_cpufv c;
        int rv, value;
 
-       if (get_cpufv(&c))
+       if (get_cpufv(eeepc, &c))
                return -ENODEV;
        rv = parse_arg(buf, count, &value);
        if (rv < 0)
                return rv;
        if (!rv || value < 0 || value >= c.num)
                return -EINVAL;
-       set_acpi(CM_ASL_CPUFV, value);
+       set_acpi(eeepc, CM_ASL_CPUFV, value);
        return rv;
 }
 
@@ -506,156 +417,125 @@ static struct attribute_group platform_attribute_group = {
        .attrs = platform_attributes
 };
 
-/*
- * Hotkey functions
- */
-static struct key_entry *eepc_get_entry_by_scancode(int code)
+static int eeepc_platform_init(struct eeepc_laptop *eeepc)
 {
-       struct key_entry *key;
-
-       for (key = eeepc_keymap; key->type != KE_END; key++)
-               if (code == key->code)
-                       return key;
+       int result;
 
-       return NULL;
-}
+       eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
+       if (!eeepc->platform_device)
+               return -ENOMEM;
+       platform_set_drvdata(eeepc->platform_device, eeepc);
 
-static struct key_entry *eepc_get_entry_by_keycode(int code)
-{
-       struct key_entry *key;
+       result = platform_device_add(eeepc->platform_device);
+       if (result)
+               goto fail_platform_device;
 
-       for (key = eeepc_keymap; key->type != KE_END; key++)
-               if (code == key->keycode && key->type == KE_KEY)
-                       return key;
+       result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
+                                   &platform_attribute_group);
+       if (result)
+               goto fail_sysfs;
+       return 0;
 
-       return NULL;
+fail_sysfs:
+       platform_device_del(eeepc->platform_device);
+fail_platform_device:
+       platform_device_put(eeepc->platform_device);
+       return result;
 }
 
-static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
 {
-       struct key_entry *key = eepc_get_entry_by_scancode(scancode);
+       sysfs_remove_group(&eeepc->platform_device->dev.kobj,
+                          &platform_attribute_group);
+       platform_device_unregister(eeepc->platform_device);
+}
 
-       if (key && key->type == KE_KEY) {
-               *keycode = key->keycode;
-               return 0;
-       }
+/*
+ * LEDs
+ */
+/*
+ * 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 void tpd_led_update(struct work_struct *work)
+ {
+       struct eeepc_laptop *eeepc;
 
-       return -EINVAL;
+       eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
+
+       set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
 }
 
-static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+static void tpd_led_set(struct led_classdev *led_cdev,
+                       enum led_brightness value)
 {
-       struct key_entry *key;
-       int old_keycode;
-
-       if (keycode < 0 || keycode > KEY_MAX)
-               return -EINVAL;
+       struct eeepc_laptop *eeepc;
 
-       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;
-       }
+       eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
 
-       return -EINVAL;
+       eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
+       queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
 }
 
-static void cmsg_quirk(int cm, const char *name)
+static int eeepc_led_init(struct eeepc_laptop *eeepc)
 {
-       int dummy;
+       int rv;
 
-       /* Some BIOSes do not report cm although it is avaliable.
-          Check if cm_getv[cm] works and, if yes, assume cm should be set. */
-       if (!(ehotk->cm_supported & (1 << cm))
-           && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
-               pr_info("%s (%x) not reported by BIOS,"
-                       " enabling anyway\n", name, 1 << cm);
-               ehotk->cm_supported |= 1 << cm;
-       }
-}
+       if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
+               return 0;
 
-static void cmsg_quirks(void)
-{
-       cmsg_quirk(CM_ASL_LID, "LID");
-       cmsg_quirk(CM_ASL_TYPE, "TYPE");
-       cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
-       cmsg_quirk(CM_ASL_TPD, "TPD");
-}
+       eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
+       if (!eeepc->led_workqueue)
+               return -ENOMEM;
+       INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
 
-static int eeepc_hotk_check(void)
-{
-       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       int result;
+       eeepc->tpd_led.name = "eeepc::touchpad";
+       eeepc->tpd_led.brightness_set = tpd_led_set;
+       eeepc->tpd_led.max_brightness = 1;
 
-       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)) {
-                       pr_err("Hotkey initialization failed\n");
-                       return -ENODEV;
-               } else {
-                       pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
-               }
-               /* get control methods supported */
-               if (read_acpi_int(ehotk->handle, "CMSG"
-                                  , &ehotk->cm_supported)) {
-                       pr_err("Get control methods supported failed\n");
-                       return -ENODEV;
-               } else {
-                       cmsg_quirks();
-                       pr_info("Get control methods supported: 0x%x\n",
-                               ehotk->cm_supported);
-               }
-       } else {
-               pr_err("Hotkey device not present, aborting\n");
-               return -EINVAL;
+       rv = led_classdev_register(&eeepc->platform_device->dev,
+                                  &eeepc->tpd_led);
+       if (rv) {
+               destroy_workqueue(eeepc->led_workqueue);
+               return rv;
        }
+
        return 0;
 }
 
-static int notify_brn(void)
+static void eeepc_led_exit(struct eeepc_laptop *eeepc)
 {
-       /* returns the *previous* brightness, or -1 */
-       struct backlight_device *bd = eeepc_backlight_device;
-       if (bd) {
-               int old = bd->props.brightness;
-               backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
-               return old;
-       }
-       return -1;
+       if (eeepc->tpd_led.dev)
+               led_classdev_unregister(&eeepc->tpd_led);
+       if (eeepc->led_workqueue)
+               destroy_workqueue(eeepc->led_workqueue);
 }
 
-static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
-                                   u8 *value)
-{
-       int val = get_acpi(CM_ASL_WLAN);
-
-       if (val == 1 || val == 0)
-               *value = val;
-       else
-               return -EINVAL;
 
-       return 0;
+/*
+ * PCI hotplug (for wlan rfkill)
+ */
+static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
+{
+       if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
+               return false;
+       return true;
 }
 
-static void eeepc_rfkill_hotplug(void)
+static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
 {
        struct pci_dev *dev;
        struct pci_bus *bus;
-       bool blocked = eeepc_wlan_rfkill_blocked();
+       bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
 
-       if (ehotk->wlan_rfkill)
-               rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
+       if (eeepc->wlan_rfkill)
+               rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
 
-       mutex_lock(&ehotk->hotplug_lock);
+       mutex_lock(&eeepc->hotplug_lock);
 
-       if (ehotk->hotplug_slot) {
+       if (eeepc->hotplug_slot) {
                bus = pci_find_bus(0, 1);
                if (!bus) {
                        pr_warning("Unable to find PCI bus 1?\n");
@@ -685,69 +565,23 @@ static void eeepc_rfkill_hotplug(void)
        }
 
 out_unlock:
-       mutex_unlock(&ehotk->hotplug_lock);
+       mutex_unlock(&eeepc->hotplug_lock);
 }
 
 static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
 {
+       struct eeepc_laptop *eeepc = data;
+
        if (event != ACPI_NOTIFY_BUS_CHECK)
                return;
 
-       eeepc_rfkill_hotplug();
-}
-
-static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
-{
-       static struct key_entry *key;
-       u16 count;
-       int brn = -ENODEV;
-
-       if (!ehotk)
-               return;
-       if (event > ACPI_MAX_SYS_NOTIFY)
-               return;
-       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
-               brn = notify_brn();
-       count = ehotk->event_count[event % 128]++;
-       acpi_bus_generate_proc_event(ehotk->device, event, count);
-       acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class,
-                                       dev_name(&ehotk->device->dev), event,
-                                       count);
-       if (ehotk->inputdev) {
-               if (brn != -ENODEV) {
-                       /* brightness-change events need special
-                        * handling for conversion to key events
-                        */
-                       if (brn < 0)
-                               brn = event;
-                       else
-                               brn += NOTIFY_BRN_MIN;
-                       if (event < brn)
-                               event = NOTIFY_BRN_MIN; /* brightness down */
-                       else if (event > brn)
-                               event = NOTIFY_BRN_MIN + 2; /* ... up */
-                       else
-                               event = NOTIFY_BRN_MIN + 1; /* ... unchanged */
-               }
-               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;
-                       }
-               }
-       }
+       eeepc_rfkill_hotplug(eeepc);
 }
 
-static int eeepc_register_rfkill_notifier(char *node)
+static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
+                                         char *node)
 {
-       acpi_status status = AE_OK;
+       acpi_status status;
        acpi_handle handle;
 
        status = acpi_get_handle(NULL, node, &handle);
@@ -756,7 +590,7 @@ static int eeepc_register_rfkill_notifier(char *node)
                status = acpi_install_notify_handler(handle,
                                                     ACPI_SYSTEM_NOTIFY,
                                                     eeepc_rfkill_notify,
-                                                    NULL);
+                                                    eeepc);
                if (ACPI_FAILURE(status))
                        pr_warning("Failed to register notify on %s\n", node);
        } else
@@ -765,7 +599,8 @@ static int eeepc_register_rfkill_notifier(char *node)
        return 0;
 }
 
-static void eeepc_unregister_rfkill_notifier(char *node)
+static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
+                                            char *node)
 {
        acpi_status status = AE_OK;
        acpi_handle handle;
@@ -782,13 +617,33 @@ static void eeepc_unregister_rfkill_notifier(char *node)
        }
 }
 
+static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
+                                   u8 *value)
+{
+       struct eeepc_laptop *eeepc = hotplug_slot->private;
+       int val = get_acpi(eeepc, CM_ASL_WLAN);
+
+       if (val == 1 || val == 0)
+               *value = val;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
 static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
 {
        kfree(hotplug_slot->info);
        kfree(hotplug_slot);
 }
 
-static int eeepc_setup_pci_hotplug(void)
+static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
+       .owner = THIS_MODULE,
+       .get_adapter_status = eeepc_get_adapter_status,
+       .get_power_status = eeepc_get_adapter_status,
+};
+
+static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
 {
        int ret = -ENOMEM;
        struct pci_bus *bus = pci_find_bus(0, 1);
@@ -798,22 +653,22 @@ static int eeepc_setup_pci_hotplug(void)
                return -ENODEV;
        }
 
-       ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
-       if (!ehotk->hotplug_slot)
+       eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+       if (!eeepc->hotplug_slot)
                goto error_slot;
 
-       ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+       eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
                                            GFP_KERNEL);
-       if (!ehotk->hotplug_slot->info)
+       if (!eeepc->hotplug_slot->info)
                goto error_info;
 
-       ehotk->hotplug_slot->private = ehotk;
-       ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
-       ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
-       eeepc_get_adapter_status(ehotk->hotplug_slot,
-                                &ehotk->hotplug_slot->info->adapter_status);
+       eeepc->hotplug_slot->private = eeepc;
+       eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
+       eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
+       eeepc_get_adapter_status(eeepc->hotplug_slot,
+                                &eeepc->hotplug_slot->info->adapter_status);
 
-       ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
+       ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
        if (ret) {
                pr_err("Unable to register hotplug slot - %d\n", ret);
                goto error_register;
@@ -822,97 +677,262 @@ static int eeepc_setup_pci_hotplug(void)
        return 0;
 
 error_register:
-       kfree(ehotk->hotplug_slot->info);
+       kfree(eeepc->hotplug_slot->info);
 error_info:
-       kfree(ehotk->hotplug_slot);
-       ehotk->hotplug_slot = NULL;
+       kfree(eeepc->hotplug_slot);
+       eeepc->hotplug_slot = NULL;
 error_slot:
        return ret;
 }
 
-static int eeepc_hotk_thaw(struct device *device)
+/*
+ * Rfkill devices
+ */
+static int eeepc_rfkill_set(void *data, bool blocked)
 {
-       if (ehotk->wlan_rfkill) {
-               bool wlan;
-
-               /*
-                * Work around bios bug - acpi _PTS turns off the wireless led
-                * during suspend.  Normally it restores it on resume, but
-                * we should kick it ourselves in case hibernation is aborted.
-                */
-               wlan = get_acpi(CM_ASL_WLAN);
-               set_acpi(CM_ASL_WLAN, wlan);
-       }
+       acpi_handle handle = data;
 
-       return 0;
+       return write_acpi_int(handle, NULL, !blocked);
 }
 
-static int eeepc_hotk_restore(struct device *device)
-{
-       /* Refresh both wlan rfkill state and pci hotplug */
-       if (ehotk->wlan_rfkill)
-               eeepc_rfkill_hotplug();
-
-       if (ehotk->bluetooth_rfkill)
-               rfkill_set_sw_state(ehotk->bluetooth_rfkill,
-                                   get_acpi(CM_ASL_BLUETOOTH) != 1);
-       if (ehotk->wwan3g_rfkill)
-               rfkill_set_sw_state(ehotk->wwan3g_rfkill,
-                                   get_acpi(CM_ASL_3G) != 1);
-       if (ehotk->wimax_rfkill)
-               rfkill_set_sw_state(ehotk->wimax_rfkill,
-                                   get_acpi(CM_ASL_WIMAX) != 1);
+static const struct rfkill_ops eeepc_rfkill_ops = {
+       .set_block = eeepc_rfkill_set,
+};
+
+static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
+                           struct rfkill **rfkill,
+                           const char *name,
+                           enum rfkill_type type, int cm)
+{
+       acpi_handle handle;
+       int result;
+
+       result = acpi_setter_handle(eeepc, cm, &handle);
+       if (result < 0)
+               return result;
+
+       *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
+                              &eeepc_rfkill_ops, handle);
 
+       if (!*rfkill)
+               return -EINVAL;
+
+       rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
+       result = rfkill_register(*rfkill);
+       if (result) {
+               rfkill_destroy(*rfkill);
+               *rfkill = NULL;
+               return result;
+       }
        return 0;
 }
 
+static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
+{
+       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
+       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
+       eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       if (eeepc->wlan_rfkill) {
+               rfkill_unregister(eeepc->wlan_rfkill);
+               rfkill_destroy(eeepc->wlan_rfkill);
+               eeepc->wlan_rfkill = NULL;
+       }
+       /*
+        * Refresh pci hotplug in case the rfkill state was changed after
+        * eeepc_unregister_rfkill_notifier()
+        */
+       eeepc_rfkill_hotplug(eeepc);
+       if (eeepc->hotplug_slot)
+               pci_hp_deregister(eeepc->hotplug_slot);
+
+       if (eeepc->bluetooth_rfkill) {
+               rfkill_unregister(eeepc->bluetooth_rfkill);
+               rfkill_destroy(eeepc->bluetooth_rfkill);
+               eeepc->bluetooth_rfkill = NULL;
+       }
+       if (eeepc->wwan3g_rfkill) {
+               rfkill_unregister(eeepc->wwan3g_rfkill);
+               rfkill_destroy(eeepc->wwan3g_rfkill);
+               eeepc->wwan3g_rfkill = NULL;
+       }
+       if (eeepc->wimax_rfkill) {
+               rfkill_unregister(eeepc->wimax_rfkill);
+               rfkill_destroy(eeepc->wimax_rfkill);
+               eeepc->wimax_rfkill = NULL;
+       }
+}
+
+static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
+{
+       int result = 0;
+
+       mutex_init(&eeepc->hotplug_lock);
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
+                                 "eeepc-wlan", RFKILL_TYPE_WLAN,
+                                 CM_ASL_WLAN);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
+                                 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
+                                 CM_ASL_BLUETOOTH);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
+                                 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
+                                 CM_ASL_3G);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
+                                 "eeepc-wimax", RFKILL_TYPE_WIMAX,
+                                 CM_ASL_WIMAX);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_setup_pci_hotplug(eeepc);
+       /*
+        * If we get -EBUSY then something else is handling the PCI hotplug -
+        * don't fail in this case
+        */
+       if (result == -EBUSY)
+               result = 0;
+
+       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
+       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
+       eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+       /*
+        * Refresh pci hotplug in case the rfkill state was changed during
+        * setup.
+        */
+       eeepc_rfkill_hotplug(eeepc);
+
+exit:
+       if (result && result != -ENODEV)
+               eeepc_rfkill_exit(eeepc);
+       return result;
+}
+
 /*
- * Hwmon
+ * Platform driver - hibernate/resume callbacks
  */
+static int eeepc_hotk_thaw(struct device *device)
+{
+       struct eeepc_laptop *eeepc = dev_get_drvdata(device);
+
+       if (eeepc->wlan_rfkill) {
+               bool wlan;
+
+               /*
+                * Work around bios bug - acpi _PTS turns off the wireless led
+                * during suspend.  Normally it restores it on resume, but
+                * we should kick it ourselves in case hibernation is aborted.
+                */
+               wlan = get_acpi(eeepc, CM_ASL_WLAN);
+               set_acpi(eeepc, CM_ASL_WLAN, wlan);
+       }
+
+       return 0;
+}
+
+static int eeepc_hotk_restore(struct device *device)
+{
+       struct eeepc_laptop *eeepc = dev_get_drvdata(device);
+
+       /* Refresh both wlan rfkill state and pci hotplug */
+       if (eeepc->wlan_rfkill)
+               eeepc_rfkill_hotplug(eeepc);
+
+       if (eeepc->bluetooth_rfkill)
+               rfkill_set_sw_state(eeepc->bluetooth_rfkill,
+                                   get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1);
+       if (eeepc->wwan3g_rfkill)
+               rfkill_set_sw_state(eeepc->wwan3g_rfkill,
+                                   get_acpi(eeepc, CM_ASL_3G) != 1);
+       if (eeepc->wimax_rfkill)
+               rfkill_set_sw_state(eeepc->wimax_rfkill,
+                                   get_acpi(eeepc, CM_ASL_WIMAX) != 1);
+
+       return 0;
+}
+
+static const struct dev_pm_ops eeepc_pm_ops = {
+       .thaw = eeepc_hotk_thaw,
+       .restore = eeepc_hotk_restore,
+};
+
+static struct platform_driver platform_driver = {
+       .driver = {
+               .name = EEEPC_LAPTOP_FILE,
+               .owner = THIS_MODULE,
+               .pm = &eeepc_pm_ops,
+       }
+};
+
+/*
+ * Hwmon device
+ */
+
+#define EEEPC_EC_SC00      0x61
+#define EEEPC_EC_FAN_PWM   (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
+#define EEEPC_EC_FAN_HRPM  (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_LRPM  (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
+
+#define EEEPC_EC_SFB0      0xD0
+#define EEEPC_EC_FAN_CTRL  (EEEPC_EC_SFB0 + 3) /* Byte containing SF25  */
+
 static int eeepc_get_fan_pwm(void)
 {
-       int value = 0;
+       u8 value = 0;
 
-       read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
-       value = value * 255 / 100;
-       return (value);
+       ec_read(EEEPC_EC_FAN_PWM, &value);
+       return value * 255 / 100;
 }
 
 static void eeepc_set_fan_pwm(int value)
 {
        value = SENSORS_LIMIT(value, 0, 255);
        value = value * 100 / 255;
-       ec_write(EEEPC_EC_SC02, value);
+       ec_write(EEEPC_EC_FAN_PWM, value);
 }
 
 static int eeepc_get_fan_rpm(void)
 {
-       int high = 0;
-       int low = 0;
+       u8 high = 0;
+       u8 low = 0;
 
-       read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
-       read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
-       return (high << 8 | low);
+       ec_read(EEEPC_EC_FAN_HRPM, &high);
+       ec_read(EEEPC_EC_FAN_LRPM, &low);
+       return high << 8 | low;
 }
 
 static int eeepc_get_fan_ctrl(void)
 {
-       int value = 0;
+       u8 value = 0;
 
-       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
-       return ((value & 0x02 ? 1 : 0));
+       ec_read(EEEPC_EC_FAN_CTRL, &value);
+       if (value & 0x02)
+               return 1; /* manual */
+       else
+               return 2; /* automatic */
 }
 
 static void eeepc_set_fan_ctrl(int manual)
 {
-       int value = 0;
+       u8 value = 0;
 
-       read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
-       if (manual)
+       ec_read(EEEPC_EC_FAN_CTRL, &value);
+       if (manual == 1)
                value |= 0x02;
        else
                value &= ~0x02;
-       ec_write(EEEPC_EC_SFB3, value);
+       ec_write(EEEPC_EC_FAN_CTRL, value);
 }
 
 static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
@@ -970,348 +990,485 @@ static struct attribute_group hwmon_attribute_group = {
        .attrs = hwmon_attributes
 };
 
-/*
- * exit/init
- */
-static void eeepc_backlight_exit(void)
+static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
 {
-       if (eeepc_backlight_device)
-               backlight_device_unregister(eeepc_backlight_device);
-       eeepc_backlight_device = NULL;
+       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 eeepc_rfkill_exit(void)
+static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
 {
-       eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
-       eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
-       eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
-       if (ehotk->wlan_rfkill) {
-               rfkill_unregister(ehotk->wlan_rfkill);
-               rfkill_destroy(ehotk->wlan_rfkill);
-               ehotk->wlan_rfkill = NULL;
-       }
-       /*
-        * Refresh pci hotplug in case the rfkill state was changed after
-        * eeepc_unregister_rfkill_notifier()
-        */
-       eeepc_rfkill_hotplug();
-       if (ehotk->hotplug_slot)
-               pci_hp_deregister(ehotk->hotplug_slot);
-
-       if (ehotk->bluetooth_rfkill) {
-               rfkill_unregister(ehotk->bluetooth_rfkill);
-               rfkill_destroy(ehotk->bluetooth_rfkill);
-               ehotk->bluetooth_rfkill = NULL;
-       }
-       if (ehotk->wwan3g_rfkill) {
-               rfkill_unregister(ehotk->wwan3g_rfkill);
-               rfkill_destroy(ehotk->wwan3g_rfkill);
-               ehotk->wwan3g_rfkill = NULL;
-       }
-       if (ehotk->wimax_rfkill) {
-               rfkill_unregister(ehotk->wimax_rfkill);
-               rfkill_destroy(ehotk->wimax_rfkill);
-               ehotk->wimax_rfkill = NULL;
+       struct device *hwmon;
+       int result;
+
+       hwmon = hwmon_device_register(&eeepc->platform_device->dev);
+       if (IS_ERR(hwmon)) {
+               pr_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(eeepc);
+       return result;
 }
 
-static void eeepc_input_exit(void)
+/*
+ * Backlight device
+ */
+static int read_brightness(struct backlight_device *bd)
 {
-       if (ehotk->inputdev)
-               input_unregister_device(ehotk->inputdev);
+       struct eeepc_laptop *eeepc = bl_get_data(bd);
+
+       return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
 }
 
-static void eeepc_hwmon_exit(void)
+static int set_brightness(struct backlight_device *bd, int value)
 {
-       struct device *hwmon;
+       struct eeepc_laptop *eeepc = bl_get_data(bd);
 
-       hwmon = eeepc_hwmon_device;
-       if (!hwmon)
-               return ;
-       sysfs_remove_group(&hwmon->kobj,
-                          &hwmon_attribute_group);
-       hwmon_device_unregister(hwmon);
-       eeepc_hwmon_device = NULL;
+       return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
 }
 
-static int eeepc_new_rfkill(struct rfkill **rfkill,
-                           const char *name, struct device *dev,
-                           enum rfkill_type type, int cm)
+static int update_bl_status(struct backlight_device *bd)
 {
-       int result;
+       return set_brightness(bd, bd->props.brightness);
+}
 
-       result = get_acpi(cm);
-       if (result < 0)
-               return result;
+static struct backlight_ops eeepcbl_ops = {
+       .get_brightness = read_brightness,
+       .update_status = update_bl_status,
+};
 
-       *rfkill = rfkill_alloc(name, dev, type,
-                              &eeepc_rfkill_ops, (void *)(unsigned long)cm);
+static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
+{
+       struct backlight_device *bd = eeepc->backlight_device;
+       int old = bd->props.brightness;
 
-       if (!*rfkill)
-               return -EINVAL;
+       backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
 
-       rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
-       result = rfkill_register(*rfkill);
-       if (result) {
-               rfkill_destroy(*rfkill);
-               *rfkill = NULL;
-               return result;
-       }
-       return 0;
+       return old;
 }
 
-
-static int eeepc_rfkill_init(struct device *dev)
+static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
 {
-       int result = 0;
-
-       mutex_init(&ehotk->hotplug_lock);
+       struct backlight_device *bd;
 
-       result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
-                                 "eeepc-wlan", dev,
-                                 RFKILL_TYPE_WLAN, CM_ASL_WLAN);
+       bd = backlight_device_register(EEEPC_LAPTOP_FILE,
+                                      &eeepc->platform_device->dev,
+                                      eeepc, &eeepcbl_ops);
+       if (IS_ERR(bd)) {
+               pr_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(bd);
+       bd->props.power = FB_BLANK_UNBLANK;
+       backlight_update_status(bd);
+       return 0;
+}
 
-       if (result && result != -ENODEV)
-               goto exit;
+static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
+{
+       if (eeepc->backlight_device)
+               backlight_device_unregister(eeepc->backlight_device);
+       eeepc->backlight_device = NULL;
+}
 
-       result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
-                                 "eeepc-bluetooth", dev,
-                                 RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
 
-       if (result && result != -ENODEV)
-               goto exit;
+/*
+ * Input device (i.e. hotkeys)
+ */
+static struct key_entry *eeepc_get_entry_by_scancode(
+       struct eeepc_laptop *eeepc,
+       int code)
+{
+       struct key_entry *key;
 
-       result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
-                                 "eeepc-wwan3g", dev,
-                                 RFKILL_TYPE_WWAN, CM_ASL_3G);
+       for (key = eeepc->keymap; key->type != KE_END; key++)
+               if (code == key->code)
+                       return key;
 
-       if (result && result != -ENODEV)
-               goto exit;
+       return NULL;
+}
 
-       result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
-                                 "eeepc-wimax", dev,
-                                 RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
+static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
+{
+       static struct key_entry *key;
 
-       if (result && result != -ENODEV)
-               goto exit;
+       key = eeepc_get_entry_by_scancode(eeepc, event);
+       if (key) {
+               switch (key->type) {
+               case KE_KEY:
+                       input_report_key(eeepc->inputdev, key->keycode,
+                                               1);
+                       input_sync(eeepc->inputdev);
+                       input_report_key(eeepc->inputdev, key->keycode,
+                                               0);
+                       input_sync(eeepc->inputdev);
+                       break;
+               }
+       }
+}
 
-       result = eeepc_setup_pci_hotplug();
-       /*
-        * If we get -EBUSY then something else is handling the PCI hotplug -
-        * don't fail in this case
-        */
-       if (result == -EBUSY)
-               result = 0;
+static struct key_entry *eeepc_get_entry_by_keycode(
+       struct eeepc_laptop *eeepc, int code)
+{
+       struct key_entry *key;
 
-       eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
-       eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
-       eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
-       /*
-        * Refresh pci hotplug in case the rfkill state was changed during
-        * setup.
-        */
-       eeepc_rfkill_hotplug();
+       for (key = eeepc->keymap; key->type != KE_END; key++)
+               if (code == key->keycode && key->type == KE_KEY)
+                       return key;
 
-exit:
-       if (result && result != -ENODEV)
-               eeepc_rfkill_exit();
-       return result;
+       return NULL;
 }
 
-static int eeepc_backlight_init(struct device *dev)
+static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
 {
-       struct backlight_device *bd;
+       struct eeepc_laptop *eeepc = input_get_drvdata(dev);
+       struct key_entry *key = eeepc_get_entry_by_scancode(eeepc, scancode);
 
-       bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
-                                      NULL, &eeepcbl_ops);
-       if (IS_ERR(bd)) {
-               pr_err("Could not register eeepc backlight device\n");
-               eeepc_backlight_device = NULL;
-               return PTR_ERR(bd);
+       if (key && key->type == KE_KEY) {
+               *keycode = key->keycode;
+               return 0;
        }
-       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;
+
+       return -EINVAL;
 }
 
-static int eeepc_hwmon_init(struct device *dev)
+static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
 {
-       struct device *hwmon;
-       int result;
+       struct eeepc_laptop *eeepc = input_get_drvdata(dev);
+       struct key_entry *key;
+       int old_keycode;
 
-       hwmon = hwmon_device_register(dev);
-       if (IS_ERR(hwmon)) {
-               pr_err("Could not register eeepc hwmon device\n");
-               eeepc_hwmon_device = NULL;
-               return PTR_ERR(hwmon);
+       if (keycode < 0 || keycode > KEY_MAX)
+               return -EINVAL;
+
+       key = eeepc_get_entry_by_scancode(eeepc, scancode);
+       if (key && key->type == KE_KEY) {
+               old_keycode = key->keycode;
+               key->keycode = keycode;
+               set_bit(keycode, dev->keybit);
+               if (!eeepc_get_entry_by_keycode(eeepc, old_keycode))
+                       clear_bit(old_keycode, dev->keybit);
+               return 0;
        }
-       eeepc_hwmon_device = hwmon;
-       result = sysfs_create_group(&hwmon->kobj,
-                                   &hwmon_attribute_group);
-       if (result)
-               eeepc_hwmon_exit();
-       return result;
+
+       return -EINVAL;
 }
 
-static int eeepc_input_init(struct device *dev)
+static int eeepc_input_init(struct eeepc_laptop *eeepc)
 {
        const struct key_entry *key;
        int result;
 
-       ehotk->inputdev = input_allocate_device();
-       if (!ehotk->inputdev) {
+       eeepc->inputdev = input_allocate_device();
+       if (!eeepc->inputdev) {
                pr_info("Unable to allocate input device\n");
                return -ENOMEM;
        }
-       ehotk->inputdev->name = "Asus EeePC extra buttons";
-       ehotk->inputdev->dev.parent = dev;
-       ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
-       ehotk->inputdev->id.bustype = BUS_HOST;
-       ehotk->inputdev->getkeycode = eeepc_getkeycode;
-       ehotk->inputdev->setkeycode = eeepc_setkeycode;
-
+       eeepc->inputdev->name = "Asus EeePC extra buttons";
+       eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
+       eeepc->inputdev->phys = EEEPC_LAPTOP_FILE "/input0";
+       eeepc->inputdev->id.bustype = BUS_HOST;
+       eeepc->inputdev->getkeycode = eeepc_getkeycode;
+       eeepc->inputdev->setkeycode = eeepc_setkeycode;
+       input_set_drvdata(eeepc->inputdev, eeepc);
+
+       eeepc->keymap = kmemdup(eeepc_keymap, sizeof(eeepc_keymap),
+                               GFP_KERNEL);
        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);
+                       set_bit(EV_KEY, eeepc->inputdev->evbit);
+                       set_bit(key->keycode, eeepc->inputdev->keybit);
                        break;
                }
        }
-       result = input_register_device(ehotk->inputdev);
+       result = input_register_device(eeepc->inputdev);
        if (result) {
                pr_info("Unable to register input device\n");
-               input_free_device(ehotk->inputdev);
+               input_free_device(eeepc->inputdev);
                return result;
        }
        return 0;
 }
 
-static int __devinit eeepc_hotk_add(struct acpi_device *device)
+static void eeepc_input_exit(struct eeepc_laptop *eeepc)
 {
-       struct device *dev;
+       if (eeepc->inputdev) {
+               input_unregister_device(eeepc->inputdev);
+               kfree(eeepc->keymap);
+       }
+}
+
+/*
+ * ACPI driver
+ */
+static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
+{
+       struct eeepc_laptop *eeepc = acpi_driver_data(device);
+       u16 count;
+
+       if (event > ACPI_MAX_SYS_NOTIFY)
+               return;
+       count = eeepc->event_count[event % 128]++;
+       acpi_bus_generate_proc_event(device, event, count);
+       acpi_bus_generate_netlink_event(device->pnp.device_class,
+                                       dev_name(&device->dev), event,
+                                       count);
+
+       /* Brightness events are special */
+       if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
+
+               /* Ignore them completely if the acpi video driver is used */
+               if (eeepc->backlight_device != NULL) {
+                       int old_brightness, new_brightness;
+
+                       /* Update the backlight device. */
+                       old_brightness = eeepc_backlight_notify(eeepc);
+
+                       /* Convert event to keypress (obsolescent hack) */
+                       new_brightness = event - NOTIFY_BRN_MIN;
+
+                       if (new_brightness < old_brightness) {
+                               event = NOTIFY_BRN_MIN; /* brightness down */
+                       } else if (new_brightness > old_brightness) {
+                               event = NOTIFY_BRN_MAX; /* brightness up */
+                       } else {
+                               /*
+                               * no change in brightness - already at min/max,
+                               * event will be desired value (or else ignored)
+                               */
+                       }
+                       eeepc_input_notify(eeepc, event);
+               }
+       } else {
+               /* Everything else is a bona-fide keypress event */
+               eeepc_input_notify(eeepc, event);
+       }
+}
+
+static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
+{
+       int dummy;
+
+       /* Some BIOSes do not report cm although it is avaliable.
+          Check if cm_getv[cm] works and, if yes, assume cm should be set. */
+       if (!(eeepc->cm_supported & (1 << cm))
+           && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
+               pr_info("%s (%x) not reported by BIOS,"
+                       " enabling anyway\n", name, 1 << cm);
+               eeepc->cm_supported |= 1 << cm;
+       }
+}
+
+static void cmsg_quirks(struct eeepc_laptop *eeepc)
+{
+       cmsg_quirk(eeepc, CM_ASL_LID, "LID");
+       cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
+       cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
+       cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
+}
+
+static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
+                          struct acpi_device *device)
+{
+       unsigned int init_flags;
        int result;
 
-       if (!device)
-               return -EINVAL;
-       pr_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();
+       result = acpi_bus_get_status(device);
        if (result)
-               goto fail_platform_driver;
-       eeepc_enable_camera();
+               return result;
+       if (!device->status.present) {
+               pr_err("Hotkey device not present, aborting\n");
+               return -ENODEV;
+       }
 
-       /* 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;
+       init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
+       pr_notice("Hotkey init flags 0x%x\n", init_flags);
+
+       if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
+               pr_err("Hotkey initialization failed\n");
+               return -ENODEV;
        }
-       result = platform_device_add(platform_device);
-       if (result)
-               goto fail_platform_device2;
-       result = sysfs_create_group(&platform_device->dev.kobj,
-                                   &platform_attribute_group);
+
+       /* get control methods supported */
+       if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
+               pr_err("Get control methods supported failed\n");
+               return -ENODEV;
+       }
+       cmsg_quirks(eeepc);
+       pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
+
+       return 0;
+}
+
+static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
+{
+       /*
+        * If the following call to set_acpi() fails, it's because there's no
+        * camera so we can ignore the error.
+        */
+       if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
+               set_acpi(eeepc, CM_ASL_CAMERA, 1);
+}
+
+static bool eeepc_device_present;
+
+static int __devinit eeepc_acpi_add(struct acpi_device *device)
+{
+       struct eeepc_laptop *eeepc;
+       int result;
+
+       pr_notice(EEEPC_LAPTOP_NAME "\n");
+       eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
+       if (!eeepc)
+               return -ENOMEM;
+       eeepc->handle = device->handle;
+       strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
+       strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
+       device->driver_data = eeepc;
+
+       result = eeepc_acpi_init(eeepc, device);
        if (result)
-               goto fail_sysfs;
+               goto fail_platform;
+       eeepc_enable_camera(eeepc);
 
-       dev = &platform_device->dev;
+       /*
+        * Register the platform device first.  It is used as a parent for the
+        * sub-devices below.
+        *
+        * Note that if there are multiple instances of this ACPI device it
+        * will bail out, because the platform device is registered with a
+        * fixed name.  Of course it doesn't make sense to have more than one,
+        * and machine-specific scripts find the fixed name convenient.  But
+        * It's also good for us to exclude multiple instances because both
+        * our hwmon and our wlan rfkill subdevice use global ACPI objects
+        * (the EC and the wlan PCI slot respectively).
+        */
+       result = eeepc_platform_init(eeepc);
+       if (result)
+               goto fail_platform;
 
        if (!acpi_video_backlight_support()) {
-               result = eeepc_backlight_init(dev);
+               result = eeepc_backlight_init(eeepc);
                if (result)
                        goto fail_backlight;
        } else
-               pr_info("Backlight controlled by ACPI video "
-                       "driver\n");
+               pr_info("Backlight controlled by ACPI video driver\n");
 
-       result = eeepc_input_init(dev);
+       result = eeepc_input_init(eeepc);
        if (result)
                goto fail_input;
 
-       result = eeepc_hwmon_init(dev);
+       result = eeepc_hwmon_init(eeepc);
        if (result)
                goto fail_hwmon;
 
-       result = eeepc_rfkill_init(dev);
+       result = eeepc_led_init(eeepc);
+       if (result)
+               goto fail_led;
+
+       result = eeepc_rfkill_init(eeepc);
        if (result)
                goto fail_rfkill;
 
+       eeepc_device_present = true;
        return 0;
 
 fail_rfkill:
-       eeepc_hwmon_exit();
+       eeepc_led_exit(eeepc);
+fail_led:
+       eeepc_hwmon_exit(eeepc);
 fail_hwmon:
-       eeepc_input_exit();
+       eeepc_input_exit(eeepc);
 fail_input:
-       eeepc_backlight_exit();
+       eeepc_backlight_exit(eeepc);
 fail_backlight:
-       sysfs_remove_group(&platform_device->dev.kobj,
-                          &platform_attribute_group);
-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:
-       kfree(ehotk);
+       eeepc_platform_exit(eeepc);
+fail_platform:
+       kfree(eeepc);
 
        return result;
 }
 
-static int eeepc_hotk_remove(struct acpi_device *device, int type)
+static int eeepc_acpi_remove(struct acpi_device *device, int type)
 {
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
+       struct eeepc_laptop *eeepc = acpi_driver_data(device);
 
-       eeepc_backlight_exit();
-       eeepc_rfkill_exit();
-       eeepc_input_exit();
-       eeepc_hwmon_exit();
-       sysfs_remove_group(&platform_device->dev.kobj,
-                          &platform_attribute_group);
-       platform_device_unregister(platform_device);
-       platform_driver_unregister(&platform_driver);
+       eeepc_backlight_exit(eeepc);
+       eeepc_rfkill_exit(eeepc);
+       eeepc_input_exit(eeepc);
+       eeepc_hwmon_exit(eeepc);
+       eeepc_led_exit(eeepc);
+       eeepc_platform_exit(eeepc);
 
-       kfree(ehotk);
+       kfree(eeepc);
        return 0;
 }
 
+
+static const struct acpi_device_id eeepc_device_ids[] = {
+       {EEEPC_ACPI_HID, 0},
+       {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
+
+static struct acpi_driver eeepc_acpi_driver = {
+       .name = EEEPC_LAPTOP_NAME,
+       .class = EEEPC_ACPI_CLASS,
+       .owner = THIS_MODULE,
+       .ids = eeepc_device_ids,
+       .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+       .ops = {
+               .add = eeepc_acpi_add,
+               .remove = eeepc_acpi_remove,
+               .notify = eeepc_acpi_notify,
+       },
+};
+
+
 static int __init eeepc_laptop_init(void)
 {
        int result;
 
-       if (acpi_disabled)
-               return -ENODEV;
-       result = acpi_bus_register_driver(&eeepc_hotk_driver);
+       result = platform_driver_register(&platform_driver);
        if (result < 0)
                return result;
-       if (!ehotk) {
-               acpi_bus_unregister_driver(&eeepc_hotk_driver);
-               return -ENODEV;
+
+       result = acpi_bus_register_driver(&eeepc_acpi_driver);
+       if (result < 0)
+               goto fail_acpi_driver;
+       if (!eeepc_device_present) {
+               result = -ENODEV;
+               goto fail_no_device;
        }
        return 0;
+
+fail_no_device:
+       acpi_bus_unregister_driver(&eeepc_acpi_driver);
+fail_acpi_driver:
+       platform_driver_unregister(&platform_driver);
+       return result;
 }
 
 static void __exit eeepc_laptop_exit(void)
 {
-       acpi_bus_unregister_driver(&eeepc_hotk_driver);
+       acpi_bus_unregister_driver(&eeepc_acpi_driver);
+       platform_driver_unregister(&platform_driver);
 }
 
 module_init(eeepc_laptop_init);
index f00a71c58e69aea037c488e5cbaf03c34ff61a7e..63c3e658a884e3f0d46b3f076e91862b3471b8b6 100644 (file)
@@ -51,6 +51,12 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
 #define HPWMI_WIRELESS_QUERY 0x5
 #define HPWMI_HOTKEY_QUERY 0xc
 
+enum hp_wmi_radio {
+       HPWMI_WIFI = 0,
+       HPWMI_BLUETOOTH = 1,
+       HPWMI_WWAN = 2,
+};
+
 static int __init hp_wmi_bios_setup(struct platform_device *device);
 static int __exit hp_wmi_bios_remove(struct platform_device *device);
 static int hp_wmi_resume_handler(struct device *device);
@@ -175,8 +181,8 @@ static int hp_wmi_tablet_state(void)
 
 static int hp_wmi_set_block(void *data, bool blocked)
 {
-       unsigned long b = (unsigned long) data;
-       int query = BIT(b + 8) | ((!blocked) << b);
+       enum hp_wmi_radio r = (enum hp_wmi_radio) data;
+       int query = BIT(r + 8) | ((!blocked) << r);
 
        return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
 }
@@ -185,31 +191,23 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = {
        .set_block = hp_wmi_set_block,
 };
 
-static bool hp_wmi_wifi_state(void)
-{
-       int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
-       if (wireless & 0x100)
-               return false;
-       else
-               return true;
-}
-
-static bool hp_wmi_bluetooth_state(void)
+static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
 {
        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+       int mask = 0x200 << (r * 8);
 
-       if (wireless & 0x10000)
+       if (wireless & mask)
                return false;
        else
                return true;
 }
 
-static bool hp_wmi_wwan_state(void)
+static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
 {
        int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+       int mask = 0x800 << (r * 8);
 
-       if (wireless & 0x1000000)
+       if (wireless & mask)
                return false;
        else
                return true;
@@ -334,49 +332,55 @@ 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;
+       int eventcode;
 
        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;
-                       }
-               } else if (eventcode == 0x1) {
-                       input_report_switch(hp_wmi_input_dev, SW_DOCK,
-                                           hp_wmi_dock_state());
-                       input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
-                                           hp_wmi_tablet_state());
+       if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) {
+               printk(KERN_INFO "HP WMI: Unknown response received\n");
+               return;
+       }
+
+       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);
-               } else if (eventcode == 0x5) {
-                       if (wifi_rfkill)
-                               rfkill_set_sw_state(wifi_rfkill,
-                                                   hp_wmi_wifi_state());
-                       if (bluetooth_rfkill)
-                               rfkill_set_sw_state(bluetooth_rfkill,
-                                                   hp_wmi_bluetooth_state());
-                       if (wwan_rfkill)
-                               rfkill_set_sw_state(wwan_rfkill,
-                                                   hp_wmi_wwan_state());
-               } else
-                       printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
-                              eventcode);
+                       break;
+               }
+       } else if (eventcode == 0x1) {
+               input_report_switch(hp_wmi_input_dev, SW_DOCK,
+                                   hp_wmi_dock_state());
+               input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+                                   hp_wmi_tablet_state());
+               input_sync(hp_wmi_input_dev);
+       } else if (eventcode == 0x5) {
+               if (wifi_rfkill)
+                       rfkill_set_states(wifi_rfkill,
+                                         hp_wmi_get_sw_state(HPWMI_WIFI),
+                                         hp_wmi_get_hw_state(HPWMI_WIFI));
+               if (bluetooth_rfkill)
+                       rfkill_set_states(bluetooth_rfkill,
+                                         hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
+                                         hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
+               if (wwan_rfkill)
+                       rfkill_set_states(wwan_rfkill,
+                                         hp_wmi_get_sw_state(HPWMI_WWAN),
+                                         hp_wmi_get_hw_state(HPWMI_WWAN));
        } else
-               printk(KERN_INFO "HP WMI: Unknown response received\n");
+               printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
+                       eventcode);
 }
 
 static int __init hp_wmi_input_setup(void)
@@ -455,7 +459,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
                wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
                                           RFKILL_TYPE_WLAN,
                                           &hp_wmi_rfkill_ops,
-                                          (void *) 0);
+                                          (void *) HPWMI_WIFI);
+               rfkill_init_sw_state(wifi_rfkill,
+                                    hp_wmi_get_sw_state(HPWMI_WIFI));
+               rfkill_set_hw_state(wifi_rfkill,
+                                   hp_wmi_get_hw_state(HPWMI_WIFI));
                err = rfkill_register(wifi_rfkill);
                if (err)
                        goto register_wifi_error;
@@ -465,7 +473,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
                bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
                                                RFKILL_TYPE_BLUETOOTH,
                                                &hp_wmi_rfkill_ops,
-                                               (void *) 1);
+                                               (void *) HPWMI_BLUETOOTH);
+               rfkill_init_sw_state(bluetooth_rfkill,
+                                    hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
+               rfkill_set_hw_state(bluetooth_rfkill,
+                                   hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
                err = rfkill_register(bluetooth_rfkill);
                if (err)
                        goto register_bluetooth_error;
@@ -475,7 +487,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
                wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
                                           RFKILL_TYPE_WWAN,
                                           &hp_wmi_rfkill_ops,
-                                          (void *) 2);
+                                          (void *) HPWMI_WWAN);
+               rfkill_init_sw_state(wwan_rfkill,
+                                    hp_wmi_get_sw_state(HPWMI_WWAN));
+               rfkill_set_hw_state(wwan_rfkill,
+                                   hp_wmi_get_hw_state(HPWMI_WWAN));
                err = rfkill_register(wwan_rfkill);
                if (err)
                        goto register_wwan_err;
@@ -533,6 +549,19 @@ static int hp_wmi_resume_handler(struct device *device)
                input_sync(hp_wmi_input_dev);
        }
 
+       if (wifi_rfkill)
+               rfkill_set_states(wifi_rfkill,
+                                 hp_wmi_get_sw_state(HPWMI_WIFI),
+                                 hp_wmi_get_hw_state(HPWMI_WIFI));
+       if (bluetooth_rfkill)
+               rfkill_set_states(bluetooth_rfkill,
+                                 hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
+                                 hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
+       if (wwan_rfkill)
+               rfkill_set_states(wwan_rfkill,
+                                 hp_wmi_get_sw_state(HPWMI_WWAN),
+                                 hp_wmi_get_hw_state(HPWMI_WWAN));
+
        return 0;
 }
 
index cf61d6a8ef6f6410f8a684410fc1f03a9669d0fe..448c8aeb166b1e25561be66b98649c6de4042637 100644 (file)
@@ -21,8 +21,8 @@
  *  02110-1301, USA.
  */
 
-#define TPACPI_VERSION "0.23"
-#define TPACPI_SYSFS_VERSION 0x020500
+#define TPACPI_VERSION "0.24"
+#define TPACPI_SYSFS_VERSION 0x020700
 
 /*
  *  Changelog:
@@ -61,6 +61,7 @@
 
 #include <linux/nvram.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/sysfs.h>
 #include <linux/backlight.h>
 #include <linux/fb.h>
 #include <linux/jiffies.h>
 #include <linux/workqueue.h>
 
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
 #include <acpi/acpi_drivers.h>
 
 #include <linux/pci_ids.h>
@@ -231,6 +236,7 @@ enum tpacpi_hkey_event_t {
 #define TPACPI_DBG_HKEY                0x0008
 #define TPACPI_DBG_FAN         0x0010
 #define TPACPI_DBG_BRGHT       0x0020
+#define TPACPI_DBG_MIXER       0x0040
 
 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
 #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
@@ -256,7 +262,7 @@ struct tp_acpi_drv_struct {
 struct ibm_struct {
        char *name;
 
-       int (*read) (char *);
+       int (*read) (struct seq_file *);
        int (*write) (char *);
        void (*exit) (void);
        void (*resume) (void);
@@ -298,6 +304,7 @@ static struct {
        u32 fan_ctrl_status_undef:1;
        u32 second_fan:1;
        u32 beep_needs_two_args:1;
+       u32 mixer_no_level_control:1;
        u32 input_device_registered:1;
        u32 platform_drv_registered:1;
        u32 platform_drv_attrs_registered:1;
@@ -309,6 +316,7 @@ static struct {
 
 static struct {
        u16 hotkey_mask_ff:1;
+       u16 volume_ctrl_forbidden:1;
 } tp_warned;
 
 struct thinkpad_id_data {
@@ -425,6 +433,12 @@ static void tpacpi_log_usertask(const char * const what)
          .ec = TPACPI_MATCH_ANY,               \
          .quirks = (__quirk) }
 
+#define TPACPI_QEC_LNV(__id1, __id2, __quirk)  \
+       { .vendor = PCI_VENDOR_ID_LENOVO,       \
+         .bios = TPACPI_MATCH_ANY,             \
+         .ec = TPID(__id1, __id2),             \
+         .quirks = (__quirk) }
+
 struct tpacpi_quirk {
        unsigned int vendor;
        u16 bios;
@@ -776,36 +790,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
  ****************************************************************************
  ****************************************************************************/
 
-static int dispatch_procfs_read(char *page, char **start, off_t off,
-                       int count, int *eof, void *data)
+static int dispatch_proc_show(struct seq_file *m, void *v)
 {
-       struct ibm_struct *ibm = data;
-       int len;
+       struct ibm_struct *ibm = m->private;
 
        if (!ibm || !ibm->read)
                return -EINVAL;
+       return ibm->read(m);
+}
 
-       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_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dispatch_proc_show, PDE(inode)->data);
 }
 
-static int dispatch_procfs_write(struct file *file,
+static ssize_t dispatch_proc_write(struct file *file,
                        const char __user *userbuf,
-                       unsigned long count, void *data)
+                       size_t count, loff_t *pos)
 {
-       struct ibm_struct *ibm = data;
+       struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data;
        char *kernbuf;
        int ret;
 
@@ -834,6 +837,15 @@ static int dispatch_procfs_write(struct file *file,
        return ret;
 }
 
+static const struct file_operations dispatch_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dispatch_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = dispatch_proc_write,
+};
+
 static char *next_cmd(char **cmds)
 {
        char *start = *cmds;
@@ -1261,6 +1273,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
        struct tpacpi_rfk *atp_rfk;
        int res;
        bool sw_state = false;
+       bool hw_state;
        int sw_status;
 
        BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]);
@@ -1295,7 +1308,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
                        rfkill_init_sw_state(atp_rfk->rfkill, sw_state);
                }
        }
-       rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state());
+       hw_state = tpacpi_rfk_check_hwblock_state();
+       rfkill_set_hw_state(atp_rfk->rfkill, hw_state);
 
        res = rfkill_register(atp_rfk->rfkill);
        if (res < 0) {
@@ -1308,6 +1322,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
        }
 
        tpacpi_rfkill_switches[id] = atp_rfk;
+
+       printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n",
+               name, (sw_state || hw_state) ? "" : "un");
        return 0;
 }
 
@@ -1380,12 +1397,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id,
 }
 
 /* procfs -------------------------------------------------------------- */
-static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
+static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m)
 {
-       int len = 0;
-
        if (id >= TPACPI_RFK_SW_MAX)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
+               seq_printf(m, "status:\t\tnot supported\n");
        else {
                int status;
 
@@ -1399,13 +1414,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
                                return status;
                }
 
-               len += sprintf(p + len, "status:\t\t%s\n",
+               seq_printf(m, "status:\t\t%s\n",
                                (status == TPACPI_RFK_RADIO_ON) ?
                                        "enabled" : "disabled");
-               len += sprintf(p + len, "commands:\tenable, disable\n");
+               seq_printf(m, "commands:\tenable, disable\n");
        }
 
-       return len;
+       return 0;
 }
 
 static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
@@ -1776,7 +1791,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = {
 
        TPV_QL1('7', '9',  'E', '3',  '5', '0'), /* T60/p */
        TPV_QL1('7', 'C',  'D', '2',  '2', '2'), /* R60, R60i */
-       TPV_QL0('7', 'E',  'D', '0'),            /* R60e, R60i */
+       TPV_QL1('7', 'E',  'D', '0',  '1', '5'), /* R60e, R60i */
 
        /*      BIOS FW    BIOS VERS  EC FW     EC VERS */
        TPV_QI2('1', 'W',  '9', '0',  '1', 'V', '2', '8'), /* R50e (1) */
@@ -1792,8 +1807,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = {
        TPV_QI1('7', '4',  '6', '4',  '2', '7'), /* X41 (0) */
        TPV_QI1('7', '5',  '6', '0',  '2', '0'), /* X41t (0) */
 
-       TPV_QL0('7', 'B',  'D', '7'),            /* X60/s */
-       TPV_QL0('7', 'J',  '3', '0'),            /* X60t */
+       TPV_QL1('7', 'B',  'D', '7',  '4', '0'), /* X60/s */
+       TPV_QL1('7', 'J',  '3', '0',  '1', '3'), /* X60t */
 
        /* (0) - older versions lack DMI EC fw string and functionality */
        /* (1) - older versions known to lack functionality */
@@ -1883,14 +1898,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
        return 0;
 }
 
-static int thinkpad_acpi_driver_read(char *p)
+static int thinkpad_acpi_driver_read(struct seq_file *m)
 {
-       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;
+       seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC);
+       seq_printf(m, "version:\t%s\n", TPACPI_VERSION);
+       return 0;
 }
 
 static struct ibm_struct thinkpad_acpi_driver_data = {
@@ -2186,7 +2198,8 @@ static int hotkey_mask_set(u32 mask)
                       fwmask, hotkey_acpi_mask);
        }
 
-       hotkey_mask_warn_incomplete_mask();
+       if (tpacpi_lifecycle != TPACPI_LIFE_EXITING)
+               hotkey_mask_warn_incomplete_mask();
 
        return rc;
 }
@@ -3182,6 +3195,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        int res, i;
        int status;
        int hkeyv;
+       bool radiosw_state  = false;
+       bool tabletsw_state = false;
 
        unsigned long quirks;
 
@@ -3287,6 +3302,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
        if (dbg_wlswemul) {
                tp_features.hotkey_wlsw = 1;
+               radiosw_state = !!tpacpi_wlsw_emulstate;
                printk(TPACPI_INFO
                        "radio switch emulation enabled\n");
        } else
@@ -3294,6 +3310,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        /* Not all thinkpads have a hardware radio switch */
        if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
                tp_features.hotkey_wlsw = 1;
+               radiosw_state = !!status;
                printk(TPACPI_INFO
                        "radio switch found; radios are %s\n",
                        enabled(status, 0));
@@ -3305,11 +3322,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        /* For X41t, X60t, X61t Tablets... */
        if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
                tp_features.hotkey_tablet = 1;
+               tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK);
                printk(TPACPI_INFO
                        "possible tablet mode switch found; "
                        "ThinkPad in %s mode\n",
-                       (status & TP_HOTKEY_TABLET_MASK)?
-                               "tablet" : "laptop");
+                       (tabletsw_state) ? "tablet" : "laptop");
                res = add_to_attr_set(hotkey_dev_attributes,
                                &dev_attr_hotkey_tablet_mode.attr);
        }
@@ -3344,16 +3361,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
                        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);
+       input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
        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);
+                       input_set_capability(tpacpi_inputdev, EV_KEY,
+                                               hotkey_keycode_map[i]);
                } else {
                        if (i < sizeof(hotkey_reserved_mask)*8)
                                hotkey_reserved_mask |= 1 << i;
@@ -3361,12 +3376,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        }
 
        if (tp_features.hotkey_wlsw) {
-               set_bit(EV_SW, tpacpi_inputdev->evbit);
-               set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
+               input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL);
+               input_report_switch(tpacpi_inputdev,
+                                   SW_RFKILL_ALL, radiosw_state);
        }
        if (tp_features.hotkey_tablet) {
-               set_bit(EV_SW, tpacpi_inputdev->evbit);
-               set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
+               input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE);
+               input_report_switch(tpacpi_inputdev,
+                                   SW_TABLET_MODE, tabletsw_state);
        }
 
        /* Do not issue duplicate brightness change events to
@@ -3433,8 +3450,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
        tpacpi_inputdev->close = &hotkey_inputdev_close;
 
        hotkey_poll_setup_safe(true);
-       tpacpi_send_radiosw_update();
-       tpacpi_input_send_tabletsw();
 
        return 0;
 
@@ -3542,49 +3557,57 @@ static bool hotkey_notify_usrevent(const u32 hkey,
        }
 }
 
+static void thermal_dump_all_sensors(void);
+
 static bool hotkey_notify_thermal(const u32 hkey,
                                 bool *send_acpi_ev,
                                 bool *ignore_acpi_ev)
 {
+       bool known = true;
+
        /* 0x6000-0x6FFF: thermal alarms */
        *send_acpi_ev = true;
        *ignore_acpi_ev = false;
 
        switch (hkey) {
+       case TP_HKEY_EV_THM_TABLE_CHANGED:
+               printk(TPACPI_INFO
+                       "EC reports that Thermal Table has changed\n");
+               /* recommended action: do nothing, we don't have
+                * Lenovo ATM information */
+               return true;
        case TP_HKEY_EV_ALARM_BAT_HOT:
                printk(TPACPI_CRIT
                        "THERMAL ALARM: battery is too hot!\n");
                /* recommended action: warn user through gui */
-               return true;
+               break;
        case TP_HKEY_EV_ALARM_BAT_XHOT:
                printk(TPACPI_ALERT
                        "THERMAL EMERGENCY: battery is extremely hot!\n");
                /* recommended action: immediate sleep/hibernate */
-               return true;
+               break;
        case TP_HKEY_EV_ALARM_SENSOR_HOT:
                printk(TPACPI_CRIT
                        "THERMAL ALARM: "
                        "a sensor reports something is too hot!\n");
                /* recommended action: warn user through gui, that */
                /* some internal component is too hot */
-               return true;
+               break;
        case TP_HKEY_EV_ALARM_SENSOR_XHOT:
                printk(TPACPI_ALERT
                        "THERMAL EMERGENCY: "
                        "a sensor reports something is extremely hot!\n");
                /* recommended action: immediate sleep/hibernate */
-               return true;
-       case TP_HKEY_EV_THM_TABLE_CHANGED:
-               printk(TPACPI_INFO
-                       "EC reports that Thermal Table has changed\n");
-               /* recommended action: do nothing, we don't have
-                * Lenovo ATM information */
-               return true;
+               break;
        default:
                printk(TPACPI_ALERT
                         "THERMAL ALERT: unknown thermal alarm received\n");
-               return false;
+               known = false;
        }
+
+       thermal_dump_all_sensors();
+
+       return known;
 }
 
 static void hotkey_notify(struct ibm_struct *ibm, u32 event)
@@ -3727,14 +3750,13 @@ static void hotkey_resume(void)
 }
 
 /* procfs -------------------------------------------------------------- */
-static int hotkey_read(char *p)
+static int hotkey_read(struct seq_file *m)
 {
        int res, status;
-       int len = 0;
 
        if (!tp_features.hotkey) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
+               seq_printf(m, "status:\t\tnot supported\n");
+               return 0;
        }
 
        if (mutex_lock_killable(&hotkey_mutex))
@@ -3746,17 +3768,16 @@ static int hotkey_read(char *p)
        if (res)
                return res;
 
-       len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+       seq_printf(m, "status:\t\t%s\n", enabled(status, 0));
        if (hotkey_all_mask) {
-               len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask);
-               len += sprintf(p + len,
-                              "commands:\tenable, disable, reset, <mask>\n");
+               seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask);
+               seq_printf(m, "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");
+               seq_printf(m, "mask:\t\tnot supported\n");
+               seq_printf(m, "commands:\tenable, disable, reset\n");
        }
 
-       return len;
+       return 0;
 }
 
 static void hotkey_enabledisable_warn(bool enable)
@@ -3863,15 +3884,6 @@ enum {
 
 #define TPACPI_RFK_BLUETOOTH_SW_NAME   "tpacpi_bluetooth_sw"
 
-static void bluetooth_suspend(pm_message_t state)
-{
-       /* Try to make sure radio will resume powered off */
-       if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd",
-                  TP_ACPI_BLTH_PWR_OFF_ON_RESUME))
-               vdbg_printk(TPACPI_DBG_RFKILL,
-                       "bluetooth power down on resume request failed\n");
-}
-
 static int bluetooth_get_status(void)
 {
        int status;
@@ -3905,10 +3917,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state)
 #endif
 
        /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
+       status = TP_ACPI_BLUETOOTH_RESUMECTRL;
        if (state == TPACPI_RFK_RADIO_ON)
-               status = TP_ACPI_BLUETOOTH_RADIOSSW;
-       else
-               status = 0;
+               status |= TP_ACPI_BLUETOOTH_RADIOSSW;
 
        if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
                return -EIO;
@@ -4032,9 +4043,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
 }
 
 /* procfs -------------------------------------------------------------- */
-static int bluetooth_read(char *p)
+static int bluetooth_read(struct seq_file *m)
 {
-       return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p);
+       return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m);
 }
 
 static int bluetooth_write(char *buf)
@@ -4047,7 +4058,6 @@ static struct ibm_struct bluetooth_driver_data = {
        .read = bluetooth_read,
        .write = bluetooth_write,
        .exit = bluetooth_exit,
-       .suspend = bluetooth_suspend,
        .shutdown = bluetooth_shutdown,
 };
 
@@ -4065,15 +4075,6 @@ enum {
 
 #define TPACPI_RFK_WWAN_SW_NAME                "tpacpi_wwan_sw"
 
-static void wan_suspend(pm_message_t state)
-{
-       /* Try to make sure radio will resume powered off */
-       if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd",
-                  TP_ACPI_WGSV_PWR_OFF_ON_RESUME))
-               vdbg_printk(TPACPI_DBG_RFKILL,
-                       "WWAN power down on resume request failed\n");
-}
-
 static int wan_get_status(void)
 {
        int status;
@@ -4106,11 +4107,10 @@ static int wan_set_status(enum tpacpi_rfkill_state state)
        }
 #endif
 
-       /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */
+       /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */
+       status = TP_ACPI_WANCARD_RESUMECTRL;
        if (state == TPACPI_RFK_RADIO_ON)
-               status = TP_ACPI_WANCARD_RADIOSSW;
-       else
-               status = 0;
+               status |= TP_ACPI_WANCARD_RADIOSSW;
 
        if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
                return -EIO;
@@ -4233,9 +4233,9 @@ static int __init wan_init(struct ibm_init_struct *iibm)
 }
 
 /* procfs -------------------------------------------------------------- */
-static int wan_read(char *p)
+static int wan_read(struct seq_file *m)
 {
-       return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p);
+       return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m);
 }
 
 static int wan_write(char *buf)
@@ -4248,7 +4248,6 @@ static struct ibm_struct wan_driver_data = {
        .read = wan_read,
        .write = wan_write,
        .exit = wan_exit,
-       .suspend = wan_suspend,
        .shutdown = wan_shutdown,
 };
 
@@ -4611,14 +4610,13 @@ static int video_expand_toggle(void)
        /* not reached */
 }
 
-static int video_read(char *p)
+static int video_read(struct seq_file *m)
 {
        int status, autosw;
-       int len = 0;
 
        if (video_supported == TPACPI_VIDEO_NONE) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
+               seq_printf(m, "status:\t\tnot supported\n");
+               return 0;
        }
 
        status = video_outputsw_get();
@@ -4629,20 +4627,20 @@ static int video_read(char *p)
        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));
+       seq_printf(m, "status:\t\tsupported\n");
+       seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0));
+       seq_printf(m, "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");
+               seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3));
+       seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0));
+       seq_printf(m, "commands:\tlcd_enable, lcd_disable\n");
+       seq_printf(m, "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");
+               seq_printf(m, "commands:\tdvi_enable, dvi_disable\n");
+       seq_printf(m, "commands:\tauto_enable, auto_disable\n");
+       seq_printf(m, "commands:\tvideo_switch, expand_toggle\n");
 
-       return len;
+       return 0;
 }
 
 static int video_write(char *buf)
@@ -4834,25 +4832,24 @@ static void light_exit(void)
                flush_workqueue(tpacpi_wq);
 }
 
-static int light_read(char *p)
+static int light_read(struct seq_file *m)
 {
-       int len = 0;
        int status;
 
        if (!tp_features.light) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
+               seq_printf(m, "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");
+               seq_printf(m, "status:\t\tunknown\n");
+               seq_printf(m, "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");
+               seq_printf(m, "status:\t\t%s\n", onoff(status, 0));
+               seq_printf(m, "commands:\ton, off\n");
        }
 
-       return len;
+       return 0;
 }
 
 static int light_write(char *buf)
@@ -4930,20 +4927,18 @@ static void cmos_exit(void)
        device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
 }
 
-static int cmos_read(char *p)
+static int cmos_read(struct seq_file *m)
 {
-       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");
+               seq_printf(m, "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");
+               seq_printf(m, "status:\t\tsupported\n");
+               seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n");
        }
 
-       return len;
+       return 0;
 }
 
 static int cmos_write(char *buf)
@@ -5318,15 +5313,13 @@ static int __init led_init(struct ibm_init_struct *iibm)
        ((s) == TPACPI_LED_OFF ? "off" : \
                ((s) == TPACPI_LED_ON ? "on" : "blinking"))
 
-static int led_read(char *p)
+static int led_read(struct seq_file *m)
 {
-       int len = 0;
-
        if (!led_supported) {
-               len += sprintf(p + len, "status:\t\tnot supported\n");
-               return len;
+               seq_printf(m, "status:\t\tnot supported\n");
+               return 0;
        }
-       len += sprintf(p + len, "status:\t\tsupported\n");
+       seq_printf(m, "status:\t\tsupported\n");
 
        if (led_supported == TPACPI_LED_570) {
                /* 570 */
@@ -5335,15 +5328,15 @@ static int led_read(char *p)
                        status = led_get_status(i);
                        if (status < 0)
                                return -EIO;
-                       len += sprintf(p + len, "%d:\t\t%s\n",
+                       seq_printf(m, "%d:\t\t%s\n",
                                       i, str_led_status(status));
                }
        }
 
-       len += sprintf(p + len, "commands:\t"
+       seq_printf(m, "commands:\t"
                       "<led> on, <led> off, <led> blink (<led> is 0-15)\n");
 
-       return len;
+       return 0;
 }
 
 static int led_write(char *buf)
@@ -5416,18 +5409,16 @@ static int __init beep_init(struct ibm_init_struct *iibm)
        return (beep_handle)? 0 : 1;
 }
 
-static int beep_read(char *p)
+static int beep_read(struct seq_file *m)
 {
-       int len = 0;
-
        if (!beep_handle)
-               len += sprintf(p + len, "status:\t\tnot supported\n");
+               seq_printf(m, "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");
+               seq_printf(m, "status:\t\tsupported\n");
+               seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n");
        }
 
-       return len;
+       return 0;
 }
 
 static int beep_write(char *buf)
@@ -5480,8 +5471,11 @@ 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 */
+
+       TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */
 };
 
+
 #define TPACPI_MAX_THERMAL_SENSORS 16  /* Max thermal sensors supported */
 struct ibm_thermal_sensors_struct {
        s32 temp[TPACPI_MAX_THERMAL_SENSORS];
@@ -5571,6 +5565,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
        return n;
 }
 
+static void thermal_dump_all_sensors(void)
+{
+       int n, i;
+       struct ibm_thermal_sensors_struct t;
+
+       n = thermal_get_sensors(&t);
+       if (n <= 0)
+               return;
+
+       printk(TPACPI_NOTICE
+               "temperatures (Celsius):");
+
+       for (i = 0; i < n; i++) {
+               if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA)
+                       printk(KERN_CONT " %d", (int)(t.temp[i] / 1000));
+               else
+                       printk(KERN_CONT " N/A");
+       }
+
+       printk(KERN_CONT "\n");
+}
+
 /* sysfs temp##_input -------------------------------------------------- */
 
 static ssize_t thermal_temp_input_show(struct device *dev,
@@ -5586,7 +5602,7 @@ static ssize_t thermal_temp_input_show(struct device *dev,
        res = thermal_get_sensor(idx, &value);
        if (res)
                return res;
-       if (value == TP_EC_THERMAL_TMP_NA * 1000)
+       if (value == TPACPI_THERMAL_SENSOR_NA)
                return -ENXIO;
 
        return snprintf(buf, PAGE_SIZE, "%d\n", value);
@@ -5763,9 +5779,8 @@ static void thermal_exit(void)
        }
 }
 
-static int thermal_read(char *p)
+static int thermal_read(struct seq_file *m)
 {
-       int len = 0;
        int n, i;
        struct ibm_thermal_sensors_struct t;
 
@@ -5773,16 +5788,16 @@ static int thermal_read(char *p)
        if (unlikely(n < 0))
                return n;
 
-       len += sprintf(p + len, "temperatures:\t");
+       seq_printf(m, "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);
+                       seq_printf(m, "%d ", t.temp[i] / 1000);
+               seq_printf(m, "%d\n", t.temp[i] / 1000);
        } else
-               len += sprintf(p + len, "not supported\n");
+               seq_printf(m, "not supported\n");
 
-       return len;
+       return 0;
 }
 
 static struct ibm_struct thermal_driver_data = {
@@ -5797,39 +5812,38 @@ static struct ibm_struct thermal_driver_data = {
 
 static u8 ecdump_regs[256];
 
-static int ecdump_read(char *p)
+static int ecdump_read(struct seq_file *m)
 {
-       int len = 0;
        int i, j;
        u8 v;
 
-       len += sprintf(p + len, "EC      "
+       seq_printf(m, "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);
+               seq_printf(m, "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);
+                               seq_printf(m, " *%02x", v);
                        else
-                               len += sprintf(p + len, "  %02x", v);
+                               seq_printf(m, "  %02x", v);
                        ecdump_regs[i + j] = v;
                }
-               len += sprintf(p + len, "\n");
+               seq_putc(m, '\n');
                if (j != 16)
                        break;
        }
 
        /* These are way too dangerous to advertise openly... */
 #if 0
-       len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
+       seq_printf(m, "commands:\t0x<offset> 0x<value>"
                       " (<offset> is 00-ff, <value> is 00-ff)\n");
-       len += sprintf(p + len, "commands:\t0x<offset> <value>  "
+       seq_printf(m, "commands:\t0x<offset> <value>  "
                       " (<offset> is 00-ff, <value> is 0-255)\n");
 #endif
-       return len;
+       return 0;
 }
 
 static int ecdump_write(char *buf)
@@ -6092,6 +6106,12 @@ static int brightness_get(struct backlight_device *bd)
        return status & TP_EC_BACKLIGHT_LVLMSK;
 }
 
+static void tpacpi_brightness_notify_change(void)
+{
+       backlight_force_update(ibm_backlight_device,
+                              BACKLIGHT_UPDATE_HOTKEY);
+}
+
 static struct backlight_ops ibm_backlight_data = {
        .get_brightness = brightness_get,
        .update_status  = brightness_update_status,
@@ -6120,8 +6140,8 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
 
        /* Models with Intel Extreme Graphics 2 */
        TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC),
-       TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
-       TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
+       TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+       TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
 
        /* Models with Intel GMA900 */
        TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC),    /* T43, R52 */
@@ -6246,6 +6266,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
        ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
        backlight_update_status(ibm_backlight_device);
 
+       vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
+                       "brightness: registering brightness hotkeys "
+                       "as change notification\n");
+       tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
+                               | TP_ACPI_HKEY_BRGHTUP_MASK
+                               | TP_ACPI_HKEY_BRGHTDWN_MASK);;
        return 0;
 }
 
@@ -6270,23 +6296,22 @@ static void brightness_exit(void)
        tpacpi_brightness_checkpoint_nvram();
 }
 
-static int brightness_read(char *p)
+static int brightness_read(struct seq_file *m)
 {
-       int len = 0;
        int level;
 
        level = brightness_get(NULL);
        if (level < 0) {
-               len += sprintf(p + len, "level:\t\tunreadable\n");
+               seq_printf(m, "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>"
+               seq_printf(m, "level:\t\t%d\n", level);
+               seq_printf(m, "commands:\tup, down\n");
+               seq_printf(m, "commands:\tlevel <level>"
                               " (<level> is 0-%d)\n",
                               (tp_features.bright_16levels) ? 15 : 7);
        }
 
-       return len;
+       return 0;
 }
 
 static int brightness_write(char *buf)
@@ -6322,6 +6347,9 @@ static int brightness_write(char *buf)
         * Doing it this way makes the syscall restartable in case of EINTR
         */
        rc = brightness_set(level);
+       if (!rc && ibm_backlight_device)
+               backlight_force_update(ibm_backlight_device,
+                                       BACKLIGHT_UPDATE_SYSFS);
        return (rc == -EINTR)? -ERESTARTSYS : rc;
 }
 
@@ -6338,99 +6366,654 @@ static struct ibm_struct brightness_driver_data = {
  * Volume subdriver
  */
 
-static int volume_offset = 0x30;
+/*
+ * IBM ThinkPads have a simple volume controller with MUTE gating.
+ * Very early Lenovo ThinkPads follow the IBM ThinkPad spec.
+ *
+ * Since the *61 series (and probably also the later *60 series), Lenovo
+ * ThinkPads only implement the MUTE gate.
+ *
+ * EC register 0x30
+ *   Bit 6: MUTE (1 mutes sound)
+ *   Bit 3-0: Volume
+ *   Other bits should be zero as far as we know.
+ *
+ * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and
+ * bits 3-0 (volume).  Other bits in NVRAM may have other functions,
+ * such as bit 7 which is used to detect repeated presses of MUTE,
+ * and we leave them unchanged.
+ */
+
+#define TPACPI_ALSA_DRVNAME  "ThinkPad EC"
+#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
+#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
+
+static int alsa_index = SNDRV_DEFAULT_IDX1;
+static char *alsa_id = "ThinkPadEC";
+static int alsa_enable = SNDRV_DEFAULT_ENABLE1;
+
+struct tpacpi_alsa_data {
+       struct snd_card *card;
+       struct snd_ctl_elem_id *ctl_mute_id;
+       struct snd_ctl_elem_id *ctl_vol_id;
+};
+
+static struct snd_card *alsa_card;
+
+enum {
+       TP_EC_AUDIO = 0x30,
+
+       /* TP_EC_AUDIO bits */
+       TP_EC_AUDIO_MUTESW = 6,
+
+       /* TP_EC_AUDIO bitmasks */
+       TP_EC_AUDIO_LVL_MSK = 0x0F,
+       TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW),
+
+       /* Maximum volume */
+       TP_EC_VOLUME_MAX = 14,
+};
+
+enum tpacpi_volume_access_mode {
+       TPACPI_VOL_MODE_AUTO = 0,       /* Not implemented yet */
+       TPACPI_VOL_MODE_EC,             /* Pure EC control */
+       TPACPI_VOL_MODE_UCMS_STEP,      /* UCMS step-based control: N/A */
+       TPACPI_VOL_MODE_ECNVRAM,        /* EC control w/ NVRAM store */
+       TPACPI_VOL_MODE_MAX
+};
+
+enum tpacpi_volume_capabilities {
+       TPACPI_VOL_CAP_AUTO = 0,        /* Use white/blacklist */
+       TPACPI_VOL_CAP_VOLMUTE,         /* Output vol and mute */
+       TPACPI_VOL_CAP_MUTEONLY,        /* Output mute only */
+       TPACPI_VOL_CAP_MAX
+};
+
+static enum tpacpi_volume_access_mode volume_mode =
+       TPACPI_VOL_MODE_MAX;
+
+static enum tpacpi_volume_capabilities volume_capabilities;
+static int volume_control_allowed;
 
-static int volume_read(char *p)
+/*
+ * Used to syncronize writers to TP_EC_AUDIO and
+ * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write
+ */
+static struct mutex volume_mutex;
+
+static void tpacpi_volume_checkpoint_nvram(void)
 {
-       int len = 0;
-       u8 level;
+       u8 lec = 0;
+       u8 b_nvram;
+       u8 ec_mask;
+
+       if (volume_mode != TPACPI_VOL_MODE_ECNVRAM)
+               return;
+       if (!volume_control_allowed)
+               return;
+
+       vdbg_printk(TPACPI_DBG_MIXER,
+               "trying to checkpoint mixer state to NVRAM...\n");
 
-       if (!acpi_ec_read(volume_offset, &level)) {
-               len += sprintf(p + len, "level:\t\tunreadable\n");
+       if (tp_features.mixer_no_level_control)
+               ec_mask = TP_EC_AUDIO_MUTESW_MSK;
+       else
+               ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK;
+
+       if (mutex_lock_killable(&volume_mutex) < 0)
+               return;
+
+       if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec)))
+               goto unlock;
+       lec &= ec_mask;
+       b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
+
+       if (lec != (b_nvram & ec_mask)) {
+               /* NVRAM needs update */
+               b_nvram &= ~ec_mask;
+               b_nvram |= lec;
+               nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER);
+               dbg_printk(TPACPI_DBG_MIXER,
+                          "updated NVRAM mixer status to 0x%02x (0x%02x)\n",
+                          (unsigned int) lec, (unsigned int) b_nvram);
        } 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");
+               vdbg_printk(TPACPI_DBG_MIXER,
+                          "NVRAM mixer status already is 0x%02x (0x%02x)\n",
+                          (unsigned int) lec, (unsigned int) b_nvram);
        }
 
-       return len;
+unlock:
+       mutex_unlock(&volume_mutex);
 }
 
-static int volume_write(char *buf)
+static int volume_get_status_ec(u8 *status)
 {
-       int cmos_cmd, inc, i;
-       u8 level, mute;
-       int new_level, new_mute;
-       char *cmd;
+       u8 s;
 
-       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 (!acpi_ec_read(TP_EC_AUDIO, &s))
+               return -EIO;
 
-               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;
+       *status = s;
 
-               if (new_level != level) {
-                       /* mute doesn't change */
+       dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s);
 
-                       cmos_cmd = (new_level > level) ?
-                                       TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
-                       inc = new_level > level ? 1 : -1;
+       return 0;
+}
 
-                       if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
-                                    !acpi_ec_write(volume_offset, level)))
-                               return -EIO;
+static int volume_get_status(u8 *status)
+{
+       return volume_get_status_ec(status);
+}
 
-                       for (i = level; i != new_level; i += inc)
-                               if (issue_thinkpad_cmos_command(cmos_cmd) ||
-                                   !acpi_ec_write(volume_offset, i + inc))
-                                       return -EIO;
+static int volume_set_status_ec(const u8 status)
+{
+       if (!acpi_ec_write(TP_EC_AUDIO, status))
+               return -EIO;
 
-                       if (mute &&
-                           (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
-                            !acpi_ec_write(volume_offset, new_level + mute))) {
-                               return -EIO;
-                       }
+       dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
+
+       return 0;
+}
+
+static int volume_set_status(const u8 status)
+{
+       return volume_set_status_ec(status);
+}
+
+static int volume_set_mute_ec(const bool mute)
+{
+       int rc;
+       u8 s, n;
+
+       if (mutex_lock_killable(&volume_mutex) < 0)
+               return -EINTR;
+
+       rc = volume_get_status_ec(&s);
+       if (rc)
+               goto unlock;
+
+       n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK :
+                    s & ~TP_EC_AUDIO_MUTESW_MSK;
+
+       if (n != s)
+               rc = volume_set_status_ec(n);
+
+unlock:
+       mutex_unlock(&volume_mutex);
+       return rc;
+}
+
+static int volume_set_mute(const bool mute)
+{
+       dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n",
+                  (mute) ? "" : "un");
+       return volume_set_mute_ec(mute);
+}
+
+static int volume_set_volume_ec(const u8 vol)
+{
+       int rc;
+       u8 s, n;
+
+       if (vol > TP_EC_VOLUME_MAX)
+               return -EINVAL;
+
+       if (mutex_lock_killable(&volume_mutex) < 0)
+               return -EINTR;
+
+       rc = volume_get_status_ec(&s);
+       if (rc)
+               goto unlock;
+
+       n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol;
+
+       if (n != s)
+               rc = volume_set_status_ec(n);
+
+unlock:
+       mutex_unlock(&volume_mutex);
+       return rc;
+}
+
+static int volume_set_volume(const u8 vol)
+{
+       dbg_printk(TPACPI_DBG_MIXER,
+                  "trying to set volume