Browse Source

Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (117 commits)
  ACPI processor: Fix section mismatch for processor_add()
  ACPI: Add platform-wide _OSC support.
  ACPI: cleanup pci_root _OSC code.
  ACPI: Add a generic API for _OSC -v2
  msi-wmi: depend on backlight and fix corner-cases problems
  msi-wmi: switch to using input sparse keymap library
  msi-wmi: replace one-condition switch-case with if statement
  msi-wmi: remove unused field 'instance' in key_entry structure
  msi-wmi: remove custom runtime debug implementation
  msi-wmi: rework init
  msi-wmi: remove useless includes
  X86 drivers: Introduce msi-wmi driver
  Toshiba Bluetooth Enabling driver (RFKill handler v3)
  ACPI: fix for lapic_timer_propagate_broadcast()
  acpi_pad: squish warning
  ACPI: dock: minor whitespace and style cleanups
  ACPI: dock: add struct dock_station * directly to platform device data
  ACPI: dock: dock_add - hoist up platform_device_register_simple()
  ACPI: dock: remove global 'dock_device_name'
  ACPI: dock: combine add|alloc_dock_dependent_device (v2)
  ...
Linus Torvalds 15 năm trước cách đây
mục cha
commit
288f02bbb6
66 tập tin đã thay đổi với 4126 bổ sung1725 xóa
  1. 66 0
      Documentation/acpi/method-customizing.txt
  2. 19 0
      Documentation/feature-removal-schedule.txt
  3. 64 50
      Documentation/laptops/thinkpad-acpi.txt
  4. 1 0
      Documentation/thermal/sysfs-api.txt
  5. 1 1
      arch/x86/kernel/acpi/cstate.c
  6. 2 1
      drivers/acpi/acpi_pad.c
  7. 6 3
      drivers/acpi/acpica/acnamesp.h
  8. 5 1
      drivers/acpi/acpica/acobject.h
  9. 1 1
      drivers/acpi/acpica/dsmethod.c
  10. 39 25
      drivers/acpi/acpica/dswload.c
  11. 2 2
      drivers/acpi/acpica/evregion.c
  12. 15 0
      drivers/acpi/acpica/evrgnini.c
  13. 2 2
      drivers/acpi/acpica/evxface.c
  14. 2 2
      drivers/acpi/acpica/evxfevnt.c
  15. 2 2
      drivers/acpi/acpica/evxfregn.c
  16. 9 9
      drivers/acpi/acpica/exmutex.c
  17. 1 1
      drivers/acpi/acpica/nsaccess.c
  18. 1 1
      drivers/acpi/acpica/nsdump.c
  19. 18 0
      drivers/acpi/acpica/nseval.c
  20. 1 1
      drivers/acpi/acpica/nsnames.c
  21. 37 56
      drivers/acpi/acpica/nspredef.c
  22. 373 74
      drivers/acpi/acpica/nsrepair.c
  23. 156 39
      drivers/acpi/acpica/nsrepair2.c
  24. 11 46
      drivers/acpi/acpica/nsutils.c
  25. 5 5
      drivers/acpi/acpica/nsxfeval.c
  26. 5 5
      drivers/acpi/acpica/nsxfname.c
  27. 7 7
      drivers/acpi/acpica/nsxfobj.c
  28. 2 1
      drivers/acpi/acpica/psxface.c
  29. 1 1
      drivers/acpi/acpica/rsxface.c
  30. 14 13
      drivers/acpi/acpica/utcopy.c
  31. 1 1
      drivers/acpi/battery.c
  32. 148 0
      drivers/acpi/bus.c
  33. 7 0
      drivers/acpi/button.c
  34. 83 1
      drivers/acpi/debug.c
  35. 91 170
      drivers/acpi/dock.c
  36. 1 1
      drivers/acpi/fan.c
  37. 6 15
      drivers/acpi/numa.c
  38. 1 1
      drivers/acpi/osl.c
  39. 14 62
      drivers/acpi/pci_root.c
  40. 5 5
      drivers/acpi/processor_core.c
  41. 8 3
      drivers/acpi/processor_idle.c
  42. 47 3
      drivers/acpi/processor_perflib.c
  43. 7 0
      drivers/acpi/thermal.c
  44. 109 9
      drivers/char/ipmi/ipmi_si_intf.c
  45. 0 13
      drivers/misc/Kconfig
  46. 29 0
      drivers/platform/x86/Kconfig
  47. 2 0
      drivers/platform/x86/Makefile
  48. 37 26
      drivers/platform/x86/acerhdf.c
  49. 13 12
      drivers/platform/x86/asus-laptop.c
  50. 5 14
      drivers/platform/x86/asus_acpi.c
  51. 62 24
      drivers/platform/x86/dell-laptop.c
  52. 116 13
      drivers/platform/x86/dell-wmi.c
  53. 799 642
      drivers/platform/x86/eeepc-laptop.c
  54. 84 55
      drivers/platform/x86/hp-wmi.c
  55. 293 0
      drivers/platform/x86/msi-wmi.c
  56. 899 279
      drivers/platform/x86/thinkpad_acpi.c
  57. 144 0
      drivers/platform/x86/toshiba_bluetooth.c
  58. 173 2
      drivers/platform/x86/wmi.c
  59. 14 6
      drivers/pnp/pnpacpi/core.c
  60. 6 3
      drivers/pnp/pnpacpi/rsparser.c
  61. 12 7
      drivers/thermal/thermal_sys.c
  62. 5 3
      include/acpi/acoutput.h
  63. 1 1
      include/acpi/acpixf.h
  64. 3 2
      include/acpi/processor.h
  65. 20 3
      include/linux/acpi.h
  66. 13 0
      include/linux/pnp.h

+ 66 - 0
Documentation/acpi/method-customizing.txt

@@ -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.

+ 19 - 0
Documentation/feature-removal-schedule.txt

@@ -474,3 +474,22 @@ Why:	Obsoleted by the adt7475 driver.
 Who:	Jean Delvare <khali@linux-fr.org>
 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>
+
+----------------------------

+ 64 - 50
Documentation/laptops/thinkpad-acpi.txt

@@ -1,7 +1,7 @@
 		     ThinkPad ACPI Extras Driver
 		     ThinkPad ACPI Extras Driver
 
 
-                            Version 0.23
-                          April 10th, 2009
+                            Version 0.24
+                        December 11th,  2009
 
 
                Borislav Deianov <borislav@users.sf.net>
                Borislav Deianov <borislav@users.sf.net>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
              Henrique de Moraes Holschuh <hmh@hmh.eng.br>
@@ -460,6 +460,8 @@ event	code	Key		Notes
 				For Lenovo ThinkPads with a new
 				For Lenovo ThinkPads with a new
 				BIOS, it has to be handled either
 				BIOS, it has to be handled either
 				by the ACPI OSI, or by userspace.
 				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
 0x1011	0x10	FN+END		Brightness down.  See brightness
 				up for details.
 				up for details.
 
 
@@ -582,46 +584,15 @@ with hotkey_report_mode.
 
 
 Brightness hotkey notes:
 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
 Bluetooth
@@ -1121,25 +1092,61 @@ WARNING:
     its level up and down at every change.
     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 up   >/proc/acpi/ibm/volume
 	echo down >/proc/acpi/ibm/volume
 	echo down >/proc/acpi/ibm/volume
 	echo mute >/proc/acpi/ibm/volume
 	echo mute >/proc/acpi/ibm/volume
+	echo unmute >/proc/acpi/ibm/volume
 	echo 'level <level>' >/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
 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 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
 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
 	0x0008			HKEY event interface, hotkeys
 	0x0010			Fan control
 	0x0010			Fan control
 	0x0020			Backlight brightness
 	0x0020			Backlight brightness
+	0x0040			Audio mixer/volume control
 
 
 There is also a kernel build option to enable more debugging
 There is also a kernel build option to enable more debugging
 information, which may be necessary to debug driver problems.
 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
 		and it is always able to disable hot keys.  Very old
 		thinkpads are properly supported.  hotkey_bios_mask
 		thinkpads are properly supported.  hotkey_bios_mask
 		is deprecated and marked for removal.
 		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.

+ 1 - 0
Documentation/thermal/sysfs-api.txt

@@ -206,6 +206,7 @@ passive
 	passive trip point for the zone. Activation is done by polling with
 	passive trip point for the zone. Activation is done by polling with
 	an interval of 1 second.
 	an interval of 1 second.
 	Unit: millidegrees Celsius
 	Unit: millidegrees Celsius
+	Valid values: 0 (disabled) or greater than 1000
 	RW, Optional
 	RW, Optional
 
 
 *****************************
 *****************************

+ 1 - 1
arch/x86/kernel/acpi/cstate.c

@@ -48,7 +48,7 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
 	 * P4, Core and beyond CPUs
 	 * P4, Core and beyond CPUs
 	 */
 	 */
 	if (c->x86_vendor == X86_VENDOR_INTEL &&
 	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;
 			flags->bm_control = 0;
 }
 }
 EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
 EXPORT_SYMBOL(acpi_processor_power_init_bm_check);

+ 2 - 1
drivers/acpi/acpi_pad.c

@@ -100,7 +100,8 @@ static void round_robin_cpu(unsigned int tsk_index)
 	struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
 	struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits);
 	cpumask_var_t tmp;
 	cpumask_var_t tmp;
 	int cpu;
 	int cpu;
-	unsigned long min_weight = -1, preferred_cpu;
+	unsigned long min_weight = -1;
+	unsigned long uninitialized_var(preferred_cpu);
 
 
 	if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
 	if (!alloc_cpumask_var(&tmp, GFP_KERNEL))
 		return;
 		return;

+ 6 - 3
drivers/acpi/acpica/acnamesp.h

@@ -296,6 +296,11 @@ acpi_ns_complex_repairs(struct acpi_predefined_data *data,
 			acpi_status validate_status,
 			acpi_status validate_status,
 			union acpi_operand_object **return_object_ptr);
 			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
  * nssearch - Namespace searching and entry
  */
  */
@@ -354,9 +359,7 @@ acpi_ns_externalize_name(u32 internal_name_length,
 			 const char *internal_name,
 			 const char *internal_name,
 			 u32 * converted_name_length, char **converted_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);
 void acpi_ns_terminate(void);
 
 

+ 5 - 1
drivers/acpi/acpica/acobject.h

@@ -180,7 +180,11 @@ struct acpi_object_method {
 	u8 sync_level;
 	u8 sync_level;
 	union acpi_operand_object *mutex;
 	union acpi_operand_object *mutex;
 	u8 *aml_start;
 	u8 *aml_start;
-	ACPI_INTERNAL_METHOD implementation;
+	union {
+		ACPI_INTERNAL_METHOD implementation;
+		union acpi_operand_object *handler;
+	} extra;
+
 	u32 aml_length;
 	u32 aml_length;
 	u8 thread_count;
 	u8 thread_count;
 	acpi_owner_id owner_id;
 	acpi_owner_id owner_id;

+ 1 - 1
drivers/acpi/acpica/dsmethod.c

@@ -414,7 +414,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread,
 	/* Invoke an internal method if necessary */
 	/* Invoke an internal method if necessary */
 
 
 	if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
 	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) {
 		if (status == AE_OK) {
 			status = AE_CTRL_TERMINATE;
 			status = AE_CTRL_TERMINATE;
 		}
 		}

+ 39 - 25
drivers/acpi/acpica/dswload.c

@@ -212,18 +212,19 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state,
 		case ACPI_TYPE_BUFFER:
 		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)
 			 *  Name (DEB, 0)
 			 *  Scope (DEB) { ... }
 			 *  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,
 			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)));
 					  acpi_ut_get_type_name(node->type)));
 
 
 			node->type = ACPI_TYPE_ANY;
 			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 */
 			/* All other types are an error */
 
 
 			ACPI_ERROR((AE_INFO,
 			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);
 			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:
 		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)
 			 *  Name (DEB, 0)
 			 *  Scope (DEB) { ... }
 			 *  Scope (DEB) { ... }
 			 */
 			 */
 			ACPI_WARNING((AE_INFO,
 			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)));
 				      acpi_ut_get_type_name(node->type)));
 
 
 			node->type = ACPI_TYPE_ANY;
 			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 */
 			/* All other types are an error */
 
 
 			ACPI_ERROR((AE_INFO,
 			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),
 				    acpi_ut_get_type_name(node->type),
-				    buffer_ptr));
+				    acpi_ut_get_node_name(node)));
 
 
 			return (AE_AML_OPERAND_TYPE);
 			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) {
 			if (walk_state->method_node) {
+				/*
+				 * Executing a method: initialize the region and unlock
+				 * the interpreter
+				 */
 				status =
 				status =
 				    acpi_ex_create_region(op->named.data,
 				    acpi_ex_create_region(op->named.data,
 							  op->named.length,
 							  op->named.length,
@@ -1058,21 +1076,17 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
 				if (ACPI_FAILURE(status)) {
 				if (ACPI_FAILURE(status)) {
 					return (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 =
 			status =
 			    acpi_ev_initialize_region
 			    acpi_ev_initialize_region
 			    (acpi_ns_get_attached_object(node), FALSE);
 			    (acpi_ns_get_attached_object(node), FALSE);
+			if (walk_state->method_node) {
+				acpi_ex_enter_interpreter();
+			}
+
 			if (ACPI_FAILURE(status)) {
 			if (ACPI_FAILURE(status)) {
 				/*
 				/*
 				 *  If AE_NOT_EXIST is returned, it is not fatal
 				 *  If AE_NOT_EXIST is returned, it is not fatal

+ 2 - 2
drivers/acpi/acpica/evregion.c

@@ -718,7 +718,7 @@ acpi_ev_install_handler(acpi_handle obj_handle,
 
 
 	/* Convert and validate the device 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) {
 	if (!node) {
 		return (AE_BAD_PARAMETER);
 		return (AE_BAD_PARAMETER);
 	}
 	}
@@ -1087,7 +1087,7 @@ acpi_ev_reg_run(acpi_handle obj_handle,
 
 
 	/* Convert and validate the device 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) {
 	if (!node) {
 		return (AE_BAD_PARAMETER);
 		return (AE_BAD_PARAMETER);
 	}
 	}

+ 15 - 0
drivers/acpi/acpica/evrgnini.c

@@ -575,6 +575,21 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
 				handler_obj = obj_desc->thermal_zone.handler;
 				handler_obj = obj_desc->thermal_zone.handler;
 				break;
 				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:
 			default:
 				/* Ignore other objects */
 				/* Ignore other objects */
 				break;
 				break;

+ 2 - 2
drivers/acpi/acpica/evxface.c

@@ -259,7 +259,7 @@ acpi_install_notify_handler(acpi_handle device,
 
 
 	/* Convert and validate the device handle */
 	/* Convert and validate the device handle */
 
 
-	node = acpi_ns_map_handle_to_node(device);
+	node = acpi_ns_validate_handle(device);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -425,7 +425,7 @@ acpi_remove_notify_handler(acpi_handle device,
 
 
 	/* Convert and validate the device handle */
 	/* Convert and validate the device handle */
 
 
-	node = acpi_ns_map_handle_to_node(device);
+	node = acpi_ns_validate_handle(device);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;

+ 2 - 2
drivers/acpi/acpica/evxfevnt.c

@@ -610,7 +610,7 @@ acpi_install_gpe_block(acpi_handle gpe_device,
 		return (status);
 		return (status);
 	}
 	}
 
 
-	node = acpi_ns_map_handle_to_node(gpe_device);
+	node = acpi_ns_validate_handle(gpe_device);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -698,7 +698,7 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device)
 		return (status);
 		return (status);
 	}
 	}
 
 
-	node = acpi_ns_map_handle_to_node(gpe_device);
+	node = acpi_ns_validate_handle(gpe_device);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;

+ 2 - 2
drivers/acpi/acpica/evxfregn.c

@@ -89,7 +89,7 @@ acpi_install_address_space_handler(acpi_handle device,
 
 
 	/* Convert and validate the device handle */
 	/* Convert and validate the device handle */
 
 
-	node = acpi_ns_map_handle_to_node(device);
+	node = acpi_ns_validate_handle(device);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -155,7 +155,7 @@ acpi_remove_address_space_handler(acpi_handle device,
 
 
 	/* Convert and validate the device handle */
 	/* Convert and validate the device handle */
 
 
-	node = acpi_ns_map_handle_to_node(device);
+	node = acpi_ns_validate_handle(device);
 	if (!node ||
 	if (!node ||
 	    ((node->type != ACPI_TYPE_DEVICE) &&
 	    ((node->type != ACPI_TYPE_DEVICE) &&
 	     (node->type != ACPI_TYPE_PROCESSOR) &&
 	     (node->type != ACPI_TYPE_PROCESSOR) &&

+ 9 - 9
drivers/acpi/acpica/exmutex.c

@@ -375,6 +375,15 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
 		return_ACPI_STATUS(AE_AML_MUTEX_NOT_ACQUIRED);
 		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.
 	 * The Mutex is owned, but this thread must be the owner.
 	 * Special case for Global Lock, any thread can release
 	 * 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);
 		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
 	 * 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
 	 * other words, the current level means that at least one mutex at that

+ 1 - 1
drivers/acpi/acpica/nsaccess.c

@@ -165,7 +165,7 @@ acpi_status acpi_ns_root_initialize(void)
 
 
 				obj_desc->method.method_flags =
 				obj_desc->method.method_flags =
 				    AML_METHOD_INTERNAL_ONLY;
 				    AML_METHOD_INTERNAL_ONLY;
-				obj_desc->method.implementation =
+				obj_desc->method.extra.implementation =
 				    acpi_ut_osi_implementation;
 				    acpi_ut_osi_implementation;
 #endif
 #endif
 				break;
 				break;

+ 1 - 1
drivers/acpi/acpica/nsdump.c

@@ -180,7 +180,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
 		return (AE_OK);
 		return (AE_OK);
 	}
 	}
 
 
-	this_node = acpi_ns_map_handle_to_node(obj_handle);
+	this_node = acpi_ns_validate_handle(obj_handle);
 	if (!this_node) {
 	if (!this_node) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n",
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n",
 				  obj_handle));
 				  obj_handle));

+ 18 - 0
drivers/acpi/acpica/nseval.c

@@ -381,6 +381,18 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
 				    method_obj->method.next_object);
 				    method_obj->method.next_object);
 	type = acpi_ns_get_type(parent_node);
 	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) */
 	/* Must clear next_object (acpi_ns_attach_object needs the field) */
 
 
 	method_obj->method.next_object = NULL;
 	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",
 	ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n",
 			  method_obj->method.aml_start));
 			  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 */
 	/* Detach the temporary method object */
 
 
 	acpi_ns_detach_object(parent_node);
 	acpi_ns_detach_object(parent_node);

+ 1 - 1
drivers/acpi/acpica/nsnames.c

@@ -232,7 +232,7 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle,
 
 
 	ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, 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) {
 	if (!node) {
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 	}

+ 37 - 56
drivers/acpi/acpica/nspredef.c

@@ -216,29 +216,38 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
 	data->pathname = pathname;
 	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,
 	status = acpi_ns_check_object_type(data, return_object_ptr,
 					   predefined->info.expected_btypes,
 					   predefined->info.expected_btypes,
 					   ACPI_NOT_PACKAGE_ELEMENT);
 					   ACPI_NOT_PACKAGE_ELEMENT);
 	if (ACPI_FAILURE(status)) {
 	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);
 		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);
 	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
 	 * If the object validation failed or if we successfully repaired one
 	 * or more objects, mark the parent node to suppress further warning
 	 * 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,
 			  data->pathname, package->ret_info.type,
 			  return_object->package.count));
 			  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 */
 	/* Extract package count and elements array */
 
 
 	elements = return_object->package.elements;
 	elements = return_object->package.elements;
@@ -461,11 +477,11 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
 		if (count < expected_count) {
 		if (count < expected_count) {
 			goto package_too_small;
 			goto package_too_small;
 		} else if (count > expected_count) {
 		} 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 */
 		/* 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_package;
 	union acpi_operand_object **sub_elements;
 	union acpi_operand_object **sub_elements;
 	acpi_status status;
 	acpi_status status;
-	u8 non_trailing_null = FALSE;
 	u32 expected_count;
 	u32 expected_count;
 	u32 i;
 	u32 i;
 	u32 j;
 	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++) {
 	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_package = *elements;
 		sub_elements = sub_package->package.elements;
 		sub_elements = sub_package->package.elements;
 
 

+ 373 - 74
drivers/acpi/acpica/nsrepair.c

@@ -45,11 +45,50 @@
 #include "accommon.h"
 #include "accommon.h"
 #include "acnamesp.h"
 #include "acnamesp.h"
 #include "acinterp.h"
 #include "acinterp.h"
-#include "acpredef.h"
 
 
 #define _COMPONENT          ACPI_NAMESPACE
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsrepair")
 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
  * FUNCTION:    acpi_ns_repair_object
@@ -68,6 +107,7 @@ ACPI_MODULE_NAME("nsrepair")
  *              not expected.
  *              not expected.
  *
  *
  ******************************************************************************/
  ******************************************************************************/
+
 acpi_status
 acpi_status
 acpi_ns_repair_object(struct acpi_predefined_data *data,
 acpi_ns_repair_object(struct acpi_predefined_data *data,
 		      u32 expected_btypes,
 		      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 *return_object = *return_object_ptr;
 	union acpi_operand_object *new_object;
 	union acpi_operand_object *new_object;
-	acpi_size length;
 	acpi_status status;
 	acpi_status status;
 
 
+	ACPI_FUNCTION_NAME(ns_repair_object);
+
 	/*
 	/*
 	 * At this point, we know that the type of the returned object was not
 	 * 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
 	 * 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:
 	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);
 			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
 		 * conversion, no transform performed on the buffer data. The best
 		 * example of this is the _BIF method, where the string data from
 		 * example of this is the _BIF method, where the string data from
 		 * the battery is often (incorrectly) returned as buffer object(s).
 		 * the battery is often (incorrectly) returned as buffer object(s).
 		 */
 		 */
 		length = 0;
 		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++;
 			length++;
 		}
 		}
 
 
@@ -117,94 +331,176 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
 		 * terminated at Length+1.
 		 * terminated at Length+1.
 		 */
 		 */
 		ACPI_MEMCPY(new_object->string.pointer,
 		ACPI_MEMCPY(new_object->string.pointer,
-			    return_object->buffer.pointer, length);
+			    original_object->buffer.pointer, length);
 		break;
 		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:
 	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);
 				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);
 		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);
 	return (AE_OK);
 }
 }
 
 
@@ -238,6 +534,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
 {
 {
 	union acpi_operand_object *pkg_obj_desc;
 	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
 	 * Create the new outer package and populate it. The new package will
 	 * have a single element, the lone subpackage.
 	 * 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;
 	*obj_desc_ptr = pkg_obj_desc;
 	data->flags |= ACPI_OBJECT_REPAIRED;
 	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);
 	return (AE_OK);
 }
 }

+ 156 - 39
drivers/acpi/acpica/nsrepair2.c

@@ -45,6 +45,7 @@
 #include <acpi/acpi.h>
 #include <acpi/acpi.h>
 #include "accommon.h"
 #include "accommon.h"
 #include "acnamesp.h"
 #include "acnamesp.h"
+#include "acpredef.h"
 
 
 #define _COMPONENT          ACPI_NAMESPACE
 #define _COMPONENT          ACPI_NAMESPACE
 ACPI_MODULE_NAME("nsrepair2")
 ACPI_MODULE_NAME("nsrepair2")
