Commit 288f02bb authored by Linus Torvalds's avatar Linus Torvalds

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)
  ...
parents 8aedf8a6 aa96ce0a
Linux ACPI Custom Control Method How To
=======================================
Written by Zhang Rui <rui.zhang@intel.com>
Linux supports customizing ACPI control methods at runtime.
Users can use this to
1. override an existing method which may not work correctly,
or just for debugging purposes.
2. insert a completely new method in order to create a missing
method such as _OFF, _ON, _STA, _INI, etc.
For these cases, it is far simpler to dynamically install a single
control method rather than override the entire DSDT, because kernel
rebuild/reboot is not needed and test result can be got in minutes.
Note: Only ACPI METHOD can be overridden, any other object types like
"Device", "OperationRegion", are not recognized.
Note: The same ACPI control method can be overridden for many times,
and it's always the latest one that used by Linux/kernel.
1. override an existing method
a) get the ACPI table via ACPI sysfs I/F. e.g. to get the DSDT,
just run "cat /sys/firmware/acpi/tables/DSDT > /tmp/dsdt.dat"
b) disassemble the table by running "iasl -d dsdt.dat".
c) rewrite the ASL code of the method and save it in a new file,
d) package the new file (psr.asl) to an ACPI table format.
Here is an example of a customized \_SB._AC._PSR method,
DefinitionBlock ("", "SSDT", 1, "", "", 0x20080715)
{
External (ACON)
Method (\_SB_.AC._PSR, 0, NotSerialized)
{
Store ("In AC _PSR", Debug)
Return (ACON)
}
}
Note that the full pathname of the method in ACPI namespace
should be used.
And remember to use "External" to declare external objects.
e) assemble the file to generate the AML code of the method.
e.g. "iasl psr.asl" (psr.aml is generated as a result)
f) mount debugfs by "mount -t debugfs none /sys/kernel/debug"
g) override the old method via the debugfs by running
"cat /tmp/psr.aml > /sys/kernel/debug/acpi/custom_method"
2. insert a new method
This is easier than overriding an existing method.
We just need to create the ASL code of the method we want to
insert and then follow the step c) ~ g) in section 1.
3. undo your changes
The "undo" operation is not supported for a new inserted method
right now, i.e. we can not remove a method currently.
For an overrided method, in order to undo your changes, please
save a copy of the method original ASL code in step c) section 1,
and redo step c) ~ g) to override the method with the original one.
Note: We can use a kernel with multiple custom ACPI method running,
But each individual write to debugfs can implement a SINGLE
method override. i.e. if we want to insert/override multiple
ACPI methods, we need to redo step c) ~ g) for multiple times.
......@@ -474,3 +474,22 @@ Why: Obsoleted by the adt7475 driver.
Who: Jean Delvare <khali@linux-fr.org>
---------------------------
What: Support for lcd_switch and display_get in asus-laptop driver
When: March 2010
Why: These two features use non-standard interfaces. There are the
only features that really need multiple path to guess what's
the right method name on a specific laptop.
Removing them will allow to remove a lot of code an significantly
clean the drivers.
This will affect the backlight code which won't be able to know
if the backlight is on or off. The platform display file will also be
write only (like the one in eeepc-laptop).
This should'nt affect a lot of user because they usually know
when their display is on or off.
Who: Corentin Chary <corentin.chary@gmail.com>
----------------------------
ThinkPad ACPI Extras Driver
Version 0.23
April 10th, 2009
Version 0.24
December 11th, 2009
Borislav Deianov <borislav@users.sf.net>
Henrique de Moraes Holschuh <hmh@hmh.eng.br>
......@@ -460,6 +460,8 @@ event code Key Notes
For Lenovo ThinkPads with a new
BIOS, it has to be handled either
by the ACPI OSI, or by userspace.
The driver does the right thing,
never mess with this.
0x1011 0x10 FN+END Brightness down. See brightness
up for details.
......@@ -582,46 +584,15 @@ with hotkey_report_mode.
Brightness hotkey notes:
These are the current sane choices for brightness key mapping in
thinkpad-acpi:
Don't mess with the brightness hotkeys in a Thinkpad. If you want
notifications for OSD, use the sysfs backlight class event support.
For IBM and Lenovo models *without* ACPI backlight control (the ones on
which thinkpad-acpi will autoload its backlight interface by default,
and on which ACPI video does not export a backlight interface):
1. Don't enable or map the brightness hotkeys in thinkpad-acpi, as
these older firmware versions unfortunately won't respect the hotkey
mask for brightness keys anyway, and always reacts to them. This
usually work fine, unless X.org drivers are doing something to block
the BIOS. In that case, use (3) below. This is the default mode of
operation.
2. Enable the hotkeys, but map them to something else that is NOT
KEY_BRIGHTNESS_UP/DOWN or any other keycode that would cause
userspace to try to change the backlight level, and use that as an
on-screen-display hint.
3. IF AND ONLY IF X.org drivers find a way to block the firmware from
automatically changing the brightness, enable the hotkeys and map
them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN, and feed that to
something that calls xbacklight. thinkpad-acpi will not be able to
change brightness in that case either, so you should disable its
backlight interface.
For Lenovo models *with* ACPI backlight control:
1. Load up ACPI video and use that. ACPI video will report ACPI
events for brightness change keys. Do not mess with thinkpad-acpi
defaults in this case. thinkpad-acpi should not have anything to do
with backlight events in a scenario where ACPI video is loaded:
brightness hotkeys must be disabled, and the backlight interface is
to be kept disabled as well. This is the default mode of operation.
2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi,
and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process
these keys on userspace somehow (e.g. by calling xbacklight).
The driver will do this automatically if it detects that ACPI video
has been disabled.
The driver will issue KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN events
automatically for the cases were userspace has to do something to
implement brightness changes. When you override these events, you will
either fail to handle properly the ThinkPads that require explicit
action to change backlight brightness, or the ThinkPads that require
that no action be taken to work properly.
Bluetooth
......@@ -1121,25 +1092,61 @@ WARNING:
its level up and down at every change.
Volume control -- /proc/acpi/ibm/volume
---------------------------------------
Volume control
--------------
procfs: /proc/acpi/ibm/volume
ALSA: "ThinkPad Console Audio Control", default ID: "ThinkPadEC"
NOTE: by default, the volume control interface operates in read-only
mode, as it is supposed to be used for on-screen-display purposes.
The read/write mode can be enabled through the use of the
"volume_control=1" module parameter.
This feature allows volume control on ThinkPad models which don't have
a hardware volume knob. The available commands are:
NOTE: distros are urged to not enable volume_control by default, this
should be done by the local admin only. The ThinkPad UI is for the
console audio control to be done through the volume keys only, and for
the desktop environment to just provide on-screen-display feedback.
Software volume control should be done only in the main AC97/HDA
mixer.
This feature allows volume control on ThinkPad models with a digital
volume knob (when available, not all models have it), as well as
mute/unmute control. The available commands are:
echo up >/proc/acpi/ibm/volume
echo down >/proc/acpi/ibm/volume
echo mute >/proc/acpi/ibm/volume
echo unmute >/proc/acpi/ibm/volume
echo 'level <level>' >/proc/acpi/ibm/volume
The <level> number range is 0 to 15 although not all of them may be
The <level> number range is 0 to 14 although not all of them may be
distinct. The unmute the volume after the mute command, use either the
up or down command (the level command will not unmute the volume).
up or down command (the level command will not unmute the volume), or
the unmute command.
The current volume level and mute state is shown in the file.
The ALSA mixer interface to this feature is still missing, but patches
to add it exist. That problem should be addressed in the not so
distant future.
You can use the volume_capabilities parameter to tell the driver
whether your thinkpad has volume control or mute-only control:
volume_capabilities=1 for mixers with mute and volume control,
volume_capabilities=2 for mixers with only mute control.
If the driver misdetects the capabilities for your ThinkPad model,
please report this to ibm-acpi-devel@lists.sourceforge.net, so that we
can update the driver.
There are two strategies for volume control. To select which one
should be used, use the volume_mode module parameter: volume_mode=1
selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing
(so that volume/mute changes are remembered across shutdown/reboot).
The driver will operate in volume_mode=3 by default. If that does not
work well on your ThinkPad model, please report this to
ibm-acpi-devel@lists.sourceforge.net.
The driver supports the standard ALSA module parameters. If the ALSA
mixer is disabled, the driver will disable all volume functionality.
Fan control and monitoring: fan speed, fan enable/disable
......@@ -1405,6 +1412,7 @@ to enable more than one output class, just add their values.
0x0008 HKEY event interface, hotkeys
0x0010 Fan control
0x0020 Backlight brightness
0x0040 Audio mixer/volume control
There is also a kernel build option to enable more debugging
information, which may be necessary to debug driver problems.
......@@ -1465,3 +1473,9 @@ Sysfs interface changelog:
and it is always able to disable hot keys. Very old
thinkpads are properly supported. hotkey_bios_mask
is deprecated and marked for removal.
0x020600: Marker for backlight change event support.
0x020700: Support for mute-only mixers.
Volume control in read-only mode by default.
Marker for ALSA mixer support.
......@@ -206,6 +206,7 @@ passive
passive trip point for the zone. Activation is done by polling with
an interval of 1 second.
Unit: millidegrees Celsius
Valid values: 0 (disabled) or greater than 1000
RW, Optional
*****************************
......
......@@ -48,7 +48,7 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
* P4, Core and beyond CPUs
*/
if (c->x86_vendor == X86_VENDOR_INTEL &&
(c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 14)))
(c->x86 > 0xf || (c->x86 == 6 && c->x86_model >= 0x0f)))
flags->bm_control = 0;
}
EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
......
......@@ -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;
......
......@@ -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);
......
......@@ -180,7 +180,11 @@ struct acpi_object_method {
u8 sync_level;
union acpi_operand_object *mutex;
u8 *aml_start;
union {
ACPI_INTERNAL_METHOD implementation;
union acpi_operand_object *handler;
} extra;
u32 aml_length;
u8 thread_count;
acpi_owner_id owner_id;
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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) &&
......
......@@ -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
......
......@@ -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;
......
......@@ -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));
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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,10 +477,10 @@ 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,
ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
"%s: Return Package is larger than needed - "
"found %u, expected %u\n",
data->pathname, count,
expected_count));
}
......@@ -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 */
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.
* 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.
*/
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;
}
for (i = 0; i < count; i++) {
sub_package = *elements;
sub_elements = sub_package->package.elements;
......
This diff is collapsed.
......@@ -45,6 +45,7 @@
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
#include "acpredef.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsrepair2")
......@@ -73,6 +74,10 @@ static acpi_status
acpi_ns_repair_ALR(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
static acpi_status
acpi_ns_repair_FDE(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
static acpi_status
acpi_ns_repair_PSS(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr);
......@@ -88,9 +93,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
u32 sort_index,
u8 sort_direction, char *sort_key_name);
static acpi_status
acpi_ns_remove_null_elements(union acpi_operand_object *package);
static acpi_status
acpi_ns_sort_list(union acpi_operand_object **elements,
u32 count, u32 index, u8 sort_direction);
......@@ -104,17 +106,27 @@ acpi_ns_sort_list(union acpi_operand_object **elements,
* This table contains the names of the predefined methods for which we can
* perform more complex repairs.
*
* _ALR: Sort the list ascending by ambient_illuminance if necessary
* _PSS: Sort the list descending by Power if necessary
* _TSS: Sort the list descending by Power if necessary
* As necessary:
*
* _ALR: Sort the list ascending by ambient_illuminance
* _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs
* _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs
* _PSS: Sort the list descending by Power
* _TSS: Sort the list descending by Power
*/
static const struct acpi_repair_info acpi_ns_repairable_names[] = {
{"_ALR", acpi_ns_repair_ALR},
{"_FDE", acpi_ns_repair_FDE},
{"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */
{"_PSS", acpi_ns_repair_PSS},
{"_TSS", acpi_ns_repair_TSS},
{{0, 0, 0, 0}, NULL} /* Table terminator */
};
#define ACPI_FDE_FIELD_COUNT 5
#define ACPI_FDE_BYTE_BUFFER_SIZE 5
#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32))
/******************************************************************************
*
* FUNCTION: acpi_ns_complex_repairs
......@@ -213,6 +225,94 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data,
return (status);
}
/******************************************************************************
*
* FUNCTION: acpi_ns_repair_FDE
*
* PARAMETERS: Data - Pointer to validation data structure
* return_object_ptr - Pointer to the object returned from the
* evaluation of a method or object
*
* RETURN: Status. AE_OK if object is OK or was repaired successfully
*
* DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return
* value is a Buffer of 5 DWORDs. This function repairs a common
* problem where the return value is a Buffer of BYTEs, not
* DWORDs.
*
*****************************************************************************/
static acpi_status
acpi_ns_repair_FDE(struct acpi_predefined_data *data,
union acpi_operand_object **return_object_ptr)
{
union acpi_operand_object *return_object = *return_object_ptr;
union acpi_operand_object *buffer_object;
u8 *byte_buffer;
u32 *dword_buffer;
u32 i;
ACPI_FUNCTION_NAME(ns_repair_FDE);
switch (return_object->common.type) {
case ACPI_TYPE_BUFFER:
/* This is the expected type. Length should be (at least) 5 DWORDs */
if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) {
return (AE_OK);
}
/* We can only repair if we have exactly 5 BYTEs */
if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) {
ACPI_WARN_PREDEFINED((AE_INFO, data->pathname,
data->node_flags,
"Incorrect return buffer length %u, expected %u",
return_object->buffer.length,
ACPI_FDE_DWORD_BUFFER_SIZE));
return (AE_AML_OPERAND_TYPE);
}
/* Create the new (larger) buffer object */
buffer_object =
acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE);
if (!buffer_object) {
return (AE_NO_MEMORY);
}
/* Expand each byte to a DWORD */
byte_buffer = return_object->buffer.pointer;
dword_buffer =
ACPI_CAST_PTR(u32, buffer_object->buffer.pointer);
for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) {
*dword_buffer = (u32) *byte_buffer;
dword_buffer++;
byte_buffer++;
}
ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
"%s Expanded Byte Buffer to expected DWord Buffer\n",
data->pathname));
break;
default:
return (AE_AML_OPERAND_TYPE);
}
/* Delete the original return object, return the new buffer object */
acpi_ut_remove_reference(return_object);
*return_object_ptr = buffer_object;
data->flags |= ACPI_OBJECT_REPAIRED;
return (AE_OK);
}
/******************************************************************************
*
* FUNCTION: acpi_ns_repair_TSS
......@@ -345,6 +445,8 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
u32 previous_value;
acpi_status status;
ACPI_FUNCTION_NAME(ns_check_sorted_list);
/* The top-level object must be a package */
if (return_object->common.type != ACPI_TYPE_PACKAGE) {
......@@ -352,24 +454,10 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
}
/*
* Detect any NULL package elements and remove them from the
* package.
*
* TBD: We may want to do this for all predefined names that
* return a variable-length package of packages.
* NOTE: assumes list of sub-packages contains no NULL elements.
* Any NULL elements should have been removed by earlier call
* to acpi_ns_remove_null_elements.
*/
status = acpi_ns_remove_null_elements(return_object);
if (status == AE_NULL_ENTRY) {
ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags,
"NULL elements removed from package"));
/* Exit if package is now zero length */
if (!return_object->package.count) {
return (AE_NULL_ENTRY);
}
}
outer_elements = return_object->package.elements;
outer_element_count = return_object->package.count;
if (!outer_element_count) {
......@@ -422,10 +510,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
data->flags |= ACPI_OBJECT_REPAIRED;
ACPI_INFO_PREDEFINED((AE_INFO, data->pathname,
data->node_flags,
"Repaired unsorted list - now sorted by %s",
sort_key_name));
ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
"%s: Repaired unsorted list - now sorted by %s\n",
data->pathname, sort_key_name));
return (AE_OK);
}
......@@ -440,36 +527,63 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data,
*
* FUNCTION: acpi_ns_remove_null_elements
*
* PARAMETERS: obj_desc - A Package object
* PARAMETERS: Data - Pointer to validation data structure
* package_type - An acpi_return_package_types value
* obj_desc - A Package object
*
* RETURN: Status. AE_NULL_ENTRY means that one or more elements were
* removed.
* RETURN: None.
*
* DESCRIPTION: Remove all NULL package elements and update the package count.
* DESCRIPTION: Remove all NULL package elements from packages that contain
* a variable number of sub-packages.
*
*****************************************************************************/
static acpi_status
acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
void
acpi_ns_remove_null_elements(struct acpi_predefined_data *data,
u8 package_type,
union acpi_operand_object *obj_desc)
{
union acpi_operand_object **source;
union acpi_operand_object **dest;
acpi_status status = AE_OK;
u32 count;
u32 new_count;
u32 i;
ACPI_FUNCTION_NAME(ns_remove_null_elements);
/*
* PTYPE1 packages contain no subpackages.
* PTYPE2 packages contain a variable number of sub-packages. We can
* safely remove all NULL elements from the PTYPE2 packages.
*/
switch (package_type) {
case ACPI_PTYPE1_FIXED:
case ACPI_PTYPE1_VAR:
case ACPI_PTYPE1_OPTION:
return;
case ACPI_PTYPE2:
case ACPI_PTYPE2_COUNT:
case ACPI_PTYPE2_PKG_COUNT:
case ACPI_PTYPE2_FIXED:
case ACPI_PTYPE2_MIN:
case ACPI_PTYPE2_REV_FIXED:
break;
default:
return;
}
count = obj_desc->package.count;
new_count = count;
source = obj_desc->package.elements;
dest = source;
/* Examine all elements of the package object */
/* Examine all elements of the package object, remove nulls */
for (i = 0; i < count; i++) {
if (!*source) {
status = AE_NULL_ENTRY;
new_count--;
} else {
*dest = *source;
......@@ -478,15 +592,18 @@ acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc)
source++;
}
if (status == AE_NULL_ENTRY) {
/* Update parent package if any null elements were removed */
if (new_count < count) {
ACPI_DEBUG_PRINT((ACPI_DB_REPAIR,
"%s: Found and removed %u NULL elements\n",
data->pathname, (count - new_count)));
/* NULL terminate list and update the package count */
*dest = NULL;
obj_desc->package.count = new_count;
}
return (status);
}
/******************************************************************************
......
......@@ -671,24 +671,25 @@ acpi_ns_externalize_name(u32 internal_name_length,
/*******************************************************************************
*
* FUNCTION: acpi_ns_map_handle_to_node
* FUNCTION: acpi_ns_validate_handle
*
* PARAMETERS: Handle - Handle to be converted to an Node
* PARAMETERS: Handle - Handle to be validated and typecast to a
* namespace node.
*
* RETURN: A Name table entry pointer
* RETURN: A pointer to a namespace node
*
* DESCRIPTION: Convert a namespace handle to a real Node
* DESCRIPTION: Convert a namespace handle to a namespace node. Handles special
* cases for the root node.
*
* Note: Real integer handles would allow for more verification
* NOTE: Real integer handles would allow for more verification
* and keep all pointers within this subsystem - however this introduces
* more (and perhaps unnecessary) overhead.
*
* The current implemenation is basically a placeholder until such time comes
* that it is needed.
* more overhead and has not been necessary to this point. Drivers
* holding handles are typically notified before a node becomes invalid
* due to a table unload.
*
******************************************************************************/
struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle)
struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle)
{
ACPI_FUNCTION_ENTRY();
......@@ -708,42 +709,6 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle)
return (ACPI_CAST_PTR(struct acpi_namespace_node, handle));
}
/*******************************************************************************
*
* FUNCTION: acpi_ns_convert_entry_to_handle
*
* PARAMETERS: Node - Node to be converted to a Handle
*
* RETURN: A user handle
*
* DESCRIPTION: Convert a real Node to a namespace handle
*
******************************************************************************/
acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node)
{
/*
* Simple implementation for now;
*/
return ((acpi_handle) node);
/* Example future implementation ---------------------
if (!Node)
{
return (NULL);
}
if (Node == acpi_gbl_root_node)
{
return (ACPI_ROOT_OBJECT);
}
return ((acpi_handle) Node);
------------------------------------------------------*/
}
/*******************************************************************************
*
* FUNCTION: acpi_ns_terminate
......
......@@ -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;
......
......@@ -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);
......
......@@ -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:
......
......@@ -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 */
......
......@@ -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);
}
......
......@@ -326,8 +326,8 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type,
* 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.
*
......@@ -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
*
......
......@@ -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
......
......@@ -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");
......
......@@ -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;
}
......
......@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/debugfs.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
......@@ -195,6 +196,80 @@ static int param_get_trace_state(char *buffer, struct kernel_param *kp)
module_param_call(trace_state, param_set_trace_state, param_get_trace_state,
NULL, 0644);
/* --------------------------------------------------------------------------
DebugFS Interface
-------------------------------------------------------------------------- */
static ssize_t cm_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
static char *buf;
static int uncopied_bytes;
struct acpi_table_header table;
acpi_status status;
if (!(*ppos)) {
/* parse the table header to get the table length */
if (count <= sizeof(struct acpi_table_header))
return -EINVAL;
if (copy_from_user(&table, user_buf,
sizeof(struct acpi_table_header)))
return -EFAULT;
uncopied_bytes = table.length;
buf = kzalloc(uncopied_bytes, GFP_KERNEL);
if (!buf)
return -ENOMEM;
}
if (uncopied_bytes < count) {
kfree(buf);
return -EINVAL;
}
if (copy_from_user(buf + (*ppos), user_buf, count)) {
kfree(buf);
return -EFAULT;
}
uncopied_bytes -= count;
*ppos += count;
if (!uncopied_bytes) {
status = acpi_install_method(buf);
kfree(buf);
if (ACPI_FAILURE(status))
return -EINVAL;
add_taint(TAINT_OVERRIDDEN_ACPI_TABLE);
}
return count;
}
static const struct file_operations cm_fops = {
.write = cm_write,
};
static int acpi_debugfs_init(void)
{
struct dentry *acpi_dir, *cm_dentry;
acpi_dir = debugfs_create_dir("acpi", NULL);
if (!acpi_dir)
goto err;
cm_dentry = debugfs_create_file("custom_method", S_IWUGO,
acpi_dir, NULL, &cm_fops);
if (!cm_dentry)
goto err;
return 0;
err:
if (acpi_dir)
debugfs_remove(acpi_dir);
return -EINVAL;
}
/* --------------------------------------------------------------------------
FS Interface (/proc)
-------------------------------------------------------------------------- */
......@@ -286,7 +361,7 @@ static const struct file_operations acpi_system_debug_proc_fops = {
};
#endif
int __init acpi_debug_init(void)
int __init acpi_procfs_init(void)
{
#ifdef CONFIG_ACPI_PROCFS
struct proc_dir_entry *entry;
......@@ -321,3 +396,10 @@ int __init acpi_debug_init(void)
return 0;
#endif
}
int __init acpi_debug_init(void)
{
acpi_debugfs_init();
acpi_procfs_init();
return 0;
}
This diff is collapsed.
......@@ -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,
......
......@@ -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 };
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)
{
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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++) {
......
......@@ -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);
......
......@@ -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
......
......@@ -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
......@@ -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
......@@ -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;
......
......@@ -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 @@ static int asus_hotk_add(struct acpi_device *device)
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;
......
......@@ -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 @@ static int asus_hotk_add(struct acpi_device *device)
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
......
......@@ -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 @@ static int dell_setup_rfkill(void)
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 @@ static int __init dell_init(void)
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);
......
......@@ -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");
......
This diff is collapsed.
......@@ -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,13 +332,18 @@ 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 (!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);
......@@ -364,19 +367,20 @@ static void hp_wmi_notify(u32 value, void *context)
input_sync(hp_wmi_input_dev);
} else if (eventcode == 0x5) {
if (wifi_rfkill)
rfkill_set_sw_state(wifi_rfkill,
hp_wmi_wifi_state());
rfkill_set_states(wifi_rfkill,
hp_wmi_get_sw_state(HPWMI_WIFI),
hp_wmi_get_hw_state(HPWMI_WIFI));
if (bluetooth_rfkill)
rfkill_set_sw_state(bluetooth_rfkill,
hp_wmi_bluetooth_state());
rfkill_set_states(bluetooth_rfkill,
hp_wmi_get_sw_state(HPWMI_BLUETOOTH),
hp_wmi_get_hw_state(HPWMI_BLUETOOTH));
if (wwan_rfkill)
rfkill_set_sw_state(wwan_rfkill,
hp_wmi_wwan_state());
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 key pressed - %x\n",
eventcode);
} else
printk(KERN_INFO "HP WMI: Unknown response received\n");
}
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;
}
......
/*
* 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);
This diff is collapsed.
/*
* 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);
......@@ -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];
......@@ -554,6 +584,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
*/
......@@ -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) {
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -85,7 +85,8 @@
#define ACPI_LV_INIT 0x00000001
#define ACPI_LV_DEBUG_OBJECT 0x00000002
#define ACPI_LV_INFO 0x00000004
#define ACPI_LV_ALL_EXCEPTIONS 0x00000007
#define ACPI_LV_REPAIR 0x00000008
#define ACPI_LV_ALL_EXCEPTIONS 0x0000000F
/* Trace verbosity level 1 [Standard Trace Level] */
......@@ -143,6 +144,7 @@
#define ACPI_DB_INIT ACPI_DEBUG_LEVEL (ACPI_LV_INIT)
#define ACPI_DB_DEBUG_OBJECT ACPI_DEBUG_LEVEL (ACPI_LV_DEBUG_OBJECT)
#define ACPI_DB_INFO ACPI_DEBUG_LEVEL (ACPI_LV_INFO)
#define ACPI_DB_REPAIR ACPI_DEBUG_LEVEL (ACPI_LV_REPAIR)
#define ACPI_DB_ALL_EXCEPTIONS ACPI_DEBUG_LEVEL (ACPI_LV_ALL_EXCEPTIONS)
/* Trace level -- also used in the global "DebugLevel" */
......@@ -174,8 +176,8 @@
/* Defaults for debug_level, debug and normal */
#define ACPI_DEBUG_DEFAULT (ACPI_LV_INFO)
#define ACPI_NORMAL_DEFAULT (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT)
#define ACPI_DEBUG_DEFAULT (ACPI_LV_INFO | ACPI_LV_REPAIR)
#define ACPI_NORMAL_DEFAULT (ACPI_LV_INIT | ACPI_LV_DEBUG_OBJECT | ACPI_LV_REPAIR)
#define ACPI_DEBUG_ALL (ACPI_LV_AML_DISASSEMBLE | ACPI_LV_ALL_EXCEPTIONS | ACPI_LV_ALL)
#if defined (ACPI_DEBUG_OUTPUT) || !defined (ACPI_NO_ERROR_MESSAGES)
......
......@@ -47,7 +47,7 @@
/* Current ACPICA subsystem version in YYYYMMDD format */
#define ACPI_CA_VERSION 0x20091112
#define ACPI_CA_VERSION 0x20091214
#include "actypes.h"
#include "actbl.h"
......
......@@ -294,7 +294,7 @@ static inline void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx
#ifdef CONFIG_CPU_FREQ
void acpi_processor_ppc_init(void);
void acpi_processor_ppc_exit(void);
int acpi_processor_ppc_has_changed(struct acpi_processor *pr);
int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag);
extern int acpi_processor_get_bios_limit(int cpu, unsigned int *limit);
#else
static inline void acpi_processor_ppc_init(void)
......@@ -305,7 +305,8 @@ static inline void acpi_processor_ppc_exit(void)
{
return;
}
static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr,
int event_flag)
{
static unsigned int printout = 1;
if (printout) {
......
......@@ -240,7 +240,7 @@ extern int pnpacpi_disabled;
#define PXM_INVAL (-1)
#define NID_INVAL (-1)
int acpi_check_resource_conflict(struct resource *res);
int acpi_check_resource_conflict(const struct resource *res);
int acpi_check_region(resource_size_t start, resource_size_t n,
const char *name);
......@@ -253,10 +253,16 @@ void __init acpi_old_suspend_ordering(void);
void __init acpi_s4_no_nvs(void);
#endif /* CONFIG_PM_SLEEP */
struct acpi_osc_context {
char *uuid_str; /* uuid string */
int rev;
struct acpi_buffer cap; /* arg2/arg3 */
struct acpi_buffer ret; /* free by caller if success */
};
#define OSC_QUERY_TYPE 0
#define OSC_SUPPORT_TYPE 1
#define OSC_CONTROL_TYPE 2
#define OSC_SUPPORT_MASKS 0x1f
/* _OSC DW0 Definition */
#define OSC_QUERY_ENABLE 1
......@@ -265,12 +271,23 @@ void __init acpi_s4_no_nvs(void);
#define OSC_INVALID_REVISION_ERROR 8
#define OSC_CAPABILITIES_MASK_ERROR 16
acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context);
/* platform-wide _OSC bits */
#define OSC_SB_PAD_SUPPORT 1
#define OSC_SB_PPC_OST_SUPPORT 2
#define OSC_SB_PR3_SUPPORT 4
#define OSC_SB_CPUHP_OST_SUPPORT 8
#define OSC_SB_APEI_SUPPORT 16
/* PCI defined _OSC bits */
/* _OSC DW1 Definition (OS Support Fields) */
#define OSC_EXT_PCI_CONFIG_SUPPORT 1
#define OSC_ACTIVE_STATE_PWR_SUPPORT 2
#define OSC_CLOCK_PWR_CAPABILITY_SUPPORT 4
#define OSC_PCI_SEGMENT_GROUPS_SUPPORT 8
#define OSC_MSI_SUPPORT 16
#define OSC_PCI_SUPPORT_MASKS 0x1f
/* _OSC DW1 Definition (OS Control Fields) */
#define OSC_PCI_EXPRESS_NATIVE_HP_CONTROL 1
......@@ -279,7 +296,7 @@ void __init acpi_s4_no_nvs(void);
#define OSC_PCI_EXPRESS_AER_CONTROL 8
#define OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL 16
#define OSC_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
#define OSC_PCI_CONTROL_MASKS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
OSC_SHPC_NATIVE_HP_CONTROL | \
OSC_PCI_EXPRESS_PME_CONTROL | \
OSC_PCI_EXPRESS_AER_CONTROL | \
......
......@@ -334,6 +334,19 @@ extern struct pnp_protocol pnpbios_protocol;
#define pnp_device_is_pnpbios(dev) 0
#endif
#ifdef CONFIG_PNPACPI
extern struct pnp_protocol pnpacpi_protocol;
static inline struct acpi_device *pnp_acpi_device(struct pnp_dev *dev)
{
if (dev->protocol == &pnpacpi_protocol)
return dev->data;
return NULL;
}
#else
#define pnp_acpi_device(dev) 0
#endif
/* status */
#define PNP_READY 0x0000
#define PNP_ATTACHED 0x0001
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment