summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 12:33:19 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 12:33:19 -0800
commit288f02bbb6e9609cbaf1eb7a9cb97ae45ce090b2 (patch)
tree4f5e5c9fe6638bdbd246379f64b3541de68f329a /drivers
parent8aedf8a6ae98d5d4df3254b6afb7e4432d9d8600 (diff)
parentaa96ce0af8385415a3450bc13e6254a4d6b4a888 (diff)
downloadlinux-288f02bbb6e9609cbaf1eb7a9cb97ae45ce090b2.tar.bz2
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) ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpi_pad.c3
-rw-r--r--drivers/acpi/acpica/acnamesp.h9
-rw-r--r--drivers/acpi/acpica/acobject.h6
-rw-r--r--drivers/acpi/acpica/dsmethod.c2
-rw-r--r--drivers/acpi/acpica/dswload.c64
-rw-r--r--drivers/acpi/acpica/evregion.c4
-rw-r--r--drivers/acpi/acpica/evrgnini.c15
-rw-r--r--drivers/acpi/acpica/evxface.c4
-rw-r--r--drivers/acpi/acpica/evxfevnt.c4
-rw-r--r--drivers/acpi/acpica/evxfregn.c4
-rw-r--r--drivers/acpi/acpica/exmutex.c18
-rw-r--r--drivers/acpi/acpica/nsaccess.c2
-rw-r--r--drivers/acpi/acpica/nsdump.c2
-rw-r--r--drivers/acpi/acpica/nseval.c18
-rw-r--r--drivers/acpi/acpica/nsnames.c2
-rw-r--r--drivers/acpi/acpica/nspredef.c93
-rw-r--r--drivers/acpi/acpica/nsrepair.c447
-rw-r--r--drivers/acpi/acpica/nsrepair2.c195
-rw-r--r--drivers/acpi/acpica/nsutils.c57
-rw-r--r--drivers/acpi/acpica/nsxfeval.c10
-rw-r--r--drivers/acpi/acpica/nsxfname.c10
-rw-r--r--drivers/acpi/acpica/nsxfobj.c14
-rw-r--r--drivers/acpi/acpica/psxface.c3
-rw-r--r--drivers/acpi/acpica/rsxface.c2
-rw-r--r--drivers/acpi/acpica/utcopy.c27
-rw-r--r--drivers/acpi/battery.c2
-rw-r--r--drivers/acpi/bus.c148
-rw-r--r--drivers/acpi/button.c7
-rw-r--r--drivers/acpi/debug.c84
-rw-r--r--drivers/acpi/dock.c261
-rw-r--r--drivers/acpi/fan.c2
-rw-r--r--drivers/acpi/numa.c21
-rw-r--r--drivers/acpi/osl.c2
-rw-r--r--drivers/acpi/pci_root.c76
-rw-r--r--drivers/acpi/processor_core.c10
-rw-r--r--drivers/acpi/processor_idle.c11
-rw-r--r--drivers/acpi/processor_perflib.c50
-rw-r--r--drivers/acpi/thermal.c7
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c118
-rw-r--r--drivers/misc/Kconfig13
-rw-r--r--drivers/platform/x86/Kconfig29
-rw-r--r--drivers/platform/x86/Makefile2
-rw-r--r--drivers/platform/x86/acerhdf.c63
-rw-r--r--drivers/platform/x86/asus-laptop.c25
-rw-r--r--drivers/platform/x86/asus_acpi.c19
-rw-r--r--drivers/platform/x86/dell-laptop.c86
-rw-r--r--drivers/platform/x86/dell-wmi.c129
-rw-r--r--drivers/platform/x86/eeepc-laptop.c1415
-rw-r--r--drivers/platform/x86/hp-wmi.c139
-rw-r--r--drivers/platform/x86/msi-wmi.c293
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c1178
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c144
-rw-r--r--drivers/platform/x86/wmi.c175
-rw-r--r--drivers/pnp/pnpacpi/core.c20
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c9
-rw-r--r--drivers/thermal/thermal_sys.c19
56 files changed, 3920 insertions, 1652 deletions
diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c
index 0d2cdb86158b..97991ac6f5fc 100644
--- a/drivers/acpi/acpi_pad.c
+++ b/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);
cpumask_var_t tmp;
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))
return;
diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h
index ab83919dda61..61edb156e8d0 100644
--- a/drivers/acpi/acpica/acnamesp.h
+++ b/drivers/acpi/acpica/acnamesp.h
@@ -296,6 +296,11 @@ acpi_ns_complex_repairs(struct acpi_predefined_data *data,
acpi_status validate_status,
union acpi_operand_object **return_object_ptr);
+void
+acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
+ u8 package_type,
+ union acpi_operand_object *obj_desc);
+
/*
* nssearch - Namespace searching and entry
*/
@@ -354,9 +359,7 @@ acpi_ns_externalize_name(u32 internal_name_length,
const char *internal_name,
u32 * converted_name_length, char **converted_name);
-struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle);
-
-acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node);
+struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle);
void acpi_ns_terminate(void);
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index b39d682a2140..64062b1be3ee 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -180,7 +180,11 @@ struct acpi_object_method {
u8 sync_level;
union acpi_operand_object *mutex;
u8 *aml_start;
- ACPI_INTERNAL_METHOD implementation;
+ union {
+ ACPI_INTERNAL_METHOD implementation;
+ union acpi_operand_object *handler;
+ } extra;
+
u32 aml_length;
u8 thread_count;
acpi_owner_id owner_id;
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index 567a4899a018..e786f9fd767f 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/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 */
if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
- status = obj_desc->method.implementation(next_walk_state);
+ status = obj_desc->method.extra.implementation(next_walk_state);
if (status == AE_OK) {
status = AE_CTRL_TERMINATE;
}
diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c
index 10fc78517843..b40513dd6a6a 100644
--- a/drivers/acpi/acpica/dswload.c
+++ b/drivers/acpi/acpica/dswload.c
@@ -212,18 +212,19 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state,
case ACPI_TYPE_BUFFER:
/*
- * These types we will allow, but we will change the type. This
- * enables some existing code of the form:
+ * These types we will allow, but we will change the type.
+ * This enables some existing code of the form:
*
* Name (DEB, 0)
* Scope (DEB) { ... }
*
- * Note: silently change the type here. On the second pass, we will report
- * a warning
+ * Note: silently change the type here. On the second pass,
+ * we will report a warning
*/
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
- "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)\n",
- path,
+ "Type override - [%4.4s] had invalid type (%s) "
+ "for Scope operator, changed to type ANY\n",
+ acpi_ut_get_node_name(node),
acpi_ut_get_type_name(node->type)));
node->type = ACPI_TYPE_ANY;
@@ -235,8 +236,10 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state,
/* All other types are an error */
ACPI_ERROR((AE_INFO,
- "Invalid type (%s) for target of Scope operator [%4.4s] (Cannot override)",
- acpi_ut_get_type_name(node->type), path));
+ "Invalid type (%s) for target of "
+ "Scope operator [%4.4s] (Cannot override)",
+ acpi_ut_get_type_name(node->type),
+ acpi_ut_get_node_name(node)));
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
}
@@ -697,15 +700,16 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
case ACPI_TYPE_BUFFER:
/*
- * These types we will allow, but we will change the type. This
- * enables some existing code of the form:
+ * These types we will allow, but we will change the type.
+ * This enables some existing code of the form:
*
* Name (DEB, 0)
* Scope (DEB) { ... }
*/
ACPI_WARNING((AE_INFO,
- "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)",
- buffer_ptr,
+ "Type override - [%4.4s] had invalid type (%s) "
+ "for Scope operator, changed to type ANY\n",
+ acpi_ut_get_node_name(node),
acpi_ut_get_type_name(node->type)));
node->type = ACPI_TYPE_ANY;
@@ -717,9 +721,10 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
/* All other types are an error */
ACPI_ERROR((AE_INFO,
- "Invalid type (%s) for target of Scope operator [%4.4s]",
+ "Invalid type (%s) for target of "
+ "Scope operator [%4.4s] (Cannot override)",
acpi_ut_get_type_name(node->type),
- buffer_ptr));
+ acpi_ut_get_node_name(node)));
return (AE_AML_OPERAND_TYPE);
}
@@ -1047,9 +1052,22 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
}
/*
- * If we are executing a method, initialize the region
+ * The op_region is not fully parsed at this time. The only valid
+ * argument is the space_id. (We must save the address of the
+ * AML of the address and length operands)
+ *
+ * If we have a valid region, initialize it. The namespace is
+ * unlocked at this point.
+ *
+ * Need to unlock interpreter if it is locked (if we are running
+ * a control method), in order to allow _REG methods to be run
+ * during acpi_ev_initialize_region.
*/
if (walk_state->method_node) {
+ /*
+ * Executing a method: initialize the region and unlock
+ * the interpreter
+ */
status =
acpi_ex_create_region(op->named.data,
op->named.length,
@@ -1058,21 +1076,17 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
if (ACPI_FAILURE(status)) {
return (status);
}
- }
- /*
- * The op_region is not fully parsed at this time. Only valid
- * argument is the space_id. (We must save the address of the
- * AML of the address and length operands)
- */
+ acpi_ex_exit_interpreter();
+ }
- /*
- * If we have a valid region, initialize it
- * Namespace is NOT locked at this point.
- */
status =
acpi_ev_initialize_region
(acpi_ns_get_attached_object(node), FALSE);
+ if (walk_state->method_node) {
+ acpi_ex_enter_interpreter();
+ }
+
if (ACPI_FAILURE(status)) {
/*
* If AE_NOT_EXIST is returned, it is not fatal
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 0bc807c33a56..5336d911fbf0 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -718,7 +718,7 @@ acpi_ev_install_handler(acpi_handle obj_handle,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
if (!node) {
return (AE_BAD_PARAMETER);
}
@@ -1087,7 +1087,7 @@ acpi_ev_reg_run(acpi_handle obj_handle,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
if (!node) {
return (AE_BAD_PARAMETER);
}
diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c
index cf29c4953028..ff168052a332 100644
--- a/drivers/acpi/acpica/evrgnini.c
+++ b/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;
break;
+ case ACPI_TYPE_METHOD:
+ /*
+ * If we are executing module level code, the original
+ * Node's object was replaced by this Method object and we
+ * saved the handler in the method object.
+ *
+ * See acpi_ns_exec_module_code
+ */
+ if (obj_desc->method.
+ flags & AOPOBJ_MODULE_LEVEL) {
+ handler_obj =
+ obj_desc->method.extra.handler;
+ }
+ break;
+
default:
/* Ignore other objects */
break;
diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c
index 10b8543dd466..2fe0809d4eb2 100644
--- a/drivers/acpi/acpica/evxface.c
+++ b/drivers/acpi/acpica/evxface.c
@@ -259,7 +259,7 @@ acpi_install_notify_handler(acpi_handle device,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(device);
+ node = acpi_ns_validate_handle(device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -425,7 +425,7 @@ acpi_remove_notify_handler(acpi_handle device,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(device);
+ node = acpi_ns_validate_handle(device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c
index 4721f58fe42c..eed7a38d25f2 100644
--- a/drivers/acpi/acpica/evxfevnt.c
+++ b/drivers/acpi/acpica/evxfevnt.c
@@ -610,7 +610,7 @@ acpi_install_gpe_block(acpi_handle gpe_device,
return (status);
}
- node = acpi_ns_map_handle_to_node(gpe_device);
+ node = acpi_ns_validate_handle(gpe_device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -698,7 +698,7 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device)
return (status);
}
- node = acpi_ns_map_handle_to_node(gpe_device);
+ node = acpi_ns_validate_handle(gpe_device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 7c3d2d356ffb..c98aa7c2d67c 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -89,7 +89,7 @@ acpi_install_address_space_handler(acpi_handle device,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(device);
+ node = acpi_ns_validate_handle(device);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -155,7 +155,7 @@ acpi_remove_address_space_handler(acpi_handle device,
/* Convert and validate the device handle */
- node = acpi_ns_map_handle_to_node(device);
+ node = acpi_ns_validate_handle(device);
if (!node ||
((node->type != ACPI_TYPE_DEVICE) &&
(node->type != ACPI_TYPE_PROCESSOR) &&
diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c
index 2f0114202b05..3c456bd575d0 100644
--- a/drivers/acpi/acpica/exmutex.c
+++ b/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);
}
+ /* Must have a valid thread ID */
+
+ if (!walk_state->thread) {
+ ACPI_ERROR((AE_INFO,
+ "Cannot release Mutex [%4.4s], null thread info",
+ acpi_ut_get_node_name(obj_desc->mutex.node)));
+ return_ACPI_STATUS(AE_AML_INTERNAL);
+ }
+
/*
* The Mutex is owned, but this thread must be the owner.
* Special case for Global Lock, any thread can release
@@ -392,15 +401,6 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(AE_AML_NOT_OWNER);
}
- /* Must have a valid thread ID */
-
- if (!walk_state->thread) {
- ACPI_ERROR((AE_INFO,
- "Cannot release Mutex [%4.4s], null thread info",
- acpi_ut_get_node_name(obj_desc->mutex.node)));
- return_ACPI_STATUS(AE_AML_INTERNAL);
- }
-
/*
* The sync level of the mutex must be equal to the current sync level. In
* other words, the current level means that at least one mutex at that
diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c
index 9c3cdbe2d82a..d622ba770000 100644
--- a/drivers/acpi/acpica/nsaccess.c
+++ b/drivers/acpi/acpica/nsaccess.c
@@ -165,7 +165,7 @@ acpi_status acpi_ns_root_initialize(void)
obj_desc->method.method_flags =
AML_METHOD_INTERNAL_ONLY;
- obj_desc->method.implementation =
+ obj_desc->method.extra.implementation =
acpi_ut_osi_implementation;
#endif
break;
diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c
index 2deb986861ca..e37836e27e29 100644
--- a/drivers/acpi/acpica/nsdump.c
+++ b/drivers/acpi/acpica/nsdump.c
@@ -180,7 +180,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
return (AE_OK);
}
- this_node = acpi_ns_map_handle_to_node(obj_handle);
+ this_node = acpi_ns_validate_handle(obj_handle);
if (!this_node) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n",
obj_handle));
diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c
index f771e978c403..af9fe9103734 100644
--- a/drivers/acpi/acpica/nseval.c
+++ b/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);
type = acpi_ns_get_type(parent_node);
+ /*
+ * Get the region handler and save it in the method object. We may need
+ * this if an operation region declaration causes a _REG method to be run.
+ *
+ * We can't do this in acpi_ps_link_module_code because
+ * acpi_gbl_root_node->Object is NULL at PASS1.
+ */
+ if ((type == ACPI_TYPE_DEVICE) && parent_node->object) {
+ method_obj->method.extra.handler =
+ parent_node->object->device.handler;
+ }
+
/* Must clear next_object (acpi_ns_attach_object needs the field) */
method_obj->method.next_object = NULL;
@@ -415,6 +427,12 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n",
method_obj->method.aml_start));
+ /* Delete a possible implicit return value (in slack mode) */
+
+ if (info->return_object) {
+ acpi_ut_remove_reference(info->return_object);
+ }
+
/* Detach the temporary method object */
acpi_ns_detach_object(parent_node);
diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c
index af8e6bcee07e..8f9a4875ce26 100644
--- a/drivers/acpi/acpica/nsnames.c
+++ b/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);
- node = acpi_ns_map_handle_to_node(target_handle);
+ node = acpi_ns_validate_handle(target_handle);
if (!node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c
index b05f42903c86..d34fa59548f7 100644
--- a/drivers/acpi/acpica/nspredef.c
+++ b/drivers/acpi/acpica/nspredef.c
@@ -216,29 +216,38 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node,
data->pathname = pathname;
/*
- * Check that the type of the return object is what is expected for
- * this predefined name
+ * Check that the type of the main return object is what is expected
+ * for this predefined name
*/
status = acpi_ns_check_object_type(data, return_object_ptr,
predefined->info.expected_btypes,
ACPI_NOT_PACKAGE_ELEMENT);
if (ACPI_FAILURE(status)) {
- goto check_validation_status;
+ goto exit;
}
- /* For returned Package objects, check the type of all sub-objects */
-
- if (return_object->common.type == ACPI_TYPE_PACKAGE) {
+ /*
+ * For returned Package objects, check the type of all sub-objects.
+ * Note: Package may have been newly created by call above.
+ */
+ if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) {
status = acpi_ns_check_package(data, return_object_ptr);
+ if (ACPI_FAILURE(status)) {
+ goto exit;
+ }
}
/*
- * Perform additional, more complicated repairs on a per-name
- * basis.
+ * The return object was OK, or it was successfully repaired above.
+ * Now make some additional checks such as verifying that package
+ * objects are sorted correctly (if required) or buffer objects have
+ * the correct data width (bytes vs. dwords). These repairs are
+ * performed on a per-name basis, i.e., the code is specific to
+ * particular predefined names.
*/
status = acpi_ns_complex_repairs(data, node, status, return_object_ptr);
-check_validation_status:
+exit:
/*
* If the object validation failed or if we successfully repaired one
* or more objects, mark the parent node to suppress further warning
@@ -427,6 +436,13 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
data->pathname, package->ret_info.type,
return_object->package.count));
+ /*
+ * For variable-length Packages, we can safely remove all embedded
+ * and trailing NULL package elements
+ */
+ acpi_ns_remove_null_elements(data, package->ret_info.type,
+ return_object);
+
/* Extract package count and elements array */
elements = return_object->package.elements;
@@ -461,11 +477,11 @@ acpi_ns_check_package(struct acpi_predefined_data *data,
if (count < expected_count) {
goto package_too_small;
} else if (count > expected_count) {
- ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
- data->node_flags,
- "Return Package is larger than needed - "
- "found %u, expected %u", count,
- expected_count));
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Return Package is larger than needed - "
+ "found %u, expected %u\n",
+ data->pathname, count,
+ expected_count));
}
/* Validate all elements of the returned package */
@@ -680,53 +696,18 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data,
union acpi_operand_object *sub_package;
union acpi_operand_object **sub_elements;
acpi_status status;
- u8 non_trailing_null = FALSE;
u32 expected_count;
u32 i;
u32 j;
- /* Validate each sub-Package in the parent Package */
-
+ /*
+ * Validate each sub-Package in the parent Package
+ *
+ * NOTE: assumes list of sub-packages contains no NULL elements.
+ * Any NULL elements should have been removed by earlier call
+ * to acpi_ns_remove_null_elements.
+ */
for (i = 0; i < count; i++) {
- /*
- * Handling for NULL package elements. For now, we will simply allow
- * a parent package with trailing NULL elements. This can happen if
- * the package was defined to be longer than the initializer list.
- * This is legal as per the ACPI specification. It is often used
- * to allow for dynamic initialization of a Package.
- *
- * A future enhancement may be to simply truncate the package to
- * remove the trailing NULL elements.
- */
- if (!(*elements)) {
- if (!non_trailing_null) {
-
- /* Ensure the remaining elements are all NULL */
-
- for (j = 1; j < (count - i + 1); j++) {
- if (elements[j]) {
- non_trailing_null = TRUE;
- }
- }
-
- if (!non_trailing_null) {
-
- /* Ignore the trailing NULL elements */
-
- return (AE_OK);
- }
- }
-
- /* There are trailing non-null elements, issue warning */
-
- ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
- data->node_flags,
- "Found NULL element at package index %u",
- i));
- elements++;
- continue;
- }
-
sub_package = *elements;
sub_elements = sub_package->package.elements;
diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c
index d563f1a564a7..4fd1bdb056b2 100644
--- a/drivers/acpi/acpica/nsrepair.c
+++ b/drivers/acpi/acpica/nsrepair.c
@@ -45,13 +45,52 @@
#include "accommon.h"
#include "acnamesp.h"
#include "acinterp.h"
-#include "acpredef.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsrepair")
/*******************************************************************************
*
+ * This module attempts to repair or convert objects returned by the
+ * predefined methods to an object type that is expected, as per the ACPI
+ * specification. The need for this code is dictated by the many machines that
+ * return incorrect types for the standard predefined methods. Performing these
+ * conversions here, in one place, eliminates the need for individual ACPI
+ * device drivers to do the same. Note: Most of these conversions are different
+ * than the internal object conversion routines used for implicit object
+ * conversion.
+ *
+ * The following conversions can be performed as necessary:
+ *
+ * Integer -> String
+ * Integer -> Buffer
+ * String -> Integer
+ * String -> Buffer
+ * Buffer -> Integer
+ * Buffer -> String
+ * Buffer -> Package of Integers
+ * Package -> Package of one Package
+ *
+ ******************************************************************************/
+/* Local prototypes */
+static acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+static acpi_status
+acpi_ns_convert_to_package(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object);
+
+/*******************************************************************************
+ *
* FUNCTION: acpi_ns_repair_object
*
* PARAMETERS: Data - Pointer to validation data structure
@@ -68,6 +107,7 @@ ACPI_MODULE_NAME("nsrepair")
* not expected.
*
******************************************************************************/
+
acpi_status
acpi_ns_repair_object(struct acpi_predefined_data *data,
u32 expected_btypes,
@@ -76,32 +116,206 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
{
union acpi_operand_object *return_object = *return_object_ptr;
union acpi_operand_object *new_object;
- acpi_size length;
acpi_status status;
+ ACPI_FUNCTION_NAME(ns_repair_object);
+
/*
* At this point, we know that the type of the returned object was not
* one of the expected types for this predefined name. Attempt to
- * repair the object. Only a limited number of repairs are possible.
+ * repair the object by converting it to one of the expected object
+ * types for this predefined name.
*/
- switch (return_object->common.type) {
+ if (expected_btypes & ACPI_RTYPE_INTEGER) {
+ status = acpi_ns_convert_to_integer(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_STRING) {
+ status = acpi_ns_convert_to_string(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_BUFFER) {
+ status = acpi_ns_convert_to_buffer(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+ if (expected_btypes & ACPI_RTYPE_PACKAGE) {
+ status = acpi_ns_convert_to_package(return_object, &new_object);
+ if (ACPI_SUCCESS(status)) {
+ goto object_repaired;
+ }
+ }
+
+ /* We cannot repair this object */
+
+ return (AE_AML_OPERAND_TYPE);
+
+ object_repaired:
+
+ /* Object was successfully repaired */
+
+ /*
+ * If the original object is a package element, we need to:
+ * 1. Set the reference count of the new object to match the
+ * reference count of the old object.
+ * 2. Decrement the reference count of the original object.
+ */
+ if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
+ new_object->common.reference_count =
+ return_object->common.reference_count;
+
+ if (return_object->common.reference_count > 1) {
+ return_object->common.reference_count--;
+ }
+
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Converted %s to expected %s at index %u\n",
+ data->pathname,
+ acpi_ut_get_object_type_name(return_object),
+ acpi_ut_get_object_type_name(new_object),
+ package_index));
+ } else {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Converted %s to expected %s\n",
+ data->pathname,
+ acpi_ut_get_object_type_name(return_object),
+ acpi_ut_get_object_type_name(new_object)));
+ }
+
+ /* Delete old object, install the new return object */
+
+ acpi_ut_remove_reference(return_object);
+ *return_object_ptr = new_object;
+ data->flags |= ACPI_OBJECT_REPAIRED;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_integer
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_status status;
+ u64 value = 0;
+ u32 i;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_STRING:
+
+ /* String-to-Integer conversion */
+
+ status = acpi_ut_strtoul64(original_object->string.pointer,
+ ACPI_ANY_BASE, &value);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
+
case ACPI_TYPE_BUFFER:
- /* Does the method/object legally return a string? */
+ /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */
- if (!(expected_btypes & ACPI_RTYPE_STRING)) {
+ if (original_object->buffer.length > 8) {
return (AE_AML_OPERAND_TYPE);
}
+ /* Extract each buffer byte to create the integer */
+
+ for (i = 0; i < original_object->buffer.length; i++) {
+ value |=
+ ((u64) original_object->buffer.
+ pointer[i] << (i * 8));
+ }
+ break;
+
+ default:
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ new_object = acpi_ut_create_integer_object(value);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_string
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_string(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_size length;
+ acpi_status status;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-String conversion. Commonly, convert
+ * an integer of value 0 to a NULL string. The last element of
+ * _BIF and _BIX packages occasionally need this fix.
+ */
+ if (original_object->integer.value == 0) {
+
+ /* Allocate a new NULL string object */
+
+ new_object = acpi_ut_create_string_object(0);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
+ }
+ } else {
+ status =
+ acpi_ex_convert_to_string(original_object,
+ &new_object,
+ ACPI_IMPLICIT_CONVERT_HEX);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ }
+ break;
+
+ case ACPI_TYPE_BUFFER:
/*
- * Have a Buffer, expected a String, convert. Use a to_string
+ * Buffer-to-String conversion. Use a to_string
* conversion, no transform performed on the buffer data. The best
* example of this is the _BIF method, where the string data from
* the battery is often (incorrectly) returned as buffer object(s).
*/
length = 0;
- while ((length < return_object->buffer.length) &&
- (return_object->buffer.pointer[length])) {
+ while ((length < original_object->buffer.length) &&
+ (original_object->buffer.pointer[length])) {
length++;
}
@@ -117,94 +331,176 @@ acpi_ns_repair_object(struct acpi_predefined_data *data,
* terminated at Length+1.
*/
ACPI_MEMCPY(new_object->string.pointer,
- return_object->buffer.pointer, length);
+ original_object->buffer.pointer, length);
break;
+ default:
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *return_object = new_object;
+ return (AE_OK);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_buffer
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ns_convert_to_buffer(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ acpi_status status;
+ union acpi_operand_object **elements;
+ u32 *dword_buffer;
+ u32 count;
+ u32 i;
+
+ switch (original_object->common.type) {
case ACPI_TYPE_INTEGER:
+ /*
+ * Integer-to-Buffer conversion.
+ * Convert the Integer to a packed-byte buffer. _MAT and other
+ * objects need this sometimes, if a read has been performed on a
+ * Field object that is less than or equal to the global integer
+ * size (32 or 64 bits).
+ */
+ status =
+ acpi_ex_convert_to_buffer(original_object, &new_object);
+ if (ACPI_FAILURE(status)) {
+ return (status);
+ }
+ break;
- /* 1) Does the method/object legally return a buffer? */
+ case ACPI_TYPE_STRING:
- if (expected_btypes & ACPI_RTYPE_BUFFER) {
- /*
- * Convert the Integer to a packed-byte buffer. _MAT needs
- * this sometimes, if a read has been performed on a Field
- * object that is less than or equal to the global integer
- * size (32 or 64 bits).
- */
- status =
- acpi_ex_convert_to_buffer(return_object,
- &new_object);
- if (ACPI_FAILURE(status)) {
- return (status);
- }
+ /* String-to-Buffer conversion. Simple data copy */
+
+ new_object =
+ acpi_ut_create_buffer_object(original_object->string.
+ length);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
}
- /* 2) Does the method/object legally return a string? */
+ ACPI_MEMCPY(new_object->buffer.pointer,
+ original_object->string.pointer,
+ original_object->string.length);
+ break;
+
+ case ACPI_TYPE_PACKAGE:
+ /*
+ * This case is often seen for predefined names that must return a
+ * Buffer object with multiple DWORD integers within. For example,
+ * _FDE and _GTM. The Package can be converted to a Buffer.
+ */
+
+ /* All elements of the Package must be integers */
- else if (expected_btypes & ACPI_RTYPE_STRING) {
- /*
- * The only supported Integer-to-String conversion is to convert
- * an integer of value 0 to a NULL string. The last element of
- * _BIF and _BIX packages occasionally need this fix.
- */
- if (return_object->integer.value != 0) {
+ elements = original_object->package.elements;
+ count = original_object->package.count;
+
+ for (i = 0; i < count; i++) {
+ if ((!*elements) ||
+ ((*elements)->common.type != ACPI_TYPE_INTEGER)) {
return (AE_AML_OPERAND_TYPE);
}
+ elements++;
+ }
- /* Allocate a new NULL string object */
+ /* Create the new buffer object to replace the Package */
- new_object = acpi_ut_create_string_object(0);
- if (!new_object) {
- return (AE_NO_MEMORY);
- }
- } else {
- return (AE_AML_OPERAND_TYPE);
+ new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count));
+ if (!new_object) {
+ return (AE_NO_MEMORY);
}
- break;
- default:
+ /* Copy the package elements (integers) to the buffer as DWORDs */
- /* We cannot repair this object */
+ elements = original_object->package.elements;
+ dword_buffer = ACPI_CAST_PTR(u32, new_object->buffer.pointer);
+
+ for (i = 0; i < count; i++) {
+ *dword_buffer = (u32) (*elements)->integer.value;
+ dword_buffer++;
+ elements++;
+ }
+ break;
+ default:
return (AE_AML_OPERAND_TYPE);
}
- /* Object was successfully repaired */
+ *return_object = new_object;
+ return (AE_OK);
+}
- /*
- * If the original object is a package element, we need to:
- * 1. Set the reference count of the new object to match the
- * reference count of the old object.
- * 2. Decrement the reference count of the original object.
- */
- if (package_index != ACPI_NOT_PACKAGE_ELEMENT) {
- new_object->common.reference_count =
- return_object->common.reference_count;
+/*******************************************************************************
+ *
+ * FUNCTION: acpi_ns_convert_to_package
+ *
+ * PARAMETERS: original_object - Object to be converted
+ * return_object - Where the new converted object is returned
+ *
+ * RETURN: Status. AE_OK if conversion was successful.
+ *
+ * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of
+ * the buffer is converted to a single integer package element.
+ *
+ ******************************************************************************/
- if (return_object->common.reference_count > 1) {
- return_object->common.reference_count--;
+static acpi_status
+acpi_ns_convert_to_package(union acpi_operand_object *original_object,
+ union acpi_operand_object **return_object)
+{
+ union acpi_operand_object *new_object;
+ union acpi_operand_object **elements;
+ u32 length;
+ u8 *buffer;
+
+ switch (original_object->common.type) {
+ case ACPI_TYPE_BUFFER:
+
+ /* Buffer-to-Package conversion */
+
+ length = original_object->buffer.length;
+ new_object = acpi_ut_create_package_object(length);
+ if (!new_object) {
+ return (AE_NO_MEMORY);
}
- ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
- "Converted %s to expected %s at index %u",
- acpi_ut_get_object_type_name
- (return_object),
- acpi_ut_get_object_type_name(new_object),
- package_index));
- } else {
- ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
- "Converted %s to expected %s",
- acpi_ut_get_object_type_name
- (return_object),
- acpi_ut_get_object_type_name
- (new_object)));
- }
+ /* Convert each buffer byte to an integer package element */
- /* Delete old object, install the new return object */
+ elements = new_object->package.elements;
+ buffer = original_object->buffer.pointer;
- acpi_ut_remove_reference(return_object);
- *return_object_ptr = new_object;
- data->flags |= ACPI_OBJECT_REPAIRED;
+ while (length--) {
+ *elements =
+ acpi_ut_create_integer_object((u64) *buffer);
+ if (!*elements) {
+ acpi_ut_remove_reference(new_object);
+ return (AE_NO_MEMORY);
+ }
+ elements++;
+ buffer++;
+ }
+ break;
+
+ default:
+ return (AE_AML_OPERAND_TYPE);
+ }
+
+ *return_object = new_object;
return (AE_OK);
}
@@ -238,6 +534,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
{
union acpi_operand_object *pkg_obj_desc;
+ ACPI_FUNCTION_NAME(ns_repair_package_list);
+
/*
* Create the new outer package and populate it. The new package will
* have a single element, the lone subpackage.
@@ -254,8 +552,9 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data,
*obj_desc_ptr = pkg_obj_desc;
data->flags |= ACPI_OBJECT_REPAIRED;
- ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
- "Repaired Incorrectly formed Package"));
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Repaired incorrectly formed Package\n",
+ data->pathname));
return (AE_OK);
}
diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c
index d07b68613818..f13691c1cca5 100644
--- a/drivers/acpi/acpica/nsrepair2.c
+++ b/drivers/acpi/acpica/nsrepair2.c
@@ -45,6 +45,7 @@
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
+#include "acpredef.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsrepair2")
@@ -74,6 +75,10 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
static acpi_status
+acpi_ns_repair_FDE(struct acpi_predefined_data *data,
+ union acpi_operand_object **return_object_ptr);
+
+static acpi_status
acpi_ns_repair_PSS(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
@@ -89,9 +94,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
u8 sort_direction, char *sort_key_name);
static acpi_status
-acpi_ns_remove_null_elements(union acpi_operand_object *package);
-
-static acpi_status
acpi_ns_sort_list(union acpi_operand_object **elements,
u32 count, u32 index, u8 sort_direction);
@@ -104,17 +106,27 @@ acpi_ns_sort_list(union acpi_operand_object **elements,
* This table contains the names of the predefined methods for which we can
* perform more complex repairs.
*
- * _ALR: Sort the list ascending by ambient_illuminance if necessary
- * _PSS: Sort the list descending by Power if necessary
- * _TSS: Sort the list descending by Power if necessary
+ * As necessary:
+ *
+ * _ALR: Sort the list ascending by ambient_illuminance
+ * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
+ * _PSS: Sort the list descending by Power
+ * _TSS: Sort the list descending by Power
*/
static const struct acpi_repair_info acpi_ns_repairable_names[] = {
{"_ALR", acpi_ns_repair_ALR},
+ {"_FDE", acpi_ns_repair_FDE},
+ {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */
{"_PSS", acpi_ns_repair_PSS},
{"_TSS", acpi_ns_repair_TSS},
{{0, 0, 0, 0}, NULL} /* Table terminator */
};
+#define ACPI_FDE_FIELD_COUNT 5
+#define ACPI_FDE_BYTE_BUFFER_SIZE 5
+#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32))
+
/******************************************************************************
*
* FUNCTION: acpi_ns_complex_repairs
@@ -215,6 +227,94 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
/******************************************************************************
*
+ * 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
*
* PARAMETERS: Data - Pointer to validation data structure
@@ -345,6 +445,8 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
u32 previous_value;
acpi_status status;
+ ACPI_FUNCTION_NAME(ns_check_sorted_list);
+
/* The top-level object must be a package */
if (return_object->common.type != ACPI_TYPE_PACKAGE) {
@@ -352,24 +454,10 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
}
/*
- * Detect any NULL package elements and remove them from the
- * package.
- *
- * TBD: We may want to do this for all predefined names that
- * return a variable-length package of packages.
+ * NOTE: assumes list of sub-packages contains no NULL elements.
+ * Any NULL elements should have been removed by earlier call
+ * to acpi_ns_remove_null_elements.
*/
- status = acpi_ns_remove_null_elements(return_object);
- if (status == AE_NULL_ENTRY) {
- ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
- "NULL elements removed from package"));
-
- /* Exit if package is now zero length */
-
- if (!return_object->package.count) {
- return (AE_NULL_ENTRY);
- }
- }
-
outer_elements = return_object->package.elements;
outer_element_count = return_object->package.count;
if (!outer_element_count) {
@@ -422,10 +510,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
data->flags |= ACPI_OBJECT_REPAIRED;
- ACPI_INFO_PREDEFINED((AE_INFO, data->pathname,
- data->node_flags,
- "Repaired unsorted list - now sorted by %s",
- sort_key_name));
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Repaired unsorted list - now sorted by %s\n",
+ data->pathname, sort_key_name));
return (AE_OK);
}
@@ -440,36 +527,63 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
*
* FUNCTION: acpi_ns_remove_null_elements
*
- * PARAMETERS: obj_desc - A Package object
+ * PARAMETERS: Data - Pointer to validation data structure
+ * package_type - An acpi_return_package_types value
+ * obj_desc - A Package object
*
- * RETURN: Status. AE_NULL_ENTRY means that one or more elements were
- * removed.
+ * RETURN: None.
*
- * DESCRIPTION: Remove all NULL package elements and update the package count.
+ * DESCRIPTION: Remove all NULL package elements from packages that contain
+ * a variable number of sub-packages.
*
*****************************************************************************/
-static acpi_status
-acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
+void
+acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
+ u8 package_type,
+ union acpi_operand_object *obj_desc)
{
union acpi_operand_object **source;
union acpi_operand_object **dest;
- acpi_status status = AE_OK;
u32 count;
u32 new_count;
u32 i;
+ ACPI_FUNCTION_NAME(ns_remove_null_elements);
+
+ /*
+ * PTYPE1 packages contain no subpackages.
+ * PTYPE2 packages contain a variable number of sub-packages. We can
+ * safely remove all NULL elements from the PTYPE2 packages.
+ */
+ switch (package_type) {
+ case ACPI_PTYPE1_FIXED:
+ case ACPI_PTYPE1_VAR:
+ case ACPI_PTYPE1_OPTION:
+ return;
+
+ case ACPI_PTYPE2:
+ case ACPI_PTYPE2_COUNT:
+ case ACPI_PTYPE2_PKG_COUNT:
+ case ACPI_PTYPE2_FIXED:
+ case ACPI_PTYPE2_MIN:
+ case ACPI_PTYPE2_REV_FIXED:
+ break;
+
+ default:
+ return;
+ }
+
count = obj_desc->package.count;
new_count = count;
source = obj_desc->package.elements;
dest = source;
- /* Examine all elements of the package object */
+ /* Examine all elements of the package object, remove nulls */
for (i = 0; i < count; i++) {
if (!*source) {
- status = AE_NULL_ENTRY;
new_count--;
} else {
*dest = *source;
@@ -478,15 +592,18 @@ acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
source++;
}
- if (status == AE_NULL_ENTRY) {
+ /* Update parent package if any null elements were removed */
+
+ if (new_count < count) {
+ ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
+ "%s: Found and removed %u NULL elements\n",
+ data->pathname, (count - new_count)));
/* NULL terminate list and update the package count */
*dest = NULL;
obj_desc->package.count = new_count;
}
-
- return (status);
}
/******************************************************************************
diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c
index ea55ab4f9849..47d91e668a1b 100644
--- a/drivers/acpi/acpica/nsutils.c
+++ b/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
- * 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();
@@ -710,42 +711,6 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle 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
*
* PARAMETERS: none
diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c
index f2bd1da77001..f0c0892bc7e5 100644
--- a/drivers/acpi/acpica/nsxfeval.c
+++ b/drivers/acpi/acpica/nsxfeval.c
@@ -190,7 +190,7 @@ acpi_evaluate_object(acpi_handle handle,
/* Convert and validate the device handle */
- info->prefix_node = acpi_ns_map_handle_to_node(handle);
+ info->prefix_node = acpi_ns_validate_handle(handle);
if (!info->prefix_node) {
status = AE_BAD_PARAMETER;
goto cleanup;
@@ -552,7 +552,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle,
return (status);
}
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
return (status);
@@ -729,7 +729,7 @@ acpi_attach_data(acpi_handle obj_handle,
/* Convert and validate the handle */
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -775,7 +775,7 @@ acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler)
/* Convert and validate the handle */
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -822,7 +822,7 @@ acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data)
/* Convert and validate the handle */
- node = acpi_ns_map_handle_to_node(obj_handle);
+ node = acpi_ns_validate_handle(obj_handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c
index ddc84af6336e..e611dd961b20 100644
--- a/drivers/acpi/acpica/nsxfname.c
+++ b/drivers/acpi/acpica/nsxfname.c
@@ -93,7 +93,7 @@ acpi_get_handle(acpi_handle parent,
/* Convert a parent handle to a prefix node */
if (parent) {
- prefix_node = acpi_ns_map_handle_to_node(parent);
+ prefix_node = acpi_ns_validate_handle(parent);
if (!prefix_node) {
return (AE_BAD_PARAMETER);
}
@@ -114,7 +114,7 @@ acpi_get_handle(acpi_handle parent,
if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) {
*ret_handle =
- acpi_ns_convert_entry_to_handle(acpi_gbl_root_node);
+ ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node);
return (AE_OK);
}
} else if (!prefix_node) {
@@ -129,7 +129,7 @@ acpi_get_handle(acpi_handle parent,
status =
acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node);
if (ACPI_SUCCESS(status)) {
- *ret_handle = acpi_ns_convert_entry_to_handle(node);
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
}
return (status);
@@ -186,7 +186,7 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer)
return (status);
}
- node = acpi_ns_map_handle_to_node(handle);
+ node = acpi_ns_validate_handle(handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -291,7 +291,7 @@ acpi_get_object_info(acpi_handle handle,
goto cleanup;
}
- node = acpi_ns_map_handle_to_node(handle);
+ node = acpi_ns_validate_handle(handle);
if (!node) {
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (AE_BAD_PARAMETER);
diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c
index 4071bad4458e..0cc6ba01a495 100644
--- a/drivers/acpi/acpica/nsxfobj.c
+++ b/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 */
- node = acpi_ns_map_handle_to_node(handle);
+ node = acpi_ns_validate_handle(handle);
if (!node) {
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (AE_BAD_PARAMETER);
@@ -132,7 +132,7 @@ acpi_status acpi_get_type(acpi_handle handle, acpi_object_type * ret_type)
/* Convert and validate the handle */
- node = acpi_ns_map_handle_to_node(handle);
+ node = acpi_ns_validate_handle(handle);
if (!node) {
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return (AE_BAD_PARAMETER);
@@ -182,7 +182,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
/* Convert and validate the handle */
- node = acpi_ns_map_handle_to_node(handle);
+ node = acpi_ns_validate_handle(handle);
if (!node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -191,7 +191,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle)
/* Get the parent entry */
parent_node = acpi_ns_get_parent_node(node);
- *ret_handle = acpi_ns_convert_entry_to_handle(parent_node);
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, parent_node);
/* Return exception if parent is null */
@@ -251,7 +251,7 @@ acpi_get_next_object(acpi_object_type type,
/* Start search at the beginning of the specified scope */
- parent_node = acpi_ns_map_handle_to_node(parent);
+ parent_node = acpi_ns_validate_handle(parent);
if (!parent_node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -260,7 +260,7 @@ acpi_get_next_object(acpi_object_type type,
/* Non-null handle, ignore the parent */
/* Convert and validate the handle */
- child_node = acpi_ns_map_handle_to_node(child);
+ child_node = acpi_ns_validate_handle(child);
if (!child_node) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
@@ -276,7 +276,7 @@ acpi_get_next_object(acpi_object_type type,
}
if (ret_handle) {
- *ret_handle = acpi_ns_convert_entry_to_handle(node);
+ *ret_handle = ACPI_CAST_PTR(acpi_handle, node);
}
unlock_and_exit:
diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c
index 12934ad6da8e..d0c1b91eb8ca 100644
--- a/drivers/acpi/acpica/psxface.c
+++ b/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 */
if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {
- status = info->obj_desc->method.implementation(walk_state);
+ status =
+ info->obj_desc->method.extra.implementation(walk_state);
info->return_object = walk_state->return_desc;
/* Cleanup states */
diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c
index 395212bcd19b..f27feb4772f6 100644
--- a/drivers/acpi/acpica/rsxface.c
+++ b/drivers/acpi/acpica/rsxface.c
@@ -104,7 +104,7 @@ acpi_rs_validate_parameters(acpi_handle device_handle,
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
- node = acpi_ns_map_handle_to_node(device_handle);
+ node = acpi_ns_validate_handle(device_handle);
if (!node) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c
index 0f0c64bf8ac9..f857c5efb79f 100644
--- a/drivers/acpi/acpica/utcopy.c
+++ b/drivers/acpi/acpica/utcopy.c
@@ -323,11 +323,11 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type,
* RETURN: Status
*
* DESCRIPTION: This function is called to place a package object in a user
- * buffer. A package object by definition contains other objects.
+ * buffer. A package object by definition contains other objects.
*
* The buffer is assumed to have sufficient space for the object.
- * The caller must have verified the buffer length needed using the
- * acpi_ut_get_object_size function before calling this function.
+ * The caller must have verified the buffer length needed using
+ * the acpi_ut_get_object_size function before calling this function.
*
******************************************************************************/
@@ -382,12 +382,12 @@ acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object,
* FUNCTION: acpi_ut_copy_iobject_to_eobject
*
* PARAMETERS: internal_object - The internal object to be converted
- * buffer_ptr - Where the object is returned
+ * ret_buffer - Where the object is returned
*
* RETURN: Status
*
- * DESCRIPTION: This function is called to build an API object to be returned to
- * the caller.
+ * DESCRIPTION: This function is called to build an API object to be returned
+ * to the caller.
*
******************************************************************************/
@@ -626,7 +626,7 @@ acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object,
* PARAMETERS: external_object - The external object to be converted
* internal_object - Where the internal object is returned
*
- * RETURN: Status - the status of the call
+ * RETURN: Status
*
* DESCRIPTION: Converts an external object to an internal object.
*
@@ -665,7 +665,7 @@ acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object,
*
* RETURN: Status
*
- * DESCRIPTION: Simple copy of one internal object to another. Reference count
+ * DESCRIPTION: Simple copy of one internal object to another. Reference count
* of the destination object is preserved.
*
******************************************************************************/
@@ -897,10 +897,11 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type,
*
* FUNCTION: acpi_ut_copy_ipackage_to_ipackage
*
- * PARAMETERS: *source_obj - Pointer to the source package object
- * *dest_obj - Where the internal object is returned
+ * PARAMETERS: source_obj - Pointer to the source package object
+ * dest_obj - Where the internal object is returned
+ * walk_state - Current Walk state descriptor
*
- * RETURN: Status - the status of the call
+ * RETURN: Status
*
* DESCRIPTION: This function is called to copy an internal package object
* into another internal package object.
@@ -953,9 +954,9 @@ acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj,
*
* FUNCTION: acpi_ut_copy_iobject_to_iobject
*
- * PARAMETERS: walk_state - Current walk state
- * source_desc - The internal object to be copied
+ * PARAMETERS: source_desc - The internal object to be copied
* dest_desc - Where the copied object is returned
+ * walk_state - Current walk state
*
* RETURN: Status
*
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 3f4602b8f287..cada73ffdfa7 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -831,7 +831,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event)
dev_name(&device->dev), event,
acpi_battery_present(battery));
#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)
kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE);
#endif
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 741191524353..65f7e335f122 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -344,6 +344,152 @@ bool acpi_bus_can_wakeup(acpi_handle handle)
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
-------------------------------------------------------------------------- */
@@ -734,6 +880,8 @@ static int __init acpi_bus_init(void)
status = acpi_ec_ecdt_probe();
/* Ignore result. Not having an ECDT is not fatal. */
+ acpi_bus_osc_support();
+
status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 0c9c6a9a002c..8a95e8329df7 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -282,6 +282,13 @@ static int acpi_lid_send_state(struct acpi_device *device)
if (ret == NOTIFY_DONE)
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
device);
+ if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
+ /*
+ * It is also regarded as success if the notifier_chain
+ * returns NOTIFY_OK or NOTIFY_DONE.
+ */
+ ret = 0;
+ }
return ret;
}
diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c
index 8a690c3b8e23..cc421b7ae166 100644
--- a/drivers/acpi/debug.c
+++ b/drivers/acpi/debug.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
+#include <linux/debugfs.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
@@ -196,6 +197,80 @@ module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
NULL, 0644);
/* --------------------------------------------------------------------------
+ DebugFS Interface
+ -------------------------------------------------------------------------- */
+
+static ssize_t cm_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ static char *buf;
+ static int uncopied_bytes;
+ struct acpi_table_header table;
+ acpi_status status;
+
+ if (!(*ppos)) {
+ /* parse the table header to get the table length */
+ if (count <= sizeof(struct acpi_table_header))
+ return -EINVAL;
+ if (copy_from_user(&table, user_buf,
+ sizeof(struct acpi_table_header)))
+ return -EFAULT;
+ uncopied_bytes = table.length;
+ buf = kzalloc(uncopied_bytes, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ }
+
+ if (uncopied_bytes < count) {
+ kfree(buf);
+ return -EINVAL;
+ }
+
+ if (copy_from_user(buf + (*ppos), user_buf, count)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+
+ uncopied_bytes -= count;
+ *ppos += count;
+
+ if (!uncopied_bytes) {
+ status = acpi_install_method(buf);
+ kfree(buf);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+ add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
+ }
+
+ return count;
+}
+
+static const struct file_operations cm_fops = {
+ .write = cm_write,
+};
+
+static int acpi_debugfs_init(void)
+{
+ struct dentry *acpi_dir, *cm_dentry;
+
+ acpi_dir = debugfs_create_dir("acpi", NULL);
+ if (!acpi_dir)
+ goto err;
+
+ cm_dentry = debugfs_create_file("custom_method", S_IWUGO,
+ acpi_dir, NULL, &cm_fops);
+ if (!cm_dentry)
+ goto err;
+
+ return 0;
+
+err:
+ if (acpi_dir)
+ debugfs_remove(acpi_dir);
+ return -EINVAL;
+}
+
+/* --------------------------------------------------------------------------
FS Interface (/proc)
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI_PROCFS
@@ -286,7 +361,7 @@ static const struct file_operations acpi_system_debug_proc_fops = {
};
#endif
-int __init acpi_debug_init(void)
+int __init acpi_procfs_init(void)
{
#ifdef CONFIG_ACPI_PROCFS
struct proc_dir_entry *entry;
@@ -321,3 +396,10 @@ int __init acpi_debug_init(void)
return 0;
#endif
}
+
+int __init acpi_debug_init(void)
+{
+ acpi_debugfs_init();
+ acpi_procfs_init();
+ return 0;
+}
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index 30be3c148f7e..bbc2c1315c47 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -50,7 +50,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to "
" before undocking");
static struct atomic_notifier_head dock_notifier_list;
-static char dock_device_name[] = "dock";
static const struct acpi_device_id dock_device_ids[] = {
{"LNXDOCK", 0},
@@ -93,40 +92,30 @@ struct dock_dependent_device {
* Dock Dependent device functions *
*****************************************************************************/
/**
- * alloc_dock_dependent_device - allocate and init a dependent device
- * @handle: the acpi_handle of the dependent device
+ * add_dock_dependent_device - associate a device with the dock station
+ * @ds: The dock station
+ * @handle: handle of the dependent device
*
- * Allocate memory for a dependent device structure for a device referenced
- * by the acpi handle
+ * Add the dependent device to the dock's dependent device list.
*/
-static struct dock_dependent_device *
-alloc_dock_dependent_device(acpi_handle handle)
+static int
+add_dock_dependent_device(struct dock_station *ds, acpi_handle handle)
{
struct dock_dependent_device *dd;
dd = kzalloc(sizeof(*dd), GFP_KERNEL);
- if (dd) {
- dd->handle = handle;
- INIT_LIST_HEAD(&dd->list);
- INIT_LIST_HEAD(&dd->hotplug_list);
- }
- return dd;
-}
+ if (!dd)
+ return -ENOMEM;
+
+ dd->handle = handle;
+ INIT_LIST_HEAD(&dd->list);
+ INIT_LIST_HEAD(&dd->hotplug_list);
-/**
- * add_dock_dependent_device - associate a device with the dock station
- * @ds: The dock station
- * @dd: The dependent device
- *
- * Add the dependent device to the dock's dependent device list.
- */
-static void
-add_dock_dependent_device(struct dock_station *ds,
- struct dock_dependent_device *dd)
-{
spin_lock(&ds->dd_lock);
list_add_tail(&dd->list, &ds->dependent_devices);
spin_unlock(&ds->dd_lock);
+
+ return 0;
}
/**
@@ -249,6 +238,7 @@ static int is_battery(acpi_handle handle)
static int is_ejectable_bay(acpi_handle handle)
{
acpi_handle phandle;
+
if (!is_ejectable(handle))
return 0;
if (is_battery(handle) || is_ata(handle))
@@ -275,14 +265,13 @@ int is_dock_device(acpi_handle handle)
if (is_dock(handle))
return 1;
- list_for_each_entry(dock_station, &dock_stations, sibling) {
+
+ list_for_each_entry(dock_station, &dock_stations, sibling)
if (find_dock_dependent_device(dock_station, handle))
return 1;
- }
return 0;
}
-
EXPORT_SYMBOL_GPL(is_dock_device);
/**
@@ -305,8 +294,6 @@ static int dock_present(struct dock_station *ds)
return 0;
}
-
-
/**
* dock_create_acpi_device - add new devices to acpi
* @handle - handle of the device to add
@@ -320,7 +307,7 @@ static int dock_present(struct dock_station *ds)
*/
static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
{
- struct acpi_device *device = NULL;
+ struct acpi_device *device;
struct acpi_device *parent_device;
acpi_handle parent;
int ret;
@@ -337,8 +324,7 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle)
ret = acpi_bus_add(&device, parent_device, handle,
ACPI_BUS_TYPE_DEVICE);
if (ret) {
- pr_debug("error adding bus, %x\n",
- -ret);
+ pr_debug("error adding bus, %x\n", -ret);
return NULL;
}
}
@@ -364,7 +350,6 @@ static void dock_remove_acpi_device(acpi_handle handle)
}
}
-
/**
* hotplug_dock_devices - insert or remove devices on the dock station
* @ds: the dock station
@@ -384,10 +369,9 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event)
/*
* First call driver specific hotplug functions
*/
- list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) {
+ list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
if (dd->ops && dd->ops->handler)
dd->ops->handler(dd->handle, event, dd->context);
- }
/*
* Now make sure that an acpi_device is created for each
@@ -426,6 +410,7 @@ static void dock_event(struct dock_station *ds, u32 event, int num)
list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list)
if (dd->ops && dd->ops->uevent)
dd->ops->uevent(dd->handle, event, dd->context);
+
if (num != DOCK_EVENT)
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
}
@@ -456,8 +441,8 @@ static void eject_dock(struct dock_station *ds)
arg.type = ACPI_TYPE_INTEGER;
arg.integer.value = 1;
- if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0",
- &arg_list, NULL)))
+ status = acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL);
+ if (ACPI_FAILURE(status))
pr_debug("Failed to evaluate _EJ0!\n");
}
@@ -577,7 +562,6 @@ int register_dock_notifier(struct notifier_block *nb)
return atomic_notifier_chain_register(&dock_notifier_list, nb);
}
-
EXPORT_SYMBOL_GPL(register_dock_notifier);
/**
@@ -591,7 +575,6 @@ void unregister_dock_notifier(struct notifier_block *nb)
atomic_notifier_chain_unregister(&dock_notifier_list, nb);
}
-
EXPORT_SYMBOL_GPL(unregister_dock_notifier);
/**
@@ -636,7 +619,6 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops,
return ret;
}
-
EXPORT_SYMBOL_GPL(register_hotplug_dock_device);
/**
@@ -657,7 +639,6 @@ void unregister_hotplug_dock_device(acpi_handle handle)
dock_del_hotplug_device(dock_station, dd);
}
}
-
EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device);
/**
@@ -772,7 +753,7 @@ struct dock_data {
static void acpi_dock_deferred_cb(void *context)
{
- struct dock_data *data = (struct dock_data *)context;
+ struct dock_data *data = context;
dock_notify(data->handle, data->event, data->ds);
kfree(data);
@@ -782,23 +763,22 @@ static int acpi_dock_notifier_call(struct notifier_block *this,
unsigned long event, void *data)
{
struct dock_station *dock_station;
- acpi_handle handle = (acpi_handle)data;
+ acpi_handle handle = data;
if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK
&& event != ACPI_NOTIFY_EJECT_REQUEST)
return 0;
list_for_each_entry(dock_station, &dock_stations, sibling) {
if (dock_station->handle == handle) {
- struct dock_data *dock_data;
+ struct dock_data *dd;
- dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL);
- if (!dock_data)
+ dd = kmalloc(sizeof(*dd), GFP_KERNEL);
+ if (!dd)
return 0;
- dock_data->handle = handle;
- dock_data->event = event;
- dock_data->ds = dock_station;
- acpi_os_hotplug_execute(acpi_dock_deferred_cb,
- dock_data);
+ dd->handle = handle;
+ dd->event = event;
+ dd->ds = dock_station;
+ acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd);
return 0 ;
}
}
@@ -826,7 +806,6 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
acpi_status status;
acpi_handle tmp, parent;
struct dock_station *ds = context;
- struct dock_dependent_device *dd;
status = acpi_bus_get_ejd(handle, &tmp);
if (ACPI_FAILURE(status)) {
@@ -840,11 +819,9 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv)
goto fdd_out;
}
- if (tmp == ds->handle) {
- dd = alloc_dock_dependent_device(handle);
- if (dd)
- add_dock_dependent_device(ds, dd);
- }
+ if (tmp == ds->handle)
+ add_dock_dependent_device(ds, handle);
+
fdd_out:
return AE_OK;
}
@@ -857,8 +834,7 @@ static ssize_t show_docked(struct device *dev,
{
struct acpi_device *tmp;
- struct dock_station *dock_station = *((struct dock_station **)
- dev->platform_data);
+ struct dock_station *dock_station = dev->platform_data;
if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp)))
return snprintf(buf, PAGE_SIZE, "1\n");
@@ -872,8 +848,7 @@ static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL);
static ssize_t show_flags(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct dock_station *dock_station = *((struct dock_station **)
- dev->platform_data);
+ struct dock_station *dock_station = dev->platform_data;
return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags);
}
@@ -886,8 +861,7 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
- struct dock_station *dock_station = *((struct dock_station **)
- dev->platform_data);
+ struct dock_station *dock_station = dev->platform_data;
if (!count)
return -EINVAL;
@@ -905,8 +879,7 @@ static ssize_t show_dock_uid(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned long long lbuf;
- struct dock_station *dock_station = *((struct dock_station **)
- dev->platform_data);
+ struct dock_station *dock_station = dev->platform_data;
acpi_status status = acpi_evaluate_integer(dock_station->handle,
"_UID", NULL, &lbuf);
if (ACPI_FAILURE(status))
@@ -919,8 +892,7 @@ static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL);
static ssize_t show_dock_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct dock_station *dock_station = *((struct dock_station **)
- dev->platform_data);
+ struct dock_station *dock_station = dev->platform_data;
char *type;
if (dock_station->flags & DOCK_IS_DOCK)
@@ -936,6 +908,19 @@ static ssize_t show_dock_type(struct device *dev,
}
static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
+static struct attribute *dock_attributes[] = {
+ &dev_attr_docked.attr,
+ &dev_attr_flags.attr,
+ &dev_attr_undock.attr,
+ &dev_attr_uid.attr,
+ &dev_attr_type.attr,
+ NULL
+};
+
+static struct attribute_group dock_attribute_group = {
+ .attrs = dock_attributes
+};
+
/**
* dock_add - add a new dock station
* @handle: the dock station handle
@@ -945,39 +930,30 @@ static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL);
*/
static int dock_add(acpi_handle handle)
{
- int ret;
- struct dock_dependent_device *dd;
- struct dock_station *dock_station;
- struct platform_device *dock_device;
+ int ret, id;
+ struct dock_station ds, *dock_station;
+ struct platform_device *dd;
+
+ id = dock_station_count;
+ dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
+ if (IS_ERR(dd))
+ return PTR_ERR(dd);
+
+ dock_station = dd->dev.platform_data;
- /* allocate & initialize the dock_station private data */
- dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL);
- if (!dock_station)
- return -ENOMEM;
dock_station->handle = handle;
+ dock_station->dock_device = dd;
dock_station->last_dock_time = jiffies - HZ;
- INIT_LIST_HEAD(&dock_station->dependent_devices);
- INIT_LIST_HEAD(&dock_station->hotplug_devices);
- INIT_LIST_HEAD(&dock_station->sibling);
- spin_lock_init(&dock_station->dd_lock);
+
mutex_init(&dock_station->hp_lock);
+ spin_lock_init(&dock_station->dd_lock);
+ INIT_LIST_HEAD(&dock_station->sibling);
+ INIT_LIST_HEAD(&dock_station->hotplug_devices);
ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list);
-
- /* initialize platform device stuff */
- dock_station->dock_device =
- platform_device_register_simple(dock_device_name,
- dock_station_count, NULL, 0);
- dock_device = dock_station->dock_device;
- if (IS_ERR(dock_device)) {
- kfree(dock_station);
- dock_station = NULL;
- return PTR_ERR(dock_device);
- }
- platform_device_add_data(dock_device, &dock_station,
- sizeof(struct dock_station *));
+ INIT_LIST_HEAD(&dock_station->dependent_devices);
/* we want the dock device to send uevents */
- dev_set_uevent_suppress(&dock_device->dev, 0);
+ dev_set_uevent_suppress(&dd->dev, 0);
if (is_dock(handle))
dock_station->flags |= DOCK_IS_DOCK;
@@ -986,47 +962,9 @@ static int dock_add(acpi_handle handle)
if (is_battery(handle))
dock_station->flags |= DOCK_IS_BAT;
- ret = device_create_file(&dock_device->dev, &dev_attr_docked);
- if (ret) {
- printk(KERN_ERR "Error %d adding sysfs file\n", ret);
- platform_device_unregister(dock_device);
- kfree(dock_station);
- dock_station = NULL;
- return ret;
- }
- ret = device_create_file(&dock_device->dev, &dev_attr_undock);
- if (ret) {
- printk(KERN_ERR "Error %d adding sysfs file\n", ret);
- device_remove_file(&dock_device->dev, &dev_attr_docked);
- platform_device_unregister(dock_device);
- kfree(dock_station);
- dock_station = NULL;
- return ret;
- }
- ret = device_create_file(&dock_device->dev, &dev_attr_uid);
- if (ret) {
- printk(KERN_ERR "Error %d adding sysfs file\n", ret);
- device_remove_file(&dock_device->dev, &dev_attr_docked);
- device_remove_file(&dock_device->dev, &dev_attr_undock);
- platform_device_unregister(dock_device);
- kfree(dock_station);
- dock_station = NULL;
- return ret;
- }
- ret = device_create_file(&dock_device->dev, &dev_attr_flags);
- if (ret) {
- printk(KERN_ERR "Error %d adding sysfs file\n", ret);
- device_remove_file(&dock_device->dev, &dev_attr_docked);
- device_remove_file(&dock_device->dev, &dev_attr_undock);
- device_remove_file(&dock_device->dev, &dev_attr_uid);
- platform_device_unregister(dock_device);
- kfree(dock_station);
- dock_station = NULL;
- return ret;
- }
- ret = device_create_file(&dock_device->dev, &dev_attr_type);
+ ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group);
if (ret)
- printk(KERN_ERR"Error %d adding sysfs file\n", ret);
+ goto err_unregister;
/* Find dependent devices */
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
@@ -1034,58 +972,43 @@ static int dock_add(acpi_handle handle)
dock_station, NULL);
/* add the dock station as a device dependent on itself */
- dd = alloc_dock_dependent_device(handle);
- if (!dd) {
- kfree(dock_station);
- dock_station = NULL;
- ret = -ENOMEM;
- goto dock_add_err_unregister;
- }
- add_dock_dependent_device(dock_station, dd);
+ ret = add_dock_dependent_device(dock_station, handle);
+ if (ret)
+ goto err_rmgroup;
dock_station_count++;
list_add(&dock_station->sibling, &dock_stations);
return 0;
-dock_add_err_unregister:
- device_remove_file(&dock_device->dev, &dev_attr_type);
- device_remove_file(&dock_device->dev, &dev_attr_docked);
- device_remove_file(&dock_device->dev, &dev_attr_undock);
- device_remove_file(&dock_device->dev, &dev_attr_uid);
- device_remove_file(&dock_device->dev, &dev_attr_flags);
- platform_device_unregister(dock_device);
- kfree(dock_station);
- dock_station = NULL;
+err_rmgroup:
+ sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group);
+err_unregister:
+ platform_device_unregister(dd);
+ printk(KERN_ERR "%s encountered error %d\n", __func__, ret);
return ret;
}
/**
* dock_remove - free up resources related to the dock station
*/
-static int dock_remove(struct dock_station *dock_station)
+static int dock_remove(struct dock_station *ds)
{
struct dock_dependent_device *dd, *tmp;
- struct platform_device *dock_device = dock_station->dock_device;
+ struct platform_device *dock_device = ds->dock_device;
if (!dock_station_count)
return 0;
/* remove dependent devices */
- list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices,
- list)
- kfree(dd);
+ list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list)
+ kfree(dd);
+
+ list_del(&ds->sibling);
/* cleanup sysfs */
- device_remove_file(&dock_device->dev, &dev_attr_type);
- device_remove_file(&dock_device->dev, &dev_attr_docked);
- device_remove_file(&dock_device->dev, &dev_attr_undock);
- device_remove_file(&dock_device->dev, &dev_attr_uid);
- device_remove_file(&dock_device->dev, &dev_attr_flags);
+ sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group);
platform_device_unregister(dock_device);
- /* free dock station memory */
- kfree(dock_station);
- dock_station = NULL;
return 0;
}
@@ -1103,11 +1026,10 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status = AE_OK;
- if (is_dock(handle)) {
- if (dock_add(handle) >= 0) {
+ if (is_dock(handle))
+ if (dock_add(handle) >= 0)
status = AE_CTRL_TERMINATE;
- }
- }
+
return status;
}
@@ -1145,8 +1067,7 @@ static int __init dock_init(void)
static void __exit dock_exit(void)
{
- struct dock_station *dock_station;
- struct dock_station *tmp;
+ struct dock_station *tmp, *dock_station;
unregister_acpi_bus_notifier(&dock_acpi_notifier);
list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling)
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index f419849a0d3f..acf2ab249842 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -267,7 +267,7 @@ static int acpi_fan_add(struct acpi_device *device)
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;
result = sysfs_create_link(&device->dev.kobj,
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index 2be2fb66204e..7ad48dfc12db 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -28,6 +28,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/acpi.h>
+#include <linux/numa.h>
#include <acpi/acpi_bus.h>
#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 */
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]
- = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
+ = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
int pxm_to_node(int pxm)
{
if (pxm < 0)
- return NID_INVAL;
+ return NUMA_NO_NODE;
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];
- if (node < 0){
+ if (node < 0) {
if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
- return NID_INVAL;
+ return NUMA_NO_NODE;
node = first_unset_node(nodes_found_map);
__acpi_map_pxm_to_node(pxm, node);
node_set(node, nodes_found_map);
@@ -79,16 +80,6 @@ int acpi_map_pxm_to_node(int pxm)
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
acpi_table_print_srat_entry(struct acpi_subtable_header *header)
{
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 7c1c59ea9ec6..02e8464e480f 100644
--- a/drivers/acpi/osl.c
+++ b/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
* 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;
int ioport;
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 1af808171d46..101cce3681d1 100644
--- a/drivers/acpi/pci_root.c
+++ b/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,
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;
- 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;
}
@@ -277,10 +229,10 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags)
u32 support_set, result, capbuf[3];
/* 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_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);
if (ACPI_SUCCESS(status)) {
@@ -427,7 +379,7 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags)
if (ACPI_FAILURE(status))
return status;
- control_req = (flags & OSC_CONTROL_MASKS);
+ control_req = (flags & OSC_PCI_CONTROL_MASKS);
if (!control_req)
return AE_TYPE;
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index cb4283f5a79d..41731236f9a1 100644
--- a/drivers/acpi/processor_core.c
+++ b/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);
}
-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;
@@ -722,7 +722,7 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event)
switch (event) {
case ACPI_PROCESSOR_NOTIFY_PERFORMANCE:
saved = pr->performance_platform_limit;
- acpi_processor_ppc_has_changed(pr);
+ acpi_processor_ppc_has_changed(pr, 1);
if (saved == pr->performance_platform_limit)
break;
acpi_bus_generate_proc_event(device, event,
@@ -758,7 +758,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
struct acpi_processor *pr = per_cpu(processors, cpu);
if (action == CPU_ONLINE && pr) {
- acpi_processor_ppc_has_changed(pr);
+ acpi_processor_ppc_has_changed(pr, 0);
acpi_processor_cst_has_changed(pr);
acpi_processor_tstate_has_changed(pr);
}
@@ -830,7 +830,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
arch_acpi_processor_cleanup_pdc(pr);
#ifdef CONFIG_CPU_FREQ
- acpi_processor_ppc_has_changed(pr);
+ acpi_processor_ppc_has_changed(pr, 0);
#endif
acpi_processor_get_throttling_info(pr);
acpi_processor_get_limit_info(pr);
@@ -845,7 +845,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
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);
result = sysfs_create_link(&device->dev.kobj,
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index bbd066e7f854..d1676b1754d9 100644
--- a/drivers/acpi/processor_idle.c
+++ b/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;
}
-static void lapic_timer_propagate_broadcast(void *arg)
+static void __lapic_timer_propagate_broadcast(void *arg)
{
struct acpi_processor *pr = (struct acpi_processor *) arg;
unsigned long reason;
@@ -175,6 +175,12 @@ static void lapic_timer_propagate_broadcast(void *arg)
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 */
static void lapic_timer_state_broadcast(struct acpi_processor *pr,
struct acpi_processor_cx *cx,
@@ -638,8 +644,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
working++;
}
- smp_call_function_single(pr->id, lapic_timer_propagate_broadcast,
- pr, 1);
+ lapic_timer_propagate_broadcast(pr);
return (working);
}
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 01e366d2b6fb..2cabadcc4d8c 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -152,15 +152,59 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
return 0;
}
-int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
+#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
+/*
+ * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status
+ * @handle: ACPI processor handle
+ * @status: the status code of _PPC evaluation
+ * 0: success. OSPM is now using the performance state specificed.
+ * 1: failure. OSPM has not changed the number of P-states in use
+ */
+static void acpi_processor_ppc_ost(acpi_handle handle, int status)
+{
+ union acpi_object params[2] = {
+ {.type = ACPI_TYPE_INTEGER,},
+ {.type = ACPI_TYPE_INTEGER,},
+ };
+ struct acpi_object_list arg_list = {2, params};
+ acpi_handle temp;
+
+ params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE;
+ params[1].integer.value = status;
+
+ /* when there is no _OST , skip it */
+ if (ACPI_FAILURE(acpi_get_handle(handle, "_OST", &temp)))
+ return;
+
+ acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
+ return;
+}
+
+int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
{
int ret;
- if (ignore_ppc)
+ if (ignore_ppc) {
+ /*
+ * Only when it is notification event, the _OST object
+ * will be evaluated. Otherwise it is skipped.
+ */
+ if (event_flag)
+ acpi_processor_ppc_ost(pr->handle, 1);
return 0;
+ }
ret = acpi_processor_get_platform_limit(pr);
-
+ /*
+ * Only when it is notification event, the _OST object
+ * will be evaluated. Otherwise it is skipped.
+ */
+ if (event_flag) {
+ if (ret < 0)
+ acpi_processor_ppc_ost(pr->handle, 1);
+ else
+ acpi_processor_ppc_ost(pr->handle, 0);
+ }
if (ret < 0)
return (ret);
else
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 65f67815902a..9073ada88835 100644
--- a/drivers/acpi/thermal.c
+++ b/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));
}
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++) {
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index d2e698096ace..679cd08b80b4 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -64,6 +64,7 @@
#include <linux/dmi.h>
#include <linux/string.h>
#include <linux/ctype.h>
+#include <linux/pnp.h>
#ifdef CONFIG_PPC_OF
#include <linux/of_device.h>
@@ -1919,7 +1920,7 @@ struct SPMITable {
s8 spmi_id[1]; /* A '\0' terminated array starts here. */
};
-static __devinit int try_init_acpi(struct SPMITable *spmi)
+static __devinit int try_init_spmi(struct SPMITable *spmi)
{
struct smi_info *info;
u8 addr_space;
@@ -1940,7 +1941,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
return -ENOMEM;
}
- info->addr_source = "ACPI";
+ info->addr_source = "SPMI";
/* Figure out the interface type. */
switch (spmi->InterfaceType) {
@@ -2002,7 +2003,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi)
return 0;
}
-static __devinit void acpi_find_bmc(void)
+static __devinit void spmi_find_bmc(void)
{
acpi_status status;
struct SPMITable *spmi;
@@ -2020,9 +2021,106 @@ static __devinit void acpi_find_bmc(void)
if (status != AE_OK)
return;
- try_init_acpi(spmi);
+ try_init_spmi(spmi);
}
}
+
+static int __devinit ipmi_pnp_probe(struct pnp_dev *dev,
+ const struct pnp_device_id *dev_id)
+{
+ struct acpi_device *acpi_dev;
+ struct smi_info *info;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long tmp;
+
+ acpi_dev = pnp_acpi_device(dev);
+ if (!acpi_dev)
+ return -ENODEV;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->addr_source = "ACPI";
+
+ handle = acpi_dev->handle;
+
+ /* _IFT tells us the interface type: KCS, BT, etc */
+ status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ goto err_free;
+
+ switch (tmp) {
+ case 1:
+ info->si_type = SI_KCS;
+ break;
+ case 2:
+ info->si_type = SI_SMIC;
+ break;
+ case 3:
+ info->si_type = SI_BT;
+ break;
+ default:
+ dev_info(&dev->dev, "unknown interface type %lld\n", tmp);
+ goto err_free;
+ }
+
+ if (pnp_port_valid(dev, 0)) {
+ info->io_setup = port_setup;
+ info->io.addr_type = IPMI_IO_ADDR_SPACE;
+ info->io.addr_data = pnp_port_start(dev, 0);
+ } else if (pnp_mem_valid(dev, 0)) {
+ info->io_setup = mem_setup;
+ info->io.addr_type = IPMI_MEM_ADDR_SPACE;
+ info->io.addr_data = pnp_mem_start(dev, 0);
+ } else {
+ dev_err(&dev->dev, "no I/O or memory address\n");
+ goto err_free;
+ }
+
+ info->io.regspacing = DEFAULT_REGSPACING;
+ info->io.regsize = DEFAULT_REGSPACING;
+ info->io.regshift = 0;
+
+ /* If _GPE exists, use it; otherwise use standard interrupts */
+ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
+ if (ACPI_SUCCESS(status)) {
+ info->irq = tmp;
+ info->irq_setup = acpi_gpe_irq_setup;
+ } else if (pnp_irq_valid(dev, 0)) {
+ info->irq = pnp_irq(dev, 0);
+ info->irq_setup = std_irq_setup;
+ }
+
+ info->dev = &acpi_dev->dev;
+ pnp_set_drvdata(dev, info);
+
+ return try_smi_init(info);
+
+err_free:
+ kfree(info);
+ return -EINVAL;
+}
+
+static void __devexit ipmi_pnp_remove(struct pnp_dev *dev)
+{
+ struct smi_info *info = pnp_get_drvdata(dev);
+
+ cleanup_one_si(info);
+}
+
+static const struct pnp_device_id pnp_dev_table[] = {
+ {"IPI0001", 0},
+ {"", 0},
+};
+
+static struct pnp_driver ipmi_pnp_driver = {
+ .name = DEVICE_NAME,
+ .probe = ipmi_pnp_probe,
+ .remove = __devexit_p(ipmi_pnp_remove),
+ .id_table = pnp_dev_table,
+};
#endif
#ifdef CONFIG_DMI
@@ -2202,7 +2300,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
int rv;
int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
struct smi_info *info;
- int first_reg_offset = 0;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
@@ -2241,9 +2338,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev,
info->addr_source_cleanup = ipmi_pci_cleanup;
info->addr_source_data = pdev;
- if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID)
- first_reg_offset = 1;
-
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
info->io_setup = port_setup;
info->io.addr_type = IPMI_IO_ADDR_SPACE;
@@ -3108,7 +3202,10 @@ static __devinit int init_ipmi_si(void)
#endif
#ifdef CONFIG_ACPI
- acpi_find_bmc();
+ spmi_find_bmc();
+#endif
+#ifdef CONFIG_PNP
+ pnp_register_driver(&ipmi_pnp_driver);
#endif
#ifdef CONFIG_PCI
@@ -3233,6 +3330,9 @@ static __exit void cleanup_ipmi_si(void)
#ifdef CONFIG_PCI
pci_unregister_driver(&ipmi_pci_driver);
#endif
+#ifdef CONFIG_PNP
+ pnp_unregister_driver(&ipmi_pnp_driver);
+#endif
#ifdef CONFIG_PPC_OF
of_unregister_platform_driver(&ipmi_of_platform_driver);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 59f4ba1b7034..1a7a9fc50ea1 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -248,19 +248,6 @@ config SGI_GRU_DEBUG
This option enables addition debugging code for the SGI GRU driver. If
you are unsure, say N.
-config DELL_LAPTOP
- tristate "Dell Laptop Extras (EXPERIMENTAL)"
- depends on X86
- depends on DCDBAS
- depends on EXPERIMENTAL
- depends on BACKLIGHT_CLASS_DEVICE
- depends on RFKILL
- depends on POWER_SUPPLY
- default n
- ---help---
- This driver adds support for rfkill and backlight control to Dell
- laptops.
-
config ISL29003
tristate "Intersil ISL29003 ambient light sensor"
depends on I2C && SYSFS
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 55ca39dea42e..fc5bf9d2a3f3 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -334,6 +334,8 @@ config EEEPC_LAPTOP
depends on HOTPLUG_PCI
select BACKLIGHT_CLASS_DEVICE
select HWMON
+ select LEDS_CLASS
+ select NEW_LEDS
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
@@ -365,6 +367,18 @@ config ACPI_WMI
It is safe to enable this driver even if your DSDT doesn't define
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
tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
depends on ACPI
@@ -435,4 +449,19 @@ config ACPI_TOSHIBA
If you have a legacy free Toshiba laptop (such as the Libretto L1
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
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index d1c16210a512..b7474b6a8bf1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -18,6 +18,8 @@ obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
+obj-$(CONFIG_MSI_WMI) += msi-wmi.o
obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index be27aa47e810..79b15b9d9cf0 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -52,7 +52,7 @@
*/
#undef START_IN_KERNEL_MODE
-#define DRV_VER "0.5.18"
+#define DRV_VER "0.5.20"
/*
* 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");
/*
- * 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
* the fan speed depending on the temperature
*/
struct fancmd {
u8 cmd_off;
+ u8 chk_off;
u8 cmd_auto;
};
@@ -134,32 +136,41 @@ struct bios_settings_t {
/* Register addresses and values for different BIOS versions */
static const struct bios_settings_t bios_tbl[] = {
/* 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 */
- {"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 */
- {"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 */
- {"", "", "", 0, 0, {0, 0} }
+ {"", "", "", 0, 0, {0, 0, 0} }
};
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))
return -EINVAL;
- if (fan != bios_cfg->cmd.cmd_off)
+ if (fan != bios_cfg->cmd.chk_off)
*state = ACERHDF_FAN_AUTO;
else
*state = ACERHDF_FAN_OFF;
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index b39d2bb3e75b..61a1c7503658 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -221,6 +221,7 @@ static struct asus_hotk *hotk;
*/
static const struct acpi_device_id asus_device_ids[] = {
{"ATK0100", 0},
+ {"ATK0101", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, asus_device_ids);
@@ -232,6 +233,7 @@ static void asus_hotk_notify(struct acpi_device *device, u32 event);
static struct acpi_driver asus_hotk_driver = {
.name = ASUS_HOTK_NAME,
.class = ASUS_HOTK_CLASS,
+ .owner = THIS_MODULE,
.ids = asus_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
@@ -293,6 +295,11 @@ struct key_entry {
enum { KE_KEY, KE_END };
static struct key_entry asus_keymap[] = {
+ {KE_KEY, 0x02, KEY_SCREENLOCK},
+ {KE_KEY, 0x05, KEY_WLAN},
+ {KE_KEY, 0x08, KEY_F13},
+ {KE_KEY, 0x17, KEY_ZOOM},
+ {KE_KEY, 0x1f, KEY_BATTERY},
{KE_KEY, 0x30, KEY_VOLUMEUP},
{KE_KEY, 0x31, KEY_VOLUMEDOWN},
{KE_KEY, 0x32, KEY_MUTE},
@@ -312,8 +319,11 @@ static struct key_entry asus_keymap[] = {
{KE_KEY, 0x5F, KEY_WLAN},
{KE_KEY, 0x60, KEY_SWITCHVIDEOMODE},
{KE_KEY, 0x61, KEY_SWITCHVIDEOMODE},
- {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */
+ {KE_KEY, 0x62, KEY_SWITCHVIDEOMODE},
+ {KE_KEY, 0x63, KEY_SWITCHVIDEOMODE},
+ {KE_KEY, 0x6B, KEY_F13}, /* Lock Touchpad */
{KE_KEY, 0x82, KEY_CAMERA},
+ {KE_KEY, 0x88, KEY_WLAN },
{KE_KEY, 0x8A, KEY_PROG1},
{KE_KEY, 0x95, KEY_MEDIA},
{KE_KEY, 0x99, KEY_PHONE},
@@ -1240,9 +1250,6 @@ static int asus_hotk_add(struct acpi_device *device)
{
int result;
- if (!device)
- return -EINVAL;
-
pr_notice("Asus Laptop Support version %s\n",
ASUS_LAPTOP_VERSION);
@@ -1283,8 +1290,8 @@ static int asus_hotk_add(struct acpi_device *device)
hotk->ledd_status = 0xFFF;
/* Set initial values of light sensor and level */
- hotk->light_switch = 1; /* Default to light sensor disabled */
- hotk->light_level = 0; /* level 5 for sensor sensitivity */
+ hotk->light_switch = 0; /* Default to light sensor disabled */
+ hotk->light_level = 5; /* level 5 for sensor sensitivity */
if (ls_switch_handle)
set_light_sens_switch(hotk->light_switch);
@@ -1306,9 +1313,6 @@ end:
static int asus_hotk_remove(struct acpi_device *device, int type)
{
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
-
kfree(hotk->name);
kfree(hotk);
@@ -1444,9 +1448,6 @@ static int __init asus_laptop_init(void)
{
int result;
- if (acpi_disabled)
- return -ENODEV;
-
result = acpi_bus_register_driver(&asus_hotk_driver);
if (result < 0)
return result;
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
index ddf5240ade8c..0c9c53111a22 100644
--- a/drivers/platform/x86/asus_acpi.c
+++ b/drivers/platform/x86/asus_acpi.c
@@ -466,6 +466,7 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
static struct acpi_driver asus_hotk_driver = {
.name = "asus_acpi",
.class = ACPI_HOTK_CLASS,
+ .owner = THIS_MODULE,
.ids = asus_device_ids,
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
.ops = {
@@ -1334,9 +1335,6 @@ static int asus_hotk_add(struct acpi_device *device)
acpi_status status = AE_OK;
int result;
- if (!device)
- return -EINVAL;
-
printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n",
ASUS_ACPI_VERSION);
@@ -1392,9 +1390,6 @@ end:
static int asus_hotk_remove(struct acpi_device *device, int type)
{
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
-
asus_hotk_remove_fs(device);
kfree(hotk);
@@ -1422,21 +1417,17 @@ static int __init asus_acpi_init(void)
{
int result;
- if (acpi_disabled)
- return -ENODEV;
+ result = acpi_bus_register_driver(&asus_hotk_driver);
+ if (result < 0)
+ return result;
asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
if (!asus_proc_dir) {
printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n");
+ acpi_bus_unregister_driver(&asus_hotk_driver);
return -ENODEV;
}
- result = acpi_bus_register_driver(&asus_hotk_driver);
- if (result < 0) {
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
- return result;
- }
-
/*
* This is a bit of a kludge. We only want this module loaded
* for ASUS systems, but there's currently no way to probe the
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 74909c4aaeea..3780994dc8f2 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -58,6 +58,14 @@ static int da_command_code;
static int da_num_tokens;
static struct calling_interface_token *da_tokens;
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "dell-laptop",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
@@ -74,7 +82,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
{ }
};
-static void parse_da_table(const struct dmi_header *dm)
+static void __init parse_da_table(const struct dmi_header *dm)
{
/* Final token is a terminator, so we don't want to copy it */
int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
@@ -103,7 +111,7 @@ static void parse_da_table(const struct dmi_header *dm)
da_num_tokens += tokens;
}
-static void find_tokens(const struct dmi_header *dm, void *dummy)
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
{
switch (dm->type) {
case 0xd4: /* Indexed IO */
@@ -197,8 +205,8 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
dell_send_request(&buffer, 17, 11);
status = buffer.output[1];
- if (status & BIT(bit))
- rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
+ rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
+ rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static const struct rfkill_ops dell_rfkill_ops = {
@@ -206,7 +214,7 @@ static const struct rfkill_ops dell_rfkill_ops = {
.query = dell_rfkill_query,
};
-static int dell_setup_rfkill(void)
+static int __init dell_setup_rfkill(void)
{
struct calling_interface_buffer buffer;
int status;
@@ -217,7 +225,8 @@ static int dell_setup_rfkill(void)
status = buffer.output[1];
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
- wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN,
+ wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
+ RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
@@ -229,7 +238,8 @@ static int dell_setup_rfkill(void)
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
- bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL,
+ bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
+ &platform_device->dev,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
@@ -242,7 +252,9 @@ static int dell_setup_rfkill(void)
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
- wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN,
+ wwan_rfkill = rfkill_alloc("dell-wwan",
+ &platform_device->dev,
+ RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
@@ -268,6 +280,22 @@ err_wifi:
return ret;
}
+static void dell_cleanup_rfkill(void)
+{
+ if (wifi_rfkill) {
+ rfkill_unregister(wifi_rfkill);
+ rfkill_destroy(wifi_rfkill);
+ }
+ if (bluetooth_rfkill) {
+ rfkill_unregister(bluetooth_rfkill);
+ rfkill_destroy(bluetooth_rfkill);
+ }
+ if (wwan_rfkill) {
+ rfkill_unregister(wwan_rfkill);
+ rfkill_destroy(wwan_rfkill);
+ }
+}
+
static int dell_send_intensity(struct backlight_device *bd)
{
struct calling_interface_buffer buffer;
@@ -326,11 +354,23 @@ static int __init dell_init(void)
return -ENODEV;
}
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc("dell-laptop", -1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device2;
+
ret = dell_setup_rfkill();
if (ret) {
printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
- goto out;
+ goto fail_rfkill;
}
#ifdef CONFIG_ACPI
@@ -352,13 +392,13 @@ static int __init dell_init(void)
if (max_intensity) {
dell_backlight_device = backlight_device_register(
"dell_backlight",
- NULL, NULL,
+ &platform_device->dev, NULL,
&dell_ops);
if (IS_ERR(dell_backlight_device)) {
ret = PTR_ERR(dell_backlight_device);
dell_backlight_device = NULL;
- goto out;
+ goto fail_backlight;
}
dell_backlight_device->props.max_brightness = max_intensity;
@@ -368,13 +408,16 @@ static int __init dell_init(void)
}
return 0;
-out:
- if (wifi_rfkill)
- rfkill_unregister(wifi_rfkill);
- if (bluetooth_rfkill)
- rfkill_unregister(bluetooth_rfkill);
- if (wwan_rfkill)
- rfkill_unregister(wwan_rfkill);
+
+fail_backlight:
+ dell_cleanup_rfkill();
+fail_rfkill:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
kfree(da_tokens);
return ret;
}
@@ -382,12 +425,7 @@ out:
static void __exit dell_exit(void)
{
backlight_device_unregister(dell_backlight_device);
- if (wifi_rfkill)
- rfkill_unregister(wifi_rfkill);
- if (bluetooth_rfkill)
- rfkill_unregister(bluetooth_rfkill);
- if (wwan_rfkill)
- rfkill_unregister(wwan_rfkill);
+ dell_cleanup_rfkill();
}
module_init(dell_init);
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 0f900cc9fa7a..67f3fe71c509 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -31,6 +31,7 @@
#include <acpi/acpi_drivers.h>
#include <linux/acpi.h>
#include <linux/string.h>
+#include <linux/dmi.h>
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -38,6 +39,8 @@ MODULE_LICENSE("GPL");
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
+static int acpi_video;
+
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
struct key_entry {
@@ -54,7 +57,7 @@ enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
* via the keyboard controller so should not be sent again.
*/
-static struct key_entry dell_wmi_keymap[] = {
+static struct key_entry dell_legacy_wmi_keymap[] = {
{KE_KEY, 0xe045, KEY_PROG1},
{KE_KEY, 0xe009, KEY_EJECTCD},
@@ -72,7 +75,7 @@ static struct key_entry dell_wmi_keymap[] = {
/* The next device is at offset 6, the active devices are at
offset 8 and the attached devices at offset 10 */
- {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
+ {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE},
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
@@ -96,6 +99,47 @@ static struct key_entry dell_wmi_keymap[] = {
{KE_END, 0}
};
+static bool dell_new_hk_type;
+
+struct dell_new_keymap_entry {
+ u16 scancode;
+ u16 keycode;
+};
+
+struct dell_hotkey_table {
+ struct dmi_header header;
+ struct dell_new_keymap_entry keymap[];
+
+};
+
+static struct key_entry *dell_new_wmi_keymap;
+
+static u16 bios_to_linux_keycode[256] = {
+
+ KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG,
+ KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+ KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE,
+ KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD,
+ KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN,
+ KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE,
+ KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN,
+ KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2,
+ KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_PROG3
+};
+
+
+static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap;
+
static struct input_dev *dell_wmi_input_dev;
static struct key_entry *dell_wmi_get_entry_by_scancode(int code)
@@ -164,24 +208,78 @@ static void dell_wmi_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer;
if (obj && obj->type == ACPI_TYPE_BUFFER) {
- int *buffer = (int *)obj->buffer.pointer;
- /*
- * The upper bytes of the event may contain
- * additional information, so mask them off for the
- * scancode lookup
- */
- key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF);
- if (key) {
+ int reported_key;
+ u16 *buffer_entry = (u16 *)obj->buffer.pointer;
+ if (dell_new_hk_type && (buffer_entry[1] != 0x10)) {
+ printk(KERN_INFO "dell-wmi: Received unknown WMI event"
+ " (0x%x)\n", buffer_entry[1]);
+ return;
+ }
+
+ if (dell_new_hk_type)
+ reported_key = (int)buffer_entry[2];
+ else
+ reported_key = (int)buffer_entry[1] & 0xffff;
+
+ key = dell_wmi_get_entry_by_scancode(reported_key);
+
+ if (!key) {
+ printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
+ reported_key);
+ } else if ((key->keycode == KEY_BRIGHTNESSUP ||
+ key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) {
+ /* Don't report brightness notifications that will also
+ * come via ACPI */
+ return;
+ } else {
input_report_key(dell_wmi_input_dev, key->keycode, 1);
input_sync(dell_wmi_input_dev);
input_report_key(dell_wmi_input_dev, key->keycode, 0);
input_sync(dell_wmi_input_dev);
- } else if (buffer[1] & 0xFFFF)
- printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
- buffer[1] & 0xFFFF);
+ }
}
}
+
+static void setup_new_hk_map(const struct dmi_header *dm)
+{
+
+ int i;
+ int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry);
+ struct dell_hotkey_table *table =
+ container_of(dm, struct dell_hotkey_table, header);
+
+ dell_new_wmi_keymap = kzalloc((hotkey_num+1) *
+ sizeof(struct key_entry), GFP_KERNEL);
+
+ for (i = 0; i < hotkey_num; i++) {
+ dell_new_wmi_keymap[i].type = KE_KEY;
+ dell_new_wmi_keymap[i].code = table->keymap[i].scancode;
+ dell_new_wmi_keymap[i].keycode =
+ (table->keymap[i].keycode > 255) ? 0 :
+ bios_to_linux_keycode[table->keymap[i].keycode];
+ }
+
+ dell_new_wmi_keymap[i].type = KE_END;
+ dell_new_wmi_keymap[i].code = 0;
+ dell_new_wmi_keymap[i].keycode = 0;
+
+ dell_wmi_keymap = dell_new_wmi_keymap;
+
+}
+
+
+static void find_hk_type(const struct dmi_header *dm, void *dummy)
+{
+
+ if ((dm->type == 0xb2) && (dm->length > 6)) {
+ dell_new_hk_type = true;
+ setup_new_hk_map(dm);
+ }
+
+}
+
+
static int __init dell_wmi_input_setup(void)
{
struct key_entry *key;
@@ -226,6 +324,9 @@ static int __init dell_wmi_init(void)
int err;
if (wmi_has_guid(DELL_EVENT_GUID)) {
+
+ dmi_walk(find_hk_type, NULL);
+
err = dell_wmi_input_setup();
if (err)
@@ -240,6 +341,8 @@ static int __init dell_wmi_init(void)
return err;
}
+ acpi_video = acpi_video_backlight_support();
+
} else
printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index e647a856b9bf..5838c69b2fb3 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/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:
* ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar
@@ -34,20 +34,23 @@
#include <linux/rfkill.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/leds.h>
#define EEEPC_LAPTOP_VERSION "0.1"
+#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
+#define EEEPC_LAPTOP_FILE "eeepc"
-#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver"
-#define EEEPC_HOTK_FILE "eeepc"
-#define EEEPC_HOTK_CLASS "hotkey"
-#define EEEPC_HOTK_DEVICE_NAME "Hotkey"
-#define EEEPC_HOTK_HID "ASUS010"
+#define EEEPC_ACPI_CLASS "hotkey"
+#define EEEPC_ACPI_DEVICE_NAME "Hotkey"
+#define EEEPC_ACPI_HID "ASUS010"
+MODULE_AUTHOR("Corentin Chary, Eric Cooper");
+MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME);
+MODULE_LICENSE("GPL");
/*
* Definitions for Asus EeePC
*/
-#define NOTIFY_WLAN_ON 0x10
#define NOTIFY_BRN_MIN 0x20
#define NOTIFY_BRN_MAX 0x2f
@@ -117,58 +120,6 @@ static const char *cm_setv[] = {
NULL, NULL, "PBPS", "TPDS"
};
-#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
-
-#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */
-#define EEEPC_EC_SC02 0x63
-#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */
-#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */
-#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */
-#define EEEPC_EC_SFB3 0xD3
-
-/*
- * This is the main structure, we can use it to store useful information
- * about the hotk device
- */
-struct eeepc_hotk {
- struct acpi_device *device; /* the device we are in */
- acpi_handle handle; /* the handle of the hotk device */
- u32 cm_supported; /* the control methods supported
- by this BIOS */
- uint init_flag; /* Init flags */
- u16 event_count[128]; /* count for each event */
- struct input_dev *inputdev;
- u16 *keycode_map;
- struct rfkill *wlan_rfkill;
- struct rfkill *bluetooth_rfkill;
- struct rfkill *wwan3g_rfkill;
- struct rfkill *wimax_rfkill;
- struct hotplug_slot *hotplug_slot;
- struct mutex hotplug_lock;
-};
-
-/* The actual device the driver binds to */
-static struct eeepc_hotk *ehotk;
-
-/* Platform device/driver */
-static int eeepc_hotk_thaw(struct device *device);
-static int eeepc_hotk_restore(struct device *device);
-
-static const struct dev_pm_ops eeepc_pm_ops = {
- .thaw = eeepc_hotk_thaw,
- .restore = eeepc_hotk_restore,
-};
-
-static struct platform_driver platform_driver = {
- .driver = {
- .name = EEEPC_HOTK_FILE,
- .owner = THIS_MODULE,
- .pm = &eeepc_pm_ops,
- }
-};
-
-static struct platform_device *platform_device;
-
struct key_entry {
char type;
u8 code;
@@ -177,7 +128,7 @@ struct key_entry {
enum { KE_KEY, KE_END };
-static struct key_entry eeepc_keymap[] = {
+static const struct key_entry eeepc_keymap[] = {
/* Sleep already handled via generic ACPI code */
{KE_KEY, 0x10, KEY_WLAN },
{KE_KEY, 0x11, KEY_WLAN },
@@ -185,77 +136,56 @@ static struct key_entry eeepc_keymap[] = {
{KE_KEY, 0x13, KEY_MUTE },
{KE_KEY, 0x14, KEY_VOLUMEDOWN },
{KE_KEY, 0x15, KEY_VOLUMEUP },
+ {KE_KEY, 0x16, KEY_DISPLAY_OFF },
{KE_KEY, 0x1a, KEY_COFFEE },
{KE_KEY, 0x1b, KEY_ZOOM },
{KE_KEY, 0x1c, KEY_PROG2 },
{KE_KEY, 0x1d, KEY_PROG3 },
- {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
- {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP },
+ {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN },
+ {KE_KEY, NOTIFY_BRN_MAX, KEY_BRIGHTNESSUP },
{KE_KEY, 0x30, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x31, KEY_SWITCHVIDEOMODE },
{KE_KEY, 0x32, KEY_SWITCHVIDEOMODE },
+ {KE_KEY, 0x37, KEY_F13 }, /* Disable Touchpad */
+ {KE_KEY, 0x38, KEY_F14 },
{KE_END, 0},
};
+
/*
- * The hotkey driver declaration
+ * This is the main structure, we can use it to store useful information
*/
-static int eeepc_hotk_add(struct acpi_device *device);
-static int eeepc_hotk_remove(struct acpi_device *device, int type);
-static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
-
-static const struct acpi_device_id eeepc_device_ids[] = {
- {EEEPC_HOTK_HID, 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
-
-static struct acpi_driver eeepc_hotk_driver = {
- .name = EEEPC_HOTK_NAME,
- .class = EEEPC_HOTK_CLASS,
- .ids = eeepc_device_ids,
- .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
- .ops = {
- .add = eeepc_hotk_add,
- .remove = eeepc_hotk_remove,
- .notify = eeepc_hotk_notify,
- },
-};
+struct eeepc_laptop {
+ acpi_handle handle; /* the handle of the acpi device */
+ u32 cm_supported; /* the control methods supported
+ by this BIOS */
+ u16 event_count[128]; /* count for each event */
-/* PCI hotplug ops */
-static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
+ struct platform_device *platform_device;
+ struct device *hwmon_device;
+ struct backlight_device *backlight_device;
-static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
- .owner = THIS_MODULE,
- .get_adapter_status = eeepc_get_adapter_status,
- .get_power_status = eeepc_get_adapter_status,
-};
+ struct input_dev *inputdev;
+ struct key_entry *keymap;
-/* The backlight device /sys/class/backlight */
-static struct backlight_device *eeepc_backlight_device;
+ struct rfkill *wlan_rfkill;
+ struct rfkill *bluetooth_rfkill;
+ struct rfkill *wwan3g_rfkill;
+ struct rfkill *wimax_rfkill;
-/* The hwmon device */
-static struct device *eeepc_hwmon_device;
+ struct hotplug_slot *hotplug_slot;
+ struct mutex hotplug_lock;
-/*
- * The backlight class declaration
- */
-static int read_brightness(struct backlight_device *bd);
-static int update_bl_status(struct backlight_device *bd);
-static struct backlight_ops eeepcbl_ops = {
- .get_brightness = read_brightness,
- .update_status = update_bl_status,
+ struct led_classdev tpd_led;
+ int tpd_led_wk;
+ struct workqueue_struct *led_workqueue;
+ struct work_struct tpd_led_work;
};
-MODULE_AUTHOR("Corentin Chary, Eric Cooper");
-MODULE_DESCRIPTION(EEEPC_HOTK_NAME);
-MODULE_LICENSE("GPL");
-
/*
* ACPI Helpers
*/
-static int write_acpi_int(acpi_handle handle, const char *method, int val,
- struct acpi_buffer *output)
+static int write_acpi_int(acpi_handle handle, const char *method, int val)
{
struct acpi_object_list params;
union acpi_object in_obj;
@@ -266,7 +196,7 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val,
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = val;
- status = acpi_evaluate_object(handle, (char *)method, &params, output);
+ status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
return (status == AE_OK ? 0 : -1);
}
@@ -285,81 +215,56 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val)
}
}
-static int set_acpi(int cm, int value)
+static int set_acpi(struct eeepc_laptop *eeepc, 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;
-}
+ const char *method = cm_setv[cm];
-static int get_acpi(int cm)
-{
- 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;
-}
-
-/*
- * Backlight
- */
-static int read_brightness(struct backlight_device *bd)
-{
- return get_acpi(CM_ASL_PANELBRIGHT);
-}
+ if (method == NULL)
+ return -ENODEV;
+ if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+ return -ENODEV;
-static int set_brightness(struct backlight_device *bd, int value)
-{
- value = max(0, min(15, value));
- return set_acpi(CM_ASL_PANELBRIGHT, value);
+ if (write_acpi_int(eeepc->handle, method, value))
+ pr_warning("Error writing %s\n", method);
+ return 0;
}
-static int update_bl_status(struct backlight_device *bd)
+static int get_acpi(struct eeepc_laptop *eeepc, int cm)
{
- return set_brightness(bd, bd->props.brightness);
-}
+ const char *method = cm_getv[cm];
+ int value;
-/*
- * Rfkill helpers
- */
+ if (method == NULL)
+ return -ENODEV;
+ if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+ return -ENODEV;
-static bool eeepc_wlan_rfkill_blocked(void)
-{
- if (get_acpi(CM_ASL_WLAN) == 1)
- return false;
- return true;
+ if (read_acpi_int(eeepc->handle, method, &value))
+ pr_warning("Error reading %s\n", method);
+ return value;
}
-static int eeepc_rfkill_set(void *data, bool blocked)
+static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm,
+ acpi_handle *handle)
{
- unsigned long asl = (unsigned long)data;
- return set_acpi(asl, !blocked);
-}
+ const char *method = cm_setv[cm];
+ acpi_status status;
-static const struct rfkill_ops eeepc_rfkill_ops = {
- .set_block = eeepc_rfkill_set,
-};
+ if (method == NULL)
+ return -ENODEV;
+ if ((eeepc->cm_supported & (0x1 << cm)) == 0)
+ return -ENODEV;
-static void __devinit eeepc_enable_camera(void)
-{
- /*
- * If the following call to set_acpi() fails, it's because there's no
- * camera so we can ignore the error.
- */
- if (get_acpi(CM_ASL_CAMERA) == 0)
- set_acpi(CM_ASL_CAMERA, 1);
+ status = acpi_get_handle(eeepc->handle, (char *)method,
+ handle);
+ if (status != AE_OK) {
+ pr_warning("Error finding %s\n", method);
+ return -ENODEV;
+ }
+ return 0;
}
+
/*
* Sys helpers
*/
@@ -372,60 +277,63 @@ static int parse_arg(const char *buf, unsigned long count, int *val)
return count;
}
-static ssize_t store_sys_acpi(int cm, const char *buf, size_t count)
+static ssize_t store_sys_acpi(struct device *dev, int cm,
+ const char *buf, size_t count)
{
+ struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
int rv, value;
rv = parse_arg(buf, count, &value);
if (rv > 0)
- value = set_acpi(cm, value);
+ value = set_acpi(eeepc, cm, value);
if (value < 0)
- return value;
+ return -EIO;
return rv;
}
-static ssize_t show_sys_acpi(int cm, char *buf)
+static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf)
{
- int value = get_acpi(cm);
+ struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
+ int value = get_acpi(eeepc, cm);
if (value < 0)
- return value;
+ return -EIO;
return sprintf(buf, "%d\n", value);
}
-#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \
+#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
static ssize_t show_##_name(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
- return show_sys_acpi(_cm, buf); \
+ return show_sys_acpi(dev, _cm, buf); \
} \
static ssize_t store_##_name(struct device *dev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
- return store_sys_acpi(_cm, buf, count); \
+ return store_sys_acpi(dev, _cm, buf, count); \
} \
static struct device_attribute dev_attr_##_name = { \
.attr = { \
.name = __stringify(_name), \
- .mode = 0644 }, \
+ .mode = _mode }, \
.show = show_##_name, \
.store = store_##_name, \
}
-EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
-EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
-EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
+EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA);
+EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER);
+EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH);
struct eeepc_cpufv {
int num;
int cur;
};
-static int get_cpufv(struct eeepc_cpufv *c)
+static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c)
{
- c->cur = get_acpi(CM_ASL_CPUFV);
+ c->cur = get_acpi(eeepc, CM_ASL_CPUFV);
c->num = (c->cur >> 8) & 0xff;
c->cur &= 0xff;
if (c->cur < 0 || c->num <= 0 || c->num > 12)
@@ -437,11 +345,12 @@ static ssize_t show_available_cpufv(struct device *dev,
struct device_attribute *attr,
char *buf)
{
+ struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
struct eeepc_cpufv c;
int i;
ssize_t len = 0;
- if (get_cpufv(&c))
+ if (get_cpufv(eeepc, &c))
return -ENODEV;
for (i = 0; i < c.num; i++)
len += sprintf(buf + len, "%d ", i);
@@ -453,9 +362,10 @@ static ssize_t show_cpufv(struct device *dev,
struct device_attribute *attr,
char *buf)
{
+ struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
struct eeepc_cpufv c;
- if (get_cpufv(&c))
+ if (get_cpufv(eeepc, &c))
return -ENODEV;
return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
}
@@ -464,17 +374,18 @@ static ssize_t store_cpufv(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
+ struct eeepc_laptop *eeepc = dev_get_drvdata(dev);
struct eeepc_cpufv c;
int rv, value;
- if (get_cpufv(&c))
+ if (get_cpufv(eeepc, &c))
return -ENODEV;
rv = parse_arg(buf, count, &value);
if (rv < 0)
return rv;
if (!rv || value < 0 || value >= c.num)
return -EINVAL;
- set_acpi(CM_ASL_CPUFV, value);
+ set_acpi(eeepc, CM_ASL_CPUFV, value);
return rv;
}
@@ -506,156 +417,125 @@ static struct attribute_group platform_attribute_group = {
.attrs = platform_attributes
};
-/*
- * Hotkey functions
- */
-static struct key_entry *eepc_get_entry_by_scancode(int code)
+static int eeepc_platform_init(struct eeepc_laptop *eeepc)
{
- struct key_entry *key;
-
- for (key = eeepc_keymap; key->type != KE_END; key++)
- if (code == key->code)
- return key;
+ int result;
- return NULL;
-}
+ eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1);
+ if (!eeepc->platform_device)
+ return -ENOMEM;
+ platform_set_drvdata(eeepc->platform_device, eeepc);
-static struct key_entry *eepc_get_entry_by_keycode(int code)
-{
- struct key_entry *key;
+ result = platform_device_add(eeepc->platform_device);
+ if (result)
+ goto fail_platform_device;
- for (key = eeepc_keymap; key->type != KE_END; key++)
- if (code == key->keycode && key->type == KE_KEY)
- return key;
+ result = sysfs_create_group(&eeepc->platform_device->dev.kobj,
+ &platform_attribute_group);
+ if (result)
+ goto fail_sysfs;
+ return 0;
- return NULL;
+fail_sysfs:
+ platform_device_del(eeepc->platform_device);
+fail_platform_device:
+ platform_device_put(eeepc->platform_device);
+ return result;
}
-static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
+static void eeepc_platform_exit(struct eeepc_laptop *eeepc)
{
- struct key_entry *key = eepc_get_entry_by_scancode(scancode);
+ sysfs_remove_group(&eeepc->platform_device->dev.kobj,
+ &platform_attribute_group);
+ platform_device_unregister(eeepc->platform_device);
+}
- if (key && key->type == KE_KEY) {
- *keycode = key->keycode;
- return 0;
- }
+/*
+ * LEDs
+ */
+/*
+ * These functions actually update the LED's, and are called from a
+ * workqueue. By doing this as separate work rather than when the LED
+ * subsystem asks, we avoid messing with the Asus ACPI stuff during a
+ * potentially bad time, such as a timer interrupt.
+ */
+static void tpd_led_update(struct work_struct *work)
+ {
+ struct eeepc_laptop *eeepc;
- return -EINVAL;
+ eeepc = container_of(work, struct eeepc_laptop, tpd_led_work);
+
+ set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk);
}
-static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
+static void tpd_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
- struct key_entry *key;
- int old_keycode;
+ struct eeepc_laptop *eeepc;
- if (keycode < 0 || keycode > KEY_MAX)
- return -EINVAL;
+ eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led);
- key = eepc_get_entry_by_scancode(scancode);
- if (key && key->type == KE_KEY) {
- old_keycode = key->keycode;
- key->keycode = keycode;
- set_bit(keycode, dev->keybit);
- if (!eepc_get_entry_by_keycode(old_keycode))
- clear_bit(old_keycode, dev->keybit);
- return 0;
- }
-
- return -EINVAL;
+ eeepc->tpd_led_wk = (value > 0) ? 1 : 0;
+ queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
}
-static void cmsg_quirk(int cm, const char *name)
+static int eeepc_led_init(struct eeepc_laptop *eeepc)
{
- int dummy;
+ int rv;
- /* Some BIOSes do not report cm although it is avaliable.
- Check if cm_getv[cm] works and, if yes, assume cm should be set. */
- if (!(ehotk->cm_supported & (1 << cm))
- && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) {
- pr_info("%s (%x) not reported by BIOS,"
- " enabling anyway\n", name, 1 << cm);
- ehotk->cm_supported |= 1 << cm;
- }
-}
+ if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV)
+ return 0;
-static void cmsg_quirks(void)
-{
- cmsg_quirk(CM_ASL_LID, "LID");
- cmsg_quirk(CM_ASL_TYPE, "TYPE");
- cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER");
- cmsg_quirk(CM_ASL_TPD, "TPD");
-}
+ eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
+ if (!eeepc->led_workqueue)
+ return -ENOMEM;
+ INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
-static int eeepc_hotk_check(void)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- int result;
+ eeepc->tpd_led.name = "eeepc::touchpad";
+ eeepc->tpd_led.brightness_set = tpd_led_set;
+ eeepc->tpd_led.max_brightness = 1;
- result = acpi_bus_get_status(ehotk->device);
- if (result)
- return result;
- if (ehotk->device->status.present) {
- if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag,
- &buffer)) {
- pr_err("Hotkey initialization failed\n");
- return -ENODEV;
- } else {
- pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag);
- }
- /* get control methods supported */
- if (read_acpi_int(ehotk->handle, "CMSG"
- , &ehotk->cm_supported)) {
- pr_err("Get control methods supported failed\n");
- return -ENODEV;
- } else {
- cmsg_quirks();
- pr_info("Get control methods supported: 0x%x\n",
- ehotk->cm_supported);
- }
- } else {
- pr_err("Hotkey device not present, aborting\n");
- return -EINVAL;
+ rv = led_classdev_register(&eeepc->platform_device->dev,
+ &eeepc->tpd_led);
+ if (rv) {
+ destroy_workqueue(eeepc->led_workqueue);
+ return rv;
}
+
return 0;
}
-static int notify_brn(void)
+static void eeepc_led_exit(struct eeepc_laptop *eeepc)
{
- /* returns the *previous* brightness, or -1 */
- struct backlight_device *bd = eeepc_backlight_device;
- if (bd) {
- int old = bd->props.brightness;
- backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
- return old;
- }
- return -1;
+ if (eeepc->tpd_led.dev)
+ led_classdev_unregister(&eeepc->tpd_led);
+ if (eeepc->led_workqueue)
+ destroy_workqueue(eeepc->led_workqueue);
}
-static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
- u8 *value)
-{
- int val = get_acpi(CM_ASL_WLAN);
- if (val == 1 || val == 0)
- *value = val;
- else
- return -EINVAL;
-
- return 0;
+/*
+ * PCI hotplug (for wlan rfkill)
+ */
+static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc)
+{
+ if (get_acpi(eeepc, CM_ASL_WLAN) == 1)
+ return false;
+ return true;
}
-static void eeepc_rfkill_hotplug(void)
+static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc)
{
struct pci_dev *dev;
struct pci_bus *bus;
- bool blocked = eeepc_wlan_rfkill_blocked();
+ bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
- if (ehotk->wlan_rfkill)
- rfkill_set_sw_state(ehotk->wlan_rfkill, blocked);
+ if (eeepc->wlan_rfkill)
+ rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
- mutex_lock(&ehotk->hotplug_lock);
+ mutex_lock(&eeepc->hotplug_lock);
- if (ehotk->hotplug_slot) {
+ if (eeepc->hotplug_slot) {
bus = pci_find_bus(0, 1);
if (!bus) {
pr_warning("Unable to find PCI bus 1?\n");
@@ -685,69 +565,23 @@ static void eeepc_rfkill_hotplug(void)
}
out_unlock:
- mutex_unlock(&ehotk->hotplug_lock);
+ mutex_unlock(&eeepc->hotplug_lock);
}
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
+ struct eeepc_laptop *eeepc = data;
+
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
- eeepc_rfkill_hotplug();
+ eeepc_rfkill_hotplug(eeepc);
}
-static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
+static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc,
+ char *node)
{
- 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;
- }
- }
- }
-}
-
-static int eeepc_register_rfkill_notifier(char *node)
-{
- acpi_status status = AE_OK;
+ acpi_status status;
acpi_handle handle;
status = acpi_get_handle(NULL, node, &handle);
@@ -756,7 +590,7 @@ static int eeepc_register_rfkill_notifier(char *node)
status = acpi_install_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
eeepc_rfkill_notify,
- NULL);
+ eeepc);
if (ACPI_FAILURE(status))
pr_warning("Failed to register notify on %s\n", node);
} else
@@ -765,7 +599,8 @@ static int eeepc_register_rfkill_notifier(char *node)
return 0;
}
-static void eeepc_unregister_rfkill_notifier(char *node)
+static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc,
+ char *node)
{
acpi_status status = AE_OK;
acpi_handle handle;
@@ -782,13 +617,33 @@ static void eeepc_unregister_rfkill_notifier(char *node)
}
}
+static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
+ u8 *value)
+{
+ struct eeepc_laptop *eeepc = hotplug_slot->private;
+ int val = get_acpi(eeepc, CM_ASL_WLAN);
+
+ if (val == 1 || val == 0)
+ *value = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
{
kfree(hotplug_slot->info);
kfree(hotplug_slot);
}
-static int eeepc_setup_pci_hotplug(void)
+static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .get_adapter_status = eeepc_get_adapter_status,
+ .get_power_status = eeepc_get_adapter_status,
+};
+
+static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc)
{
int ret = -ENOMEM;
struct pci_bus *bus = pci_find_bus(0, 1);
@@ -798,22 +653,22 @@ static int eeepc_setup_pci_hotplug(void)
return -ENODEV;
}
- ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
- if (!ehotk->hotplug_slot)
+ eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!eeepc->hotplug_slot)
goto error_slot;
- ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+ eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
GFP_KERNEL);
- if (!ehotk->hotplug_slot->info)
+ if (!eeepc->hotplug_slot->info)
goto error_info;
- ehotk->hotplug_slot->private = ehotk;
- ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
- ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
- eeepc_get_adapter_status(ehotk->hotplug_slot,
- &ehotk->hotplug_slot->info->adapter_status);
+ eeepc->hotplug_slot->private = eeepc;
+ eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
+ eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
+ eeepc_get_adapter_status(eeepc->hotplug_slot,
+ &eeepc->hotplug_slot->info->adapter_status);
- ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
+ ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
if (ret) {
pr_err("Unable to register hotplug slot - %d\n", ret);
goto error_register;
@@ -822,17 +677,156 @@ static int eeepc_setup_pci_hotplug(void)
return 0;
error_register:
- kfree(ehotk->hotplug_slot->info);
+ kfree(eeepc->hotplug_slot->info);
error_info:
- kfree(ehotk->hotplug_slot);
- ehotk->hotplug_slot = NULL;
+ kfree(eeepc->hotplug_slot);
+ eeepc->hotplug_slot = NULL;
error_slot:
return ret;
}
+/*
+ * Rfkill devices
+ */
+static int eeepc_rfkill_set(void *data, bool blocked)
+{
+ acpi_handle handle = data;
+
+ return write_acpi_int(handle, NULL, !blocked);
+}
+
+static const struct rfkill_ops eeepc_rfkill_ops = {
+ .set_block = eeepc_rfkill_set,
+};
+
+static int eeepc_new_rfkill(struct eeepc_laptop *eeepc,
+ struct rfkill **rfkill,
+ const char *name,
+ enum rfkill_type type, int cm)
+{
+ acpi_handle handle;
+ int result;
+
+ result = acpi_setter_handle(eeepc, cm, &handle);
+ if (result < 0)
+ return result;
+
+ *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
+ &eeepc_rfkill_ops, handle);
+
+ if (!*rfkill)
+ return -EINVAL;
+
+ rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1);
+ result = rfkill_register(*rfkill);
+ if (result) {
+ rfkill_destroy(*rfkill);
+ *rfkill = NULL;
+ return result;
+ }
+ return 0;
+}
+
+static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc)
+{
+ eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
+ eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
+ eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+ if (eeepc->wlan_rfkill) {
+ rfkill_unregister(eeepc->wlan_rfkill);
+ rfkill_destroy(eeepc->wlan_rfkill);
+ eeepc->wlan_rfkill = NULL;
+ }
+ /*
+ * Refresh pci hotplug in case the rfkill state was changed after
+ * eeepc_unregister_rfkill_notifier()
+ */
+ eeepc_rfkill_hotplug(eeepc);
+ if (eeepc->hotplug_slot)
+ pci_hp_deregister(eeepc->hotplug_slot);
+
+ if (eeepc->bluetooth_rfkill) {
+ rfkill_unregister(eeepc->bluetooth_rfkill);
+ rfkill_destroy(eeepc->bluetooth_rfkill);
+ eeepc->bluetooth_rfkill = NULL;
+ }
+ if (eeepc->wwan3g_rfkill) {
+ rfkill_unregister(eeepc->wwan3g_rfkill);
+ rfkill_destroy(eeepc->wwan3g_rfkill);
+ eeepc->wwan3g_rfkill = NULL;
+ }
+ if (eeepc->wimax_rfkill) {
+ rfkill_unregister(eeepc->wimax_rfkill);
+ rfkill_destroy(eeepc->wimax_rfkill);
+ eeepc->wimax_rfkill = NULL;
+ }
+}
+
+static int eeepc_rfkill_init(struct eeepc_laptop *eeepc)
+{
+ int result = 0;
+
+ mutex_init(&eeepc->hotplug_lock);
+
+ result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
+ "eeepc-wlan", RFKILL_TYPE_WLAN,
+ CM_ASL_WLAN);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
+ result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
+ "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
+ CM_ASL_BLUETOOTH);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
+ result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
+ "eeepc-wwan3g", RFKILL_TYPE_WWAN,
+ CM_ASL_3G);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
+ result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
+ "eeepc-wimax", RFKILL_TYPE_WIMAX,
+ CM_ASL_WIMAX);
+
+ if (result && result != -ENODEV)
+ goto exit;
+
+ result = eeepc_setup_pci_hotplug(eeepc);
+ /*
+ * If we get -EBUSY then something else is handling the PCI hotplug -
+ * don't fail in this case
+ */
+ if (result == -EBUSY)
+ result = 0;
+
+ eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
+ eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
+ eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
+ /*
+ * Refresh pci hotplug in case the rfkill state was changed during
+ * setup.
+ */
+ eeepc_rfkill_hotplug(eeepc);
+
+exit:
+ if (result && result != -ENODEV)
+ eeepc_rfkill_exit(eeepc);
+ return result;
+}
+
+/*
+ * Platform driver - hibernate/resume callbacks
+ */
static int eeepc_hotk_thaw(struct device *device)
{
- if (ehotk->wlan_rfkill) {
+ struct eeepc_laptop *eeepc = dev_get_drvdata(device);
+
+ if (eeepc->wlan_rfkill) {
bool wlan;
/*
@@ -840,8 +834,8 @@ static int eeepc_hotk_thaw(struct device *device)
* 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);
+ wlan = get_acpi(eeepc, CM_ASL_WLAN);
+ set_acpi(eeepc, CM_ASL_WLAN, wlan);
}
return 0;
@@ -849,70 +843,96 @@ static int eeepc_hotk_thaw(struct device *device)
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 (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);
+ 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
+ * Hwmon device
*/
+
+#define EEEPC_EC_SC00 0x61
+#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */
+#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */
+#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */
+
+#define EEEPC_EC_SFB0 0xD0
+#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */
+
static int eeepc_get_fan_pwm(void)
{
- int value = 0;
+ u8 value = 0;
- read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value);
- value = value * 255 / 100;
- return (value);
+ ec_read(EEEPC_EC_FAN_PWM, &value);
+ return value * 255 / 100;
}
static void eeepc_set_fan_pwm(int value)
{
value = SENSORS_LIMIT(value, 0, 255);
value = value * 100 / 255;
- ec_write(EEEPC_EC_SC02, value);
+ ec_write(EEEPC_EC_FAN_PWM, value);
}
static int eeepc_get_fan_rpm(void)
{
- int high = 0;
- int low = 0;
+ u8 high = 0;
+ u8 low = 0;
- read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high);
- read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low);
- return (high << 8 | low);
+ ec_read(EEEPC_EC_FAN_HRPM, &high);
+ ec_read(EEEPC_EC_FAN_LRPM, &low);
+ return high << 8 | low;
}
static int eeepc_get_fan_ctrl(void)
{
- int value = 0;
+ u8 value = 0;
- read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
- return ((value & 0x02 ? 1 : 0));
+ ec_read(EEEPC_EC_FAN_CTRL, &value);
+ if (value & 0x02)
+ return 1; /* manual */
+ else
+ return 2; /* automatic */
}
static void eeepc_set_fan_ctrl(int manual)
{
- int value = 0;
+ u8 value = 0;
- read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value);
- if (manual)
+ ec_read(EEEPC_EC_FAN_CTRL, &value);
+ if (manual == 1)
value |= 0x02;
else
value &= ~0x02;
- ec_write(EEEPC_EC_SFB3, value);
+ ec_write(EEEPC_EC_FAN_CTRL, value);
}
static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count)
@@ -970,348 +990,485 @@ static struct attribute_group hwmon_attribute_group = {
.attrs = hwmon_attributes
};
-/*
- * exit/init
- */
-static void eeepc_backlight_exit(void)
+static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
{
- if (eeepc_backlight_device)
- backlight_device_unregister(eeepc_backlight_device);
- eeepc_backlight_device = NULL;
+ struct device *hwmon;
+
+ hwmon = eeepc->hwmon_device;
+ if (!hwmon)
+ return;
+ sysfs_remove_group(&hwmon->kobj,
+ &hwmon_attribute_group);
+ hwmon_device_unregister(hwmon);
+ eeepc->hwmon_device = NULL;
}
-static void eeepc_rfkill_exit(void)
+static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
{
- eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5");
- eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
- eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
- if (ehotk->wlan_rfkill) {
- rfkill_unregister(ehotk->wlan_rfkill);
- rfkill_destroy(ehotk->wlan_rfkill);
- ehotk->wlan_rfkill = NULL;
- }
- /*
- * Refresh pci hotplug in case the rfkill state was changed after
- * eeepc_unregister_rfkill_notifier()
- */
- eeepc_rfkill_hotplug();
- if (ehotk->hotplug_slot)
- pci_hp_deregister(ehotk->hotplug_slot);
-
- if (ehotk->bluetooth_rfkill) {
- rfkill_unregister(ehotk->bluetooth_rfkill);
- rfkill_destroy(ehotk->bluetooth_rfkill);
- ehotk->bluetooth_rfkill = NULL;
- }
- if (ehotk->wwan3g_rfkill) {
- rfkill_unregister(ehotk->wwan3g_rfkill);
- rfkill_destroy(ehotk->wwan3g_rfkill);
- ehotk->wwan3g_rfkill = NULL;
- }
- if (ehotk->wimax_rfkill) {
- rfkill_unregister(ehotk->wimax_rfkill);
- rfkill_destroy(ehotk->wimax_rfkill);
- ehotk->wimax_rfkill = NULL;
+ struct device *hwmon;
+ int result;
+
+ hwmon = hwmon_device_register(&eeepc->platform_device->dev);
+ if (IS_ERR(hwmon)) {
+ pr_err("Could not register eeepc hwmon device\n");
+ eeepc->hwmon_device = NULL;
+ return PTR_ERR(hwmon);
}
+ eeepc->hwmon_device = hwmon;
+ result = sysfs_create_group(&hwmon->kobj,
+ &hwmon_attribute_group);
+ if (result)
+ eeepc_hwmon_exit(eeepc);
+ return result;
}
-static void eeepc_input_exit(void)
+/*
+ * Backlight device
+ */
+static int read_brightness(struct backlight_device *bd)
{
- if (ehotk->inputdev)
- input_unregister_device(ehotk->inputdev);
+ struct eeepc_laptop *eeepc = bl_get_data(bd);
+
+ return get_acpi(eeepc, CM_ASL_PANELBRIGHT);
}
-static void eeepc_hwmon_exit(void)
+static int set_brightness(struct backlight_device *bd, int value)
{
- struct device *hwmon;
+ struct eeepc_laptop *eeepc = bl_get_data(bd);
- hwmon = eeepc_hwmon_device;
- if (!hwmon)
- return ;
- sysfs_remove_group(&hwmon->kobj,
- &hwmon_attribute_group);
- hwmon_device_unregister(hwmon);
- eeepc_hwmon_device = NULL;
+ return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value);
}
-static int eeepc_new_rfkill(struct rfkill **rfkill,
- const char *name, struct device *dev,
- enum rfkill_type type, int cm)
+static int update_bl_status(struct backlight_device *bd)
{
- int result;
+ return set_brightness(bd, bd->props.brightness);
+}
- result = get_acpi(cm);
- if (result < 0)
- return result;
+static struct backlight_ops eeepcbl_ops = {
+ .get_brightness = read_brightness,
+ .update_status = update_bl_status,
+};
- *rfkill = rfkill_alloc(name, dev, type,
- &eeepc_rfkill_ops, (void *)(unsigned long)cm);
+static int eeepc_backlight_notify(struct eeepc_laptop *eeepc)
+{
+ struct backlight_device *bd = eeepc->backlight_device;
+ int old = bd->props.brightness;
- if (!*rfkill)
- return -EINVAL;
+ backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
- rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1);
- result = rfkill_register(*rfkill);
- if (result) {
- rfkill_destroy(*rfkill);
- *rfkill = NULL;
- return result;
- }
- return 0;
+ return old;
}
-
-static int eeepc_rfkill_init(struct device *dev)
+static int eeepc_backlight_init(struct eeepc_laptop *eeepc)
{
- int result = 0;
-
- mutex_init(&ehotk->hotplug_lock);
+ struct backlight_device *bd;
- result = eeepc_new_rfkill(&ehotk->wlan_rfkill,
- "eeepc-wlan", dev,
- RFKILL_TYPE_WLAN, CM_ASL_WLAN);
+ bd = backlight_device_register(EEEPC_LAPTOP_FILE,
+ &eeepc->platform_device->dev,
+ eeepc, &eeepcbl_ops);
+ if (IS_ERR(bd)) {
+ pr_err("Could not register eeepc backlight device\n");
+ eeepc->backlight_device = NULL;
+ return PTR_ERR(bd);
+ }
+ eeepc->backlight_device = bd;
+ bd->props.max_brightness = 15;
+ bd->props.brightness = read_brightness(bd);
+ bd->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(bd);
+ return 0;
+}
- if (result && result != -ENODEV)
- goto exit;
+static void eeepc_backlight_exit(struct eeepc_laptop *eeepc)
+{
+ if (eeepc->backlight_device)
+ backlight_device_unregister(eeepc->backlight_device);
+ eeepc->backlight_device = NULL;
+}
- result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill,
- "eeepc-bluetooth", dev,
- RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH);
- if (result && result != -ENODEV)
- goto exit;
+/*
+ * Input device (i.e. hotkeys)
+ */
+static struct key_entry *eeepc_get_entry_by_scancode(
+ struct eeepc_laptop *eeepc,
+ int code)
+{
+ struct key_entry *key;
- result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill,
- "eeepc-wwan3g", dev,
- RFKILL_TYPE_WWAN, CM_ASL_3G);
+ for (key = eeepc->keymap; key->type != KE_END; key++)
+ if (code == key->code)
+ return key;
- if (result && result != -ENODEV)
- goto exit;
+ return NULL;
+}
- result = eeepc_new_rfkill(&ehotk->wimax_rfkill,
- "eeepc-wimax", dev,
- RFKILL_TYPE_WIMAX, CM_ASL_WIMAX);
+static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
+{
+ static struct key_entry *key;
- if (result && result != -ENODEV)
- goto exit;
+ key = eeepc_get_entry_by_scancode(eeepc, event);
+ if (key) {
+ switch (key->type) {
+ case KE_KEY:
+ input_report_key(eeepc->inputdev, key->keycode,
+ 1);
+ input_sync(eeepc->inputdev);
+ input_report_key(eeepc->inputdev, key->keycode,
+ 0);
+ input_sync(eeepc->inputdev);
+ break;
+ }
+ }
+}
- result = eeepc_setup_pci_hotplug();
- /*
- * If we get -EBUSY then something else is handling the PCI hotplug -
- * don't fail in this case
- */
- if (result == -EBUSY)
- result = 0;
+static struct key_entry *eeepc_get_entry_by_keycode(
+ struct eeepc_laptop *eeepc, int code)
+{
+ struct key_entry *key;
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5");
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
- eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
- /*
- * Refresh pci hotplug in case the rfkill state was changed during
- * setup.
- */
- eeepc_rfkill_hotplug();
+ for (key = eeepc->keymap; key->type != KE_END; key++)
+ if (code == key->keycode && key->type == KE_KEY)
+ return key;
-exit:
- if (result && result != -ENODEV)
- eeepc_rfkill_exit();
- return result;
+ return NULL;
}
-static int eeepc_backlight_init(struct device *dev)
+static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode)
{
- struct backlight_device *bd;
+ struct eeepc_laptop *eeepc = input_get_drvdata(dev);
+ struct key_entry *key = eeepc_get_entry_by_scancode(eeepc, scancode);
- bd = backlight_device_register(EEEPC_HOTK_FILE, dev,
- NULL, &eeepcbl_ops);
- if (IS_ERR(bd)) {
- pr_err("Could not register eeepc backlight device\n");
- eeepc_backlight_device = NULL;
- return PTR_ERR(bd);
+ if (key && key->type == KE_KEY) {
+ *keycode = key->keycode;
+ return 0;
}
- eeepc_backlight_device = bd;
- bd->props.max_brightness = 15;
- bd->props.brightness = read_brightness(NULL);
- bd->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(bd);
- return 0;
+
+ return -EINVAL;
}
-static int eeepc_hwmon_init(struct device *dev)
+static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode)
{
- struct device *hwmon;
- int result;
+ struct eeepc_laptop *eeepc = input_get_drvdata(dev);
+ struct key_entry *key;
+ int old_keycode;
- hwmon = hwmon_device_register(dev);
- if (IS_ERR(hwmon)) {
- pr_err("Could not register eeepc hwmon device\n");
- eeepc_hwmon_device = NULL;
- return PTR_ERR(hwmon);
+ if (keycode < 0 || keycode > KEY_MAX)
+ return -EINVAL;
+
+ key = eeepc_get_entry_by_scancode(eeepc, scancode);
+ if (key && key->type == KE_KEY) {
+ old_keycode = key->keycode;
+ key->keycode = keycode;
+ set_bit(keycode, dev->keybit);
+ if (!eeepc_get_entry_by_keycode(eeepc, old_keycode))
+ clear_bit(old_keycode, dev->keybit);
+ return 0;
}
- eeepc_hwmon_device = hwmon;
- result = sysfs_create_group(&hwmon->kobj,
- &hwmon_attribute_group);
- if (result)
- eeepc_hwmon_exit();
- return result;
+
+ return -EINVAL;
}
-static int eeepc_input_init(struct device *dev)
+static int eeepc_input_init(struct eeepc_laptop *eeepc)
{
const struct key_entry *key;
int result;
- ehotk->inputdev = input_allocate_device();
- if (!ehotk->inputdev) {
+ eeepc->inputdev = input_allocate_device();
+ if (!eeepc->inputdev) {
pr_info("Unable to allocate input device\n");
return -ENOMEM;
}
- ehotk->inputdev->name = "Asus EeePC extra buttons";
- ehotk->inputdev->dev.parent = dev;
- ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0";
- ehotk->inputdev->id.bustype = BUS_HOST;
- ehotk->inputdev->getkeycode = eeepc_getkeycode;
- ehotk->inputdev->setkeycode = eeepc_setkeycode;
-
+ eeepc->inputdev->name = "Asus EeePC extra buttons";
+ eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
+ eeepc->inputdev->phys = EEEPC_LAPTOP_FILE "/input0";
+ eeepc->inputdev->id.bustype = BUS_HOST;
+ eeepc->inputdev->getkeycode = eeepc_getkeycode;
+ eeepc->inputdev->setkeycode = eeepc_setkeycode;
+ input_set_drvdata(eeepc->inputdev, eeepc);
+
+ eeepc->keymap = kmemdup(eeepc_keymap, sizeof(eeepc_keymap),
+ GFP_KERNEL);
for (key = eeepc_keymap; key->type != KE_END; key++) {
switch (key->type) {
case KE_KEY:
- set_bit(EV_KEY, ehotk->inputdev->evbit);
- set_bit(key->keycode, ehotk->inputdev->keybit);
+ set_bit(EV_KEY, eeepc->inputdev->evbit);
+ set_bit(key->keycode, eeepc->inputdev->keybit);
break;
}
}
- result = input_register_device(ehotk->inputdev);
+ result = input_register_device(eeepc->inputdev);
if (result) {
pr_info("Unable to register input device\n");
- input_free_device(ehotk->inputdev);
+ input_free_device(eeepc->inputdev);
return result;
}
return 0;
}
-static int __devinit eeepc_hotk_add(struct acpi_device *device)
+static void eeepc_input_exit(struct eeepc_laptop *eeepc)
{
- struct device *dev;
+ if (eeepc->inputdev) {
+ input_unregister_device(eeepc->inputdev);
+ kfree(eeepc->keymap);
+ }
+}
+
+/*
+ * ACPI driver
+ */
+static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
+{
+ struct eeepc_laptop *eeepc = acpi_driver_data(device);
+ u16 count;
+
+ if (event > ACPI_MAX_SYS_NOTIFY)
+ return;
+ count = eeepc->event_count[event % 128]++;
+ acpi_bus_generate_proc_event(device, event, count);
+ acpi_bus_generate_netlink_event(device->pnp.device_class,
+ dev_name(&device->dev), event,
+ count);
+
+ /* Brightness events are special */
+ if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) {
+
+ /* Ignore them completely if the acpi video driver is used */
+ if (eeepc->backlight_device != NULL) {
+ int old_brightness, new_brightness;
+
+ /* Update the backlight device. */
+ old_brightness = eeepc_backlight_notify(eeepc);
+
+ /* Convert event to keypress (obsolescent hack) */
+ new_brightness = event - NOTIFY_BRN_MIN;
+
+ if (new_brightness < old_brightness) {
+ event = NOTIFY_BRN_MIN; /* brightness down */
+ } else if (new_brightness > old_brightness) {
+ event = NOTIFY_BRN_MAX; /* brightness up */
+ } else {
+ /*
+ * no change in brightness - already at min/max,
+ * event will be desired value (or else ignored)
+ */
+ }
+ eeepc_input_notify(eeepc, event);
+ }
+ } else {
+ /* Everything else is a bona-fide keypress event */
+ eeepc_input_notify(eeepc, event);
+ }
+}
+
+static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
+{
+ int dummy;
+
+ /* Some BIOSes do not report cm although it is avaliable.
+ Check if cm_getv[cm] works and, if yes, assume cm should be set. */
+ if (!(eeepc->cm_supported & (1 << cm))
+ && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
+ pr_info("%s (%x) not reported by BIOS,"
+ " enabling anyway\n", name, 1 << cm);
+ eeepc->cm_supported |= 1 << cm;
+ }
+}
+
+static void cmsg_quirks(struct eeepc_laptop *eeepc)
+{
+ cmsg_quirk(eeepc, CM_ASL_LID, "LID");
+ cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE");
+ cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER");
+ cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
+}
+
+static int eeepc_acpi_init(struct eeepc_laptop *eeepc,
+ struct acpi_device *device)
+{
+ unsigned int init_flags;
int result;
- if (!device)
- return -EINVAL;
- pr_notice(EEEPC_HOTK_NAME "\n");
- ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL);
- if (!ehotk)
- return -ENOMEM;
- ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
- ehotk->handle = device->handle;
- strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME);
- strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS);
- device->driver_data = ehotk;
- ehotk->device = device;
-
- result = eeepc_hotk_check();
+ result = acpi_bus_get_status(device);
if (result)
- goto fail_platform_driver;
- eeepc_enable_camera();
+ return result;
+ if (!device->status.present) {
+ pr_err("Hotkey device not present, aborting\n");
+ return -ENODEV;
+ }
- /* Register platform stuff */
- result = platform_driver_register(&platform_driver);
- if (result)
- goto fail_platform_driver;
- platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1);
- if (!platform_device) {
- result = -ENOMEM;
- goto fail_platform_device1;
+ init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH;
+ pr_notice("Hotkey init flags 0x%x\n", init_flags);
+
+ if (write_acpi_int(eeepc->handle, "INIT", init_flags)) {
+ pr_err("Hotkey initialization failed\n");
+ return -ENODEV;
}
- result = platform_device_add(platform_device);
- if (result)
- goto fail_platform_device2;
- result = sysfs_create_group(&platform_device->dev.kobj,
- &platform_attribute_group);
+
+ /* get control methods supported */
+ if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) {
+ pr_err("Get control methods supported failed\n");
+ return -ENODEV;
+ }
+ cmsg_quirks(eeepc);
+ pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported);
+
+ return 0;
+}
+
+static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
+{
+ /*
+ * If the following call to set_acpi() fails, it's because there's no
+ * camera so we can ignore the error.
+ */
+ if (get_acpi(eeepc, CM_ASL_CAMERA) == 0)
+ set_acpi(eeepc, CM_ASL_CAMERA, 1);
+}
+
+static bool eeepc_device_present;
+
+static int __devinit eeepc_acpi_add(struct acpi_device *device)
+{
+ struct eeepc_laptop *eeepc;
+ int result;
+
+ pr_notice(EEEPC_LAPTOP_NAME "\n");
+ eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL);
+ if (!eeepc)
+ return -ENOMEM;
+ eeepc->handle = device->handle;
+ strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME);
+ strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS);
+ device->driver_data = eeepc;
+
+ result = eeepc_acpi_init(eeepc, device);
if (result)
- goto fail_sysfs;
+ goto fail_platform;
+ eeepc_enable_camera(eeepc);
- dev = &platform_device->dev;
+ /*
+ * Register the platform device first. It is used as a parent for the
+ * sub-devices below.
+ *
+ * Note that if there are multiple instances of this ACPI device it
+ * will bail out, because the platform device is registered with a
+ * fixed name. Of course it doesn't make sense to have more than one,
+ * and machine-specific scripts find the fixed name convenient. But
+ * It's also good for us to exclude multiple instances because both
+ * our hwmon and our wlan rfkill subdevice use global ACPI objects
+ * (the EC and the wlan PCI slot respectively).
+ */
+ result = eeepc_platform_init(eeepc);
+ if (result)
+ goto fail_platform;
if (!acpi_video_backlight_support()) {
- result = eeepc_backlight_init(dev);
+ result = eeepc_backlight_init(eeepc);
if (result)
goto fail_backlight;
} else
- pr_info("Backlight controlled by ACPI video "
- "driver\n");
+ pr_info("Backlight controlled by ACPI video driver\n");
- result = eeepc_input_init(dev);
+ result = eeepc_input_init(eeepc);
if (result)
goto fail_input;
- result = eeepc_hwmon_init(dev);
+ result = eeepc_hwmon_init(eeepc);
if (result)
goto fail_hwmon;
- result = eeepc_rfkill_init(dev);
+ result = eeepc_led_init(eeepc);
+ if (result)
+ goto fail_led;
+
+ result = eeepc_rfkill_init(eeepc);
if (result)
goto fail_rfkill;
+ eeepc_device_present = true;
return 0;
fail_rfkill:
- eeepc_hwmon_exit();
+ eeepc_led_exit(eeepc);
+fail_led:
+ eeepc_hwmon_exit(eeepc);
fail_hwmon:
- eeepc_input_exit();
+ eeepc_input_exit(eeepc);
fail_input:
- eeepc_backlight_exit();
+ eeepc_backlight_exit(eeepc);
fail_backlight:
- sysfs_remove_group(&platform_device->dev.kobj,
- &platform_attribute_group);
-fail_sysfs:
- platform_device_del(platform_device);
-fail_platform_device2:
- platform_device_put(platform_device);
-fail_platform_device1:
- platform_driver_unregister(&platform_driver);
-fail_platform_driver:
- kfree(ehotk);
+ eeepc_platform_exit(eeepc);
+fail_platform:
+ kfree(eeepc);
return result;
}
-static int eeepc_hotk_remove(struct acpi_device *device, int type)
+static int eeepc_acpi_remove(struct acpi_device *device, int type)
{
- if (!device || !acpi_driver_data(device))
- return -EINVAL;
+ struct eeepc_laptop *eeepc = acpi_driver_data(device);
- eeepc_backlight_exit();
- eeepc_rfkill_exit();
- eeepc_input_exit();
- eeepc_hwmon_exit();
- sysfs_remove_group(&platform_device->dev.kobj,
- &platform_attribute_group);
- platform_device_unregister(platform_device);
- platform_driver_unregister(&platform_driver);
+ eeepc_backlight_exit(eeepc);
+ eeepc_rfkill_exit(eeepc);
+ eeepc_input_exit(eeepc);
+ eeepc_hwmon_exit(eeepc);
+ eeepc_led_exit(eeepc);
+ eeepc_platform_exit(eeepc);
- kfree(ehotk);
+ kfree(eeepc);
return 0;
}
+
+static const struct acpi_device_id eeepc_device_ids[] = {
+ {EEEPC_ACPI_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, eeepc_device_ids);
+
+static struct acpi_driver eeepc_acpi_driver = {
+ .name = EEEPC_LAPTOP_NAME,
+ .class = EEEPC_ACPI_CLASS,
+ .owner = THIS_MODULE,
+ .ids = eeepc_device_ids,
+ .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
+ .ops = {
+ .add = eeepc_acpi_add,
+ .remove = eeepc_acpi_remove,
+ .notify = eeepc_acpi_notify,
+ },
+};
+
+
static int __init eeepc_laptop_init(void)
{
int result;
- if (acpi_disabled)
- return -ENODEV;
- result = acpi_bus_register_driver(&eeepc_hotk_driver);
+ result = platform_driver_register(&platform_driver);
if (result < 0)
return result;
- if (!ehotk) {
- acpi_bus_unregister_driver(&eeepc_hotk_driver);
- return -ENODEV;
+
+ result = acpi_bus_register_driver(&eeepc_acpi_driver);
+ if (result < 0)
+ goto fail_acpi_driver;
+ if (!eeepc_device_present) {
+ result = -ENODEV;
+ goto fail_no_device;
}
return 0;
+
+fail_no_device:
+ acpi_bus_unregister_driver(&eeepc_acpi_driver);
+fail_acpi_driver:
+ platform_driver_unregister(&platform_driver);
+ return result;
}
static void __exit eeepc_laptop_exit(void)
{
- acpi_bus_unregister_driver(&eeepc_hotk_driver);
+ acpi_bus_unregister_driver(&eeepc_acpi_driver);
+ platform_driver_unregister(&platform_driver);
}
module_init(eeepc_laptop_init);
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index f00a71c58e69..63c3e658a884 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/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_HOTKEY_QUERY 0xc
+enum hp_wmi_radio {
+ HPWMI_WIFI = 0,
+ HPWMI_BLUETOOTH = 1,
+ HPWMI_WWAN = 2,
+};
+
static int __init hp_wmi_bios_setup(struct platform_device *device);
static int __exit hp_wmi_bios_remove(struct platform_device *device);
static int hp_wmi_resume_handler(struct device *device);
@@ -175,8 +181,8 @@ static int hp_wmi_tablet_state(void)
static int hp_wmi_set_block(void *data, bool blocked)
{
- unsigned long b = (unsigned long) data;
- int query = BIT(b + 8) | ((!blocked) << b);
+ enum hp_wmi_radio r = (enum hp_wmi_radio) data;
+ int query = BIT(r + 8) | ((!blocked) << r);
return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query);
}
@@ -185,31 +191,23 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = {
.set_block = hp_wmi_set_block,
};
-static bool hp_wmi_wifi_state(void)
-{
- int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
-
- if (wireless & 0x100)
- return false;
- else
- return true;
-}
-
-static bool hp_wmi_bluetooth_state(void)
+static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+ int mask = 0x200 << (r * 8);
- if (wireless & 0x10000)
+ if (wireless & mask)
return false;
else
return true;
}
-static bool hp_wmi_wwan_state(void)
+static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
{
int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0);
+ int mask = 0x800 << (r * 8);
- if (wireless & 0x1000000)
+ if (wireless & mask)
return false;
else
return true;
@@ -334,49 +332,55 @@ static void hp_wmi_notify(u32 value, void *context)
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj;
+ int eventcode;
wmi_get_event_data(value, &response);
obj = (union acpi_object *)response.pointer;
- if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) {
- int eventcode = *((u8 *) obj->buffer.pointer);
- if (eventcode == 0x4)
- eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
- 0);
- key = hp_wmi_get_entry_by_scancode(eventcode);
- if (key) {
- switch (key->type) {
- case KE_KEY:
- input_report_key(hp_wmi_input_dev,
- key->keycode, 1);
- input_sync(hp_wmi_input_dev);
- input_report_key(hp_wmi_input_dev,
- key->keycode, 0);
- input_sync(hp_wmi_input_dev);
- break;
- }
- } else if (eventcode == 0x1) {
- input_report_switch(hp_wmi_input_dev, SW_DOCK,
- hp_wmi_dock_state());
- input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
- hp_wmi_tablet_state());
+ if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) {
+ printk(KERN_INFO "HP WMI: Unknown response received\n");
+ return;
+ }
+
+ eventcode = *((u8 *) obj->buffer.pointer);
+ if (eventcode == 0x4)
+ eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
+ 0);
+ key = hp_wmi_get_entry_by_scancode(eventcode);
+ if (key) {
+ switch (key->type) {
+ case KE_KEY:
+ input_report_key(hp_wmi_input_dev,
+ key->keycode, 1);
+ input_sync(hp_wmi_input_dev);
+ input_report_key(hp_wmi_input_dev,
+ key->keycode, 0);
input_sync(hp_wmi_input_dev);
- } else if (eventcode == 0x5) {
- if (wifi_rfkill)
- rfkill_set_sw_state(wifi_rfkill,
- hp_wmi_wifi_state());
- if (bluetooth_rfkill)
- rfkill_set_sw_state(bluetooth_rfkill,
- hp_wmi_bluetooth_state());
- if (wwan_rfkill)
- rfkill_set_sw_state(wwan_rfkill,
- hp_wmi_wwan_state());
- } else
- printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
- eventcode);
+ break;
+ }
+ } else if (eventcode == 0x1) {
+ input_report_switch(hp_wmi_input_dev, SW_DOCK,
+ hp_wmi_dock_state());
+ input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+ hp_wmi_tablet_state());
+ input_sync(hp_wmi_input_dev);
+ } else if (eventcode == 0x5) {
+ if (wifi_rfkill)
+ rfkill_set_states(wifi_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WIFI),
+ hp_wmi_get_hw_state(HPWMI_WIFI));
+ if (bluetooth_rfkill)
+ rfkill_set_states(bluetooth_rfkill,
+ hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
+ hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
+ if (wwan_rfkill)
+ rfkill_set_states(wwan_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WWAN),
+ hp_wmi_get_hw_state(HPWMI_WWAN));
} else
- printk(KERN_INFO "HP WMI: Unknown response received\n");
+ printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n",
+ eventcode);
}
static int __init hp_wmi_input_setup(void)
@@ -455,7 +459,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN,
&hp_wmi_rfkill_ops,
- (void *) 0);
+ (void *) HPWMI_WIFI);
+ rfkill_init_sw_state(wifi_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WIFI));
+ rfkill_set_hw_state(wifi_rfkill,
+ hp_wmi_get_hw_state(HPWMI_WIFI));
err = rfkill_register(wifi_rfkill);
if (err)
goto register_wifi_error;
@@ -465,7 +473,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev,
RFKILL_TYPE_BLUETOOTH,
&hp_wmi_rfkill_ops,
- (void *) 1);
+ (void *) HPWMI_BLUETOOTH);
+ rfkill_init_sw_state(bluetooth_rfkill,
+ hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
+ rfkill_set_hw_state(bluetooth_rfkill,
+ hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
err = rfkill_register(bluetooth_rfkill);
if (err)
goto register_bluetooth_error;
@@ -475,7 +487,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev,
RFKILL_TYPE_WWAN,
&hp_wmi_rfkill_ops,
- (void *) 2);
+ (void *) HPWMI_WWAN);
+ rfkill_init_sw_state(wwan_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WWAN));
+ rfkill_set_hw_state(wwan_rfkill,
+ hp_wmi_get_hw_state(HPWMI_WWAN));
err = rfkill_register(wwan_rfkill);
if (err)
goto register_wwan_err;
@@ -533,6 +549,19 @@ static int hp_wmi_resume_handler(struct device *device)
input_sync(hp_wmi_input_dev);
}
+ if (wifi_rfkill)
+ rfkill_set_states(wifi_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WIFI),
+ hp_wmi_get_hw_state(HPWMI_WIFI));
+ if (bluetooth_rfkill)
+ rfkill_set_states(bluetooth_rfkill,
+ hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
+ hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
+ if (wwan_rfkill)
+ rfkill_set_states(wwan_rfkill,
+ hp_wmi_get_sw_state(HPWMI_WWAN),
+ hp_wmi_get_hw_state(HPWMI_WWAN));
+
return 0;
}
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
new file mode 100644
index 000000000000..0c8fe145c4af
--- /dev/null
+++ b/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);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index cf61d6a8ef6f..448c8aeb166b 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -21,8 +21,8 @@
* 02110-1301, USA.
*/
-#define TPACPI_VERSION "0.23"
-#define TPACPI_SYSFS_VERSION 0x020500
+#define TPACPI_VERSION "0.24"
+#define TPACPI_SYSFS_VERSION 0x020700
/*
* Changelog:
@@ -61,6 +61,7 @@
#include <linux/nvram.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/backlight.h>
#include <linux/fb.h>
@@ -76,6 +77,10 @@
#include <linux/jiffies.h>
#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
#include <acpi/acpi_drivers.h>
#include <linux/pci_ids.h>
@@ -231,6 +236,7 @@ enum tpacpi_hkey_event_t {
#define TPACPI_DBG_HKEY 0x0008
#define TPACPI_DBG_FAN 0x0010
#define TPACPI_DBG_BRGHT 0x0020
+#define TPACPI_DBG_MIXER 0x0040
#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
@@ -256,7 +262,7 @@ struct tp_acpi_drv_struct {
struct ibm_struct {
char *name;
- int (*read) (char *);
+ int (*read) (struct seq_file *);
int (*write) (char *);
void (*exit) (void);
void (*resume) (void);
@@ -298,6 +304,7 @@ static struct {
u32 fan_ctrl_status_undef:1;
u32 second_fan:1;
u32 beep_needs_two_args:1;
+ u32 mixer_no_level_control:1;
u32 input_device_registered:1;
u32 platform_drv_registered:1;
u32 platform_drv_attrs_registered:1;
@@ -309,6 +316,7 @@ static struct {
static struct {
u16 hotkey_mask_ff:1;
+ u16 volume_ctrl_forbidden:1;
} tp_warned;
struct thinkpad_id_data {
@@ -425,6 +433,12 @@ static void tpacpi_log_usertask(const char * const what)
.ec = TPACPI_MATCH_ANY, \
.quirks = (__quirk) }
+#define TPACPI_QEC_LNV(__id1, __id2, __quirk) \
+ { .vendor = PCI_VENDOR_ID_LENOVO, \
+ .bios = TPACPI_MATCH_ANY, \
+ .ec = TPID(__id1, __id2), \
+ .quirks = (__quirk) }
+
struct tpacpi_quirk {
unsigned int vendor;
u16 bios;
@@ -776,36 +790,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
****************************************************************************
****************************************************************************/
-static int dispatch_procfs_read(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int dispatch_proc_show(struct seq_file *m, void *v)
{
- struct ibm_struct *ibm = data;
- int len;
+ struct ibm_struct *ibm = m->private;
if (!ibm || !ibm->read)
return -EINVAL;
+ return ibm->read(m);
+}
- len = ibm->read(page);
- if (len < 0)
- return len;
-
- if (len <= off + count)
- *eof = 1;
- *start = page + off;
- len -= off;
- if (len > count)
- len = count;
- if (len < 0)
- len = 0;
-
- return len;
+static int dispatch_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, dispatch_proc_show, PDE(inode)->data);
}
-static int dispatch_procfs_write(struct file *file,
+static ssize_t dispatch_proc_write(struct file *file,
const char __user *userbuf,
- unsigned long count, void *data)
+ size_t count, loff_t *pos)
{
- struct ibm_struct *ibm = data;
+ struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data;
char *kernbuf;
int ret;
@@ -834,6 +837,15 @@ static int dispatch_procfs_write(struct file *file,
return ret;
}
+static const struct file_operations dispatch_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = dispatch_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = dispatch_proc_write,
+};
+
static char *next_cmd(char **cmds)
{
char *start = *cmds;
@@ -1261,6 +1273,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
struct tpacpi_rfk *atp_rfk;
int res;
bool sw_state = false;
+ bool hw_state;
int sw_status;
BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]);
@@ -1295,7 +1308,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
rfkill_init_sw_state(atp_rfk->rfkill, sw_state);
}
}
- rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state());
+ hw_state = tpacpi_rfk_check_hwblock_state();
+ rfkill_set_hw_state(atp_rfk->rfkill, hw_state);
res = rfkill_register(atp_rfk->rfkill);
if (res < 0) {
@@ -1308,6 +1322,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id,
}
tpacpi_rfkill_switches[id] = atp_rfk;
+
+ printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n",
+ name, (sw_state || hw_state) ? "" : "un");
return 0;
}
@@ -1380,12 +1397,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id,
}
/* procfs -------------------------------------------------------------- */
-static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
+static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m)
{
- int len = 0;
-
if (id >= TPACPI_RFK_SW_MAX)
- len += sprintf(p + len, "status:\t\tnot supported\n");
+ seq_printf(m, "status:\t\tnot supported\n");
else {
int status;
@@ -1399,13 +1414,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p)
return status;
}
- len += sprintf(p + len, "status:\t\t%s\n",
+ seq_printf(m, "status:\t\t%s\n",
(status == TPACPI_RFK_RADIO_ON) ?
"enabled" : "disabled");
- len += sprintf(p + len, "commands:\tenable, disable\n");
+ seq_printf(m, "commands:\tenable, disable\n");
}
- return len;
+ return 0;
}
static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
@@ -1776,7 +1791,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = {
TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */
TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */
- TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */
+ TPV_QL1('7', 'E', 'D', '0', '1', '5'), /* R60e, R60i */
/* BIOS FW BIOS VERS EC FW EC VERS */
TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */
@@ -1792,8 +1807,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = {
TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */
TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */
- TPV_QL0('7', 'B', 'D', '7'), /* X60/s */
- TPV_QL0('7', 'J', '3', '0'), /* X60t */
+ TPV_QL1('7', 'B', 'D', '7', '4', '0'), /* X60/s */
+ TPV_QL1('7', 'J', '3', '0', '1', '3'), /* X60t */
/* (0) - older versions lack DMI EC fw string and functionality */
/* (1) - older versions known to lack functionality */
@@ -1883,14 +1898,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
return 0;
}
-static int thinkpad_acpi_driver_read(char *p)
+static int thinkpad_acpi_driver_read(struct seq_file *m)
{
- int len = 0;
-
- len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC);
- len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION);
-
- return len;
+ seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC);
+ seq_printf(m, "version:\t%s\n", TPACPI_VERSION);
+ return 0;
}
static struct ibm_struct thinkpad_acpi_driver_data = {
@@ -2186,7 +2198,8 @@ static int hotkey_mask_set(u32 mask)
fwmask, hotkey_acpi_mask);
}
- hotkey_mask_warn_incomplete_mask();
+ if (tpacpi_lifecycle != TPACPI_LIFE_EXITING)
+ hotkey_mask_warn_incomplete_mask();
return rc;
}
@@ -3182,6 +3195,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
int res, i;
int status;
int hkeyv;
+ bool radiosw_state = false;
+ bool tabletsw_state = false;
unsigned long quirks;
@@ -3287,6 +3302,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
if (dbg_wlswemul) {
tp_features.hotkey_wlsw = 1;
+ radiosw_state = !!tpacpi_wlsw_emulstate;
printk(TPACPI_INFO
"radio switch emulation enabled\n");
} else
@@ -3294,6 +3310,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Not all thinkpads have a hardware radio switch */
if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
tp_features.hotkey_wlsw = 1;
+ radiosw_state = !!status;
printk(TPACPI_INFO
"radio switch found; radios are %s\n",
enabled(status, 0));
@@ -3305,11 +3322,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* For X41t, X60t, X61t Tablets... */
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
tp_features.hotkey_tablet = 1;
+ tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK);
printk(TPACPI_INFO
"possible tablet mode switch found; "
"ThinkPad in %s mode\n",
- (status & TP_HOTKEY_TABLET_MASK)?
- "tablet" : "laptop");
+ (tabletsw_state) ? "tablet" : "laptop");
res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_tablet_mode.attr);
}
@@ -3344,16 +3361,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
TPACPI_HOTKEY_MAP_SIZE);
}
- set_bit(EV_KEY, tpacpi_inputdev->evbit);
- set_bit(EV_MSC, tpacpi_inputdev->evbit);
- set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
+ input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN);
tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
tpacpi_inputdev->keycode = hotkey_keycode_map;
for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
if (hotkey_keycode_map[i] != KEY_RESERVED) {
- set_bit(hotkey_keycode_map[i],
- tpacpi_inputdev->keybit);
+ input_set_capability(tpacpi_inputdev, EV_KEY,
+ hotkey_keycode_map[i]);
} else {
if (i < sizeof(hotkey_reserved_mask)*8)
hotkey_reserved_mask |= 1 << i;
@@ -3361,12 +3376,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
}
if (tp_features.hotkey_wlsw) {
- set_bit(EV_SW, tpacpi_inputdev->evbit);
- set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
+ input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL);
+ input_report_switch(tpacpi_inputdev,
+ SW_RFKILL_ALL, radiosw_state);
}
if (tp_features.hotkey_tablet) {
- set_bit(EV_SW, tpacpi_inputdev->evbit);
- set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
+ input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE);
+ input_report_switch(tpacpi_inputdev,
+ SW_TABLET_MODE, tabletsw_state);
}
/* Do not issue duplicate brightness change events to
@@ -3433,8 +3450,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
tpacpi_inputdev->close = &hotkey_inputdev_close;
hotkey_poll_setup_safe(true);
- tpacpi_send_radiosw_update();
- tpacpi_input_send_tabletsw();
return 0;
@@ -3542,49 +3557,57 @@ static bool hotkey_notify_usrevent(const u32 hkey,
}
}
+static void thermal_dump_all_sensors(void);
+
static bool hotkey_notify_thermal(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
{
+ bool known = true;
+
/* 0x6000-0x6FFF: thermal alarms */
*send_acpi_ev = true;
*ignore_acpi_ev = false;
switch (hkey) {
+ case TP_HKEY_EV_THM_TABLE_CHANGED:
+ printk(TPACPI_INFO
+ "EC reports that Thermal Table has changed\n");
+ /* recommended action: do nothing, we don't have
+ * Lenovo ATM information */
+ return true;
case TP_HKEY_EV_ALARM_BAT_HOT:
printk(TPACPI_CRIT
"THERMAL ALARM: battery is too hot!\n");
/* recommended action: warn user through gui */
- return true;
+ break;
case TP_HKEY_EV_ALARM_BAT_XHOT:
printk(TPACPI_ALERT
"THERMAL EMERGENCY: battery is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
- return true;
+ break;
case TP_HKEY_EV_ALARM_SENSOR_HOT:
printk(TPACPI_CRIT
"THERMAL ALARM: "
"a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */
/* some internal component is too hot */
- return true;
+ break;
case TP_HKEY_EV_ALARM_SENSOR_XHOT:
printk(TPACPI_ALERT
"THERMAL EMERGENCY: "
"a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
- return true;
- case TP_HKEY_EV_THM_TABLE_CHANGED:
- printk(TPACPI_INFO
- "EC reports that Thermal Table has changed\n");
- /* recommended action: do nothing, we don't have
- * Lenovo ATM information */
- return true;
+ break;
default:
printk(TPACPI_ALERT
"THERMAL ALERT: unknown thermal alarm received\n");
- return false;
+ known = false;
}
+
+ thermal_dump_all_sensors();
+
+ return known;
}
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
@@ -3727,14 +3750,13 @@ static void hotkey_resume(void)
}
/* procfs -------------------------------------------------------------- */
-static int hotkey_read(char *p)
+static int hotkey_read(struct seq_file *m)
{
int res, status;
- int len = 0;
if (!tp_features.hotkey) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
+ seq_printf(m, "status:\t\tnot supported\n");
+ return 0;
}
if (mutex_lock_killable(&hotkey_mutex))
@@ -3746,17 +3768,16 @@ static int hotkey_read(char *p)
if (res)
return res;
- len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
+ seq_printf(m, "status:\t\t%s\n", enabled(status, 0));
if (hotkey_all_mask) {
- len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask);
- len += sprintf(p + len,
- "commands:\tenable, disable, reset, <mask>\n");
+ seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask);
+ seq_printf(m, "commands:\tenable, disable, reset, <mask>\n");
} else {
- len += sprintf(p + len, "mask:\t\tnot supported\n");
- len += sprintf(p + len, "commands:\tenable, disable, reset\n");
+ seq_printf(m, "mask:\t\tnot supported\n");
+ seq_printf(m, "commands:\tenable, disable, reset\n");
}
- return len;
+ return 0;
}
static void hotkey_enabledisable_warn(bool enable)
@@ -3863,15 +3884,6 @@ enum {
#define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw"
-static void bluetooth_suspend(pm_message_t state)
-{
- /* Try to make sure radio will resume powered off */
- if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd",
- TP_ACPI_BLTH_PWR_OFF_ON_RESUME))
- vdbg_printk(TPACPI_DBG_RFKILL,
- "bluetooth power down on resume request failed\n");
-}
-
static int bluetooth_get_status(void)
{
int status;
@@ -3905,10 +3917,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state)
#endif
/* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */
+ status = TP_ACPI_BLUETOOTH_RESUMECTRL;
if (state == TPACPI_RFK_RADIO_ON)
- status = TP_ACPI_BLUETOOTH_RADIOSSW;
- else
- status = 0;
+ status |= TP_ACPI_BLUETOOTH_RADIOSSW;
if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
return -EIO;
@@ -4032,9 +4043,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
}
/* procfs -------------------------------------------------------------- */
-static int bluetooth_read(char *p)
+static int bluetooth_read(struct seq_file *m)
{
- return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p);
+ return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m);
}
static int bluetooth_write(char *buf)
@@ -4047,7 +4058,6 @@ static struct ibm_struct bluetooth_driver_data = {
.read = bluetooth_read,
.write = bluetooth_write,
.exit = bluetooth_exit,
- .suspend = bluetooth_suspend,
.shutdown = bluetooth_shutdown,
};
@@ -4065,15 +4075,6 @@ enum {
#define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw"
-static void wan_suspend(pm_message_t state)
-{
- /* Try to make sure radio will resume powered off */
- if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd",
- TP_ACPI_WGSV_PWR_OFF_ON_RESUME))
- vdbg_printk(TPACPI_DBG_RFKILL,
- "WWAN power down on resume request failed\n");
-}
-
static int wan_get_status(void)
{
int status;
@@ -4106,11 +4107,10 @@ static int wan_set_status(enum tpacpi_rfkill_state state)
}
#endif
- /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */
+ /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */
+ status = TP_ACPI_WANCARD_RESUMECTRL;
if (state == TPACPI_RFK_RADIO_ON)
- status = TP_ACPI_WANCARD_RADIOSSW;
- else
- status = 0;
+ status |= TP_ACPI_WANCARD_RADIOSSW;
if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
return -EIO;
@@ -4233,9 +4233,9 @@ static int __init wan_init(struct ibm_init_struct *iibm)
}
/* procfs -------------------------------------------------------------- */
-static int wan_read(char *p)
+static int wan_read(struct seq_file *m)
{
- return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p);
+ return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m);
}
static int wan_write(char *buf)
@@ -4248,7 +4248,6 @@ static struct ibm_struct wan_driver_data = {
.read = wan_read,
.write = wan_write,
.exit = wan_exit,
- .suspend = wan_suspend,
.shutdown = wan_shutdown,
};
@@ -4611,14 +4610,13 @@ static int video_expand_toggle(void)
/* not reached */
}
-static int video_read(char *p)
+static int video_read(struct seq_file *m)
{
int status, autosw;
- int len = 0;
if (video_supported == TPACPI_VIDEO_NONE) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
+ seq_printf(m, "status:\t\tnot supported\n");
+ return 0;
}
status = video_outputsw_get();
@@ -4629,20 +4627,20 @@ static int video_read(char *p)
if (autosw < 0)
return autosw;
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
- len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
+ seq_printf(m, "status:\t\tsupported\n");
+ seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0));
+ seq_printf(m, "crt:\t\t%s\n", enabled(status, 1));
if (video_supported == TPACPI_VIDEO_NEW)
- len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
- len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
- len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
- len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
+ seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3));
+ seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0));
+ seq_printf(m, "commands:\tlcd_enable, lcd_disable\n");
+ seq_printf(m, "commands:\tcrt_enable, crt_disable\n");
if (video_supported == TPACPI_VIDEO_NEW)
- len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
- len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
- len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
+ seq_printf(m, "commands:\tdvi_enable, dvi_disable\n");
+ seq_printf(m, "commands:\tauto_enable, auto_disable\n");
+ seq_printf(m, "commands:\tvideo_switch, expand_toggle\n");
- return len;
+ return 0;
}
static int video_write(char *buf)
@@ -4834,25 +4832,24 @@ static void light_exit(void)
flush_workqueue(tpacpi_wq);
}
-static int light_read(char *p)
+static int light_read(struct seq_file *m)
{
- int len = 0;
int status;
if (!tp_features.light) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
+ seq_printf(m, "status:\t\tnot supported\n");
} else if (!tp_features.light_status) {
- len += sprintf(p + len, "status:\t\tunknown\n");
- len += sprintf(p + len, "commands:\ton, off\n");
+ seq_printf(m, "status:\t\tunknown\n");
+ seq_printf(m, "commands:\ton, off\n");
} else {
status = light_get_status();
if (status < 0)
return status;
- len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
- len += sprintf(p + len, "commands:\ton, off\n");
+ seq_printf(m, "status:\t\t%s\n", onoff(status, 0));
+ seq_printf(m, "commands:\ton, off\n");
}
- return len;
+ return 0;
}
static int light_write(char *buf)
@@ -4930,20 +4927,18 @@ static void cmos_exit(void)
device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
}
-static int cmos_read(char *p)
+static int cmos_read(struct seq_file *m)
{
- int len = 0;
-
/* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
R30, R31, T20-22, X20-21 */
if (!cmos_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
+ seq_printf(m, "status:\t\tnot supported\n");
else {
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
+ seq_printf(m, "status:\t\tsupported\n");
+ seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n");
}
- return len;
+ return 0;
}
static int cmos_write(char *buf)
@@ -5318,15 +5313,13 @@ static int __init led_init(struct ibm_init_struct *iibm)
((s) == TPACPI_LED_OFF ? "off" : \
((s) == TPACPI_LED_ON ? "on" : "blinking"))
-static int led_read(char *p)
+static int led_read(struct seq_file *m)
{
- int len = 0;
-
if (!led_supported) {
- len += sprintf(p + len, "status:\t\tnot supported\n");
- return len;
+ seq_printf(m, "status:\t\tnot supported\n");
+ return 0;
}
- len += sprintf(p + len, "status:\t\tsupported\n");
+ seq_printf(m, "status:\t\tsupported\n");
if (led_supported == TPACPI_LED_570) {
/* 570 */
@@ -5335,15 +5328,15 @@ static int led_read(char *p)
status = led_get_status(i);
if (status < 0)
return -EIO;
- len += sprintf(p + len, "%d:\t\t%s\n",
+ seq_printf(m, "%d:\t\t%s\n",
i, str_led_status(status));
}
}
- len += sprintf(p + len, "commands:\t"
+ seq_printf(m, "commands:\t"
"<led> on, <led> off, <led> blink (<led> is 0-15)\n");
- return len;
+ return 0;
}
static int led_write(char *buf)
@@ -5416,18 +5409,16 @@ static int __init beep_init(struct ibm_init_struct *iibm)
return (beep_handle)? 0 : 1;
}
-static int beep_read(char *p)
+static int beep_read(struct seq_file *m)
{
- int len = 0;
-
if (!beep_handle)
- len += sprintf(p + len, "status:\t\tnot supported\n");
+ seq_printf(m, "status:\t\tnot supported\n");
else {
- len += sprintf(p + len, "status:\t\tsupported\n");
- len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
+ seq_printf(m, "status:\t\tsupported\n");
+ seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n");
}
- return len;
+ return 0;
}
static int beep_write(char *buf)
@@ -5480,8 +5471,11 @@ enum { /* TPACPI_THERMAL_TPEC_* */
TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */
TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */
TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */
+
+ TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */
};
+
#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */
struct ibm_thermal_sensors_struct {
s32 temp[TPACPI_MAX_THERMAL_SENSORS];
@@ -5571,6 +5565,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
return n;
}
+static void thermal_dump_all_sensors(void)
+{
+ int n, i;
+ struct ibm_thermal_sensors_struct t;
+
+ n = thermal_get_sensors(&t);
+ if (n <= 0)
+ return;
+
+ printk(TPACPI_NOTICE
+ "temperatures (Celsius):");
+
+ for (i = 0; i < n; i++) {
+ if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA)
+ printk(KERN_CONT " %d", (int)(t.temp[i] / 1000));
+ else
+ printk(KERN_CONT " N/A");
+ }
+
+ printk(KERN_CONT "\n");
+}
+
/* sysfs temp##_input -------------------------------------------------- */
static ssize_t thermal_temp_input_show(struct device *dev,
@@ -5586,7 +5602,7 @@ static ssize_t thermal_temp_input_show(struct device *dev,
res = thermal_get_sensor(idx, &value);
if (res)
return res;
- if (value == TP_EC_THERMAL_TMP_NA * 1000)
+ if (value == TPACPI_THERMAL_SENSOR_NA)
return -ENXIO;
return snprintf(buf, PAGE_SIZE, "%d\n", value);
@@ -5763,9 +5779,8 @@ static void thermal_exit(void)
}
}
-static int thermal_read(char *p)
+static int thermal_read(struct seq_file *m)
{
- int len = 0;
int n, i;
struct ibm_thermal_sensors_struct t;
@@ -5773,16 +5788,16 @@ static int thermal_read(char *p)
if (unlikely(n < 0))
return n;
- len += sprintf(p + len, "temperatures:\t");
+ seq_printf(m, "temperatures:\t");
if (n > 0) {
for (i = 0; i < (n - 1); i++)
- len += sprintf(p + len, "%d ", t.temp[i] / 1000);
- len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
+ seq_printf(m, "%d ", t.temp[i] / 1000);
+ seq_printf(m, "%d\n", t.temp[i] / 1000);
} else
- len += sprintf(p + len, "not supported\n");
+ seq_printf(m, "not supported\n");
- return len;
+ return 0;
}
static struct ibm_struct thermal_driver_data = {
@@ -5797,39 +5812,38 @@ static struct ibm_struct thermal_driver_data = {
static u8 ecdump_regs[256];
-static int ecdump_read(char *p)
+static int ecdump_read(struct seq_file *m)
{
- int len = 0;
int i, j;
u8 v;
- len += sprintf(p + len, "EC "
+ seq_printf(m, "EC "
" +00 +01 +02 +03 +04 +05 +06 +07"
" +08 +09 +0a +0b +0c +0d +0e +0f\n");
for (i = 0; i < 256; i += 16) {
- len += sprintf(p + len, "EC 0x%02x:", i);
+ seq_printf(m, "EC 0x%02x:", i);
for (j = 0; j < 16; j++) {
if (!acpi_ec_read(i + j, &v))
break;
if (v != ecdump_regs[i + j])
- len += sprintf(p + len, " *%02x", v);
+ seq_printf(m, " *%02x", v);
else
- len += sprintf(p + len, " %02x", v);
+ seq_printf(m, " %02x", v);
ecdump_regs[i + j] = v;
}
- len += sprintf(p + len, "\n");
+ seq_putc(m, '\n');
if (j != 16)
break;
}
/* These are way too dangerous to advertise openly... */
#if 0
- len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
+ seq_printf(m, "commands:\t0x<offset> 0x<value>"
" (<offset> is 00-ff, <value> is 00-ff)\n");
- len += sprintf(p + len, "commands:\t0x<offset> <value> "
+ seq_printf(m, "commands:\t0x<offset> <value> "
" (<offset> is 00-ff, <value> is 0-255)\n");
#endif
- return len;
+ return 0;
}
static int ecdump_write(char *buf)
@@ -6092,6 +6106,12 @@ static int brightness_get(struct backlight_device *bd)
return status & TP_EC_BACKLIGHT_LVLMSK;
}
+static void tpacpi_brightness_notify_change(void)
+{
+ backlight_force_update(ibm_backlight_device,
+ BACKLIGHT_UPDATE_HOTKEY);
+}
+
static struct backlight_ops ibm_backlight_data = {
.get_brightness = brightness_get,
.update_status = brightness_update_status,
@@ -6120,8 +6140,8 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = {
/* Models with Intel Extreme Graphics 2 */
TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC),
- TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
- TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC),
+ TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
+ TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC),
/* Models with Intel GMA900 */
TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */
@@ -6246,6 +6266,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK;
backlight_update_status(ibm_backlight_device);
+ vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
+ "brightness: registering brightness hotkeys "
+ "as change notification\n");
+ tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
+ | TP_ACPI_HKEY_BRGHTUP_MASK
+ | TP_ACPI_HKEY_BRGHTDWN_MASK);;
return 0;
}
@@ -6270,23 +6296,22 @@ static void brightness_exit(void)
tpacpi_brightness_checkpoint_nvram();
}
-static int brightness_read(char *p)
+static int brightness_read(struct seq_file *m)
{
- int len = 0;
int level;
level = brightness_get(NULL);
if (level < 0) {
- len += sprintf(p + len, "level:\t\tunreadable\n");
+ seq_printf(m, "level:\t\tunreadable\n");
} else {
- len += sprintf(p + len, "level:\t\t%d\n", level);
- len += sprintf(p + len, "commands:\tup, down\n");
- len += sprintf(p + len, "commands:\tlevel <level>"
+ seq_printf(m, "level:\t\t%d\n", level);
+ seq_printf(m, "commands:\tup, down\n");
+ seq_printf(m, "commands:\tlevel <level>"
" (<level> is 0-%d)\n",
(tp_features.bright_16levels) ? 15 : 7);
}
- return len;
+ return 0;
}
static int brightness_write(char *buf)
@@ -6322,6 +6347,9 @@ static int brightness_write(char *buf)
* Doing it this way makes the syscall restartable in case of EINTR
*/
rc = brightness_set(level);
+ if (!rc && ibm_backlight_device)
+ backlight_force_update(ibm_backlight_device,
+ BACKLIGHT_UPDATE_SYSFS);
return (rc == -EINTR)? -ERESTARTSYS : rc;
}
@@ -6338,99 +6366,654 @@ static struct ibm_struct brightness_driver_data = {
* Volume subdriver
*/
-static int volume_offset = 0x30;
+/*
+ * IBM ThinkPads have a simple volume controller with MUTE gating.
+ * Very early Lenovo ThinkPads follow the IBM ThinkPad spec.
+ *
+ * Since the *61 series (and probably also the later *60 series), Lenovo
+ * ThinkPads only implement the MUTE gate.
+ *
+ * EC register 0x30
+ * Bit 6: MUTE (1 mutes sound)
+ * Bit 3-0: Volume
+ * Other bits should be zero as far as we know.
+ *
+ * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and
+ * bits 3-0 (volume). Other bits in NVRAM may have other functions,
+ * such as bit 7 which is used to detect repeated presses of MUTE,
+ * and we leave them unchanged.
+ */
+
+#define TPACPI_ALSA_DRVNAME "ThinkPad EC"
+#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
+#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
+
+static int alsa_index = SNDRV_DEFAULT_IDX1;
+static char *alsa_id = "ThinkPadEC";
+static int alsa_enable = SNDRV_DEFAULT_ENABLE1;
+
+struct tpacpi_alsa_data {
+ struct snd_card *card;
+ struct snd_ctl_elem_id *ctl_mute_id;
+ struct snd_ctl_elem_id *ctl_vol_id;
+};
+
+static struct snd_card *alsa_card;
+
+enum {
+ TP_EC_AUDIO = 0x30,
+
+ /* TP_EC_AUDIO bits */
+ TP_EC_AUDIO_MUTESW = 6,
+
+ /* TP_EC_AUDIO bitmasks */
+ TP_EC_AUDIO_LVL_MSK = 0x0F,
+ TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW),
+
+ /* Maximum volume */
+ TP_EC_VOLUME_MAX = 14,
+};
+
+enum tpacpi_volume_access_mode {
+ TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */
+ TPACPI_VOL_MODE_EC, /* Pure EC control */
+ TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */
+ TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */
+ TPACPI_VOL_MODE_MAX
+};
+
+enum tpacpi_volume_capabilities {
+ TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */
+ TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */
+ TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */
+ TPACPI_VOL_CAP_MAX
+};
+
+static enum tpacpi_volume_access_mode volume_mode =
+ TPACPI_VOL_MODE_MAX;
+
+static enum tpacpi_volume_capabilities volume_capabilities;
+static int volume_control_allowed;
-static int volume_read(char *p)
+/*
+ * Used to syncronize writers to TP_EC_AUDIO and
+ * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write
+ */
+static struct mutex volume_mutex;
+
+static void tpacpi_volume_checkpoint_nvram(void)
{
- int len = 0;
- u8 level;
+ u8 lec = 0;
+ u8 b_nvram;
+ u8 ec_mask;
+
+ if (volume_mode != TPACPI_VOL_MODE_ECNVRAM)
+ return;
+ if (!volume_control_allowed)
+ return;
+
+ vdbg_printk(TPACPI_DBG_MIXER,
+ "trying to checkpoint mixer state to NVRAM...\n");
- if (!acpi_ec_read(volume_offset, &level)) {
- len += sprintf(p + len, "level:\t\tunreadable\n");
+ if (tp_features.mixer_no_level_control)
+ ec_mask = TP_EC_AUDIO_MUTESW_MSK;
+ else
+ ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK;
+
+ if (mutex_lock_killable(&volume_mutex) < 0)
+ return;
+
+ if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec)))
+ goto unlock;
+ lec &= ec_mask;
+ b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER);
+
+ if (lec != (b_nvram & ec_mask)) {
+ /* NVRAM needs update */
+ b_nvram &= ~ec_mask;
+ b_nvram |= lec;
+ nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER);
+ dbg_printk(TPACPI_DBG_MIXER,
+ "updated NVRAM mixer status to 0x%02x (0x%02x)\n",
+ (unsigned int) lec, (unsigned int) b_nvram);
} else {
- len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
- len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
- len += sprintf(p + len, "commands:\tup, down, mute\n");
- len += sprintf(p + len, "commands:\tlevel <level>"
- " (<level> is 0-15)\n");
+ vdbg_printk(TPACPI_DBG_MIXER,
+ "NVRAM mixer status already is 0x%02x (0x%02x)\n",
+ (unsigned int) lec, (unsigned int) b_nvram);
}
- return len;
+unlock:
+ mutex_unlock(&volume_mutex);
}
-static int volume_write(char *buf)
+static int volume_get_status_ec(u8 *status)
{
- int cmos_cmd, inc, i;
- u8 level, mute;
- int new_level, new_mute;
- char *cmd;
+ u8 s;
- while ((cmd = next_cmd(&buf))) {
- if (!acpi_ec_read(volume_offset, &level))
- return -EIO;
- new_mute = mute = level & 0x40;
- new_level = level = level & 0xf;
+ if (!acpi_ec_read(TP_EC_AUDIO, &s))
+ return -EIO;
- if (strlencmp(cmd, "up") == 0) {
- if (mute)
- new_mute = 0;
- else
- new_level = level == 15 ? 15 : level + 1;
- } else if (strlencmp(cmd, "down") == 0) {
- if (mute)
- new_mute = 0;
- else
- new_level = level == 0 ? 0 : level - 1;
- } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
- new_level >= 0 && new_level <= 15) {
- /* new_level set */
- } else if (strlencmp(cmd, "mute") == 0) {
- new_mute = 0x40;
- } else
- return -EINVAL;
+ *status = s;
- if (new_level != level) {
- /* mute doesn't change */
+ dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s);
- cmos_cmd = (new_level > level) ?
- TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
- inc = new_level > level ? 1 : -1;
+ return 0;
+}
- if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
- !acpi_ec_write(volume_offset, level)))
- return -EIO;
+static int volume_get_status(u8 *status)
+{
+ return volume_get_status_ec(status);
+}
- for (i = level; i != new_level; i += inc)
- if (issue_thinkpad_cmos_command(cmos_cmd) ||
- !acpi_ec_write(volume_offset, i + inc))
- return -EIO;
+static int volume_set_status_ec(const u8 status)
+{
+ if (!acpi_ec_write(TP_EC_AUDIO, status))
+ return -EIO;
- if (mute &&
- (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
- !acpi_ec_write(volume_offset, new_level + mute))) {
- return -EIO;
- }
+ dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status);
+
+ return 0;
+}
+
+static int volume_set_status(const u8 status)
+{
+ return volume_set_status_ec(status);
+}
+
+static int volume_set_mute_ec(const bool mute)
+{
+ int rc;
+ u8 s, n;
+
+ if (mutex_lock_killable(&volume_mutex) < 0)
+ return -EINTR;
+
+ rc = volume_get_status_ec(&s);
+ if (rc)
+ goto unlock;
+
+ n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK :
+ s & ~TP_EC_AUDIO_MUTESW_MSK;
+
+ if (n != s)
+ rc = volume_set_status_ec(n);
+
+unlock:
+ mutex_unlock(&volume_mutex);
+ return rc;
+}
+
+static int volume_set_mute(const bool mute)
+{
+ dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n",
+ (mute) ? "" : "un");
+ return volume_set_mute_ec(mute);
+}
+
+static int volume_set_volume_ec(const u8 vol)
+{
+ int rc;
+ u8 s, n;
+
+ if (vol > TP_EC_VOLUME_MAX)
+ return -EINVAL;
+
+ if (mutex_lock_killable(&volume_mutex) < 0)
+ return -EINTR;
+
+ rc = volume_get_status_ec(&s);
+ if (rc)
+ goto unlock;
+
+ n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol;
+
+ if (n != s)
+ rc = volume_set_status_ec(n);
+
+unlock:
+ mutex_unlock(&volume_mutex);
+ return rc;
+}
+
+static int volume_set_volume(const u8 vol)
+{
+ dbg_printk(TPACPI_DBG_MIXER,
+ "trying to set volume 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;
}
+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 = {
.name = "volume",
.read = volume_read,
.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;
u8 status;
unsigned int speed = 0;
@@ -7521,7 +8103,7 @@ static int fan_read(char *p)
if (rc < 0)
return rc;
- len += sprintf(p + len, "status:\t\t%s\n"
+ seq_printf(m, "status:\t\t%s\n"
"level:\t\t%d\n",
(status != 0) ? "enabled" : "disabled", status);
break;
@@ -7532,54 +8114,54 @@ static int fan_read(char *p)
if (rc < 0)
return rc;
- len += sprintf(p + len, "status:\t\t%s\n",
+ seq_printf(m, "status:\t\t%s\n",
(status != 0) ? "enabled" : "disabled");
rc = fan_get_speed(&speed);
if (rc < 0)
return rc;
- len += sprintf(p + len, "speed:\t\t%d\n", speed);
+ seq_printf(m, "speed:\t\t%d\n", speed);
if (status & TP_EC_FAN_FULLSPEED)
/* 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)
- len += sprintf(p + len, "level:\t\tauto\n");
+ seq_printf(m, "level:\t\tauto\n");
else
- len += sprintf(p + len, "level:\t\t%d\n", status);
+ seq_printf(m, "level:\t\t%d\n", status);
break;
case TPACPI_FAN_NONE:
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) {
- len += sprintf(p + len, "commands:\tlevel <level>");
+ seq_printf(m, "commands:\tlevel <level>");
switch (fan_control_access_mode) {
case TPACPI_FAN_WR_ACPI_SFAN:
- len += sprintf(p + len, " (<level> is 0-7)\n");
+ seq_printf(m, " (<level> is 0-7)\n");
break;
default:
- len += sprintf(p + len, " (<level> is 0-7, "
+ seq_printf(m, " (<level> is 0-7, "
"auto, disengaged, full-speed)\n");
break;
}
}
if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
- len += sprintf(p + len, "commands:\tenable, disable\n"
+ seq_printf(m, "commands:\tenable, disable\n"
"commands:\twatchdog <timeout> (<timeout> "
"is 0 (off), 1-120 (seconds))\n");
if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
- len += sprintf(p + len, "commands:\tspeed <speed>"
+ seq_printf(m, "commands:\tspeed <speed>"
" (<speed> is 0-65535)\n");
- return len;
+ return 0;
}
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)
{
+ 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)
{
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);
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) {
printk(TPACPI_ERR "unable to create proc entry %s\n",
ibm->name);
ret = -ENODEV;
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;
}
@@ -8077,6 +8672,7 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.data = &brightness_driver_data,
},
{
+ .init = volume_init,
.data = &volume_driver_data,
},
{
@@ -8112,36 +8708,59 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp)
return -EINVAL;
}
-module_param(experimental, int, 0);
+module_param(experimental, int, 0444);
MODULE_PARM_DESC(experimental,
"Enables experimental features when non-zero");
module_param_named(debug, dbg_level, uint, 0);
MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
-module_param(force_load, bool, 0);
+module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load,
"Attempts to load the driver even on a "
"mis-identified ThinkPad when true");
-module_param_named(fan_control, fan_control_allowed, bool, 0);
+module_param_named(fan_control, fan_control_allowed, bool, 0444);
MODULE_PARM_DESC(fan_control,
"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,
"Selects brightness control strategy: "
"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,
"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,
"used for backwards compatibility with userspace, "
"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) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
@@ -8160,25 +8779,25 @@ TPACPI_PARAM(volume);
TPACPI_PARAM(fan);
#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_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0);
MODULE_PARM_DESC(wlsw_state,
"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_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0);
MODULE_PARM_DESC(bluetooth_state,
"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_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
MODULE_PARM_DESC(wwan_state,
"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_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
MODULE_PARM_DESC(uwb_state,
@@ -8371,6 +8990,7 @@ static int __init thinkpad_acpi_module_init(void)
PCI_VENDOR_ID_IBM;
tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT;
tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION;
+ tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev;
}
for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
ret = ibm_init(&ibms_init[i]);
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
new file mode 100644
index 000000000000..a350418e87ea
--- /dev/null
+++ b/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);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 177f8d767df4..e425a868cd3a 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -30,6 +30,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/device.h>
#include <linux/list.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
@@ -65,6 +66,7 @@ struct wmi_block {
acpi_handle handle;
wmi_notify_handler handler;
void *handler_data;
+ struct device *dev;
};
static struct wmi_block wmi_blocks;
@@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
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)
{
char tmp[16], guid_input[16];
@@ -555,6 +585,138 @@ bool wmi_has_guid(const char *guid_string)
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
*/
static __init acpi_status parse_wdg(acpi_handle handle)
@@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void)
if (result < 0) {
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;
}
@@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void)
struct list_head *p, *tmp;
struct wmi_block *wblock;
+ wmi_class_exit();
+
acpi_bus_unregister_driver(&acpi_wmi_driver);
list_for_each_safe(p, tmp, &wmi_blocks.list) {
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 83b8b5ac49c9..5314bf630bc4 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/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)
{
- acpi_handle handle = dev->data;
+ struct acpi_device *acpi_dev = dev->data;
+ acpi_handle handle = acpi_dev->handle;
struct acpi_buffer buffer;
int ret;
@@ -103,7 +104,8 @@ static int pnpacpi_set_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;
dev_dbg(&dev->dev, "disable resources\n");
@@ -121,6 +123,8 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
#ifdef CONFIG_ACPI_SLEEP
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;
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) ?
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)
{
- 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
-static struct pnp_protocol pnpacpi_protocol = {
+struct pnp_protocol pnpacpi_protocol = {
.name = "Plug and Play ACPI",
.get = pnpacpi_get_resources,
.set = pnpacpi_set_resources,
@@ -147,6 +154,7 @@ static struct pnp_protocol pnpacpi_protocol = {
.resume = pnpacpi_resume,
#endif
};
+EXPORT_SYMBOL(pnpacpi_protocol);
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)
return -ENOMEM;
- dev->data = device->handle;
+ dev->data = device;
/* .enabled means the device can decode the resources */
dev->active = device->status.enabled;
status = acpi_get_handle(device->handle, "_SRS", &temp);
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index ef3a2cd3a7a0..5702b2c8691f 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/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)
{
- acpi_handle handle = dev->data;
+ struct acpi_device *acpi_dev = dev->data;
+ acpi_handle handle = acpi_dev->handle;
acpi_status status;
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)
{
- acpi_handle handle = dev->data;
+ struct acpi_device *acpi_dev = dev->data;
+ acpi_handle handle = acpi_dev->handle;
acpi_status status;
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,
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;
int res_cnt = 0;
acpi_status status;
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index 6f8d8f971212..5066de5cfc0c 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -225,6 +225,12 @@ passive_store(struct device *dev, struct device_attribute *attr,
if (!sscanf(buf, "%d\n", &state))
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) {
mutex_lock(&thermal_list_lock);
list_for_each_entry(cdev, &thermal_cdev_list, node) {
@@ -235,6 +241,8 @@ passive_store(struct device *dev, struct device_attribute *attr,
cdev);
}
mutex_unlock(&thermal_list_lock);
+ if (!tz->passive_delay)
+ tz->passive_delay = 1000;
} else if (!state && tz->forced_passive) {
mutex_lock(&thermal_list_lock);
list_for_each_entry(cdev, &thermal_cdev_list, node) {
@@ -245,17 +253,12 @@ passive_store(struct device *dev, struct device_attribute *attr,
cdev);
}
mutex_unlock(&thermal_list_lock);
+ tz->passive_delay = 0;
}
tz->tc1 = 1;
tz->tc2 = 1;
- if (!tz->passive_delay)
- tz->passive_delay = 1000;
-
- if (!tz->polling_delay)
- tz->polling_delay = 10000;
-
tz->forced_passive = state;
thermal_zone_device_update(tz);
@@ -374,7 +377,7 @@ thermal_cooling_device_cur_state_store(struct device *dev,
if (!sscanf(buf, "%ld\n", &state))
return -EINVAL;
- if (state < 0)
+ if ((long)state < 0)
return -EINVAL;
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);
else if (tz->polling_delay)
thermal_zone_device_set_polling(tz, tz->polling_delay);
+ else
+ thermal_zone_device_set_polling(tz, 0);
mutex_unlock(&tz->lock);
}
EXPORT_SYMBOL(thermal_zone_device_update);