@@ -73,6 +74,10 @@ static acpi_status
 acpi_ns_repair_ALR(struct acpi_predefined_data *data,
 acpi_ns_repair_ALR(struct acpi_predefined_data *data,
 		   union acpi_operand_object **return_object_ptr);
 		   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
 static acpi_status
 acpi_ns_repair_PSS(struct acpi_predefined_data *data,
 acpi_ns_repair_PSS(struct acpi_predefined_data *data,
 		   union acpi_operand_object **return_object_ptr);
 		   union acpi_operand_object **return_object_ptr);
@@ -88,9 +93,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
 			  u32 sort_index,
 			  u32 sort_index,
 			  u8 sort_direction, char *sort_key_name);
 			  u8 sort_direction, char *sort_key_name);
 
 
-static acpi_status
-acpi_ns_remove_null_elements(union acpi_operand_object *package);
-
 static acpi_status
 static acpi_status
 acpi_ns_sort_list(union acpi_operand_object **elements,
 acpi_ns_sort_list(union acpi_operand_object **elements,
 		  u32 count, u32 index, u8 sort_direction);
 		  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
  * This table contains the names of the predefined methods for which we can
  * perform more complex repairs.
  * 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[] = {
 static const struct acpi_repair_info acpi_ns_repairable_names[] = {
 	{"_ALR", acpi_ns_repair_ALR},
 	{"_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},
 	{"_PSS", acpi_ns_repair_PSS},
 	{"_TSS", acpi_ns_repair_TSS},
 	{"_TSS", acpi_ns_repair_TSS},
 	{{0, 0, 0, 0}, NULL}	/* Table terminator */
 	{{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
  * FUNCTION:    acpi_ns_complex_repairs
@@ -213,6 +225,94 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
 	return (status);
 	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
  * FUNCTION:    acpi_ns_repair_TSS
@@ -345,6 +445,8 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
 	u32 previous_value;
 	u32 previous_value;
 	acpi_status status;
 	acpi_status status;
 
 
+	ACPI_FUNCTION_NAME(ns_check_sorted_list);
+
 	/* The top-level object must be a package */
 	/* The top-level object must be a package */
 
 
 	if (return_object->common.type != ACPI_TYPE_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_elements = return_object->package.elements;
 	outer_element_count = return_object->package.count;
 	outer_element_count = return_object->package.count;
 	if (!outer_element_count) {
 	if (!outer_element_count) {
@@ -422,10 +510,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
 
 
 			data->flags |= ACPI_OBJECT_REPAIRED;
 			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);
 			return (AE_OK);
 		}
 		}
 
 
@@ -440,36 +527,63 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
  *
  *
  * FUNCTION:    acpi_ns_remove_null_elements
  * 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 **source;
 	union acpi_operand_object **dest;
 	union acpi_operand_object **dest;
-	acpi_status status = AE_OK;
 	u32 count;
 	u32 count;
 	u32 new_count;
 	u32 new_count;
 	u32 i;
 	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;
 	count = obj_desc->package.count;
 	new_count = count;
 	new_count = count;
 
 
 	source = obj_desc->package.elements;
 	source = obj_desc->package.elements;
 	dest = source;
 	dest = source;
 
 
-	/* Examine all elements of the package object */
+	/* Examine all elements of the package object, remove nulls */
 
 
 	for (i = 0; i < count; i++) {
 	for (i = 0; i < count; i++) {
 		if (!*source) {
 		if (!*source) {
-			status = AE_NULL_ENTRY;
 			new_count--;
 			new_count--;
 		} else {
 		} else {
 			*dest = *source;
 			*dest = *source;
@@ -478,15 +592,18 @@ acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
 		source++;
 		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 */
 		/* NULL terminate list and update the package count */
 
 
 		*dest = NULL;
 		*dest = NULL;
 		obj_desc->package.count = new_count;
 		obj_desc->package.count = new_count;
 	}
 	}
-
-	return (status);
 }
 }
 
 
 /******************************************************************************
 /******************************************************************************

+ 11 - 46
drivers/acpi/acpica/nsutils.c

@@ -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
  *       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();
 	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));
 	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
  * FUNCTION:    acpi_ns_terminate

+ 5 - 5
drivers/acpi/acpica/nsxfeval.c

@@ -190,7 +190,7 @@ acpi_evaluate_object(acpi_handle handle,
 
 
 	/* Convert and validate the device 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) {
 	if (!info->prefix_node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto cleanup;
 		goto cleanup;
@@ -552,7 +552,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
 		return (status);
 		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);
 	status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 	if (ACPI_FAILURE(status)) {
 	if (ACPI_FAILURE(status)) {
 		return (status);
 		return (status);
@@ -729,7 +729,7 @@ acpi_attach_data(acpi_handle obj_handle,
 
 
 	/* Convert and validate the handle */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(obj_handle);
+	node = acpi_ns_validate_handle(obj_handle);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -775,7 +775,7 @@ acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler)
 
 
 	/* Convert and validate the handle */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(obj_handle);
+	node = acpi_ns_validate_handle(obj_handle);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		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 */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(obj_handle);
+	node = acpi_ns_validate_handle(obj_handle);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;

+ 5 - 5
drivers/acpi/acpica/nsxfname.c

@@ -93,7 +93,7 @@ acpi_get_handle(acpi_handle parent,
 	/* Convert a parent handle to a prefix node */
 	/* Convert a parent handle to a prefix node */
 
 
 	if (parent) {
 	if (parent) {
-		prefix_node = acpi_ns_map_handle_to_node(parent);
+		prefix_node = acpi_ns_validate_handle(parent);
 		if (!prefix_node) {
 		if (!prefix_node) {
 			return (AE_BAD_PARAMETER);
 			return (AE_BAD_PARAMETER);
 		}
 		}
@@ -114,7 +114,7 @@ acpi_get_handle(acpi_handle parent,
 
 
 		if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) {
 		if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) {
 			*ret_handle =
 			*ret_handle =
-			    acpi_ns_convert_entry_to_handle(acpi_gbl_root_node);
+			    ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node);
 			return (AE_OK);
 			return (AE_OK);
 		}
 		}
 	} else if (!prefix_node) {
 	} else if (!prefix_node) {
@@ -129,7 +129,7 @@ acpi_get_handle(acpi_handle parent,
 	status =
 	status =
 	    acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node);
 	    acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node);
 	if (ACPI_SUCCESS(status)) {
 	if (ACPI_SUCCESS(status)) {
-		*ret_handle = acpi_ns_convert_entry_to_handle(node);
+		*ret_handle = ACPI_CAST_PTR(acpi_handle, node);
 	}
 	}
 
 
 	return (status);
 	return (status);
@@ -186,7 +186,7 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer)
 		return (status);
 		return (status);
 	}
 	}
 
 
-	node = acpi_ns_map_handle_to_node(handle);
+	node = acpi_ns_validate_handle(handle);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -291,7 +291,7 @@ acpi_get_object_info(acpi_handle handle,
 		goto cleanup;
 		goto cleanup;
 	}
 	}
 
 
-	node = acpi_ns_map_handle_to_node(handle);
+	node = acpi_ns_validate_handle(handle);
 	if (!node) {
 	if (!node) {
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		return (AE_BAD_PARAMETER);
 		return (AE_BAD_PARAMETER);

+ 7 - 7
drivers/acpi/acpica/nsxfobj.c

@@ -79,7 +79,7 @@ acpi_status acpi_get_id(acpi_handle handle, acpi_owner_id * ret_id)
 
 
 	/* Convert and validate the handle */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(handle);
+	node = acpi_ns_validate_handle(handle);
 	if (!node) {
 	if (!node) {
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		return (AE_BAD_PARAMETER);
 		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 */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(handle);
+	node = acpi_ns_validate_handle(handle);
 	if (!node) {
 	if (!node) {
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
 		return (AE_BAD_PARAMETER);
 		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 */
 	/* Convert and validate the handle */
 
 
-	node = acpi_ns_map_handle_to_node(handle);
+	node = acpi_ns_validate_handle(handle);
 	if (!node) {
 	if (!node) {
 		status = AE_BAD_PARAMETER;
 		status = AE_BAD_PARAMETER;
 		goto unlock_and_exit;
 		goto unlock_and_exit;
@@ -191,7 +191,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
 	/* Get the parent entry */
 	/* Get the parent entry */
 
 
 	parent_node = acpi_ns_get_parent_node(node);
 	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 */
 	/* 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 */
 		/* 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) {
 		if (!parent_node) {
 			status = AE_BAD_PARAMETER;
 			status = AE_BAD_PARAMETER;
 			goto unlock_and_exit;
 			goto unlock_and_exit;
@@ -260,7 +260,7 @@ acpi_get_next_object(acpi_object_type type,
 		/* Non-null handle, ignore the parent */
 		/* Non-null handle, ignore the parent */
 		/* Convert and validate the handle */
 		/* Convert and validate the handle */
 
 
-		child_node = acpi_ns_map_handle_to_node(child);
+		child_node = acpi_ns_validate_handle(child);
 		if (!child_node) {
 		if (!child_node) {
 			status = AE_BAD_PARAMETER;
 			status = AE_BAD_PARAMETER;
 			goto unlock_and_exit;
 			goto unlock_and_exit;
@@ -276,7 +276,7 @@ acpi_get_next_object(acpi_object_type type,
 	}
 	}
 
 
 	if (ret_handle) {
 	if (ret_handle) {
-		*ret_handle = acpi_ns_convert_entry_to_handle(node);
+		*ret_handle = ACPI_CAST_PTR(acpi_handle, node);
 	}
 	}
 
 
       unlock_and_exit:
       unlock_and_exit:

+ 2 - 1
drivers/acpi/acpica/psxface.c

@@ -287,7 +287,8 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
 	/* Invoke an internal method if necessary */
 	/* Invoke an internal method if necessary */
 
 
 	if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
 	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;
 		info->return_object = walk_state->return_desc;
 
 
 		/* Cleanup states */
 		/* Cleanup states */

+ 1 - 1
drivers/acpi/acpica/rsxface.c

@@ -104,7 +104,7 @@ acpi_rs_validate_parameters(acpi_handle device_handle,
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 	}
 
 
-	node = acpi_ns_map_handle_to_node(device_handle);
+	node = acpi_ns_validate_handle(device_handle);
 	if (!node) {
 	if (!node) {
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 	}

+ 14 - 13
drivers/acpi/acpica/utcopy.c

@@ -323,11 +323,11 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type,
  * RETURN:      Status
  * RETURN:      Status
  *
  *
  * DESCRIPTION: This function is called to place a package object in a user
  * 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 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
  * FUNCTION:    acpi_ut_copy_iobject_to_eobject
  *
  *
  * PARAMETERS:  internal_object     - The internal object to be converted
  * 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
  * 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
  * PARAMETERS:  external_object     - The external object to be converted
  *              internal_object     - Where the internal object is returned
  *              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.
  * 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
  * 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.
  *              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
  * 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
  * DESCRIPTION: This function is called to copy an internal package object
  *              into another 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
  * 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
  *              dest_desc           - Where the copied object is returned
+ *              walk_state          - Current walk state
  *
  *
  * RETURN:      Status
  * RETURN:      Status
  *
  *

+ 1 - 1
drivers/acpi/battery.c

@@ -831,7 +831,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
 					dev_name(&device->dev), event,
 					dev_name(&device->dev), event,
 					acpi_battery_present(battery));
 					acpi_battery_present(battery));
 #ifdef CONFIG_ACPI_SYSFS_POWER
 #ifdef CONFIG_ACPI_SYSFS_POWER
-	/* acpi_batter_update could remove power_supply object */
+	/* acpi_battery_update could remove power_supply object */
 	if (battery->bat.dev)
 	if (battery->bat.dev)
 		kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE);
 		kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE);
 #endif
 #endif

+ 148 - 0
drivers/acpi/bus.c

@@ -344,6 +344,152 @@ bool acpi_bus_can_wakeup(acpi_handle handle)
 
 
 EXPORT_SYMBOL(acpi_bus_can_wakeup);
 EXPORT_SYMBOL(acpi_bus_can_wakeup);
 
 
+static void acpi_print_osc_error(acpi_handle handle,
+	struct acpi_osc_context *context, char *error)
+{
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
+	int i;
+
+	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))
+		printk(KERN_DEBUG "%s\n", error);
+	else {
+		printk(KERN_DEBUG "%s:%s\n", (char *)buffer.pointer, error);
+		kfree(buffer.pointer);
+	}
+	printk(KERN_DEBUG"_OSC request data:");
+	for (i = 0; i < context->cap.length; i += sizeof(u32))
+		printk("%x ", *((u32 *)(context->cap.pointer + i)));
+	printk("\n");
+}
+
+static u8 hex_val(unsigned char c)
+{
+	return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10;
+}
+
+static acpi_status acpi_str_to_uuid(char *str, u8 *uuid)
+{
+	int i;
+	static int opc_map_to_uuid[16] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21,
+		24, 26, 28, 30, 32, 34};
+
+	if (strlen(str) != 36)
+		return AE_BAD_PARAMETER;
+	for (i = 0; i < 36; i++) {
+		if (i == 8 || i == 13 || i == 18 || i == 23) {
+			if (str[i] != '-')
+				return AE_BAD_PARAMETER;
+		} else if (!isxdigit(str[i]))
+			return AE_BAD_PARAMETER;
+	}
+	for (i = 0; i < 16; i++) {
+		uuid[i] = hex_val(str[opc_map_to_uuid[i]]) << 4;
+		uuid[i] |= hex_val(str[opc_map_to_uuid[i] + 1]);
+	}
+	return AE_OK;
+}
+
+acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context)
+{
+	acpi_status status;
+	struct acpi_object_list input;
+	union acpi_object in_params[4];
+	union acpi_object *out_obj;
+	u8 uuid[16];
+	u32 errors;
+
+	if (!context)
+		return AE_ERROR;
+	if (ACPI_FAILURE(acpi_str_to_uuid(context->uuid_str, uuid)))
+		return AE_ERROR;
+	context->ret.length = ACPI_ALLOCATE_BUFFER;
+	context->ret.pointer = NULL;
+
+	/* Setting up input parameters */
+	input.count = 4;
+	input.pointer = in_params;
+	in_params[0].type 		= ACPI_TYPE_BUFFER;
+	in_params[0].buffer.length 	= 16;
+	in_params[0].buffer.pointer	= uuid;
+	in_params[1].type 		= ACPI_TYPE_INTEGER;
+	in_params[1].integer.value 	= context->rev;
+	in_params[2].type 		= ACPI_TYPE_INTEGER;
+	in_params[2].integer.value	= context->cap.length/sizeof(u32);
+	in_params[3].type		= ACPI_TYPE_BUFFER;
+	in_params[3].buffer.length 	= context->cap.length;
+	in_params[3].buffer.pointer 	= context->cap.pointer;
+
+	status = acpi_evaluate_object(handle, "_OSC", &input, &context->ret);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	/* return buffer should have the same length as cap buffer */
+	if (context->ret.length != context->cap.length)
+		return AE_NULL_OBJECT;
+
+	out_obj = context->ret.pointer;
+	if (out_obj->type != ACPI_TYPE_BUFFER) {
+		acpi_print_osc_error(handle, context,
+			"_OSC evaluation returned wrong type");
+		status = AE_TYPE;
+		goto out_kfree;
+	}
+	/* Need to ignore the bit0 in result code */
+	errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
+	if (errors) {
+		if (errors & OSC_REQUEST_ERROR)
+			acpi_print_osc_error(handle, context,
+				"_OSC request failed");
+		if (errors & OSC_INVALID_UUID_ERROR)
+			acpi_print_osc_error(handle, context,
+				"_OSC invalid UUID");
+		if (errors & OSC_INVALID_REVISION_ERROR)
+			acpi_print_osc_error(handle, context,
+				"_OSC invalid revision");
+		if (errors & OSC_CAPABILITIES_MASK_ERROR) {
+			if (((u32 *)context->cap.pointer)[OSC_QUERY_TYPE]
+			    & OSC_QUERY_ENABLE)
+				goto out_success;
+			status = AE_SUPPORT;
+			goto out_kfree;
+		}
+		status = AE_ERROR;
+		goto out_kfree;
+	}
+out_success:
+	return AE_OK;
+
+out_kfree:
+	kfree(context->ret.pointer);
+	context->ret.pointer = NULL;
+	return status;
+}
+EXPORT_SYMBOL(acpi_run_osc);
+
+static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
+static void acpi_bus_osc_support(void)
+{
+	u32 capbuf[2];
+	struct acpi_osc_context context = {
+		.uuid_str = sb_uuid_str,
+		.rev = 1,
+		.cap.length = 8,
+		.cap.pointer = capbuf,
+	};
+	acpi_handle handle;
+
+	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+	capbuf[OSC_SUPPORT_TYPE] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */
+#ifdef CONFIG_ACPI_PROCESSOR_AGGREGATOR
+	capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PAD_SUPPORT;
+#endif
+	if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))
+		return;
+	if (ACPI_SUCCESS(acpi_run_osc(handle, &context)))
+		kfree(context.ret.pointer);
+	/* do we need to check the returned cap? Sounds no */
+}
+
 /* --------------------------------------------------------------------------
 /* --------------------------------------------------------------------------
                                 Event Management
                                 Event Management
    -------------------------------------------------------------------------- */
    -------------------------------------------------------------------------- */
@@ -734,6 +880,8 @@ static int __init acpi_bus_init(void)
 	status = acpi_ec_ecdt_probe();
 	status = acpi_ec_ecdt_probe();
 	/* Ignore result. Not having an ECDT is not fatal. */
 	/* Ignore result. Not having an ECDT is not fatal. */
 
 
+	acpi_bus_osc_support();
+
 	status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
 	status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
 	if (ACPI_FAILURE(status)) {
 	if (ACPI_FAILURE(status)) {
 		printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
 		printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");

+ 7 - 0
drivers/acpi/button.c

@@ -282,6 +282,13 @@ static int acpi_lid_send_state(struct acpi_device *device)
 	if (ret == NOTIFY_DONE)
 	if (ret == NOTIFY_DONE)
 		ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
 		ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
 						   device);
 						   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;
 	return ret;
 }
 }
 
 

+ 83 - 1
drivers/acpi/debug.c

@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
+#include <linux/debugfs.h>
 #include <asm/uaccess.h>
 #include <asm/uaccess.h>
 #include <acpi/acpi_drivers.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,
 module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
 		  NULL, 0644);
 		  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)
                               FS Interface (/proc)
    -------------------------------------------------------------------------- */
    -------------------------------------------------------------------------- */
@@ -286,7 +361,7 @@ static const struct file_operations acpi_system_debug_proc_fops = {
 };
 };
 #endif
 #endif
 
 
-int __init acpi_debug_init(void)
+int __init acpi_procfs_init(void)
 {
 {
 #ifdef CONFIG_ACPI_PROCFS
 #ifdef CONFIG_ACPI_PROCFS
 	struct proc_dir_entry *entry;
 	struct proc_dir_entry *entry;
@@ -321,3 +396,10 @@ int __init acpi_debug_init(void)
 	return 0;
 	return 0;
 #endif
 #endif
 }
 }
+
+int __init acpi_debug_init(void)
+{
+	acpi_debugfs_init();
+	acpi_procfs_init();
+	return 0;
+}

+ 91 - 170
drivers/acpi/dock.c

@@ -50,7 +50,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
 	" before undocking");
 	" before undocking");
 
 
 static struct atomic_notifier_head dock_notifier_list;
 static struct atomic_notifier_head dock_notifier_list;
-static char dock_device_name[] = "dock";
 
 
 static const struct acpi_device_id dock_device_ids[] = {
 static const struct acpi_device_id dock_device_ids[] = {
 	{"LNXDOCK", 0},
 	{"LNXDOCK", 0},
@@ -93,40 +92,30 @@ struct dock_dependent_device {
  *                         Dock Dependent device functions                   *
  *                         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;
 	struct dock_dependent_device *dd;
 
 
 	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
 	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);
 	spin_lock(&ds->dd_lock);
 	list_add_tail(&dd->list, &ds->dependent_devices);
 	list_add_tail(&dd->list, &ds->dependent_devices);
 	spin_unlock(&ds->dd_lock);
 	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)
 static int is_ejectable_bay(acpi_handle handle)
 {
 {
 	acpi_handle phandle;
 	acpi_handle phandle;
+
 	if (!is_ejectable(handle))
 	if (!is_ejectable(handle))
 		return 0;
 		return 0;
 	if (is_battery(handle) || is_ata(handle))
 	if (is_battery(handle) || is_ata(handle))
@@ -275,14 +265,13 @@ int is_dock_device(acpi_handle handle)
 
 
 	if (is_dock(handle))
 	if (is_dock(handle))
 		return 1;
 		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))
 		if (find_dock_dependent_device(dock_station, handle))
 			return 1;
 			return 1;
-	}
 
 
 	return 0;
 	return 0;
 }
 }
