diff options
66 files changed, 4113 insertions, 1712 deletions
diff --git a/Documentation/acpi/method-customizing.txt b/Documentation/acpi/method-customizing.txt new file mode 100644 index 0000000..e628cd2 --- /dev/null +++ b/Documentation/acpi/method-customizing.txt @@ -0,0 +1,66 @@ +Linux ACPI Custom Control Method How To +======================================= + +Written by Zhang Rui <rui.zhang@intel.com> + + +Linux supports customizing ACPI control methods at runtime. + +Users can use this to +1. override an existing method which may not work correctly, + or just for debugging purposes. +2. insert a completely new method in order to create a missing + method such as _OFF, _ON, _STA, _INI, etc. +For these cases, it is far simpler to dynamically install a single +control method rather than override the entire DSDT, because kernel +rebuild/reboot is not needed and test result can be got in minutes. + +Note: Only ACPI METHOD can be overridden, any other object types like + "Device", "OperationRegion", are not recognized. +Note: The same ACPI control method can be overridden for many times, + and it's always the latest one that used by Linux/kernel. + +1. override an existing method + a) get the ACPI table via ACPI sysfs I/F. e.g. to get the DSDT, + just run "cat /sys/firmware/acpi/tables/DSDT > /tmp/dsdt.dat" + b) disassemble the table by running "iasl -d dsdt.dat". + c) rewrite the ASL code of the method and save it in a new file, + d) package the new file (psr.asl) to an ACPI table format. + Here is an example of a customized \_SB._AC._PSR method, + + DefinitionBlock ("", "SSDT", 1, "", "", 0x20080715) + { + External (ACON) + + Method (\_SB_.AC._PSR, 0, NotSerialized) + { + Store ("In AC _PSR", Debug) + Return (ACON) + } + } + Note that the full pathname of the method in ACPI namespace + should be used. + And remember to use "External" to declare external objects. + e) assemble the file to generate the AML code of the method. + e.g. "iasl psr.asl" (psr.aml is generated as a result) + f) mount debugfs by "mount -t debugfs none /sys/kernel/debug" + g) override the old method via the debugfs by running + "cat /tmp/psr.aml > /sys/kernel/debug/acpi/custom_method" + +2. insert a new method + This is easier than overriding an existing method. + We just need to create the ASL code of the method we want to + insert and then follow the step c) ~ g) in section 1. + +3. undo your changes + The "undo" operation is not supported for a new inserted method + right now, i.e. we can not remove a method currently. + For an overrided method, in order to undo your changes, please + save a copy of the method original ASL code in step c) section 1, + and redo step c) ~ g) to override the method with the original one. + + +Note: We can use a kernel with multiple custom ACPI method running, + But each individual write to debugfs can implement a SINGLE + method override. i.e. if we want to insert/override multiple + ACPI methods, we need to redo step c) ~ g) for multiple times. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 21ab935..870d190 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -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> + +---------------------------- diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index aafcaa6..169091f 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1,7 +1,7 @@ ThinkPad ACPI Extras Driver - Version 0.23 - April 10th, 2009 + Version 0.24 + December 11th, 2009 Borislav Deianov <borislav@users.sf.net> Henrique de Moraes Holschuh <hmh@hmh.eng.br> @@ -460,6 +460,8 @@ event code Key Notes For Lenovo ThinkPads with a new BIOS, it has to be handled either by the ACPI OSI, or by userspace. + The driver does the right thing, + never mess with this. 0x1011 0x10 FN+END Brightness down. See brightness up for details. @@ -582,46 +584,15 @@ with hotkey_report_mode. Brightness hotkey notes: -These are the current sane choices for brightness key mapping in -thinkpad-acpi: +Don't mess with the brightness hotkeys in a Thinkpad. If you want +notifications for OSD, use the sysfs backlight class event support. -For IBM and Lenovo models *without* ACPI backlight control (the ones on -which thinkpad-acpi will autoload its backlight interface by default, -and on which ACPI video does not export a backlight interface): - -1. Don't enable or map the brightness hotkeys in thinkpad-acpi, as - these older firmware versions unfortunately won't respect the hotkey - mask for brightness keys anyway, and always reacts to them. This - usually work fine, unless X.org drivers are doing something to block - the BIOS. In that case, use (3) below. This is the default mode of - operation. - -2. Enable the hotkeys, but map them to something else that is NOT - KEY_BRIGHTNESS_UP/DOWN or any other keycode that would cause - userspace to try to change the backlight level, and use that as an - on-screen-display hint. - -3. IF AND ONLY IF X.org drivers find a way to block the firmware from - automatically changing the brightness, enable the hotkeys and map - them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN, and feed that to - something that calls xbacklight. thinkpad-acpi will not be able to - change brightness in that case either, so you should disable its - backlight interface. - -For Lenovo models *with* ACPI backlight control: - -1. Load up ACPI video and use that. ACPI video will report ACPI - events for brightness change keys. Do not mess with thinkpad-acpi - defaults in this case. thinkpad-acpi should not have anything to do - with backlight events in a scenario where ACPI video is loaded: - brightness hotkeys must be disabled, and the backlight interface is - to be kept disabled as well. This is the default mode of operation. - -2. Do *NOT* load up ACPI video, enable the hotkeys in thinkpad-acpi, - and map them to KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN. Process - these keys on userspace somehow (e.g. by calling xbacklight). - The driver will do this automatically if it detects that ACPI video - has been disabled. +The driver will issue KEY_BRIGHTNESS_UP and KEY_BRIGHTNESS_DOWN events +automatically for the cases were userspace has to do something to +implement brightness changes. When you override these events, you will +either fail to handle properly the ThinkPads that require explicit +action to change backlight brightness, or the ThinkPads that require +that no action be taken to work properly. Bluetooth @@ -1121,25 +1092,61 @@ WARNING: its level up and down at every change. -Volume control -- /proc/acpi/ibm/volume ---------------------------------------- +Volume control +-------------- + +procfs: /proc/acpi/ibm/volume +ALSA: "ThinkPad Console Audio Control", default ID: "ThinkPadEC" + +NOTE: by default, the volume control interface operates in read-only +mode, as it is supposed to be used for on-screen-display purposes. +The read/write mode can be enabled through the use of the +"volume_control=1" module parameter. -This feature allows volume control on ThinkPad models which don't have -a hardware volume knob. The available commands are: +NOTE: distros are urged to not enable volume_control by default, this +should be done by the local admin only. The ThinkPad UI is for the +console audio control to be done through the volume keys only, and for +the desktop environment to just provide on-screen-display feedback. +Software volume control should be done only in the main AC97/HDA +mixer. + +This feature allows volume control on ThinkPad models with a digital +volume knob (when available, not all models have it), as well as +mute/unmute control. The available commands are: echo up >/proc/acpi/ibm/volume echo down >/proc/acpi/ibm/volume echo mute >/proc/acpi/ibm/volume + echo unmute >/proc/acpi/ibm/volume echo 'level <level>' >/proc/acpi/ibm/volume -The <level> number range is 0 to 15 although not all of them may be +The <level> number range is 0 to 14 although not all of them may be distinct. The unmute the volume after the mute command, use either the -up or down command (the level command will not unmute the volume). +up or down command (the level command will not unmute the volume), or +the unmute command. + The current volume level and mute state is shown in the file. -The ALSA mixer interface to this feature is still missing, but patches -to add it exist. That problem should be addressed in the not so -distant future. +You can use the volume_capabilities parameter to tell the driver +whether your thinkpad has volume control or mute-only control: +volume_capabilities=1 for mixers with mute and volume control, +volume_capabilities=2 for mixers with only mute control. + +If the driver misdetects the capabilities for your ThinkPad model, +please report this to ibm-acpi-devel@lists.sourceforge.net, so that we +can update the driver. + +There are two strategies for volume control. To select which one +should be used, use the volume_mode module parameter: volume_mode=1 +selects EC mode, and volume_mode=3 selects EC mode with NVRAM backing +(so that volume/mute changes are remembered across shutdown/reboot). + +The driver will operate in volume_mode=3 by default. If that does not +work well on your ThinkPad model, please report this to +ibm-acpi-devel@lists.sourceforge.net. + +The driver supports the standard ALSA module parameters. If the ALSA +mixer is disabled, the driver will disable all volume functionality. Fan control and monitoring: fan speed, fan enable/disable @@ -1405,6 +1412,7 @@ to enable more than one output class, just add their values. 0x0008 HKEY event interface, hotkeys 0x0010 Fan control 0x0020 Backlight brightness + 0x0040 Audio mixer/volume control There is also a kernel build option to enable more debugging information, which may be necessary to debug driver problems. @@ -1465,3 +1473,9 @@ Sysfs interface changelog: and it is always able to disable hot keys. Very old thinkpads are properly supported. hotkey_bios_mask is deprecated and marked for removal. + +0x020600: Marker for backlight change event support. + +0x020700: Support for mute-only mixers. + Volume control in read-only mode by default. + Marker for ALSA mixer support. diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index a87dc27..cb3d15b 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -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 ***************************** diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c index 59cdfa4..2e837f5 100644 --- a/arch/x86/kernel/acpi/cstate.c +++ b/arch/x86/kernel/acpi/cstate.c @@ -48,7 +48,7 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, * P4, Core and beyond CPUs */ 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); diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 0d2cdb8..97991ac 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -100,7 +100,8 @@ static void round_robin_cpu(unsigned int tsk_index) struct cpumask *pad_busy_cpus = to_cpumask(pad_busy_cpus_bits); cpumask_var_t tmp; int cpu; - unsigned long min_weight = -1, preferred_cpu; + unsigned long min_weight = -1; + unsigned long uninitialized_var(preferred_cpu); if (!alloc_cpumask_var(&tmp, GFP_KERNEL)) return; diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index ab83919..61edb15 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -296,6 +296,11 @@ acpi_ns_complex_repairs(struct acpi_predefined_data *data, acpi_status validate_status, union acpi_operand_object **return_object_ptr); +void +acpi_ns_remove_null_elements(struct acpi_predefined_data *data, + u8 package_type, + union acpi_operand_object *obj_desc); + /* * nssearch - Namespace searching and entry */ @@ -354,9 +359,7 @@ acpi_ns_externalize_name(u32 internal_name_length, const char *internal_name, u32 * converted_name_length, char **converted_name); -struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle); - -acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node); +struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle); void acpi_ns_terminate(void); diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index b39d682..64062b1 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -180,7 +180,11 @@ struct acpi_object_method { u8 sync_level; union acpi_operand_object *mutex; u8 *aml_start; - ACPI_INTERNAL_METHOD implementation; + union { + ACPI_INTERNAL_METHOD implementation; + union acpi_operand_object *handler; + } extra; + u32 aml_length; u8 thread_count; acpi_owner_id owner_id; diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 567a489..e786f9f 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -414,7 +414,7 @@ acpi_ds_call_control_method(struct acpi_thread_state *thread, /* Invoke an internal method if necessary */ if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { - status = obj_desc->method.implementation(next_walk_state); + status = obj_desc->method.extra.implementation(next_walk_state); if (status == AE_OK) { status = AE_CTRL_TERMINATE; } diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 10fc785..b40513d 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -212,18 +212,19 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, case ACPI_TYPE_BUFFER: /* - * These types we will allow, but we will change the type. This - * enables some existing code of the form: + * These types we will allow, but we will change the type. + * This enables some existing code of the form: * * Name (DEB, 0) * Scope (DEB) { ... } * - * Note: silently change the type here. On the second pass, we will report - * a warning + * Note: silently change the type here. On the second pass, + * we will report a warning */ ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)\n", - path, + "Type override - [%4.4s] had invalid type (%s) " + "for Scope operator, changed to type ANY\n", + acpi_ut_get_node_name(node), acpi_ut_get_type_name(node->type))); node->type = ACPI_TYPE_ANY; @@ -235,8 +236,10 @@ acpi_ds_load1_begin_op(struct acpi_walk_state * walk_state, /* All other types are an error */ ACPI_ERROR((AE_INFO, - "Invalid type (%s) for target of Scope operator [%4.4s] (Cannot override)", - acpi_ut_get_type_name(node->type), path)); + "Invalid type (%s) for target of " + "Scope operator [%4.4s] (Cannot override)", + acpi_ut_get_type_name(node->type), + acpi_ut_get_node_name(node))); return_ACPI_STATUS(AE_AML_OPERAND_TYPE); } @@ -697,15 +700,16 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, case ACPI_TYPE_BUFFER: /* - * These types we will allow, but we will change the type. This - * enables some existing code of the form: + * These types we will allow, but we will change the type. + * This enables some existing code of the form: * * Name (DEB, 0) * Scope (DEB) { ... } */ ACPI_WARNING((AE_INFO, - "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)", - buffer_ptr, + "Type override - [%4.4s] had invalid type (%s) " + "for Scope operator, changed to type ANY\n", + acpi_ut_get_node_name(node), acpi_ut_get_type_name(node->type))); node->type = ACPI_TYPE_ANY; @@ -717,9 +721,10 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state, /* All other types are an error */ ACPI_ERROR((AE_INFO, - "Invalid type (%s) for target of Scope operator [%4.4s]", + "Invalid type (%s) for target of " + "Scope operator [%4.4s] (Cannot override)", acpi_ut_get_type_name(node->type), - buffer_ptr)); + acpi_ut_get_node_name(node))); return (AE_AML_OPERAND_TYPE); } @@ -1047,9 +1052,22 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) } /* - * If we are executing a method, initialize the region + * The op_region is not fully parsed at this time. The only valid + * argument is the space_id. (We must save the address of the + * AML of the address and length operands) + * + * If we have a valid region, initialize it. The namespace is + * unlocked at this point. + * + * Need to unlock interpreter if it is locked (if we are running + * a control method), in order to allow _REG methods to be run + * during acpi_ev_initialize_region. */ if (walk_state->method_node) { + /* + * Executing a method: initialize the region and unlock + * the interpreter + */ status = acpi_ex_create_region(op->named.data, op->named.length, @@ -1058,21 +1076,17 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { return (status); } - } - /* - * The op_region is not fully parsed at this time. Only valid - * argument is the space_id. (We must save the address of the - * AML of the address and length operands) - */ + acpi_ex_exit_interpreter(); + } - /* - * If we have a valid region, initialize it - * Namespace is NOT locked at this point. - */ status = acpi_ev_initialize_region (acpi_ns_get_attached_object(node), FALSE); + if (walk_state->method_node) { + acpi_ex_enter_interpreter(); + } + if (ACPI_FAILURE(status)) { /* * If AE_NOT_EXIST is returned, it is not fatal diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 0bc807c..5336d91 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -718,7 +718,7 @@ acpi_ev_install_handler(acpi_handle obj_handle, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); if (!node) { return (AE_BAD_PARAMETER); } @@ -1087,7 +1087,7 @@ acpi_ev_reg_run(acpi_handle obj_handle, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); if (!node) { return (AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index cf29c49..ff16805 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -575,6 +575,21 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj, handler_obj = obj_desc->thermal_zone.handler; break; + case ACPI_TYPE_METHOD: + /* + * If we are executing module level code, the original + * Node's object was replaced by this Method object and we + * saved the handler in the method object. + * + * See acpi_ns_exec_module_code + */ + if (obj_desc->method. + flags & AOPOBJ_MODULE_LEVEL) { + handler_obj = + obj_desc->method.extra.handler; + } + break; + default: /* Ignore other objects */ break; diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 10b8543..2fe0809 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -259,7 +259,7 @@ acpi_install_notify_handler(acpi_handle device, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(device); + node = acpi_ns_validate_handle(device); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -425,7 +425,7 @@ acpi_remove_notify_handler(acpi_handle device, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(device); + node = acpi_ns_validate_handle(device); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index 4721f58..eed7a38 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -610,7 +610,7 @@ acpi_install_gpe_block(acpi_handle gpe_device, return (status); } - node = acpi_ns_map_handle_to_node(gpe_device); + node = acpi_ns_validate_handle(gpe_device); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -698,7 +698,7 @@ acpi_status acpi_remove_gpe_block(acpi_handle gpe_device) return (status); } - node = acpi_ns_map_handle_to_node(gpe_device); + node = acpi_ns_validate_handle(gpe_device); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index 7c3d2d3..c98aa7c 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -89,7 +89,7 @@ acpi_install_address_space_handler(acpi_handle device, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(device); + node = acpi_ns_validate_handle(device); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -155,7 +155,7 @@ acpi_remove_address_space_handler(acpi_handle device, /* Convert and validate the device handle */ - node = acpi_ns_map_handle_to_node(device); + node = acpi_ns_validate_handle(device); if (!node || ((node->type != ACPI_TYPE_DEVICE) && (node->type != ACPI_TYPE_PROCESSOR) && diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index 2f01142..3c456bd 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -375,6 +375,15 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_AML_MUTEX_NOT_ACQUIRED); } + /* Must have a valid thread ID */ + + if (!walk_state->thread) { + ACPI_ERROR((AE_INFO, + "Cannot release Mutex [%4.4s], null thread info", + acpi_ut_get_node_name(obj_desc->mutex.node))); + return_ACPI_STATUS(AE_AML_INTERNAL); + } + /* * The Mutex is owned, but this thread must be the owner. * Special case for Global Lock, any thread can release @@ -392,15 +401,6 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_AML_NOT_OWNER); } - /* Must have a valid thread ID */ - - if (!walk_state->thread) { - ACPI_ERROR((AE_INFO, - "Cannot release Mutex [%4.4s], null thread info", - acpi_ut_get_node_name(obj_desc->mutex.node))); - return_ACPI_STATUS(AE_AML_INTERNAL); - } - /* * The sync level of the mutex must be equal to the current sync level. In * other words, the current level means that at least one mutex at that diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index 9c3cdbe..d622ba7 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -165,7 +165,7 @@ acpi_status acpi_ns_root_initialize(void) obj_desc->method.method_flags = AML_METHOD_INTERNAL_ONLY; - obj_desc->method.implementation = + obj_desc->method.extra.implementation = acpi_ut_osi_implementation; #endif break; diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 2deb986..e37836e 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -180,7 +180,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle, return (AE_OK); } - this_node = acpi_ns_map_handle_to_node(obj_handle); + this_node = acpi_ns_validate_handle(obj_handle); if (!this_node) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid object handle %p\n", obj_handle)); diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index f771e97..af9fe91 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -381,6 +381,18 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, method_obj->method.next_object); type = acpi_ns_get_type(parent_node); + /* + * Get the region handler and save it in the method object. We may need + * this if an operation region declaration causes a _REG method to be run. + * + * We can't do this in acpi_ps_link_module_code because + * acpi_gbl_root_node->Object is NULL at PASS1. + */ + if ((type == ACPI_TYPE_DEVICE) && parent_node->object) { + method_obj->method.extra.handler = + parent_node->object->device.handler; + } + /* Must clear next_object (acpi_ns_attach_object needs the field) */ method_obj->method.next_object = NULL; @@ -415,6 +427,12 @@ acpi_ns_exec_module_code(union acpi_operand_object *method_obj, ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n", method_obj->method.aml_start)); + /* Delete a possible implicit return value (in slack mode) */ + + if (info->return_object) { + acpi_ut_remove_reference(info->return_object); + } + /* Detach the temporary method object */ acpi_ns_detach_object(parent_node); diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index af8e6bc..8f9a487 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -232,7 +232,7 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle, ACPI_FUNCTION_TRACE_PTR(ns_handle_to_pathname, target_handle); - node = acpi_ns_map_handle_to_node(target_handle); + node = acpi_ns_validate_handle(target_handle); if (!node) { return_ACPI_STATUS(AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index b05f429..d34fa59 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -216,29 +216,38 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, data->pathname = pathname; /* - * Check that the type of the return object is what is expected for - * this predefined name + * Check that the type of the main return object is what is expected + * for this predefined name */ status = acpi_ns_check_object_type(data, return_object_ptr, predefined->info.expected_btypes, ACPI_NOT_PACKAGE_ELEMENT); if (ACPI_FAILURE(status)) { - goto check_validation_status; + goto exit; } - /* For returned Package objects, check the type of all sub-objects */ - - if (return_object->common.type == ACPI_TYPE_PACKAGE) { + /* + * For returned Package objects, check the type of all sub-objects. + * Note: Package may have been newly created by call above. + */ + if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) { status = acpi_ns_check_package(data, return_object_ptr); + if (ACPI_FAILURE(status)) { + goto exit; + } } /* - * Perform additional, more complicated repairs on a per-name - * basis. + * The return object was OK, or it was successfully repaired above. + * Now make some additional checks such as verifying that package + * objects are sorted correctly (if required) or buffer objects have + * the correct data width (bytes vs. dwords). These repairs are + * performed on a per-name basis, i.e., the code is specific to + * particular predefined names. */ status = acpi_ns_complex_repairs(data, node, status, return_object_ptr); -check_validation_status: +exit: /* * If the object validation failed or if we successfully repaired one * or more objects, mark the parent node to suppress further warning @@ -427,6 +436,13 @@ acpi_ns_check_package(struct acpi_predefined_data *data, data->pathname, package->ret_info.type, return_object->package.count)); + /* + * For variable-length Packages, we can safely remove all embedded + * and trailing NULL package elements + */ + acpi_ns_remove_null_elements(data, package->ret_info.type, + return_object); + /* Extract package count and elements array */ elements = return_object->package.elements; @@ -461,11 +477,11 @@ acpi_ns_check_package(struct acpi_predefined_data *data, if (count < expected_count) { goto package_too_small; } else if (count > expected_count) { - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, - "Return Package is larger than needed - " - "found %u, expected %u", count, - expected_count)); + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Return Package is larger than needed - " + "found %u, expected %u\n", + data->pathname, count, + expected_count)); } /* Validate all elements of the returned package */ @@ -680,53 +696,18 @@ acpi_ns_check_package_list(struct acpi_predefined_data *data, union acpi_operand_object *sub_package; union acpi_operand_object **sub_elements; acpi_status status; - u8 non_trailing_null = FALSE; u32 expected_count; u32 i; u32 j; - /* Validate each sub-Package in the parent Package */ - + /* + * Validate each sub-Package in the parent Package + * + * NOTE: assumes list of sub-packages contains no NULL elements. + * Any NULL elements should have been removed by earlier call + * to acpi_ns_remove_null_elements. + */ for (i = 0; i < count; i++) { - /* - * Handling for NULL package elements. For now, we will simply allow - * a parent package with trailing NULL elements. This can happen if - * the package was defined to be longer than the initializer list. - * This is legal as per the ACPI specification. It is often used - * to allow for dynamic initialization of a Package. - * - * A future enhancement may be to simply truncate the package to - * remove the trailing NULL elements. - */ - if (!(*elements)) { - if (!non_trailing_null) { - - /* Ensure the remaining elements are all NULL */ - - for (j = 1; j < (count - i + 1); j++) { - if (elements[j]) { - non_trailing_null = TRUE; - } - } - - if (!non_trailing_null) { - - /* Ignore the trailing NULL elements */ - - return (AE_OK); - } - } - - /* There are trailing non-null elements, issue warning */ - - ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, - "Found NULL element at package index %u", - i)); - elements++; - continue; - } - sub_package = *elements; sub_elements = sub_package->package.elements; diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index d563f1a..4fd1bdb 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -45,13 +45,52 @@ #include "accommon.h" #include "acnamesp.h" #include "acinterp.h" -#include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsrepair") /******************************************************************************* * + * This module attempts to repair or convert objects returned by the + * predefined methods to an object type that is expected, as per the ACPI + * specification. The need for this code is dictated by the many machines that + * return incorrect types for the standard predefined methods. Performing these + * conversions here, in one place, eliminates the need for individual ACPI + * device drivers to do the same. Note: Most of these conversions are different + * than the internal object conversion routines used for implicit object + * conversion. + * + * The following conversions can be performed as necessary: + * + * Integer -> String + * Integer -> Buffer + * String -> Integer + * String -> Buffer + * Buffer -> Integer + * Buffer -> String + * Buffer -> Package of Integers + * Package -> Package of one Package + * + ******************************************************************************/ +/* Local prototypes */ +static acpi_status +acpi_ns_convert_to_integer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_string(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_package(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +/******************************************************************************* + * * FUNCTION: acpi_ns_repair_object * * PARAMETERS: Data - Pointer to validation data structure @@ -68,6 +107,7 @@ ACPI_MODULE_NAME("nsrepair") * not expected. * ******************************************************************************/ + acpi_status acpi_ns_repair_object(struct acpi_predefined_data *data, u32 expected_btypes, @@ -76,32 +116,206 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, { union acpi_operand_object *return_object = *return_object_ptr; union acpi_operand_object *new_object; - acpi_size length; acpi_status status; + ACPI_FUNCTION_NAME(ns_repair_object); + /* * At this point, we know that the type of the returned object was not * one of the expected types for this predefined name. Attempt to - * repair the object. Only a limited number of repairs are possible. + * repair the object by converting it to one of the expected object + * types for this predefined name. */ - switch (return_object->common.type) { + if (expected_btypes & ACPI_RTYPE_INTEGER) { + status = acpi_ns_convert_to_integer(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_STRING) { + status = acpi_ns_convert_to_string(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_BUFFER) { + status = acpi_ns_convert_to_buffer(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + if (expected_btypes & ACPI_RTYPE_PACKAGE) { + status = acpi_ns_convert_to_package(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; + } + } + + /* We cannot repair this object */ + + return (AE_AML_OPERAND_TYPE); + + object_repaired: + + /* Object was successfully repaired */ + + /* + * If the original object is a package element, we need to: + * 1. Set the reference count of the new object to match the + * reference count of the old object. + * 2. Decrement the reference count of the original object. + */ + if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { + new_object->common.reference_count = + return_object->common.reference_count; + + if (return_object->common.reference_count > 1) { + return_object->common.reference_count--; + } + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Converted %s to expected %s at index %u\n", + data->pathname, + acpi_ut_get_object_type_name(return_object), + acpi_ut_get_object_type_name(new_object), + package_index)); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Converted %s to expected %s\n", + data->pathname, + acpi_ut_get_object_type_name(return_object), + acpi_ut_get_object_type_name(new_object))); + } + + /* Delete old object, install the new return object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = new_object; + data->flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_integer + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a String/Buffer object to an Integer. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_convert_to_integer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_status status; + u64 value = 0; + u32 i; + + switch (original_object->common.type) { + case ACPI_TYPE_STRING: + + /* String-to-Integer conversion */ + + status = acpi_ut_strtoul64(original_object->string.pointer, + ACPI_ANY_BASE, &value); + if (ACPI_FAILURE(status)) { + return (status); + } + break; + case ACPI_TYPE_BUFFER: - /* Does the method/object legally return a string? */ + /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */ - if (!(expected_btypes & ACPI_RTYPE_STRING)) { + if (original_object->buffer.length > 8) { return (AE_AML_OPERAND_TYPE); } + /* Extract each buffer byte to create the integer */ + + for (i = 0; i < original_object->buffer.length; i++) { + value |= + ((u64) original_object->buffer. + pointer[i] << (i * 8)); + } + break; + + default: + return (AE_AML_OPERAND_TYPE); + } + + new_object = acpi_ut_create_integer_object(value); + if (!new_object) { + return (AE_NO_MEMORY); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_string + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a Integer/Buffer object to a String. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_convert_to_string(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_size length; + acpi_status status; + + switch (original_object->common.type) { + case ACPI_TYPE_INTEGER: + /* + * Integer-to-String conversion. Commonly, convert + * an integer of value 0 to a NULL string. The last element of + * _BIF and _BIX packages occasionally need this fix. + */ + if (original_object->integer.value == 0) { + + /* Allocate a new NULL string object */ + + new_object = acpi_ut_create_string_object(0); + if (!new_object) { + return (AE_NO_MEMORY); + } + } else { + status = + acpi_ex_convert_to_string(original_object, + &new_object, + ACPI_IMPLICIT_CONVERT_HEX); + if (ACPI_FAILURE(status)) { + return (status); + } + } + break; + + case ACPI_TYPE_BUFFER: /* - * Have a Buffer, expected a String, convert. Use a to_string + * Buffer-to-String conversion. Use a to_string * conversion, no transform performed on the buffer data. The best * example of this is the _BIF method, where the string data from * the battery is often (incorrectly) returned as buffer object(s). */ length = 0; - while ((length < return_object->buffer.length) && - (return_object->buffer.pointer[length])) { + while ((length < original_object->buffer.length) && + (original_object->buffer.pointer[length])) { length++; } @@ -117,94 +331,176 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, * terminated at Length+1. */ ACPI_MEMCPY(new_object->string.pointer, - return_object->buffer.pointer, length); + original_object->buffer.pointer, length); break; + default: + return (AE_AML_OPERAND_TYPE); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_buffer + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + acpi_status status; + union acpi_operand_object **elements; + u32 *dword_buffer; + u32 count; + u32 i; + + switch (original_object->common.type) { case ACPI_TYPE_INTEGER: + /* + * Integer-to-Buffer conversion. + * Convert the Integer to a packed-byte buffer. _MAT and other + * objects need this sometimes, if a read has been performed on a + * Field object that is less than or equal to the global integer + * size (32 or 64 bits). + */ + status = + acpi_ex_convert_to_buffer(original_object, &new_object); + if (ACPI_FAILURE(status)) { + return (status); + } + break; - /* 1) Does the method/object legally return a buffer? */ + case ACPI_TYPE_STRING: - if (expected_btypes & ACPI_RTYPE_BUFFER) { - /* - * Convert the Integer to a packed-byte buffer. _MAT needs - * this sometimes, if a read has been performed on a Field - * object that is less than or equal to the global integer - * size (32 or 64 bits). - */ - status = - acpi_ex_convert_to_buffer(return_object, - &new_object); - if (ACPI_FAILURE(status)) { - return (status); - } + /* String-to-Buffer conversion. Simple data copy */ + + new_object = + acpi_ut_create_buffer_object(original_object->string. + length); + if (!new_object) { + return (AE_NO_MEMORY); } - /* 2) Does the method/object legally return a string? */ + ACPI_MEMCPY(new_object->buffer.pointer, + original_object->string.pointer, + original_object->string.length); + break; + + case ACPI_TYPE_PACKAGE: + /* + * This case is often seen for predefined names that must return a + * Buffer object with multiple DWORD integers within. For example, + * _FDE and _GTM. The Package can be converted to a Buffer. + */ + + /* All elements of the Package must be integers */ - else if (expected_btypes & ACPI_RTYPE_STRING) { - /* - * The only supported Integer-to-String conversion is to convert - * an integer of value 0 to a NULL string. The last element of - * _BIF and _BIX packages occasionally need this fix. - */ - if (return_object->integer.value != 0) { + elements = original_object->package.elements; + count = original_object->package.count; + + for (i = 0; i < count; i++) { + if ((!*elements) || + ((*elements)->common.type != ACPI_TYPE_INTEGER)) { return (AE_AML_OPERAND_TYPE); } + elements++; + } - /* Allocate a new NULL string object */ + /* Create the new buffer object to replace the Package */ - new_object = acpi_ut_create_string_object(0); - if (!new_object) { - return (AE_NO_MEMORY); - } - } else { - return (AE_AML_OPERAND_TYPE); + new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count)); + if (!new_object) { + return (AE_NO_MEMORY); } - break; - default: + /* Copy the package elements (integers) to the buffer as DWORDs */ - /* We cannot repair this object */ + elements = original_object->package.elements; + dword_buffer = ACPI_CAST_PTR(u32, new_object->buffer.pointer); + + for (i = 0; i < count; i++) { + *dword_buffer = (u32) (*elements)->integer.value; + dword_buffer++; + elements++; + } + break; + default: return (AE_AML_OPERAND_TYPE); } - /* Object was successfully repaired */ + *return_object = new_object; + return (AE_OK); +} - /* - * If the original object is a package element, we need to: - * 1. Set the reference count of the new object to match the - * reference count of the old object. - * 2. Decrement the reference count of the original object. - */ - if (package_index != ACPI_NOT_PACKAGE_ELEMENT) { - new_object->common.reference_count = - return_object->common.reference_count; +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_to_package + * + * PARAMETERS: original_object - Object to be converted + * return_object - Where the new converted object is returned + * + * RETURN: Status. AE_OK if conversion was successful. + * + * DESCRIPTION: Attempt to convert a Buffer object to a Package. Each byte of + * the buffer is converted to a single integer package element. + * + ******************************************************************************/ - if (return_object->common.reference_count > 1) { - return_object->common.reference_count--; +static acpi_status +acpi_ns_convert_to_package(union acpi_operand_object *original_object, + union acpi_operand_object **return_object) +{ + union acpi_operand_object *new_object; + union acpi_operand_object **elements; + u32 length; + u8 *buffer; + + switch (original_object->common.type) { + case ACPI_TYPE_BUFFER: + + /* Buffer-to-Package conversion */ + + length = original_object->buffer.length; + new_object = acpi_ut_create_package_object(length); + if (!new_object) { + return (AE_NO_MEMORY); } - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Converted %s to expected %s at index %u", - acpi_ut_get_object_type_name - (return_object), - acpi_ut_get_object_type_name(new_object), - package_index)); - } else { - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Converted %s to expected %s", - acpi_ut_get_object_type_name - (return_object), - acpi_ut_get_object_type_name - (new_object))); - } + /* Convert each buffer byte to an integer package element */ - /* Delete old object, install the new return object */ + elements = new_object->package.elements; + buffer = original_object->buffer.pointer; - acpi_ut_remove_reference(return_object); - *return_object_ptr = new_object; - data->flags |= ACPI_OBJECT_REPAIRED; + while (length--) { + *elements = + acpi_ut_create_integer_object((u64) *buffer); + if (!*elements) { + acpi_ut_remove_reference(new_object); + return (AE_NO_MEMORY); + } + elements++; + buffer++; + } + break; + + default: + return (AE_AML_OPERAND_TYPE); + } + + *return_object = new_object; return (AE_OK); } @@ -238,6 +534,8 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data, { union acpi_operand_object *pkg_obj_desc; + ACPI_FUNCTION_NAME(ns_repair_package_list); + /* * Create the new outer package and populate it. The new package will * have a single element, the lone subpackage. @@ -254,8 +552,9 @@ acpi_ns_repair_package_list(struct acpi_predefined_data *data, *obj_desc_ptr = pkg_obj_desc; data->flags |= ACPI_OBJECT_REPAIRED; - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Repaired Incorrectly formed Package")); + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Repaired incorrectly formed Package\n", + data->pathname)); return (AE_OK); } diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index d07b686..f13691c 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -45,6 +45,7 @@ #include <acpi/acpi.h> #include "accommon.h" #include "acnamesp.h" +#include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsrepair2") @@ -74,6 +75,10 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); static acpi_status +acpi_ns_repair_FDE(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + +static acpi_status acpi_ns_repair_PSS(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); @@ -89,9 +94,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, u8 sort_direction, char *sort_key_name); static acpi_status -acpi_ns_remove_null_elements(union acpi_operand_object *package); - -static acpi_status acpi_ns_sort_list(union acpi_operand_object **elements, u32 count, u32 index, u8 sort_direction); @@ -104,17 +106,27 @@ acpi_ns_sort_list(union acpi_operand_object **elements, * This table contains the names of the predefined methods for which we can * perform more complex repairs. * - * _ALR: Sort the list ascending by ambient_illuminance if necessary - * _PSS: Sort the list descending by Power if necessary - * _TSS: Sort the list descending by Power if necessary + * As necessary: + * + * _ALR: Sort the list ascending by ambient_illuminance + * _FDE: Convert Buffer of BYTEs to a Buffer of DWORDs + * _GTM: Convert Buffer of BYTEs to a Buffer of DWORDs + * _PSS: Sort the list descending by Power + * _TSS: Sort the list descending by Power */ static const struct acpi_repair_info acpi_ns_repairable_names[] = { {"_ALR", acpi_ns_repair_ALR}, + {"_FDE", acpi_ns_repair_FDE}, + {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ {"_PSS", acpi_ns_repair_PSS}, {"_TSS", acpi_ns_repair_TSS}, {{0, 0, 0, 0}, NULL} /* Table terminator */ }; +#define ACPI_FDE_FIELD_COUNT 5 +#define ACPI_FDE_BYTE_BUFFER_SIZE 5 +#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32)) + /****************************************************************************** * * FUNCTION: acpi_ns_complex_repairs @@ -215,6 +227,94 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, /****************************************************************************** * + * FUNCTION: acpi_ns_repair_FDE + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return + * value is a Buffer of 5 DWORDs. This function repairs a common + * problem where the return value is a Buffer of BYTEs, not + * DWORDs. + * + *****************************************************************************/ + +static acpi_status +acpi_ns_repair_FDE(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr) +{ + union acpi_operand_object *return_object = *return_object_ptr; + union acpi_operand_object *buffer_object; + u8 *byte_buffer; + u32 *dword_buffer; + u32 i; + + ACPI_FUNCTION_NAME(ns_repair_FDE); + + switch (return_object->common.type) { + case ACPI_TYPE_BUFFER: + + /* This is the expected type. Length should be (at least) 5 DWORDs */ + + if (return_object->buffer.length >= ACPI_FDE_DWORD_BUFFER_SIZE) { + return (AE_OK); + } + + /* We can only repair if we have exactly 5 BYTEs */ + + if (return_object->buffer.length != ACPI_FDE_BYTE_BUFFER_SIZE) { + ACPI_WARN_PREDEFINED((AE_INFO, data->pathname, + data->node_flags, + "Incorrect return buffer length %u, expected %u", + return_object->buffer.length, + ACPI_FDE_DWORD_BUFFER_SIZE)); + + return (AE_AML_OPERAND_TYPE); + } + + /* Create the new (larger) buffer object */ + + buffer_object = + acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE); + if (!buffer_object) { + return (AE_NO_MEMORY); + } + + /* Expand each byte to a DWORD */ + + byte_buffer = return_object->buffer.pointer; + dword_buffer = + ACPI_CAST_PTR(u32, buffer_object->buffer.pointer); + + for (i = 0; i < ACPI_FDE_FIELD_COUNT; i++) { + *dword_buffer = (u32) *byte_buffer; + dword_buffer++; + byte_buffer++; + } + + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s Expanded Byte Buffer to expected DWord Buffer\n", + data->pathname)); + break; + + default: + return (AE_AML_OPERAND_TYPE); + } + + /* Delete the original return object, return the new buffer object */ + + acpi_ut_remove_reference(return_object); + *return_object_ptr = buffer_object; + + data->flags |= ACPI_OBJECT_REPAIRED; + return (AE_OK); +} + +/****************************************************************************** + * * FUNCTION: acpi_ns_repair_TSS * * PARAMETERS: Data - Pointer to validation data structure @@ -345,6 +445,8 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, u32 previous_value; acpi_status status; + ACPI_FUNCTION_NAME(ns_check_sorted_list); + /* The top-level object must be a package */ if (return_object->common.type != ACPI_TYPE_PACKAGE) { @@ -352,24 +454,10 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, } /* - * Detect any NULL package elements and remove them from the - * package. - * - * TBD: We may want to do this for all predefined names that - * return a variable-length package of packages. + * NOTE: assumes list of sub-packages contains no NULL elements. + * Any NULL elements should have been removed by earlier call + * to acpi_ns_remove_null_elements. */ - status = acpi_ns_remove_null_elements(return_object); - if (status == AE_NULL_ENTRY) { - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "NULL elements removed from package")); - - /* Exit if package is now zero length */ - - if (!return_object->package.count) { - return (AE_NULL_ENTRY); - } - } - outer_elements = return_object->package.elements; outer_element_count = return_object->package.count; if (!outer_element_count) { @@ -422,10 +510,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, data->flags |= ACPI_OBJECT_REPAIRED; - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, - data->node_flags, - "Repaired unsorted list - now sorted by %s", - sort_key_name)); + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Repaired unsorted list - now sorted by %s\n", + data->pathname, sort_key_name)); return (AE_OK); } @@ -440,36 +527,63 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, * * FUNCTION: acpi_ns_remove_null_elements * - * PARAMETERS: obj_desc - A Package object + * PARAMETERS: Data - Pointer to validation data structure + * package_type - An acpi_return_package_types value + * obj_desc - A Package object * - * RETURN: Status. AE_NULL_ENTRY means that one or more elements were - * removed. + * RETURN: None. * - * DESCRIPTION: Remove all NULL package elements and update the package count. + * DESCRIPTION: Remove all NULL package elements from packages that contain + * a variable number of sub-packages. * *****************************************************************************/ -static acpi_status -acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc) +void +acpi_ns_remove_null_elements(struct acpi_predefined_data *data, + u8 package_type, + union acpi_operand_object *obj_desc) { union acpi_operand_object **source; union acpi_operand_object **dest; - acpi_status status = AE_OK; u32 count; u32 new_count; u32 i; + ACPI_FUNCTION_NAME(ns_remove_null_elements); + + /* + * PTYPE1 packages contain no subpackages. + * PTYPE2 packages contain a variable number of sub-packages. We can + * safely remove all NULL elements from the PTYPE2 packages. + */ + switch (package_type) { + case ACPI_PTYPE1_FIXED: + case ACPI_PTYPE1_VAR: + case ACPI_PTYPE1_OPTION: + return; + + case ACPI_PTYPE2: + case ACPI_PTYPE2_COUNT: + case ACPI_PTYPE2_PKG_COUNT: + case ACPI_PTYPE2_FIXED: + case ACPI_PTYPE2_MIN: + case ACPI_PTYPE2_REV_FIXED: + break; + + default: + return; + } + count = obj_desc->package.count; new_count = count; source = obj_desc->package.elements; dest = source; - /* Examine all elements of the package object */ + /* Examine all elements of the package object, remove nulls */ for (i = 0; i < count; i++) { if (!*source) { - status = AE_NULL_ENTRY; new_count--; } else { *dest = *source; @@ -478,15 +592,18 @@ acpi_ns_remove_null_elements(union acpi_operand_object *obj_desc) source++; } - if (status == AE_NULL_ENTRY) { + /* Update parent package if any null elements were removed */ + + if (new_count < count) { + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: Found and removed %u NULL elements\n", + data->pathname, (count - new_count))); /* NULL terminate list and update the package count */ *dest = NULL; obj_desc->package.count = new_count; } - - return (status); } /****************************************************************************** diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index ea55ab4..47d91e6 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -671,24 +671,25 @@ acpi_ns_externalize_name(u32 internal_name_length, /******************************************************************************* * - * FUNCTION: acpi_ns_map_handle_to_node + * FUNCTION: acpi_ns_validate_handle * - * PARAMETERS: Handle - Handle to be converted to an Node + * PARAMETERS: Handle - Handle to be validated and typecast to a + * namespace node. * - * RETURN: A Name table entry pointer + * RETURN: A pointer to a namespace node * - * DESCRIPTION: Convert a namespace handle to a real Node + * DESCRIPTION: Convert a namespace handle to a namespace node. Handles special + * cases for the root node. * - * Note: Real integer handles would allow for more verification + * NOTE: Real integer handles would allow for more verification * and keep all pointers within this subsystem - however this introduces - * more (and perhaps unnecessary) overhead. - * - * The current implemenation is basically a placeholder until such time comes - * that it is needed. + * more overhead and has not been necessary to this point. Drivers + * holding handles are typically notified before a node becomes invalid + * due to a table unload. * ******************************************************************************/ -struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle) +struct acpi_namespace_node *acpi_ns_validate_handle(acpi_handle handle) { ACPI_FUNCTION_ENTRY(); @@ -710,42 +711,6 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle) /******************************************************************************* * - * FUNCTION: acpi_ns_convert_entry_to_handle - * - * PARAMETERS: Node - Node to be converted to a Handle - * - * RETURN: A user handle - * - * DESCRIPTION: Convert a real Node to a namespace handle - * - ******************************************************************************/ - -acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node) -{ - - /* - * Simple implementation for now; - */ - return ((acpi_handle) node); - -/* Example future implementation --------------------- - - if (!Node) - { - return (NULL); - } - - if (Node == acpi_gbl_root_node) - { - return (ACPI_ROOT_OBJECT); - } - - return ((acpi_handle) Node); -------------------------------------------------------*/ -} - -/******************************************************************************* - * * FUNCTION: acpi_ns_terminate * * PARAMETERS: none diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index f2bd1da..f0c0892 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -190,7 +190,7 @@ acpi_evaluate_object(acpi_handle handle, /* Convert and validate the device handle */ - info->prefix_node = acpi_ns_map_handle_to_node(handle); + info->prefix_node = acpi_ns_validate_handle(handle); if (!info->prefix_node) { status = AE_BAD_PARAMETER; goto cleanup; @@ -552,7 +552,7 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, return (status); } - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { return (status); @@ -729,7 +729,7 @@ acpi_attach_data(acpi_handle obj_handle, /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -775,7 +775,7 @@ acpi_detach_data(acpi_handle obj_handle, acpi_object_handler handler) /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -822,7 +822,7 @@ acpi_get_data(acpi_handle obj_handle, acpi_object_handler handler, void **data) /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(obj_handle); + node = acpi_ns_validate_handle(obj_handle); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index ddc84af..e611dd9 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -93,7 +93,7 @@ acpi_get_handle(acpi_handle parent, /* Convert a parent handle to a prefix node */ if (parent) { - prefix_node = acpi_ns_map_handle_to_node(parent); + prefix_node = acpi_ns_validate_handle(parent); if (!prefix_node) { return (AE_BAD_PARAMETER); } @@ -114,7 +114,7 @@ acpi_get_handle(acpi_handle parent, if (!ACPI_STRCMP(pathname, ACPI_NS_ROOT_PATH)) { *ret_handle = - acpi_ns_convert_entry_to_handle(acpi_gbl_root_node); + ACPI_CAST_PTR(acpi_handle, acpi_gbl_root_node); return (AE_OK); } } else if (!prefix_node) { @@ -129,7 +129,7 @@ acpi_get_handle(acpi_handle parent, status = acpi_ns_get_node(prefix_node, pathname, ACPI_NS_NO_UPSEARCH, &node); if (ACPI_SUCCESS(status)) { - *ret_handle = acpi_ns_convert_entry_to_handle(node); + *ret_handle = ACPI_CAST_PTR(acpi_handle, node); } return (status); @@ -186,7 +186,7 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer) return (status); } - node = acpi_ns_map_handle_to_node(handle); + node = acpi_ns_validate_handle(handle); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -291,7 +291,7 @@ acpi_get_object_info(acpi_handle handle, goto cleanup; } - node = acpi_ns_map_handle_to_node(handle); + node = acpi_ns_validate_handle(handle); if (!node) { (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return (AE_BAD_PARAMETER); diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index 4071bad..0cc6ba0 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -79,7 +79,7 @@ acpi_status acpi_get_id(acpi_handle handle, acpi_owner_id * ret_id) /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(handle); + node = acpi_ns_validate_handle(handle); if (!node) { (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return (AE_BAD_PARAMETER); @@ -132,7 +132,7 @@ acpi_status acpi_get_type(acpi_handle handle, acpi_object_type * ret_type) /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(handle); + node = acpi_ns_validate_handle(handle); if (!node) { (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return (AE_BAD_PARAMETER); @@ -182,7 +182,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle) /* Convert and validate the handle */ - node = acpi_ns_map_handle_to_node(handle); + node = acpi_ns_validate_handle(handle); if (!node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -191,7 +191,7 @@ acpi_status acpi_get_parent(acpi_handle handle, acpi_handle * ret_handle) /* Get the parent entry */ parent_node = acpi_ns_get_parent_node(node); - *ret_handle = acpi_ns_convert_entry_to_handle(parent_node); + *ret_handle = ACPI_CAST_PTR(acpi_handle, parent_node); /* Return exception if parent is null */ @@ -251,7 +251,7 @@ acpi_get_next_object(acpi_object_type type, /* Start search at the beginning of the specified scope */ - parent_node = acpi_ns_map_handle_to_node(parent); + parent_node = acpi_ns_validate_handle(parent); if (!parent_node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -260,7 +260,7 @@ acpi_get_next_object(acpi_object_type type, /* Non-null handle, ignore the parent */ /* Convert and validate the handle */ - child_node = acpi_ns_map_handle_to_node(child); + child_node = acpi_ns_validate_handle(child); if (!child_node) { status = AE_BAD_PARAMETER; goto unlock_and_exit; @@ -276,7 +276,7 @@ acpi_get_next_object(acpi_object_type type, } if (ret_handle) { - *ret_handle = acpi_ns_convert_entry_to_handle(node); + *ret_handle = ACPI_CAST_PTR(acpi_handle, node); } unlock_and_exit: diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 12934ad..d0c1b91 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -287,7 +287,8 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) /* Invoke an internal method if necessary */ if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { - status = info->obj_desc->method.implementation(walk_state); + status = + info->obj_desc->method.extra.implementation(walk_state); info->return_object = walk_state->return_desc; /* Cleanup states */ diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 395212b..f27feb4 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -104,7 +104,7 @@ acpi_rs_validate_parameters(acpi_handle device_handle, return_ACPI_STATUS(AE_BAD_PARAMETER); } - node = acpi_ns_map_handle_to_node(device_handle); + node = acpi_ns_validate_handle(device_handle); if (!node) { return_ACPI_STATUS(AE_BAD_PARAMETER); } diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 0f0c64b..f857c5e 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -323,11 +323,11 @@ acpi_ut_copy_ielement_to_eelement(u8 object_type, * RETURN: Status * * DESCRIPTION: This function is called to place a package object in a user - * buffer. A package object by definition contains other objects. + * buffer. A package object by definition contains other objects. * * The buffer is assumed to have sufficient space for the object. - * The caller must have verified the buffer length needed using the - * acpi_ut_get_object_size function before calling this function. + * The caller must have verified the buffer length needed using + * the acpi_ut_get_object_size function before calling this function. * ******************************************************************************/ @@ -382,12 +382,12 @@ acpi_ut_copy_ipackage_to_epackage(union acpi_operand_object *internal_object, * FUNCTION: acpi_ut_copy_iobject_to_eobject * * PARAMETERS: internal_object - The internal object to be converted - * buffer_ptr - Where the object is returned + * ret_buffer - Where the object is returned * * RETURN: Status * - * DESCRIPTION: This function is called to build an API object to be returned to - * the caller. + * DESCRIPTION: This function is called to build an API object to be returned + * to the caller. * ******************************************************************************/ @@ -626,7 +626,7 @@ acpi_ut_copy_epackage_to_ipackage(union acpi_object *external_object, * PARAMETERS: external_object - The external object to be converted * internal_object - Where the internal object is returned * - * RETURN: Status - the status of the call + * RETURN: Status * * DESCRIPTION: Converts an external object to an internal object. * @@ -665,7 +665,7 @@ acpi_ut_copy_eobject_to_iobject(union acpi_object *external_object, * * RETURN: Status * - * DESCRIPTION: Simple copy of one internal object to another. Reference count + * DESCRIPTION: Simple copy of one internal object to another. Reference count * of the destination object is preserved. * ******************************************************************************/ @@ -897,10 +897,11 @@ acpi_ut_copy_ielement_to_ielement(u8 object_type, * * FUNCTION: acpi_ut_copy_ipackage_to_ipackage * - * PARAMETERS: *source_obj - Pointer to the source package object - * *dest_obj - Where the internal object is returned + * PARAMETERS: source_obj - Pointer to the source package object + * dest_obj - Where the internal object is returned + * walk_state - Current Walk state descriptor * - * RETURN: Status - the status of the call + * RETURN: Status * * DESCRIPTION: This function is called to copy an internal package object * into another internal package object. @@ -953,9 +954,9 @@ acpi_ut_copy_ipackage_to_ipackage(union acpi_operand_object *source_obj, * * FUNCTION: acpi_ut_copy_iobject_to_iobject * - * PARAMETERS: walk_state - Current walk state - * source_desc - The internal object to be copied + * PARAMETERS: source_desc - The internal object to be copied * dest_desc - Where the copied object is returned + * walk_state - Current walk state * * RETURN: Status * diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 3f4602b..cada73f 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -831,7 +831,7 @@ static void acpi_battery_notify(struct acpi_device *device, u32 event) dev_name(&device->dev), event, acpi_battery_present(battery)); #ifdef CONFIG_ACPI_SYSFS_POWER - /* acpi_batter_update could remove power_supply object */ + /* acpi_battery_update could remove power_supply object */ if (battery->bat.dev) kobject_uevent(&battery->bat.dev->kobj, KOBJ_CHANGE); #endif diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 7411915..65f7e33 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -344,6 +344,152 @@ bool acpi_bus_can_wakeup(acpi_handle handle) EXPORT_SYMBOL(acpi_bus_can_wakeup); +static void acpi_print_osc_error(acpi_handle handle, + struct acpi_osc_context *context, char *error) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER}; + int i; + + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer))) + printk(KERN_DEBUG "%s\n", error); + else { + printk(KERN_DEBUG "%s:%s\n", (char *)buffer.pointer, error); + kfree(buffer.pointer); + } + printk(KERN_DEBUG"_OSC request data:"); + for (i = 0; i < context->cap.length; i += sizeof(u32)) + printk("%x ", *((u32 *)(context->cap.pointer + i))); + printk("\n"); +} + +static u8 hex_val(unsigned char c) +{ + return isdigit(c) ? c - '0' : toupper(c) - 'A' + 10; +} + +static acpi_status acpi_str_to_uuid(char *str, u8 *uuid) +{ + int i; + static int opc_map_to_uuid[16] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, + 24, 26, 28, 30, 32, 34}; + + if (strlen(str) != 36) + return AE_BAD_PARAMETER; + for (i = 0; i < 36; i++) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (str[i] != '-') + return AE_BAD_PARAMETER; + } else if (!isxdigit(str[i])) + return AE_BAD_PARAMETER; + } + for (i = 0; i < 16; i++) { + uuid[i] = hex_val(str[opc_map_to_uuid[i]]) << 4; + uuid[i] |= hex_val(str[opc_map_to_uuid[i] + 1]); + } + return AE_OK; +} + +acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[4]; + union acpi_object *out_obj; + u8 uuid[16]; + u32 errors; + + if (!context) + return AE_ERROR; + if (ACPI_FAILURE(acpi_str_to_uuid(context->uuid_str, uuid))) + return AE_ERROR; + context->ret.length = ACPI_ALLOCATE_BUFFER; + context->ret.pointer = NULL; + + /* Setting up input parameters */ + input.count = 4; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = 16; + in_params[0].buffer.pointer = uuid; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = context->rev; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = context->cap.length/sizeof(u32); + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = context->cap.length; + in_params[3].buffer.pointer = context->cap.pointer; + + status = acpi_evaluate_object(handle, "_OSC", &input, &context->ret); + if (ACPI_FAILURE(status)) + return status; + + /* return buffer should have the same length as cap buffer */ + if (context->ret.length != context->cap.length) + return AE_NULL_OBJECT; + + out_obj = context->ret.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + acpi_print_osc_error(handle, context, + "_OSC evaluation returned wrong type"); + status = AE_TYPE; + goto out_kfree; + } + /* Need to ignore the bit0 in result code */ + errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + if (errors) { + if (errors & OSC_REQUEST_ERROR) + acpi_print_osc_error(handle, context, + "_OSC request failed"); + if (errors & OSC_INVALID_UUID_ERROR) + acpi_print_osc_error(handle, context, + "_OSC invalid UUID"); + if (errors & OSC_INVALID_REVISION_ERROR) + acpi_print_osc_error(handle, context, + "_OSC invalid revision"); + if (errors & OSC_CAPABILITIES_MASK_ERROR) { + if (((u32 *)context->cap.pointer)[OSC_QUERY_TYPE] + & OSC_QUERY_ENABLE) + goto out_success; + status = AE_SUPPORT; + goto out_kfree; + } + status = AE_ERROR; + goto out_kfree; + } +out_success: + return AE_OK; + +out_kfree: + kfree(context->ret.pointer); + context->ret.pointer = NULL; + return status; +} +EXPORT_SYMBOL(acpi_run_osc); + +static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; +static void acpi_bus_osc_support(void) +{ + u32 capbuf[2]; + struct acpi_osc_context context = { + .uuid_str = sb_uuid_str, + .rev = 1, + .cap.length = 8, + .cap.pointer = capbuf, + }; + acpi_handle handle; + + capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; + capbuf[OSC_SUPPORT_TYPE] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ +#ifdef CONFIG_ACPI_PROCESSOR_AGGREGATOR + capbuf[OSC_SUPPORT_TYPE] |= OSC_SB_PAD_SUPPORT; +#endif + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) + return; + if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) + kfree(context.ret.pointer); + /* do we need to check the returned cap? Sounds no */ +} + /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ @@ -734,6 +880,8 @@ static int __init acpi_bus_init(void) status = acpi_ec_ecdt_probe(); /* Ignore result. Not having an ECDT is not fatal. */ + acpi_bus_osc_support(); + status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n"); diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 0c9c6a9..8a95e83 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -282,6 +282,13 @@ static int acpi_lid_send_state(struct acpi_device *device) if (ret == NOTIFY_DONE) ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); + if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { + /* + * It is also regarded as success if the notifier_chain + * returns NOTIFY_OK or NOTIFY_DONE. + */ + ret = 0; + } return ret; } diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index 8a690c3..cc421b7 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/moduleparam.h> +#include <linux/debugfs.h> #include <asm/uaccess.h> #include <acpi/acpi_drivers.h> @@ -196,6 +197,80 @@ module_param_call(trace_state, param_set_trace_state, param_get_trace_state, NULL, 0644); /* -------------------------------------------------------------------------- + DebugFS Interface + -------------------------------------------------------------------------- */ + +static ssize_t cm_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + static char *buf; + static int uncopied_bytes; + struct acpi_table_header table; + acpi_status status; + + if (!(*ppos)) { + /* parse the table header to get the table length */ + if (count <= sizeof(struct acpi_table_header)) + return -EINVAL; + if (copy_from_user(&table, user_buf, + sizeof(struct acpi_table_header))) + return -EFAULT; + uncopied_bytes = table.length; + buf = kzalloc(uncopied_bytes, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + if (uncopied_bytes < count) { + kfree(buf); + return -EINVAL; + } + + if (copy_from_user(buf + (*ppos), user_buf, count)) { + kfree(buf); + return -EFAULT; + } + + uncopied_bytes -= count; + *ppos += count; + + if (!uncopied_bytes) { + status = acpi_install_method(buf); + kfree(buf); + if (ACPI_FAILURE(status)) + return -EINVAL; + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); + } + + return count; +} + +static const struct file_operations cm_fops = { + .write = cm_write, +}; + +static int acpi_debugfs_init(void) +{ + struct dentry *acpi_dir, *cm_dentry; + + acpi_dir = debugfs_create_dir("acpi", NULL); + if (!acpi_dir) + goto err; + + cm_dentry = debugfs_create_file("custom_method", S_IWUGO, + acpi_dir, NULL, &cm_fops); + if (!cm_dentry) + goto err; + + return 0; + +err: + if (acpi_dir) + debugfs_remove(acpi_dir); + return -EINVAL; +} + +/* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ #ifdef CONFIG_ACPI_PROCFS @@ -286,7 +361,7 @@ static const struct file_operations acpi_system_debug_proc_fops = { }; #endif -int __init acpi_debug_init(void) +int __init acpi_procfs_init(void) { #ifdef CONFIG_ACPI_PROCFS struct proc_dir_entry *entry; @@ -321,3 +396,10 @@ int __init acpi_debug_init(void) return 0; #endif } + +int __init acpi_debug_init(void) +{ + acpi_debugfs_init(); + acpi_procfs_init(); + return 0; +} diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 30be3c1..bbc2c13 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -50,7 +50,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " " before undocking"); static struct atomic_notifier_head dock_notifier_list; -static char dock_device_name[] = "dock"; static const struct acpi_device_id dock_device_ids[] = { {"LNXDOCK", 0}, @@ -93,40 +92,30 @@ struct dock_dependent_device { * Dock Dependent device functions * *****************************************************************************/ /** - * alloc_dock_dependent_device - allocate and init a dependent device - * @handle: the acpi_handle of the dependent device + * add_dock_dependent_device - associate a device with the dock station + * @ds: The dock station + * @handle: handle of the dependent device * - * Allocate memory for a dependent device structure for a device referenced - * by the acpi handle + * Add the dependent device to the dock's dependent device list. */ -static struct dock_dependent_device * -alloc_dock_dependent_device(acpi_handle handle) +static int +add_dock_dependent_device(struct dock_station *ds, acpi_handle handle) { struct dock_dependent_device *dd; dd = kzalloc(sizeof(*dd), GFP_KERNEL); - if (dd) { - dd->handle = handle; - INIT_LIST_HEAD(&dd->list); - INIT_LIST_HEAD(&dd->hotplug_list); - } - return dd; -} + if (!dd) + return -ENOMEM; + + dd->handle = handle; + INIT_LIST_HEAD(&dd->list); + INIT_LIST_HEAD(&dd->hotplug_list); -/** - * add_dock_dependent_device - associate a device with the dock station - * @ds: The dock station - * @dd: The dependent device - * - * Add the dependent device to the dock's dependent device list. - */ -static void -add_dock_dependent_device(struct dock_station *ds, - struct dock_dependent_device *dd) -{ spin_lock(&ds->dd_lock); list_add_tail(&dd->list, &ds->dependent_devices); spin_unlock(&ds->dd_lock); + + return 0; } /** @@ -249,6 +238,7 @@ static int is_battery(acpi_handle handle) static int is_ejectable_bay(acpi_handle handle) { acpi_handle phandle; + if (!is_ejectable(handle)) return 0; if (is_battery(handle) || is_ata(handle)) @@ -275,14 +265,13 @@ int is_dock_device(acpi_handle handle) if (is_dock(handle)) return 1; - list_for_each_entry(dock_station, &dock_stations, sibling) { + + list_for_each_entry(dock_station, &dock_stations, sibling) if (find_dock_dependent_device(dock_station, handle)) return 1; - } return 0; } - EXPORT_SYMBOL_GPL(is_dock_device); /** @@ -305,8 +294,6 @@ static int dock_present(struct dock_station *ds) return 0; } - - /** * dock_create_acpi_device - add new devices to acpi * @handle - handle of the device to add @@ -320,7 +307,7 @@ static int dock_present(struct dock_station *ds) */ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) { - struct acpi_device *device = NULL; + struct acpi_device *device; struct acpi_device *parent_device; acpi_handle parent; int ret; @@ -337,8 +324,7 @@ static struct acpi_device * dock_create_acpi_device(acpi_handle handle) ret = acpi_bus_add(&device, parent_device, handle, ACPI_BUS_TYPE_DEVICE); if (ret) { - pr_debug("error adding bus, %x\n", - -ret); + pr_debug("error adding bus, %x\n", -ret); return NULL; } } @@ -364,7 +350,6 @@ static void dock_remove_acpi_device(acpi_handle handle) } } - /** * hotplug_dock_devices - insert or remove devices on the dock station * @ds: the dock station @@ -384,10 +369,9 @@ static void hotplug_dock_devices(struct dock_station *ds, u32 event) /* * First call driver specific hotplug functions */ - list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { + list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) if (dd->ops && dd->ops->handler) dd->ops->handler(dd->handle, event, dd->context); - } /* * Now make sure that an acpi_device is created for each @@ -426,6 +410,7 @@ static void dock_event(struct dock_station *ds, u32 event, int num) list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) if (dd->ops && dd->ops->uevent) dd->ops->uevent(dd->handle, event, dd->context); + if (num != DOCK_EVENT) kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); } @@ -456,8 +441,8 @@ static void eject_dock(struct dock_station *ds) arg.type = ACPI_TYPE_INTEGER; arg.integer.value = 1; - if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", - &arg_list, NULL))) + status = acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL); + if (ACPI_FAILURE(status)) pr_debug("Failed to evaluate _EJ0!\n"); } @@ -577,7 +562,6 @@ int register_dock_notifier(struct notifier_block *nb) return atomic_notifier_chain_register(&dock_notifier_list, nb); } - EXPORT_SYMBOL_GPL(register_dock_notifier); /** @@ -591,7 +575,6 @@ void unregister_dock_notifier(struct notifier_block *nb) atomic_notifier_chain_unregister(&dock_notifier_list, nb); } - EXPORT_SYMBOL_GPL(unregister_dock_notifier); /** @@ -636,7 +619,6 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops, return ret; } - EXPORT_SYMBOL_GPL(register_hotplug_dock_device); /** @@ -657,7 +639,6 @@ void unregister_hotplug_dock_device(acpi_handle handle) dock_del_hotplug_device(dock_station, dd); } } - EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); /** @@ -772,7 +753,7 @@ struct dock_data { static void acpi_dock_deferred_cb(void *context) { - struct dock_data *data = (struct dock_data *)context; + struct dock_data *data = context; dock_notify(data->handle, data->event, data->ds); kfree(data); @@ -782,23 +763,22 @@ static int acpi_dock_notifier_call(struct notifier_block *this, unsigned long event, void *data) { struct dock_station *dock_station; - acpi_handle handle = (acpi_handle)data; + acpi_handle handle = data; if (event != ACPI_NOTIFY_BUS_CHECK && event != ACPI_NOTIFY_DEVICE_CHECK && event != ACPI_NOTIFY_EJECT_REQUEST) return 0; list_for_each_entry(dock_station, &dock_stations, sibling) { if (dock_station->handle == handle) { - struct dock_data *dock_data; + struct dock_data *dd; - dock_data = kmalloc(sizeof(*dock_data), GFP_KERNEL); - if (!dock_data) + dd = kmalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) return 0; - dock_data->handle = handle; - dock_data->event = event; - dock_data->ds = dock_station; - acpi_os_hotplug_execute(acpi_dock_deferred_cb, - dock_data); + dd->handle = handle; + dd->event = event; + dd->ds = dock_station; + acpi_os_hotplug_execute(acpi_dock_deferred_cb, dd); return 0 ; } } @@ -826,7 +806,6 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_status status; acpi_handle tmp, parent; struct dock_station *ds = context; - struct dock_dependent_device *dd; status = acpi_bus_get_ejd(handle, &tmp); if (ACPI_FAILURE(status)) { @@ -840,11 +819,9 @@ find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) goto fdd_out; } - if (tmp == ds->handle) { - dd = alloc_dock_dependent_device(handle); - if (dd) - add_dock_dependent_device(ds, dd); - } + if (tmp == ds->handle) + add_dock_dependent_device(ds, handle); + fdd_out: return AE_OK; } @@ -857,8 +834,7 @@ static ssize_t show_docked(struct device *dev, { struct acpi_device *tmp; - struct dock_station *dock_station = *((struct dock_station **) - dev->platform_data); + struct dock_station *dock_station = dev->platform_data; if (ACPI_SUCCESS(acpi_bus_get_device(dock_station->handle, &tmp))) return snprintf(buf, PAGE_SIZE, "1\n"); @@ -872,8 +848,7 @@ static DEVICE_ATTR(docked, S_IRUGO, show_docked, NULL); static ssize_t show_flags(struct device *dev, struct device_attribute *attr, char *buf) { - struct dock_station *dock_station = *((struct dock_station **) - dev->platform_data); + struct dock_station *dock_station = dev->platform_data; return snprintf(buf, PAGE_SIZE, "%d\n", dock_station->flags); } @@ -886,8 +861,7 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; - struct dock_station *dock_station = *((struct dock_station **) - dev->platform_data); + struct dock_station *dock_station = dev->platform_data; if (!count) return -EINVAL; @@ -905,8 +879,7 @@ static ssize_t show_dock_uid(struct device *dev, struct device_attribute *attr, char *buf) { unsigned long long lbuf; - struct dock_station *dock_station = *((struct dock_station **) - dev->platform_data); + struct dock_station *dock_station = dev->platform_data; acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf); if (ACPI_FAILURE(status)) @@ -919,8 +892,7 @@ static DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); static ssize_t show_dock_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct dock_station *dock_station = *((struct dock_station **) - dev->platform_data); + struct dock_station *dock_station = dev->platform_data; char *type; if (dock_station->flags & DOCK_IS_DOCK) @@ -936,6 +908,19 @@ static ssize_t show_dock_type(struct device *dev, } static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL); +static struct attribute *dock_attributes[] = { + &dev_attr_docked.attr, + &dev_attr_flags.attr, + &dev_attr_undock.attr, + &dev_attr_uid.attr, + &dev_attr_type.attr, + NULL +}; + +static struct attribute_group dock_attribute_group = { + .attrs = dock_attributes +}; + /** * dock_add - add a new dock station * @handle: the dock station handle @@ -945,39 +930,30 @@ static DEVICE_ATTR(type, S_IRUGO, show_dock_type, NULL); */ static int dock_add(acpi_handle handle) { - int ret; - struct dock_dependent_device *dd; - struct dock_station *dock_station; - struct platform_device *dock_device; + int ret, id; + struct dock_station ds, *dock_station; + struct platform_device *dd; + + id = dock_station_count; + dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds)); + if (IS_ERR(dd)) + return PTR_ERR(dd); + + dock_station = dd->dev.platform_data; - /* allocate & initialize the dock_station private data */ - dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); - if (!dock_station) - return -ENOMEM; dock_station->handle = handle; + dock_station->dock_device = dd; dock_station->last_dock_time = jiffies - HZ; - INIT_LIST_HEAD(&dock_station->dependent_devices); - INIT_LIST_HEAD(&dock_station->hotplug_devices); - INIT_LIST_HEAD(&dock_station->sibling); - spin_lock_init(&dock_station->dd_lock); + mutex_init(&dock_station->hp_lock); + spin_lock_init(&dock_station->dd_lock); + INIT_LIST_HEAD(&dock_station->sibling); + INIT_LIST_HEAD(&dock_station->hotplug_devices); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); - - /* initialize platform device stuff */ - dock_station->dock_device = - platform_device_register_simple(dock_device_name, - dock_station_count, NULL, 0); - dock_device = dock_station->dock_device; - if (IS_ERR(dock_device)) { - kfree(dock_station); - dock_station = NULL; - return PTR_ERR(dock_device); - } - platform_device_add_data(dock_device, &dock_station, - sizeof(struct dock_station *)); + INIT_LIST_HEAD(&dock_station->dependent_devices); /* we want the dock device to send uevents */ - dev_set_uevent_suppress(&dock_device->dev, 0); + dev_set_uevent_suppress(&dd->dev, 0); if (is_dock(handle)) dock_station->flags |= DOCK_IS_DOCK; @@ -986,47 +962,9 @@ static int dock_add(acpi_handle handle) if (is_battery(handle)) dock_station->flags |= DOCK_IS_BAT; - ret = device_create_file(&dock_device->dev, &dev_attr_docked); - if (ret) { - printk(KERN_ERR "Error %d adding sysfs file\n", ret); - platform_device_unregister(dock_device); - kfree(dock_station); - dock_station = NULL; - return ret; - } - ret = device_create_file(&dock_device->dev, &dev_attr_undock); - if (ret) { - printk(KERN_ERR "Error %d adding sysfs file\n", ret); - device_remove_file(&dock_device->dev, &dev_attr_docked); - platform_device_unregister(dock_device); - kfree(dock_station); - dock_station = NULL; - return ret; - } - ret = device_create_file(&dock_device->dev, &dev_attr_uid); - if (ret) { - printk(KERN_ERR "Error %d adding sysfs file\n", ret); - device_remove_file(&dock_device->dev, &dev_attr_docked); - device_remove_file(&dock_device->dev, &dev_attr_undock); - platform_device_unregister(dock_device); - kfree(dock_station); - dock_station = NULL; - return ret; - } - ret = device_create_file(&dock_device->dev, &dev_attr_flags); - if (ret) { - printk(KERN_ERR "Error %d adding sysfs file\n", ret); - device_remove_file(&dock_device->dev, &dev_attr_docked); - device_remove_file(&dock_device->dev, &dev_attr_undock); - device_remove_file(&dock_device->dev, &dev_attr_uid); - platform_device_unregister(dock_device); - kfree(dock_station); - dock_station = NULL; - return ret; - } - ret = device_create_file(&dock_device->dev, &dev_attr_type); + ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); if (ret) - printk(KERN_ERR"Error %d adding sysfs file\n", ret); + goto err_unregister; /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, @@ -1034,58 +972,43 @@ static int dock_add(acpi_handle handle) dock_station, NULL); /* add the dock station as a device dependent on itself */ - dd = alloc_dock_dependent_device(handle); - if (!dd) { - kfree(dock_station); - dock_station = NULL; - ret = -ENOMEM; - goto dock_add_err_unregister; - } - add_dock_dependent_device(dock_station, dd); + ret = add_dock_dependent_device(dock_station, handle); + if (ret) + goto err_rmgroup; dock_station_count++; list_add(&dock_station->sibling, &dock_stations); return 0; -dock_add_err_unregister: - device_remove_file(&dock_device->dev, &dev_attr_type); - device_remove_file(&dock_device->dev, &dev_attr_docked); - device_remove_file(&dock_device->dev, &dev_attr_undock); - device_remove_file(&dock_device->dev, &dev_attr_uid); - device_remove_file(&dock_device->dev, &dev_attr_flags); - platform_device_unregister(dock_device); - kfree(dock_station); - dock_station = NULL; +err_rmgroup: + sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); +err_unregister: + platform_device_unregister(dd); + printk(KERN_ERR "%s encountered error %d\n", __func__, ret); return ret; } /** * dock_remove - free up resources related to the dock station */ -static int dock_remove(struct dock_station *dock_station) +static int dock_remove(struct dock_station *ds) { struct dock_dependent_device *dd, *tmp; - struct platform_device *dock_device = dock_station->dock_device; + struct platform_device *dock_device = ds->dock_device; if (!dock_station_count) return 0; /* remove dependent devices */ - list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, - list) - kfree(dd); + list_for_each_entry_safe(dd, tmp, &ds->dependent_devices, list) + kfree(dd); + + list_del(&ds->sibling); /* cleanup sysfs */ - device_remove_file(&dock_device->dev, &dev_attr_type); - device_remove_file(&dock_device->dev, &dev_attr_docked); - device_remove_file(&dock_device->dev, &dev_attr_undock); - device_remove_file(&dock_device->dev, &dev_attr_uid); - device_remove_file(&dock_device->dev, &dev_attr_flags); + sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group); platform_device_unregister(dock_device); - /* free dock station memory */ - kfree(dock_station); - dock_station = NULL; return 0; } @@ -1103,11 +1026,10 @@ find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status = AE_OK; - if (is_dock(handle)) { - if (dock_add(handle) >= 0) { + if (is_dock(handle)) + if (dock_add(handle) >= 0) status = AE_CTRL_TERMINATE; - } - } + return status; } @@ -1145,8 +1067,7 @@ static int __init dock_init(void) static void __exit dock_exit(void) { - struct dock_station *dock_station; - struct dock_station *tmp; + struct dock_station *tmp, *dock_station; unregister_acpi_bus_notifier(&dock_acpi_notifier); list_for_each_entry_safe(dock_station, tmp, &dock_stations, sibling) diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index f419849..acf2ab2 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -267,7 +267,7 @@ static int acpi_fan_add(struct acpi_device *device) goto end; } - dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id); + dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id); device->driver_data = cdev; result = sysfs_create_link(&device->dev.kobj, diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 2be2fb6..7ad48df 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -28,6 +28,7 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/acpi.h> +#include <linux/numa.h> #include <acpi/acpi_bus.h> #define PREFIX "ACPI: " @@ -40,14 +41,14 @@ static nodemask_t nodes_found_map = NODE_MASK_NONE; /* maps to convert between proximity domain and logical node ID */ static int pxm_to_node_map[MAX_PXM_DOMAINS] - = { [0 ... MAX_PXM_DOMAINS - 1] = NID_INVAL }; + = { [0 ... MAX_PXM_DOMAINS - 1] = NUMA_NO_NODE }; static int node_to_pxm_map[MAX_NUMNODES] - = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; + = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; int pxm_to_node(int pxm) { if (pxm < 0) - return NID_INVAL; + return NUMA_NO_NODE; return pxm_to_node_map[pxm]; } @@ -68,9 +69,9 @@ int acpi_map_pxm_to_node(int pxm) { int node = pxm_to_node_map[pxm]; - if (node < 0){ + if (node < 0) { if (nodes_weight(nodes_found_map) >= MAX_NUMNODES) - return NID_INVAL; + return NUMA_NO_NODE; node = first_unset_node(nodes_found_map); __acpi_map_pxm_to_node(pxm, node); node_set(node, nodes_found_map); @@ -79,16 +80,6 @@ int acpi_map_pxm_to_node(int pxm) return node; } -#if 0 -void __cpuinit acpi_unmap_pxm_to_node(int node) -{ - int pxm = node_to_pxm_map[node]; - pxm_to_node_map[pxm] = NID_INVAL; - node_to_pxm_map[node] = PXM_INVAL; - node_clear(node, nodes_found_map); -} -#endif /* 0 */ - static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) { diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7c1c59e..02e8464 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1118,7 +1118,7 @@ __setup("acpi_enforce_resources=", acpi_enforce_resources_setup); /* Check for resource conflicts between ACPI OperationRegions and native * drivers */ -int acpi_check_resource_conflict(struct resource *res) +int acpi_check_resource_conflict(const struct resource *res) { struct acpi_res_list *res_list_elem; int ioport; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1af8081..101cce3 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -202,72 +202,24 @@ static void acpi_pci_bridge_scan(struct acpi_device *device) } } -static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, - 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; +static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766"; static acpi_status acpi_pci_run_osc(acpi_handle handle, const u32 *capbuf, u32 *retval) { + struct acpi_osc_context context = { + .uuid_str = pci_osc_uuid_str, + .rev = 1, + .cap.length = 12, + .cap.pointer = (void *)capbuf, + }; acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *out_obj; - u32 errors; - - /* Setting up input parameters */ - input.count = 4; - input.pointer = in_params; - in_params[0].type = ACPI_TYPE_BUFFER; - in_params[0].buffer.length = 16; - in_params[0].buffer.pointer = OSC_UUID; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = 1; - in_params[2].type = ACPI_TYPE_INTEGER; - in_params[2].integer.value = 3; - in_params[3].type = ACPI_TYPE_BUFFER; - in_params[3].buffer.length = 12; - in_params[3].buffer.pointer = (u8 *)capbuf; - - status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE(status)) - return status; - if (!output.length) - return AE_NULL_OBJECT; - - out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER) { - printk(KERN_DEBUG "_OSC evaluation returned wrong type\n"); - status = AE_TYPE; - goto out_kfree; - } - /* Need to ignore the bit0 in result code */ - errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); - if (errors) { - if (errors & OSC_REQUEST_ERROR) - printk(KERN_DEBUG "_OSC request failed\n"); - if (errors & OSC_INVALID_UUID_ERROR) - printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (errors & OSC_INVALID_REVISION_ERROR) - printk(KERN_DEBUG "_OSC invalid revision\n"); - if (errors & OSC_CAPABILITIES_MASK_ERROR) { - if (capbuf[OSC_QUERY_TYPE] & OSC_QUERY_ENABLE) - goto out_success; - printk(KERN_DEBUG - "Firmware did not grant requested _OSC control\n"); - status = AE_SUPPORT; - goto out_kfree; - } - status = AE_ERROR; - goto out_kfree; + status = acpi_run_osc(handle, &context); + if (ACPI_SUCCESS(status)) { + *retval = *((u32 *)(context.ret.pointer + 8)); + kfree(context.ret.pointer); } -out_success: - *retval = *((u32 *)(out_obj->buffer.pointer + 8)); - status = AE_OK; - -out_kfree: - kfree(output.pointer); return status; } @@ -277,10 +229,10 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root, u32 flags) u32 support_set, result, capbuf[3]; /* do _OSC query for all possible controls */ - support_set = root->osc_support_set | (flags & OSC_SUPPORT_MASKS); + support_set = root->osc_support_set | (flags & OSC_PCI_SUPPORT_MASKS); capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_TYPE] = support_set; - capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; + capbuf[OSC_CONTROL_TYPE] = OSC_PCI_CONTROL_MASKS; status = acpi_pci_run_osc(root->device->handle, capbuf, &result); if (ACPI_SUCCESS(status)) { @@ -427,7 +379,7 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags) if (ACPI_FAILURE(status)) return status; - control_req = (flags & OSC_CONTROL_MASKS); + control_req = (flags & OSC_PCI_CONTROL_MASKS); if (!control_req) return AE_TYPE; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index cb4283f..4173123 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -353,7 +353,7 @@ static int acpi_processor_info_open_fs(struct inode *inode, struct file *file) PDE(inode)->data); } -static int acpi_processor_add_fs(struct acpi_device *device) +static int __cpuinit acpi_processor_add_fs(struct acpi_device *device) { struct proc_dir_entry *entry = NULL; @@ -722,7 +722,7 @@ static void acpi_processor_notify(struct acpi_device *device, u32 event) switch (event) { case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: saved = pr->performance_platform_limit; - acpi_processor_ppc_has_changed(pr); + acpi_processor_ppc_has_changed(pr, 1); if (saved == pr->performance_platform_limit) break; acpi_bus_generate_proc_event(device, event, @@ -758,7 +758,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, struct acpi_processor *pr = per_cpu(processors, cpu); if (action == CPU_ONLINE && pr) { - acpi_processor_ppc_has_changed(pr); + acpi_processor_ppc_has_changed(pr, 0); acpi_processor_cst_has_changed(pr); acpi_processor_tstate_has_changed(pr); } @@ -830,7 +830,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) arch_acpi_processor_cleanup_pdc(pr); #ifdef CONFIG_CPU_FREQ - acpi_processor_ppc_has_changed(pr); + acpi_processor_ppc_has_changed(pr, 0); #endif acpi_processor_get_throttling_info(pr); acpi_processor_get_limit_info(pr); @@ -845,7 +845,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device) goto err_power_exit; } - dev_info(&device->dev, "registered as cooling_device%d\n", + dev_dbg(&device->dev, "registered as cooling_device%d\n", pr->cdev->id); result = sysfs_create_link(&device->dev.kobj, diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index bbd066e..d1676b1 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -164,7 +164,7 @@ static void lapic_timer_check_state(int state, struct acpi_processor *pr, pr->power.timer_broadcast_on_state = state; } -static void lapic_timer_propagate_broadcast(void *arg) +static void __lapic_timer_propagate_broadcast(void *arg) { struct acpi_processor *pr = (struct acpi_processor *) arg; unsigned long reason; @@ -175,6 +175,12 @@ static void lapic_timer_propagate_broadcast(void *arg) clockevents_notify(reason, &pr->id); } +static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) +{ + smp_call_function_single(pr->id, __lapic_timer_propagate_broadcast, + (void *)pr, 1); +} + /* Power(C) State timer broadcast control */ static void lapic_timer_state_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, @@ -638,8 +644,7 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) working++; } - smp_call_function_single(pr->id, lapic_timer_propagate_broadcast, - pr, 1); + lapic_timer_propagate_broadcast(pr); return (working); } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 01e366d..2cabadc 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -152,15 +152,59 @@ static int acpi_processor_get_platform_limit(struct acpi_processor *pr) return 0; } -int acpi_processor_ppc_has_changed(struct acpi_processor *pr) +#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 +/* + * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status + * @handle: ACPI processor handle + * @status: the status code of _PPC evaluation + * 0: success. OSPM is now using the performance state specificed. + * 1: failure. OSPM has not changed the number of P-states in use + */ +static void acpi_processor_ppc_ost(acpi_handle handle, int status) +{ + union acpi_object params[2] = { + {.type = ACPI_TYPE_INTEGER,}, + {.type = ACPI_TYPE_INTEGER,}, + }; + struct acpi_object_list arg_list = {2, params}; + acpi_handle temp; + + params[0].integer.value = ACPI_PROCESSOR_NOTIFY_PERFORMANCE; + params[1].integer.value = status; + + /* when there is no _OST , skip it */ + if (ACPI_FAILURE(acpi_get_handle(handle, "_OST", &temp))) + return; + + acpi_evaluate_object(handle, "_OST", &arg_list, NULL); + return; +} + +int acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag) { int ret; - if (ignore_ppc) + if (ignore_ppc) { + /* + * Only when it is notification event, the _OST object + * will be evaluated. Otherwise it is skipped. + */ + if (event_flag) + acpi_processor_ppc_ost(pr->handle, 1); return 0; + } ret = acpi_processor_get_platform_limit(pr); - + /* + * Only when it is notification event, the _OST object + * will be evaluated. Otherwise it is skipped. + */ + if (event_flag) { + if (ret < 0) + acpi_processor_ppc_ost(pr->handle, 1); + else + acpi_processor_ppc_ost(pr->handle, 0); + } if (ret < 0) return (ret); else diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 65f6781..9073ada 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -1052,6 +1052,13 @@ static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) acpi_device_bid(device)); } seq_puts(seq, "\n"); + } else { + seq_printf(seq, "passive (forced):"); + if (tz->thermal_zone->forced_passive) + seq_printf(seq, " %i C\n", + tz->thermal_zone->forced_passive / 1000); + else + seq_printf(seq, "<not set>\n"); } for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index d2e6980..679cd08 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -64,6 +64,7 @@ #include <linux/dmi.h> #include <linux/string.h> #include <linux/ctype.h> +#include <linux/pnp.h> #ifdef CONFIG_PPC_OF #include <linux/of_device.h> @@ -1919,7 +1920,7 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static __devinit int try_init_acpi(struct SPMITable *spmi) +static __devinit int try_init_spmi(struct SPMITable *spmi) { struct smi_info *info; u8 addr_space; @@ -1940,7 +1941,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) return -ENOMEM; } - info->addr_source = "ACPI"; + info->addr_source = "SPMI"; /* Figure out the interface type. */ switch (spmi->InterfaceType) { @@ -2002,7 +2003,7 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) return 0; } -static __devinit void acpi_find_bmc(void) +static __devinit void spmi_find_bmc(void) { acpi_status status; struct SPMITable *spmi; @@ -2020,9 +2021,106 @@ static __devinit void acpi_find_bmc(void) if (status != AE_OK) return; - try_init_acpi(spmi); + try_init_spmi(spmi); } } + +static int __devinit ipmi_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) +{ + struct acpi_device *acpi_dev; + struct smi_info *info; + acpi_handle handle; + acpi_status status; + unsigned long long tmp; + + acpi_dev = pnp_acpi_device(dev); + if (!acpi_dev) + return -ENODEV; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->addr_source = "ACPI"; + + handle = acpi_dev->handle; + + /* _IFT tells us the interface type: KCS, BT, etc */ + status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); + if (ACPI_FAILURE(status)) + goto err_free; + + switch (tmp) { + case 1: + info->si_type = SI_KCS; + break; + case 2: + info->si_type = SI_SMIC; + break; + case 3: + info->si_type = SI_BT; + break; + default: + dev_info(&dev->dev, "unknown interface type %lld\n", tmp); + goto err_free; + } + + if (pnp_port_valid(dev, 0)) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + info->io.addr_data = pnp_port_start(dev, 0); + } else if (pnp_mem_valid(dev, 0)) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + info->io.addr_data = pnp_mem_start(dev, 0); + } else { + dev_err(&dev->dev, "no I/O or memory address\n"); + goto err_free; + } + + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = 0; + + /* If _GPE exists, use it; otherwise use standard interrupts */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); + if (ACPI_SUCCESS(status)) { + info->irq = tmp; + info->irq_setup = acpi_gpe_irq_setup; + } else if (pnp_irq_valid(dev, 0)) { + info->irq = pnp_irq(dev, 0); + info->irq_setup = std_irq_setup; + } + + info->dev = &acpi_dev->dev; + pnp_set_drvdata(dev, info); + + return try_smi_init(info); + +err_free: + kfree(info); + return -EINVAL; +} + +static void __devexit ipmi_pnp_remove(struct pnp_dev *dev) +{ + struct smi_info *info = pnp_get_drvdata(dev); + + cleanup_one_si(info); +} + +static const struct pnp_device_id pnp_dev_table[] = { + {"IPI0001", 0}, + {"", 0}, +}; + +static struct pnp_driver ipmi_pnp_driver = { + .name = DEVICE_NAME, + .probe = ipmi_pnp_probe, + .remove = __devexit_p(ipmi_pnp_remove), + .id_table = pnp_dev_table, +}; #endif #ifdef CONFIG_DMI @@ -2202,7 +2300,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, int rv; int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; struct smi_info *info; - int first_reg_offset = 0; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) @@ -2241,9 +2338,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, info->addr_source_cleanup = ipmi_pci_cleanup; info->addr_source_data = pdev; - if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID) - first_reg_offset = 1; - if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { info->io_setup = port_setup; info->io.addr_type = IPMI_IO_ADDR_SPACE; @@ -3108,7 +3202,10 @@ static __devinit int init_ipmi_si(void) #endif #ifdef CONFIG_ACPI - acpi_find_bmc(); + spmi_find_bmc(); +#endif +#ifdef CONFIG_PNP + pnp_register_driver(&ipmi_pnp_driver); #endif #ifdef CONFIG_PCI @@ -3233,6 +3330,9 @@ static __exit void cleanup_ipmi_si(void) #ifdef CONFIG_PCI pci_unregister_driver(&ipmi_pci_driver); #endif +#ifdef CONFIG_PNP + pnp_unregister_driver(&ipmi_pnp_driver); +#endif #ifdef CONFIG_PPC_OF of_unregister_platform_driver(&ipmi_of_platform_driver); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 59f4ba1..1a7a9fc 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -248,19 +248,6 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. -config DELL_LAPTOP - tristate "Dell Laptop Extras (EXPERIMENTAL)" - depends on X86 - depends on DCDBAS - depends on EXPERIMENTAL - depends on BACKLIGHT_CLASS_DEVICE - depends on RFKILL - depends on POWER_SUPPLY - default n - ---help--- - This driver adds support for rfkill and backlight control to Dell - laptops. - config ISL29003 tristate "Intersil ISL29003 ambient light sensor" depends on I2C && SYSFS diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39d..fc5bf9d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -334,6 +334,8 @@ config EEEPC_LAPTOP depends on HOTPLUG_PCI select BACKLIGHT_CLASS_DEVICE select HWMON + select LEDS_CLASS + select NEW_LEDS ---help--- This driver supports the Fn-Fx keys on Eee PC laptops. @@ -365,6 +367,18 @@ config ACPI_WMI It is safe to enable this driver even if your DSDT doesn't define any ACPI-WMI devices. +config MSI_WMI + tristate "MSI WMI extras" + depends on ACPI_WMI + depends on INPUT + depends on BACKLIGHT_CLASS_DEVICE + select INPUT_SPARSEKMAP + help + Say Y here if you want to support WMI-based hotkeys on MSI laptops. + + To compile this driver as a module, choose M here: the module will + be called msi-wmi. + config ACPI_ASUS tristate "ASUS/Medion Laptop Extras (DEPRECATED)" depends on ACPI @@ -435,4 +449,19 @@ config ACPI_TOSHIBA If you have a legacy free Toshiba laptop (such as the Libretto L1 series), say Y. + +config TOSHIBA_BT_RFKILL + tristate "Toshiba Bluetooth RFKill switch support" + depends on ACPI + ---help--- + This driver adds support for Bluetooth events for the RFKill + switch on modern Toshiba laptops with full ACPI support and + an RFKill switch. + + This driver handles RFKill events for the TOS6205 Bluetooth, + and re-enables it when the switch is set back to the 'on' + position. + + If you have a modern Toshiba laptop with a Bluetooth and an + RFKill switch (such as the Portege R500), say Y. endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d1c1621..b7474b6 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o +obj-$(CONFIG_MSI_WMI) += msi-wmi.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o +obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index be27aa4..79b15b9 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -52,7 +52,7 @@ */ #undef START_IN_KERNEL_MODE -#define DRV_VER "0.5.18" +#define DRV_VER "0.5.20" /* * According to the Atom N270 datasheet, @@ -112,12 +112,14 @@ module_param_string(force_product, force_product, 16, 0); MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check"); /* - * cmd_off: to switch the fan completely off / to check if the fan is off + * cmd_off: to switch the fan completely off + * chk_off: to check if the fan is off * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then * the fan speed depending on the temperature */ struct fancmd { u8 cmd_off; + u8 chk_off; u8 cmd_auto; }; @@ -134,32 +136,41 @@ struct bios_settings_t { /* Register addresses and values for different BIOS versions */ static const struct bios_settings_t bios_tbl[] = { /* AOA110 */ - {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x00} }, - {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x00} }, - {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0x00} }, - {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x00} }, - {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} }, - {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} }, + {"Acer", "AOA110", "v0.3109", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, + {"Acer", "AOA110", "v0.3114", 0x55, 0x58, {0x1f, 0x1f, 0x00} }, + {"Acer", "AOA110", "v0.3301", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3304", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3305", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3307", 0x55, 0x58, {0xaf, 0xaf, 0x00} }, + {"Acer", "AOA110", "v0.3308", 0x55, 0x58, {0x21, 0x21, 0x00} }, + {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x21, 0x00} }, + {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x21, 0x00} }, /* AOA150 */ - {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} }, - {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} }, + {"Acer", "AOA150", "v0.3114", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3301", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3304", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3305", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3307", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3308", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x20, 0x00} }, + /* Acer 1410 */ + {"Acer", "Aspire 1410", "v0.3120", 0x55, 0x58, {0x9e, 0x9e, 0x00} }, /* special BIOS / other */ - {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} }, - {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x00} }, - {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} }, - {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} }, - {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x00} }, + {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x21, 0x00} }, + {"Gateway", "AOA150", "v0.3103", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Gateway ", "LT31 ", "v1.3103 ", 0x55, 0x58, + {0x10, 0x0f, 0x00} }, + {"Gateway ", "LT31 ", "v1.3201 ", 0x55, 0x58, + {0x10, 0x0f, 0x00} }, + {"Gateway ", "LT31 ", "v1.3302 ", 0x55, 0x58, + {0x10, 0x0f, 0x00} }, + {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x21, 0x00} }, + {"Packard Bell", "DOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, + {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x21, 0x00} }, + {"Packard Bell", "AOA150", "v0.3105", 0x55, 0x58, {0x20, 0x20, 0x00} }, /* pewpew-terminator */ - {"", "", "", 0, 0, {0, 0} } + {"", "", "", 0, 0, {0, 0, 0} } }; static const struct bios_settings_t *bios_cfg __read_mostly; @@ -183,7 +194,7 @@ static int acerhdf_get_fanstate(int *state) if (ec_read(bios_cfg->fanreg, &fan)) return -EINVAL; - if (fan != bios_cfg->cmd.cmd_off) + if (fan != bios_cfg->cmd.chk_off) *state = ACERHDF_FAN_AUTO; else *state = ACERHDF_FAN_OFF; diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b39d2bb..61a1c75 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -221,6 +221,7 @@ static struct asus_hotk *hotk; */ static const struct acpi_device_id asus_device_ids[] = { {"ATK0100", 0}, + {"ATK0101", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, asus_device_ids); @@ -232,6 +233,7 @@ static void asus_hotk_notify(struct acpi_device *device, u32 event); static struct acpi_driver asus_hotk_driver = { .name = ASUS_HOTK_NAME, .class = ASUS_HOTK_CLASS, + .owner = THIS_MODULE, .ids = asus_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { @@ -293,6 +295,11 @@ struct key_entry { enum { KE_KEY, KE_END }; static struct key_entry asus_keymap[] = { + {KE_KEY, 0x02, KEY_SCREENLOCK}, + {KE_KEY, 0x05, KEY_WLAN}, + {KE_KEY, 0x08, KEY_F13}, + {KE_KEY, 0x17, KEY_ZOOM}, + {KE_KEY, 0x1f, KEY_BATTERY}, {KE_KEY, 0x30, KEY_VOLUMEUP}, {KE_KEY, 0x31, KEY_VOLUMEDOWN}, {KE_KEY, 0x32, KEY_MUTE}, @@ -312,8 +319,11 @@ static struct key_entry asus_keymap[] = { {KE_KEY, 0x5F, KEY_WLAN}, {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, - {KE_KEY, 0x6B, BTN_TOUCH}, /* Lock Mouse */ + {KE_KEY, 0x62, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x63, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x6B, KEY_F13}, /* Lock Touchpad */ {KE_KEY, 0x82, KEY_CAMERA}, + {KE_KEY, 0x88, KEY_WLAN }, {KE_KEY, 0x8A, KEY_PROG1}, {KE_KEY, 0x95, KEY_MEDIA}, {KE_KEY, 0x99, KEY_PHONE}, @@ -1240,9 +1250,6 @@ static int asus_hotk_add(struct acpi_device *device) { int result; - if (!device) - return -EINVAL; - pr_notice("Asus Laptop Support version %s\n", ASUS_LAPTOP_VERSION); @@ -1283,8 +1290,8 @@ static int asus_hotk_add(struct acpi_device *device) hotk->ledd_status = 0xFFF; /* Set initial values of light sensor and level */ - hotk->light_switch = 1; /* Default to light sensor disabled */ - hotk->light_level = 0; /* level 5 for sensor sensitivity */ + hotk->light_switch = 0; /* Default to light sensor disabled */ + hotk->light_level = 5; /* level 5 for sensor sensitivity */ if (ls_switch_handle) set_light_sens_switch(hotk->light_switch); @@ -1306,9 +1313,6 @@ end: static int asus_hotk_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) - return -EINVAL; - kfree(hotk->name); kfree(hotk); @@ -1444,9 +1448,6 @@ static int __init asus_laptop_init(void) { int result; - if (acpi_disabled) - return -ENODEV; - result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) return result; diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ddf5240..0c9c531 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -466,6 +466,7 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids); static struct acpi_driver asus_hotk_driver = { .name = "asus_acpi", .class = ACPI_HOTK_CLASS, + .owner = THIS_MODULE, .ids = asus_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { @@ -1334,9 +1335,6 @@ static int asus_hotk_add(struct acpi_device *device) acpi_status status = AE_OK; int result; - if (!device) - return -EINVAL; - printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION); @@ -1392,9 +1390,6 @@ end: static int asus_hotk_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) - return -EINVAL; - asus_hotk_remove_fs(device); kfree(hotk); @@ -1422,21 +1417,17 @@ static int __init asus_acpi_init(void) { int result; - if (acpi_disabled) - return -ENODEV; + result = acpi_bus_register_driver(&asus_hotk_driver); + if (result < 0) + return result; asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); if (!asus_proc_dir) { printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); + acpi_bus_unregister_driver(&asus_hotk_driver); return -ENODEV; } - result = acpi_bus_register_driver(&asus_hotk_driver); - if (result < 0) { - remove_proc_entry(PROC_ASUS, acpi_root_dir); - return result; - } - /* * This is a bit of a kludge. We only want this module loaded * for ASUS systems, but there's currently no way to probe the diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 74909c4..3780994 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -58,6 +58,14 @@ static int da_command_code; static int da_num_tokens; static struct calling_interface_token *da_tokens; +static struct platform_driver platform_driver = { + .driver = { + .name = "dell-laptop", + .owner = THIS_MODULE, + } +}; + +static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; static struct rfkill *wifi_rfkill; static struct rfkill *bluetooth_rfkill; @@ -74,7 +82,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = { { } }; -static void parse_da_table(const struct dmi_header *dm) +static void __init parse_da_table(const struct dmi_header *dm) { /* Final token is a terminator, so we don't want to copy it */ int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; @@ -103,7 +111,7 @@ static void parse_da_table(const struct dmi_header *dm) da_num_tokens += tokens; } -static void find_tokens(const struct dmi_header *dm, void *dummy) +static void __init find_tokens(const struct dmi_header *dm, void *dummy) { switch (dm->type) { case 0xd4: /* Indexed IO */ @@ -197,8 +205,8 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data) dell_send_request(&buffer, 17, 11); status = buffer.output[1]; - if (status & BIT(bit)) - rfkill_set_hw_state(rfkill, !!(status & BIT(16))); + rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); + rfkill_set_hw_state(rfkill, !(status & BIT(16))); } static const struct rfkill_ops dell_rfkill_ops = { @@ -206,7 +214,7 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; -static int dell_setup_rfkill(void) +static int __init dell_setup_rfkill(void) { struct calling_interface_buffer buffer; int status; @@ -217,7 +225,8 @@ static int dell_setup_rfkill(void) status = buffer.output[1]; if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { - wifi_rfkill = rfkill_alloc("dell-wifi", NULL, RFKILL_TYPE_WLAN, + wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, + RFKILL_TYPE_WLAN, &dell_rfkill_ops, (void *) 1); if (!wifi_rfkill) { ret = -ENOMEM; @@ -229,7 +238,8 @@ static int dell_setup_rfkill(void) } if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { - bluetooth_rfkill = rfkill_alloc("dell-bluetooth", NULL, + bluetooth_rfkill = rfkill_alloc("dell-bluetooth", + &platform_device->dev, RFKILL_TYPE_BLUETOOTH, &dell_rfkill_ops, (void *) 2); if (!bluetooth_rfkill) { @@ -242,7 +252,9 @@ static int dell_setup_rfkill(void) } if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { - wwan_rfkill = rfkill_alloc("dell-wwan", NULL, RFKILL_TYPE_WWAN, + wwan_rfkill = rfkill_alloc("dell-wwan", + &platform_device->dev, + RFKILL_TYPE_WWAN, &dell_rfkill_ops, (void *) 3); if (!wwan_rfkill) { ret = -ENOMEM; @@ -268,6 +280,22 @@ err_wifi: return ret; } +static void dell_cleanup_rfkill(void) +{ + if (wifi_rfkill) { + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + } + if (bluetooth_rfkill) { + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + } + if (wwan_rfkill) { + rfkill_unregister(wwan_rfkill); + rfkill_destroy(wwan_rfkill); + } +} + static int dell_send_intensity(struct backlight_device *bd) { struct calling_interface_buffer buffer; @@ -326,11 +354,23 @@ static int __init dell_init(void) return -ENODEV; } + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + platform_device = platform_device_alloc("dell-laptop", -1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device1; + } + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device2; + ret = dell_setup_rfkill(); if (ret) { printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); - goto out; + goto fail_rfkill; } #ifdef CONFIG_ACPI @@ -352,13 +392,13 @@ static int __init dell_init(void) if (max_intensity) { dell_backlight_device = backlight_device_register( "dell_backlight", - NULL, NULL, + &platform_device->dev, NULL, &dell_ops); if (IS_ERR(dell_backlight_device)) { ret = PTR_ERR(dell_backlight_device); dell_backlight_device = NULL; - goto out; + goto fail_backlight; } dell_backlight_device->props.max_brightness = max_intensity; @@ -368,13 +408,16 @@ static int __init dell_init(void) } return 0; -out: - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); - if (wwan_rfkill) - rfkill_unregister(wwan_rfkill); + +fail_backlight: + dell_cleanup_rfkill(); +fail_rfkill: + platform_device_del(platform_device); +fail_platform_device2: + platform_device_put(platform_device); +fail_platform_device1: + platform_driver_unregister(&platform_driver); +fail_platform_driver: kfree(da_tokens); return ret; } @@ -382,12 +425,7 @@ out: static void __exit dell_exit(void) { backlight_device_unregister(dell_backlight_device); - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); - if (wwan_rfkill) - rfkill_unregister(wwan_rfkill); + dell_cleanup_rfkill(); } module_init(dell_init); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 0f900cc..67f3fe7 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -31,6 +31,7 @@ #include <acpi/acpi_drivers.h> #include <linux/acpi.h> #include <linux/string.h> +#include <linux/dmi.h> MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); @@ -38,6 +39,8 @@ MODULE_LICENSE("GPL"); #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" +static int acpi_video; + MODULE_ALIAS("wmi:"DELL_EVENT_GUID); struct key_entry { @@ -54,7 +57,7 @@ enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; * via the keyboard controller so should not be sent again. */ -static struct key_entry dell_wmi_keymap[] = { +static struct key_entry dell_legacy_wmi_keymap[] = { {KE_KEY, 0xe045, KEY_PROG1}, {KE_KEY, 0xe009, KEY_EJECTCD}, @@ -72,7 +75,7 @@ static struct key_entry dell_wmi_keymap[] = { /* The next device is at offset 6, the active devices are at offset 8 and the attached devices at offset 10 */ - {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, + {KE_KEY, 0xe00b, KEY_SWITCHVIDEOMODE}, {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, @@ -96,6 +99,47 @@ static struct key_entry dell_wmi_keymap[] = { {KE_END, 0} }; +static bool dell_new_hk_type; + +struct dell_new_keymap_entry { + u16 scancode; + u16 keycode; +}; + +struct dell_hotkey_table { + struct dmi_header header; + struct dell_new_keymap_entry keymap[]; + +}; + +static struct key_entry *dell_new_wmi_keymap; + +static u16 bios_to_linux_keycode[256] = { + + KEY_MEDIA, KEY_NEXTSONG, KEY_PLAYPAUSE, KEY_PREVIOUSSONG, + KEY_STOPCD, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_WWW, KEY_UNKNOWN, KEY_VOLUMEDOWN, KEY_MUTE, + KEY_VOLUMEUP, KEY_UNKNOWN, KEY_BATTERY, KEY_EJECTCD, + KEY_UNKNOWN, KEY_SLEEP, KEY_PROG1, KEY_BRIGHTNESSDOWN, + KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE, + KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, + KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2, + KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + KEY_PROG3 +}; + + +static struct key_entry *dell_wmi_keymap = dell_legacy_wmi_keymap; + static struct input_dev *dell_wmi_input_dev; static struct key_entry *dell_wmi_get_entry_by_scancode(int code) @@ -164,24 +208,78 @@ static void dell_wmi_notify(u32 value, void *context) obj = (union acpi_object *)response.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) { - int *buffer = (int *)obj->buffer.pointer; - /* - * The upper bytes of the event may contain - * additional information, so mask them off for the - * scancode lookup - */ - key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); - if (key) { + int reported_key; + u16 *buffer_entry = (u16 *)obj->buffer.pointer; + if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { + printk(KERN_INFO "dell-wmi: Received unknown WMI event" + " (0x%x)\n", buffer_entry[1]); + return; + } + + if (dell_new_hk_type) + reported_key = (int)buffer_entry[2]; + else + reported_key = (int)buffer_entry[1] & 0xffff; + + key = dell_wmi_get_entry_by_scancode(reported_key); + + if (!key) { + printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", + reported_key); + } else if ((key->keycode == KEY_BRIGHTNESSUP || + key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video) { + /* Don't report brightness notifications that will also + * come via ACPI */ + return; + } else { input_report_key(dell_wmi_input_dev, key->keycode, 1); input_sync(dell_wmi_input_dev); input_report_key(dell_wmi_input_dev, key->keycode, 0); input_sync(dell_wmi_input_dev); - } else if (buffer[1] & 0xFFFF) - printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", - buffer[1] & 0xFFFF); + } } } + +static void setup_new_hk_map(const struct dmi_header *dm) +{ + + int i; + int hotkey_num = (dm->length-4)/sizeof(struct dell_new_keymap_entry); + struct dell_hotkey_table *table = + container_of(dm, struct dell_hotkey_table, header); + + dell_new_wmi_keymap = kzalloc((hotkey_num+1) * + sizeof(struct key_entry), GFP_KERNEL); + + for (i = 0; i < hotkey_num; i++) { + dell_new_wmi_keymap[i].type = KE_KEY; + dell_new_wmi_keymap[i].code = table->keymap[i].scancode; + dell_new_wmi_keymap[i].keycode = + (table->keymap[i].keycode > 255) ? 0 : + bios_to_linux_keycode[table->keymap[i].keycode]; + } + + dell_new_wmi_keymap[i].type = KE_END; + dell_new_wmi_keymap[i].code = 0; + dell_new_wmi_keymap[i].keycode = 0; + + dell_wmi_keymap = dell_new_wmi_keymap; + +} + + +static void find_hk_type(const struct dmi_header *dm, void *dummy) +{ + + if ((dm->type == 0xb2) && (dm->length > 6)) { + dell_new_hk_type = true; + setup_new_hk_map(dm); + } + +} + + static int __init dell_wmi_input_setup(void) { struct key_entry *key; @@ -226,6 +324,9 @@ static int __init dell_wmi_init(void) int err; if (wmi_has_guid(DELL_EVENT_GUID)) { + + dmi_walk(find_hk_type, NULL); + err = dell_wmi_input_setup(); if (err) @@ -240,6 +341,8 @@ static int __init dell_wmi_init(void) return err; } + acpi_video = acpi_video_backlight_support(); + } else printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index e647a85..5838c69 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1,5 +1,5 @@ /* - * eepc-laptop.c - Asus Eee PC extras + * eeepc-laptop.c - Asus Eee PC extras * * Based on asus_acpi.c as patched for the Eee PC by Asus: * ftp://ftp.asus.com/pub/ASUS/EeePC/701/ASUS_ACPI_071126.rar @@ -34,20 +34,23 @@ #include <linux/rfkill.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> +#include <linux/leds.h> #define EEEPC_LAPTOP_VERSION "0.1" +#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" +#define EEEPC_LAPTOP_FILE "eeepc" -#define EEEPC_HOTK_NAME "Eee PC Hotkey Driver" -#define EEEPC_HOTK_FILE "eeepc" -#define EEEPC_HOTK_CLASS "hotkey" -#define EEEPC_HOTK_DEVICE_NAME "Hotkey" -#define EEEPC_HOTK_HID "ASUS010" +#define EEEPC_ACPI_CLASS "hotkey" +#define EEEPC_ACPI_DEVICE_NAME "Hotkey" +#define EEEPC_ACPI_HID "ASUS010" +MODULE_AUTHOR("Corentin Chary, Eric Cooper"); +MODULE_DESCRIPTION(EEEPC_LAPTOP_NAME); +MODULE_LICENSE("GPL"); /* * Definitions for Asus EeePC */ -#define NOTIFY_WLAN_ON 0x10 #define NOTIFY_BRN_MIN 0x20 #define NOTIFY_BRN_MAX 0x2f @@ -117,58 +120,6 @@ static const char *cm_setv[] = { NULL, NULL, "PBPS", "TPDS" }; -#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." - -#define EEEPC_EC_FAN_PWM EEEPC_EC "SC02" /* Fan PWM duty cycle (%) */ -#define EEEPC_EC_SC02 0x63 -#define EEEPC_EC_FAN_HRPM EEEPC_EC "SC05" /* High byte, fan speed (RPM) */ -#define EEEPC_EC_FAN_LRPM EEEPC_EC "SC06" /* Low byte, fan speed (RPM) */ -#define EEEPC_EC_FAN_CTRL EEEPC_EC "SFB3" /* Byte containing SF25 */ -#define EEEPC_EC_SFB3 0xD3 - -/* - * This is the main structure, we can use it to store useful information - * about the hotk device - */ -struct eeepc_hotk { - struct acpi_device *device; /* the device we are in */ - acpi_handle handle; /* the handle of the hotk device */ - u32 cm_supported; /* the control methods supported - by this BIOS */ - uint init_flag; /* Init flags */ - u16 event_count[128]; /* count for each event */ - struct input_dev *inputdev; - u16 *keycode_map; - struct rfkill *wlan_rfkill; - struct rfkill *bluetooth_rfkill; - struct rfkill *wwan3g_rfkill; - struct rfkill *wimax_rfkill; - struct hotplug_slot *hotplug_slot; - struct mutex hotplug_lock; -}; - -/* The actual device the driver binds to */ -static struct eeepc_hotk *ehotk; - -/* Platform device/driver */ -static int eeepc_hotk_thaw(struct device *device); -static int eeepc_hotk_restore(struct device *device); - -static const struct dev_pm_ops eeepc_pm_ops = { - .thaw = eeepc_hotk_thaw, - .restore = eeepc_hotk_restore, -}; - -static struct platform_driver platform_driver = { - .driver = { - .name = EEEPC_HOTK_FILE, - .owner = THIS_MODULE, - .pm = &eeepc_pm_ops, - } -}; - -static struct platform_device *platform_device; - struct key_entry { char type; u8 code; @@ -177,7 +128,7 @@ struct key_entry { enum { KE_KEY, KE_END }; -static struct key_entry eeepc_keymap[] = { +static const struct key_entry eeepc_keymap[] = { /* Sleep already handled via generic ACPI code */ {KE_KEY, 0x10, KEY_WLAN }, {KE_KEY, 0x11, KEY_WLAN }, @@ -185,77 +136,56 @@ static struct key_entry eeepc_keymap[] = { {KE_KEY, 0x13, KEY_MUTE }, {KE_KEY, 0x14, KEY_VOLUMEDOWN }, {KE_KEY, 0x15, KEY_VOLUMEUP }, + {KE_KEY, 0x16, KEY_DISPLAY_OFF }, {KE_KEY, 0x1a, KEY_COFFEE }, {KE_KEY, 0x1b, KEY_ZOOM }, {KE_KEY, 0x1c, KEY_PROG2 }, {KE_KEY, 0x1d, KEY_PROG3 }, - {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN }, - {KE_KEY, NOTIFY_BRN_MIN + 2, KEY_BRIGHTNESSUP }, + {KE_KEY, NOTIFY_BRN_MIN, KEY_BRIGHTNESSDOWN }, + {KE_KEY, NOTIFY_BRN_MAX, KEY_BRIGHTNESSUP }, {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE }, {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE }, {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE }, + {KE_KEY, 0x37, KEY_F13 }, /* Disable Touchpad */ + {KE_KEY, 0x38, KEY_F14 }, {KE_END, 0}, }; + /* - * The hotkey driver declaration + * This is the main structure, we can use it to store useful information */ -static int eeepc_hotk_add(struct acpi_device *device); -static int eeepc_hotk_remove(struct acpi_device *device, int type); -static void eeepc_hotk_notify(struct acpi_device *device, u32 event); - -static const struct acpi_device_id eeepc_device_ids[] = { - {EEEPC_HOTK_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); - -static struct acpi_driver eeepc_hotk_driver = { - .name = EEEPC_HOTK_NAME, - .class = EEEPC_HOTK_CLASS, - .ids = eeepc_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = eeepc_hotk_add, - .remove = eeepc_hotk_remove, - .notify = eeepc_hotk_notify, - }, -}; +struct eeepc_laptop { + acpi_handle handle; /* the handle of the acpi device */ + u32 cm_supported; /* the control methods supported + by this BIOS */ + u16 event_count[128]; /* count for each event */ -/* PCI hotplug ops */ -static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value); + struct platform_device *platform_device; + struct device *hwmon_device; + struct backlight_device *backlight_device; -static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { - .owner = THIS_MODULE, - .get_adapter_status = eeepc_get_adapter_status, - .get_power_status = eeepc_get_adapter_status, -}; + struct input_dev *inputdev; + struct key_entry *keymap; -/* The backlight device /sys/class/backlight */ -static struct backlight_device *eeepc_backlight_device; + struct rfkill *wlan_rfkill; + struct rfkill *bluetooth_rfkill; + struct rfkill *wwan3g_rfkill; + struct rfkill *wimax_rfkill; -/* The hwmon device */ -static struct device *eeepc_hwmon_device; + struct hotplug_slot *hotplug_slot; + struct mutex hotplug_lock; -/* - * The backlight class declaration - */ -static int read_brightness(struct backlight_device *bd); -static int update_bl_status(struct backlight_device *bd); -static struct backlight_ops eeepcbl_ops = { - .get_brightness = read_brightness, - .update_status = update_bl_status, + struct led_classdev tpd_led; + int tpd_led_wk; + struct workqueue_struct *led_workqueue; + struct work_struct tpd_led_work; }; -MODULE_AUTHOR("Corentin Chary, Eric Cooper"); -MODULE_DESCRIPTION(EEEPC_HOTK_NAME); -MODULE_LICENSE("GPL"); - /* * ACPI Helpers */ -static int write_acpi_int(acpi_handle handle, const char *method, int val, - struct acpi_buffer *output) +static int write_acpi_int(acpi_handle handle, const char *method, int val) { struct acpi_object_list params; union acpi_object in_obj; @@ -266,7 +196,7 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val, in_obj.type = ACPI_TYPE_INTEGER; in_obj.integer.value = val; - status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); + status = acpi_evaluate_object(handle, (char *)method, ¶ms, NULL); return (status == AE_OK ? 0 : -1); } @@ -285,81 +215,56 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val) } } -static int set_acpi(int cm, int value) +static int set_acpi(struct eeepc_laptop *eeepc, int cm, int value) { - if (ehotk->cm_supported & (0x1 << cm)) { - const char *method = cm_setv[cm]; - if (method == NULL) - return -ENODEV; - if (write_acpi_int(ehotk->handle, method, value, NULL)) - pr_warning("Error writing %s\n", method); - } - return 0; -} + const char *method = cm_setv[cm]; -static int get_acpi(int cm) -{ - int value = -ENODEV; - if ((ehotk->cm_supported & (0x1 << cm))) { - const char *method = cm_getv[cm]; - if (method == NULL) - return -ENODEV; - if (read_acpi_int(ehotk->handle, method, &value)) - pr_warning("Error reading %s\n", method); - } - return value; -} - -/* - * Backlight - */ -static int read_brightness(struct backlight_device *bd) -{ - return get_acpi(CM_ASL_PANELBRIGHT); -} + if (method == NULL) + return -ENODEV; + if ((eeepc->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; -static int set_brightness(struct backlight_device *bd, int value) -{ - value = max(0, min(15, value)); - return set_acpi(CM_ASL_PANELBRIGHT, value); + if (write_acpi_int(eeepc->handle, method, value)) + pr_warning("Error writing %s\n", method); + return 0; } -static int update_bl_status(struct backlight_device *bd) +static int get_acpi(struct eeepc_laptop *eeepc, int cm) { - return set_brightness(bd, bd->props.brightness); -} + const char *method = cm_getv[cm]; + int value; -/* - * Rfkill helpers - */ + if (method == NULL) + return -ENODEV; + if ((eeepc->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; -static bool eeepc_wlan_rfkill_blocked(void) -{ - if (get_acpi(CM_ASL_WLAN) == 1) - return false; - return true; + if (read_acpi_int(eeepc->handle, method, &value)) + pr_warning("Error reading %s\n", method); + return value; } -static int eeepc_rfkill_set(void *data, bool blocked) +static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, + acpi_handle *handle) { - unsigned long asl = (unsigned long)data; - return set_acpi(asl, !blocked); -} + const char *method = cm_setv[cm]; + acpi_status status; -static const struct rfkill_ops eeepc_rfkill_ops = { - .set_block = eeepc_rfkill_set, -}; + if (method == NULL) + return -ENODEV; + if ((eeepc->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; -static void __devinit eeepc_enable_camera(void) -{ - /* - * If the following call to set_acpi() fails, it's because there's no - * camera so we can ignore the error. - */ - if (get_acpi(CM_ASL_CAMERA) == 0) - set_acpi(CM_ASL_CAMERA, 1); + status = acpi_get_handle(eeepc->handle, (char *)method, + handle); + if (status != AE_OK) { + pr_warning("Error finding %s\n", method); + return -ENODEV; + } + return 0; } + /* * Sys helpers */ @@ -372,60 +277,63 @@ static int parse_arg(const char *buf, unsigned long count, int *val) return count; } -static ssize_t store_sys_acpi(int cm, const char *buf, size_t count) +static ssize_t store_sys_acpi(struct device *dev, int cm, + const char *buf, size_t count) { + struct eeepc_laptop *eeepc = dev_get_drvdata(dev); int rv, value; rv = parse_arg(buf, count, &value); if (rv > 0) - value = set_acpi(cm, value); + value = set_acpi(eeepc, cm, value); if (value < 0) - return value; + return -EIO; return rv; } -static ssize_t show_sys_acpi(int cm, char *buf) +static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) { - int value = get_acpi(cm); + struct eeepc_laptop *eeepc = dev_get_drvdata(dev); + int value = get_acpi(eeepc, cm); if (value < 0) - return value; + return -EIO; return sprintf(buf, "%d\n", value); } -#define EEEPC_CREATE_DEVICE_ATTR(_name, _cm) \ +#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ static ssize_t show_##_name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - return show_sys_acpi(_cm, buf); \ + return show_sys_acpi(dev, _cm, buf); \ } \ static ssize_t store_##_name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ - return store_sys_acpi(_cm, buf, count); \ + return store_sys_acpi(dev, _cm, buf, count); \ } \ static struct device_attribute dev_attr_##_name = { \ .attr = { \ .name = __stringify(_name), \ - .mode = 0644 }, \ + .mode = _mode }, \ .show = show_##_name, \ .store = store_##_name, \ } -EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); -EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); -EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); +EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA); +EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER); +EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH); struct eeepc_cpufv { int num; int cur; }; -static int get_cpufv(struct eeepc_cpufv *c) +static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c) { - c->cur = get_acpi(CM_ASL_CPUFV); + c->cur = get_acpi(eeepc, CM_ASL_CPUFV); c->num = (c->cur >> 8) & 0xff; c->cur &= 0xff; if (c->cur < 0 || c->num <= 0 || c->num > 12) @@ -437,11 +345,12 @@ static ssize_t show_available_cpufv(struct device *dev, struct device_attribute *attr, char *buf) { + struct eeepc_laptop *eeepc = dev_get_drvdata(dev); struct eeepc_cpufv c; int i; ssize_t len = 0; - if (get_cpufv(&c)) + if (get_cpufv(eeepc, &c)) return -ENODEV; for (i = 0; i < c.num; i++) len += sprintf(buf + len, "%d ", i); @@ -453,9 +362,10 @@ static ssize_t show_cpufv(struct device *dev, struct device_attribute *attr, char *buf) { + struct eeepc_laptop *eeepc = dev_get_drvdata(dev); struct eeepc_cpufv c; - if (get_cpufv(&c)) + if (get_cpufv(eeepc, &c)) return -ENODEV; return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); } @@ -464,17 +374,18 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct eeepc_laptop *eeepc = dev_get_drvdata(dev); struct eeepc_cpufv c; int rv, value; - if (get_cpufv(&c)) + if (get_cpufv(eeepc, &c)) return -ENODEV; rv = parse_arg(buf, count, &value); if (rv < 0) return rv; if (!rv || value < 0 || value >= c.num) return -EINVAL; - set_acpi(CM_ASL_CPUFV, value); + set_acpi(eeepc, CM_ASL_CPUFV, value); return rv; } @@ -506,156 +417,125 @@ static struct attribute_group platform_attribute_group = { .attrs = platform_attributes }; -/* - * Hotkey functions - */ -static struct key_entry *eepc_get_entry_by_scancode(int code) +static int eeepc_platform_init(struct eeepc_laptop *eeepc) { - struct key_entry *key; - - for (key = eeepc_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; + int result; - return NULL; -} + eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); + if (!eeepc->platform_device) + return -ENOMEM; + platform_set_drvdata(eeepc->platform_device, eeepc); -static struct key_entry *eepc_get_entry_by_keycode(int code) -{ - struct key_entry *key; + result = platform_device_add(eeepc->platform_device); + if (result) + goto fail_platform_device; - for (key = eeepc_keymap; key->type != KE_END; key++) - if (code == key->keycode && key->type == KE_KEY) - return key; + result = sysfs_create_group(&eeepc->platform_device->dev.kobj, + &platform_attribute_group); + if (result) + goto fail_sysfs; + return 0; - return NULL; +fail_sysfs: + platform_device_del(eeepc->platform_device); +fail_platform_device: + platform_device_put(eeepc->platform_device); + return result; } -static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) +static void eeepc_platform_exit(struct eeepc_laptop *eeepc) { - struct key_entry *key = eepc_get_entry_by_scancode(scancode); + sysfs_remove_group(&eeepc->platform_device->dev.kobj, + &platform_attribute_group); + platform_device_unregister(eeepc->platform_device); +} - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } +/* + * LEDs + */ +/* + * These functions actually update the LED's, and are called from a + * workqueue. By doing this as separate work rather than when the LED + * subsystem asks, we avoid messing with the Asus ACPI stuff during a + * potentially bad time, such as a timer interrupt. + */ +static void tpd_led_update(struct work_struct *work) + { + struct eeepc_laptop *eeepc; - return -EINVAL; + eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); + + set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk); } -static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) +static void tpd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) { - struct key_entry *key; - int old_keycode; + struct eeepc_laptop *eeepc; - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; + eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); - key = eepc_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!eepc_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; + eeepc->tpd_led_wk = (value > 0) ? 1 : 0; + queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } -static void cmsg_quirk(int cm, const char *name) +static int eeepc_led_init(struct eeepc_laptop *eeepc) { - int dummy; + int rv; - /* Some BIOSes do not report cm although it is avaliable. - Check if cm_getv[cm] works and, if yes, assume cm should be set. */ - if (!(ehotk->cm_supported & (1 << cm)) - && !read_acpi_int(ehotk->handle, cm_getv[cm], &dummy)) { - pr_info("%s (%x) not reported by BIOS," - " enabling anyway\n", name, 1 << cm); - ehotk->cm_supported |= 1 << cm; - } -} + if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV) + return 0; -static void cmsg_quirks(void) -{ - cmsg_quirk(CM_ASL_LID, "LID"); - cmsg_quirk(CM_ASL_TYPE, "TYPE"); - cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER"); - cmsg_quirk(CM_ASL_TPD, "TPD"); -} + eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!eeepc->led_workqueue) + return -ENOMEM; + INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); -static int eeepc_hotk_check(void) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - int result; + eeepc->tpd_led.name = "eeepc::touchpad"; + eeepc->tpd_led.brightness_set = tpd_led_set; + eeepc->tpd_led.max_brightness = 1; - result = acpi_bus_get_status(ehotk->device); - if (result) - return result; - if (ehotk->device->status.present) { - if (write_acpi_int(ehotk->handle, "INIT", ehotk->init_flag, - &buffer)) { - pr_err("Hotkey initialization failed\n"); - return -ENODEV; - } else { - pr_notice("Hotkey init flags 0x%x\n", ehotk->init_flag); - } - /* get control methods supported */ - if (read_acpi_int(ehotk->handle, "CMSG" - , &ehotk->cm_supported)) { - pr_err("Get control methods supported failed\n"); - return -ENODEV; - } else { - cmsg_quirks(); - pr_info("Get control methods supported: 0x%x\n", - ehotk->cm_supported); - } - } else { - pr_err("Hotkey device not present, aborting\n"); - return -EINVAL; + rv = led_classdev_register(&eeepc->platform_device->dev, + &eeepc->tpd_led); + if (rv) { + destroy_workqueue(eeepc->led_workqueue); + return rv; } + return 0; } -static int notify_brn(void) +static void eeepc_led_exit(struct eeepc_laptop *eeepc) { - /* returns the *previous* brightness, or -1 */ - struct backlight_device *bd = eeepc_backlight_device; - if (bd) { - int old = bd->props.brightness; - backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); - return old; - } - return -1; + if (eeepc->tpd_led.dev) + led_classdev_unregister(&eeepc->tpd_led); + if (eeepc->led_workqueue) + destroy_workqueue(eeepc->led_workqueue); } -static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, - u8 *value) -{ - int val = get_acpi(CM_ASL_WLAN); - if (val == 1 || val == 0) - *value = val; - else - return -EINVAL; - - return 0; +/* + * PCI hotplug (for wlan rfkill) + */ +static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) +{ + if (get_acpi(eeepc, CM_ASL_WLAN) == 1) + return false; + return true; } -static void eeepc_rfkill_hotplug(void) +static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc) { struct pci_dev *dev; struct pci_bus *bus; - bool blocked = eeepc_wlan_rfkill_blocked(); + bool blocked = eeepc_wlan_rfkill_blocked(eeepc); - if (ehotk->wlan_rfkill) - rfkill_set_sw_state(ehotk->wlan_rfkill, blocked); + if (eeepc->wlan_rfkill) + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); - mutex_lock(&ehotk->hotplug_lock); + mutex_lock(&eeepc->hotplug_lock); - if (ehotk->hotplug_slot) { + if (eeepc->hotplug_slot) { bus = pci_find_bus(0, 1); if (!bus) { pr_warning("Unable to find PCI bus 1?\n"); @@ -685,69 +565,23 @@ static void eeepc_rfkill_hotplug(void) } out_unlock: - mutex_unlock(&ehotk->hotplug_lock); + mutex_unlock(&eeepc->hotplug_lock); } static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) { + struct eeepc_laptop *eeepc = data; + if (event != ACPI_NOTIFY_BUS_CHECK) return; - eeepc_rfkill_hotplug(); + eeepc_rfkill_hotplug(eeepc); } -static void eeepc_hotk_notify(struct acpi_device *device, u32 event) +static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, + char *node) { - static struct key_entry *key; - u16 count; - int brn = -ENODEV; - - if (!ehotk) - return; - if (event > ACPI_MAX_SYS_NOTIFY) - return; - if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) - brn = notify_brn(); - count = ehotk->event_count[event % 128]++; - acpi_bus_generate_proc_event(ehotk->device, event, count); - acpi_bus_generate_netlink_event(ehotk->device->pnp.device_class, - dev_name(&ehotk->device->dev), event, - count); - if (ehotk->inputdev) { - if (brn != -ENODEV) { - /* brightness-change events need special - * handling for conversion to key events - */ - if (brn < 0) - brn = event; - else - brn += NOTIFY_BRN_MIN; - if (event < brn) - event = NOTIFY_BRN_MIN; /* brightness down */ - else if (event > brn) - event = NOTIFY_BRN_MIN + 2; /* ... up */ - else - event = NOTIFY_BRN_MIN + 1; /* ... unchanged */ - } - key = eepc_get_entry_by_scancode(event); - if (key) { - switch (key->type) { - case KE_KEY: - input_report_key(ehotk->inputdev, key->keycode, - 1); - input_sync(ehotk->inputdev); - input_report_key(ehotk->inputdev, key->keycode, - 0); - input_sync(ehotk->inputdev); - break; - } - } - } -} - -static int eeepc_register_rfkill_notifier(char *node) -{ - acpi_status status = AE_OK; + acpi_status status; acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); @@ -756,7 +590,7 @@ static int eeepc_register_rfkill_notifier(char *node) status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, eeepc_rfkill_notify, - NULL); + eeepc); if (ACPI_FAILURE(status)) pr_warning("Failed to register notify on %s\n", node); } else @@ -765,7 +599,8 @@ static int eeepc_register_rfkill_notifier(char *node) return 0; } -static void eeepc_unregister_rfkill_notifier(char *node) +static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, + char *node) { acpi_status status = AE_OK; acpi_handle handle; @@ -782,13 +617,33 @@ static void eeepc_unregister_rfkill_notifier(char *node) } } +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, + u8 *value) +{ + struct eeepc_laptop *eeepc = hotplug_slot->private; + int val = get_acpi(eeepc, CM_ASL_WLAN); + + if (val == 1 || val == 0) + *value = val; + else + return -EINVAL; + + return 0; +} + static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) { kfree(hotplug_slot->info); kfree(hotplug_slot); } -static int eeepc_setup_pci_hotplug(void) +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { + .owner = THIS_MODULE, + .get_adapter_status = eeepc_get_adapter_status, + .get_power_status = eeepc_get_adapter_status, +}; + +static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) { int ret = -ENOMEM; struct pci_bus *bus = pci_find_bus(0, 1); @@ -798,22 +653,22 @@ static int eeepc_setup_pci_hotplug(void) return -ENODEV; } - ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!ehotk->hotplug_slot) + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!eeepc->hotplug_slot) goto error_slot; - ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), + eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); - if (!ehotk->hotplug_slot->info) + if (!eeepc->hotplug_slot->info) goto error_info; - ehotk->hotplug_slot->private = ehotk; - ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; - ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops; - eeepc_get_adapter_status(ehotk->hotplug_slot, - &ehotk->hotplug_slot->info->adapter_status); + eeepc->hotplug_slot->private = eeepc; + eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; + eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; + eeepc_get_adapter_status(eeepc->hotplug_slot, + &eeepc->hotplug_slot->info->adapter_status); - ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi"); + ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -822,17 +677,156 @@ static int eeepc_setup_pci_hotplug(void) return 0; error_register: - kfree(ehotk->hotplug_slot->info); + kfree(eeepc->hotplug_slot->info); error_info: - kfree(ehotk->hotplug_slot); - ehotk->hotplug_slot = NULL; + kfree(eeepc->hotplug_slot); + eeepc->hotplug_slot = NULL; error_slot: return ret; } +/* + * Rfkill devices + */ +static int eeepc_rfkill_set(void *data, bool blocked) +{ + acpi_handle handle = data; + + return write_acpi_int(handle, NULL, !blocked); +} + +static const struct rfkill_ops eeepc_rfkill_ops = { + .set_block = eeepc_rfkill_set, +}; + +static int eeepc_new_rfkill(struct eeepc_laptop *eeepc, + struct rfkill **rfkill, + const char *name, + enum rfkill_type type, int cm) +{ + acpi_handle handle; + int result; + + result = acpi_setter_handle(eeepc, cm, &handle); + if (result < 0) + return result; + + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, handle); + + if (!*rfkill) + return -EINVAL; + + rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1); + result = rfkill_register(*rfkill); + if (result) { + rfkill_destroy(*rfkill); + *rfkill = NULL; + return result; + } + return 0; +} + +static void eeepc_rfkill_exit(struct eeepc_laptop *eeepc) +{ + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); + if (eeepc->wlan_rfkill) { + rfkill_unregister(eeepc->wlan_rfkill); + rfkill_destroy(eeepc->wlan_rfkill); + eeepc->wlan_rfkill = NULL; + } + /* + * Refresh pci hotplug in case the rfkill state was changed after + * eeepc_unregister_rfkill_notifier() + */ + eeepc_rfkill_hotplug(eeepc); + if (eeepc->hotplug_slot) + pci_hp_deregister(eeepc->hotplug_slot); + + if (eeepc->bluetooth_rfkill) { + rfkill_unregister(eeepc->bluetooth_rfkill); + rfkill_destroy(eeepc->bluetooth_rfkill); + eeepc->bluetooth_rfkill = NULL; + } + if (eeepc->wwan3g_rfkill) { + rfkill_unregister(eeepc->wwan3g_rfkill); + rfkill_destroy(eeepc->wwan3g_rfkill); + eeepc->wwan3g_rfkill = NULL; + } + if (eeepc->wimax_rfkill) { + rfkill_unregister(eeepc->wimax_rfkill); + rfkill_destroy(eeepc->wimax_rfkill); + eeepc->wimax_rfkill = NULL; + } +} + +static int eeepc_rfkill_init(struct eeepc_laptop *eeepc) +{ + int result = 0; + + mutex_init(&eeepc->hotplug_lock); + + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, + "eeepc-wlan", RFKILL_TYPE_WLAN, + CM_ASL_WLAN); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, + "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, + CM_ASL_BLUETOOTH); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, + "eeepc-wwan3g", RFKILL_TYPE_WWAN, + CM_ASL_3G); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, + "eeepc-wimax", RFKILL_TYPE_WIMAX, + CM_ASL_WIMAX); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_setup_pci_hotplug(eeepc); + /* + * If we get -EBUSY then something else is handling the PCI hotplug - + * don't fail in this case + */ + if (result == -EBUSY) + result = 0; + + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); + /* + * Refresh pci hotplug in case the rfkill state was changed during + * setup. + */ + eeepc_rfkill_hotplug(eeepc); + +exit: + if (result && result != -ENODEV) + eeepc_rfkill_exit(eeepc); + return result; +} + +/* + * Platform driver - hibernate/resume callbacks + */ static int eeepc_hotk_thaw(struct device *device) { - if (ehotk->wlan_rfkill) { + struct eeepc_laptop *eeepc = dev_get_drvdata(device); + + if (eeepc->wlan_rfkill) { bool wlan; /* @@ -840,8 +834,8 @@ static int eeepc_hotk_thaw(struct device *device) * during suspend. Normally it restores it on resume, but * we should kick it ourselves in case hibernation is aborted. */ - wlan = get_acpi(CM_ASL_WLAN); - set_acpi(CM_ASL_WLAN, wlan); + wlan = get_acpi(eeepc, CM_ASL_WLAN); + set_acpi(eeepc, CM_ASL_WLAN, wlan); } return 0; @@ -849,70 +843,96 @@ static int eeepc_hotk_thaw(struct device *device) static int eeepc_hotk_restore(struct device *device) { + struct eeepc_laptop *eeepc = dev_get_drvdata(device); + /* Refresh both wlan rfkill state and pci hotplug */ - if (ehotk->wlan_rfkill) - eeepc_rfkill_hotplug(); - - if (ehotk->bluetooth_rfkill) - rfkill_set_sw_state(ehotk->bluetooth_rfkill, - get_acpi(CM_ASL_BLUETOOTH) != 1); - if (ehotk->wwan3g_rfkill) - rfkill_set_sw_state(ehotk->wwan3g_rfkill, - get_acpi(CM_ASL_3G) != 1); - if (ehotk->wimax_rfkill) - rfkill_set_sw_state(ehotk->wimax_rfkill, - get_acpi(CM_ASL_WIMAX) != 1); + if (eeepc->wlan_rfkill) + eeepc_rfkill_hotplug(eeepc); + + if (eeepc->bluetooth_rfkill) + rfkill_set_sw_state(eeepc->bluetooth_rfkill, + get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); + if (eeepc->wwan3g_rfkill) + rfkill_set_sw_state(eeepc->wwan3g_rfkill, + get_acpi(eeepc, CM_ASL_3G) != 1); + if (eeepc->wimax_rfkill) + rfkill_set_sw_state(eeepc->wimax_rfkill, + get_acpi(eeepc, CM_ASL_WIMAX) != 1); return 0; } +static const struct dev_pm_ops eeepc_pm_ops = { + .thaw = eeepc_hotk_thaw, + .restore = eeepc_hotk_restore, +}; + +static struct platform_driver platform_driver = { + .driver = { + .name = EEEPC_LAPTOP_FILE, + .owner = THIS_MODULE, + .pm = &eeepc_pm_ops, + } +}; + /* - * Hwmon + * Hwmon device */ + +#define EEEPC_EC_SC00 0x61 +#define EEEPC_EC_FAN_PWM (EEEPC_EC_SC00 + 2) /* Fan PWM duty cycle (%) */ +#define EEEPC_EC_FAN_HRPM (EEEPC_EC_SC00 + 5) /* High byte, fan speed (RPM) */ +#define EEEPC_EC_FAN_LRPM (EEEPC_EC_SC00 + 6) /* Low byte, fan speed (RPM) */ + +#define EEEPC_EC_SFB0 0xD0 +#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */ + static int eeepc_get_fan_pwm(void) { - int value = 0; + u8 value = 0; - read_acpi_int(NULL, EEEPC_EC_FAN_PWM, &value); - value = value * 255 / 100; - return (value); + ec_read(EEEPC_EC_FAN_PWM, &value); + return value * 255 / 100; } static void eeepc_set_fan_pwm(int value) { value = SENSORS_LIMIT(value, 0, 255); value = value * 100 / 255; - ec_write(EEEPC_EC_SC02, value); + ec_write(EEEPC_EC_FAN_PWM, value); } static int eeepc_get_fan_rpm(void) { - int high = 0; - int low = 0; + u8 high = 0; + u8 low = 0; - read_acpi_int(NULL, EEEPC_EC_FAN_HRPM, &high); - read_acpi_int(NULL, EEEPC_EC_FAN_LRPM, &low); - return (high << 8 | low); + ec_read(EEEPC_EC_FAN_HRPM, &high); + ec_read(EEEPC_EC_FAN_LRPM, &low); + return high << 8 | low; } static int eeepc_get_fan_ctrl(void) { - int value = 0; + u8 value = 0; - read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); - return ((value & 0x02 ? 1 : 0)); + ec_read(EEEPC_EC_FAN_CTRL, &value); + if (value & 0x02) + return 1; /* manual */ + else + return 2; /* automatic */ } static void eeepc_set_fan_ctrl(int manual) { - int value = 0; + u8 value = 0; - read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); - if (manual) + ec_read(EEEPC_EC_FAN_CTRL, &value); + if (manual == 1) value |= 0x02; else value &= ~0x02; - ec_write(EEEPC_EC_SFB3, value); + ec_write(EEEPC_EC_FAN_CTRL, value); } static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) @@ -970,348 +990,485 @@ static struct attribute_group hwmon_attribute_group = { .attrs = hwmon_attributes }; -/* - * exit/init - */ -static void eeepc_backlight_exit(void) +static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc) { - if (eeepc_backlight_device) - backlight_device_unregister(eeepc_backlight_device); - eeepc_backlight_device = NULL; + struct device *hwmon; + + hwmon = eeepc->hwmon_device; + if (!hwmon) + return; + sysfs_remove_group(&hwmon->kobj, + &hwmon_attribute_group); + hwmon_device_unregister(hwmon); + eeepc->hwmon_device = NULL; } -static void eeepc_rfkill_exit(void) +static int eeepc_hwmon_init(struct eeepc_laptop *eeepc) { - eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P5"); - eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); - eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); - if (ehotk->wlan_rfkill) { - rfkill_unregister(ehotk->wlan_rfkill); - rfkill_destroy(ehotk->wlan_rfkill); - ehotk->wlan_rfkill = NULL; - } - /* - * Refresh pci hotplug in case the rfkill state was changed after - * eeepc_unregister_rfkill_notifier() - */ - eeepc_rfkill_hotplug(); - if (ehotk->hotplug_slot) - pci_hp_deregister(ehotk->hotplug_slot); - - if (ehotk->bluetooth_rfkill) { - rfkill_unregister(ehotk->bluetooth_rfkill); - rfkill_destroy(ehotk->bluetooth_rfkill); - ehotk->bluetooth_rfkill = NULL; - } - if (ehotk->wwan3g_rfkill) { - rfkill_unregister(ehotk->wwan3g_rfkill); - rfkill_destroy(ehotk->wwan3g_rfkill); - ehotk->wwan3g_rfkill = NULL; - } - if (ehotk->wimax_rfkill) { - rfkill_unregister(ehotk->wimax_rfkill); - rfkill_destroy(ehotk->wimax_rfkill); - ehotk->wimax_rfkill = NULL; + struct device *hwmon; + int result; + + hwmon = hwmon_device_register(&eeepc->platform_device->dev); + if (IS_ERR(hwmon)) { + pr_err("Could not register eeepc hwmon device\n"); + eeepc->hwmon_device = NULL; + return PTR_ERR(hwmon); } + eeepc->hwmon_device = hwmon; + result = sysfs_create_group(&hwmon->kobj, + &hwmon_attribute_group); + if (result) + eeepc_hwmon_exit(eeepc); + return result; } -static void eeepc_input_exit(void) +/* + * Backlight device + */ +static int read_brightness(struct backlight_device *bd) { - if (ehotk->inputdev) - input_unregister_device(ehotk->inputdev); + struct eeepc_laptop *eeepc = bl_get_data(bd); + + return get_acpi(eeepc, CM_ASL_PANELBRIGHT); } -static void eeepc_hwmon_exit(void) +static int set_brightness(struct backlight_device *bd, int value) { - struct device *hwmon; + struct eeepc_laptop *eeepc = bl_get_data(bd); - hwmon = eeepc_hwmon_device; - if (!hwmon) - return ; - sysfs_remove_group(&hwmon->kobj, - &hwmon_attribute_group); - hwmon_device_unregister(hwmon); - eeepc_hwmon_device = NULL; + return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value); } -static int eeepc_new_rfkill(struct rfkill **rfkill, - const char *name, struct device *dev, - enum rfkill_type type, int cm) +static int update_bl_status(struct backlight_device *bd) { - int result; + return set_brightness(bd, bd->props.brightness); +} - result = get_acpi(cm); - if (result < 0) - return result; +static struct backlight_ops eeepcbl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; - *rfkill = rfkill_alloc(name, dev, type, - &eeepc_rfkill_ops, (void *)(unsigned long)cm); +static int eeepc_backlight_notify(struct eeepc_laptop *eeepc) +{ + struct backlight_device *bd = eeepc->backlight_device; + int old = bd->props.brightness; - if (!*rfkill) - return -EINVAL; + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); - rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1); - result = rfkill_register(*rfkill); - if (result) { - rfkill_destroy(*rfkill); - *rfkill = NULL; - return result; - } - return 0; + return old; } - -static int eeepc_rfkill_init(struct device *dev) +static int eeepc_backlight_init(struct eeepc_laptop *eeepc) { - int result = 0; - - mutex_init(&ehotk->hotplug_lock); + struct backlight_device *bd; - result = eeepc_new_rfkill(&ehotk->wlan_rfkill, - "eeepc-wlan", dev, - RFKILL_TYPE_WLAN, CM_ASL_WLAN); + bd = backlight_device_register(EEEPC_LAPTOP_FILE, + &eeepc->platform_device->dev, + eeepc, &eeepcbl_ops); + if (IS_ERR(bd)) { + pr_err("Could not register eeepc backlight device\n"); + eeepc->backlight_device = NULL; + return PTR_ERR(bd); + } + eeepc->backlight_device = bd; + bd->props.max_brightness = 15; + bd->props.brightness = read_brightness(bd); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); + return 0; +} - if (result && result != -ENODEV) - goto exit; +static void eeepc_backlight_exit(struct eeepc_laptop *eeepc) +{ + if (eeepc->backlight_device) + backlight_device_unregister(eeepc->backlight_device); + eeepc->backlight_device = NULL; +} - result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill, - "eeepc-bluetooth", dev, - RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); - if (result && result != -ENODEV) - goto exit; +/* + * Input device (i.e. hotkeys) + */ +static struct key_entry *eeepc_get_entry_by_scancode( + struct eeepc_laptop *eeepc, + int code) +{ + struct key_entry *key; - result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill, - "eeepc-wwan3g", dev, - RFKILL_TYPE_WWAN, CM_ASL_3G); + for (key = eeepc->keymap; key->type != KE_END; key++) + if (code == key->code) + return key; - if (result && result != -ENODEV) - goto exit; + return NULL; +} - result = eeepc_new_rfkill(&ehotk->wimax_rfkill, - "eeepc-wimax", dev, - RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); +static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) +{ + static struct key_entry *key; - if (result && result != -ENODEV) - goto exit; + key = eeepc_get_entry_by_scancode(eeepc, event); + if (key) { + switch (key->type) { + case KE_KEY: + input_report_key(eeepc->inputdev, key->keycode, + 1); + input_sync(eeepc->inputdev); + input_report_key(eeepc->inputdev, key->keycode, + 0); + input_sync(eeepc->inputdev); + break; + } + } +} - result = eeepc_setup_pci_hotplug(); - /* - * If we get -EBUSY then something else is handling the PCI hotplug - - * don't fail in this case - */ - if (result == -EBUSY) - result = 0; +static struct key_entry *eeepc_get_entry_by_keycode( + struct eeepc_laptop *eeepc, int code) +{ + struct key_entry *key; - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); - /* - * Refresh pci hotplug in case the rfkill state was changed during - * setup. - */ - eeepc_rfkill_hotplug(); + for (key = eeepc->keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; -exit: - if (result && result != -ENODEV) - eeepc_rfkill_exit(); - return result; + return NULL; } -static int eeepc_backlight_init(struct device *dev) +static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) { - struct backlight_device *bd; + struct eeepc_laptop *eeepc = input_get_drvdata(dev); + struct key_entry *key = eeepc_get_entry_by_scancode(eeepc, scancode); - bd = backlight_device_register(EEEPC_HOTK_FILE, dev, - NULL, &eeepcbl_ops); - if (IS_ERR(bd)) { - pr_err("Could not register eeepc backlight device\n"); - eeepc_backlight_device = NULL; - return PTR_ERR(bd); + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; } - eeepc_backlight_device = bd; - bd->props.max_brightness = 15; - bd->props.brightness = read_brightness(NULL); - bd->props.power = FB_BLANK_UNBLANK; - backlight_update_status(bd); - return 0; + + return -EINVAL; } -static int eeepc_hwmon_init(struct device *dev) +static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) { - struct device *hwmon; - int result; + struct eeepc_laptop *eeepc = input_get_drvdata(dev); + struct key_entry *key; + int old_keycode; - hwmon = hwmon_device_register(dev); - if (IS_ERR(hwmon)) { - pr_err("Could not register eeepc hwmon device\n"); - eeepc_hwmon_device = NULL; - return PTR_ERR(hwmon); + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = eeepc_get_entry_by_scancode(eeepc, scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!eeepc_get_entry_by_keycode(eeepc, old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; } - eeepc_hwmon_device = hwmon; - result = sysfs_create_group(&hwmon->kobj, - &hwmon_attribute_group); - if (result) - eeepc_hwmon_exit(); - return result; + + return -EINVAL; } -static int eeepc_input_init(struct device *dev) +static int eeepc_input_init(struct eeepc_laptop *eeepc) { const struct key_entry *key; int result; - ehotk->inputdev = input_allocate_device(); - if (!ehotk->inputdev) { + eeepc->inputdev = input_allocate_device(); + if (!eeepc->inputdev) { pr_info("Unable to allocate input device\n"); return -ENOMEM; } - ehotk->inputdev->name = "Asus EeePC extra buttons"; - ehotk->inputdev->dev.parent = dev; - ehotk->inputdev->phys = EEEPC_HOTK_FILE "/input0"; - ehotk->inputdev->id.bustype = BUS_HOST; - ehotk->inputdev->getkeycode = eeepc_getkeycode; - ehotk->inputdev->setkeycode = eeepc_setkeycode; - + eeepc->inputdev->name = "Asus EeePC extra buttons"; + eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; + eeepc->inputdev->phys = EEEPC_LAPTOP_FILE "/input0"; + eeepc->inputdev->id.bustype = BUS_HOST; + eeepc->inputdev->getkeycode = eeepc_getkeycode; + eeepc->inputdev->setkeycode = eeepc_setkeycode; + input_set_drvdata(eeepc->inputdev, eeepc); + + eeepc->keymap = kmemdup(eeepc_keymap, sizeof(eeepc_keymap), + GFP_KERNEL); for (key = eeepc_keymap; key->type != KE_END; key++) { switch (key->type) { case KE_KEY: - set_bit(EV_KEY, ehotk->inputdev->evbit); - set_bit(key->keycode, ehotk->inputdev->keybit); + set_bit(EV_KEY, eeepc->inputdev->evbit); + set_bit(key->keycode, eeepc->inputdev->keybit); break; } } - result = input_register_device(ehotk->inputdev); + result = input_register_device(eeepc->inputdev); if (result) { pr_info("Unable to register input device\n"); - input_free_device(ehotk->inputdev); + input_free_device(eeepc->inputdev); return result; } return 0; } -static int __devinit eeepc_hotk_add(struct acpi_device *device) +static void eeepc_input_exit(struct eeepc_laptop *eeepc) { - struct device *dev; + if (eeepc->inputdev) { + input_unregister_device(eeepc->inputdev); + kfree(eeepc->keymap); + } +} + +/* + * ACPI driver + */ +static void eeepc_acpi_notify(struct acpi_device *device, u32 event) +{ + struct eeepc_laptop *eeepc = acpi_driver_data(device); + u16 count; + + if (event > ACPI_MAX_SYS_NOTIFY) + return; + count = eeepc->event_count[event % 128]++; + acpi_bus_generate_proc_event(device, event, count); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, + count); + + /* Brightness events are special */ + if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { + + /* Ignore them completely if the acpi video driver is used */ + if (eeepc->backlight_device != NULL) { + int old_brightness, new_brightness; + + /* Update the backlight device. */ + old_brightness = eeepc_backlight_notify(eeepc); + + /* Convert event to keypress (obsolescent hack) */ + new_brightness = event - NOTIFY_BRN_MIN; + + if (new_brightness < old_brightness) { + event = NOTIFY_BRN_MIN; /* brightness down */ + } else if (new_brightness > old_brightness) { + event = NOTIFY_BRN_MAX; /* brightness up */ + } else { + /* + * no change in brightness - already at min/max, + * event will be desired value (or else ignored) + */ + } + eeepc_input_notify(eeepc, event); + } + } else { + /* Everything else is a bona-fide keypress event */ + eeepc_input_notify(eeepc, event); + } +} + +static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) +{ + int dummy; + + /* Some BIOSes do not report cm although it is avaliable. + Check if cm_getv[cm] works and, if yes, assume cm should be set. */ + if (!(eeepc->cm_supported & (1 << cm)) + && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { + pr_info("%s (%x) not reported by BIOS," + " enabling anyway\n", name, 1 << cm); + eeepc->cm_supported |= 1 << cm; + } +} + +static void cmsg_quirks(struct eeepc_laptop *eeepc) +{ + cmsg_quirk(eeepc, CM_ASL_LID, "LID"); + cmsg_quirk(eeepc, CM_ASL_TYPE, "TYPE"); + cmsg_quirk(eeepc, CM_ASL_PANELPOWER, "PANELPOWER"); + cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); +} + +static int eeepc_acpi_init(struct eeepc_laptop *eeepc, + struct acpi_device *device) +{ + unsigned int init_flags; int result; - if (!device) - return -EINVAL; - pr_notice(EEEPC_HOTK_NAME "\n"); - ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); - if (!ehotk) - return -ENOMEM; - ehotk->init_flag = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; - ehotk->handle = device->handle; - strcpy(acpi_device_name(device), EEEPC_HOTK_DEVICE_NAME); - strcpy(acpi_device_class(device), EEEPC_HOTK_CLASS); - device->driver_data = ehotk; - ehotk->device = device; - - result = eeepc_hotk_check(); + result = acpi_bus_get_status(device); if (result) - goto fail_platform_driver; - eeepc_enable_camera(); + return result; + if (!device->status.present) { + pr_err("Hotkey device not present, aborting\n"); + return -ENODEV; + } - /* Register platform stuff */ - result = platform_driver_register(&platform_driver); - if (result) - goto fail_platform_driver; - platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); - if (!platform_device) { - result = -ENOMEM; - goto fail_platform_device1; + init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; + pr_notice("Hotkey init flags 0x%x\n", init_flags); + + if (write_acpi_int(eeepc->handle, "INIT", init_flags)) { + pr_err("Hotkey initialization failed\n"); + return -ENODEV; } - result = platform_device_add(platform_device); - if (result) - goto fail_platform_device2; - result = sysfs_create_group(&platform_device->dev.kobj, - &platform_attribute_group); + + /* get control methods supported */ + if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) { + pr_err("Get control methods supported failed\n"); + return -ENODEV; + } + cmsg_quirks(eeepc); + pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); + + return 0; +} + +static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc) +{ + /* + * If the following call to set_acpi() fails, it's because there's no + * camera so we can ignore the error. + */ + if (get_acpi(eeepc, CM_ASL_CAMERA) == 0) + set_acpi(eeepc, CM_ASL_CAMERA, 1); +} + +static bool eeepc_device_present; + +static int __devinit eeepc_acpi_add(struct acpi_device *device) +{ + struct eeepc_laptop *eeepc; + int result; + + pr_notice(EEEPC_LAPTOP_NAME "\n"); + eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL); + if (!eeepc) + return -ENOMEM; + eeepc->handle = device->handle; + strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); + strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); + device->driver_data = eeepc; + + result = eeepc_acpi_init(eeepc, device); if (result) - goto fail_sysfs; + goto fail_platform; + eeepc_enable_camera(eeepc); - dev = &platform_device->dev; + /* + * Register the platform device first. It is used as a parent for the + * sub-devices below. + * + * Note that if there are multiple instances of this ACPI device it + * will bail out, because the platform device is registered with a + * fixed name. Of course it doesn't make sense to have more than one, + * and machine-specific scripts find the fixed name convenient. But + * It's also good for us to exclude multiple instances because both + * our hwmon and our wlan rfkill subdevice use global ACPI objects + * (the EC and the wlan PCI slot respectively). + */ + result = eeepc_platform_init(eeepc); + if (result) + goto fail_platform; if (!acpi_video_backlight_support()) { - result = eeepc_backlight_init(dev); + result = eeepc_backlight_init(eeepc); if (result) goto fail_backlight; } else - pr_info("Backlight controlled by ACPI video " - "driver\n"); + pr_info("Backlight controlled by ACPI video driver\n"); - result = eeepc_input_init(dev); + result = eeepc_input_init(eeepc); if (result) goto fail_input; - result = eeepc_hwmon_init(dev); + result = eeepc_hwmon_init(eeepc); if (result) goto fail_hwmon; - result = eeepc_rfkill_init(dev); + result = eeepc_led_init(eeepc); + if (result) + goto fail_led; + + result = eeepc_rfkill_init(eeepc); if (result) goto fail_rfkill; + eeepc_device_present = true; return 0; fail_rfkill: - eeepc_hwmon_exit(); + eeepc_led_exit(eeepc); +fail_led: + eeepc_hwmon_exit(eeepc); fail_hwmon: - eeepc_input_exit(); + eeepc_input_exit(eeepc); fail_input: - eeepc_backlight_exit(); + eeepc_backlight_exit(eeepc); fail_backlight: - sysfs_remove_group(&platform_device->dev.kobj, - &platform_attribute_group); -fail_sysfs: - platform_device_del(platform_device); -fail_platform_device2: - platform_device_put(platform_device); -fail_platform_device1: - platform_driver_unregister(&platform_driver); -fail_platform_driver: - kfree(ehotk); + eeepc_platform_exit(eeepc); +fail_platform: + kfree(eeepc); return result; } -static int eeepc_hotk_remove(struct acpi_device *device, int type) +static int eeepc_acpi_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) - return -EINVAL; + struct eeepc_laptop *eeepc = acpi_driver_data(device); - eeepc_backlight_exit(); - eeepc_rfkill_exit(); - eeepc_input_exit(); - eeepc_hwmon_exit(); - sysfs_remove_group(&platform_device->dev.kobj, - &platform_attribute_group); - platform_device_unregister(platform_device); - platform_driver_unregister(&platform_driver); + eeepc_backlight_exit(eeepc); + eeepc_rfkill_exit(eeepc); + eeepc_input_exit(eeepc); + eeepc_hwmon_exit(eeepc); + eeepc_led_exit(eeepc); + eeepc_platform_exit(eeepc); - kfree(ehotk); + kfree(eeepc); return 0; } + +static const struct acpi_device_id eeepc_device_ids[] = { + {EEEPC_ACPI_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); + +static struct acpi_driver eeepc_acpi_driver = { + .name = EEEPC_LAPTOP_NAME, + .class = EEEPC_ACPI_CLASS, + .owner = THIS_MODULE, + .ids = eeepc_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = eeepc_acpi_add, + .remove = eeepc_acpi_remove, + .notify = eeepc_acpi_notify, + }, +}; + + static int __init eeepc_laptop_init(void) { int result; - if (acpi_disabled) - return -ENODEV; - result = acpi_bus_register_driver(&eeepc_hotk_driver); + result = platform_driver_register(&platform_driver); if (result < 0) return result; - if (!ehotk) { - acpi_bus_unregister_driver(&eeepc_hotk_driver); - return -ENODEV; + + result = acpi_bus_register_driver(&eeepc_acpi_driver); + if (result < 0) + goto fail_acpi_driver; + if (!eeepc_device_present) { + result = -ENODEV; + goto fail_no_device; } return 0; + +fail_no_device: + acpi_bus_unregister_driver(&eeepc_acpi_driver); +fail_acpi_driver: + platform_driver_unregister(&platform_driver); + return result; } static void __exit eeepc_laptop_exit(void) { - acpi_bus_unregister_driver(&eeepc_hotk_driver); + acpi_bus_unregister_driver(&eeepc_acpi_driver); + platform_driver_unregister(&platform_driver); } module_init(eeepc_laptop_init); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index f00a71c..63c3e65 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -51,6 +51,12 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_HOTKEY_QUERY 0xc +enum hp_wmi_radio { + HPWMI_WIFI = 0, + HPWMI_BLUETOOTH = 1, + HPWMI_WWAN = 2, +}; + static int __init hp_wmi_bios_setup(struct platform_device *device); static int __exit hp_wmi_bios_remove(struct platform_device *device); static int hp_wmi_resume_handler(struct device *device); @@ -175,8 +181,8 @@ static int hp_wmi_tablet_state(void) static int hp_wmi_set_block(void *data, bool blocked) { - unsigned long b = (unsigned long) data; - int query = BIT(b + 8) | ((!blocked) << b); + enum hp_wmi_radio r = (enum hp_wmi_radio) data; + int query = BIT(r + 8) | ((!blocked) << r); return hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, query); } @@ -185,31 +191,23 @@ static const struct rfkill_ops hp_wmi_rfkill_ops = { .set_block = hp_wmi_set_block, }; -static bool hp_wmi_wifi_state(void) -{ - int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); - - if (wireless & 0x100) - return false; - else - return true; -} - -static bool hp_wmi_bluetooth_state(void) +static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) { int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); + int mask = 0x200 << (r * 8); - if (wireless & 0x10000) + if (wireless & mask) return false; else return true; } -static bool hp_wmi_wwan_state(void) +static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) { int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); + int mask = 0x800 << (r * 8); - if (wireless & 0x1000000) + if (wireless & mask) return false; else return true; @@ -334,49 +332,55 @@ static void hp_wmi_notify(u32 value, void *context) struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; static struct key_entry *key; union acpi_object *obj; + int eventcode; wmi_get_event_data(value, &response); obj = (union acpi_object *)response.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == 8) { - int eventcode = *((u8 *) obj->buffer.pointer); - if (eventcode == 0x4) - eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, - 0); - key = hp_wmi_get_entry_by_scancode(eventcode); - if (key) { - switch (key->type) { - case KE_KEY: - input_report_key(hp_wmi_input_dev, - key->keycode, 1); - input_sync(hp_wmi_input_dev); - input_report_key(hp_wmi_input_dev, - key->keycode, 0); - input_sync(hp_wmi_input_dev); - break; - } - } else if (eventcode == 0x1) { - input_report_switch(hp_wmi_input_dev, SW_DOCK, - hp_wmi_dock_state()); - input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, - hp_wmi_tablet_state()); + if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length != 8) { + printk(KERN_INFO "HP WMI: Unknown response received\n"); + return; + } + + eventcode = *((u8 *) obj->buffer.pointer); + if (eventcode == 0x4) + eventcode = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, + 0); + key = hp_wmi_get_entry_by_scancode(eventcode); + if (key) { + switch (key->type) { + case KE_KEY: + input_report_key(hp_wmi_input_dev, + key->keycode, 1); + input_sync(hp_wmi_input_dev); + input_report_key(hp_wmi_input_dev, + key->keycode, 0); input_sync(hp_wmi_input_dev); - } else if (eventcode == 0x5) { - if (wifi_rfkill) - rfkill_set_sw_state(wifi_rfkill, - hp_wmi_wifi_state()); - if (bluetooth_rfkill) - rfkill_set_sw_state(bluetooth_rfkill, - hp_wmi_bluetooth_state()); - if (wwan_rfkill) - rfkill_set_sw_state(wwan_rfkill, - hp_wmi_wwan_state()); - } else - printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", - eventcode); + break; + } + } else if (eventcode == 0x1) { + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); + } else if (eventcode == 0x5) { + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); } else - printk(KERN_INFO "HP WMI: Unknown response received\n"); + printk(KERN_INFO "HP WMI: Unknown key pressed - %x\n", + eventcode); } static int __init hp_wmi_input_setup(void) @@ -455,7 +459,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, RFKILL_TYPE_WLAN, &hp_wmi_rfkill_ops, - (void *) 0); + (void *) HPWMI_WIFI); + rfkill_init_sw_state(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI)); + rfkill_set_hw_state(wifi_rfkill, + hp_wmi_get_hw_state(HPWMI_WIFI)); err = rfkill_register(wifi_rfkill); if (err) goto register_wifi_error; @@ -465,7 +473,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) bluetooth_rfkill = rfkill_alloc("hp-bluetooth", &device->dev, RFKILL_TYPE_BLUETOOTH, &hp_wmi_rfkill_ops, - (void *) 1); + (void *) HPWMI_BLUETOOTH); + rfkill_init_sw_state(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH)); + rfkill_set_hw_state(bluetooth_rfkill, + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); err = rfkill_register(bluetooth_rfkill); if (err) goto register_bluetooth_error; @@ -475,7 +487,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) wwan_rfkill = rfkill_alloc("hp-wwan", &device->dev, RFKILL_TYPE_WWAN, &hp_wmi_rfkill_ops, - (void *) 2); + (void *) HPWMI_WWAN); + rfkill_init_sw_state(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN)); + rfkill_set_hw_state(wwan_rfkill, + hp_wmi_get_hw_state(HPWMI_WWAN)); err = rfkill_register(wwan_rfkill); if (err) goto register_wwan_err; @@ -533,6 +549,19 @@ static int hp_wmi_resume_handler(struct device *device) input_sync(hp_wmi_input_dev); } + if (wifi_rfkill) + rfkill_set_states(wifi_rfkill, + hp_wmi_get_sw_state(HPWMI_WIFI), + hp_wmi_get_hw_state(HPWMI_WIFI)); + if (bluetooth_rfkill) + rfkill_set_states(bluetooth_rfkill, + hp_wmi_get_sw_state(HPWMI_BLUETOOTH), + hp_wmi_get_hw_state(HPWMI_BLUETOOTH)); + if (wwan_rfkill) + rfkill_set_states(wwan_rfkill, + hp_wmi_get_sw_state(HPWMI_WWAN), + hp_wmi_get_hw_state(HPWMI_WWAN)); + return 0; } diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c new file mode 100644 index 0000000..0c8fe14 --- /dev/null +++ b/drivers/platform/x86/msi-wmi.c @@ -0,0 +1,293 @@ +/* + * MSI WMI hotkeys + * + * Copyright (C) 2009 Novell <trenn@suse.de> + * + * Most stuff taken over from hp-wmi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/input/sparse-keymap.h> +#include <linux/acpi.h> +#include <linux/backlight.h> + +MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); +MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); +MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); + +/* Temporary workaround until the WMI sysfs interface goes in + { "svn", DMI_SYS_VENDOR }, + { "pn", DMI_PRODUCT_NAME }, + { "pvr", DMI_PRODUCT_VERSION }, + { "rvn", DMI_BOARD_VENDOR }, + { "rn", DMI_BOARD_NAME }, +*/ + +MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*"); + +#define DRV_NAME "msi-wmi" +#define DRV_PFX DRV_NAME ": " + +#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" +#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" + +#define dprintk(msg...) pr_debug(DRV_PFX msg) + +#define KEYCODE_BASE 0xD0 +#define MSI_WMI_BRIGHTNESSUP KEYCODE_BASE +#define MSI_WMI_BRIGHTNESSDOWN (KEYCODE_BASE + 1) +#define MSI_WMI_VOLUMEUP (KEYCODE_BASE + 2) +#define MSI_WMI_VOLUMEDOWN (KEYCODE_BASE + 3) +static struct key_entry msi_wmi_keymap[] = { + { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, + { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, + { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, + { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, + { KE_END, 0} +}; +static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; + +struct backlight_device *backlight; + +static int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; + +static struct input_dev *msi_wmi_input_dev; + +static int msi_wmi_query_block(int instance, int *ret) +{ + acpi_status status; + union acpi_object *obj; + + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output); + + obj = output.pointer; + + if (!obj || obj->type != ACPI_TYPE_INTEGER) { + if (obj) { + printk(KERN_ERR DRV_PFX "query block returned object " + "type: %d - buffer length:%d\n", obj->type, + obj->type == ACPI_TYPE_BUFFER ? + obj->buffer.length : 0); + } + kfree(obj); + return -EINVAL; + } + *ret = obj->integer.value; + kfree(obj); + return 0; +} + +static int msi_wmi_set_block(int instance, int value) +{ + acpi_status status; + + struct acpi_buffer input = { sizeof(int), &value }; + + dprintk("Going to set block of instance: %d - value: %d\n", + instance, value); + + status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); + + return ACPI_SUCCESS(status) ? 0 : 1; +} + +static int bl_get(struct backlight_device *bd) +{ + int level, err, ret; + + /* Instance 1 is "get backlight", cmp with DSDT */ + err = msi_wmi_query_block(1, &ret); + if (err) { + printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err); + return -EINVAL; + } + dprintk("Get: Query block returned: %d\n", ret); + for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { + if (backlight_map[level] == ret) { + dprintk("Current backlight level: 0x%X - index: %d\n", + backlight_map[level], level); + break; + } + } + if (level == ARRAY_SIZE(backlight_map)) { + printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n", + ret); + return -EINVAL; + } + return level; +} + +static int bl_set_status(struct backlight_device *bd) +{ + int bright = bd->props.brightness; + if (bright >= ARRAY_SIZE(backlight_map) || bright < 0) + return -EINVAL; + + /* Instance 0 is "set backlight" */ + return msi_wmi_set_block(0, backlight_map[bright]); +} + +static struct backlight_ops msi_backlight_ops = { + .get_brightness = bl_get, + .update_status = bl_set_status, +}; + +static void msi_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; + ktime_t cur; + + wmi_get_event_data(value, &response); + + obj = (union acpi_object *)response.pointer; + + if (obj && obj->type == ACPI_TYPE_INTEGER) { + int eventcode = obj->integer.value; + dprintk("Eventcode: 0x%x\n", eventcode); + key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, + eventcode); + if (key) { + ktime_t diff; + cur = ktime_get_real(); + diff = ktime_sub(cur, last_pressed[key->code - + KEYCODE_BASE]); + /* Ignore event if the same event happened in a 50 ms + timeframe -> Key press may result in 10-20 GPEs */ + if (ktime_to_us(diff) < 1000 * 50) { + dprintk("Suppressed key event 0x%X - " + "Last press was %lld us ago\n", + key->code, ktime_to_us(diff)); + return; + } + last_pressed[key->code - KEYCODE_BASE] = cur; + + if (key->type == KE_KEY && + /* Brightness is served via acpi video driver */ + (!acpi_video_backlight_support() || + (key->code != MSI_WMI_BRIGHTNESSUP && + key->code != MSI_WMI_BRIGHTNESSDOWN))) { + dprintk("Send key: 0x%X - " + "Input layer keycode: %d\n", key->code, + key->keycode); + sparse_keymap_report_entry(msi_wmi_input_dev, + key, 1, true); + } + } else + printk(KERN_INFO "Unknown key pressed - %x\n", + eventcode); + } else + printk(KERN_INFO DRV_PFX "Unknown event received\n"); + kfree(response.pointer); +} + +static int __init msi_wmi_input_setup(void) +{ + int err; + + msi_wmi_input_dev = input_allocate_device(); + if (!msi_wmi_input_dev) + return -ENOMEM; + + msi_wmi_input_dev->name = "MSI WMI hotkeys"; + msi_wmi_input_dev->phys = "wmi/input0"; + msi_wmi_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + err = input_register_device(msi_wmi_input_dev); + + if (err) + goto err_free_keymap; + + memset(last_pressed, 0, sizeof(last_pressed)); + + return 0; + +err_free_keymap: + sparse_keymap_free(msi_wmi_input_dev); +err_free_dev: + input_free_device(msi_wmi_input_dev); + return err; +} + +static int __init msi_wmi_init(void) +{ + int err; + + if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { + printk(KERN_ERR + "This machine doesn't have MSI-hotkeys through WMI\n"); + return -ENODEV; + } + err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, + msi_wmi_notify, NULL); + if (err) + return -EINVAL; + + err = msi_wmi_input_setup(); + if (err) + goto err_uninstall_notifier; + + if (!acpi_video_backlight_support()) { + backlight = backlight_device_register(DRV_NAME, + NULL, NULL, &msi_backlight_ops); + if (IS_ERR(backlight)) + goto err_free_input; + + backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1; + err = bl_get(NULL); + if (err < 0) + goto err_free_backlight; + + backlight->props.brightness = err; + } + dprintk("Event handler installed\n"); + + return 0; + +err_free_backlight: + backlight_device_unregister(backlight); +err_free_input: + input_unregister_device(msi_wmi_input_dev); +err_uninstall_notifier: + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + return err; +} + +static void __exit msi_wmi_exit(void) +{ + if (wmi_has_guid(MSIWMI_EVENT_GUID)) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + sparse_keymap_free(msi_wmi_input_dev); + input_unregister_device(msi_wmi_input_dev); + backlight_device_unregister(backlight); + } +} + +module_init(msi_wmi_init); +module_exit(msi_wmi_exit); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index cf61d6a..448c8ae 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -21,8 +21,8 @@ * 02110-1301, USA. */ -#define TPACPI_VERSION "0.23" -#define TPACPI_SYSFS_VERSION 0x020500 +#define TPACPI_VERSION "0.24" +#define TPACPI_SYSFS_VERSION 0x020700 /* * Changelog: @@ -61,6 +61,7 @@ #include <linux/nvram.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/sysfs.h> #include <linux/backlight.h> #include <linux/fb.h> @@ -76,6 +77,10 @@ #include <linux/jiffies.h> #include <linux/workqueue.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/initval.h> + #include <acpi/acpi_drivers.h> #include <linux/pci_ids.h> @@ -231,6 +236,7 @@ enum tpacpi_hkey_event_t { #define TPACPI_DBG_HKEY 0x0008 #define TPACPI_DBG_FAN 0x0010 #define TPACPI_DBG_BRGHT 0x0020 +#define TPACPI_DBG_MIXER 0x0040 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -256,7 +262,7 @@ struct tp_acpi_drv_struct { struct ibm_struct { char *name; - int (*read) (char *); + int (*read) (struct seq_file *); int (*write) (char *); void (*exit) (void); void (*resume) (void); @@ -298,6 +304,7 @@ static struct { u32 fan_ctrl_status_undef:1; u32 second_fan:1; u32 beep_needs_two_args:1; + u32 mixer_no_level_control:1; u32 input_device_registered:1; u32 platform_drv_registered:1; u32 platform_drv_attrs_registered:1; @@ -309,6 +316,7 @@ static struct { static struct { u16 hotkey_mask_ff:1; + u16 volume_ctrl_forbidden:1; } tp_warned; struct thinkpad_id_data { @@ -425,6 +433,12 @@ static void tpacpi_log_usertask(const char * const what) .ec = TPACPI_MATCH_ANY, \ .quirks = (__quirk) } +#define TPACPI_QEC_LNV(__id1, __id2, __quirk) \ + { .vendor = PCI_VENDOR_ID_LENOVO, \ + .bios = TPACPI_MATCH_ANY, \ + .ec = TPID(__id1, __id2), \ + .quirks = (__quirk) } + struct tpacpi_quirk { unsigned int vendor; u16 bios; @@ -776,36 +790,25 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) **************************************************************************** ****************************************************************************/ -static int dispatch_procfs_read(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int dispatch_proc_show(struct seq_file *m, void *v) { - struct ibm_struct *ibm = data; - int len; + struct ibm_struct *ibm = m->private; if (!ibm || !ibm->read) return -EINVAL; + return ibm->read(m); +} - len = ibm->read(page); - if (len < 0) - return len; - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; +static int dispatch_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, dispatch_proc_show, PDE(inode)->data); } -static int dispatch_procfs_write(struct file *file, +static ssize_t dispatch_proc_write(struct file *file, const char __user *userbuf, - unsigned long count, void *data) + size_t count, loff_t *pos) { - struct ibm_struct *ibm = data; + struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; char *kernbuf; int ret; @@ -834,6 +837,15 @@ static int dispatch_procfs_write(struct file *file, return ret; } +static const struct file_operations dispatch_proc_fops = { + .owner = THIS_MODULE, + .open = dispatch_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dispatch_proc_write, +}; + static char *next_cmd(char **cmds) { char *start = *cmds; @@ -1261,6 +1273,7 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, struct tpacpi_rfk *atp_rfk; int res; bool sw_state = false; + bool hw_state; int sw_status; BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); @@ -1295,7 +1308,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, rfkill_init_sw_state(atp_rfk->rfkill, sw_state); } } - rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); + hw_state = tpacpi_rfk_check_hwblock_state(); + rfkill_set_hw_state(atp_rfk->rfkill, hw_state); res = rfkill_register(atp_rfk->rfkill); if (res < 0) { @@ -1308,6 +1322,9 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, } tpacpi_rfkill_switches[id] = atp_rfk; + + printk(TPACPI_INFO "rfkill switch %s: radio is %sblocked\n", + name, (sw_state || hw_state) ? "" : "un"); return 0; } @@ -1380,12 +1397,10 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, } /* procfs -------------------------------------------------------------- */ -static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) +static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) { - int len = 0; - if (id >= TPACPI_RFK_SW_MAX) - len += sprintf(p + len, "status:\t\tnot supported\n"); + seq_printf(m, "status:\t\tnot supported\n"); else { int status; @@ -1399,13 +1414,13 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, char *p) return status; } - len += sprintf(p + len, "status:\t\t%s\n", + seq_printf(m, "status:\t\t%s\n", (status == TPACPI_RFK_RADIO_ON) ? "enabled" : "disabled"); - len += sprintf(p + len, "commands:\tenable, disable\n"); + seq_printf(m, "commands:\tenable, disable\n"); } - return len; + return 0; } static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) @@ -1776,7 +1791,7 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { TPV_QL1('7', '9', 'E', '3', '5', '0'), /* T60/p */ TPV_QL1('7', 'C', 'D', '2', '2', '2'), /* R60, R60i */ - TPV_QL0('7', 'E', 'D', '0'), /* R60e, R60i */ + TPV_QL1('7', 'E', 'D', '0', '1', '5'), /* R60e, R60i */ /* BIOS FW BIOS VERS EC FW EC VERS */ TPV_QI2('1', 'W', '9', '0', '1', 'V', '2', '8'), /* R50e (1) */ @@ -1792,8 +1807,8 @@ static const struct tpacpi_quirk tpacpi_bios_version_qtable[] __initconst = { TPV_QI1('7', '4', '6', '4', '2', '7'), /* X41 (0) */ TPV_QI1('7', '5', '6', '0', '2', '0'), /* X41t (0) */ - TPV_QL0('7', 'B', 'D', '7'), /* X60/s */ - TPV_QL0('7', 'J', '3', '0'), /* X60t */ + TPV_QL1('7', 'B', 'D', '7', '4', '0'), /* X60/s */ + TPV_QL1('7', 'J', '3', '0', '1', '3'), /* X60t */ /* (0) - older versions lack DMI EC fw string and functionality */ /* (1) - older versions known to lack functionality */ @@ -1883,14 +1898,11 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) return 0; } -static int thinkpad_acpi_driver_read(char *p) +static int thinkpad_acpi_driver_read(struct seq_file *m) { - int len = 0; - - len += sprintf(p + len, "driver:\t\t%s\n", TPACPI_DESC); - len += sprintf(p + len, "version:\t%s\n", TPACPI_VERSION); - - return len; + seq_printf(m, "driver:\t\t%s\n", TPACPI_DESC); + seq_printf(m, "version:\t%s\n", TPACPI_VERSION); + return 0; } static struct ibm_struct thinkpad_acpi_driver_data = { @@ -2186,7 +2198,8 @@ static int hotkey_mask_set(u32 mask) fwmask, hotkey_acpi_mask); } - hotkey_mask_warn_incomplete_mask(); + if (tpacpi_lifecycle != TPACPI_LIFE_EXITING) + hotkey_mask_warn_incomplete_mask(); return rc; } @@ -3182,6 +3195,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) int res, i; int status; int hkeyv; + bool radiosw_state = false; + bool tabletsw_state = false; unsigned long quirks; @@ -3287,6 +3302,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wlswemul) { tp_features.hotkey_wlsw = 1; + radiosw_state = !!tpacpi_wlsw_emulstate; printk(TPACPI_INFO "radio switch emulation enabled\n"); } else @@ -3294,6 +3310,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Not all thinkpads have a hardware radio switch */ if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { tp_features.hotkey_wlsw = 1; + radiosw_state = !!status; printk(TPACPI_INFO "radio switch found; radios are %s\n", enabled(status, 0)); @@ -3305,11 +3322,11 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* For X41t, X60t, X61t Tablets... */ if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) { tp_features.hotkey_tablet = 1; + tabletsw_state = !!(status & TP_HOTKEY_TABLET_MASK); printk(TPACPI_INFO "possible tablet mode switch found; " "ThinkPad in %s mode\n", - (status & TP_HOTKEY_TABLET_MASK)? - "tablet" : "laptop"); + (tabletsw_state) ? "tablet" : "laptop"); res = add_to_attr_set(hotkey_dev_attributes, &dev_attr_hotkey_tablet_mode.attr); } @@ -3344,16 +3361,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) TPACPI_HOTKEY_MAP_SIZE); } - set_bit(EV_KEY, tpacpi_inputdev->evbit); - set_bit(EV_MSC, tpacpi_inputdev->evbit); - set_bit(MSC_SCAN, tpacpi_inputdev->mscbit); + input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN; tpacpi_inputdev->keycode = hotkey_keycode_map; for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) { if (hotkey_keycode_map[i] != KEY_RESERVED) { - set_bit(hotkey_keycode_map[i], - tpacpi_inputdev->keybit); + input_set_capability(tpacpi_inputdev, EV_KEY, + hotkey_keycode_map[i]); } else { if (i < sizeof(hotkey_reserved_mask)*8) hotkey_reserved_mask |= 1 << i; @@ -3361,12 +3376,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } if (tp_features.hotkey_wlsw) { - set_bit(EV_SW, tpacpi_inputdev->evbit); - set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit); + input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); + input_report_switch(tpacpi_inputdev, + SW_RFKILL_ALL, radiosw_state); } if (tp_features.hotkey_tablet) { - set_bit(EV_SW, tpacpi_inputdev->evbit); - set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); + input_set_capability(tpacpi_inputdev, EV_SW, SW_TABLET_MODE); + input_report_switch(tpacpi_inputdev, + SW_TABLET_MODE, tabletsw_state); } /* Do not issue duplicate brightness change events to @@ -3433,8 +3450,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) tpacpi_inputdev->close = &hotkey_inputdev_close; hotkey_poll_setup_safe(true); - tpacpi_send_radiosw_update(); - tpacpi_input_send_tabletsw(); return 0; @@ -3542,49 +3557,57 @@ static bool hotkey_notify_usrevent(const u32 hkey, } } +static void thermal_dump_all_sensors(void); + static bool hotkey_notify_thermal(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) { + bool known = true; + /* 0x6000-0x6FFF: thermal alarms */ *send_acpi_ev = true; *ignore_acpi_ev = false; switch (hkey) { + case TP_HKEY_EV_THM_TABLE_CHANGED: + printk(TPACPI_INFO + "EC reports that Thermal Table has changed\n"); + /* recommended action: do nothing, we don't have + * Lenovo ATM information */ + return true; case TP_HKEY_EV_ALARM_BAT_HOT: printk(TPACPI_CRIT "THERMAL ALARM: battery is too hot!\n"); /* recommended action: warn user through gui */ - return true; + break; case TP_HKEY_EV_ALARM_BAT_XHOT: printk(TPACPI_ALERT "THERMAL EMERGENCY: battery is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ - return true; + break; case TP_HKEY_EV_ALARM_SENSOR_HOT: printk(TPACPI_CRIT "THERMAL ALARM: " "a sensor reports something is too hot!\n"); /* recommended action: warn user through gui, that */ /* some internal component is too hot */ - return true; + break; case TP_HKEY_EV_ALARM_SENSOR_XHOT: printk(TPACPI_ALERT "THERMAL EMERGENCY: " "a sensor reports something is extremely hot!\n"); /* recommended action: immediate sleep/hibernate */ - return true; - case TP_HKEY_EV_THM_TABLE_CHANGED: - printk(TPACPI_INFO - "EC reports that Thermal Table has changed\n"); - /* recommended action: do nothing, we don't have - * Lenovo ATM information */ - return true; + break; default: printk(TPACPI_ALERT "THERMAL ALERT: unknown thermal alarm received\n"); - return false; + known = false; } + + thermal_dump_all_sensors(); + + return known; } static void hotkey_notify(struct ibm_struct *ibm, u32 event) @@ -3727,14 +3750,13 @@ static void hotkey_resume(void) } /* procfs -------------------------------------------------------------- */ -static int hotkey_read(char *p) +static int hotkey_read(struct seq_file *m) { int res, status; - int len = 0; if (!tp_features.hotkey) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; + seq_printf(m, "status:\t\tnot supported\n"); + return 0; } if (mutex_lock_killable(&hotkey_mutex)) @@ -3746,17 +3768,16 @@ static int hotkey_read(char *p) if (res) return res; - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); + seq_printf(m, "status:\t\t%s\n", enabled(status, 0)); if (hotkey_all_mask) { - len += sprintf(p + len, "mask:\t\t0x%08x\n", hotkey_user_mask); - len += sprintf(p + len, - "commands:\tenable, disable, reset, <mask>\n"); + seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); + seq_printf(m, "commands:\tenable, disable, reset, <mask>\n"); } else { - len += sprintf(p + len, "mask:\t\tnot supported\n"); - len += sprintf(p + len, "commands:\tenable, disable, reset\n"); + seq_printf(m, "mask:\t\tnot supported\n"); + seq_printf(m, "commands:\tenable, disable, reset\n"); } - return len; + return 0; } static void hotkey_enabledisable_warn(bool enable) @@ -3863,15 +3884,6 @@ enum { #define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" -static void bluetooth_suspend(pm_message_t state) -{ - /* Try to make sure radio will resume powered off */ - if (!acpi_evalf(NULL, NULL, "\\BLTH", "vd", - TP_ACPI_BLTH_PWR_OFF_ON_RESUME)) - vdbg_printk(TPACPI_DBG_RFKILL, - "bluetooth power down on resume request failed\n"); -} - static int bluetooth_get_status(void) { int status; @@ -3905,10 +3917,9 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) #endif /* We make sure to keep TP_ACPI_BLUETOOTH_RESUMECTRL off */ + status = TP_ACPI_BLUETOOTH_RESUMECTRL; if (state == TPACPI_RFK_RADIO_ON) - status = TP_ACPI_BLUETOOTH_RADIOSSW; - else - status = 0; + status |= TP_ACPI_BLUETOOTH_RADIOSSW; if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) return -EIO; @@ -4032,9 +4043,9 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) } /* procfs -------------------------------------------------------------- */ -static int bluetooth_read(char *p) +static int bluetooth_read(struct seq_file *m) { - return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, p); + return tpacpi_rfk_procfs_read(TPACPI_RFK_BLUETOOTH_SW_ID, m); } static int bluetooth_write(char *buf) @@ -4047,7 +4058,6 @@ static struct ibm_struct bluetooth_driver_data = { .read = bluetooth_read, .write = bluetooth_write, .exit = bluetooth_exit, - .suspend = bluetooth_suspend, .shutdown = bluetooth_shutdown, }; @@ -4065,15 +4075,6 @@ enum { #define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" -static void wan_suspend(pm_message_t state) -{ - /* Try to make sure radio will resume powered off */ - if (!acpi_evalf(NULL, NULL, "\\WGSV", "qvd", - TP_ACPI_WGSV_PWR_OFF_ON_RESUME)) - vdbg_printk(TPACPI_DBG_RFKILL, - "WWAN power down on resume request failed\n"); -} - static int wan_get_status(void) { int status; @@ -4106,11 +4107,10 @@ static int wan_set_status(enum tpacpi_rfkill_state state) } #endif - /* We make sure to keep TP_ACPI_WANCARD_RESUMECTRL off */ + /* We make sure to set TP_ACPI_WANCARD_RESUMECTRL */ + status = TP_ACPI_WANCARD_RESUMECTRL; if (state == TPACPI_RFK_RADIO_ON) - status = TP_ACPI_WANCARD_RADIOSSW; - else - status = 0; + status |= TP_ACPI_WANCARD_RADIOSSW; if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) return -EIO; @@ -4233,9 +4233,9 @@ static int __init wan_init(struct ibm_init_struct *iibm) } /* procfs -------------------------------------------------------------- */ -static int wan_read(char *p) +static int wan_read(struct seq_file *m) { - return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, p); + return tpacpi_rfk_procfs_read(TPACPI_RFK_WWAN_SW_ID, m); } static int wan_write(char *buf) @@ -4248,7 +4248,6 @@ static struct ibm_struct wan_driver_data = { .read = wan_read, .write = wan_write, .exit = wan_exit, - .suspend = wan_suspend, .shutdown = wan_shutdown, }; @@ -4611,14 +4610,13 @@ static int video_expand_toggle(void) /* not reached */ } -static int video_read(char *p) +static int video_read(struct seq_file *m) { int status, autosw; - int len = 0; if (video_supported == TPACPI_VIDEO_NONE) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; + seq_printf(m, "status:\t\tnot supported\n"); + return 0; } status = video_outputsw_get(); @@ -4629,20 +4627,20 @@ static int video_read(char *p) if (autosw < 0) return autosw; - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); - len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); + seq_printf(m, "status:\t\tsupported\n"); + seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0)); + seq_printf(m, "crt:\t\t%s\n", enabled(status, 1)); if (video_supported == TPACPI_VIDEO_NEW) - len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); - len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); - len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); - len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); + seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3)); + seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0)); + seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); + seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); if (video_supported == TPACPI_VIDEO_NEW) - len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); - len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); - len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); + seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); + seq_printf(m, "commands:\tauto_enable, auto_disable\n"); + seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); - return len; + return 0; } static int video_write(char *buf) @@ -4834,25 +4832,24 @@ static void light_exit(void) flush_workqueue(tpacpi_wq); } -static int light_read(char *p) +static int light_read(struct seq_file *m) { - int len = 0; int status; if (!tp_features.light) { - len += sprintf(p + len, "status:\t\tnot supported\n"); + seq_printf(m, "status:\t\tnot supported\n"); } else if (!tp_features.light_status) { - len += sprintf(p + len, "status:\t\tunknown\n"); - len += sprintf(p + len, "commands:\ton, off\n"); + seq_printf(m, "status:\t\tunknown\n"); + seq_printf(m, "commands:\ton, off\n"); } else { status = light_get_status(); if (status < 0) return status; - len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); - len += sprintf(p + len, "commands:\ton, off\n"); + seq_printf(m, "status:\t\t%s\n", onoff(status, 0)); + seq_printf(m, "commands:\ton, off\n"); } - return len; + return 0; } static int light_write(char *buf) @@ -4930,20 +4927,18 @@ static void cmos_exit(void) device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); } -static int cmos_read(char *p) +static int cmos_read(struct seq_file *m) { - int len = 0; - /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, R30, R31, T20-22, X20-21 */ if (!cmos_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); + seq_printf(m, "status:\t\tnot supported\n"); else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); + seq_printf(m, "status:\t\tsupported\n"); + seq_printf(m, "commands:\t<cmd> (<cmd> is 0-21)\n"); } - return len; + return 0; } static int cmos_write(char *buf) @@ -5318,15 +5313,13 @@ static int __init led_init(struct ibm_init_struct *iibm) ((s) == TPACPI_LED_OFF ? "off" : \ ((s) == TPACPI_LED_ON ? "on" : "blinking")) -static int led_read(char *p) +static int led_read(struct seq_file *m) { - int len = 0; - if (!led_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; + seq_printf(m, "status:\t\tnot supported\n"); + return 0; } - len += sprintf(p + len, "status:\t\tsupported\n"); + seq_printf(m, "status:\t\tsupported\n"); if (led_supported == TPACPI_LED_570) { /* 570 */ @@ -5335,15 +5328,15 @@ static int led_read(char *p) status = led_get_status(i); if (status < 0) return -EIO; - len += sprintf(p + len, "%d:\t\t%s\n", + seq_printf(m, "%d:\t\t%s\n", i, str_led_status(status)); } } - len += sprintf(p + len, "commands:\t" + seq_printf(m, "commands:\t" "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); - return len; + return 0; } static int led_write(char *buf) @@ -5416,18 +5409,16 @@ static int __init beep_init(struct ibm_init_struct *iibm) return (beep_handle)? 0 : 1; } -static int beep_read(char *p) +static int beep_read(struct seq_file *m) { - int len = 0; - if (!beep_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); + seq_printf(m, "status:\t\tnot supported\n"); else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); + seq_printf(m, "status:\t\tsupported\n"); + seq_printf(m, "commands:\t<cmd> (<cmd> is 0-17)\n"); } - return len; + return 0; } static int beep_write(char *buf) @@ -5480,8 +5471,11 @@ enum { /* TPACPI_THERMAL_TPEC_* */ TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ + + TPACPI_THERMAL_SENSOR_NA = -128000, /* Sensor not available */ }; + #define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ struct ibm_thermal_sensors_struct { s32 temp[TPACPI_MAX_THERMAL_SENSORS]; @@ -5571,6 +5565,28 @@ static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) return n; } +static void thermal_dump_all_sensors(void) +{ + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (n <= 0) + return; + + printk(TPACPI_NOTICE + "temperatures (Celsius):"); + + for (i = 0; i < n; i++) { + if (t.temp[i] != TPACPI_THERMAL_SENSOR_NA) + printk(KERN_CONT " %d", (int)(t.temp[i] / 1000)); + else + printk(KERN_CONT " N/A"); + } + + printk(KERN_CONT "\n"); +} + /* sysfs temp##_input -------------------------------------------------- */ static ssize_t thermal_temp_input_show(struct device *dev, @@ -5586,7 +5602,7 @@ static ssize_t thermal_temp_input_show(struct device *dev, res = thermal_get_sensor(idx, &value); if (res) return res; - if (value == TP_EC_THERMAL_TMP_NA * 1000) + if (value == TPACPI_THERMAL_SENSOR_NA) return -ENXIO; return snprintf(buf, PAGE_SIZE, "%d\n", value); @@ -5763,9 +5779,8 @@ static void thermal_exit(void) } } -static int thermal_read(char *p) +static int thermal_read(struct seq_file *m) { - int len = 0; int n, i; struct ibm_thermal_sensors_struct t; @@ -5773,16 +5788,16 @@ static int thermal_read(char *p) if (unlikely(n < 0)) return n; - len += sprintf(p + len, "temperatures:\t"); + seq_printf(m, "temperatures:\t"); if (n > 0) { for (i = 0; i < (n - 1); i++) - len += sprintf(p + len, "%d ", t.temp[i] / 1000); - len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + seq_printf(m, "%d ", t.temp[i] / 1000); + seq_printf(m, "%d\n", t.temp[i] / 1000); } else - len += sprintf(p + len, "not supported\n"); + seq_printf(m, "not supported\n"); - return len; + return 0; } static struct ibm_struct thermal_driver_data = { @@ -5797,39 +5812,38 @@ static struct ibm_struct thermal_driver_data = { static u8 ecdump_regs[256]; -static int ecdump_read(char *p) +static int ecdump_read(struct seq_file *m) { - int len = 0; int i, j; u8 v; - len += sprintf(p + len, "EC " + seq_printf(m, "EC " " +00 +01 +02 +03 +04 +05 +06 +07" " +08 +09 +0a +0b +0c +0d +0e +0f\n"); for (i = 0; i < 256; i += 16) { - len += sprintf(p + len, "EC 0x%02x:", i); + seq_printf(m, "EC 0x%02x:", i); for (j = 0; j < 16; j++) { if (!acpi_ec_read(i + j, &v)) break; if (v != ecdump_regs[i + j]) - len += sprintf(p + len, " *%02x", v); + seq_printf(m, " *%02x", v); else - len += sprintf(p + len, " %02x", v); + seq_printf(m, " %02x", v); ecdump_regs[i + j] = v; } - len += sprintf(p + len, "\n"); + seq_putc(m, '\n'); if (j != 16) break; } /* These are way too dangerous to advertise openly... */ #if 0 - len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" + seq_printf(m, "commands:\t0x<offset> 0x<value>" " (<offset> is 00-ff, <value> is 00-ff)\n"); - len += sprintf(p + len, "commands:\t0x<offset> <value> " + seq_printf(m, "commands:\t0x<offset> <value> " " (<offset> is 00-ff, <value> is 0-255)\n"); #endif - return len; + return 0; } static int ecdump_write(char *buf) @@ -6092,6 +6106,12 @@ static int brightness_get(struct backlight_device *bd) return status & TP_EC_BACKLIGHT_LVLMSK; } +static void tpacpi_brightness_notify_change(void) +{ + backlight_force_update(ibm_backlight_device, + BACKLIGHT_UPDATE_HOTKEY); +} + static struct backlight_ops ibm_backlight_data = { .get_brightness = brightness_get, .update_status = brightness_update_status, @@ -6120,8 +6140,8 @@ static const struct tpacpi_quirk brightness_quirk_table[] __initconst = { /* Models with Intel Extreme Graphics 2 */ TPACPI_Q_IBM('1', 'U', TPACPI_BRGHT_Q_NOEC), - TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), - TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_NOEC), + TPACPI_Q_IBM('1', 'V', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), + TPACPI_Q_IBM('1', 'W', TPACPI_BRGHT_Q_ASK|TPACPI_BRGHT_Q_EC), /* Models with Intel GMA900 */ TPACPI_Q_IBM('7', '0', TPACPI_BRGHT_Q_NOEC), /* T43, R52 */ @@ -6246,6 +6266,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; backlight_update_status(ibm_backlight_device); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, + "brightness: registering brightness hotkeys " + "as change notification\n"); + tpacpi_hotkey_driver_mask_set(hotkey_driver_mask + | TP_ACPI_HKEY_BRGHTUP_MASK + | TP_ACPI_HKEY_BRGHTDWN_MASK);; return 0; } @@ -6270,23 +6296,22 @@ static void brightness_exit(void) tpacpi_brightness_checkpoint_nvram(); } -static int brightness_read(char *p) +static int brightness_read(struct seq_file *m) { - int len = 0; int level; level = brightness_get(NULL); if (level < 0) { - len += sprintf(p + len, "level:\t\tunreadable\n"); + seq_printf(m, "level:\t\tunreadable\n"); } else { - len += sprintf(p + len, "level:\t\t%d\n", level); - len += sprintf(p + len, "commands:\tup, down\n"); - len += sprintf(p + len, "commands:\tlevel <level>" + seq_printf(m, "level:\t\t%d\n", level); + seq_printf(m, "commands:\tup, down\n"); + seq_printf(m, "commands:\tlevel <level>" " (<level> is 0-%d)\n", (tp_features.bright_16levels) ? 15 : 7); } - return len; + return 0; } static int brightness_write(char *buf) @@ -6322,6 +6347,9 @@ static int brightness_write(char *buf) * Doing it this way makes the syscall restartable in case of EINTR */ rc = brightness_set(level); + if (!rc && ibm_backlight_device) + backlight_force_update(ibm_backlight_device, + BACKLIGHT_UPDATE_SYSFS); return (rc == -EINTR)? -ERESTARTSYS : rc; } @@ -6338,99 +6366,654 @@ static struct ibm_struct brightness_driver_data = { * Volume subdriver */ -static int volume_offset = 0x30; +/* + * IBM ThinkPads have a simple volume controller with MUTE gating. + * Very early Lenovo ThinkPads follow the IBM ThinkPad spec. + * + * Since the *61 series (and probably also the later *60 series), Lenovo + * ThinkPads only implement the MUTE gate. + * + * EC register 0x30 + * Bit 6: MUTE (1 mutes sound) + * Bit 3-0: Volume + * Other bits should be zero as far as we know. + * + * This is also stored in CMOS NVRAM, byte 0x60, bit 6 (MUTE), and + * bits 3-0 (volume). Other bits in NVRAM may have other functions, + * such as bit 7 which is used to detect repeated presses of MUTE, + * and we leave them unchanged. + */ + +#define TPACPI_ALSA_DRVNAME "ThinkPad EC" +#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" +#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME + +static int alsa_index = SNDRV_DEFAULT_IDX1; +static char *alsa_id = "ThinkPadEC"; +static int alsa_enable = SNDRV_DEFAULT_ENABLE1; + +struct tpacpi_alsa_data { + struct snd_card *card; + struct snd_ctl_elem_id *ctl_mute_id; + struct snd_ctl_elem_id *ctl_vol_id; +}; + +static struct snd_card *alsa_card; + +enum { + TP_EC_AUDIO = 0x30, + + /* TP_EC_AUDIO bits */ + TP_EC_AUDIO_MUTESW = 6, + + /* TP_EC_AUDIO bitmasks */ + TP_EC_AUDIO_LVL_MSK = 0x0F, + TP_EC_AUDIO_MUTESW_MSK = (1 << TP_EC_AUDIO_MUTESW), + + /* Maximum volume */ + TP_EC_VOLUME_MAX = 14, +}; + +enum tpacpi_volume_access_mode { + TPACPI_VOL_MODE_AUTO = 0, /* Not implemented yet */ + TPACPI_VOL_MODE_EC, /* Pure EC control */ + TPACPI_VOL_MODE_UCMS_STEP, /* UCMS step-based control: N/A */ + TPACPI_VOL_MODE_ECNVRAM, /* EC control w/ NVRAM store */ + TPACPI_VOL_MODE_MAX +}; + +enum tpacpi_volume_capabilities { + TPACPI_VOL_CAP_AUTO = 0, /* Use white/blacklist */ + TPACPI_VOL_CAP_VOLMUTE, /* Output vol and mute */ + TPACPI_VOL_CAP_MUTEONLY, /* Output mute only */ + TPACPI_VOL_CAP_MAX +}; + +static enum tpacpi_volume_access_mode volume_mode = + TPACPI_VOL_MODE_MAX; + +static enum tpacpi_volume_capabilities volume_capabilities; +static int volume_control_allowed; -static int volume_read(char *p) +/* + * Used to syncronize writers to TP_EC_AUDIO and + * TP_NVRAM_ADDR_MIXER, as we need to do read-modify-write + */ +static struct mutex volume_mutex; + +static void tpacpi_volume_checkpoint_nvram(void) { - int len = 0; - u8 level; + u8 lec = 0; + u8 b_nvram; + u8 ec_mask; + + if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) + return; + if (!volume_control_allowed) + return; + + vdbg_printk(TPACPI_DBG_MIXER, + "trying to checkpoint mixer state to NVRAM...\n"); - if (!acpi_ec_read(volume_offset, &level)) { - len += sprintf(p + len, "level:\t\tunreadable\n"); + if (tp_features.mixer_no_level_control) + ec_mask = TP_EC_AUDIO_MUTESW_MSK; + else + ec_mask = TP_EC_AUDIO_MUTESW_MSK | TP_EC_AUDIO_LVL_MSK; + + if (mutex_lock_killable(&volume_mutex) < 0) + return; + + if (unlikely(!acpi_ec_read(TP_EC_AUDIO, &lec))) + goto unlock; + lec &= ec_mask; + b_nvram = nvram_read_byte(TP_NVRAM_ADDR_MIXER); + + if (lec != (b_nvram & ec_mask)) { + /* NVRAM needs update */ + b_nvram &= ~ec_mask; + b_nvram |= lec; + nvram_write_byte(b_nvram, TP_NVRAM_ADDR_MIXER); + dbg_printk(TPACPI_DBG_MIXER, + "updated NVRAM mixer status to 0x%02x (0x%02x)\n", + (unsigned int) lec, (unsigned int) b_nvram); } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); - len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); - len += sprintf(p + len, "commands:\tup, down, mute\n"); - len += sprintf(p + len, "commands:\tlevel <level>" - " (<level> is 0-15)\n"); + vdbg_printk(TPACPI_DBG_MIXER, + "NVRAM mixer status already is 0x%02x (0x%02x)\n", + (unsigned int) lec, (unsigned int) b_nvram); } - return len; +unlock: + mutex_unlock(&volume_mutex); } -static int volume_write(char *buf) +static int volume_get_status_ec(u8 *status) { - int cmos_cmd, inc, i; - u8 level, mute; - int new_level, new_mute; - char *cmd; + u8 s; - while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(volume_offset, &level)) - return -EIO; - new_mute = mute = level & 0x40; - new_level = level = level & 0xf; + if (!acpi_ec_read(TP_EC_AUDIO, &s)) + return -EIO; - if (strlencmp(cmd, "up") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 15 ? 15 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 15) { - /* new_level set */ - } else if (strlencmp(cmd, "mute") == 0) { - new_mute = 0x40; - } else - return -EINVAL; + *status = s; - if (new_level != level) { - /* mute doesn't change */ + dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s); - cmos_cmd = (new_level > level) ? - TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; - inc = new_level > level ? 1 : -1; + return 0; +} - if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, level))) - return -EIO; +static int volume_get_status(u8 *status) +{ + return volume_get_status_ec(status); +} - for (i = level; i != new_level; i += inc) - if (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, i + inc)) - return -EIO; +static int volume_set_status_ec(const u8 status) +{ + if (!acpi_ec_write(TP_EC_AUDIO, status)) + return -EIO; - if (mute && - (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || - !acpi_ec_write(volume_offset, new_level + mute))) { - return -EIO; - } + dbg_printk(TPACPI_DBG_MIXER, "set EC mixer to 0x%02x\n", status); + + return 0; +} + +static int volume_set_status(const u8 status) +{ + return volume_set_status_ec(status); +} + +static int volume_set_mute_ec(const bool mute) +{ + int rc; + u8 s, n; + + if (mutex_lock_killable(&volume_mutex) < 0) + return -EINTR; + + rc = volume_get_status_ec(&s); + if (rc) + goto unlock; + + n = (mute) ? s | TP_EC_AUDIO_MUTESW_MSK : + s & ~TP_EC_AUDIO_MUTESW_MSK; + + if (n != s) + rc = volume_set_status_ec(n); + +unlock: + mutex_unlock(&volume_mutex); + return rc; +} + +static int volume_set_mute(const bool mute) +{ + dbg_printk(TPACPI_DBG_MIXER, "trying to %smute\n", + (mute) ? "" : "un"); + return volume_set_mute_ec(mute); +} + +static int volume_set_volume_ec(const u8 vol) +{ + int rc; + u8 s, n; + + if (vol > TP_EC_VOLUME_MAX) + return -EINVAL; + + if (mutex_lock_killable(&volume_mutex) < 0) + return -EINTR; + + rc = volume_get_status_ec(&s); + if (rc) + goto unlock; + + n = (s & ~TP_EC_AUDIO_LVL_MSK) | vol; + + if (n != s) + rc = volume_set_status_ec(n); + +unlock: + mutex_unlock(&volume_mutex); + return rc; +} + +static int volume_set_volume(const u8 vol) +{ + dbg_printk(TPACPI_DBG_MIXER, + "trying to set volume level to %hu\n", vol); + return volume_set_volume_ec(vol); +} + +static void volume_alsa_notify_change(void) +{ + struct tpacpi_alsa_data *d; + + if (alsa_card && alsa_card->private_data) { + d = alsa_card->private_data; + if (d->ctl_mute_id) + snd_ctl_notify(alsa_card, + SNDRV_CTL_EVENT_MASK_VALUE, + d->ctl_mute_id); + if (d->ctl_vol_id) + snd_ctl_notify(alsa_card, + SNDRV_CTL_EVENT_MASK_VALUE, + d->ctl_vol_id); + } +} + +static int volume_alsa_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = TP_EC_VOLUME_MAX; + return 0; +} + +static int volume_alsa_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 s; + int rc; + + rc = volume_get_status(&s); + if (rc < 0) + return rc; + + ucontrol->value.integer.value[0] = s & TP_EC_AUDIO_LVL_MSK; + return 0; +} + +static int volume_alsa_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return volume_set_volume(ucontrol->value.integer.value[0]); +} + +#define volume_alsa_mute_info snd_ctl_boolean_mono_info + +static int volume_alsa_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 s; + int rc; + + rc = volume_get_status(&s); + if (rc < 0) + return rc; + + ucontrol->value.integer.value[0] = + (s & TP_EC_AUDIO_MUTESW_MSK) ? 0 : 1; + return 0; +} + +static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return volume_set_mute(!ucontrol->value.integer.value[0]); +} + +static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Console Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = volume_alsa_vol_info, + .get = volume_alsa_vol_get, +}; + +static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Console Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = volume_alsa_mute_info, + .get = volume_alsa_mute_get, +}; + +static void volume_suspend(pm_message_t state) +{ + tpacpi_volume_checkpoint_nvram(); +} + +static void volume_resume(void) +{ + volume_alsa_notify_change(); +} + +static void volume_shutdown(void) +{ + tpacpi_volume_checkpoint_nvram(); +} + +static void volume_exit(void) +{ + if (alsa_card) { + snd_card_free(alsa_card); + alsa_card = NULL; + } + + tpacpi_volume_checkpoint_nvram(); +} + +static int __init volume_create_alsa_mixer(void) +{ + struct snd_card *card; + struct tpacpi_alsa_data *data; + struct snd_kcontrol *ctl_vol; + struct snd_kcontrol *ctl_mute; + int rc; + + rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, + sizeof(struct tpacpi_alsa_data), &card); + if (rc < 0) + return rc; + if (!card) + return -ENOMEM; + + BUG_ON(!card->private_data); + data = card->private_data; + data->card = card; + + strlcpy(card->driver, TPACPI_ALSA_DRVNAME, + sizeof(card->driver)); + strlcpy(card->shortname, TPACPI_ALSA_SHRTNAME, + sizeof(card->shortname)); + snprintf(card->mixername, sizeof(card->mixername), "ThinkPad EC %s", + (thinkpad_id.ec_version_str) ? + thinkpad_id.ec_version_str : "(unknown)"); + snprintf(card->longname, sizeof(card->longname), + "%s at EC reg 0x%02x, fw %s", card->shortname, TP_EC_AUDIO, + (thinkpad_id.ec_version_str) ? + thinkpad_id.ec_version_str : "unknown"); + + if (volume_control_allowed) { + volume_alsa_control_vol.put = volume_alsa_vol_put; + volume_alsa_control_vol.access = + SNDRV_CTL_ELEM_ACCESS_READWRITE; + + volume_alsa_control_mute.put = volume_alsa_mute_put; + volume_alsa_control_mute.access = + SNDRV_CTL_ELEM_ACCESS_READWRITE; + } + + if (!tp_features.mixer_no_level_control) { + ctl_vol = snd_ctl_new1(&volume_alsa_control_vol, NULL); + rc = snd_ctl_add(card, ctl_vol); + if (rc < 0) { + printk(TPACPI_ERR + "Failed to create ALSA volume control\n"); + goto err_out; } + data->ctl_vol_id = &ctl_vol->id; + } - if (new_mute != mute) { - /* level doesn't change */ + ctl_mute = snd_ctl_new1(&volume_alsa_control_mute, NULL); + rc = snd_ctl_add(card, ctl_mute); + if (rc < 0) { + printk(TPACPI_ERR "Failed to create ALSA mute control\n"); + goto err_out; + } + data->ctl_mute_id = &ctl_mute->id; - cmos_cmd = (new_mute) ? - TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; + snd_card_set_dev(card, &tpacpi_pdev->dev); + rc = snd_card_register(card); - if (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, level + new_mute)) - return -EIO; +err_out: + if (rc < 0) { + snd_card_free(card); + card = NULL; + } + + alsa_card = card; + return rc; +} + +#define TPACPI_VOL_Q_MUTEONLY 0x0001 /* Mute-only control available */ +#define TPACPI_VOL_Q_LEVEL 0x0002 /* Volume control available */ + +static const struct tpacpi_quirk volume_quirk_table[] __initconst = { + /* Whitelist volume level on all IBM by default */ + { .vendor = PCI_VENDOR_ID_IBM, + .bios = TPACPI_MATCH_ANY, + .ec = TPACPI_MATCH_ANY, + .quirks = TPACPI_VOL_Q_LEVEL }, + + /* Lenovo models with volume control (needs confirmation) */ + TPACPI_QEC_LNV('7', 'C', TPACPI_VOL_Q_LEVEL), /* R60/i */ + TPACPI_QEC_LNV('7', 'E', TPACPI_VOL_Q_LEVEL), /* R60e/i */ + TPACPI_QEC_LNV('7', '9', TPACPI_VOL_Q_LEVEL), /* T60/p */ + TPACPI_QEC_LNV('7', 'B', TPACPI_VOL_Q_LEVEL), /* X60/s */ + TPACPI_QEC_LNV('7', 'J', TPACPI_VOL_Q_LEVEL), /* X60t */ + TPACPI_QEC_LNV('7', '7', TPACPI_VOL_Q_LEVEL), /* Z60 */ + TPACPI_QEC_LNV('7', 'F', TPACPI_VOL_Q_LEVEL), /* Z61 */ + + /* Whitelist mute-only on all Lenovo by default */ + { .vendor = PCI_VENDOR_ID_LENOVO, + .bios = TPACPI_MATCH_ANY, + .ec = TPACPI_MATCH_ANY, + .quirks = TPACPI_VOL_Q_MUTEONLY } +}; + +static int __init volume_init(struct ibm_init_struct *iibm) +{ + unsigned long quirks; + int rc; + + vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); + + mutex_init(&volume_mutex); + + /* + * Check for module parameter bogosity, note that we + * init volume_mode to TPACPI_VOL_MODE_MAX in order to be + * able to detect "unspecified" + */ + if (volume_mode > TPACPI_VOL_MODE_MAX) + return -EINVAL; + + if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) { + printk(TPACPI_ERR + "UCMS step volume mode not implemented, " + "please contact %s\n", TPACPI_MAIL); + return 1; + } + + if (volume_capabilities >= TPACPI_VOL_CAP_MAX) + return -EINVAL; + + /* + * The ALSA mixer is our primary interface. + * When disabled, don't install the subdriver at all + */ + if (!alsa_enable) { + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "ALSA mixer disabled by parameter, " + "not loading volume subdriver...\n"); + return 1; + } + + quirks = tpacpi_check_quirks(volume_quirk_table, + ARRAY_SIZE(volume_quirk_table)); + + switch (volume_capabilities) { + case TPACPI_VOL_CAP_AUTO: + if (quirks & TPACPI_VOL_Q_MUTEONLY) + tp_features.mixer_no_level_control = 1; + else if (quirks & TPACPI_VOL_Q_LEVEL) + tp_features.mixer_no_level_control = 0; + else + return 1; /* no mixer */ + break; + case TPACPI_VOL_CAP_VOLMUTE: + tp_features.mixer_no_level_control = 0; + break; + case TPACPI_VOL_CAP_MUTEONLY: + tp_features.mixer_no_level_control = 1; + break; + default: + return 1; + } + + if (volume_capabilities != TPACPI_VOL_CAP_AUTO) + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "using user-supplied volume_capabilities=%d\n", + volume_capabilities); + + if (volume_mode == TPACPI_VOL_MODE_AUTO || + volume_mode == TPACPI_VOL_MODE_MAX) { + volume_mode = TPACPI_VOL_MODE_ECNVRAM; + + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "driver auto-selected volume_mode=%d\n", + volume_mode); + } else { + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "using user-supplied volume_mode=%d\n", + volume_mode); + } + + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "mute is supported, volume control is %s\n", + str_supported(!tp_features.mixer_no_level_control)); + + rc = volume_create_alsa_mixer(); + if (rc) { + printk(TPACPI_ERR + "Could not create the ALSA mixer interface\n"); + return rc; + } + + printk(TPACPI_INFO + "Console audio control enabled, mode: %s\n", + (volume_control_allowed) ? + "override (read/write)" : + "monitor (read only)"); + + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, + "registering volume hotkeys as change notification\n"); + tpacpi_hotkey_driver_mask_set(hotkey_driver_mask + | TP_ACPI_HKEY_VOLUP_MASK + | TP_ACPI_HKEY_VOLDWN_MASK + | TP_ACPI_HKEY_MUTE_MASK); + + return 0; +} + +static int volume_read(struct seq_file *m) +{ + u8 status; + + if (volume_get_status(&status) < 0) { + seq_printf(m, "level:\t\tunreadable\n"); + } else { + if (tp_features.mixer_no_level_control) + seq_printf(m, "level:\t\tunsupported\n"); + else + seq_printf(m, "level:\t\t%d\n", + status & TP_EC_AUDIO_LVL_MSK); + + seq_printf(m, "mute:\t\t%s\n", + onoff(status, TP_EC_AUDIO_MUTESW)); + + if (volume_control_allowed) { + seq_printf(m, "commands:\tunmute, mute\n"); + if (!tp_features.mixer_no_level_control) { + seq_printf(m, + "commands:\tup, down\n"); + seq_printf(m, + "commands:\tlevel <level>" + " (<level> is 0-%d)\n", + TP_EC_VOLUME_MAX); + } } } return 0; } +static int volume_write(char *buf) +{ + u8 s; + u8 new_level, new_mute; + int l; + char *cmd; + int rc; + + /* + * We do allow volume control at driver startup, so that the + * user can set initial state through the volume=... parameter hack. + */ + if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) { + if (unlikely(!tp_warned.volume_ctrl_forbidden)) { + tp_warned.volume_ctrl_forbidden = 1; + printk(TPACPI_NOTICE + "Console audio control in monitor mode, " + "changes are not allowed.\n"); + printk(TPACPI_NOTICE + "Use the volume_control=1 module parameter " + "to enable volume control\n"); + } + return -EPERM; + } + + rc = volume_get_status(&s); + if (rc < 0) + return rc; + + new_level = s & TP_EC_AUDIO_LVL_MSK; + new_mute = s & TP_EC_AUDIO_MUTESW_MSK; + + while ((cmd = next_cmd(&buf))) { + if (!tp_features.mixer_no_level_control) { + if (strlencmp(cmd, "up") == 0) { + if (new_mute) + new_mute = 0; + else if (new_level < TP_EC_VOLUME_MAX) + new_level++; + continue; + } else if (strlencmp(cmd, "down") == 0) { + if (new_mute) + new_mute = 0; + else if (new_level > 0) + new_level--; + continue; + } else if (sscanf(cmd, "level %u", &l) == 1 && + l >= 0 && l <= TP_EC_VOLUME_MAX) { + new_level = l; + continue; + } + } + if (strlencmp(cmd, "mute") == 0) + new_mute = TP_EC_AUDIO_MUTESW_MSK; + else if (strlencmp(cmd, "unmute") == 0) + new_mute = 0; + else + return -EINVAL; + } + + if (tp_features.mixer_no_level_control) { + tpacpi_disclose_usertask("procfs volume", "%smute\n", + new_mute ? "" : "un"); + rc = volume_set_mute(!!new_mute); + } else { + tpacpi_disclose_usertask("procfs volume", + "%smute and set level to %d\n", + new_mute ? "" : "un", new_level); + rc = volume_set_status(new_mute | new_level); + } + volume_alsa_notify_change(); + + return (rc == -EINTR) ? -ERESTARTSYS : rc; +} + static struct ibm_struct volume_driver_data = { .name = "volume", .read = volume_read, .write = volume_write, + .exit = volume_exit, + .suspend = volume_suspend, + .resume = volume_resume, + .shutdown = volume_shutdown, }; /************************************************************************* @@ -7507,9 +8090,8 @@ static void fan_resume(void) } } -static int fan_read(char *p) +static int fan_read(struct seq_file *m) { - int len = 0; int rc; u8 status; unsigned int speed = 0; @@ -7521,7 +8103,7 @@ static int fan_read(char *p) if (rc < 0) return rc; - len += sprintf(p + len, "status:\t\t%s\n" + seq_printf(m, "status:\t\t%s\n" "level:\t\t%d\n", (status != 0) ? "enabled" : "disabled", status); break; @@ -7532,54 +8114,54 @@ static int fan_read(char *p) if (rc < 0) return rc; - len += sprintf(p + len, "status:\t\t%s\n", + seq_printf(m, "status:\t\t%s\n", (status != 0) ? "enabled" : "disabled"); rc = fan_get_speed(&speed); if (rc < 0) return rc; - len += sprintf(p + len, "speed:\t\t%d\n", speed); + seq_printf(m, "speed:\t\t%d\n", speed); if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ - len += sprintf(p + len, "level:\t\tdisengaged\n"); + seq_printf(m, "level:\t\tdisengaged\n"); else if (status & TP_EC_FAN_AUTO) - len += sprintf(p + len, "level:\t\tauto\n"); + seq_printf(m, "level:\t\tauto\n"); else - len += sprintf(p + len, "level:\t\t%d\n", status); + seq_printf(m, "level:\t\t%d\n", status); break; case TPACPI_FAN_NONE: default: - len += sprintf(p + len, "status:\t\tnot supported\n"); + seq_printf(m, "status:\t\tnot supported\n"); } if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { - len += sprintf(p + len, "commands:\tlevel <level>"); + seq_printf(m, "commands:\tlevel <level>"); switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: - len += sprintf(p + len, " (<level> is 0-7)\n"); + seq_printf(m, " (<level> is 0-7)\n"); break; default: - len += sprintf(p + len, " (<level> is 0-7, " + seq_printf(m, " (<level> is 0-7, " "auto, disengaged, full-speed)\n"); break; } } if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n" + seq_printf(m, "commands:\tenable, disable\n" "commands:\twatchdog <timeout> (<timeout> " "is 0 (off), 1-120 (seconds))\n"); if (fan_control_commands & TPACPI_FAN_CMD_SPEED) - len += sprintf(p + len, "commands:\tspeed <speed>" + seq_printf(m, "commands:\tspeed <speed>" " (<speed> is 0-65535)\n"); - return len; + return 0; } static int fan_write_cmd_level(const char *cmd, int *rc) @@ -7721,10 +8303,23 @@ static struct ibm_struct fan_driver_data = { */ static void tpacpi_driver_event(const unsigned int hkey_event) { + if (ibm_backlight_device) { + switch (hkey_event) { + case TP_HKEY_EV_BRGHT_UP: + case TP_HKEY_EV_BRGHT_DOWN: + tpacpi_brightness_notify_change(); + } + } + if (alsa_card) { + switch (hkey_event) { + case TP_HKEY_EV_VOL_UP: + case TP_HKEY_EV_VOL_DOWN: + case TP_HKEY_EV_VOL_MUTE: + volume_alsa_notify_change(); + } + } } - - static void hotkey_driver_event(const unsigned int scancode) { tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode); @@ -7853,19 +8448,19 @@ static int __init ibm_init(struct ibm_init_struct *iibm) "%s installed\n", ibm->name); if (ibm->read) { - entry = create_proc_entry(ibm->name, - S_IFREG | S_IRUGO | S_IWUSR, - proc_dir); + mode_t mode; + + mode = S_IRUGO; + if (ibm->write) + mode |= S_IWUSR; + entry = proc_create_data(ibm->name, mode, proc_dir, + &dispatch_proc_fops, ibm); if (!entry) { printk(TPACPI_ERR "unable to create proc entry %s\n", ibm->name); ret = -ENODEV; goto err_out; } - entry->data = ibm; - entry->read_proc = &dispatch_procfs_read; - if (ibm->write) - entry->write_proc = &dispatch_procfs_write; ibm->flags.proc_created = 1; } @@ -8077,6 +8672,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { .data = &brightness_driver_data, }, { + .init = volume_init, .data = &volume_driver_data, }, { @@ -8112,36 +8708,59 @@ static int __init set_ibm_param(const char *val, struct kernel_param *kp) return -EINVAL; } -module_param(experimental, int, 0); +module_param(experimental, int, 0444); MODULE_PARM_DESC(experimental, "Enables experimental features when non-zero"); module_param_named(debug, dbg_level, uint, 0); MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); -module_param(force_load, bool, 0); +module_param(force_load, bool, 0444); MODULE_PARM_DESC(force_load, "Attempts to load the driver even on a " "mis-identified ThinkPad when true"); -module_param_named(fan_control, fan_control_allowed, bool, 0); +module_param_named(fan_control, fan_control_allowed, bool, 0444); MODULE_PARM_DESC(fan_control, "Enables setting fan parameters features when true"); -module_param_named(brightness_mode, brightness_mode, uint, 0); +module_param_named(brightness_mode, brightness_mode, uint, 0444); MODULE_PARM_DESC(brightness_mode, "Selects brightness control strategy: " "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); -module_param(brightness_enable, uint, 0); +module_param(brightness_enable, uint, 0444); MODULE_PARM_DESC(brightness_enable, "Enables backlight control when 1, disables when 0"); -module_param(hotkey_report_mode, uint, 0); +module_param(hotkey_report_mode, uint, 0444); MODULE_PARM_DESC(hotkey_report_mode, "used for backwards compatibility with userspace, " "see documentation"); +module_param_named(volume_mode, volume_mode, uint, 0444); +MODULE_PARM_DESC(volume_mode, + "Selects volume control strategy: " + "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM"); + +module_param_named(volume_capabilities, volume_capabilities, uint, 0444); +MODULE_PARM_DESC(volume_capabilities, + "Selects the mixer capabilites: " + "0=auto, 1=volume and mute, 2=mute only"); + +module_param_named(volume_control, volume_control_allowed, bool, 0444); +MODULE_PARM_DESC(volume_control, + "Enables software override for the console audio " + "control when true"); + +/* ALSA module API parameters */ +module_param_named(index, alsa_index, int, 0444); +MODULE_PARM_DESC(index, "ALSA index for the ACPI EC Mixer"); +module_param_named(id, alsa_id, charp, 0444); +MODULE_PARM_DESC(id, "ALSA id for the ACPI EC Mixer"); +module_param_named(enable, alsa_enable, bool, 0444); +MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer"); + #define TPACPI_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ @@ -8160,25 +8779,25 @@ TPACPI_PARAM(volume); TPACPI_PARAM(fan); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES -module_param(dbg_wlswemul, uint, 0); +module_param(dbg_wlswemul, uint, 0444); MODULE_PARM_DESC(dbg_wlswemul, "Enables WLSW emulation"); module_param_named(wlsw_state, tpacpi_wlsw_emulstate, bool, 0); MODULE_PARM_DESC(wlsw_state, "Initial state of the emulated WLSW switch"); -module_param(dbg_bluetoothemul, uint, 0); +module_param(dbg_bluetoothemul, uint, 0444); MODULE_PARM_DESC(dbg_bluetoothemul, "Enables bluetooth switch emulation"); module_param_named(bluetooth_state, tpacpi_bluetooth_emulstate, bool, 0); MODULE_PARM_DESC(bluetooth_state, "Initial state of the emulated bluetooth switch"); -module_param(dbg_wwanemul, uint, 0); +module_param(dbg_wwanemul, uint, 0444); MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation"); module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0); MODULE_PARM_DESC(wwan_state, "Initial state of the emulated WWAN switch"); -module_param(dbg_uwbemul, uint, 0); +module_param(dbg_uwbemul, uint, 0444); MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation"); module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0); MODULE_PARM_DESC(uwb_state, @@ -8371,6 +8990,7 @@ static int __init thinkpad_acpi_module_init(void) PCI_VENDOR_ID_IBM; tpacpi_inputdev->id.product = TPACPI_HKEY_INPUT_PRODUCT; tpacpi_inputdev->id.version = TPACPI_HKEY_INPUT_VERSION; + tpacpi_inputdev->dev.parent = &tpacpi_pdev->dev; } for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { ret = ibm_init(&ibms_init[i]); diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c new file mode 100644 index 0000000..a350418 --- /dev/null +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -0,0 +1,144 @@ +/* + * Toshiba Bluetooth Enable Driver + * + * Copyright (C) 2009 Jes Sorensen <Jes.Sorensen@gmail.com> + * + * Thanks to Matthew Garrett for background info on ACPI innards which + * normal people aren't meant to understand :-) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Note the Toshiba Bluetooth RFKill switch seems to be a strange + * fish. It only provides a BT event when the switch is flipped to + * the 'on' position. When flipping it to 'off', the USB device is + * simply pulled away underneath us, without any BT event being + * delivered. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>"); +MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver"); +MODULE_LICENSE("GPL"); + + +static int toshiba_bt_rfkill_add(struct acpi_device *device); +static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); +static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); +static int toshiba_bt_resume(struct acpi_device *device); + +static const struct acpi_device_id bt_device_ids[] = { + { "TOS6205", 0}, + { "", 0}, +}; +MODULE_DEVICE_TABLE(acpi, bt_device_ids); + +static struct acpi_driver toshiba_bt_rfkill_driver = { + .name = "Toshiba BT", + .class = "Toshiba", + .ids = bt_device_ids, + .ops = { + .add = toshiba_bt_rfkill_add, + .remove = toshiba_bt_rfkill_remove, + .notify = toshiba_bt_rfkill_notify, + .resume = toshiba_bt_resume, + }, + .owner = THIS_MODULE, +}; + + +static int toshiba_bluetooth_enable(acpi_handle handle) +{ + acpi_status res1, res2; + acpi_integer result; + + /* + * Query ACPI to verify RFKill switch is set to 'on'. + * If not, we return silently, no need to report it as + * an error. + */ + res1 = acpi_evaluate_integer(handle, "BTST", NULL, &result); + if (ACPI_FAILURE(res1)) + return res1; + if (!(result & 0x01)) + return 0; + + printk(KERN_INFO "toshiba_bluetooth: Re-enabling Toshiba Bluetooth\n"); + res1 = acpi_evaluate_object(handle, "AUSB", NULL, NULL); + res2 = acpi_evaluate_object(handle, "BTPO", NULL, NULL); + if (!ACPI_FAILURE(res1) || !ACPI_FAILURE(res2)) + return 0; + + printk(KERN_WARNING "toshiba_bluetooth: Failed to re-enable " + "Toshiba Bluetooth\n"); + + return -ENODEV; +} + +static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) +{ + toshiba_bluetooth_enable(device->handle); +} + +static int toshiba_bt_resume(struct acpi_device *device) +{ + return toshiba_bluetooth_enable(device->handle); +} + +static int toshiba_bt_rfkill_add(struct acpi_device *device) +{ + acpi_status status; + acpi_integer bt_present; + int result = -ENODEV; + + /* + * Some Toshiba laptops may have a fake TOS6205 device in + * their ACPI BIOS, so query the _STA method to see if there + * is really anything there, before trying to enable it. + */ + status = acpi_evaluate_integer(device->handle, "_STA", NULL, + &bt_present); + + if (!ACPI_FAILURE(status) && bt_present) { + printk(KERN_INFO "Detected Toshiba ACPI Bluetooth device - " + "installing RFKill handler\n"); + result = toshiba_bluetooth_enable(device->handle); + } + + return result; +} + +static int __init toshiba_bt_rfkill_init(void) +{ + int result; + + result = acpi_bus_register_driver(&toshiba_bt_rfkill_driver); + if (result < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error registering driver\n")); + return result; + } + + return 0; +} + +static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type) +{ + /* clean up */ + return 0; +} + +static void __exit toshiba_bt_rfkill_exit(void) +{ + acpi_bus_unregister_driver(&toshiba_bt_rfkill_driver); +} + +module_init(toshiba_bt_rfkill_init); +module_exit(toshiba_bt_rfkill_exit); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 177f8d7..e425a86 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -30,6 +30,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> +#include <linux/device.h> #include <linux/list.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> @@ -65,6 +66,7 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; + struct device *dev; }; static struct wmi_block wmi_blocks; @@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest) return true; } +/* + * Convert a raw GUID to the ACII string representation + */ +static int wmi_gtoa(const char *in, char *out) +{ + int i; + + for (i = 3; i >= 0; i--) + out += sprintf(out, "%02X", in[i] & 0xFF); + + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[5] & 0xFF); + out += sprintf(out, "%02X", in[4] & 0xFF); + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[7] & 0xFF); + out += sprintf(out, "%02X", in[6] & 0xFF); + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[8] & 0xFF); + out += sprintf(out, "%02X", in[9] & 0xFF); + out += sprintf(out, "-"); + + for (i = 10; i <= 15; i++) + out += sprintf(out, "%02X", in[i] & 0xFF); + + out = '\0'; + return 0; +} + static bool find_guid(const char *guid_string, struct wmi_block **out) { char tmp[16], guid_input[16]; @@ -555,6 +585,138 @@ bool wmi_has_guid(const char *guid_string) EXPORT_SYMBOL_GPL(wmi_has_guid); /* + * sysfs interface + */ +static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, + char *buf) +{ + char guid_string[37]; + struct wmi_block *wblock; + + wblock = dev_get_drvdata(dev); + if (!wblock) + return -ENOMEM; + + wmi_gtoa(wblock->gblock.guid, guid_string); + + return sprintf(buf, "wmi:%s\n", guid_string); +} +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char guid_string[37]; + + struct wmi_block *wblock; + + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + wblock = dev_get_drvdata(dev); + if (!wblock) + return -ENOMEM; + + wmi_gtoa(wblock->gblock.guid, guid_string); + + strcpy(&env->buf[env->buflen - 1], "wmi:"); + memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); + env->buflen += 40; + + return 0; +} + +static void wmi_dev_free(struct device *dev) +{ + kfree(dev); +} + +static struct class wmi_class = { + .name = "wmi", + .dev_release = wmi_dev_free, + .dev_uevent = wmi_dev_uevent, +}; + +static int wmi_create_devs(void) +{ + int result; + char guid_string[37]; + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; + + /* Create devices for all the GUIDs */ + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!guid_dev) + return -ENOMEM; + + wblock->dev = guid_dev; + + guid_dev->class = &wmi_class; + dev_set_drvdata(guid_dev, wblock); + + gblock = &wblock->gblock; + + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(guid_dev, guid_string); + + result = device_register(guid_dev); + if (result) + return result; + + result = device_create_file(guid_dev, &dev_attr_modalias); + if (result) + return result; + } + + return 0; +} + +static void wmi_remove_devs(void) +{ + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; + + /* Delete devices for all the GUIDs */ + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = wblock->dev; + gblock = &wblock->gblock; + + device_remove_file(guid_dev, &dev_attr_modalias); + + device_unregister(guid_dev); + } +} + +static void wmi_class_exit(void) +{ + wmi_remove_devs(); + class_unregister(&wmi_class); +} + +static int wmi_class_init(void) +{ + int ret; + + ret = class_register(&wmi_class); + if (ret) + return ret; + + ret = wmi_create_devs(); + if (ret) + wmi_class_exit(); + + return ret; +} + +/* * Parse the _WDG method for the GUID data blocks */ static __init acpi_status parse_wdg(acpi_handle handle) @@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void) if (result < 0) { printk(KERN_INFO PREFIX "Error loading mapper\n"); - } else { - printk(KERN_INFO PREFIX "Mapper loaded\n"); + return -ENODEV; + } + + result = wmi_class_init(); + if (result) { + acpi_bus_unregister_driver(&acpi_wmi_driver); + return result; } + printk(KERN_INFO PREFIX "Mapper loaded\n"); + return result; } @@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void) struct list_head *p, *tmp; struct wmi_block *wblock; + wmi_class_exit(); + acpi_bus_unregister_driver(&acpi_wmi_driver); list_for_each_safe(p, tmp, &wmi_blocks.list) { diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 83b8b5a..5314bf6 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -80,7 +80,8 @@ static int pnpacpi_get_resources(struct pnp_dev *dev) static int pnpacpi_set_resources(struct pnp_dev *dev) { - acpi_handle handle = dev->data; + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; struct acpi_buffer buffer; int ret; @@ -103,7 +104,8 @@ static int pnpacpi_set_resources(struct pnp_dev *dev) static int pnpacpi_disable_resources(struct pnp_dev *dev) { - acpi_handle handle = dev->data; + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; int ret; dev_dbg(&dev->dev, "disable resources\n"); @@ -121,6 +123,8 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev) #ifdef CONFIG_ACPI_SLEEP static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) { + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; int power_state; power_state = acpi_pm_device_sleep_state(&dev->dev, NULL); @@ -128,16 +132,19 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) power_state = (state.event == PM_EVENT_ON) ? ACPI_STATE_D0 : ACPI_STATE_D3; - return acpi_bus_set_power((acpi_handle) dev->data, power_state); + return acpi_bus_set_power(handle, power_state); } static int pnpacpi_resume(struct pnp_dev *dev) { - return acpi_bus_set_power((acpi_handle) dev->data, ACPI_STATE_D0); + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; + + return acpi_bus_set_power(handle, ACPI_STATE_D0); } #endif -static struct pnp_protocol pnpacpi_protocol = { +struct pnp_protocol pnpacpi_protocol = { .name = "Plug and Play ACPI", .get = pnpacpi_get_resources, .set = pnpacpi_set_resources, @@ -147,6 +154,7 @@ static struct pnp_protocol pnpacpi_protocol = { .resume = pnpacpi_resume, #endif }; +EXPORT_SYMBOL(pnpacpi_protocol); static int __init pnpacpi_add_device(struct acpi_device *device) { @@ -168,7 +176,7 @@ static int __init pnpacpi_add_device(struct acpi_device *device) if (!dev) return -ENOMEM; - dev->data = device->handle; + dev->data = device; /* .enabled means the device can decode the resources */ dev->active = device->status.enabled; status = acpi_get_handle(device->handle, "_SRS", &temp); diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index ef3a2cd3a..5702b2c 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -465,7 +465,8 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, int pnpacpi_parse_allocated_resource(struct pnp_dev *dev) { - acpi_handle handle = dev->data; + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; acpi_status status; pnp_dbg(&dev->dev, "parse allocated resources\n"); @@ -773,7 +774,8 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, int __init pnpacpi_parse_resource_option_data(struct pnp_dev *dev) { - acpi_handle handle = dev->data; + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; acpi_status status; struct acpipnp_parse_option_s parse_data; @@ -845,7 +847,8 @@ static acpi_status pnpacpi_type_resources(struct acpi_resource *res, void *data) int pnpacpi_build_resource_template(struct pnp_dev *dev, struct acpi_buffer *buffer) { - acpi_handle handle = dev->data; + struct acpi_device *acpi_dev = dev->data; + acpi_handle handle = acpi_dev->handle; struct acpi_resource *resource; int res_cnt = 0; acpi_status status; diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6f8d8f9..5066de5 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -225,6 +225,12 @@ passive_store(struct device *dev, struct device_attribute *attr, if (!sscanf(buf, "%d\n", &state)) return -EINVAL; + /* sanity check: values below 1000 millicelcius don't make sense + * and can cause the system to go into a thermal heart attack + */ + if (state && state < 1000) + return -EINVAL; + if (state && !tz->forced_passive) { mutex_lock(&thermal_list_lock); list_for_each_entry(cdev, &thermal_cdev_list, node) { @@ -235,6 +241,8 @@ passive_store(struct device *dev, struct device_attribute *attr, cdev); } mutex_unlock(&thermal_list_lock); + if (!tz->passive_delay) + tz->passive_delay = 1000; } else if (!state && tz->forced_passive) { mutex_lock(&thermal_list_lock); list_for_each_entry(cdev, &thermal_cdev_list, node) { @@ -245,17 +253,12 @@ passive_store(struct device *dev, struct device_attribute *attr, cdev); } mutex_unlock(&thermal_list_lock); + tz->passive_delay = 0; } tz->tc1 = 1; tz->tc2 = 1; - if (!tz->passive_delay) - tz->passive_delay = 1000; - - if (!tz->polling_delay) - tz->polling_delay = 10000; - tz->forced_passive = state; thermal_zone_device_update(tz); @@ -374,7 +377,7 @@ thermal_cooling_device_cur_state_store(struct device *dev, if (!sscanf(buf, "%ld\n", &state)) return -EINVAL; - if (state < 0) + if ((long)state < 0) return -EINVAL; result = cdev->ops->set_cur_state(cdev, state); @@ -1016,6 +1019,8 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) thermal_zone_device_set_polling(tz, tz->passive_delay); else if (tz->polling_delay) thermal_zone_device_set_polling(tz, tz->polling_delay); + else + thermal_zone_device_set_polling(tz, 0); mutex_unlock(&tz->lock); } EXPORT_SYMBOL(thermal_zone_device_update); diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 5c823d5..d814da4 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -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) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 5e1ad3c..86e9735 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -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" diff --git a/include/acpi/processor.h b/include/acpi/processor.h index 8b668ea..29245c6 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.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) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index dfcd920..ce945d4 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -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 | \ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index fddfafa..7c4193e 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -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 |