-
 EXPORT_SYMBOL_GPL(is_dock_device);
 EXPORT_SYMBOL_GPL(is_dock_device);
 
 
 /**
 /**
@@ -305,8 +294,6 @@ static int dock_present(struct dock_station *ds)
 	return 0;
 	return 0;
 }
 }
 
 
-
-
 /**
 /**
  * dock_create_acpi_device - add new devices to acpi
  * dock_create_acpi_device - add new devices to acpi
  * @handle - handle of the device to add
  * @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)
 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;
 	struct acpi_device *parent_device;
 	acpi_handle parent;
 	acpi_handle parent;
 	int ret;
 	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,
 		ret = acpi_bus_add(&device, parent_device, handle,
 			ACPI_BUS_TYPE_DEVICE);
 			ACPI_BUS_TYPE_DEVICE);
 		if (ret) {
 		if (ret) {
-			pr_debug("error adding bus, %x\n",
-				-ret);
+			pr_debug("error adding bus, %x\n", -ret);
 			return NULL;
 			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
  * hotplug_dock_devices - insert or remove devices on the dock station
  * @ds: 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
 	 * 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)
 		if (dd->ops && dd->ops->handler)
 			dd->ops->handler(dd->handle, event, dd->context);
 			dd->ops->handler(dd->handle, event, dd->context);
-	}
 
 
 	/*
 	/*
 	 * Now make sure that an acpi_device is created for each
 	 * 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)
 	list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
 		if (dd->ops && dd->ops->uevent)
 		if (dd->ops && dd->ops->uevent)
 			dd->ops->uevent(dd->handle, event, dd->context);
 			dd->ops->uevent(dd->handle, event, dd->context);
+
 	if (num != DOCK_EVENT)
 	if (num != DOCK_EVENT)
 		kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
 		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.type = ACPI_TYPE_INTEGER;
 	arg.integer.value = 1;
 	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");
 		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);
 	return atomic_notifier_chain_register(&dock_notifier_list, nb);
 }
 }
-
 EXPORT_SYMBOL_GPL(register_dock_notifier);
 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);
 	atomic_notifier_chain_unregister(&dock_notifier_list, nb);
 }
 }
-
 EXPORT_SYMBOL_GPL(unregister_dock_notifier);
 EXPORT_SYMBOL_GPL(unregister_dock_notifier);
 
 
 /**
 /**
@@ -636,7 +619,6 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
 
 
 	return ret;
 	return ret;
 }
 }
-
 EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
 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);
 			dock_del_hotplug_device(dock_station, dd);
 	}
 	}
 }
 }
-
 EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
 EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
 
 
 /**
 /**
@@ -772,7 +753,7 @@ struct dock_data {
 
 
 static void acpi_dock_deferred_cb(void *context)
 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);
 	dock_notify(data->handle, data->event, data->ds);
 	kfree(data);
 	kfree(data);
@@ -782,23 +763,22 @@ static int acpi_dock_notifier_call(struct notifier_block *this,
 	unsigned long event, void *data)
 	unsigned long event, void *data)
 {
 {
 	struct dock_station *dock_station;
 	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
 	if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
 	   && event != ACPI_NOTIFY_EJECT_REQUEST)
 	   && event != ACPI_NOTIFY_EJECT_REQUEST)
 		return 0;
 		return 0;
 	list_for_each_entry(dock_station, &dock_stations, sibling) {
 	list_for_each_entry(dock_station, &dock_stations, sibling) {
 		if (dock_station->handle == handle) {
 		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;
 				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 ;
 			return 0 ;
 		}
 		}
 	}
 	}
@@ -826,7 +806,6 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
 	acpi_status status;
 	acpi_status status;
 	acpi_handle tmp, parent;
 	acpi_handle tmp, parent;
 	struct dock_station *ds = context;
 	struct dock_station *ds = context;
-	struct dock_dependent_device *dd;
 
 
 	status = acpi_bus_get_ejd(handle, &tmp);
 	status = acpi_bus_get_ejd(handle, &tmp);
 	if (ACPI_FAILURE(status)) {
 	if (ACPI_FAILURE(status)) {
@@ -840,11 +819,9 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
 			goto fdd_out;
 			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:
 fdd_out:
 	return AE_OK;
 	return AE_OK;
 }
 }
@@ -857,8 +834,7 @@ static ssize_t show_docked(struct device *dev,
 {
 {
 	struct acpi_device *tmp;
 	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)))
 	if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
 		return snprintf(buf, PAGE_SIZE, "1\n");
 		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,
 static ssize_t show_flags(struct device *dev,
 			  struct device_attribute *attr, char *buf)
 			  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);
 	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)
 			   const char *buf, size_t count)
 {
 {
 	int ret;
 	int ret;
-	struct dock_station *dock_station = *((struct dock_station **)
-		dev->platform_data);
+	struct dock_station *dock_station = dev->platform_data;
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
@@ -905,8 +879,7 @@ static ssize_t show_dock_uid(struct device *dev,
 			     struct device_attribute *attr, char *buf)
 			     struct device_attribute *attr, char *buf)
 {
 {
 	unsigned long long lbuf;
 	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,
 	acpi_status status = acpi_evaluate_integer(dock_station->handle,
 					"_UID", NULL, &lbuf);
 					"_UID", NULL, &lbuf);
 	if (ACPI_FAILURE(status))
 	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,
 static ssize_t show_dock_type(struct device *dev,
 		struct device_attribute *attr, char *buf)
 		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;
 	char *type;
 
 
 	if (dock_station->flags & DOCK_IS_DOCK)
 	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 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
  * dock_add - add a new dock station
  * @handle: the dock station handle
  * @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)
 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->handle = handle;
+	dock_station->dock_device = dd;
 	dock_station->last_dock_time = jiffies - HZ;
 	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);
 	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);
 	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 */
 	/* 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))
 	if (is_dock(handle))
 		dock_station->flags |= DOCK_IS_DOCK;
 		dock_station->flags |= DOCK_IS_DOCK;
@@ -986,47 +962,9 @@ static int dock_add(acpi_handle handle)
 	if (is_battery(handle))
 	if (is_battery(handle))
 		dock_station->flags |= DOCK_IS_BAT;
 		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)
 	if (ret)
-		printk(KERN_ERR"Error %d adding sysfs file\n", ret);
+		goto err_unregister;
 
 
 	/* Find dependent devices */
 	/* Find dependent devices */
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
@@ -1034,58 +972,43 @@ static int dock_add(acpi_handle handle)
 			    dock_station, NULL);
 			    dock_station, NULL);
 
 
 	/* add the dock station as a device dependent on itself */
 	/* 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++;
 	dock_station_count++;
 	list_add(&dock_station->sibling, &dock_stations);
 	list_add(&dock_station->sibling, &dock_stations);
 	return 0;
 	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;
 	return ret;
 }
 }
 
 
 /**
 /**
  * dock_remove - free up resources related to the dock station
  * 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 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)
 	if (!dock_station_count)
 		return 0;
 		return 0;
 
 
 	/* remove dependent devices */
 	/* 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 */
 	/* 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);
 	platform_device_unregister(dock_device);
 
 
-	/* free dock station memory */
-	kfree(dock_station);
-	dock_station = NULL;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1103,11 +1026,10 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
 {
 	acpi_status status = AE_OK;
 	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;
 			status = AE_CTRL_TERMINATE;
-		}
-	}
+
 	return status;
 	return status;
 }
 }
 
 
@@ -1145,8 +1067,7 @@ static int __init dock_init(void)
 
 
 static void __exit dock_exit(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);
 	unregister_acpi_bus_notifier(&dock_acpi_notifier);
 	list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
 	list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)

+ 1 - 1
drivers/acpi/fan.c

@@ -267,7 +267,7 @@ static int acpi_fan_add(struct acpi_device *device)
 		goto end;
 		goto end;
 	}
 	}
 
 
-	dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id);
+	dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
 
 
 	device->driver_data = cdev;
 	device->driver_data = cdev;
 	result = sysfs_create_link(&device->dev.kobj,
 	result = sysfs_create_link(&device->dev.kobj,

+ 6 - 15
drivers/acpi/numa.c

@@ -28,6 +28,7 @@
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
 #include <linux/acpi.h>
 #include <linux/acpi.h>
+#include <linux/numa.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_bus.h>
 
 
 #define PREFIX "ACPI: "
 #define PREFIX "ACPI: "
@@ -40,14 +41,14 @@ static nodemask_t nodes_found_map = NODE_MASK_NONE;
 
 
 /* maps to convert between proximity domain and logical node ID */
 /* maps to convert between proximity domain and logical node ID */
 static int pxm_to_node_map[MAX_PXM_DOMAINS]
 static int pxm_to_node_map[MAX_PXM_DOMAINS]
-				= { [0 ... MAX_PXM_DOMAINS - 1] = NID_INVAL };
+			= { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE };
 static int node_to_pxm_map[MAX_NUMNODES]
 static int node_to_pxm_map[MAX_NUMNODES]
-				= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
+			= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
 
 
 int pxm_to_node(int pxm)
 int pxm_to_node(int pxm)
 {
 {
 	if (pxm < 0)
 	if (pxm < 0)
-		return NID_INVAL;
+		return NUMA_NO_NODE;
 	return pxm_to_node_map[pxm];
 	return pxm_to_node_map[pxm];
 }
 }
 
 
@@ -68,9 +69,9 @@ int acpi_map_pxm_to_node(int pxm)
 {
 {
 	int node = pxm_to_node_map[pxm];
 	int node = pxm_to_node_map[pxm];
 
 
-	if (node < 0){
+	if (node < 0) {
 		if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
 		if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
-			return NID_INVAL;
+			return NUMA_NO_NODE;
 		node = first_unset_node(nodes_found_map);
 		node = first_unset_node(nodes_found_map);
 		__acpi_map_pxm_to_node(pxm, node);
 		__acpi_map_pxm_to_node(pxm, node);
 		node_set(node, nodes_found_map);
 		node_set(node, nodes_found_map);
@@ -79,16 +80,6 @@ int acpi_map_pxm_to_node(int pxm)
 	return node;
 	return node;
 }
 }
 
 
-#if 0
-void __cpuinit acpi_unmap_pxm_to_node(int node)
-{
-	int pxm = node_to_pxm_map[node];
-	pxm_to_node_map[pxm] = NID_INVAL;
-	node_to_pxm_map[node] = PXM_INVAL;
-	node_clear(node, nodes_found_map);
-}
-#endif  /*  0  */
-
 static void __init
 static void __init
 acpi_table_print_srat_entry(struct acpi_subtable_header *header)
 acpi_table_print_srat_entry(struct acpi_subtable_header *header)
 {
 {

+ 1 - 1
drivers/acpi/osl.c

@@ -1118,7 +1118,7 @@ __setup("acpi_enforce_resources=", acpi_enforce_resources_setup);
 
 
 /* Check for resource conflicts between ACPI OperationRegions and native
 /* Check for resource conflicts between ACPI OperationRegions and native
  * drivers */
  * drivers */
-int acpi_check_resource_conflict(struct resource *res)
+int acpi_check_resource_conflict(const struct resource *res)
 {
 {
 	struct acpi_res_list *res_list_elem;
 	struct acpi_res_list *res_list_elem;
 	int ioport;
 	int ioport;

+ 14 - 62
drivers/acpi/pci_root.c

@@ -202,72 +202,24 @@ static void acpi_pci_bridge_scan(struct acpi_device *device)
 		}
 		}
 }
 }
 
 
-static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
-			  0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766";
 
 
 static acpi_status acpi_pci_run_osc(acpi_handle handle,
 static acpi_status acpi_pci_run_osc(acpi_handle handle,
 				    const u32 *capbuf, u32 *retval)
 				    const u32 *capbuf, u32 *retval)
 {
 {
+	struct acpi_osc_context context = {
+		.uuid_str = pci_osc_uuid_str,
+		.rev = 1,
+		.cap.length = 12,
+		.cap.pointer = (void *)capbuf,
+	};
 	acpi_status status;
 	acpi_status status;
-	struct acpi_object_list input;
-	union acpi_object in_params[4];
-	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
-	union acpi_object *out_obj;
-	u32 errors;
-
-	/* Setting up input parameters */
-	input.count = 4;
-	input.pointer = in_params;
-	in_params[0].type 		= ACPI_TYPE_BUFFER;
-	in_params[0].buffer.length 	= 16;
-	in_params[0].buffer.pointer	= OSC_UUID;
-	in_params[1].type 		= ACPI_TYPE_INTEGER;
-	in_params[1].integer.value 	= 1;
-	in_params[2].type 		= ACPI_TYPE_INTEGER;
-	in_params[2].integer.value	= 3;
-	in_params[3].type		= ACPI_TYPE_BUFFER;
-	in_params[3].buffer.length 	= 12;
-	in_params[3].buffer.pointer 	= (u8 *)capbuf;
-
-	status = acpi_evaluate_object(handle, "_OSC", &input, &output);
-	if (ACPI_FAILURE(status))
-		return status;
 
 
-	if (!output.length)
-		return AE_NULL_OBJECT;
-
-	out_obj = output.pointer;
-	if (out_obj->type != ACPI_TYPE_BUFFER) {
-		printk(KERN_DEBUG "_OSC evaluation returned wrong type\n");
-		status = AE_TYPE;
-		goto out_kfree;
-	}
-	/* Need to ignore the bit0 in result code */
-	errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
-	if (errors) {
-		if (errors & OSC_REQUEST_ERROR)
-			printk(KERN_DEBUG "_OSC request failed\n");
-		if (errors & OSC_INVALID_UUID_ERROR)
-			printk(KERN_DEBUG "_OSC invalid UUID\n");
-		if (errors & OSC_INVALID_REVISION_ERROR)
-			printk(KERN_DEBUG "_OSC invalid revision\n");
-		if (errors & OSC_CAPABILITIES_MASK_ERROR) {
-			if (capbuf[OSC_QUERY_TYPE] & OSC_QUERY_ENABLE)
-				goto out_success;
-			printk(KERN_DEBUG
-			       "Firmware did not grant requested _OSC control\n");
-			status = AE_SUPPORT;
-			goto out_kfree;
-		}
-		status = AE_ERROR;
-		goto out_kfree;
+	status = acpi_run_osc(handle, &context);
+	if (ACPI_SUCCESS(status)) {
+		*retval = *((u32 *)(context.ret.pointer + 8));
+		kfree(context.ret.pointer);
 	}
 	}
-out_success:
-	*retval = *((u32 *)(out_obj->buffer.pointer + 8));
-	status = AE_OK;
-
-out_kfree:
-	kfree(output.pointer);
 	return status;
 	return status;
 }
 }
 
 
@@ -277,10 +229,10 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
 	u32 support_set, result, capbuf[3];
 	u32 support_set, result, capbuf[3];
 
 
 	/* do _OSC query for all possible controls */
 	/* do _OSC query for all possible controls */
-	support_set = root->osc_support_set | (flags & OSC_SUPPORT_MASKS);
+	support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS);
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
 	capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
 	capbuf[OSC_SUPPORT_TYPE] = support_set;
 	capbuf[OSC_SUPPORT_TYPE] = support_set;
-	capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+	capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS;
 
 
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	status = acpi_pci_run_osc(root->device->handle, capbuf, &result);
 	if (ACPI_SUCCESS(status)) {
 	if (ACPI_SUCCESS(status)) {
@@ -427,7 +379,7 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
 	if (ACPI_FAILURE(status))
 	if (ACPI_FAILURE(status))
 		return status;
 		return status;
 
 
-	control_req = (flags & OSC_CONTROL_MASKS);
+	control_req = (flags & OSC_PCI_CONTROL_MASKS);
 	if (!control_req)
 	if (!control_req)
 		return AE_TYPE;
 		return AE_TYPE;
 
 

+ 5 - 5
drivers/acpi/processor_core.c

@@ -353,7 +353,7 @@ static int acpi_processor_info_open_fs(struct inode *inode, struct file *file)
 			   PDE(inode)->data);
 			   PDE(inode)->data);
 }
 }
 
 
-static int acpi_processor_add_fs(struct acpi_device *device)
+static int __cpuinit acpi_processor_add_fs(struct acpi_device *device)
 {
 {
 	struct proc_dir_entry *entry = NULL;
 	struct proc_dir_entry *entry = NULL;
 
 
@@ -722,7 +722,7 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event)
 	switch (event) {
 	switch (event) {
 	case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
 	case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
 		saved = pr->performance_platform_limit;
 		saved = pr->performance_platform_limit;
-		acpi_processor_ppc_has_changed(pr);
+		acpi_processor_ppc_has_changed(pr, 1);
 		if (saved == pr->performance_platform_limit)
 		if (saved == pr->performance_platform_limit)
 			break;
 			break;
 		acpi_bus_generate_proc_event(device, event,
 		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);
 	struct acpi_processor *pr = per_cpu(processors, cpu);
 
 
 	if (action == CPU_ONLINE && pr) {
 	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_cst_has_changed(pr);
 		acpi_processor_tstate_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);
 	arch_acpi_processor_cleanup_pdc(pr);
 
 
 #ifdef CONFIG_CPU_FREQ
 #ifdef CONFIG_CPU_FREQ
-	acpi_processor_ppc_has_changed(pr);
+	acpi_processor_ppc_has_changed(pr, 0);
 #endif
 #endif
 	acpi_processor_get_throttling_info(pr);
 	acpi_processor_get_throttling_info(pr);
 	acpi_processor_get_limit_info(pr);
 	acpi_processor_get_limit_info(pr);
@@ -845,7 +845,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 		goto err_power_exit;
 		goto err_power_exit;
 	}
 	}
 
 
-	dev_info(&device->dev, "registered as cooling_device%d\n",
+	dev_dbg(&device->dev, "registered as cooling_device%d\n",
 		 pr->cdev->id);
 		 pr->cdev->id);
 
 
 	result = sysfs_create_link(&device->dev.kobj,
 	result = sysfs_create_link(&device->dev.kobj,

+ 8 - 3
drivers/acpi/processor_idle.c

@@ -164,7 +164,7 @@ static void lapic_timer_check_state(int state, struct acpi_processor *pr,
 		pr->power.timer_broadcast_on_state = state;
 		pr->power.timer_broadcast_on_state = state;
 }
 }
 
 
-static void lapic_timer_propagate_broadcast(void *arg)
+static void __lapic_timer_propagate_broadcast(void *arg)
 {
 {
 	struct acpi_processor *pr = (struct acpi_processor *) arg;
 	struct acpi_processor *pr = (struct acpi_processor *) arg;
 	unsigned long reason;
 	unsigned long reason;
@@ -175,6 +175,12 @@ static void lapic_timer_propagate_broadcast(void *arg)
 	clockevents_notify(reason, &pr->id);
 	clockevents_notify(reason, &pr->id);
 }
 }
 
 
+static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
+{
+	smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast,
+				 (void *)pr, 1);
+}
+
 /* Power(C) State timer broadcast control */
 /* Power(C) State timer broadcast control */
 static void lapic_timer_state_broadcast(struct acpi_processor *pr,
 static void lapic_timer_state_broadcast(struct acpi_processor *pr,
 				       struct acpi_processor_cx *cx,
 				       struct acpi_processor_cx *cx,
@@ -638,8 +644,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
 		working++;
 		working++;
 	}
 	}
 
 
-	smp_call_function_single(pr->id, lapic_timer_propagate_broadcast,
-				 pr, 1);
+	lapic_timer_propagate_broadcast(pr);
 
 
 	return (working);
 	return (working);
 }
 }

+ 47 - 3
drivers/acpi/processor_perflib.c

@@ -152,15 +152,59 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
 	return 0;
 	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;
 	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;
 		return 0;
+	}
 
 
 	ret = acpi_processor_get_platform_limit(pr);
 	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)
 	if (ret < 0)
 		return (ret);
 		return (ret);
 	else
 	else

+ 7 - 0
drivers/acpi/thermal.c

@@ -1052,6 +1052,13 @@ static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset)
 				   acpi_device_bid(device));
 				   acpi_device_bid(device));
 		}
 		}
 		seq_puts(seq, "\n");
 		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++) {
 	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) {

+ 109 - 9
drivers/char/ipmi/ipmi_si_intf.c

@@ -64,6 +64,7 @@
 #include <linux/dmi.h>
 #include <linux/dmi.h>
 #include <linux/string.h>
 #include <linux/string.h>
 #include <linux/ctype.h>
 #include <linux/ctype.h>
+#include <linux/pnp.h>
 
 
 #ifdef CONFIG_PPC_OF
 #ifdef CONFIG_PPC_OF
 #include <linux/of_device.h>
 #include <linux/of_device.h>
@@ -1919,7 +1920,7 @@ struct SPMITable {
 	s8      spmi_id[1]; /* A '\0' terminated array starts here. */
 	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;
 	struct smi_info  *info;
 	u8 		 addr_space;
 	u8 		 addr_space;
@@ -1940,7 +1941,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	info->addr_source = "ACPI";
+	info->addr_source = "SPMI";
 
 
 	/* Figure out the interface type. */
 	/* Figure out the interface type. */
 	switch (spmi->InterfaceType) {
 	switch (spmi->InterfaceType) {
@@ -2002,7 +2003,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
 	return 0;
 	return 0;
 }
 }
 
 
-static __devinit void acpi_find_bmc(void)
+static __devinit void spmi_find_bmc(void)
 {
 {
 	acpi_status      status;
 	acpi_status      status;
 	struct SPMITable *spmi;
 	struct SPMITable *spmi;
@@ -2020,9 +2021,106 @@ static __devinit void acpi_find_bmc(void)
 		if (status != AE_OK)
 		if (status != AE_OK)
 			return;
 			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
 #endif
 
 
 #ifdef CONFIG_DMI
 #ifdef CONFIG_DMI
@@ -2202,7 +2300,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
 	int rv;
 	int rv;
 	int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
 	int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
 	struct smi_info *info;
 	struct smi_info *info;
-	int first_reg_offset = 0;
 
 
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (!info)
 	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_cleanup = ipmi_pci_cleanup;
 	info->addr_source_data = pdev;
 	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) {
 	if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
 		info->io_setup = port_setup;
 		info->io_setup = port_setup;
 		info->io.addr_type = IPMI_IO_ADDR_SPACE;
 		info->io.addr_type = IPMI_IO_ADDR_SPACE;
@@ -3108,7 +3202,10 @@ static __devinit int init_ipmi_si(void)
 #endif
 #endif
 
 
 #ifdef CONFIG_ACPI
 #ifdef CONFIG_ACPI
-	acpi_find_bmc();
+	spmi_find_bmc();
+#endif
+#ifdef CONFIG_PNP
+	pnp_register_driver(&ipmi_pnp_driver);
 #endif
 #endif
 
 
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
@@ -3233,6 +3330,9 @@ static __exit void cleanup_ipmi_si(void)
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
 	pci_unregister_driver(&ipmi_pci_driver);
 	pci_unregister_driver(&ipmi_pci_driver);
 #endif
 #endif
+#ifdef CONFIG_PNP
+	pnp_unregister_driver(&ipmi_pnp_driver);
+#endif
 
 
 #ifdef CONFIG_PPC_OF
 #ifdef CONFIG_PPC_OF
 	of_unregister_platform_driver(&ipmi_of_platform_driver);
 	of_unregister_platform_driver(&ipmi_of_platform_driver);

+ 0 - 13
drivers/misc/Kconfig

@@ -248,19 +248,6 @@ config SGI_GRU_DEBUG
 	This option enables addition debugging code for the SGI GRU driver. If
 	This option enables addition debugging code for the SGI GRU driver. If
 	you are unsure, say N.
 	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
 config ISL29003
 	tristate "Intersil ISL29003 ambient light sensor"
 	tristate "Intersil ISL29003 ambient light sensor"
 	depends on I2C && SYSFS
 	depends on I2C && SYSFS

+ 29 - 0
drivers/platform/x86/Kconfig

@@ -334,6 +334,8 @@ config EEEPC_LAPTOP
 	depends on HOTPLUG_PCI
 	depends on HOTPLUG_PCI
 	select BACKLIGHT_CLASS_DEVICE
 	select BACKLIGHT_CLASS_DEVICE
 	select HWMON
 	select HWMON
+	select LEDS_CLASS
+	select NEW_LEDS
 	---help---
 	---help---
 	  This driver supports the Fn-Fx keys on Eee PC laptops.
 	  This driver supports the Fn-Fx keys on Eee PC laptops.
 
 
@@ -365,6 +367,18 @@ config ACPI_WMI
 	  It is safe to enable this driver even if your DSDT doesn't define
 	  It is safe to enable this driver even if your DSDT doesn't define
 	  any ACPI-WMI devices.
 	  any ACPI-WMI devices.
 
 
+config MSI_WMI
+	tristate "MSI WMI extras"
+	depends on ACPI_WMI
+	depends on INPUT
+	depends on BACKLIGHT_CLASS_DEVICE
+	select INPUT_SPARSEKMAP
+	help
+	 Say Y here if you want to support WMI-based hotkeys on MSI laptops.
+
+	 To compile this driver as a module, choose M here: the module will
+	 be called msi-wmi.
+
 config ACPI_ASUS
 config ACPI_ASUS
 	tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
 	tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
 	depends on ACPI
 	depends on ACPI
@@ -435,4 +449,19 @@ config ACPI_TOSHIBA
 
 
 	  If you have a legacy free Toshiba laptop (such as the Libretto L1
 	  If you have a legacy free Toshiba laptop (such as the Libretto L1
 	  series), say Y.
 	  series), say Y.
+
+config TOSHIBA_BT_RFKILL
+	tristate "Toshiba Bluetooth RFKill switch support"
+	depends on ACPI
+	---help---
+	  This driver adds support for Bluetooth events for the RFKill
+	  switch on modern Toshiba laptops with full ACPI support and
+	  an RFKill switch.
+
+	  This driver handles RFKill events for the TOS6205 Bluetooth,
+	  and re-enables it when the switch is set back to the 'on'
+	  position.
+
+	  If you have a modern Toshiba laptop with a Bluetooth and an
+	  RFKill switch (such as the Portege R500), say Y.
 endif # X86_PLATFORM_DEVICES
 endif # X86_PLATFORM_DEVICES

+ 2 - 0
drivers/platform/x86/Makefile

@@ -18,6 +18,8 @@ obj-$(CONFIG_FUJITSU_LAPTOP)	+= fujitsu-laptop.o
 obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o
 obj-$(CONFIG_PANASONIC_LAPTOP)	+= panasonic-laptop.o
 obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o
 obj-$(CONFIG_INTEL_MENLOW)	+= intel_menlow.o
 obj-$(CONFIG_ACPI_WMI)		+= wmi.o
 obj-$(CONFIG_ACPI_WMI)		+= wmi.o
+obj-$(CONFIG_MSI_WMI)		+= msi-wmi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_ACPI_ASUS)		+= asus_acpi.o
 obj-$(CONFIG_TOPSTAR_LAPTOP)	+= topstar-laptop.o
 obj-$(CONFIG_TOPSTAR_LAPTOP)	+= topstar-laptop.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
 obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
+obj-$(CONFIG_TOSHIBA_BT_RFKILL)	+= toshiba_bluetooth.o

+ 37 - 26
drivers/platform/x86/acerhdf.c

@@ -52,7 +52,7 @@
  */
  */
 #undef START_IN_KERNEL_MODE
 #undef START_IN_KERNEL_MODE
 
 
-#define DRV_VER "0.5.18"
+#define DRV_VER "0.5.20"
 
 
 /*
 /*
  * According to the Atom N270 datasheet,
  * According to the Atom N270 datasheet,
@@ -112,12 +112,14 @@ module_param_string(force_product, force_product, 16, 0);
 MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
 MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
 
 
 /*
 /*
- * cmd_off: to switch the fan completely off / to check if the fan is off
+ * cmd_off: to switch the fan completely off
+ * chk_off: to check if the fan is off
  *	cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
  *	cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
  *		the fan speed depending on the temperature
  *		the fan speed depending on the temperature
  */
  */
 struct fancmd {
 struct fancmd {
 	u8 cmd_off;
 	u8 cmd_off;
+	u8 chk_off;
 	u8 cmd_auto;
 	u8 cmd_auto;
 };
 };
 
 
@@ -134,32 +136,41 @@ struct bios_settings_t {
 /* Register addresses and values for different BIOS versions */
 /* Register addresses and values for different BIOS versions */
 static const struct bios_settings_t bios_tbl[] = {
 static const struct bios_settings_t bios_tbl[] = {
 	/* AOA110 */
 	/* AOA110 */
-	{"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
-	{"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
-	{"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
-	{"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
-	{"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
-	{"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} },
-	{"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
-	{"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
-	{"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+	{"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x1f, 0x00} },
+	{"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x1f, 0x00} },
+	{"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
+	{"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
+	{"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
+	{"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0xaf, 0x00} },
+	{"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x21, 0x00} },
+	{"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x21, 0x00} },
+	{"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x21, 0x00} },
 	/* AOA150 */
 	/* AOA150 */
-	{"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
-	{"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	/* Acer 1410 */
+	{"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} },
 	/* special BIOS / other */
 	/* special BIOS / other */
-	{"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
-	{"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} },
-	{"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} },
-	{"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
-	{"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} },
+	{"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x21, 0x00} },
+	{"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Gateway         ", "LT31            ", "v1.3103 ", 0x55, 0x58,
+		{0x10, 0x0f, 0x00} },
+	{"Gateway         ", "LT31            ", "v1.3201 ", 0x55, 0x58,
+		{0x10, 0x0f, 0x00} },
+	{"Gateway         ", "LT31            ", "v1.3302 ", 0x55, 0x58,
+		{0x10, 0x0f, 0x00} },
+	{"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x21, 0x00} },
+	{"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} },
+	{"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x21, 0x00} },
+	{"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} },
 	/* pewpew-terminator */
 	/* pewpew-terminator */
-	{"", "", "", 0, 0, {0, 0} }
+	{"", "", "", 0, 0, {0, 0, 0} }
 };
 };
 
 
 static const struct bios_settings_t *bios_cfg __read_mostly;
 static const struct bios_settings_t *bios_cfg __read_mostly;
@@ -183,7 +194,7 @@ static int acerhdf_get_fanstate(int *state)
 	if (ec_read(bios_cfg->fanreg, &fan))
 	if (ec_read(bios_cfg->fanreg, &fan))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (fan != bios_cfg->cmd.cmd_off)
+	if (fan != bios_cfg->cmd.chk_off)
 		*state = ACERHDF_FAN_AUTO;
 		*state = ACERHDF_FAN_AUTO;
 	else
 	else
 		*state = ACERHDF_FAN_OFF;
 		*state = ACERHDF_FAN_OFF;

+ 13 - 12
drivers/platform/x86/asus-laptop.c

@@ -221,6 +221,7 @@ static struct asus_hotk *hotk;
  */
  */
 static const struct acpi_device_id asus_device_ids[] = {
 static const struct acpi_device_id asus_device_ids[] = {
 	{"ATK0100", 0},
 	{"ATK0100", 0},
+	{"ATK0101", 0},
 	{"", 0},
 	{"", 0},
 };
 };
 MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 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 = {
 static struct acpi_driver asus_hotk_driver = {
 	.name = ASUS_HOTK_NAME,
 	.name = ASUS_HOTK_NAME,
 	.class = ASUS_HOTK_CLASS,
 	.class = ASUS_HOTK_CLASS,
+	.owner = THIS_MODULE,
 	.ids = asus_device_ids,
 	.ids = asus_device_ids,
 	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
 	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
 	.ops = {
 	.ops = {
@@ -293,6 +295,11 @@ struct key_entry {
 enum { KE_KEY, KE_END };
 enum { KE_KEY, KE_END };
 
 
 static struct key_entry asus_keymap[] = {
 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, 0x30, KEY_VOLUMEUP},
 	{KE_KEY, 0x31, KEY_VOLUMEDOWN},
 	{KE_KEY, 0x31, KEY_VOLUMEDOWN},
 	{KE_KEY, 0x32, KEY_MUTE},
 	{KE_KEY, 0x32, KEY_MUTE},
@@ -312,8 +319,11 @@ static struct key_entry asus_keymap[] = {
 	{KE_KEY, 0x5F, KEY_WLAN},
 	{KE_KEY, 0x5F, KEY_WLAN},
 	{KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
 	{KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
 	{KE_KEY, 0x61, 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, 0x82, KEY_CAMERA},
+	{KE_KEY, 0x88, KEY_WLAN },
 	{KE_KEY, 0x8A, KEY_PROG1},
 	{KE_KEY, 0x8A, KEY_PROG1},
 	{KE_KEY, 0x95, KEY_MEDIA},
 	{KE_KEY, 0x95, KEY_MEDIA},
 	{KE_KEY, 0x99, KEY_PHONE},
 	{KE_KEY, 0x99, KEY_PHONE},
@@ -1240,9 +1250,6 @@ static int asus_hotk_add(struct acpi_device *device)
 {
 {
 	int result;
 	int result;
 
 
-	if (!device)
-		return -EINVAL;
-
 	pr_notice("Asus Laptop Support version %s\n",
 	pr_notice("Asus Laptop Support version %s\n",
 	       ASUS_LAPTOP_VERSION);
 	       ASUS_LAPTOP_VERSION);
 
 
@@ -1283,8 +1290,8 @@ static int asus_hotk_add(struct acpi_device *device)
 	hotk->ledd_status = 0xFFF;
 	hotk->ledd_status = 0xFFF;
 
 
 	/* Set initial values of light sensor and level */
 	/* 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)
 	if (ls_switch_handle)
 		set_light_sens_switch(hotk->light_switch);
 		set_light_sens_switch(hotk->light_switch);
@@ -1306,9 +1313,6 @@ end:
 
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
 {
-	if (!device || !acpi_driver_data(device))
-		return -EINVAL;
-
 	kfree(hotk->name);
 	kfree(hotk->name);
 	kfree(hotk);
 	kfree(hotk);
 
 
@@ -1444,9 +1448,6 @@ static int __init asus_laptop_init(void)
 {
 {
 	int result;
 	int result;
 
 
-	if (acpi_disabled)
-		return -ENODEV;
-
 	result = acpi_bus_register_driver(&asus_hotk_driver);
 	result = acpi_bus_register_driver(&asus_hotk_driver);
 	if (result < 0)
 	if (result < 0)
 		return result;
 		return result;

+ 5 - 14
drivers/platform/x86/asus_acpi.c

@@ -466,6 +466,7 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
 static struct acpi_driver asus_hotk_driver = {
 static struct acpi_driver asus_hotk_driver = {
 	.name = "asus_acpi",
 	.name = "asus_acpi",
 	.class = ACPI_HOTK_CLASS,
 	.class = ACPI_HOTK_CLASS,
+	.owner = THIS_MODULE,
 	.ids = asus_device_ids,
 	.ids = asus_device_ids,
 	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
 	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
 	.ops = {
 	.ops = {
@@ -1334,9 +1335,6 @@ static int asus_hotk_add(struct acpi_device *device)
 	acpi_status status = AE_OK;
 	acpi_status status = AE_OK;
 	int result;
 	int result;
 
 
-	if (!device)
-		return -EINVAL;
-
 	printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
 	printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
 	       ASUS_ACPI_VERSION);
 	       ASUS_ACPI_VERSION);
 
 
@@ -1392,9 +1390,6 @@ end:
 
 
 static int asus_hotk_remove(struct acpi_device *device, int type)
 static int asus_hotk_remove(struct acpi_device *device, int type)
 {
 {
-	if (!device || !acpi_driver_data(device))
-		return -EINVAL;
-
 	asus_hotk_remove_fs(device);
 	asus_hotk_remove_fs(device);
 
 
 	kfree(hotk);
 	kfree(hotk);
@@ -1422,21 +1417,17 @@ static int __init asus_acpi_init(void)
 {
 {
 	int result;
 	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);
 	asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
 	if (!asus_proc_dir) {
 	if (!asus_proc_dir) {
 		printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
 		printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
+		acpi_bus_unregister_driver(&asus_hotk_driver);
 		return -ENODEV;
 		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
 	 * 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
 	 * for ASUS systems, but there's currently no way to probe the

+ 62 - 24
drivers/platform/x86/dell-laptop.c

@@ -58,6 +58,14 @@ static int da_command_code;
 static int da_num_tokens;
 static int da_num_tokens;
 static struct calling_interface_token *da_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 backlight_device *dell_backlight_device;
 static struct rfkill *wifi_rfkill;
 static struct rfkill *wifi_rfkill;
 static struct rfkill *bluetooth_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 */
 	/* Final token is a terminator, so we don't want to copy it */
 	int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
 	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;
 	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) {
 	switch (dm->type) {
 	case 0xd4: /* Indexed IO */
 	case 0xd4: /* Indexed IO */
@@ -197,8 +205,8 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 	dell_send_request(&buffer, 17, 11);
 	dell_send_request(&buffer, 17, 11);
 	status = buffer.output[1];
 	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 = {
 static const struct rfkill_ops dell_rfkill_ops = {
@@ -206,7 +214,7 @@ static const struct rfkill_ops dell_rfkill_ops = {
 	.query = dell_rfkill_query,
 	.query = dell_rfkill_query,
 };
 };
 
 
-static int dell_setup_rfkill(void)
+static int __init dell_setup_rfkill(void)
 {
 {
 	struct calling_interface_buffer buffer;
 	struct calling_interface_buffer buffer;
 	int status;
 	int status;
@@ -217,7 +225,8 @@ static int dell_setup_rfkill(void)
 	status = buffer.output[1];
 	status = buffer.output[1];
 
 
 	if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
 	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);
 					   &dell_rfkill_ops, (void *) 1);
 		if (!wifi_rfkill) {
 		if (!wifi_rfkill) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
@@ -229,7 +238,8 @@ static int dell_setup_rfkill(void)
 	}
 	}
 
 
 	if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
 	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,
 						RFKILL_TYPE_BLUETOOTH,
 						&dell_rfkill_ops, (void *) 2);
 						&dell_rfkill_ops, (void *) 2);
 		if (!bluetooth_rfkill) {
 		if (!bluetooth_rfkill) {
@@ -242,7 +252,9 @@ static int dell_setup_rfkill(void)
 	}
 	}
 
 
 	if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
 	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);
 					   &dell_rfkill_ops, (void *) 3);
 		if (!wwan_rfkill) {
 		if (!wwan_rfkill) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
@@ -268,6 +280,22 @@ err_wifi:
 	return ret;
 	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)
 static int dell_send_intensity(struct backlight_device *bd)
 {
 {
 	struct calling_interface_buffer buffer;
 	struct calling_interface_buffer buffer;
@@ -326,11 +354,23 @@ static int __init dell_init(void)
 		return -ENODEV;
 		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();
 	ret = dell_setup_rfkill();
 
 
 	if (ret) {
 	if (ret) {
 		printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
 		printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
-		goto out;
+		goto fail_rfkill;
 	}
 	}
 
 
 #ifdef CONFIG_ACPI
 #ifdef CONFIG_ACPI
@@ -352,13 +392,13 @@ static int __init dell_init(void)
 	if (max_intensity) {
 	if (max_intensity) {
 		dell_backlight_device = backlight_device_register(
 		dell_backlight_device = backlight_device_register(
 			"dell_backlight",
 			"dell_backlight",
-			NULL, NULL,
+			&platform_device->dev, NULL,
 			&dell_ops);
 			&dell_ops);
 
 
 		if (IS_ERR(dell_backlight_device)) {
 		if (IS_ERR(dell_backlight_device)) {
 			ret = PTR_ERR(dell_backlight_device);
 			ret = PTR_ERR(dell_backlight_device);
 			dell_backlight_device = NULL;
 			dell_backlight_device = NULL;
-			goto out;
+			goto fail_backlight;
 		}
 		}
 
 
 		dell_backlight_device->props.max_brightness = max_intensity;
 		dell_backlight_device->props.max_brightness = max_intensity;
@@ -368,13 +408,16 @@ static int __init dell_init(void)
 	}
 	}
 
 
 	return 0;
 	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);
 	kfree(da_tokens);
 	return ret;
 	return ret;
 }
 }
@@ -382,12 +425,7 @@ out:
 static void __exit dell_exit(void)
 static void __exit dell_exit(void)
 {
 {
 	backlight_device_unregister(dell_backlight_device);
 	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);
 module_init(dell_init);

+ 116 - 13
drivers/platform/x86/dell-wmi.c

@@ -31,6 +31,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_drivers.h>
 #include <linux/acpi.h>
 #include <linux/acpi.h>
 #include <linux/string.h>
 #include <linux/string.h>
+#include <linux/dmi.h>
 
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
 MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -38,6 +39,8 @@ MODULE_LICENSE("GPL");
 
 
 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
 
 
+static int acpi_video;
+
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
 
 
 struct key_entry {
 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.
  * 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, 0xe045, KEY_PROG1},
 	{KE_KEY, 0xe009, KEY_EJECTCD},
 	{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
 	/* The next device is at offset 6, the active devices are at
 	   offset 8 and the attached devices at offset 10 */
 	   offset 8 and the attached devices at offset 10 */
-	{KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
+	{KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
 
 
 	{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
 	{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
 
 
@@ -96,6 +99,47 @@ static struct key_entry dell_wmi_keymap[] = {
 	{KE_END, 0}
 	{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 input_dev *dell_wmi_input_dev;
 
 
 static struct key_entry *dell_wmi_get_entry_by_scancode(int code)
 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;
 	obj = (union acpi_object *)response.pointer;
 
 
 	if (obj && obj->type == ACPI_TYPE_BUFFER) {
 	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_report_key(dell_wmi_input_dev, key->keycode, 1);
 			input_sync(dell_wmi_input_dev);
 			input_sync(dell_wmi_input_dev);
 			input_report_key(dell_wmi_input_dev, key->keycode, 0);
 			input_report_key(dell_wmi_input_dev, key->keycode, 0);
 			input_sync(dell_wmi_input_dev);
 			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)
 static int __init dell_wmi_input_setup(void)
 {
 {
 	struct key_entry *key;
 	struct key_entry *key;
@@ -226,6 +324,9 @@ static int __init dell_wmi_init(void)
 	int err;
 	int err;
 
 
 	if (wmi_has_guid(DELL_EVENT_GUID)) {
 	if (wmi_has_guid(DELL_EVENT_GUID)) {
+
+		dmi_walk(find_hk_type, NULL);
+
 		err = dell_wmi_input_setup();
 		err = dell_wmi_input_setup();
 
 
 		if (err)
 		if (err)
@@ -240,6 +341,8 @@ static int __init dell_wmi_init(void)
 			return err;
 			return err;
 		}
 		}
 
 
+		acpi_video = acpi_video_backlight_support();
+
 	} else
 	} else
 		printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n");
 		printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n");
 
 

+ 799 - 642
drivers/platform/x86/eeepc-laptop.c

@@ -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:
  *  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
  *  ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
@@ -34,20 +34,23 @@
 #include <linux/rfkill.h>
 #include <linux/rfkill.h>
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/pci_hotplug.h>
 #include <linux/pci_hotplug.h>
+#include <linux/leds.h>
 
 
 #define EEEPC_LAPTOP_VERSION	"0.1"
 #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
  * Definitions for Asus EeePC
  */
  */
-#define	NOTIFY_WLAN_ON	0x10
 #define NOTIFY_BRN_MIN	0x20
 #define NOTIFY_BRN_MIN	0x20
 #define NOTIFY_BRN_MAX	0x2f
 #define NOTIFY_BRN_MAX	0x2f
 
 
@@ -117,58 +120,6 @@ static const char *cm_setv[] = {
 	NULL, NULL, "PBPS", "TPDS"
 	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 {
 struct key_entry {
 	char type;
 	char type;
 	u8 code;
 	u8 code;
@@ -177,7 +128,7 @@ struct key_entry {
 
 
 enum { KE_KEY, KE_END };
 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 */
 	/* Sleep already handled via generic ACPI code */
 	{KE_KEY, 0x10, KEY_WLAN },
 	{KE_KEY, 0x10, KEY_WLAN },
 	{KE_KEY, 0x11, KEY_WLAN },
 	{KE_KEY, 0x11, KEY_WLAN },
@@ -185,77 +136,56 @@ static struct key_entry eeepc_keymap[] = {
 	{KE_KEY, 0x13, KEY_MUTE },
 	{KE_KEY, 0x13, KEY_MUTE },
 	{KE_KEY, 0x14, KEY_VOLUMEDOWN },
 	{KE_KEY, 0x14, KEY_VOLUMEDOWN },
 	{KE_KEY, 0x15, KEY_VOLUMEUP },
 	{KE_KEY, 0x15, KEY_VOLUMEUP },
+	{KE_KEY, 0x16, KEY_DISPLAY_OFF },
 	{KE_KEY, 0x1a, KEY_COFFEE },
 	{KE_KEY, 0x1a, KEY_COFFEE },
 	{KE_KEY, 0x1b, KEY_ZOOM },
 	{KE_KEY, 0x1b, KEY_ZOOM },
 	{KE_KEY, 0x1c, KEY_PROG2 },
 	{KE_KEY, 0x1c, KEY_PROG2 },
 	{KE_KEY, 0x1d, KEY_PROG3 },
 	{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, 0x30, KEY_SWITCHVIDEOMODE },
 	{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
 	{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
 	{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
 	{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
+	{KE_KEY, 0x37, KEY_F13 }, /* Disable Touchpad */
+	{KE_KEY, 0x38, KEY_F14 },
 	{KE_END, 0},
 	{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
  * 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;
 	struct acpi_object_list params;
 	union acpi_object in_obj;
 	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.type = ACPI_TYPE_INTEGER;
 	in_obj.integer.value = val;
 	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);
 	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
  * Sys helpers
  */
  */
@@ -372,60 +277,63 @@ static int parse_arg(const char *buf, unsigned long count, int *val)
 	return count;
 	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;
 	int rv, value;
 
 
 	rv = parse_arg(buf, count, &value);
 	rv = parse_arg(buf, count, &value);
 	if (rv > 0)
 	if (rv > 0)
-		value = set_acpi(cm, value);
+		value = set_acpi(eeepc, cm, value);
 	if (value < 0)
 	if (value < 0)
-		return value;
+		return -EIO;
 	return rv;
 	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)
 	if (value < 0)
-		return value;
+		return -EIO;
 	return sprintf(buf, "%d\n", value);
 	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,			\
 	static ssize_t show_##_name(struct device *dev,			\
 				    struct device_attribute *attr,	\
 				    struct device_attribute *attr,	\
 				    char *buf)				\
 				    char *buf)				\
 	{								\
 	{								\
-		return show_sys_acpi(_cm, buf);				\
+		return show_sys_acpi(dev, _cm, buf);			\
 	}								\
 	}								\
 	static ssize_t store_##_name(struct device *dev,		\
 	static ssize_t store_##_name(struct device *dev,		\
 				     struct device_attribute *attr,	\
 				     struct device_attribute *attr,	\
 				     const char *buf, size_t count)	\
 				     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 = {		\
 	static struct device_attribute dev_attr_##_name = {		\
 		.attr = {						\
 		.attr = {						\
 			.name = __stringify(_name),			\
 			.name = __stringify(_name),			\
-			.mode = 0644 },					\
+			.mode = _mode },				\
 		.show   = show_##_name,					\
 		.show   = show_##_name,					\
 		.store  = store_##_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 {
 struct eeepc_cpufv {
 	int num;
 	int num;
 	int cur;
 	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->num = (c->cur >> 8) & 0xff;
 	c->cur &= 0xff;
 	c->cur &= 0xff;
 	if (c->cur < 0 || c->num <= 0 || c->num > 12)
 	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,
 				    struct device_attribute *attr,
 				    char *buf)
 				    char *buf)
 {
 {
+	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
 	struct eeepc_cpufv c;
 	struct eeepc_cpufv c;
 	int i;
 	int i;
 	ssize_t len = 0;
 	ssize_t len = 0;
 
 
-	if (get_cpufv(&c))
+	if (get_cpufv(eeepc, &c))
 		return -ENODEV;
 		return -ENODEV;
 	for (i = 0; i < c.num; i++)
 	for (i = 0; i < c.num; i++)
 		len += sprintf(buf + len, "%d ", i);
 		len += sprintf(buf + len, "%d ", i);
@@ -453,9 +362,10 @@ static ssize_t show_cpufv(struct device *dev,
 			  struct device_attribute *attr,
 			  struct device_attribute *attr,
 			  char *buf)
 			  char *buf)
 {
 {
+	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
 	struct eeepc_cpufv c;
 	struct eeepc_cpufv c;
 
 
-	if (get_cpufv(&c))
+	if (get_cpufv(eeepc, &c))
 		return -ENODEV;
 		return -ENODEV;
 	return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
 	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,
 			   struct device_attribute *attr,
 			   const char *buf, size_t count)
 			   const char *buf, size_t count)
 {
 {
+	struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
 	struct eeepc_cpufv c;
 	struct eeepc_cpufv c;
 	int rv, value;
 	int rv, value;
 
 
-	if (get_cpufv(&c))
+	if (get_cpufv(eeepc, &c))
 		return -ENODEV;
 		return -ENODEV;
 	rv = parse_arg(buf, count, &value);
 	rv = parse_arg(buf, count, &value);
 	if (rv < 0)
 	if (rv < 0)
 		return rv;
 		return rv;
 	if (!rv || value < 0 || value >= c.num)
 	if (!rv || value < 0 || value >= c.num)
 		return -EINVAL;
 		return -EINVAL;
-	set_acpi(CM_ASL_CPUFV, value);
+	set_acpi(eeepc, CM_ASL_CPUFV, value);
 	return rv;
 	return rv;
 }
 }
 
 
@@ -506,156 +417,125 @@ static struct attribute_group platform_attribute_group = {
 	.attrs = platform_attributes
 	.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;
 	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_dev *dev;
 	struct pci_bus *bus;
 	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);
 		bus = pci_find_bus(0, 1);
 		if (!bus) {
 		if (!bus) {
 			pr_warning("Unable to find PCI bus 1?\n");
 			pr_warning("Unable to find PCI bus 1?\n");
@@ -685,69 +565,23 @@ static void eeepc_rfkill_hotplug(void)
 	}
 	}
 
 
 out_unlock:
 out_unlock:
-	mutex_unlock(&ehotk->hotplug_lock);
+	mutex_unlock(&eeepc->hotplug_lock);
 }
 }
 
 
 static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
 static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
 {
 {
+	struct eeepc_laptop *eeepc = data;
+
 	if (event != ACPI_NOTIFY_BUS_CHECK)
 	if (event != ACPI_NOTIFY_BUS_CHECK)
 		return;
 		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;
 	acpi_handle handle;
 
 
 	status = acpi_get_handle(NULL, node, &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,
 		status = acpi_install_notify_handler(handle,
 						     ACPI_SYSTEM_NOTIFY,
 						     ACPI_SYSTEM_NOTIFY,
 						     eeepc_rfkill_notify,
 						     eeepc_rfkill_notify,
-						     NULL);
+						     eeepc);
 		if (ACPI_FAILURE(status))
 		if (ACPI_FAILURE(status))
 			pr_warning("Failed to register notify on %s\n", node);
 			pr_warning("Failed to register notify on %s\n", node);
 	} else
 	} else
@@ -765,7 +599,8 @@ static int eeepc_register_rfkill_notifier(char *node)
 	return 0;
 	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_status status = AE_OK;
 	acpi_handle handle;
 	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)
 static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
 {
 {
 	kfree(hotplug_slot->info);
 	kfree(hotplug_slot->info);
 	kfree(hotplug_slot);
 	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;
 	int ret = -ENOMEM;
 	struct pci_bus *bus = pci_find_bus(0, 1);
 	struct pci_bus *bus = pci_find_bus(0, 1);
@@ -798,22 +653,22 @@ static int eeepc_setup_pci_hotplug(void)
 		return -ENODEV;
 		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;
 		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);
 					    GFP_KERNEL);
-	if (!ehotk->hotplug_slot->info)
+	if (!eeepc->hotplug_slot->info)
 		goto error_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) {
 	if (ret) {
 		pr_err("Unable to register hotplug slot - %d\n", ret);
 		pr_err("Unable to register hotplug slot - %d\n", ret);
 		goto error_register;
 		goto error_register;
@@ -822,97 +677,262 @@ static int eeepc_setup_pci_hotplug(void)
 	return 0;
 	return 0;
 
 
 error_register:
 error_register:
-	kfree(ehotk->hotplug_slot->info);
+	kfree(eeepc->hotplug_slot->info);
 error_info:
 error_info:
-	kfree(ehotk->hotplug_slot);
-	ehotk->hotplug_slot = NULL;
+	kfree(eeepc->hotplug_slot);
+	eeepc->hotplug_slot = NULL;
 error_slot:
 error_slot:
 	return ret;
 	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;
 	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)
 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)
 static void eeepc_set_fan_pwm(int value)
 {
 {
 	value = SENSORS_LIMIT(value, 0, 255);
 	value = SENSORS_LIMIT(value, 0, 255);
 	value = value * 100 / 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)
 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)
 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)
 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;
 		value |= 0x02;
 	else
 	else
 		value &= ~0x02;
 		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)
 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
 	.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;
 	const struct key_entry *key;
 	int result;
 	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");
 		pr_info("Unable to allocate input device\n");
 		return -ENOMEM;
 		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++) {
 	for (key = eeepc_keymap; key->type != KE_END; key++) {
 		switch (key->type) {
 		switch (key->type) {
 		case KE_KEY:
 		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;
 			break;
 		}
 		}
 	}
 	}
-	result = input_register_device(ehotk->inputdev);
+	result = input_register_device(eeepc->inputdev);
 	if (result) {
 	if (result) {
 		pr_info("Unable to register input device\n");
 		pr_info("Unable to register input device\n");
-		input_free_device(ehotk->inputdev);
+		input_free_device(eeepc->inputdev);
 		return result;
 		return result;
 	}
 	}
 	return 0;
 	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;
 	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)
 	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)
 	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()) {
 	if (!acpi_video_backlight_support()) {
-		result = eeepc_backlight_init(dev);
+		result = eeepc_backlight_init(eeepc);
 		if (result)
 		if (result)
 			goto fail_backlight;
 			goto fail_backlight;
 	} else
 	} 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)
 	if (result)
 		goto fail_input;
 		goto fail_input;
 
 
-	result = eeepc_hwmon_init(dev);
+	result = eeepc_hwmon_init(eeepc);
 	if (result)
 	if (result)
 		goto fail_hwmon;
 		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)
 	if (result)
 		goto fail_rfkill;
 		goto fail_rfkill;
 
 
+	eeepc_device_present = true;
 	return 0;
 	return 0;
 
 
 fail_rfkill:
 fail_rfkill:
-	eeepc_hwmon_exit();
+	eeepc_led_exit(eeepc);
+fail_led:
+	eeepc_hwmon_exit(eeepc);
 fail_hwmon:
 fail_hwmon:
-	eeepc_input_exit();
+	eeepc_input_exit(eeepc);
 fail_input:
 fail_input:
-	eeepc_backlight_exit();
+	eeepc_backlight_exit(eeepc);
 fail_backlight:
 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;
 	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;
 	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)
 static int __init eeepc_laptop_init(void)
 {
 {
 	int result;
 	int result;
 
 
-	if (acpi_disabled)
-		return -ENODEV;
-	result = acpi_bus_register_driver(&eeepc_hotk_driver);
+	result = platform_driver_register(&platform_driver);
 	if (result < 0)
 	if (result < 0)
 		return result;
 		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;
 	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)
 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);
 module_init(eeepc_laptop_init);

+ 84 - 55
drivers/platform/x86/hp-wmi.c

@@ -51,6 +51,12 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
 #define HPWMI_WIRELESS_QUERY 0x5
 #define HPWMI_WIRELESS_QUERY 0x5
 #define HPWMI_HOTKEY_QUERY 0xc
 #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 __init hp_wmi_bios_setup(struct platform_device *device);
 static int __exit hp_wmi_bios_remove(struct platform_device *device);
 static int __exit hp_wmi_bios_remove(struct platform_device *device);
 static int hp_wmi_resume_handler(struct 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)
 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);
 	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,
 	.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 wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+	int mask = 0x200 << (r * 8);
 
 
-	if (wireless & 0x10000)
+	if (wireless & mask)
 		return false;
 		return false;
 	else
 	else
 		return true;
 		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 wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+	int mask = 0x800 << (r * 8);
 
 
-	if (wireless & 0x1000000)
+	if (wireless & mask)
 		return false;
 		return false;
 	else
 	else
 		return true;
 		return true;
@@ -334,49 +332,55 @@ static void hp_wmi_notify(u32 value, void *context)
 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
 	static struct key_entry *key;
 	static struct key_entry *key;
 	union acpi_object *obj;
 	union acpi_object *obj;
+	int eventcode;
 
 
 	wmi_get_event_data(value, &response);
 	wmi_get_event_data(value, &response);
 
 
 	obj = (union acpi_object *)response.pointer;
 	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);
 			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
 	} 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)
 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,
 		wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
 					   RFKILL_TYPE_WLAN,
 					   RFKILL_TYPE_WLAN,
 					   &hp_wmi_rfkill_ops,
 					   &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);
 		err = rfkill_register(wifi_rfkill);
 		if (err)
 		if (err)
 			goto register_wifi_error;
 			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,
 		bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
 						RFKILL_TYPE_BLUETOOTH,
 						RFKILL_TYPE_BLUETOOTH,
 						&hp_wmi_rfkill_ops,
 						&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);
 		err = rfkill_register(bluetooth_rfkill);
 		if (err)
 		if (err)
 			goto register_bluetooth_error;
 			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,
 		wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
 					   RFKILL_TYPE_WWAN,
 					   RFKILL_TYPE_WWAN,
 					   &hp_wmi_rfkill_ops,
 					   &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);
 		err = rfkill_register(wwan_rfkill);
 		if (err)
 		if (err)
 			goto register_wwan_err;
 			goto register_wwan_err;
@@ -533,6 +549,19 @@ static int hp_wmi_resume_handler(struct device *device)
 		input_sync(hp_wmi_input_dev);
 		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;
 	return 0;
 }
 }
 
 

+ 293 - 0
drivers/platform/x86/msi-wmi.c

@@ -0,0 +1,293 @@
+/*
+ * MSI WMI hotkeys
+ *
+ * Copyright (C) 2009 Novell <trenn@suse.de>
+ *
+ * Most stuff taken over from hp-wmi
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/acpi.h>
+#include <linux/backlight.h>
+
+MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
+MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
+MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
+
+/* Temporary workaround until the WMI sysfs interface goes in
+		{ "svn", DMI_SYS_VENDOR },
+		{ "pn",  DMI_PRODUCT_NAME },
+		{ "pvr", DMI_PRODUCT_VERSION },
+		{ "rvn", DMI_BOARD_VENDOR },
+		{ "rn",  DMI_BOARD_NAME },
+*/
+
+MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*");
+
+#define DRV_NAME "msi-wmi"
+#define DRV_PFX DRV_NAME ": "
+
+#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
+#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
+
+#define dprintk(msg...) pr_debug(DRV_PFX msg)
+
+#define KEYCODE_BASE 0xD0
+#define MSI_WMI_BRIGHTNESSUP   KEYCODE_BASE
+#define MSI_WMI_BRIGHTNESSDOWN (KEYCODE_BASE + 1)
+#define MSI_WMI_VOLUMEUP       (KEYCODE_BASE + 2)
+#define MSI_WMI_VOLUMEDOWN     (KEYCODE_BASE + 3)
+static struct key_entry msi_wmi_keymap[] = {
+	{ KE_KEY, MSI_WMI_BRIGHTNESSUP,   {KEY_BRIGHTNESSUP} },
+	{ KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
+	{ KE_KEY, MSI_WMI_VOLUMEUP,       {KEY_VOLUMEUP} },
+	{ KE_KEY, MSI_WMI_VOLUMEDOWN,     {KEY_VOLUMEDOWN} },
+	{ KE_END, 0}
+};
+static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];
+
+struct backlight_device *backlight;
+
+static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF };
+
+static struct input_dev *msi_wmi_input_dev;
+
+static int msi_wmi_query_block(int instance, int *ret)
+{
+	acpi_status status;
+	union acpi_object *obj;
+
+	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+
+	status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output);
+
+	obj = output.pointer;
+
+	if (!obj || obj->type != ACPI_TYPE_INTEGER) {
+		if (obj) {
+			printk(KERN_ERR DRV_PFX "query block returned object "
+			       "type: %d - buffer length:%d\n", obj->type,
+			       obj->type == ACPI_TYPE_BUFFER ?
+			       obj->buffer.length : 0);
+		}
+		kfree(obj);
+		return -EINVAL;
+	}
+	*ret = obj->integer.value;
+	kfree(obj);
+	return 0;
+}
+
+static int msi_wmi_set_block(int instance, int value)
+{
+	acpi_status status;
+
+	struct acpi_buffer input = { sizeof(int), &value };
+
+	dprintk("Going to set block of instance: %d - value: %d\n",
+		instance, value);
+
+	status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input);
+
+	return ACPI_SUCCESS(status) ? 0 : 1;
+}
+
+static int bl_get(struct backlight_device *bd)
+{
+	int level, err, ret;
+
+	/* Instance 1 is "get backlight", cmp with DSDT */
+	err = msi_wmi_query_block(1, &ret);
+	if (err) {
+		printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err);
+		return -EINVAL;
+	}
+	dprintk("Get: Query block returned: %d\n", ret);
+	for (level = 0; level < ARRAY_SIZE(backlight_map); level++) {
+		if (backlight_map[level] == ret) {
+			dprintk("Current backlight level: 0x%X - index: %d\n",
+				backlight_map[level], level);
+			break;
+		}
+	}
+	if (level == ARRAY_SIZE(backlight_map)) {
+		printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n",
+		       ret);
+		return -EINVAL;
+	}
+	return level;
+}
+
+static int bl_set_status(struct backlight_device *bd)
+{
+	int bright = bd->props.brightness;
+	if (bright >= ARRAY_SIZE(backlight_map) || bright < 0)
+		return -EINVAL;
+
+	/* Instance 0 is "set backlight" */
+	return msi_wmi_set_block(0, backlight_map[bright]);
+}
+
+static struct backlight_ops msi_backlight_ops = {
+	.get_brightness	= bl_get,
+	.update_status	= bl_set_status,
+};
+
+static void msi_wmi_notify(u32 value, void *context)
+{
+	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+	static struct key_entry *key;
+	union acpi_object *obj;
+	ktime_t cur;
+
+	wmi_get_event_data(value, &response);
+
+	obj = (union acpi_object *)response.pointer;
+
+	if (obj && obj->type == ACPI_TYPE_INTEGER) {
+		int eventcode = obj->integer.value;
+		dprintk("Eventcode: 0x%x\n", eventcode);
+		key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
+				eventcode);
+		if (key) {
+			ktime_t diff;
+			cur = ktime_get_real();
+			diff = ktime_sub(cur, last_pressed[key->code -
+					KEYCODE_BASE]);
+			/* Ignore event if the same event happened in a 50 ms
+			   timeframe -> Key press may result in 10-20 GPEs */
+			if (ktime_to_us(diff) < 1000 * 50) {
+				dprintk("Suppressed key event 0x%X - "
+					"Last press was %lld us ago\n",
+					 key->code, ktime_to_us(diff));
+				return;
+			}
+			last_pressed[key->code - KEYCODE_BASE] = cur;
+
+			if (key->type == KE_KEY &&
+			/* Brightness is served via acpi video driver */
+			(!acpi_video_backlight_support() ||
+			(key->code != MSI_WMI_BRIGHTNESSUP &&
+			key->code != MSI_WMI_BRIGHTNESSDOWN))) {
+				dprintk("Send key: 0x%X - "
+					"Input layer keycode: %d\n", key->code,
+					 key->keycode);
+				sparse_keymap_report_entry(msi_wmi_input_dev,
+						key, 1, true);
+			}
+		} else
+			printk(KERN_INFO "Unknown key pressed - %x\n",
+			       eventcode);
+	} else
+		printk(KERN_INFO DRV_PFX "Unknown event received\n");
+	kfree(response.pointer);
+}
+
+static int __init msi_wmi_input_setup(void)
+{
+	int err;
+
+	msi_wmi_input_dev = input_allocate_device();
+	if (!msi_wmi_input_dev)
+		return -ENOMEM;
+
+	msi_wmi_input_dev->name = "MSI WMI hotkeys";
+	msi_wmi_input_dev->phys = "wmi/input0";
+	msi_wmi_input_dev->id.bustype = BUS_HOST;
+
+	err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL);
+	if (err)
+		goto err_free_dev;
+
+	err = input_register_device(msi_wmi_input_dev);
+
+	if (err)
+		goto err_free_keymap;
+
+	memset(last_pressed, 0, sizeof(last_pressed));
+
+	return 0;
+
+err_free_keymap:
+	sparse_keymap_free(msi_wmi_input_dev);
+err_free_dev:
+	input_free_device(msi_wmi_input_dev);
+	return err;
+}
+
+static int __init msi_wmi_init(void)
+{
+	int err;
+
+	if (!wmi_has_guid(MSIWMI_EVENT_GUID)) {
+		printk(KERN_ERR
+		       "This machine doesn't have MSI-hotkeys through WMI\n");
+		return -ENODEV;
+	}
+	err = wmi_install_notify_handler(MSIWMI_EVENT_GUID,
+			msi_wmi_notify, NULL);
+	if (err)
+		return -EINVAL;
+
+	err = msi_wmi_input_setup();
+	if (err)
+		goto err_uninstall_notifier;
+
+	if (!acpi_video_backlight_support()) {
+		backlight = backlight_device_register(DRV_NAME,
+				NULL, NULL, &msi_backlight_ops);
+		if (IS_ERR(backlight))
+			goto err_free_input;
+
+		backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
+		err = bl_get(NULL);
+		if (err < 0)
+			goto err_free_backlight;
+
+		backlight->props.brightness = err;
+	}
+	dprintk("Event handler installed\n");
+
+	return 0;
+
+err_free_backlight:
+	backlight_device_unregister(backlight);
+err_free_input:
+	input_unregister_device(msi_wmi_input_dev);
+err_uninstall_notifier:
+	wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
+	return err;
+}
+
+static void __exit msi_wmi_exit(void)
+{
+	if (wmi_has_guid(MSIWMI_EVENT_GUID)) {
+		wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
+		sparse_keymap_free(msi_wmi_input_dev);
+		input_unregister_device(msi_wmi_input_dev);
+		backlight_device_unregister(backlight);
+	}
+}
+
+module_init(msi_wmi_init);
+module_exit(msi_wmi_exit);

+ 899 - 279
drivers/platform/x86/thinkpad_acpi.c

@@ -21,8 +21,8 @@
  *  02110-1301, USA.
  *  02110-1301, USA.
  */
  */
 
 
-#define TPACPI_VERSION "0.23"
-#define TPACPI_SYSFS_VERSION 0x020500
+#define TPACPI_VERSION "0.24"
+#define TPACPI_SYSFS_VERSION 0x020700
 
 
 /*
 /*
  *  Changelog:
  *  Changelog:
@@ -61,6 +61,7 @@
 
 
 #include <linux/nvram.h>
 #include <linux/nvram.h>
 #include <linux/proc_fs.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/sysfs.h>
 #include <linux/sysfs.h>
 #include <linux/backlight.h>
 #include <linux/backlight.h>
 #include <linux/fb.h>
 #include <linux/fb.h>
@@ -76,6 +77,10 @@
 #include <linux/jiffies.h>
 #include <linux/jiffies.h>
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 
 
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
 #include <acpi/acpi_drivers.h>
 #include <acpi/acpi_drivers.h>
 
 
 #include <linux/pci_ids.h>
 #include <linux/pci_ids.h>
@@ -231,6 +236,7 @@ enum tpacpi_hkey_event_t {
 #define TPACPI_DBG_HKEY		0x0008
 #define TPACPI_DBG_HKEY		0x0008
 #define TPACPI_DBG_FAN		0x0010
 #define TPACPI_DBG_FAN		0x0010
 #define TPACPI_DBG_BRGHT	0x0020
 #define TPACPI_DBG_BRGHT	0x0020
+#define TPACPI_DBG_MIXER	0x0040
 
 
 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
 #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
 #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
@@ -256,7 +262,7 @@ struct tp_acpi_drv_struct {
 struct ibm_struct {
 struct ibm_struct {
 	char *name;
 	char *name;
 
 
-	int (*read) (char *);
+	int (*read) (struct seq_file *);
 	int (*write) (char *);
 	int (*write) (char *);
 	void (*exit) (void);
 	void (*exit) (void);
 	void (*resume) (void);
 	void (*resume) (void);
@@ -298,6 +304,7 @@ static struct {
 	u32 fan_ctrl_status_undef:1;
 	u32 fan_ctrl_status_undef:1;
 	u32 second_fan:1;
 	u32 second_fan:1;
 	u32 beep_needs_two_args:1;
 	u32 beep_needs_two_args:1;
+	u32 mixer_no_level_control:1;
 	u32 input_device_registered:1;
 	u32 input_device_registered:1;
 	u32 platform_drv_registered:1;
 	u32 platform_drv_registered:1;
 	u32 platform_drv_attrs_registered:1;
 	u32 platform_drv_attrs_registered:1;
@@ -309,6 +316,7 @@ static struct {
 
 
 static struct {
 static struct {
 	u16 hotkey_mask_ff:1;
 	u16 hotkey_mask_ff:1;
+	u16 volume_ctrl_forbidden:1;
 } tp_warned;
 } tp_warned;
 
 
 struct thinkpad_id_data {
 struct thinkpad_id_data {
@@ -425,6 +433,12 @@ static void tpacpi_log_usertask(const char * const what)
 	  .ec = TPACPI_MATCH_ANY,		\
 	  .ec = TPACPI_MATCH_ANY,		\
 	  .quirks = (__quirk) }
 	  .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 {
 struct tpacpi_quirk {
 	unsigned int vendor;
 	unsigned int vendor;
 	u16 bios;
 	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)
 	if (!ibm || !ibm->read)
 		return -EINVAL;
 		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,
 			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;
 	char *kernbuf;
 	int ret;
 	int ret;
 
 
@@ -834,6 +837,15 @@ static int dispatch_procfs_write(struct file *file,
 	return ret;
 	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)
 static char *next_cmd(char **cmds)
 {
 {
 	char *start = *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;
 	struct tpacpi_rfk *atp_rfk;
 	int res;
 	int res;
 	bool sw_state = false;
 	bool sw_state = false;
+	bool hw_state;
 	int sw_status;
 	int sw_status;
 
 
 	BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]);
 	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_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);
 	res = rfkill_register(atp_rfk->rfkill);
 	if (res < 0) {
 	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;
 	tpacpi_rfkill_switches[id] = atp_rfk;
+
+	printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n",
+		name, (sw_state || hw_state) ? "" : "un");
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1380,12 +1397,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id,
 }
 }
 
 
 /* procfs -------------------------------------------------------------- */
 /* 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)
 	if (id >= TPACPI_RFK_SW_MAX)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
+		seq_printf(m, "status:\t\tnot supported\n");
 	else {
 	else {
 		int status;
 		int status;
 
 
@@ -1399,13 +1414,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
 				return status;
 				return status;
 		}
 		}
 
 
-		len += sprintf(p + len, "status:\t\t%s\n",
+		seq_printf(m, "status:\t\t%s\n",
 				(status == TPACPI_RFK_RADIO_ON) ?
 				(status == TPACPI_RFK_RADIO_ON) ?
 					"enabled" : "disabled");
 					"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)
 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', '9',  'E', '3',  '5', '0'), /* T60/p */
 	TPV_QL1('7', 'C',  'D', '2',  '2', '2'), /* R60, R60i */
 	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 */
 	/*      BIOS FW    BIOS VERS  EC FW     EC VERS */
 	TPV_QI2('1', 'W',  '9', '0',  '1', 'V', '2', '8'), /* R50e (1) */
 	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', '4',  '6', '4',  '2', '7'), /* X41 (0) */
 	TPV_QI1('7', '5',  '6', '0',  '2', '0'), /* X41t (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 */
 	/* (0) - older versions lack DMI EC fw string and functionality */
 	/* (1) - older versions known to lack 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;
 	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 = {
 static struct ibm_struct thinkpad_acpi_driver_data = {
@@ -2186,7 +2198,8 @@ static int hotkey_mask_set(u32 mask)
 		       fwmask, hotkey_acpi_mask);
 		       fwmask, hotkey_acpi_mask);
 	}
 	}
 
 
-	hotkey_mask_warn_incomplete_mask();
+	if (tpacpi_lifecycle != TPACPI_LIFE_EXITING)
+		hotkey_mask_warn_incomplete_mask();
 
 
 	return rc;
 	return rc;
 }
 }
@@ -3182,6 +3195,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 	int res, i;
 	int res, i;
 	int status;
 	int status;
 	int hkeyv;
 	int hkeyv;
+	bool radiosw_state  = false;
+	bool tabletsw_state = false;
 
 
 	unsigned long quirks;
 	unsigned long quirks;
 
 
@@ -3287,6 +3302,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
 #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
 	if (dbg_wlswemul) {
 	if (dbg_wlswemul) {
 		tp_features.hotkey_wlsw = 1;
 		tp_features.hotkey_wlsw = 1;
+		radiosw_state = !!tpacpi_wlsw_emulstate;
 		printk(TPACPI_INFO
 		printk(TPACPI_INFO
 			"radio switch emulation enabled\n");
 			"radio switch emulation enabled\n");
 	} else
 	} else
@@ -3294,6 +3310,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 	/* Not all thinkpads have a hardware radio switch */
 	/* Not all thinkpads have a hardware radio switch */
 	if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
 	if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
 		tp_features.hotkey_wlsw = 1;
 		tp_features.hotkey_wlsw = 1;
+		radiosw_state = !!status;
 		printk(TPACPI_INFO
 		printk(TPACPI_INFO
 			"radio switch found; radios are %s\n",
 			"radio switch found; radios are %s\n",
 			enabled(status, 0));
 			enabled(status, 0));
@@ -3305,11 +3322,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 	/* For X41t, X60t, X61t Tablets... */
 	/* For X41t, X60t, X61t Tablets... */
 	if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
 	if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
 		tp_features.hotkey_tablet = 1;
 		tp_features.hotkey_tablet = 1;
+		tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK);
 		printk(TPACPI_INFO
 		printk(TPACPI_INFO
 			"possible tablet mode switch found; "
 			"possible tablet mode switch found; "
 			"ThinkPad in %s mode\n",
 			"ThinkPad in %s mode\n",
-			(status & TP_HOTKEY_TABLET_MASK)?
-				"tablet" : "laptop");
+			(tabletsw_state) ? "tablet" : "laptop");
 		res = add_to_attr_set(hotkey_dev_attributes,
 		res = add_to_attr_set(hotkey_dev_attributes,
 				&dev_attr_hotkey_tablet_mode.attr);
 				&dev_attr_hotkey_tablet_mode.attr);
 	}
 	}
@@ -3344,16 +3361,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 			TPACPI_HOTKEY_MAP_SIZE);
 			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->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
 	tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
 	tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
 	tpacpi_inputdev->keycode = hotkey_keycode_map;
 	tpacpi_inputdev->keycode = hotkey_keycode_map;
 	for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
 	for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
 		if (hotkey_keycode_map[i] != KEY_RESERVED) {
 		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 {
 		} else {
 			if (i < sizeof(hotkey_reserved_mask)*8)
 			if (i < sizeof(hotkey_reserved_mask)*8)
 				hotkey_reserved_mask |= 1 << i;
 				hotkey_reserved_mask |= 1 << i;
@@ -3361,12 +3376,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
 	}
 	}
 
 
 	if (tp_features.hotkey_wlsw) {
 	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) {
 	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
 	/* 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;
 	tpacpi_inputdev->close = &hotkey_inputdev_close;
 
 
 	hotkey_poll_setup_safe(true);
 	hotkey_poll_setup_safe(true);
-	tpacpi_send_radiosw_update();
-	tpacpi_input_send_tabletsw();
 
 
 	return 0;
 	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,
 static bool hotkey_notify_thermal(const u32 hkey,
 				 bool *send_acpi_ev,
 				 bool *send_acpi_ev,
 				 bool *ignore_acpi_ev)
 				 bool *ignore_acpi_ev)
 {
 {
+	bool known = true;
+
 	/* 0x6000-0x6FFF: thermal alarms */
 	/* 0x6000-0x6FFF: thermal alarms */
 	*send_acpi_ev = true;
 	*send_acpi_ev = true;
 	*ignore_acpi_ev = false;
 	*ignore_acpi_ev = false;
 
 
 	switch (hkey) {
 	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:
 	case TP_HKEY_EV_ALARM_BAT_HOT:
 		printk(TPACPI_CRIT
 		printk(TPACPI_CRIT
 			"THERMAL ALARM: battery is too hot!\n");
 			"THERMAL ALARM: battery is too hot!\n");
 		/* recommended action: warn user through gui */
 		/* recommended action: warn user through gui */
-		return true;
+		break;
 	case TP_HKEY_EV_ALARM_BAT_XHOT:
 	case TP_HKEY_EV_ALARM_BAT_XHOT:
 		printk(TPACPI_ALERT
 		printk(TPACPI_ALERT
 			"THERMAL EMERGENCY: battery is extremely hot!\n");
 			"THERMAL EMERGENCY: battery is extremely hot!\n");
 		/* recommended action: immediate sleep/hibernate */
 		/* recommended action: immediate sleep/hibernate */
-		return true;
+		break;
 	case TP_HKEY_EV_ALARM_SENSOR_HOT:
 	case TP_HKEY_EV_ALARM_SENSOR_HOT:
 		printk(TPACPI_CRIT
 		printk(TPACPI_CRIT
 			"THERMAL ALARM: "
 			"THERMAL ALARM: "
 			"a sensor reports something is too hot!\n");
 			"a sensor reports something is too hot!\n");
 		/* recommended action: warn user through gui, that */
 		/* recommended action: warn user through gui, that */
 		/* some internal component is too hot */
 		/* some internal component is too hot */
-		return true;
+		break;
 	case TP_HKEY_EV_ALARM_SENSOR_XHOT:
 	case TP_HKEY_EV_ALARM_SENSOR_XHOT:
 		printk(TPACPI_ALERT
 		printk(TPACPI_ALERT
 			"THERMAL EMERGENCY: "
 			"THERMAL EMERGENCY: "
 			"a sensor reports something is extremely hot!\n");
 			"a sensor reports something is extremely hot!\n");
 		/* recommended action: immediate sleep/hibernate */
 		/* 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:
 	default:
 		printk(TPACPI_ALERT
 		printk(TPACPI_ALERT
 			 "THERMAL ALERT: unknown thermal alarm received\n");
 			 "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)
 static void hotkey_notify(struct ibm_struct *ibm, u32 event)
@@ -3727,14 +3750,13 @@ static void hotkey_resume(void)
 }
 }
 
 
 /* procfs -------------------------------------------------------------- */
 /* procfs -------------------------------------------------------------- */
-static int hotkey_read(char *p)
+static int hotkey_read(struct seq_file *m)
 {
 {
 	int res, status;
 	int res, status;
-	int len = 0;
 
 
 	if (!tp_features.hotkey) {
 	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))
 	if (mutex_lock_killable(&hotkey_mutex))
@@ -3746,17 +3768,16 @@ static int hotkey_read(char *p)
 	if (res)
 	if (res)
 		return 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) {
 	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 {
 	} 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)
 static void hotkey_enabledisable_warn(bool enable)
@@ -3863,15 +3884,6 @@ enum {
 
 
 #define TPACPI_RFK_BLUETOOTH_SW_NAME	"tpacpi_bluetooth_sw"
 #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)
 static int bluetooth_get_status(void)
 {
 {
 	int status;
 	int status;
@@ -3905,10 +3917,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state)
 #endif
 #endif
 
 
 	/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
 	/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
+	status = TP_ACPI_BLUETOOTH_RESUMECTRL;
 	if (state == TPACPI_RFK_RADIO_ON)
 	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))
 	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
 		return -EIO;
 		return -EIO;
@@ -4032,9 +4043,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
 }
 }
 
 
 /* procfs -------------------------------------------------------------- */
 /* 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)
 static int bluetooth_write(char *buf)
@@ -4047,7 +4058,6 @@ static struct ibm_struct bluetooth_driver_data = {
 	.read = bluetooth_read,
 	.read = bluetooth_read,
 	.write = bluetooth_write,
 	.write = bluetooth_write,
 	.exit = bluetooth_exit,
 	.exit = bluetooth_exit,
-	.suspend = bluetooth_suspend,
 	.shutdown = bluetooth_shutdown,
 	.shutdown = bluetooth_shutdown,
 };
 };
 
 
@@ -4065,15 +4075,6 @@ enum {
 
 
 #define TPACPI_RFK_WWAN_SW_NAME		"tpacpi_wwan_sw"
 #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)
 static int wan_get_status(void)
 {
 {
 	int status;
 	int status;
@@ -4106,11 +4107,10 @@ static int wan_set_status(enum tpacpi_rfkill_state state)
 	}
 	}
 #endif
 #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)
 	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))
 	if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
 		return -EIO;
 		return -EIO;
@@ -4233,9 +4233,9 @@ static int __init wan_init(struct ibm_init_struct *iibm)
 }
 }
 
 
 /* procfs -------------------------------------------------------------- */
 /* 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)
 static int wan_write(char *buf)
@@ -4248,7 +4248,6 @@ static struct ibm_struct wan_driver_data = {
 	.read = wan_read,
 	.read = wan_read,
 	.write = wan_write,
 	.write = wan_write,
 	.exit = wan_exit,
 	.exit = wan_exit,
-	.suspend = wan_suspend,
 	.shutdown = wan_shutdown,
 	.shutdown = wan_shutdown,
 };
 };
 
 
@@ -4611,14 +4610,13 @@ static int video_expand_toggle(void)
 	/* not reached */
 	/* not reached */
 }
 }
 
 
-static int video_read(char *p)
+static int video_read(struct seq_file *m)
 {
 {
 	int status, autosw;
 	int status, autosw;
-	int len = 0;
 
 
 	if (video_supported == TPACPI_VIDEO_NONE) {
 	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();
 	status = video_outputsw_get();
@@ -4629,20 +4627,20 @@ static int video_read(char *p)
 	if (autosw < 0)
 	if (autosw < 0)
 		return autosw;
 		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)
 	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)
 	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)
 static int video_write(char *buf)
@@ -4834,25 +4832,24 @@ static void light_exit(void)
 		flush_workqueue(tpacpi_wq);
 		flush_workqueue(tpacpi_wq);
 }
 }
 
 
-static int light_read(char *p)
+static int light_read(struct seq_file *m)
 {
 {
-	int len = 0;
 	int status;
 	int status;
 
 
 	if (!tp_features.light) {
 	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) {
 	} 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 {
 	} else {
 		status = light_get_status();
 		status = light_get_status();
 		if (status < 0)
 		if (status < 0)
 			return status;
 			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)
 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);
 	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,
 	/* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
 	   R30, R31, T20-22, X20-21 */
 	   R30, R31, T20-22, X20-21 */
 	if (!cmos_handle)
 	if (!cmos_handle)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
+		seq_printf(m, "status:\t\tnot supported\n");
 	else {
 	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)
 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_OFF ? "off" : \
 		((s) == TPACPI_LED_ON ? "on" : "blinking"))
 		((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) {
 	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) {
 	if (led_supported == TPACPI_LED_570) {
 		/* 570 */
 		/* 570 */
@@ -5335,15 +5328,15 @@ static int led_read(char *p)
 			status = led_get_status(i);
 			status = led_get_status(i);
 			if (status < 0)
 			if (status < 0)
 				return -EIO;
 				return -EIO;
-			len += sprintf(p + len, "%d:\t\t%s\n",
+			seq_printf(m, "%d:\t\t%s\n",
 				       i, str_led_status(status));
 				       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");
 		       "<led> on, <led> off, <led> blink (<led> is 0-15)\n");
 
 
-	return len;
+	return 0;
 }
 }
 
 
 static int led_write(char *buf)
 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;
 	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)
 	if (!beep_handle)
-		len += sprintf(p + len, "status:\t\tnot supported\n");
+		seq_printf(m, "status:\t\tnot supported\n");
 	else {
 	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)
 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_TMP0 = 0x78,	/* ACPI EC regs TMP 0..7 */
 	TP_EC_THERMAL_TMP8 = 0xC0,	/* ACPI EC regs TMP 8..15 */
 	TP_EC_THERMAL_TMP8 = 0xC0,	/* ACPI EC regs TMP 8..15 */
 	TP_EC_THERMAL_TMP_NA = -128,	/* ACPI EC sensor not available */
 	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 */
 #define TPACPI_MAX_THERMAL_SENSORS 16	/* Max thermal sensors supported */
 struct ibm_thermal_sensors_struct {
 struct ibm_thermal_sensors_struct {
 	s32 temp[TPACPI_MAX_THERMAL_SENSORS];
 	s32 temp[TPACPI_MAX_THERMAL_SENSORS];
@@ -5571,6 +5565,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
 	return n;
 	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 -------------------------------------------------- */
 /* sysfs temp##_input -------------------------------------------------- */
 
 
 static ssize_t thermal_temp_input_show(struct device *dev,
 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);
 	res = thermal_get_sensor(idx, &value);
 	if (res)
 	if (res)
 		return res;
 		return res;
-	if (value == TP_EC_THERMAL_TMP_NA * 1000)
+	if (value == TPACPI_THERMAL_SENSOR_NA)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	return snprintf(buf, PAGE_SIZE, "%d\n", value);
 	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;
 	int n, i;
 	struct ibm_thermal_sensors_struct t;
 	struct ibm_thermal_sensors_struct t;
 
 
@@ -5773,16 +5788,16 @@ static int thermal_read(char *p)
 	if (unlikely(n < 0))
 	if (unlikely(n < 0))
 		return n;
 		return n;
 
 
-	len += sprintf(p + len, "temperatures:\t");
+	seq_printf(m, "temperatures:\t");
 
 
 	if (n > 0) {
 	if (n > 0) {
 		for (i = 0; i < (n - 1); i++)
 		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
 	} 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 = {
 static struct ibm_struct thermal_driver_data = {
@@ -5797,39 +5812,38 @@ static struct ibm_struct thermal_driver_data = {
 
 
 static u8 ecdump_regs[256];
 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;
 	int i, j;
 	u8 v;
 	u8 v;
 
 
-	len += sprintf(p + len, "EC      "
+	seq_printf(m, "EC      "
 		       " +00 +01 +02 +03 +04 +05 +06 +07"
 		       " +00 +01 +02 +03 +04 +05 +06 +07"
 		       " +08 +09 +0a +0b +0c +0d +0e +0f\n");
 		       " +08 +09 +0a +0b +0c +0d +0e +0f\n");
 	for (i = 0; i < 256; i += 16) {
 	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++) {
 		for (j = 0; j < 16; j++) {
 			if (!acpi_ec_read(i + j, &v))
 			if (!acpi_ec_read(i + j, &v))
 				break;
 				break;
 			if (v != ecdump_regs[i + j])
 			if (v != ecdump_regs[i + j])
-				len += sprintf(p + len, " *%02x", v);
+				seq_printf(m, " *%02x", v);
 			else
 			else
-				len += sprintf(p + len, "  %02x", v);
+				seq_printf(m, "  %02x", v);
 			ecdump_regs[i + j] = v;
 			ecdump_regs[i + j] = v;
 		}
 		}
-		len += sprintf(p + len, "\n");
+		seq_putc(m, '\n');
 		if (j != 16)
 		if (j != 16)
 			break;
 			break;
 	}
 	}
 
 
 	/* These are way too dangerous to advertise openly... */
 	/* These are way too dangerous to advertise openly... */
 #if 0
 #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");
 		       " (<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");
 		       " (<offset> is 00-ff, <value> is 0-255)\n");
 #endif
 #endif
-	return len;
+	return 0;
 }
 }
 
 
 static int ecdump_write(char *buf)
 static int ecdump_write(char *buf)
@@ -6092,6 +6106,12 @@ static int brightness_get(struct backlight_device *bd)
 	return status & TP_EC_BACKLIGHT_LVLMSK;
 	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 = {
 static struct backlight_ops ibm_backlight_data = {
 	.get_brightness = brightness_get,
 	.get_brightness = brightness_get,
 	.update_status  = brightness_update_status,
 	.update_status  = brightness_update_status,
@@ -6120,8 +6140,8 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
 
 
 	/* Models with Intel Extreme Graphics 2 */
 	/* Models with Intel Extreme Graphics 2 */
 	TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC),
 	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 */
 	/* Models with Intel GMA900 */
 	TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC),	/* T43, R52 */
 	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;
 	ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
 	backlight_update_status(ibm_backlight_device);
 	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;
 	return 0;
 }
 }
 
 
@@ -6270,23 +6296,22 @@ static void brightness_exit(void)
 	tpacpi_brightness_checkpoint_nvram();
 	tpacpi_brightness_checkpoint_nvram();
 }
 }
 
 
-static int brightness_read(char *p)
+static int brightness_read(struct seq_file *m)
 {
 {
-	int len = 0;
 	int level;
 	int level;
 
 
 	level = brightness_get(NULL);
 	level = brightness_get(NULL);
 	if (level < 0) {
 	if (level < 0) {
-		len += sprintf(p + len, "level:\t\tunreadable\n");
+		seq_printf(m, "level:\t\tunreadable\n");
 	} else {
 	} 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",
 			       " (<level> is 0-%d)\n",
 			       (tp_features.bright_16levels) ? 15 : 7);
 			       (tp_features.bright_16levels) ? 15 : 7);
 	}
 	}
 
 
-	return len;
+	return 0;
 }
 }
 
 
 static int brightness_write(char *buf)
 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
 	 * Doing it this way makes the syscall restartable in case of EINTR
 	 */
 	 */
 	rc = brightness_set(level);
 	rc = brightness_set(level);
+	if (!rc && ibm_backlight_device)
+		backlight_force_update(ibm_backlight_device,
+					BACKLIGHT_UPDATE_SYSFS);
 	return (rc == -EINTR)? -ERESTARTSYS : rc;
 	return (rc == -EINTR)? -ERESTARTSYS : rc;
 }
 }
 
 
@@ -6338,99 +6366,654 @@ static struct ibm_struct brightness_driver_data = {
  * Volume subdriver
  * 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 {
 	} 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 level to %hu\n", vol);
+	return volume_set_volume_ec(vol);
+}
+
+static void volume_alsa_notify_change(void)
+{
+	struct tpacpi_alsa_data *d;
+
+	if (alsa_card && alsa_card->private_data) {
+		d = alsa_card->private_data;
+		if (d->ctl_mute_id)
+			snd_ctl_notify(alsa_card,
+					SNDRV_CTL_EVENT_MASK_VALUE,
+					d->ctl_mute_id);
+		if (d->ctl_vol_id)
+			snd_ctl_notify(alsa_card,
+					SNDRV_CTL_EVENT_MASK_VALUE,
+					d->ctl_vol_id);
+	}
+}
+
+static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = TP_EC_VOLUME_MAX;
+	return 0;
+}
+
+static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 s;
+	int rc;
+
+	rc = volume_get_status(&s);
+	if (rc < 0)
+		return rc;
+
+	ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK;
+	return 0;
+}
+
+static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	return volume_set_volume(ucontrol->value.integer.value[0]);
+}
+
+#define volume_alsa_mute_info snd_ctl_boolean_mono_info
+
+static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 s;
+	int rc;
+
+	rc = volume_get_status(&s);
+	if (rc < 0)
+		return rc;
+
+	ucontrol->value.integer.value[0] =
+				(s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1;
+	return 0;
+}
+
+static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	return volume_set_mute(!ucontrol->value.integer.value[0]);
+}
+
+static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Console Playback Volume",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = volume_alsa_vol_info,
+	.get = volume_alsa_vol_get,
+};
+
+static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Console Playback Switch",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = volume_alsa_mute_info,
+	.get = volume_alsa_mute_get,
+};
+
+static void volume_suspend(pm_message_t state)
+{
+	tpacpi_volume_checkpoint_nvram();
+}
+
+static void volume_resume(void)
+{
+	volume_alsa_notify_change();
+}
+
+static void volume_shutdown(void)
+{
+	tpacpi_volume_checkpoint_nvram();
+}
+
+static void volume_exit(void)
+{
+	if (alsa_card) {
+		snd_card_free(alsa_card);
+		alsa_card = NULL;
+	}
+
+	tpacpi_volume_checkpoint_nvram();
+}
+
+static int __init volume_create_alsa_mixer(void)
+{
+	struct snd_card *card;
+	struct tpacpi_alsa_data *data;
+	struct snd_kcontrol *ctl_vol;
+	struct snd_kcontrol *ctl_mute;
+	int rc;
+
+	rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE,
+			    sizeof(struct tpacpi_alsa_data), &card);
+	if (rc < 0)
+		return rc;
+	if (!card)
+		return -ENOMEM;
+
+	BUG_ON(!card->private_data);
+	data = card->private_data;
+	data->card = card;
+
+	strlcpy(card->driver, TPACPI_ALSA_DRVNAME,
+		sizeof(card->driver));
+	strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME,
+		sizeof(card->shortname));
+	snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s",
+		 (thinkpad_id.ec_version_str) ?
+			thinkpad_id.ec_version_str : "(unknown)");
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO,
+		 (thinkpad_id.ec_version_str) ?
+			thinkpad_id.ec_version_str : "unknown");
+
+	if (volume_control_allowed) {
+		volume_alsa_control_vol.put = volume_alsa_vol_put;
+		volume_alsa_control_vol.access =
+				SNDRV_CTL_ELEM_ACCESS_READWRITE;
+
+		volume_alsa_control_mute.put = volume_alsa_mute_put;
+		volume_alsa_control_mute.access =
+				SNDRV_CTL_ELEM_ACCESS_READWRITE;
+	}
+
+	if (!tp_features.mixer_no_level_control) {
+		ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL);
+		rc = snd_ctl_add(card, ctl_vol);
+		if (rc < 0) {
+			printk(TPACPI_ERR
+				"Failed to create ALSA volume control\n");
+			goto err_out;
 		}
 		}
+		data->ctl_vol_id = &ctl_vol->id;
+	}
 
 
-		if (new_mute != mute) {
-			/* level doesn't change */
+	ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL);
+	rc = snd_ctl_add(card, ctl_mute);
+	if (rc < 0) {
+		printk(TPACPI_ERR "Failed to create ALSA mute control\n");
+		goto err_out;
+	}
+	data->ctl_mute_id = &ctl_mute->id;
 
 
-			cmos_cmd = (new_mute) ?
-				   TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
+	snd_card_set_dev(card, &tpacpi_pdev->dev);
+	rc = snd_card_register(card);
 
 
-			if (issue_thinkpad_cmos_command(cmos_cmd) ||
-			    !acpi_ec_write(volume_offset, level + new_mute))
-				return -EIO;
+err_out:
+	if (rc < 0) {
+		snd_card_free(card);
+		card = NULL;
+	}
+
+	alsa_card = card;
+	return rc;
+}
+
+#define TPACPI_VOL_Q_MUTEONLY	0x0001	/* Mute-only control available */
+#define TPACPI_VOL_Q_LEVEL	0x0002  /* Volume control available */
+
+static const struct tpacpi_quirk volume_quirk_table[] __initconst = {
+	/* Whitelist volume level on all IBM by default */
+	{ .vendor = PCI_VENDOR_ID_IBM,
+	  .bios   = TPACPI_MATCH_ANY,
+	  .ec     = TPACPI_MATCH_ANY,
+	  .quirks = TPACPI_VOL_Q_LEVEL },
+
+	/* Lenovo models with volume control (needs confirmation) */
+	TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */
+	TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */
+	TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */
+	TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */
+	TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */
+	TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */
+	TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */
+
+	/* Whitelist mute-only on all Lenovo by default */
+	{ .vendor = PCI_VENDOR_ID_LENOVO,
+	  .bios   = TPACPI_MATCH_ANY,
+	  .ec	  = TPACPI_MATCH_ANY,
+	  .quirks = TPACPI_VOL_Q_MUTEONLY }
+};
+
+static int __init volume_init(struct ibm_init_struct *iibm)
+{
+	unsigned long quirks;
+	int rc;
+
+	vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n");
+
+	mutex_init(&volume_mutex);
+
+	/*
+	 * Check for module parameter bogosity, note that we
+	 * init volume_mode to TPACPI_VOL_MODE_MAX in order to be
+	 * able to detect "unspecified"
+	 */
+	if (volume_mode > TPACPI_VOL_MODE_MAX)
+		return -EINVAL;
+
+	if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
+		printk(TPACPI_ERR
+			"UCMS step volume mode not implemented, "
+			"please contact %s\n", TPACPI_MAIL);
+		return 1;
+	}
+
+	if (volume_capabilities >= TPACPI_VOL_CAP_MAX)
+		return -EINVAL;
+
+	/*
+	 * The ALSA mixer is our primary interface.
+	 * When disabled, don't install the subdriver at all
+	 */
+	if (!alsa_enable) {
+		vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+			    "ALSA mixer disabled by parameter, "
+			    "not loading volume subdriver...\n");
+		return 1;
+	}
+
+	quirks = tpacpi_check_quirks(volume_quirk_table,
+				     ARRAY_SIZE(volume_quirk_table));
+
+	switch (volume_capabilities) {
+	case TPACPI_VOL_CAP_AUTO:
+		if (quirks & TPACPI_VOL_Q_MUTEONLY)
+			tp_features.mixer_no_level_control = 1;
+		else if (quirks & TPACPI_VOL_Q_LEVEL)
+			tp_features.mixer_no_level_control = 0;
+		else
+			return 1; /* no mixer */
+		break;
+	case TPACPI_VOL_CAP_VOLMUTE:
+		tp_features.mixer_no_level_control = 0;
+		break;
+	case TPACPI_VOL_CAP_MUTEONLY:
+		tp_features.mixer_no_level_control = 1;
+		break;
+	default:
+		return 1;
+	}
+
+	if (volume_capabilities != TPACPI_VOL_CAP_AUTO)
+		dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+				"using user-supplied volume_capabilities=%d\n",
+				volume_capabilities);
+
+	if (volume_mode == TPACPI_VOL_MODE_AUTO ||
+	    volume_mode == TPACPI_VOL_MODE_MAX) {
+		volume_mode = TPACPI_VOL_MODE_ECNVRAM;
+
+		dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+				"driver auto-selected volume_mode=%d\n",
+				volume_mode);
+	} else {
+		dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+				"using user-supplied volume_mode=%d\n",
+				volume_mode);
+	}
+
+	vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+			"mute is supported, volume control is %s\n",
+			str_supported(!tp_features.mixer_no_level_control));
+
+	rc = volume_create_alsa_mixer();
+	if (rc) {
+		printk(TPACPI_ERR
+			"Could not create the ALSA mixer interface\n");
+		return rc;
+	}
+
+	printk(TPACPI_INFO
+		"Console audio control enabled, mode: %s\n",
+		(volume_control_allowed) ?
+			"override (read/write)" :
+			"monitor (read only)");
+
+	vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
+		"registering volume hotkeys as change notification\n");
+	tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
+			| TP_ACPI_HKEY_VOLUP_MASK
+			| TP_ACPI_HKEY_VOLDWN_MASK
+			| TP_ACPI_HKEY_MUTE_MASK);
+
+	return 0;
+}
+
+static int volume_read(struct seq_file *m)
+{
+	u8 status;
+
+	if (volume_get_status(&status) < 0) {
+		seq_printf(m, "level:\t\tunreadable\n");
+	} else {
+		if (tp_features.mixer_no_level_control)
+			seq_printf(m, "level:\t\tunsupported\n");
+		else
+			seq_printf(m, "level:\t\t%d\n",
+					status & TP_EC_AUDIO_LVL_MSK);
+
+		seq_printf(m, "mute:\t\t%s\n",
+				onoff(status, TP_EC_AUDIO_MUTESW));
+
+		if (volume_control_allowed) {
+			seq_printf(m, "commands:\tunmute, mute\n");
+			if (!tp_features.mixer_no_level_control) {
+				seq_printf(m,
+					       "commands:\tup, down\n");
+				seq_printf(m,
+					       "commands:\tlevel <level>"
+					       " (<level> is 0-%d)\n",
+					       TP_EC_VOLUME_MAX);
+			}
 		}
 		}
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+static int volume_write(char *buf)
+{
+	u8 s;
+	u8 new_level, new_mute;
+	int l;
+	char *cmd;
+	int rc;
+
+	/*
+	 * We do allow volume control at driver startup, so that the
+	 * user can set initial state through the volume=... parameter hack.
+	 */
+	if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) {
+		if (unlikely(!tp_warned.volume_ctrl_forbidden)) {
+			tp_warned.volume_ctrl_forbidden = 1;
+			printk(TPACPI_NOTICE
+				"Console audio control in monitor mode, "
+				"changes are not allowed.\n");
+			printk(TPACPI_NOTICE
+				"Use the volume_control=1 module parameter "
+				"to enable volume control\n");
+		}
+		return -EPERM;
+	}
+
+	rc = volume_get_status(&s);
+	if (rc < 0)
+		return rc;
+
+	new_level = s & TP_EC_AUDIO_LVL_MSK;
+	new_mute  = s & TP_EC_AUDIO_MUTESW_MSK;
+
+	while ((cmd = next_cmd(&buf))) {
+		if (!tp_features.mixer_no_level_control) {
+			if (strlencmp(cmd, "up") == 0) {
+				if (new_mute)
+					new_mute = 0;
+				else if (new_level < TP_EC_VOLUME_MAX)
+					new_level++;
+				continue;
+			} else if (strlencmp(cmd, "down") == 0) {
+				if (new_mute)
+					new_mute = 0;
+				else if (new_level > 0)
+					new_level--;
+				continue;
+			} else if (sscanf(cmd, "level %u", &l) == 1 &&
+				   l >= 0 && l <= TP_EC_VOLUME_MAX) {
+					new_level = l;
+				continue;
+			}
+		}
+		if (strlencmp(cmd, "mute") == 0)
+			new_mute = TP_EC_AUDIO_MUTESW_MSK;
+		else if (strlencmp(cmd, "unmute") == 0)
+			new_mute = 0;
+		else
+			return -EINVAL;
+	}
+
+	if (tp_features.mixer_no_level_control) {
+		tpacpi_disclose_usertask("procfs volume", "%smute\n",
+					new_mute ? "" : "un");
+		rc = volume_set_mute(!!new_mute);
+	} else {
+		tpacpi_disclose_usertask("procfs volume",
+					"%smute and set level to %d\n",
+					new_mute ? "" : "un", new_level);
+		rc = volume_set_status(new_mute | new_level);
+	}
+	volume_alsa_notify_change();
+
+	return (rc == -EINTR) ? -ERESTARTSYS : rc;
+}
+
 static struct ibm_struct volume_driver_data = {
 static struct ibm_struct volume_driver_data = {
 	.name = "volume",
 	.name = "volume",
 	.read = volume_read,
 	.read = volume_read,
 	.write = volume_write,
 	.write = volume_write,
+	.exit = volume_exit,
+	.suspend = volume_suspend,
+	.resume = volume_resume,
+	.shutdown = volume_shutdown,
 };
 };
 
 
 /*************************************************************************
 /*************************************************************************
@@ -7507,9 +8090,8 @@ static void fan_resume(void)
 	}
 	}
 }
 }
 
 
-static int fan_read(char *p)
+static int fan_read(struct seq_file *m)
 {
 {
-	int len = 0;
 	int rc;
 	int rc;
 	u8 status;
 	u8 status;
 	unsigned int speed = 0;
 	unsigned int speed = 0;
@@ -7521,7 +8103,7 @@ static int fan_read(char *p)
 		if (rc < 0)
 		if (rc < 0)
 			return rc;
 			return rc;
 
 
-		len += sprintf(p + len, "status:\t\t%s\n"
+		seq_printf(m, "status:\t\t%s\n"
 			       "level:\t\t%d\n",
 			       "level:\t\t%d\n",
 			       (status != 0) ? "enabled" : "disabled", status);
 			       (status != 0) ? "enabled" : "disabled", status);
 		break;
 		break;
@@ -7532,54 +8114,54 @@ static int fan_read(char *p)
 		if (rc < 0)
 		if (rc < 0)
 			return rc;
 			return rc;
 
 
-		len += sprintf(p + len, "status:\t\t%s\n",
+		seq_printf(m, "status:\t\t%s\n",
 			       (status != 0) ? "enabled" : "disabled");
 			       (status != 0) ? "enabled" : "disabled");
 
 
 		rc = fan_get_speed(&speed);
 		rc = fan_get_speed(&speed);
 		if (rc < 0)
 		if (rc < 0)
 			return rc;
 			return rc;
 
 
-		len += sprintf(p + len, "speed:\t\t%d\n", speed);
+		seq_printf(m, "speed:\t\t%d\n", speed);
 
 
 		if (status & TP_EC_FAN_FULLSPEED)
 		if (status & TP_EC_FAN_FULLSPEED)
 			/* Disengaged mode takes precedence */
 			/* Disengaged mode takes precedence */
-			len += sprintf(p + len, "level:\t\tdisengaged\n");
+			seq_printf(m, "level:\t\tdisengaged\n");
 		else if (status & TP_EC_FAN_AUTO)
 		else if (status & TP_EC_FAN_AUTO)
-			len += sprintf(p + len, "level:\t\tauto\n");
+			seq_printf(m, "level:\t\tauto\n");
 		else
 		else
-			len += sprintf(p + len, "level:\t\t%d\n", status);
+			seq_printf(m, "level:\t\t%d\n", status);
 		break;
 		break;
 
 
 	case TPACPI_FAN_NONE:
 	case TPACPI_FAN_NONE:
 	default:
 	default:
-		len += sprintf(p + len, "status:\t\tnot supported\n");
+		seq_printf(m, "status:\t\tnot supported\n");
 	}
 	}
 
 
 	if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
 	if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
-		len += sprintf(p + len, "commands:\tlevel <level>");
+		seq_printf(m, "commands:\tlevel <level>");
 
 
 		switch (fan_control_access_mode) {
 		switch (fan_control_access_mode) {
 		case TPACPI_FAN_WR_ACPI_SFAN:
 		case TPACPI_FAN_WR_ACPI_SFAN:
-			len += sprintf(p + len, " (<level> is 0-7)\n");
+			seq_printf(m, " (<level> is 0-7)\n");
 			break;
 			break;
 
 
 		default:
 		default:
-			len += sprintf(p + len, " (<level> is 0-7, "
+			seq_printf(m, " (<level> is 0-7, "
 				       "auto, disengaged, full-speed)\n");
 				       "auto, disengaged, full-speed)\n");
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
 	if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
 	if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
-		len += sprintf(p + len, "commands:\tenable, disable\n"
+		seq_printf(m, "commands:\tenable, disable\n"
 			       "commands:\twatchdog <timeout> (<timeout> "
 			       "commands:\twatchdog <timeout> (<timeout> "
 			       "is 0 (off), 1-120 (seconds))\n");
 			       "is 0 (off), 1-120 (seconds))\n");
 
 
 	if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
 	if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
-		len += sprintf(p + len, "commands:\tspeed <speed>"
+		seq_printf(m, "commands:\tspeed <speed>"
 			       " (<speed> is 0-65535)\n");
 			       " (<speed> is 0-65535)\n");
 
 
-	return len;
+	return 0;
 }
 }
 
 
 static int fan_write_cmd_level(const char *cmd, int *rc)
 static int fan_write_cmd_level(const char *cmd, int *rc)
@@ -7721,10 +8303,23 @@ static struct ibm_struct fan_driver_data = {
  */
  */
 static void tpacpi_driver_event(const unsigned int hkey_event)
 static void tpacpi_driver_event(const unsigned int hkey_event)
 {
 {
+	if (ibm_backlight_device) {
+		switch (hkey_event) {
+		case TP_HKEY_EV_BRGHT_UP:
+		case TP_HKEY_EV_BRGHT_DOWN:
+			tpacpi_brightness_notify_change();
+		}
+	}
+	if (alsa_card) {
+		switch (hkey_event) {
+		case TP_HKEY_EV_VOL_UP:
+		case TP_HKEY_EV_VOL_DOWN:
+		case TP_HKEY_EV_VOL_MUTE:
+			volume_alsa_notify_change();
+		}
+	}
 }
 }
 
 
-
-
 static void hotkey_driver_event(const unsigned int scancode)
 static void hotkey_driver_event(const unsigned int scancode)
 {
 {
 	tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
 	tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
@@ -7853,19 +8448,19 @@ static int __init ibm_init(struct ibm_init_struct *iibm)
 		"%s installed\n", ibm->name);
 		"%s installed\n", ibm->name);
 
 
 	if (ibm->read) {
 	if (ibm->read) {
-		entry = create_proc_entry(ibm->name,
-					  S_IFREG | S_IRUGO | S_IWUSR,
-					  proc_dir);
+		mode_t mode;
+
+		mode = S_IRUGO;
+		if (ibm->write)
+			mode |= S_IWUSR;
+		entry = proc_create_data(ibm->name, mode, proc_dir,
+					 &dispatch_proc_fops, ibm);
 		if (!entry) {
 		if (!entry) {
 			printk(TPACPI_ERR "unable to create proc entry %s\n",
 			printk(TPACPI_ERR "unable to create proc entry %s\n",
 			       ibm->name);
 			       ibm->name);
 			ret = -ENODEV;
 			ret = -ENODEV;
 			goto err_out;
 			goto err_out;
 		}
 		}
-		entry->data = ibm;
-		entry->read_proc = &dispatch_procfs_read;
-		if (ibm->write)
-			entry->write_proc = &dispatch_procfs_write;
 		ibm->flags.proc_created = 1;
 		ibm->flags.proc_created = 1;
 	}
 	}
 
 
@@ -8077,6 +8672,7 @@ static struct ibm_init_struct ibms_init[] __initdata = {
 		.data = &brightness_driver_data,
 		.data = &brightness_driver_data,
 	},
 	},
 	{
 	{
+		.init = volume_init,
 		.data = &volume_driver_data,
 		.data = &volume_driver_data,
 	},
 	},
 	{
 	{
@@ -8112,36 +8708,59 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp)
 	return -EINVAL;
 	return -EINVAL;
 }
 }
 
 
-module_param(experimental, int, 0);
+module_param(experimental, int, 0444);
 MODULE_PARM_DESC(experimental,
 MODULE_PARM_DESC(experimental,
 		 "Enables experimental features when non-zero");
 		 "Enables experimental features when non-zero");
 
 
 module_param_named(debug, dbg_level, uint, 0);
 module_param_named(debug, dbg_level, uint, 0);
 MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
 MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
 
 
-module_param(force_load, bool, 0);
+module_param(force_load, bool, 0444);
 MODULE_PARM_DESC(force_load,
 MODULE_PARM_DESC(force_load,
 		 "Attempts to load the driver even on a "
 		 "Attempts to load the driver even on a "
 		 "mis-identified ThinkPad when true");
 		 "mis-identified ThinkPad when true");
 
 
-module_param_named(fan_control, fan_control_allowed, bool, 0);
+module_param_named(fan_control, fan_control_allowed, bool, 0444);
 MODULE_PARM_DESC(fan_control,
 MODULE_PARM_DESC(fan_control,
 		 "Enables setting fan parameters features when true");
 		 "Enables setting fan parameters features when true");
 
 
-module_param_named(brightness_mode, brightness_mode, uint, 0);
+module_param_named(brightness_mode, brightness_mode, uint, 0444);
 MODULE_PARM_DESC(brightness_mode,
 MODULE_PARM_DESC(brightness_mode,
 		 "Selects brightness control strategy: "
 		 "Selects brightness control strategy: "
 		 "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
 		 "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
 
 
-module_param(brightness_enable, uint, 0);
+module_param(brightness_enable, uint, 0444);
 MODULE_PARM_DESC(brightness_enable,
 MODULE_PARM_DESC(brightness_enable,
 		 "Enables backlight control when 1, disables when 0");
 		 "Enables backlight control when 1, disables when 0");
 
 
-module_param(hotkey_report_mode, uint, 0);
+module_param(hotkey_report_mode, uint, 0444);
 MODULE_PARM_DESC(hotkey_report_mode,
 MODULE_PARM_DESC(hotkey_report_mode,
 		 "used for backwards compatibility with userspace, "
 		 "used for backwards compatibility with userspace, "
 		 "see documentation");
 		 "see documentation");
 
 
+module_param_named(volume_mode, volume_mode, uint, 0444);
+MODULE_PARM_DESC(volume_mode,
+		 "Selects volume control strategy: "
+		 "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
+
+module_param_named(volume_capabilities, volume_capabilities, uint, 0444);
+MODULE_PARM_DESC(volume_capabilities,
+		 "Selects the mixer capabilites: "
+		 "0=auto, 1=volume and mute, 2=mute only");
+
+module_param_named(volume_control, volume_control_allowed, bool, 0444);
+MODULE_PARM_DESC(volume_control,
+		 "Enables software override for the console audio "
+		 "control when true");
+
+/* ALSA module API parameters */
+module_param_named(index, alsa_index, int, 0444);
+MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer");
+module_param_named(id, alsa_id, charp, 0444);
+MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer");
+module_param_named(enable, alsa_enable, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer");
+
 #define TPACPI_PARAM(feature) \
 #define TPACPI_PARAM(feature) \
 	module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
 	module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
 	MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
 	MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
@@ -8160,25 +8779,25 @@ TPACPI_PARAM(volume);
 TPACPI_PARAM(fan);
 TPACPI_PARAM(fan);
 
 
 #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
 #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
-module_param(dbg_wlswemul, uint, 0);
+module_param(dbg_wlswemul, uint, 0444);
 MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation");
 MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation");
 module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0);
 module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0);
 MODULE_PARM_DESC(wlsw_state,
 MODULE_PARM_DESC(wlsw_state,
 		 "Initial state of the emulated WLSW switch");
 		 "Initial state of the emulated WLSW switch");
 
 
-module_param(dbg_bluetoothemul, uint, 0);
+module_param(dbg_bluetoothemul, uint, 0444);
 MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation");
 MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation");
 module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0);
 module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0);
 MODULE_PARM_DESC(bluetooth_state,
 MODULE_PARM_DESC(bluetooth_state,
 		 "Initial state of the emulated bluetooth switch");
 		 "Initial state of the emulated bluetooth switch");
 
 
-module_param(dbg_wwanemul, uint, 0);
+module_param(dbg_wwanemul, uint, 0444);
 MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation");
 MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation");
 module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
 module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
 MODULE_PARM_DESC(wwan_state,
 MODULE_PARM_DESC(wwan_state,
 		 "Initial state of the emulated WWAN switch");
 		 "Initial state of the emulated WWAN switch");
 
 
-module_param(dbg_uwbemul, uint, 0);
+module_param(dbg_uwbemul, uint, 0444);
 MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation");
 MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation");
 module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
 module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
 MODULE_PARM_DESC(uwb_state,
 MODULE_PARM_DESC(uwb_state,
@@ -8371,6 +8990,7 @@ static int __init thinkpad_acpi_module_init(void)
 						PCI_VENDOR_ID_IBM;
 						PCI_VENDOR_ID_IBM;
 		tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
 		tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
 		tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
 		tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
+		tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
 	}
 	}
 	for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
 	for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
 		ret = ibm_init(&ibms_init[i]);
 		ret = ibm_init(&ibms_init[i]);

+ 144 - 0
drivers/platform/x86/toshiba_bluetooth.c

@@ -0,0 +1,144 @@
+/*
+ * Toshiba Bluetooth Enable Driver
+ *
+ * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com>
+ *
+ * Thanks to Matthew Garrett for background info on ACPI innards which
+ * normal people aren't meant to understand :-)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Note the Toshiba Bluetooth RFKill switch seems to be a strange
+ * fish. It only provides a BT event when the switch is flipped to
+ * the 'on' position. When flipping it to 'off', the USB device is
+ * simply pulled away underneath us, without any BT event being
+ * delivered.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
+MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
+MODULE_LICENSE("GPL");
+
+
+static int toshiba_bt_rfkill_add(struct acpi_device *device);
+static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type);
+static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
+static int toshiba_bt_resume(struct acpi_device *device);
+
+static const struct acpi_device_id bt_device_ids[] = {
+	{ "TOS6205", 0},
+	{ "", 0},
+};
+MODULE_DEVICE_TABLE(acpi, bt_device_ids);
+
+static struct acpi_driver toshiba_bt_rfkill_driver = {
+	.name =		"Toshiba BT",
+	.class =	"Toshiba",
+	.ids =		bt_device_ids,
+	.ops =		{
+				.add =		toshiba_bt_rfkill_add,
+				.remove =	toshiba_bt_rfkill_remove,
+				.notify =	toshiba_bt_rfkill_notify,
+				.resume =	toshiba_bt_resume,
+			},
+	.owner = 	THIS_MODULE,
+};
+
+
+static int toshiba_bluetooth_enable(acpi_handle handle)
+{
+	acpi_status res1, res2;
+	acpi_integer result;
+
+	/*
+	 * Query ACPI to verify RFKill switch is set to 'on'.
+	 * If not, we return silently, no need to report it as
+	 * an error.
+	 */
+	res1 = acpi_evaluate_integer(handle, "BTST", NULL, &result);
+	if (ACPI_FAILURE(res1))
+		return res1;
+	if (!(result & 0x01))
+		return 0;
+
+	printk(KERN_INFO "toshiba_bluetooth: Re-enabling Toshiba Bluetooth\n");
+	res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL);
+	res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL);
+	if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2))
+		return 0;
+
+	printk(KERN_WARNING "toshiba_bluetooth: Failed to re-enable "
+	       "Toshiba Bluetooth\n");
+
+	return -ENODEV;
+}
+
+static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
+{
+	toshiba_bluetooth_enable(device->handle);
+}
+
+static int toshiba_bt_resume(struct acpi_device *device)
+{
+	return toshiba_bluetooth_enable(device->handle);
+}
+
+static int toshiba_bt_rfkill_add(struct acpi_device *device)
+{
+	acpi_status status;
+	acpi_integer bt_present;
+	int result = -ENODEV;
+
+	/*
+	 * Some Toshiba laptops may have a fake TOS6205 device in
+	 * their ACPI BIOS, so query the _STA method to see if there
+	 * is really anything there, before trying to enable it.
+	 */
+	status = acpi_evaluate_integer(device->handle, "_STA", NULL,
+				       &bt_present);
+
+	if (!ACPI_FAILURE(status) && bt_present) {
+		printk(KERN_INFO "Detected Toshiba ACPI Bluetooth device - "
+		      "installing RFKill handler\n");
+		result = toshiba_bluetooth_enable(device->handle);
+	}
+
+	return result;
+}
+
+static int __init toshiba_bt_rfkill_init(void)
+{
+	int result;
+
+	result = acpi_bus_register_driver(&toshiba_bt_rfkill_driver);
+	if (result < 0) {
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				  "Error registering driver\n"));
+		return result;
+	}
+
+	return 0;
+}
+
+static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type)
+{
+	/* clean up */
+	return 0;
+}
+
+static void __exit toshiba_bt_rfkill_exit(void)
+{
+	acpi_bus_unregister_driver(&toshiba_bt_rfkill_driver);
+}
+
+module_init(toshiba_bt_rfkill_init);
+module_exit(toshiba_bt_rfkill_exit);

+ 173 - 2
drivers/platform/x86/wmi.c

@@ -30,6 +30,7 @@
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/types.h>
 #include <linux/types.h>
+#include <linux/device.h>
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/acpi.h>
 #include <linux/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_bus.h>
@@ -65,6 +66,7 @@ struct wmi_block {
 	acpi_handle handle;
 	acpi_handle handle;
 	wmi_notify_handler handler;
 	wmi_notify_handler handler;
 	void *handler_data;
 	void *handler_data;
+	struct device *dev;
 };
 };
 
 
 static struct wmi_block wmi_blocks;
 static struct wmi_block wmi_blocks;
@@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
 	return true;
 	return true;
 }
 }
 
 
+/*
+ * Convert a raw GUID to the ACII string representation
+ */
+static int wmi_gtoa(const char *in, char *out)
+{
+	int i;
+
+	for (i = 3; i >= 0; i--)
+		out += sprintf(out, "%02X", in[i] & 0xFF);
+
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[5] & 0xFF);
+	out += sprintf(out, "%02X", in[4] & 0xFF);
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[7] & 0xFF);
+	out += sprintf(out, "%02X", in[6] & 0xFF);
+	out += sprintf(out, "-");
+	out += sprintf(out, "%02X", in[8] & 0xFF);
+	out += sprintf(out, "%02X", in[9] & 0xFF);
+	out += sprintf(out, "-");
+
+	for (i = 10; i <= 15; i++)
+		out += sprintf(out, "%02X", in[i] & 0xFF);
+
+	out = '\0';
+	return 0;
+}
+
 static bool find_guid(const char *guid_string, struct wmi_block **out)
 static bool find_guid(const char *guid_string, struct wmi_block **out)
 {
 {
 	char tmp[16], guid_input[16];
 	char tmp[16], guid_input[16];
@@ -554,6 +584,138 @@ bool wmi_has_guid(const char *guid_string)
 }
 }
 EXPORT_SYMBOL_GPL(wmi_has_guid);
 EXPORT_SYMBOL_GPL(wmi_has_guid);
 
 
+/*
+ * sysfs interface
+ */
+static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	char guid_string[37];
+	struct wmi_block *wblock;
+
+	wblock = dev_get_drvdata(dev);
+	if (!wblock)
+		return -ENOMEM;
+
+	wmi_gtoa(wblock->gblock.guid, guid_string);
+
+	return sprintf(buf, "wmi:%s\n", guid_string);
+}
+static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
+
+static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	char guid_string[37];
+
+	struct wmi_block *wblock;
+
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+
+	wblock = dev_get_drvdata(dev);
+	if (!wblock)
+		return -ENOMEM;
+
+	wmi_gtoa(wblock->gblock.guid, guid_string);
+
+	strcpy(&env->buf[env->buflen - 1], "wmi:");
+	memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
+	env->buflen += 40;
+
+	return 0;
+}
+
+static void wmi_dev_free(struct device *dev)
+{
+	kfree(dev);
+}
+
+static struct class wmi_class = {
+	.name = "wmi",
+	.dev_release = wmi_dev_free,
+	.dev_uevent = wmi_dev_uevent,
+};
+
+static int wmi_create_devs(void)
+{
+	int result;
+	char guid_string[37];
+	struct guid_block *gblock;
+	struct wmi_block *wblock;
+	struct list_head *p;
+	struct device *guid_dev;
+
+	/* Create devices for all the GUIDs */
+	list_for_each(p, &wmi_blocks.list) {
+		wblock = list_entry(p, struct wmi_block, list);
+
+		guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+		if (!guid_dev)
+			return -ENOMEM;
+
+		wblock->dev = guid_dev;
+
+		guid_dev->class = &wmi_class;
+		dev_set_drvdata(guid_dev, wblock);
+
+		gblock = &wblock->gblock;
+
+		wmi_gtoa(gblock->guid, guid_string);
+		dev_set_name(guid_dev, guid_string);
+
+		result = device_register(guid_dev);
+		if (result)
+			return result;
+
+		result = device_create_file(guid_dev, &dev_attr_modalias);
+		if (result)
+			return result;
+	}
+
+	return 0;
+}
+
+static void wmi_remove_devs(void)
+{
+	struct guid_block *gblock;
+	struct wmi_block *wblock;
+	struct list_head *p;
+	struct device *guid_dev;
+
+	/* Delete devices for all the GUIDs */
+	list_for_each(p, &wmi_blocks.list) {
+		wblock = list_entry(p, struct wmi_block, list);
+
+		guid_dev = wblock->dev;
+		gblock = &wblock->gblock;
+
+		device_remove_file(guid_dev, &dev_attr_modalias);
+
+		device_unregister(guid_dev);
+	}
+}
+
+static void wmi_class_exit(void)
+{
+	wmi_remove_devs();
+	class_unregister(&wmi_class);
+}
+
+static int wmi_class_init(void)
+{
+	int ret;
+
+	ret = class_register(&wmi_class);
+	if (ret)
+		return ret;
+
+	ret = wmi_create_devs();
+	if (ret)
+		wmi_class_exit();
+
+	return ret;
+}
+
 /*
 /*
  * Parse the _WDG method for the GUID data blocks
  * Parse the _WDG method for the GUID data blocks
  */
  */
@@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void)
 
 
 	if (result < 0) {
 	if (result < 0) {
 		printk(KERN_INFO PREFIX "Error loading mapper\n");
 		printk(KERN_INFO PREFIX "Error loading mapper\n");
-	} else {
-		printk(KERN_INFO PREFIX "Mapper loaded\n");
+		return -ENODEV;
+	}
+
+	result = wmi_class_init();
+	if (result) {
+		acpi_bus_unregister_driver(&acpi_wmi_driver);
+		return result;
 	}
 	}
 
 
+	printk(KERN_INFO PREFIX "Mapper loaded\n");
+
 	return result;
 	return result;
 }
 }
 
 
@@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void)
 	struct list_head *p, *tmp;
 	struct list_head *p, *tmp;
 	struct wmi_block *wblock;
 	struct wmi_block *wblock;
 
 
+	wmi_class_exit();
+
 	acpi_bus_unregister_driver(&acpi_wmi_driver);
 	acpi_bus_unregister_driver(&acpi_wmi_driver);
 
 
 	list_for_each_safe(p, tmp, &wmi_blocks.list) {
 	list_for_each_safe(p, tmp, &wmi_blocks.list) {

+ 14 - 6
drivers/pnp/pnpacpi/core.c

@@ -80,7 +80,8 @@ static int pnpacpi_get_resources(struct pnp_dev *dev)
 
 
 static int pnpacpi_set_resources(struct pnp_dev *dev)
 static int pnpacpi_set_resources(struct pnp_dev *dev)
 {
 {
-	acpi_handle handle = dev->data;
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	struct acpi_buffer buffer;
 	struct acpi_buffer buffer;
 	int ret;
 	int ret;
 
 
@@ -103,7 +104,8 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 
 
 static int pnpacpi_disable_resources(struct pnp_dev *dev)
 static int pnpacpi_disable_resources(struct pnp_dev *dev)
 {
 {
-	acpi_handle handle = dev->data;
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	int ret;
 	int ret;
 
 
 	dev_dbg(&dev->dev, "disable resources\n");
 	dev_dbg(&dev->dev, "disable resources\n");
@@ -121,6 +123,8 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
 #ifdef CONFIG_ACPI_SLEEP
 #ifdef CONFIG_ACPI_SLEEP
 static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 {
 {
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	int power_state;
 	int power_state;
 
 
 	power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
 	power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
@@ -128,16 +132,19 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 		power_state = (state.event == PM_EVENT_ON) ?
 		power_state = (state.event == PM_EVENT_ON) ?
 				ACPI_STATE_D0 : ACPI_STATE_D3;
 				ACPI_STATE_D0 : ACPI_STATE_D3;
 
 
-	return acpi_bus_set_power((acpi_handle) dev->data, power_state);
+	return acpi_bus_set_power(handle, power_state);
 }
 }
 
 
 static int pnpacpi_resume(struct pnp_dev *dev)
 static int pnpacpi_resume(struct pnp_dev *dev)
 {
 {
-	return acpi_bus_set_power((acpi_handle) dev->data, ACPI_STATE_D0);
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
+
+	return acpi_bus_set_power(handle, ACPI_STATE_D0);
 }
 }
 #endif
 #endif
 
 
-static struct pnp_protocol pnpacpi_protocol = {
+struct pnp_protocol pnpacpi_protocol = {
 	.name	 = "Plug and Play ACPI",
 	.name	 = "Plug and Play ACPI",
 	.get	 = pnpacpi_get_resources,
 	.get	 = pnpacpi_get_resources,
 	.set	 = pnpacpi_set_resources,
 	.set	 = pnpacpi_set_resources,
@@ -147,6 +154,7 @@ static struct pnp_protocol pnpacpi_protocol = {
 	.resume = pnpacpi_resume,
 	.resume = pnpacpi_resume,
 #endif
 #endif
 };
 };
+EXPORT_SYMBOL(pnpacpi_protocol);
 
 
 static int __init pnpacpi_add_device(struct acpi_device *device)
 static int __init pnpacpi_add_device(struct acpi_device *device)
 {
 {
@@ -168,7 +176,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
 	if (!dev)
 	if (!dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	dev->data = device->handle;
+	dev->data = device;
 	/* .enabled means the device can decode the resources */
 	/* .enabled means the device can decode the resources */
 	dev->active = device->status.enabled;
 	dev->active = device->status.enabled;
 	status = acpi_get_handle(device->handle, "_SRS", &temp);
 	status = acpi_get_handle(device->handle, "_SRS", &temp);

+ 6 - 3
drivers/pnp/pnpacpi/rsparser.c

@@ -465,7 +465,8 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
 
 
 int pnpacpi_parse_allocated_resource(struct pnp_dev *dev)
 int pnpacpi_parse_allocated_resource(struct pnp_dev *dev)
 {
 {
-	acpi_handle handle = dev->data;
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	acpi_status status;
 	acpi_status status;
 
 
 	pnp_dbg(&dev->dev, "parse allocated resources\n");
 	pnp_dbg(&dev->dev, "parse allocated resources\n");
@@ -773,7 +774,8 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res,
 
 
 int __init pnpacpi_parse_resource_option_data(struct pnp_dev *dev)
 int __init pnpacpi_parse_resource_option_data(struct pnp_dev *dev)
 {
 {
-	acpi_handle handle = dev->data;
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	acpi_status status;
 	acpi_status status;
 	struct acpipnp_parse_option_s parse_data;
 	struct acpipnp_parse_option_s parse_data;
 
 
@@ -845,7 +847,8 @@ static acpi_status pnpacpi_type_resources(struct acpi_resource *res, void *data)
 int pnpacpi_build_resource_template(struct pnp_dev *dev,
 int pnpacpi_build_resource_template(struct pnp_dev *dev,
 				    struct acpi_buffer *buffer)
 				    struct acpi_buffer *buffer)
 {
 {
-	acpi_handle handle = dev->data;
+	struct acpi_device *acpi_dev = dev->data;
+	acpi_handle handle = acpi_dev->handle;
 	struct acpi_resource *resource;
 	struct acpi_resource *resource;
 	int res_cnt = 0;
 	int res_cnt = 0;
 	acpi_status status;
 	acpi_status status;

+ 12 - 7
drivers/thermal/thermal_sys.c

@@ -225,6 +225,12 @@ passive_store(struct device *dev, struct device_attribute *attr,
 	if (!sscanf(buf, "%d\n", &state))
 	if (!sscanf(buf, "%d\n", &state))
 		return -EINVAL;
 		return -EINVAL;
 
 
+	/* sanity check: values below 1000 millicelcius don't make sense
+	 * and can cause the system to go into a thermal heart attack
+	 */
+	if (state && state < 1000)
+		return -EINVAL;
+
 	if (state && !tz->forced_passive) {
 	if (state && !tz->forced_passive) {
 		mutex_lock(&thermal_list_lock);
 		mutex_lock(&thermal_list_lock);
 		list_for_each_entry(cdev, &thermal_cdev_list, node) {
 		list_for_each_entry(cdev, &thermal_cdev_list, node) {
@@ -235,6 +241,8 @@ passive_store(struct device *dev, struct device_attribute *attr,
 								 cdev);
 								 cdev);
 		}
 		}
 		mutex_unlock(&thermal_list_lock);
 		mutex_unlock(&thermal_list_lock);
+		if (!tz->passive_delay)
+			tz->passive_delay = 1000;
 	} else if (!state && tz->forced_passive) {
 	} else if (!state && tz->forced_passive) {
 		mutex_lock(&thermal_list_lock);
 		mutex_lock(&thermal_list_lock);
 		list_for_each_entry(cdev, &thermal_cdev_list, node) {
 		list_for_each_entry(cdev, &thermal_cdev_list, node) {
@@ -245,17 +253,12 @@ passive_store(struct device *dev, struct device_attribute *attr,
 								   cdev);
 								   cdev);
 		}
 		}
 		mutex_unlock(&thermal_list_lock);
 		mutex_unlock(&thermal_list_lock);
+		tz->passive_delay = 0;
 	}
 	}
 
 
 	tz->tc1 = 1;
 	tz->tc1 = 1;
 	tz->tc2 = 1;
 	tz->tc2 = 1;
 
 
-	if (!tz->passive_delay)
-		tz->passive_delay = 1000;
-
-	if (!tz->polling_delay)
-		tz->polling_delay = 10000;
-
 	tz->forced_passive = state;
 	tz->forced_passive = state;
 
 
 	thermal_zone_device_update(tz);
 	thermal_zone_device_update(tz);
@@ -374,7 +377,7 @@ thermal_cooling_device_cur_state_store(struct device *dev,
 	if (!sscanf(buf, "%ld\n", &state))
 	if (!sscanf(buf, "%ld\n", &state))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (state < 0)
+	if ((long)state < 0)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	result = cdev->ops->set_cur_state(cdev, state);
 	result = cdev->ops->set_cur_state(cdev, state);
@@ -1016,6 +1019,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
 		thermal_zone_device_set_polling(tz, tz->passive_delay);
 		thermal_zone_device_set_polling(tz, tz->passive_delay);
 	else if (tz->polling_delay)
 	else if (tz->polling_delay)
 		thermal_zone_device_set_polling(tz, tz->polling_delay);
 		thermal_zone_device_set_polling(tz, tz->polling_delay);
+	else
+		thermal_zone_device_set_polling(tz, 0);
 	mutex_unlock(&tz->lock);
 	mutex_unlock(&tz->lock);
 }
 }
 EXPORT_SYMBOL(thermal_zone_device_update);
 EXPORT_SYMBOL(thermal_zone_device_update);

+ 5 - 3
include/acpi/acoutput.h

@@ -85,7 +85,8 @@
 #define ACPI_LV_INIT                0x00000001
 #define ACPI_LV_INIT                0x00000001
 #define ACPI_LV_DEBUG_OBJECT        0x00000002
 #define ACPI_LV_DEBUG_OBJECT        0x00000002
 #define ACPI_LV_INFO                0x00000004
 #define ACPI_LV_INFO                0x00000004
-#define ACPI_LV_ALL_EXCEPTIONS      0x00000007
+#define ACPI_LV_REPAIR              0x00000008
+#define ACPI_LV_ALL_EXCEPTIONS      0x0000000F
 
 
 /* Trace verbosity level 1 [Standard Trace Level] */
 /* Trace verbosity level 1 [Standard Trace Level] */
 
 
@@ -143,6 +144,7 @@
 #define ACPI_DB_INIT                ACPI_DEBUG_LEVEL (ACPI_LV_INIT)
 #define ACPI_DB_INIT                ACPI_DEBUG_LEVEL (ACPI_LV_INIT)
 #define ACPI_DB_DEBUG_OBJECT        ACPI_DEBUG_LEVEL (ACPI_LV_DEBUG_OBJECT)
 #define ACPI_DB_DEBUG_OBJECT        ACPI_DEBUG_LEVEL (ACPI_LV_DEBUG_OBJECT)
 #define ACPI_DB_INFO                ACPI_DEBUG_LEVEL (ACPI_LV_INFO)
 #define ACPI_DB_INFO                ACPI_DEBUG_LEVEL (ACPI_LV_INFO)
+#define ACPI_DB_REPAIR              ACPI_DEBUG_LEVEL (ACPI_LV_REPAIR)
 #define ACPI_DB_ALL_EXCEPTIONS      ACPI_DEBUG_LEVEL (ACPI_LV_ALL_EXCEPTIONS)
 #define ACPI_DB_ALL_EXCEPTIONS      ACPI_DEBUG_LEVEL (ACPI_LV_ALL_EXCEPTIONS)
 
 
 /* Trace level -- also used in the global "DebugLevel" */
 /* Trace level -- also used in the global "DebugLevel" */
@@ -174,8 +176,8 @@
 
 
 /* Defaults for debug_level, debug and normal */
 /* Defaults for debug_level, debug and normal */
 
 
-#define ACPI_DEBUG_DEFAULT          (ACPI_LV_INFO)
-#define ACPI_NORMAL_DEFAULT         (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT)
+#define ACPI_DEBUG_DEFAULT          (ACPI_LV_INFO | ACPI_LV_REPAIR)
+#define ACPI_NORMAL_DEFAULT         (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT | ACPI_LV_REPAIR)
 #define ACPI_DEBUG_ALL              (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
 #define ACPI_DEBUG_ALL              (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
 
 
 #if defined (ACPI_DEBUG_OUTPUT) || !defined (ACPI_NO_ERROR_MESSAGES)
 #if defined (ACPI_DEBUG_OUTPUT) || !defined (ACPI_NO_ERROR_MESSAGES)

+ 1 - 1
include/acpi/acpixf.h

@@ -47,7 +47,7 @@
 
 
 /* Current ACPICA subsystem version in YYYYMMDD format */
 /* Current ACPICA subsystem version in YYYYMMDD format */
 
 
-#define ACPI_CA_VERSION                 0x20091112
+#define ACPI_CA_VERSION                 0x20091214
 
 
 #include "actypes.h"
 #include "actypes.h"
 #include "actbl.h"
 #include "actbl.h"

+ 3 - 2
include/acpi/processor.h

@@ -294,7 +294,7 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx
 #ifdef CONFIG_CPU_FREQ
 #ifdef CONFIG_CPU_FREQ
 void acpi_processor_ppc_init(void);
 void acpi_processor_ppc_init(void);
 void acpi_processor_ppc_exit(void);
 void acpi_processor_ppc_exit(void);
-int acpi_processor_ppc_has_changed(struct acpi_processor *pr);
+int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag);
 extern int acpi_processor_get_bios_limit(int cpu, unsigned int *limit);
 extern int acpi_processor_get_bios_limit(int cpu, unsigned int *limit);
 #else
 #else
 static inline void acpi_processor_ppc_init(void)
 static inline void acpi_processor_ppc_init(void)
@@ -305,7 +305,8 @@ static inline void acpi_processor_ppc_exit(void)
 {
 {
 	return;
 	return;
 }
 }
-static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
+static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr,
+								int event_flag)
 {
 {
 	static unsigned int printout = 1;
 	static unsigned int printout = 1;
 	if (printout) {
 	if (printout) {

+ 20 - 3
include/linux/acpi.h

@@ -240,7 +240,7 @@ extern int pnpacpi_disabled;
 #define PXM_INVAL	(-1)
 #define PXM_INVAL	(-1)
 #define NID_INVAL	(-1)
 #define NID_INVAL	(-1)
 
 
-int acpi_check_resource_conflict(struct resource *res);
+int acpi_check_resource_conflict(const struct resource *res);
 
 
 int acpi_check_region(resource_size_t start, resource_size_t n,
 int acpi_check_region(resource_size_t start, resource_size_t n,
 		      const char *name);
 		      const char *name);
@@ -253,10 +253,16 @@ void __init acpi_old_suspend_ordering(void);
 void __init acpi_s4_no_nvs(void);
 void __init acpi_s4_no_nvs(void);
 #endif /* CONFIG_PM_SLEEP */
 #endif /* CONFIG_PM_SLEEP */
 
 
+struct acpi_osc_context {
+	char *uuid_str; /* uuid string */
+	int rev;
+	struct acpi_buffer cap; /* arg2/arg3 */
+	struct acpi_buffer ret; /* free by caller if success */
+};
+
 #define OSC_QUERY_TYPE			0
 #define OSC_QUERY_TYPE			0
 #define OSC_SUPPORT_TYPE 		1
 #define OSC_SUPPORT_TYPE 		1
 #define OSC_CONTROL_TYPE		2
 #define OSC_CONTROL_TYPE		2
-#define OSC_SUPPORT_MASKS		0x1f
 
 
 /* _OSC DW0 Definition */
 /* _OSC DW0 Definition */
 #define OSC_QUERY_ENABLE		1
 #define OSC_QUERY_ENABLE		1
@@ -265,12 +271,23 @@ void __init acpi_s4_no_nvs(void);
 #define OSC_INVALID_REVISION_ERROR	8
 #define OSC_INVALID_REVISION_ERROR	8
 #define OSC_CAPABILITIES_MASK_ERROR	16
 #define OSC_CAPABILITIES_MASK_ERROR	16
 
 
+acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context);
+
+/* platform-wide _OSC bits */
+#define OSC_SB_PAD_SUPPORT		1
+#define OSC_SB_PPC_OST_SUPPORT		2
+#define OSC_SB_PR3_SUPPORT		4
+#define OSC_SB_CPUHP_OST_SUPPORT	8
+#define OSC_SB_APEI_SUPPORT		16
+
+/* PCI defined _OSC bits */
 /* _OSC DW1 Definition (OS Support Fields) */
 /* _OSC DW1 Definition (OS Support Fields) */
 #define OSC_EXT_PCI_CONFIG_SUPPORT		1
 #define OSC_EXT_PCI_CONFIG_SUPPORT		1
 #define OSC_ACTIVE_STATE_PWR_SUPPORT 		2
 #define OSC_ACTIVE_STATE_PWR_SUPPORT 		2
 #define OSC_CLOCK_PWR_CAPABILITY_SUPPORT	4
 #define OSC_CLOCK_PWR_CAPABILITY_SUPPORT	4
 #define OSC_PCI_SEGMENT_GROUPS_SUPPORT		8
 #define OSC_PCI_SEGMENT_GROUPS_SUPPORT		8
 #define OSC_MSI_SUPPORT				16
 #define OSC_MSI_SUPPORT				16
+#define OSC_PCI_SUPPORT_MASKS			0x1f
 
 
 /* _OSC DW1 Definition (OS Control Fields) */
 /* _OSC DW1 Definition (OS Control Fields) */
 #define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL	1
 #define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL	1
@@ -279,7 +296,7 @@ void __init acpi_s4_no_nvs(void);
 #define OSC_PCI_EXPRESS_AER_CONTROL		8
 #define OSC_PCI_EXPRESS_AER_CONTROL		8
 #define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL	16
 #define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL	16
 
 
-#define OSC_CONTROL_MASKS 	(OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | 	\
+#define OSC_PCI_CONTROL_MASKS 	(OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | 	\
 				OSC_SHPC_NATIVE_HP_CONTROL | 		\
 				OSC_SHPC_NATIVE_HP_CONTROL | 		\
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_PME_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\
 				OSC_PCI_EXPRESS_AER_CONTROL |		\

+ 13 - 0
include/linux/pnp.h

@@ -334,6 +334,19 @@ extern struct pnp_protocol pnpbios_protocol;
 #define pnp_device_is_pnpbios(dev) 0
 #define pnp_device_is_pnpbios(dev) 0
 #endif
 #endif
 
 
+#ifdef CONFIG_PNPACPI
+extern struct pnp_protocol pnpacpi_protocol;
+
+static inline struct acpi_device *pnp_acpi_device(struct pnp_dev *dev)
+{
+	if (dev->protocol == &pnpacpi_protocol)
+		return dev->data;
+	return NULL;
+}
+#else
+#define pnp_acpi_device(dev) 0
+#endif
+
 /* status */
 /* status */
 #define PNP_READY		0x0000
 #define PNP_READY		0x0000
 #define PNP_ATTACHED		0x0001
 #define PNP_ATTACHED		0x0001