From 5f46c2f25cfbb5faca7550738ac42c4d1491ddc8 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:24 -0600 Subject: [PATCH 001/117] ACPI: dock: convert sysfs attributes to an attribute_group As suggested by Dmitry Torokhov, convert the individual sysfs attributes into an attribute group. This change eliminates quite a bit of copy/paste code in the error handling paths. Signed-off-by: Alex Chiang Reviewed-by: Dmitry Torokhov Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 81 ++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 57 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7338b6a3e04..4f2aa98e2ce 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -936,6 +936,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 @@ -969,9 +982,8 @@ static int dock_add(acpi_handle handle) 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); + ret = PTR_ERR(dock_device); + goto out; } platform_device_add_data(dock_device, &dock_station, sizeof(struct dock_station *)); @@ -986,47 +998,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(&dock_device->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, @@ -1036,10 +1010,8 @@ static int dock_add(acpi_handle handle) /* 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; + goto err_rmgroup; } add_dock_dependent_device(dock_station, dd); @@ -1047,15 +1019,14 @@ static int dock_add(acpi_handle handle) 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); +err_rmgroup: + sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group); +err_unregister: platform_device_unregister(dock_device); +out: kfree(dock_station); dock_station = NULL; + printk(KERN_ERR "%s encountered error %d\n", __func__, ret); return ret; } @@ -1076,11 +1047,7 @@ static int dock_remove(struct dock_station *dock_station) kfree(dd); /* 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 */ From 1caab3c1a90be3aa4ec3599409d8fe044b077478 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 4 Nov 2009 14:17:53 -0500 Subject: [PATCH 002/117] wmi: Add support for module autoloading WMI provides interface-specific GUIDs that are exported from modules as modalises, but the core currently generates no events to trigger module loading. This patch adds support for registering devices for each WMI GUID and generating the appropriate uevent. Based heavily on a patch by Carlos Corbacho (). Signed-off-by: Matthew Garrett Tested-by: Carlos Corbacho Acked-by: Carlos Corbacho Signed-off-by: Len Brown --- drivers/platform/x86/wmi.c | 175 ++++++++++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 177f8d767df..e425a868cd3 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,7 @@ struct wmi_block { acpi_handle handle; wmi_notify_handler handler; void *handler_data; + struct device *dev; }; static struct wmi_block wmi_blocks; @@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest) return true; } +/* + * Convert a raw GUID to the ACII string representation + */ +static int wmi_gtoa(const char *in, char *out) +{ + int i; + + for (i = 3; i >= 0; i--) + out += sprintf(out, "%02X", in[i] & 0xFF); + + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[5] & 0xFF); + out += sprintf(out, "%02X", in[4] & 0xFF); + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[7] & 0xFF); + out += sprintf(out, "%02X", in[6] & 0xFF); + out += sprintf(out, "-"); + out += sprintf(out, "%02X", in[8] & 0xFF); + out += sprintf(out, "%02X", in[9] & 0xFF); + out += sprintf(out, "-"); + + for (i = 10; i <= 15; i++) + out += sprintf(out, "%02X", in[i] & 0xFF); + + out = '\0'; + return 0; +} + static bool find_guid(const char *guid_string, struct wmi_block **out) { char tmp[16], guid_input[16]; @@ -554,6 +584,138 @@ bool wmi_has_guid(const char *guid_string) } EXPORT_SYMBOL_GPL(wmi_has_guid); +/* + * sysfs interface + */ +static ssize_t show_modalias(struct device *dev, struct device_attribute *attr, + char *buf) +{ + char guid_string[37]; + struct wmi_block *wblock; + + wblock = dev_get_drvdata(dev); + if (!wblock) + return -ENOMEM; + + wmi_gtoa(wblock->gblock.guid, guid_string); + + return sprintf(buf, "wmi:%s\n", guid_string); +} +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + char guid_string[37]; + + struct wmi_block *wblock; + + if (add_uevent_var(env, "MODALIAS=")) + return -ENOMEM; + + wblock = dev_get_drvdata(dev); + if (!wblock) + return -ENOMEM; + + wmi_gtoa(wblock->gblock.guid, guid_string); + + strcpy(&env->buf[env->buflen - 1], "wmi:"); + memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36); + env->buflen += 40; + + return 0; +} + +static void wmi_dev_free(struct device *dev) +{ + kfree(dev); +} + +static struct class wmi_class = { + .name = "wmi", + .dev_release = wmi_dev_free, + .dev_uevent = wmi_dev_uevent, +}; + +static int wmi_create_devs(void) +{ + int result; + char guid_string[37]; + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; + + /* Create devices for all the GUIDs */ + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + if (!guid_dev) + return -ENOMEM; + + wblock->dev = guid_dev; + + guid_dev->class = &wmi_class; + dev_set_drvdata(guid_dev, wblock); + + gblock = &wblock->gblock; + + wmi_gtoa(gblock->guid, guid_string); + dev_set_name(guid_dev, guid_string); + + result = device_register(guid_dev); + if (result) + return result; + + result = device_create_file(guid_dev, &dev_attr_modalias); + if (result) + return result; + } + + return 0; +} + +static void wmi_remove_devs(void) +{ + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; + struct device *guid_dev; + + /* Delete devices for all the GUIDs */ + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + + guid_dev = wblock->dev; + gblock = &wblock->gblock; + + device_remove_file(guid_dev, &dev_attr_modalias); + + device_unregister(guid_dev); + } +} + +static void wmi_class_exit(void) +{ + wmi_remove_devs(); + class_unregister(&wmi_class); +} + +static int wmi_class_init(void) +{ + int ret; + + ret = class_register(&wmi_class); + if (ret) + return ret; + + ret = wmi_create_devs(); + if (ret) + wmi_class_exit(); + + return ret; +} + /* * Parse the _WDG method for the GUID data blocks */ @@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void) if (result < 0) { printk(KERN_INFO PREFIX "Error loading mapper\n"); - } else { - printk(KERN_INFO PREFIX "Mapper loaded\n"); + return -ENODEV; } + result = wmi_class_init(); + if (result) { + acpi_bus_unregister_driver(&acpi_wmi_driver); + return result; + } + + printk(KERN_INFO PREFIX "Mapper loaded\n"); + return result; } @@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void) struct list_head *p, *tmp; struct wmi_block *wblock; + wmi_class_exit(); + acpi_bus_unregister_driver(&acpi_wmi_driver); list_for_each_safe(p, tmp, &wmi_blocks.list) { From 7fb2616e249184e217f9818a7662596165710ea4 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Mon, 26 Oct 2009 08:39:01 +0100 Subject: [PATCH 003/117] acpi: thermal: display forced passive trip points in proc Users can force a passive trip point for a thermal zone that does not have _PSV defined in ACPI by setting the passive attribute in sysfs. It's useful to display such trip points in /proc/acpi/thermal_zone. .../TZ1/cooling_mode: .../TZ1/polling_frequency:polling frequency: 10 seconds .../TZ1/state:state: ok .../TZ1/temperature:temperature: 53 C .../TZ1/trip_points:critical (S5): 110 C .../TZ1/trip_points:passive (forced): 95 C And if not set (passive is 0): .../TZ1/trip_points:passive (forced): Signed-off-by: Frans Pop Acked-by: Zhang Rui Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/thermal.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 65f67815902..9073ada8883 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, "\n"); } for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { From 3d8e3ad879441ae14c5957b933028daf39d252b0 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Mon, 26 Oct 2009 08:39:02 +0100 Subject: [PATCH 004/117] thermal: add sanity check for the passive attribute Values below 1000 milli-celsius don't make sense and can cause the system to go into a thermal heart attack: the actual temperature will always be lower and thus the system will be throttled down to its lowest setting. An additional problem is that values below 1000 will show as 0 in /proc/acpi/thermal/TZx/trip_points:passive. cat passive 0 echo -n 90 >passive bash: echo: write error: Invalid argument echo -n 90000 >passive cat passive 90000 Signed-off-by: Frans Pop Acked-by: Zhang Rui Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- Documentation/thermal/sysfs-api.txt | 1 + drivers/thermal/thermal_sys.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index a87dc277a5c..cb3d15bc1ae 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/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6f8d8f97121..310e40ab00b 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) { From e4143b0324a6a468cb00c4341cd20d60cc441252 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Mon, 26 Oct 2009 08:39:03 +0100 Subject: [PATCH 005/117] thermal: Only set passive_delay for forced_passive cooling Setting polling_delay is useless as passive_delay has priority, so the value shown in proc isn't the actual polling delay. It also gives the impression to the user that he can change the polling interval through proc, while in fact he can't. Also, unset passive_delay when the forced passive trip point is unbound to allow polling to be disabled. Signed-off-by: Frans Pop Acked-by: Matthew Garrett Acked-by: Zhang Rui Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/thermal/thermal_sys.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 310e40ab00b..663c2fdba77 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -241,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) { @@ -251,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); From 3767cb54ac718eb049d2a29f7a575ab923550ba5 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Mon, 26 Oct 2009 08:39:04 +0100 Subject: [PATCH 006/117] thermal: disable polling if passive_delay and polling_delay are both unset Otherwise polling will continue for the thermal zone even when it is no longer needed, for example because forced passive cooling was disabled. Signed-off-by: Frans Pop Acked-by: Matthew Garrett Acked-by: Zhang Rui Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/thermal/thermal_sys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 663c2fdba77..3bc72ea57e0 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1019,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); From d81c45e1c9369855901420f79114852eba2ea16a Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 16 Oct 2009 09:20:41 +0800 Subject: [PATCH 007/117] ACPI: Notify the _PPC evaluation status to the platform According to the ACPI spec(section 8.4.4.3) OSPM should convey the _PPC evaluations status to the platform if there exists the _OST object. The _OST contains two arguments: The first is the PERFORMANCE notificatin event. The second is the status of _PPC object. OSPM will convey the _PPC evaluation status to the platform. Of course when the module parameter of "ignore_ppc" is added, OSPM won't evaluate the _PPC object. But it will call the _OST object. At the same time the _OST object will be evaluated only when the PERFORMANCE notification event is received. Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 6 ++-- drivers/acpi/processor_perflib.c | 50 ++++++++++++++++++++++++++++++-- include/acpi/processor.h | 5 ++-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index c567b46dfa0..773d7e76f30 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -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 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); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 8ba0ed0b9dd..fc1f49bbbee 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/include/acpi/processor.h b/include/acpi/processor.h index 740ac3ad8fd..a920237f7c6 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); #else static inline void acpi_processor_ppc_init(void) { @@ -304,7 +304,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) { From 876fba43cc810e3c37ce26995933f9547b83cb0e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 11 Nov 2009 15:22:15 +0100 Subject: [PATCH 008/117] ACPI: add const to acpi_check_resource_conflict() acpi_check_resource_conflict() doesn't change the resource it operates on, so the res parameter can be marked const. Signed-off-by: Jean Delvare Signed-off-by: Len Brown --- drivers/acpi/osl.c | 2 +- include/linux/acpi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7c1c59ea9ec..02e8464e480 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/include/linux/acpi.h b/include/linux/acpi.h index dfcd920c3e5..c920d2def4d 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); From a9f8eacca4e9e8693de9b896c1fa7aadaa9402e8 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:21 +0000 Subject: [PATCH 009/117] thinkpad-acpi: fix default brightness_mode for R50e/R51 According to a report, the R50e wants EC-based brightness control, even if it uses an Intel GPU. The current driver default was reported to not work at all. This bug can be worked around by the "brightness_mode=3" module parameter. Change the default of the R50e and R51 2xxx models (which use the same EC firmware, 1V) to TPACPI_BRGHT_Q_EC, but keep TPACPI_BRGHT_Q_ASK set for now, as I'd like to get more reports. This fixes a regression caused by commit 59fe4fe34d7afdf63208124f313be9056feaa2f4, "thinkpad-acpi: fix incorrect use of TPACPI_BRGHT_MODE_ECNVRAM" Kernel 2.6.31 also needs this fix. Reported-by: Ferenc Wagner Tested-by: Ferenc Wagner Signed-off-by: Henrique de Moraes Holschuh Cc: stable@kernel.org Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a848c7e20ae..9c6d5a929e9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6123,8 +6123,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 */ From 208b996b6c460285650d39b2330f8ef82c007d10 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:22 +0000 Subject: [PATCH 010/117] thinkpad-acpi: preserve rfkill state across suspend/resume Since the rfkill rework in 2.6.31, the driver is always resuming with the radios disabled. Change thinkpad-acpi to ask the firmware to resume with the radios in the last state. This fixes the Bluetooth and WWAN rfkill switches. Note that it means we respect the firmware's oddities. Should the user toggle the hardware rfkill switch on and off, it might cause the radios to resume enabled. UWB is an unknown quantity since it has nowhere the same level of firmware support (no control over state storage in NVRAM, for example), and might need further fixing. Testers welcome. This change fixes a regression from 2.6.30. Reported-by: Jerone Young Reported-by: Ian Molton Signed-off-by: Henrique de Moraes Holschuh Tested-by: Ian Molton Cc: stable@kernel.org Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 32 +++++----------------------- 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9c6d5a929e9..1ee734c14cc 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3866,15 +3866,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; @@ -3908,10 +3899,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; @@ -4050,7 +4040,6 @@ static struct ibm_struct bluetooth_driver_data = { .read = bluetooth_read, .write = bluetooth_write, .exit = bluetooth_exit, - .suspend = bluetooth_suspend, .shutdown = bluetooth_shutdown, }; @@ -4068,15 +4057,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; @@ -4109,11 +4089,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; @@ -4251,7 +4230,6 @@ static struct ibm_struct wan_driver_data = { .read = wan_read, .write = wan_write, .exit = wan_exit, - .suspend = wan_suspend, .shutdown = wan_shutdown, }; From 90765c6aee568137521ba19347c744b5abde8161 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:23 +0000 Subject: [PATCH 011/117] thinkpad-acpi: fix some version quirks Update some of the BIOS/EC version quirks. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 1ee734c14cc..6160813d906 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1779,7 +1779,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) */ @@ -1795,8 +1795,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 */ From 347a26860e2293b1347996876d3550499c7bb31f Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:24 +0000 Subject: [PATCH 012/117] thinkpad-acpi: issue backlight class events Take advantage of the new events capabilities of the backlight class to notify userspace of backlight changes. This depends on "backlight: Allow drivers to update the core, and generate events on changes", by Matthew Garrett. Signed-off-by: Henrique de Moraes Holschuh Cc: Matthew Garrett Cc: Richard Purdie Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 51 ++++++------------------- drivers/platform/x86/thinkpad_acpi.c | 24 +++++++++++- 2 files changed, 35 insertions(+), 40 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index aafcaa63419..f5056c7fb5b 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -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 @@ -1465,3 +1436,5 @@ 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. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 6160813d906..44061367a10 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define TPACPI_VERSION "0.23" -#define TPACPI_SYSFS_VERSION 0x020500 +#define TPACPI_SYSFS_VERSION 0x020600 /* * Changelog: @@ -6073,6 +6073,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, @@ -6227,6 +6233,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; } @@ -6303,6 +6315,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; } @@ -7702,6 +7717,13 @@ 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(); + } + } } From 6b30eb7d211840ba1a03f855d9e7b80a921368f2 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:25 +0000 Subject: [PATCH 013/117] thinkpad-acpi: silence bogus complain during rmmod Fix this bogus warning during module shutdown, when backlight event reporting is enabled: "thinkpad_acpi: required events 0x00018000 not enabled!" Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 44061367a10..86418cd3e59 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2189,7 +2189,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; } From d112ef95d4ec1ee7fe7123e3f21e4aac0d57570c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:26 +0000 Subject: [PATCH 014/117] thinkpad-acpi: adopt input device Properly init the parent field of the input device. Thanks to Alan Jenkins, who noted this problem in a different driver. Reported-by: Alan Jenkins Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 86418cd3e59..dd8bd072c79 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8375,6 +8375,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]); From b09c72259e88cec3d602aef987a3209297f3a9c2 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:27 +0000 Subject: [PATCH 015/117] thinkpad-acpi: expose module parameters Export the normal (non-command) module paramenters as mode 0444, so that they will show up in sysfs. These parameters must not be changed at runtime as a rule, with very few exceptions. Reported-by: Ferenc Wagner Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index dd8bd072c79..4b96a961b7e 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8116,32 +8116,32 @@ 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"); @@ -8164,25 +8164,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, From 9ebd9e833648745fa5ac6998b9e0153ccd3ba839 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:28 +0000 Subject: [PATCH 016/117] thinkpad-acpi: log temperatures on termal alarm (v2) Log temperatures on any of the EC thermal alarms. It could be useful to help tracking down what is happening... Signed-off-by: Henrique de Moraes Holschuh Acked-by: Pavel Machek Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 83 +++++++++++++++++++--------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 4b96a961b7e..b3e256b2cf1 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3546,49 +3546,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_ALARM_BAT_HOT: - printk(TPACPI_CRIT - "THERMAL ALARM: battery is too hot!\n"); - /* recommended action: warn user through gui */ - return true; - case TP_HKEY_EV_ALARM_BAT_XHOT: - printk(TPACPI_ALERT - "THERMAL EMERGENCY: battery is extremely hot!\n"); - /* recommended action: immediate sleep/hibernate */ - return true; - 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; - 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; + case TP_HKEY_EV_ALARM_BAT_HOT: + printk(TPACPI_CRIT + "THERMAL ALARM: battery is too hot!\n"); + /* recommended action: warn user through gui */ + break; + case TP_HKEY_EV_ALARM_BAT_XHOT: + printk(TPACPI_ALERT + "THERMAL EMERGENCY: battery is extremely hot!\n"); + /* recommended action: immediate sleep/hibernate */ + 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 */ + 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 */ + 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) @@ -5462,8 +5470,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]; @@ -5553,6 +5564,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, @@ -5568,7 +5601,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); From 792979c8032b8f5adb77ea986db7082fff04c8e7 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Wed, 9 Dec 2009 01:36:29 +0000 Subject: [PATCH 017/117] thinkpad-acpi: use input_set_capability Use input_set_capability() instead of set_bit. Signed-off-by: Henrique de Moraes Holschuh Cc: Dmitry Torokhov Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index b3e256b2cf1..53d6c331806 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3348,16 +3348,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; @@ -3365,12 +3363,10 @@ 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); } 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); } /* Do not issue duplicate brightness change events to From 6dff29b63a5bf2eaf3313cb8a84f0b7520c43401 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:45 +0000 Subject: [PATCH 018/117] eeepc-laptop: disp attribute should be write-only Currently, reading from the disp attribute fails with "No such device", which is misleading. According to CMSG table on acpi4asus project site, no models have a getter method corresponding to SDSP. Change the file permission to disallow reads. If some joker changes the permission to permit reads, then return -EIO to be consistent with sysfs' behaviour when no show() method is provided. Signed-off-by: Alan Jenkins Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 4226e535273..2c65a377296 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -380,7 +380,7 @@ static ssize_t store_sys_acpi(int cm, const char *buf, size_t count) if (rv > 0) value = set_acpi(cm, value); if (value < 0) - return value; + return -EIO; return rv; } @@ -389,11 +389,11 @@ static ssize_t show_sys_acpi(int cm, char *buf) int value = get_acpi(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) \ @@ -409,14 +409,14 @@ static ssize_t show_sys_acpi(int cm, char *buf) 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; From 1df8d8d4efb7bce0e44d2aa91c4c0fa7ffe613c0 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:46 +0000 Subject: [PATCH 019/117] asus-laptop: Remove redundant NULL checks The acpi device callbacks add, start, remove, suspend and resume can never be called with a NULL acpi_device. Each callsite in acpi/scan.c has to dereference the device in order to get the ops structure, e.g. struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) return acpi_drv->ops.suspend(acpi_dev, state); Remove all checks for acpi_dev == NULL within these callbacks. Also remove the checks for acpi_driver_data(acpi_dev) == NULL. None of these checks could fail unless the driver does something strange (which none of them do), the acpi core did something terribly wrong, or we have a memory corruption issue. If this does happen then it's best to dereference the pointer and crash noisily. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b39d2bb3e75..8af43e9b618 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1240,9 +1240,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); @@ -1306,9 +1303,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); From 2d5db0be4ce36b265f9ddd8debab40acd8580403 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:47 +0000 Subject: [PATCH 020/117] asus-acpi: Remove redundant NULL checks The acpi device callbacks add, start, remove, suspend and resume can never be called with a NULL acpi_device. Each callsite in acpi/scan.c has to dereference the device in order to get the ops structure, e.g. struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) return acpi_drv->ops.suspend(acpi_dev, state); Remove all checks for acpi_dev == NULL within these callbacks. Also remove the checks for acpi_driver_data(acpi_dev) == NULL. None of these checks could fail unless the driver does something strange (which none of them do), the acpi core did something terribly wrong, or we have a memory corruption issue. If this does happen then it's best to dereference the pointer and crash noisily. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus_acpi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ddf5240ade8..25a7d57fe44 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -1334,9 +1334,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 +1389,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); From b7fab7a0703a9d3a3b4b59aa2f7b098b2b83b8fe Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:48 +0000 Subject: [PATCH 021/117] asus-laptop: Remove uneccesary acpi_disabled check acpi_bus_register_driver() already checks acpi_disabled, so acpi bus drivers don't need to. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 8af43e9b618..4234edbe6ff 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1438,9 +1438,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; From 5a4a9f6fd3dedefe06aed0e35c76bb6e0177adb6 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:49 +0000 Subject: [PATCH 022/117] asus-acpi: Remove uneccesary acpi_disabled checks acpi_bus_register_driver() already checks acpi_disabled, so acpi bus drivers don't need to. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus_acpi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 25a7d57fe44..783243f7e7a 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -1416,21 +1416,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 From db7c554afeee7e535a5476dedb1339cb30d0b5df Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:50 +0000 Subject: [PATCH 023/117] asus-acpi: set acpi_driver.owner The owner field provides the link between drivers and modules in sysfs, but no ACPI driver was setting it. After setting the owner field, we can see which module provides which driver and vice versa by looking at /sys/bus/acpi/driver/*/module and /sys/module/*/drivers/acpi:*. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 783243f7e7a..0c9c53111a2 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 = { From edf624522757adec8ceb83a4b97747eba645c454 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:51 +0000 Subject: [PATCH 024/117] asus-laptop: set acpi_driver.owner The owner field provides the link between drivers and modules in sysfs, but no ACPI driver was setting it. After setting the owner field, we can see which module provides which driver and vice versa by looking at /sys/bus/acpi/driver/*/module and /sys/module/*/drivers/acpi:*. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 4234edbe6ff..6c05172bf3d 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -232,6 +232,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 = { From 3c0eb510697dbbb53674c72544350624a04ab5b4 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:44:52 +0000 Subject: [PATCH 025/117] eeepc-laptop: add touchpad led This led can be found on Eeepc 1005 series. Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/eeepc-laptop.c | 67 +++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39dea42..e5e43121995 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. diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 2c65a377296..91304342f8b 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -34,6 +34,7 @@ #include #include #include +#include #define EEEPC_LAPTOP_VERSION "0.1" @@ -506,6 +507,39 @@ static struct attribute_group platform_attribute_group = { .attrs = platform_attributes }; +/* + * 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 int tpd_led_wk; + +static void tpd_led_update(struct work_struct *ignored) +{ + int value = tpd_led_wk; + set_acpi(CM_ASL_TPD, value); +} + +static struct workqueue_struct *led_workqueue; +static DECLARE_WORK(tpd_led_work, tpd_led_update); + +static void tpd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + tpd_led_wk = (value > 0) ? 1 : 0; + queue_work(led_workqueue, &tpd_led_work); +} + +static struct led_classdev tpd_led = { + .name = "eeepc::touchpad", + .brightness_set = tpd_led_set, + .max_brightness = 1 +}; + /* * Hotkey functions */ @@ -1034,6 +1068,14 @@ static void eeepc_hwmon_exit(void) eeepc_hwmon_device = NULL; } +static void eeepc_led_exit(void) +{ + if (led_workqueue) + destroy_workqueue(led_workqueue); + if (tpd_led.dev) + led_classdev_unregister(&tpd_led); +} + static int eeepc_new_rfkill(struct rfkill **rfkill, const char *name, struct device *dev, enum rfkill_type type, int cm) @@ -1190,6 +1232,24 @@ static int eeepc_input_init(struct device *dev) return 0; } +static int eeepc_led_init(struct device *dev) +{ + int rv; + + if (get_acpi(CM_ASL_TPD) == -ENODEV) + return 0; + + rv = led_classdev_register(dev, &tpd_led); + if (rv) + return rv; + + led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!led_workqueue) + return -ENOMEM; + + return 0; +} + static int __devinit eeepc_hotk_add(struct acpi_device *device) { struct device *dev; @@ -1248,6 +1308,10 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) if (result) goto fail_hwmon; + result = eeepc_led_init(dev); + if (result) + goto fail_led; + result = eeepc_rfkill_init(dev); if (result) goto fail_rfkill; @@ -1255,6 +1319,8 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) return 0; fail_rfkill: + eeepc_led_exit(); +fail_led: eeepc_hwmon_exit(); fail_hwmon: eeepc_input_exit(); @@ -1284,6 +1350,7 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) eeepc_rfkill_exit(); eeepc_input_exit(); eeepc_hwmon_exit(); + eeepc_led_exit(); sysfs_remove_group(&platform_device->dev.kobj, &platform_attribute_group); platform_device_unregister(platform_device); From fbe3d8942e8fd1e947e4d11a3e9e15675a1cac7e Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:53 +0000 Subject: [PATCH 026/117] eeepc-laptop: Remove redundant NULL checks The acpi device callbacks add, start, remove, suspend and resume can never be called with a NULL acpi_device. Each callsite in acpi/scan.c has to dereference the device in order to get the ops structure, e.g. struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) return acpi_drv->ops.suspend(acpi_dev, state); Remove all checks for acpi_dev == NULL within these callbacks. Also remove the checks for acpi_driver_data(acpi_dev) == NULL. None of these checks could fail unless the driver does something strange (which none of them do), the acpi core did something terribly wrong, or we have a memory corruption issue. If this does happen then it's best to dereference the pointer and crash noisily. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 91304342f8b..7dde47a6799 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1255,8 +1255,6 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) struct device *dev; int result; - if (!device) - return -EINVAL; pr_notice(EEEPC_HOTK_NAME "\n"); ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); if (!ehotk) @@ -1343,9 +1341,6 @@ fail_platform_driver: static int eeepc_hotk_remove(struct acpi_device *device, int type) { - if (!device || !acpi_driver_data(device)) - return -EINVAL; - eeepc_backlight_exit(); eeepc_rfkill_exit(); eeepc_input_exit(); From 2adb8bd380314feb8170d58b3852cad2c374d20f Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:54 +0000 Subject: [PATCH 027/117] eeepc-laptop: Remove uneccesary acpi_disabled check acpi_bus_register_driver() already checks acpi_disabled, so acpi bus drivers don't need to. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 7dde47a6799..f5efe8da5e7 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1359,8 +1359,6 @@ static int __init eeepc_laptop_init(void) { int result; - if (acpi_disabled) - return -ENODEV; result = acpi_bus_register_driver(&eeepc_hotk_driver); if (result < 0) return result; From eacec3031d1f444a618cf2d023d52f088cf82a7e Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:55 +0000 Subject: [PATCH 028/117] eeepc-laptop: set acpi_driver.owner The owner field provides the link between drivers and modules in sysfs, but no ACPI driver was setting it. After setting the owner field, we can see which module provides which driver and vice versa by looking at /sys/bus/acpi/driver/*/module and /sys/module/*/drivers/acpi:*. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index f5efe8da5e7..1c04c877b82 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -214,6 +214,7 @@ MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); static struct acpi_driver eeepc_hotk_driver = { .name = EEEPC_HOTK_NAME, .class = EEEPC_HOTK_CLASS, + .owner = THIS_MODULE, .ids = eeepc_device_ids, .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { From 487186880d31821eaaba0cc1f27d5a581c56981d Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:56 +0000 Subject: [PATCH 029/117] eeepc-laptop: fix value of pwm1_enable to match documentation Documentation/hwmon/sysfs-interface tells us that automatic fan speed control should be represented by a value of 2 or above for pwm1_enable. Fix eeepc_get_fan_ctrl() to return 2 for automatic fan control. Setting "1" for manual control is already consistent with the documentation, so this remains unchanged. Let's preserve the ABI for this specific driver, so that writing "0" will still invoke automatic control. (The documentation says setting "0" should leave the fan at full speed all the time. This mode is not directly supported by our hardware. Full speed is rather noisy on my 701 and the automatic control has never used it. If you really want this e.g. to prolong the life of an EeePC used as a server, you can always use manual mode. hwmon has always been fairly machine-specific, and you're in a tiny minority (or elite :-). I'm sure you're smart enough to notice that the fan doesn't turn on to full speed when you try this mode, either by ear or checking fan_input1. We could even claim to be honouring the spirit of the documentation. "0" really means "safe mode". EeePCs default to automatic mode, ie that is what Asus will actually test. Since we do not provide any way to tamper with the temperature threshold, automatic mode _is_ the safe option). Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 1c04c877b82..6c982d6c3a5 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -935,7 +935,10 @@ static int eeepc_get_fan_ctrl(void) int value = 0; read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); - return ((value & 0x02 ? 1 : 0)); + if (value & 0x02) + return 1; /* manual */ + else + return 2; /* automatic */ } static void eeepc_set_fan_ctrl(int manual) @@ -943,7 +946,7 @@ static void eeepc_set_fan_ctrl(int manual) int value = 0; read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); - if (manual) + if (manual == 1) value |= 0x02; else value &= ~0x02; From 2b56f1c170fc6338a7d907d6a7132669f9ccdf62 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:57 +0000 Subject: [PATCH 030/117] eeepc-laptop: fix led initialization order Create the workqueue thread used by tpd_led_set() *before* we register the led device. (And vice versa for unregistration). Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6c982d6c3a5..ac45fafbb79 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1074,10 +1074,10 @@ static void eeepc_hwmon_exit(void) static void eeepc_led_exit(void) { - if (led_workqueue) - destroy_workqueue(led_workqueue); if (tpd_led.dev) led_classdev_unregister(&tpd_led); + if (led_workqueue) + destroy_workqueue(led_workqueue); } static int eeepc_new_rfkill(struct rfkill **rfkill, @@ -1243,14 +1243,14 @@ static int eeepc_led_init(struct device *dev) if (get_acpi(CM_ASL_TPD) == -ENODEV) return 0; - rv = led_classdev_register(dev, &tpd_led); - if (rv) - return rv; - led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!led_workqueue) return -ENOMEM; + rv = led_classdev_register(dev, &tpd_led); + if (rv) + return rv; + return 0; } From dc56ad9b49d20e38bb9745bf3beca84291b21a51 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:58 +0000 Subject: [PATCH 031/117] eeepc-laptop: fix potential leak (led_init() failure) If we bail out because we can't create the led class device, we need to ensure the led workqueue is cleaned up. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ac45fafbb79..8b686b563ec 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1248,8 +1248,10 @@ static int eeepc_led_init(struct device *dev) return -ENOMEM; rv = led_classdev_register(dev, &tpd_led); - if (rv) + if (rv) { + destroy_workqueue(led_workqueue); return rv; + } return 0; } From 13f70029daa3cd7f9983e4aec82f32939b1a6e6a Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:44:59 +0000 Subject: [PATCH 032/117] eeepc-laptop: fix set_acpi() to return non-zero on failure If the control method does not exist, return -ENODEV for consistency with get_acpi() Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 8b686b563ec..abd7389a449 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -289,26 +289,30 @@ static int read_acpi_int(acpi_handle handle, const char *method, int *val) static int set_acpi(int cm, int value) { - if (ehotk->cm_supported & (0x1 << cm)) { - const char *method = cm_setv[cm]; - if (method == NULL) - return -ENODEV; - if (write_acpi_int(ehotk->handle, method, value, NULL)) - pr_warning("Error writing %s\n", method); - } + const char *method = cm_setv[cm]; + + if (method == NULL) + return -ENODEV; + if ((ehotk->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; + + if (write_acpi_int(ehotk->handle, method, value, NULL)) + pr_warning("Error writing %s\n", method); return 0; } static int get_acpi(int cm) { - 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); - } + const char *method = cm_getv[cm]; + int value; + + if (method == NULL) + return -ENODEV; + if ((ehotk->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; + + if (read_acpi_int(ehotk->handle, method, &value)) + pr_warning("Error reading %s\n", method); return value; } From a2a1d36c78e90977e4ded9a20a7d8d27d84b13e4 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:00 +0000 Subject: [PATCH 033/117] eeepc-laptop: remove redundant NULL checks eeepc_hotk_notify() cannot be called with ehotk == NULL or bd == NULL. We check both variables for allocation failure and would bail out before the notifier is registered. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 42 +++++++++++++---------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index abd7389a449..f6b7796b24c 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -662,12 +662,11 @@ static int notify_brn(void) { /* 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; + int old = bd->props.brightness; + + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); + + return old; } static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, @@ -741,8 +740,6 @@ static void eeepc_hotk_notify(struct acpi_device *device, u32 event) u16 count; int brn = -ENODEV; - if (!ehotk) - return; if (event > ACPI_MAX_SYS_NOTIFY) return; if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) @@ -753,21 +750,20 @@ static void eeepc_hotk_notify(struct acpi_device *device, u32 event) 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 */ - } + /* 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) { From 951037ea1cf4dc323906fd45d55ff015fd295d0c Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:01 +0000 Subject: [PATCH 034/117] eeepc-laptop: no need to check argument of set_brightness() We already tell the backlight class our maximum brightness value; it will validate the user requested values for us. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index f6b7796b24c..9f33e5178d6 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -326,7 +326,6 @@ static int read_brightness(struct backlight_device *bd) static int set_brightness(struct backlight_device *bd, int value) { - value = max(0, min(15, value)); return set_acpi(CM_ASL_PANELBRIGHT, value); } From 6b188a7b218cb33d918e72f24995341f949297d2 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:02 +0000 Subject: [PATCH 035/117] eeepc-laptop: simplify acpi initialization We don't need to store init_flags after using them. And we don't use the result of INIT, so we don't need to allocate a buffer for it. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 55 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 9f33e5178d6..50ceaaf411c 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -136,7 +136,6 @@ struct eeepc_hotk { 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; @@ -256,8 +255,7 @@ 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; @@ -268,7 +266,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); } @@ -296,7 +294,7 @@ static int set_acpi(int cm, int value) if ((ehotk->cm_supported & (0x1 << cm)) == 0) return -ENODEV; - if (write_acpi_int(ehotk->handle, method, value, NULL)) + if (write_acpi_int(ehotk->handle, method, value)) pr_warning("Error writing %s\n", method); return 0; } @@ -624,36 +622,36 @@ static void cmsg_quirks(void) cmsg_quirk(CM_ASL_TPD, "TPD"); } -static int eeepc_hotk_check(void) +static int eeepc_hotk_init(void) { - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + unsigned int init_flags; int result; 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 { + if (!ehotk->device->status.present) { pr_err("Hotkey device not present, aborting\n"); - return -EINVAL; + return -ENODEV; } + + init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; + pr_notice("Hotkey init flags 0x%x\n", init_flags); + + if (write_acpi_int(ehotk->handle, "INIT", init_flags)) { + pr_err("Hotkey initialization failed\n"); + return -ENODEV; + } + + /* get control methods supported */ + if (read_acpi_int(ehotk->handle, "CMSG", + &ehotk->cm_supported)) { + pr_err("Get control methods supported failed\n"); + return -ENODEV; + } + cmsg_quirks(); + pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported); + return 0; } @@ -1264,14 +1262,13 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) 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 = eeepc_hotk_init(); if (result) goto fail_platform_driver; eeepc_enable_camera(); From 463b4e474ed0905ffc27ee347648739dbfb03acc Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:03 +0000 Subject: [PATCH 036/117] eeepc-laptop: simplify how the hwmon device reads values from the EC The hwmon device uses ec_write() to write values to the EC. So for consistency it should use ec_read() to read values. The extra layers of indirection used did not add any value. This may mean we no longer take the ACPI global lock for such reads (if the EC operation region requires the lock and the EC does not). But there is no point locking each one-byte read individually, when write operations do not use the lock at all. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 43 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 50ceaaf411c..04a59d3bcad 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -118,14 +118,14 @@ static const char *cm_setv[] = { NULL, NULL, "PBPS", "TPDS" }; -#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." +#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 */ -#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 @@ -903,35 +903,34 @@ static int eeepc_hotk_restore(struct device *device) */ 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); + ec_read(EEEPC_EC_FAN_CTRL, &value); if (value & 0x02) return 1; /* manual */ else @@ -940,14 +939,14 @@ static int eeepc_get_fan_ctrl(void) static void eeepc_set_fan_ctrl(int manual) { - int value = 0; + u8 value = 0; - read_acpi_int(NULL, EEEPC_EC_FAN_CTRL, &value); + 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) From bf9598bcd5a73385ced7880ea09998a545e03dd8 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:04 +0000 Subject: [PATCH 037/117] eeepc-laptop: refactor notifications Separate out input_notify(), in a similar way to how notify_brn() is already separated. This will allow all the functions which refer to the input device to be grouped together. This includes a small behaviour change - we now synthesize brightness up/down key events even if the brightness is already at the maximum/minimum value. This is consistent with the new uevent interface. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 71 ++++++++++++++++------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 04a59d3bcad..b4eacb68a19 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -655,9 +655,8 @@ static int eeepc_hotk_init(void) return 0; } -static int notify_brn(void) +static int eeepc_backlight_notify(void) { - /* returns the *previous* brightness, or -1 */ struct backlight_device *bd = eeepc_backlight_device; int old = bd->props.brightness; @@ -731,50 +730,58 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) eeepc_rfkill_hotplug(); } -static void eeepc_hotk_notify(struct acpi_device *device, u32 event) +static void eeepc_input_notify(int event) { static struct key_entry *key; + + 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 void eeepc_hotk_notify(struct acpi_device *device, u32 event) +{ u16 count; - int brn = -ENODEV; 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) { - /* 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; - } + if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { + int old_brightness, new_brightness; + + /* Update backlight device. */ + old_brightness = eeepc_backlight_notify(); + + /* Convert brightness 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(event); } static int eeepc_register_rfkill_notifier(char *node) From 22072e92a038d2ee5848b3e54499ecab730c722c Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:05 +0000 Subject: [PATCH 038/117] eeepc-laptop: move platform driver registration out of eeepc_hotk_add() Strictly speaking we should register the platform driver exactly once, whether there are zero, one, or multiple matching acpi devices. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index b4eacb68a19..3f9b286b854 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1276,13 +1276,10 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) result = eeepc_hotk_init(); if (result) - goto fail_platform_driver; + goto fail_platform_device1; eeepc_enable_camera(); /* 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; @@ -1340,8 +1337,6 @@ fail_sysfs: fail_platform_device2: platform_device_put(platform_device); fail_platform_device1: - platform_driver_unregister(&platform_driver); -fail_platform_driver: kfree(ehotk); return result; @@ -1357,7 +1352,6 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) sysfs_remove_group(&platform_device->dev.kobj, &platform_attribute_group); platform_device_unregister(platform_device); - platform_driver_unregister(&platform_driver); kfree(ehotk); return 0; @@ -1367,19 +1361,30 @@ static int __init eeepc_laptop_init(void) { int result; - result = acpi_bus_register_driver(&eeepc_hotk_driver); + result = platform_driver_register(&platform_driver); if (result < 0) return result; + + result = acpi_bus_register_driver(&eeepc_hotk_driver); + if (result < 0) + goto fail_acpi_driver; if (!ehotk) { - acpi_bus_unregister_driver(&eeepc_hotk_driver); - return -ENODEV; + result = -ENODEV; + goto fail_no_device; } return 0; + +fail_no_device: + acpi_bus_unregister_driver(&eeepc_hotk_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); + platform_driver_unregister(&platform_driver); } module_init(eeepc_laptop_init); From 9db106be554288df5a0a7c56c20257a4391b9738 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:06 +0000 Subject: [PATCH 039/117] eeepc-laptop: move platform device initialisation to a separate function This moves the sysfs_create_group() call just after the declaration of the platform device attributes. It should make it easier to examine the implementation of the platform device attributes in isolation from the rest of the code. (The next commit will apply this pattern to all of the sub-devices as well). Signed-off-by: Alan Jenkins --- drivers/platform/x86/eeepc-laptop.c | 64 ++++++++++++++++++----------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 3f9b286b854..f4f67967aae 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -509,6 +509,38 @@ static struct attribute_group platform_attribute_group = { .attrs = platform_attributes }; +static int eeepc_platform_init(void) +{ + int result; + + platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); + if (!platform_device) + return -ENOMEM; + + result = platform_device_add(platform_device); + if (result) + goto fail_platform_device; + + result = sysfs_create_group(&platform_device->dev.kobj, + &platform_attribute_group); + if (result) + goto fail_sysfs; + return 0; + +fail_sysfs: + platform_device_del(platform_device); +fail_platform_device: + platform_device_put(platform_device); + return result; +} + +static void eeepc_platform_exit(void) +{ + sysfs_remove_group(&platform_device->dev.kobj, + &platform_attribute_group); + platform_device_unregister(platform_device); +} + /* * LEDs */ @@ -1276,22 +1308,12 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) result = eeepc_hotk_init(); if (result) - goto fail_platform_device1; + goto fail_platform; eeepc_enable_camera(); - /* Register platform stuff */ - platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); - if (!platform_device) { - result = -ENOMEM; - goto fail_platform_device1; - } - result = platform_device_add(platform_device); + result = eeepc_platform_init(); if (result) - goto fail_platform_device2; - result = sysfs_create_group(&platform_device->dev.kobj, - &platform_attribute_group); - if (result) - goto fail_sysfs; + goto fail_platform; dev = &platform_device->dev; @@ -1300,8 +1322,7 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) 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); if (result) @@ -1330,13 +1351,8 @@ fail_hwmon: fail_input: eeepc_backlight_exit(); 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: + eeepc_platform_exit(); +fail_platform: kfree(ehotk); return result; @@ -1349,9 +1365,7 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) eeepc_input_exit(); eeepc_hwmon_exit(); eeepc_led_exit(); - sysfs_remove_group(&platform_device->dev.kobj, - &platform_attribute_group); - platform_device_unregister(platform_device); + eeepc_platform_exit(); kfree(ehotk); return 0; From 52bbe3c7b413d656833686f9f08e5dcab3786eeb Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:07 +0000 Subject: [PATCH 040/117] eeepc-laptop: code movement Move e.g. backlight_init() and backlight_exit() together along with the other backlight functions, instead of grouping init() and exit() functions. Move e.g. backlight_ops to follow the functions it refers to, and remove the forward declarations. The code itself should remain unchanged. The eeepc-laptop driver implements a number of interfaces like the backlight class driver. This change makes it easier to examine the implementation of one interface at at a time, without having to search through the file to find init() and exit() functions etc. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 964 ++++++++++++++-------------- 1 file changed, 477 insertions(+), 487 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index f4f67967aae..f457587e64d 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -44,6 +44,9 @@ #define EEEPC_HOTK_DEVICE_NAME "Hotkey" #define EEEPC_HOTK_HID "ASUS010" +MODULE_AUTHOR("Corentin Chary, Eric Cooper"); +MODULE_DESCRIPTION(EEEPC_HOTK_NAME); +MODULE_LICENSE("GPL"); /* * Definitions for Asus EeePC @@ -118,13 +121,33 @@ static const char *cm_setv[] = { NULL, NULL, "PBPS", "TPDS" }; -#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) */ +struct key_entry { + char type; + u8 code; + u16 keycode; +}; -#define EEEPC_EC_SFB0 0xD0 -#define EEEPC_EC_FAN_CTRL (EEEPC_EC_SFB0 + 3) /* Byte containing SF25 */ +enum { KE_KEY, KE_END }; + +static struct key_entry eeepc_keymap[] = { + /* Sleep already handled via generic ACPI code */ + {KE_KEY, 0x10, KEY_WLAN }, + {KE_KEY, 0x11, KEY_WLAN }, + {KE_KEY, 0x12, KEY_PROG1 }, + {KE_KEY, 0x13, KEY_MUTE }, + {KE_KEY, 0x14, KEY_VOLUMEDOWN }, + {KE_KEY, 0x15, KEY_VOLUMEUP }, + {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_MAX, KEY_BRIGHTNESSUP }, + {KE_KEY, 0x30, KEY_SWITCHVIDEOMODE }, + {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE }, + {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE }, + {KE_END, 0}, +}; /* @@ -150,107 +173,15 @@ struct eeepc_hotk { /* 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 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, - } -}; - +/* The platform device */ static struct platform_device *platform_device; -struct key_entry { - char type; - u8 code; - u16 keycode; -}; - -enum { KE_KEY, KE_END }; - -static struct key_entry eeepc_keymap[] = { - /* Sleep already handled via generic ACPI code */ - {KE_KEY, 0x10, KEY_WLAN }, - {KE_KEY, 0x11, KEY_WLAN }, - {KE_KEY, 0x12, KEY_PROG1 }, - {KE_KEY, 0x13, KEY_MUTE }, - {KE_KEY, 0x14, KEY_VOLUMEDOWN }, - {KE_KEY, 0x15, KEY_VOLUMEUP }, - {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, 0x30, KEY_SWITCHVIDEOMODE }, - {KE_KEY, 0x31, KEY_SWITCHVIDEOMODE }, - {KE_KEY, 0x32, KEY_SWITCHVIDEOMODE }, - {KE_END, 0}, -}; - -/* - * The hotkey driver declaration - */ -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, - .owner = THIS_MODULE, - .ids = eeepc_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = eeepc_hotk_add, - .remove = eeepc_hotk_remove, - .notify = eeepc_hotk_notify, - }, -}; - -/* PCI hotplug ops */ -static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value); - -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, -}; - /* The backlight device /sys/class/backlight */ static struct backlight_device *eeepc_backlight_device; /* The hwmon device */ static struct device *eeepc_hwmon_device; -/* - * 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, -}; - -MODULE_AUTHOR("Corentin Chary, Eric Cooper"); -MODULE_DESCRIPTION(EEEPC_HOTK_NAME); -MODULE_LICENSE("GPL"); /* * ACPI Helpers @@ -314,55 +245,6 @@ static int get_acpi(int cm) return value; } -/* - * Backlight - */ -static int read_brightness(struct backlight_device *bd) -{ - return get_acpi(CM_ASL_PANELBRIGHT); -} - -static int set_brightness(struct backlight_device *bd, int value) -{ - return set_acpi(CM_ASL_PANELBRIGHT, value); -} - -static int update_bl_status(struct backlight_device *bd) -{ - return set_brightness(bd, bd->props.brightness); -} - -/* - * Rfkill helpers - */ - -static bool eeepc_wlan_rfkill_blocked(void) -{ - if (get_acpi(CM_ASL_WLAN) == 1) - return false; - return true; -} - -static int eeepc_rfkill_set(void *data, bool blocked) -{ - unsigned long asl = (unsigned long)data; - return set_acpi(asl, !blocked); -} - -static const struct rfkill_ops eeepc_rfkill_ops = { - .set_block = eeepc_rfkill_set, -}; - -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); -} - /* * Sys helpers */ @@ -574,140 +456,43 @@ static struct led_classdev tpd_led = { .max_brightness = 1 }; +static int eeepc_led_init(struct device *dev) +{ + int rv; + + if (get_acpi(CM_ASL_TPD) == -ENODEV) + return 0; + + led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!led_workqueue) + return -ENOMEM; + + rv = led_classdev_register(dev, &tpd_led); + if (rv) { + destroy_workqueue(led_workqueue); + return rv; + } + + return 0; +} + +static void eeepc_led_exit(void) +{ + if (tpd_led.dev) + led_classdev_unregister(&tpd_led); + if (led_workqueue) + destroy_workqueue(led_workqueue); +} + + /* - * Hotkey functions + * PCI hotplug (for wlan rfkill) */ -static struct key_entry *eepc_get_entry_by_scancode(int code) +static bool eeepc_wlan_rfkill_blocked(void) { - struct key_entry *key; - - for (key = eeepc_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *eepc_get_entry_by_keycode(int code) -{ - struct key_entry *key; - - for (key = eeepc_keymap; key->type != KE_END; key++) - if (code == key->keycode && key->type == KE_KEY) - return key; - - return NULL; -} - -static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) -{ - struct key_entry *key = eepc_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) -{ - struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - - 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; -} - -static void cmsg_quirk(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 (!(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; - } -} - -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"); -} - -static int eeepc_hotk_init(void) -{ - unsigned int init_flags; - int result; - - result = acpi_bus_get_status(ehotk->device); - if (result) - return result; - if (!ehotk->device->status.present) { - pr_err("Hotkey device not present, aborting\n"); - return -ENODEV; - } - - init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; - pr_notice("Hotkey init flags 0x%x\n", init_flags); - - if (write_acpi_int(ehotk->handle, "INIT", init_flags)) { - pr_err("Hotkey initialization failed\n"); - return -ENODEV; - } - - /* get control methods supported */ - if (read_acpi_int(ehotk->handle, "CMSG", - &ehotk->cm_supported)) { - pr_err("Get control methods supported failed\n"); - return -ENODEV; - } - cmsg_quirks(); - pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported); - - return 0; -} - -static int eeepc_backlight_notify(void) -{ - struct backlight_device *bd = eeepc_backlight_device; - int old = bd->props.brightness; - - backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); - - return old; -} - -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; + if (get_acpi(CM_ASL_WLAN) == 1) + return false; + return true; } static void eeepc_rfkill_hotplug(void) @@ -762,60 +547,6 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) eeepc_rfkill_hotplug(); } -static void eeepc_input_notify(int event) -{ - static struct key_entry *key; - - 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 void eeepc_hotk_notify(struct acpi_device *device, u32 event) -{ - u16 count; - - if (event > ACPI_MAX_SYS_NOTIFY) - return; - 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 (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { - int old_brightness, new_brightness; - - /* Update backlight device. */ - old_brightness = eeepc_backlight_notify(); - - /* Convert brightness 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(event); -} - static int eeepc_register_rfkill_notifier(char *node) { acpi_status status = AE_OK; @@ -853,12 +584,31 @@ static void eeepc_unregister_rfkill_notifier(char *node) } } +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; +} + static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) { kfree(hotplug_slot->info); kfree(hotplug_slot); } +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(void) { int ret = -ENOMEM; @@ -901,6 +651,140 @@ error_slot: return ret; } +/* + * Rfkill devices + */ +static int eeepc_rfkill_set(void *data, bool blocked) +{ + unsigned long asl = (unsigned long)data; + return set_acpi(asl, !blocked); +} + +static const struct rfkill_ops eeepc_rfkill_ops = { + .set_block = eeepc_rfkill_set, +}; + +static int eeepc_new_rfkill(struct rfkill **rfkill, + const char *name, struct device *dev, + enum rfkill_type type, int cm) +{ + int result; + + result = get_acpi(cm); + if (result < 0) + return result; + + *rfkill = rfkill_alloc(name, dev, type, + &eeepc_rfkill_ops, (void *)(unsigned long)cm); + + if (!*rfkill) + return -EINVAL; + + rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1); + result = rfkill_register(*rfkill); + if (result) { + rfkill_destroy(*rfkill); + *rfkill = NULL; + return result; + } + return 0; +} + +static void eeepc_rfkill_exit(void) +{ + 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; + } +} + +static int eeepc_rfkill_init(struct device *dev) +{ + int result = 0; + + mutex_init(&ehotk->hotplug_lock); + + result = eeepc_new_rfkill(&ehotk->wlan_rfkill, + "eeepc-wlan", dev, + RFKILL_TYPE_WLAN, CM_ASL_WLAN); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill, + "eeepc-bluetooth", dev, + RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill, + "eeepc-wwan3g", dev, + RFKILL_TYPE_WWAN, CM_ASL_3G); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(&ehotk->wimax_rfkill, + "eeepc-wimax", dev, + RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); + + if (result && result != -ENODEV) + goto exit; + + 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; + + 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(); + +exit: + if (result && result != -ENODEV) + eeepc_rfkill_exit(); + return result; +} + +/* + * Platform driver - hibernate/resume callbacks + */ static int eeepc_hotk_thaw(struct device *device) { if (ehotk->wlan_rfkill) { @@ -937,9 +821,31 @@ static int eeepc_hotk_restore(struct device *device) return 0; } +static 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, + } +}; + /* - * 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) { u8 value = 0; @@ -1043,57 +949,6 @@ static struct attribute_group hwmon_attribute_group = { .attrs = hwmon_attributes }; -/* - * exit/init - */ -static void eeepc_backlight_exit(void) -{ - if (eeepc_backlight_device) - backlight_device_unregister(eeepc_backlight_device); - eeepc_backlight_device = NULL; -} - -static void eeepc_rfkill_exit(void) -{ - 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; - } -} - -static void eeepc_input_exit(void) -{ - if (ehotk->inputdev) - input_unregister_device(ehotk->inputdev); -} - static void eeepc_hwmon_exit(void) { struct device *hwmon; @@ -1107,96 +962,56 @@ static void eeepc_hwmon_exit(void) eeepc_hwmon_device = NULL; } -static void eeepc_led_exit(void) -{ - if (tpd_led.dev) - led_classdev_unregister(&tpd_led); - if (led_workqueue) - destroy_workqueue(led_workqueue); -} - -static int eeepc_new_rfkill(struct rfkill **rfkill, - const char *name, struct device *dev, - enum rfkill_type type, int cm) +static int eeepc_hwmon_init(struct device *dev) { + struct device *hwmon; int result; - result = get_acpi(cm); - if (result < 0) - return result; - - *rfkill = rfkill_alloc(name, dev, type, - &eeepc_rfkill_ops, (void *)(unsigned long)cm); - - if (!*rfkill) - return -EINVAL; - - rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1); - result = rfkill_register(*rfkill); - if (result) { - rfkill_destroy(*rfkill); - *rfkill = NULL; - return result; + 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); } - return 0; + eeepc_hwmon_device = hwmon; + result = sysfs_create_group(&hwmon->kobj, + &hwmon_attribute_group); + if (result) + eeepc_hwmon_exit(); + return result; } - -static int eeepc_rfkill_init(struct device *dev) +/* + * Backlight device + */ +static int read_brightness(struct backlight_device *bd) { - int result = 0; + return get_acpi(CM_ASL_PANELBRIGHT); +} - mutex_init(&ehotk->hotplug_lock); +static int set_brightness(struct backlight_device *bd, int value) +{ + return set_acpi(CM_ASL_PANELBRIGHT, value); +} - result = eeepc_new_rfkill(&ehotk->wlan_rfkill, - "eeepc-wlan", dev, - RFKILL_TYPE_WLAN, CM_ASL_WLAN); +static int update_bl_status(struct backlight_device *bd) +{ + return set_brightness(bd, bd->props.brightness); +} - if (result && result != -ENODEV) - goto exit; +static struct backlight_ops eeepcbl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; - result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill, - "eeepc-bluetooth", dev, - RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); +static int eeepc_backlight_notify(void) +{ + struct backlight_device *bd = eeepc_backlight_device; + int old = bd->props.brightness; - if (result && result != -ENODEV) - goto exit; + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); - result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill, - "eeepc-wwan3g", dev, - RFKILL_TYPE_WWAN, CM_ASL_3G); - - if (result && result != -ENODEV) - goto exit; - - result = eeepc_new_rfkill(&ehotk->wimax_rfkill, - "eeepc-wimax", dev, - RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); - - if (result && result != -ENODEV) - goto exit; - - 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; - - 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(); - -exit: - if (result && result != -ENODEV) - eeepc_rfkill_exit(); - return result; + return old; } static int eeepc_backlight_init(struct device *dev) @@ -1218,23 +1033,89 @@ static int eeepc_backlight_init(struct device *dev) return 0; } -static int eeepc_hwmon_init(struct device *dev) +static void eeepc_backlight_exit(void) { - struct device *hwmon; - int result; + if (eeepc_backlight_device) + backlight_device_unregister(eeepc_backlight_device); + eeepc_backlight_device = NULL; +} - 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); + +/* + * Input device (i.e. hotkeys) + */ +static struct key_entry *eeepc_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = eeepc_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static void eeepc_input_notify(int event) +{ + static struct key_entry *key; + + key = eeepc_get_entry_by_scancode(event); + if (key) { + switch (key->type) { + case KE_KEY: + input_report_key(ehotk->inputdev, key->keycode, + 1); + input_sync(ehotk->inputdev); + input_report_key(ehotk->inputdev, key->keycode, + 0); + input_sync(ehotk->inputdev); + break; + } } - eeepc_hwmon_device = hwmon; - result = sysfs_create_group(&hwmon->kobj, - &hwmon_attribute_group); - if (result) - eeepc_hwmon_exit(); - return result; +} + +static struct key_entry *eepc_get_entry_by_keycode(int code) +{ + struct key_entry *key; + + for (key = eeepc_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; +} + +static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct key_entry *key = eeepc_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = eeepc_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; } static int eeepc_input_init(struct device *dev) @@ -1271,26 +1152,114 @@ static int eeepc_input_init(struct device *dev) return 0; } -static int eeepc_led_init(struct device *dev) +static void eeepc_input_exit(void) { - int rv; + if (ehotk->inputdev) + input_unregister_device(ehotk->inputdev); +} - if (get_acpi(CM_ASL_TPD) == -ENODEV) - return 0; +/* + * ACPI driver + */ +static void eeepc_hotk_notify(struct acpi_device *device, u32 event) +{ + u16 count; - led_workqueue = create_singlethread_workqueue("led_workqueue"); - if (!led_workqueue) - return -ENOMEM; + if (event > ACPI_MAX_SYS_NOTIFY) + return; + 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); - rv = led_classdev_register(dev, &tpd_led); - if (rv) { - destroy_workqueue(led_workqueue); - return rv; + if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { + int old_brightness, new_brightness; + + /* Update backlight device. */ + old_brightness = eeepc_backlight_notify(); + + /* Convert brightness 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(event); +} + +static void cmsg_quirk(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 (!(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; + } +} + +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"); +} + +static int eeepc_hotk_init(void) +{ + unsigned int init_flags; + int result; + + result = acpi_bus_get_status(ehotk->device); + if (result) + return result; + if (!ehotk->device->status.present) { + pr_err("Hotkey device not present, aborting\n"); + return -ENODEV; } + init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; + pr_notice("Hotkey init flags 0x%x\n", init_flags); + + if (write_acpi_int(ehotk->handle, "INIT", init_flags)) { + pr_err("Hotkey initialization failed\n"); + return -ENODEV; + } + + /* get control methods supported */ + if (read_acpi_int(ehotk->handle, "CMSG", &ehotk->cm_supported)) { + pr_err("Get control methods supported failed\n"); + return -ENODEV; + } + cmsg_quirks(); + pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported); + return 0; } +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); +} + static int __devinit eeepc_hotk_add(struct acpi_device *device) { struct device *dev; @@ -1371,6 +1340,27 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) return 0; } + +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, + .owner = THIS_MODULE, + .ids = eeepc_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, + .ops = { + .add = eeepc_hotk_add, + .remove = eeepc_hotk_remove, + .notify = eeepc_hotk_notify, + }, +}; + + static int __init eeepc_laptop_init(void) { int result; From a7624b63fdf50d7f460170891a49397280f08758 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:08 +0000 Subject: [PATCH 041/117] eeepc-laptop: revise names eeepc-laptop now does a lot more than just hotkeys. Replace the "hotk" names used throughout the driver with some slightly more appropriate names. The actual strings used in kernel messages and sysfs are left unchanged. e.g. EEEPC_HOTK_FILE -> EEEPC_LAPTOP_FILE EEEPC_HOTK_HID -> EEEPC_ACPI_HID eeepc_hotk_notify -> eeepc_acpi_notify struct eeepc_hotk -> struct eeepc_laptop ehotk -> eeepc I'm about to refactor the entire driver to remove the global "ehotk" variable, and I don't wish to add "struct eeepc_hotk *ehotk" to functions which have nothing to do with hotkeys. Also - fix the name of "eepc_get_entry_by_keycode()" - remove the unused definition of NOTIFY_WLAN_ON. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 253 ++++++++++++++-------------- 1 file changed, 125 insertions(+), 128 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index f457587e64d..b9b5aebbd5b 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 @@ -37,21 +37,20 @@ #include #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_HOTK_NAME); +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 @@ -152,9 +151,8 @@ static struct key_entry eeepc_keymap[] = { /* * This is the main structure, we can use it to store useful information - * about the hotk device */ -struct eeepc_hotk { +struct eeepc_laptop { 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 @@ -171,7 +169,7 @@ struct eeepc_hotk { }; /* The actual device the driver binds to */ -static struct eeepc_hotk *ehotk; +static struct eeepc_laptop *eeepc; /* The platform device */ static struct platform_device *platform_device; @@ -222,10 +220,10 @@ static int set_acpi(int cm, int value) if (method == NULL) return -ENODEV; - if ((ehotk->cm_supported & (0x1 << cm)) == 0) + if ((eeepc->cm_supported & (0x1 << cm)) == 0) return -ENODEV; - if (write_acpi_int(ehotk->handle, method, value)) + if (write_acpi_int(eeepc->handle, method, value)) pr_warning("Error writing %s\n", method); return 0; } @@ -237,10 +235,10 @@ static int get_acpi(int cm) if (method == NULL) return -ENODEV; - if ((ehotk->cm_supported & (0x1 << cm)) == 0) + if ((eeepc->cm_supported & (0x1 << cm)) == 0) return -ENODEV; - if (read_acpi_int(ehotk->handle, method, &value)) + if (read_acpi_int(eeepc->handle, method, &value)) pr_warning("Error reading %s\n", method); return value; } @@ -395,7 +393,7 @@ static int eeepc_platform_init(void) { int result; - platform_device = platform_device_alloc(EEEPC_HOTK_FILE, -1); + platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); if (!platform_device) return -ENOMEM; @@ -501,12 +499,12 @@ static void eeepc_rfkill_hotplug(void) struct pci_bus *bus; bool blocked = eeepc_wlan_rfkill_blocked(); - 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"); @@ -536,7 +534,7 @@ 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) @@ -619,22 +617,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; @@ -643,10 +641,10 @@ 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; } @@ -695,33 +693,33 @@ static void eeepc_rfkill_exit(void) 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; + 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(); - if (ehotk->hotplug_slot) - pci_hp_deregister(ehotk->hotplug_slot); + if (eeepc->hotplug_slot) + pci_hp_deregister(eeepc->hotplug_slot); - if (ehotk->bluetooth_rfkill) { - rfkill_unregister(ehotk->bluetooth_rfkill); - rfkill_destroy(ehotk->bluetooth_rfkill); - ehotk->bluetooth_rfkill = NULL; + if (eeepc->bluetooth_rfkill) { + rfkill_unregister(eeepc->bluetooth_rfkill); + rfkill_destroy(eeepc->bluetooth_rfkill); + eeepc->bluetooth_rfkill = NULL; } - if (ehotk->wwan3g_rfkill) { - rfkill_unregister(ehotk->wwan3g_rfkill); - rfkill_destroy(ehotk->wwan3g_rfkill); - ehotk->wwan3g_rfkill = NULL; + if (eeepc->wwan3g_rfkill) { + rfkill_unregister(eeepc->wwan3g_rfkill); + rfkill_destroy(eeepc->wwan3g_rfkill); + eeepc->wwan3g_rfkill = NULL; } - if (ehotk->wimax_rfkill) { - rfkill_unregister(ehotk->wimax_rfkill); - rfkill_destroy(ehotk->wimax_rfkill); - ehotk->wimax_rfkill = NULL; + if (eeepc->wimax_rfkill) { + rfkill_unregister(eeepc->wimax_rfkill); + rfkill_destroy(eeepc->wimax_rfkill); + eeepc->wimax_rfkill = NULL; } } @@ -729,30 +727,30 @@ static int eeepc_rfkill_init(struct device *dev) { int result = 0; - mutex_init(&ehotk->hotplug_lock); + mutex_init(&eeepc->hotplug_lock); - result = eeepc_new_rfkill(&ehotk->wlan_rfkill, + result = eeepc_new_rfkill(&eeepc->wlan_rfkill, "eeepc-wlan", dev, RFKILL_TYPE_WLAN, CM_ASL_WLAN); if (result && result != -ENODEV) goto exit; - result = eeepc_new_rfkill(&ehotk->bluetooth_rfkill, + result = eeepc_new_rfkill(&eeepc->bluetooth_rfkill, "eeepc-bluetooth", dev, RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); if (result && result != -ENODEV) goto exit; - result = eeepc_new_rfkill(&ehotk->wwan3g_rfkill, + result = eeepc_new_rfkill(&eeepc->wwan3g_rfkill, "eeepc-wwan3g", dev, RFKILL_TYPE_WWAN, CM_ASL_3G); if (result && result != -ENODEV) goto exit; - result = eeepc_new_rfkill(&ehotk->wimax_rfkill, + result = eeepc_new_rfkill(&eeepc->wimax_rfkill, "eeepc-wimax", dev, RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); @@ -785,9 +783,9 @@ exit: /* * Platform driver - hibernate/resume callbacks */ -static int eeepc_hotk_thaw(struct device *device) +static int eeepc_thaw(struct device *device) { - if (ehotk->wlan_rfkill) { + if (eeepc->wlan_rfkill) { bool wlan; /* @@ -802,33 +800,33 @@ static int eeepc_hotk_thaw(struct device *device) return 0; } -static int eeepc_hotk_restore(struct device *device) +static int eeepc_restore(struct device *device) { /* Refresh both wlan rfkill state and pci hotplug */ - if (ehotk->wlan_rfkill) + if (eeepc->wlan_rfkill) eeepc_rfkill_hotplug(); - if (ehotk->bluetooth_rfkill) - rfkill_set_sw_state(ehotk->bluetooth_rfkill, + if (eeepc->bluetooth_rfkill) + rfkill_set_sw_state(eeepc->bluetooth_rfkill, get_acpi(CM_ASL_BLUETOOTH) != 1); - if (ehotk->wwan3g_rfkill) - rfkill_set_sw_state(ehotk->wwan3g_rfkill, + if (eeepc->wwan3g_rfkill) + rfkill_set_sw_state(eeepc->wwan3g_rfkill, get_acpi(CM_ASL_3G) != 1); - if (ehotk->wimax_rfkill) - rfkill_set_sw_state(ehotk->wimax_rfkill, + if (eeepc->wimax_rfkill) + rfkill_set_sw_state(eeepc->wimax_rfkill, get_acpi(CM_ASL_WIMAX) != 1); return 0; } static struct dev_pm_ops eeepc_pm_ops = { - .thaw = eeepc_hotk_thaw, - .restore = eeepc_hotk_restore, + .thaw = eeepc_thaw, + .restore = eeepc_restore, }; static struct platform_driver platform_driver = { .driver = { - .name = EEEPC_HOTK_FILE, + .name = EEEPC_LAPTOP_FILE, .owner = THIS_MODULE, .pm = &eeepc_pm_ops, } @@ -1018,7 +1016,7 @@ static int eeepc_backlight_init(struct device *dev) { struct backlight_device *bd; - bd = backlight_device_register(EEEPC_HOTK_FILE, dev, + bd = backlight_device_register(EEEPC_LAPTOP_FILE, dev, NULL, &eeepcbl_ops); if (IS_ERR(bd)) { pr_err("Could not register eeepc backlight device\n"); @@ -1063,12 +1061,12 @@ static void eeepc_input_notify(int event) if (key) { switch (key->type) { case KE_KEY: - input_report_key(ehotk->inputdev, key->keycode, + input_report_key(eeepc->inputdev, key->keycode, 1); - input_sync(ehotk->inputdev); - input_report_key(ehotk->inputdev, key->keycode, + input_sync(eeepc->inputdev); + input_report_key(eeepc->inputdev, key->keycode, 0); - input_sync(ehotk->inputdev); + input_sync(eeepc->inputdev); break; } } @@ -1123,30 +1121,30 @@ static int eeepc_input_init(struct device *dev) 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 = dev; + eeepc->inputdev->phys = EEEPC_LAPTOP_FILE "/input0"; + eeepc->inputdev->id.bustype = BUS_HOST; + eeepc->inputdev->getkeycode = eeepc_getkeycode; + eeepc->inputdev->setkeycode = eeepc_setkeycode; 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; @@ -1154,23 +1152,23 @@ static int eeepc_input_init(struct device *dev) static void eeepc_input_exit(void) { - if (ehotk->inputdev) - input_unregister_device(ehotk->inputdev); + if (eeepc->inputdev) + input_unregister_device(eeepc->inputdev); } /* * ACPI driver */ -static void eeepc_hotk_notify(struct acpi_device *device, u32 event) +static void eeepc_acpi_notify(struct acpi_device *device, u32 event) { u16 count; if (event > ACPI_MAX_SYS_NOTIFY) return; - 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 = eeepc->event_count[event % 128]++; + acpi_bus_generate_proc_event(eeepc->device, event, count); + acpi_bus_generate_netlink_event(eeepc->device->pnp.device_class, + dev_name(&eeepc->device->dev), event, count); if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { @@ -1202,11 +1200,11 @@ static void cmsg_quirk(int cm, const char *name) /* 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)) { + 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); - ehotk->cm_supported |= 1 << cm; + eeepc->cm_supported |= 1 << cm; } } @@ -1218,15 +1216,15 @@ static void cmsg_quirks(void) cmsg_quirk(CM_ASL_TPD, "TPD"); } -static int eeepc_hotk_init(void) +static int eeepc_acpi_init(void) { unsigned int init_flags; int result; - result = acpi_bus_get_status(ehotk->device); + result = acpi_bus_get_status(eeepc->device); if (result) return result; - if (!ehotk->device->status.present) { + if (!eeepc->device->status.present) { pr_err("Hotkey device not present, aborting\n"); return -ENODEV; } @@ -1234,18 +1232,18 @@ static int eeepc_hotk_init(void) init_flags = DISABLE_ASL_WLAN | DISABLE_ASL_DISPLAYSWITCH; pr_notice("Hotkey init flags 0x%x\n", init_flags); - if (write_acpi_int(ehotk->handle, "INIT", init_flags)) { + if (write_acpi_int(eeepc->handle, "INIT", init_flags)) { pr_err("Hotkey initialization failed\n"); return -ENODEV; } /* get control methods supported */ - if (read_acpi_int(ehotk->handle, "CMSG", &ehotk->cm_supported)) { + if (read_acpi_int(eeepc->handle, "CMSG", &eeepc->cm_supported)) { pr_err("Get control methods supported failed\n"); return -ENODEV; } cmsg_quirks(); - pr_info("Get control methods supported: 0x%x\n", ehotk->cm_supported); + pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); return 0; } @@ -1260,22 +1258,22 @@ static void __devinit eeepc_enable_camera(void) set_acpi(CM_ASL_CAMERA, 1); } -static int __devinit eeepc_hotk_add(struct acpi_device *device) +static int __devinit eeepc_acpi_add(struct acpi_device *device) { struct device *dev; int result; - pr_notice(EEEPC_HOTK_NAME "\n"); - ehotk = kzalloc(sizeof(struct eeepc_hotk), GFP_KERNEL); - if (!ehotk) + pr_notice(EEEPC_LAPTOP_NAME "\n"); + eeepc = kzalloc(sizeof(struct eeepc_laptop), GFP_KERNEL); + if (!eeepc) return -ENOMEM; - 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; + 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; + eeepc->device = device; - result = eeepc_hotk_init(); + result = eeepc_acpi_init(); if (result) goto fail_platform; eeepc_enable_camera(); @@ -1283,7 +1281,6 @@ static int __devinit eeepc_hotk_add(struct acpi_device *device) result = eeepc_platform_init(); if (result) goto fail_platform; - dev = &platform_device->dev; if (!acpi_video_backlight_support()) { @@ -1322,12 +1319,12 @@ fail_input: fail_backlight: eeepc_platform_exit(); fail_platform: - kfree(ehotk); + 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) { eeepc_backlight_exit(); eeepc_rfkill_exit(); @@ -1336,27 +1333,27 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) eeepc_led_exit(); eeepc_platform_exit(); - kfree(ehotk); + kfree(eeepc); return 0; } static const struct acpi_device_id eeepc_device_ids[] = { - {EEEPC_HOTK_HID, 0}, + {EEEPC_ACPI_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, +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_hotk_add, - .remove = eeepc_hotk_remove, - .notify = eeepc_hotk_notify, + .add = eeepc_acpi_add, + .remove = eeepc_acpi_remove, + .notify = eeepc_acpi_notify, }, }; @@ -1369,17 +1366,17 @@ static int __init eeepc_laptop_init(void) if (result < 0) return result; - result = acpi_bus_register_driver(&eeepc_hotk_driver); + result = acpi_bus_register_driver(&eeepc_acpi_driver); if (result < 0) goto fail_acpi_driver; - if (!ehotk) { + if (!eeepc) { result = -ENODEV; goto fail_no_device; } return 0; fail_no_device: - acpi_bus_unregister_driver(&eeepc_hotk_driver); + acpi_bus_unregister_driver(&eeepc_acpi_driver); fail_acpi_driver: platform_driver_unregister(&platform_driver); return result; @@ -1387,7 +1384,7 @@ fail_acpi_driver: 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); } From 854c78363f37f03e30e2856ef17d7eefc62e0d06 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:09 +0000 Subject: [PATCH 042/117] eeepc-laptop: callbacks should use "driver data" parameter or field Callback methods should not refer to a variable like "eeepc" (formally "ehotk"). Instead, they should extract the data they need either from a "driver data" parameter, or the "driver data" field of the object which they operate on. The "eeepc" variable can then be removed. In practice, drivers under "drivers/platform" can get away without using driver data, because it doesn't make sense to have more than one instance of them. However this makes it harder to review them for correctness. This is especially true for core ACPI developers who have not previously been exposed to this anti-pattern :-). This will serve as an example of best practice for new driver writers (whether they find it themselves, or have it pointed out during review :-). The hwmon sub-device is a special case. It uses ec_{read,write} which are defined to communicate with the (first) EC, so it does not require any driver data. It should still only be instantiated in the context of an ASUS010 device because we don't have a safe way to probe for it. Signed-off-by: Alan Jenkins CC: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 466 ++++++++++++++++------------ 1 file changed, 268 insertions(+), 198 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index b9b5aebbd5b..935ec4404f0 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -128,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 }, @@ -153,34 +153,32 @@ static struct key_entry eeepc_keymap[] = { * This is the main structure, we can use it to store useful information */ struct eeepc_laptop { - struct acpi_device *device; /* the device we are in */ - acpi_handle handle; /* the handle of the hotk device */ + 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 */ + + struct platform_device *platform_device; + struct device *hwmon_device; + struct backlight_device *backlight_device; + struct input_dev *inputdev; - u16 *keycode_map; + struct key_entry *keymap; + 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; + + struct led_classdev tpd_led; + int tpd_led_wk; + struct workqueue_struct *led_workqueue; + struct work_struct tpd_led_work; }; -/* The actual device the driver binds to */ -static struct eeepc_laptop *eeepc; - -/* The platform device */ -static struct platform_device *platform_device; - -/* The backlight device /sys/class/backlight */ -static struct backlight_device *eeepc_backlight_device; - -/* The hwmon device */ -static struct device *eeepc_hwmon_device; - - /* * ACPI Helpers */ @@ -214,7 +212,7 @@ 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) { const char *method = cm_setv[cm]; @@ -228,7 +226,7 @@ static int set_acpi(int cm, int value) return 0; } -static int get_acpi(int cm) +static int get_acpi(struct eeepc_laptop *eeepc, int cm) { const char *method = cm_getv[cm]; int value; @@ -243,6 +241,26 @@ static int get_acpi(int cm) return value; } +static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, acpi_handle *handle) +{ + const char *method = cm_setv[cm]; + acpi_status status; + + if (method == NULL) + return -ENODEV; + if ((eeepc->cm_supported & (0x1 << cm)) == 0) + return -ENODEV; + + 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 */ @@ -255,21 +273,24 @@ 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 -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 -EIO; @@ -281,13 +302,13 @@ static ssize_t show_sys_acpi(int cm, char *buf) 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 = { \ @@ -306,9 +327,9 @@ struct eeepc_cpufv { 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) @@ -320,11 +341,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); @@ -336,9 +358,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); } @@ -347,17 +370,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; } @@ -389,36 +413,37 @@ static struct attribute_group platform_attribute_group = { .attrs = platform_attributes }; -static int eeepc_platform_init(void) +static int eeepc_platform_init(struct eeepc_laptop *eeepc) { int result; - platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); - if (!platform_device) + eeepc->platform_device = platform_device_alloc(EEEPC_LAPTOP_FILE, -1); + if (!eeepc->platform_device) return -ENOMEM; + platform_set_drvdata(eeepc->platform_device, eeepc); - result = platform_device_add(platform_device); + result = platform_device_add(eeepc->platform_device); if (result) goto fail_platform_device; - result = sysfs_create_group(&platform_device->dev.kobj, + result = sysfs_create_group(&eeepc->platform_device->dev.kobj, &platform_attribute_group); if (result) goto fail_sysfs; return 0; fail_sysfs: - platform_device_del(platform_device); + platform_device_del(eeepc->platform_device); fail_platform_device: - platform_device_put(platform_device); + platform_device_put(eeepc->platform_device); return result; } -static void eeepc_platform_exit(void) +static void eeepc_platform_exit(struct eeepc_laptop *eeepc) { - sysfs_remove_group(&platform_device->dev.kobj, + sysfs_remove_group(&eeepc->platform_device->dev.kobj, &platform_attribute_group); - platform_device_unregister(platform_device); + platform_device_unregister(eeepc->platform_device); } /* @@ -430,74 +455,76 @@ static void eeepc_platform_exit(void) * subsystem asks, we avoid messing with the Asus ACPI stuff during a * potentially bad time, such as a timer interrupt. */ -static int tpd_led_wk; +static void tpd_led_update(struct work_struct *work) + { + struct eeepc_laptop *eeepc; -static void tpd_led_update(struct work_struct *ignored) -{ - int value = tpd_led_wk; - set_acpi(CM_ASL_TPD, value); + eeepc = container_of(work, struct eeepc_laptop, tpd_led_work); + + set_acpi(eeepc, CM_ASL_TPD, eeepc->tpd_led_wk); } -static struct workqueue_struct *led_workqueue; -static DECLARE_WORK(tpd_led_work, tpd_led_update); - static void tpd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { - tpd_led_wk = (value > 0) ? 1 : 0; - queue_work(led_workqueue, &tpd_led_work); + struct eeepc_laptop *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); + + eeepc->tpd_led_wk = (value > 0) ? 1 : 0; + queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } -static struct led_classdev tpd_led = { - .name = "eeepc::touchpad", - .brightness_set = tpd_led_set, - .max_brightness = 1 -}; - -static int eeepc_led_init(struct device *dev) +static int eeepc_led_init(struct eeepc_laptop *eeepc) { int rv; - if (get_acpi(CM_ASL_TPD) == -ENODEV) + if (get_acpi(eeepc, CM_ASL_TPD) == -ENODEV) return 0; - led_workqueue = create_singlethread_workqueue("led_workqueue"); - if (!led_workqueue) + eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!eeepc->led_workqueue) return -ENOMEM; + INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); - rv = led_classdev_register(dev, &tpd_led); + eeepc->tpd_led.name = "eeepc::touchpad"; + eeepc->tpd_led.brightness_set = tpd_led_set; + eeepc->tpd_led.max_brightness = 1; + + rv = led_classdev_register(&eeepc->platform_device->dev, + &eeepc->tpd_led); if (rv) { - destroy_workqueue(led_workqueue); + destroy_workqueue(eeepc->led_workqueue); return rv; } return 0; } -static void eeepc_led_exit(void) +static void eeepc_led_exit(struct eeepc_laptop *eeepc) { - if (tpd_led.dev) - led_classdev_unregister(&tpd_led); - if (led_workqueue) - destroy_workqueue(led_workqueue); + if (eeepc->tpd_led.dev) + led_classdev_unregister(&eeepc->tpd_led); + if (eeepc->led_workqueue) + destroy_workqueue(eeepc->led_workqueue); } /* * PCI hotplug (for wlan rfkill) */ -static bool eeepc_wlan_rfkill_blocked(void) +static bool eeepc_wlan_rfkill_blocked(struct eeepc_laptop *eeepc) { - if (get_acpi(CM_ASL_WLAN) == 1) + 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 (eeepc->wlan_rfkill) rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); @@ -539,15 +566,18 @@ out_unlock: 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 int eeepc_register_rfkill_notifier(char *node) +static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, + char *node) { - acpi_status status = AE_OK; + acpi_status status; acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); @@ -556,7 +586,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 @@ -565,7 +595,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; @@ -585,7 +616,8 @@ static void eeepc_unregister_rfkill_notifier(char *node) static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - int val = get_acpi(CM_ASL_WLAN); + struct eeepc_laptop *eeepc = hotplug_slot->private; + int val = get_acpi(eeepc, CM_ASL_WLAN); if (val == 1 || val == 0) *value = val; @@ -607,7 +639,7 @@ static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { .get_power_status = eeepc_get_adapter_status, }; -static int eeepc_setup_pci_hotplug(void) +static int eeepc_setup_pci_hotplug(struct eeepc_laptop *eeepc) { int ret = -ENOMEM; struct pci_bus *bus = pci_find_bus(0, 1); @@ -654,31 +686,34 @@ error_slot: */ static int eeepc_rfkill_set(void *data, bool blocked) { - unsigned long asl = (unsigned long)data; - return set_acpi(asl, !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 rfkill **rfkill, - const char *name, struct device *dev, +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 = get_acpi(cm); + result = acpi_setter_handle(eeepc, cm, &handle); if (result < 0) return result; - *rfkill = rfkill_alloc(name, dev, type, - &eeepc_rfkill_ops, (void *)(unsigned long)cm); + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, handle); if (!*rfkill) return -EINVAL; - rfkill_init_sw_state(*rfkill, get_acpi(cm) != 1); + rfkill_init_sw_state(*rfkill, get_acpi(eeepc, cm) != 1); result = rfkill_register(*rfkill); if (result) { rfkill_destroy(*rfkill); @@ -688,11 +723,11 @@ static int eeepc_new_rfkill(struct rfkill **rfkill, return 0; } -static void eeepc_rfkill_exit(void) +static void eeepc_rfkill_exit(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"); + 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); @@ -702,7 +737,7 @@ static void eeepc_rfkill_exit(void) * Refresh pci hotplug in case the rfkill state was changed after * eeepc_unregister_rfkill_notifier() */ - eeepc_rfkill_hotplug(); + eeepc_rfkill_hotplug(eeepc); if (eeepc->hotplug_slot) pci_hp_deregister(eeepc->hotplug_slot); @@ -723,41 +758,41 @@ static void eeepc_rfkill_exit(void) } } -static int eeepc_rfkill_init(struct device *dev) +static int eeepc_rfkill_init(struct eeepc_laptop *eeepc) { int result = 0; mutex_init(&eeepc->hotplug_lock); - result = eeepc_new_rfkill(&eeepc->wlan_rfkill, - "eeepc-wlan", dev, - RFKILL_TYPE_WLAN, CM_ASL_WLAN); + 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->bluetooth_rfkill, - "eeepc-bluetooth", dev, - RFKILL_TYPE_BLUETOOTH, CM_ASL_BLUETOOTH); + 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->wwan3g_rfkill, - "eeepc-wwan3g", dev, - RFKILL_TYPE_WWAN, CM_ASL_3G); + 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->wimax_rfkill, - "eeepc-wimax", dev, - RFKILL_TYPE_WIMAX, CM_ASL_WIMAX); + 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(); + result = eeepc_setup_pci_hotplug(eeepc); /* * If we get -EBUSY then something else is handling the PCI hotplug - * don't fail in this case @@ -765,26 +800,28 @@ static int eeepc_rfkill_init(struct device *dev) if (result == -EBUSY) result = 0; - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P5"); - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); - eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); + 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_rfkill_hotplug(eeepc); exit: if (result && result != -ENODEV) - eeepc_rfkill_exit(); + eeepc_rfkill_exit(eeepc); return result; } /* * Platform driver - hibernate/resume callbacks */ -static int eeepc_thaw(struct device *device) +static int eeepc_hotk_thaw(struct device *device) { + struct eeepc_laptop *eeepc = dev_get_drvdata(device); + if (eeepc->wlan_rfkill) { bool wlan; @@ -793,35 +830,37 @@ static int eeepc_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; } -static int eeepc_restore(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 (eeepc->wlan_rfkill) - eeepc_rfkill_hotplug(); + eeepc_rfkill_hotplug(eeepc); if (eeepc->bluetooth_rfkill) rfkill_set_sw_state(eeepc->bluetooth_rfkill, - get_acpi(CM_ASL_BLUETOOTH) != 1); + get_acpi(eeepc, CM_ASL_BLUETOOTH) != 1); if (eeepc->wwan3g_rfkill) rfkill_set_sw_state(eeepc->wwan3g_rfkill, - get_acpi(CM_ASL_3G) != 1); + get_acpi(eeepc, CM_ASL_3G) != 1); if (eeepc->wimax_rfkill) rfkill_set_sw_state(eeepc->wimax_rfkill, - get_acpi(CM_ASL_WIMAX) != 1); + get_acpi(eeepc, CM_ASL_WIMAX) != 1); return 0; } static struct dev_pm_ops eeepc_pm_ops = { - .thaw = eeepc_thaw, - .restore = eeepc_restore, + .thaw = eeepc_hotk_thaw, + .restore = eeepc_hotk_restore, }; static struct platform_driver platform_driver = { @@ -947,35 +986,35 @@ static struct attribute_group hwmon_attribute_group = { .attrs = hwmon_attributes }; -static void eeepc_hwmon_exit(void) +static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc) { struct device *hwmon; - hwmon = eeepc_hwmon_device; + hwmon = eeepc->hwmon_device; if (!hwmon) - return ; + return; sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group); hwmon_device_unregister(hwmon); - eeepc_hwmon_device = NULL; + eeepc->hwmon_device = NULL; } -static int eeepc_hwmon_init(struct device *dev) +static int eeepc_hwmon_init(struct eeepc_laptop *eeepc) { struct device *hwmon; int result; - hwmon = hwmon_device_register(dev); + 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; + eeepc->hwmon_device = NULL; return PTR_ERR(hwmon); } - eeepc_hwmon_device = hwmon; + eeepc->hwmon_device = hwmon; result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group); if (result) - eeepc_hwmon_exit(); + eeepc_hwmon_exit(eeepc); return result; } @@ -984,12 +1023,16 @@ static int eeepc_hwmon_init(struct device *dev) */ static int read_brightness(struct backlight_device *bd) { - return get_acpi(CM_ASL_PANELBRIGHT); + struct eeepc_laptop *eeepc = bl_get_data(bd); + + return get_acpi(eeepc, CM_ASL_PANELBRIGHT); } static int set_brightness(struct backlight_device *bd, int value) { - return set_acpi(CM_ASL_PANELBRIGHT, value); + struct eeepc_laptop *eeepc = bl_get_data(bd); + + return set_acpi(eeepc, CM_ASL_PANELBRIGHT, value); } static int update_bl_status(struct backlight_device *bd) @@ -1002,9 +1045,9 @@ static struct backlight_ops eeepcbl_ops = { .update_status = update_bl_status, }; -static int eeepc_backlight_notify(void) +static int eeepc_backlight_notify(struct eeepc_laptop *eeepc) { - struct backlight_device *bd = eeepc_backlight_device; + struct backlight_device *bd = eeepc->backlight_device; int old = bd->props.brightness; backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); @@ -1012,52 +1055,55 @@ static int eeepc_backlight_notify(void) return old; } -static int eeepc_backlight_init(struct device *dev) +static int eeepc_backlight_init(struct eeepc_laptop *eeepc) { struct backlight_device *bd; - bd = backlight_device_register(EEEPC_LAPTOP_FILE, dev, - NULL, &eeepcbl_ops); + 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; + eeepc->backlight_device = NULL; return PTR_ERR(bd); } - eeepc_backlight_device = bd; + eeepc->backlight_device = bd; bd->props.max_brightness = 15; - bd->props.brightness = read_brightness(NULL); + bd->props.brightness = read_brightness(bd); bd->props.power = FB_BLANK_UNBLANK; backlight_update_status(bd); return 0; } -static void eeepc_backlight_exit(void) +static void eeepc_backlight_exit(struct eeepc_laptop *eeepc) { - if (eeepc_backlight_device) - backlight_device_unregister(eeepc_backlight_device); - eeepc_backlight_device = NULL; + if (eeepc->backlight_device) + backlight_device_unregister(eeepc->backlight_device); + eeepc->backlight_device = NULL; } /* * Input device (i.e. hotkeys) */ -static struct key_entry *eeepc_get_entry_by_scancode(int code) +static struct key_entry *eeepc_get_entry_by_scancode( + struct eeepc_laptop *eeepc, + int code) { struct key_entry *key; - for (key = eeepc_keymap; key->type != KE_END; key++) + for (key = eeepc->keymap; key->type != KE_END; key++) if (code == key->code) return key; return NULL; } -static void eeepc_input_notify(int event) +static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) { static struct key_entry *key; - key = eeepc_get_entry_by_scancode(event); + key = eeepc_get_entry_by_scancode(eeepc, event); if (key) { switch (key->type) { case KE_KEY: @@ -1072,11 +1118,12 @@ static void eeepc_input_notify(int event) } } -static struct key_entry *eepc_get_entry_by_keycode(int code) +static struct key_entry *eeepc_get_entry_by_keycode( + struct eeepc_laptop *eeepc, int code) { struct key_entry *key; - for (key = eeepc_keymap; key->type != KE_END; key++) + for (key = eeepc->keymap; key->type != KE_END; key++) if (code == key->keycode && key->type == KE_KEY) return key; @@ -1085,7 +1132,8 @@ static struct key_entry *eepc_get_entry_by_keycode(int code) static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) { - struct key_entry *key = eeepc_get_entry_by_scancode(scancode); + struct eeepc_laptop *eeepc = input_get_drvdata(dev); + struct key_entry *key = eeepc_get_entry_by_scancode(eeepc, scancode); if (key && key->type == KE_KEY) { *keycode = key->keycode; @@ -1097,18 +1145,19 @@ static int eeepc_getkeycode(struct input_dev *dev, int scancode, int *keycode) static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) { + struct eeepc_laptop *eeepc = input_get_drvdata(dev); struct key_entry *key; int old_keycode; if (keycode < 0 || keycode > KEY_MAX) return -EINVAL; - key = eeepc_get_entry_by_scancode(scancode); + 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 (!eepc_get_entry_by_keycode(old_keycode)) + if (!eeepc_get_entry_by_keycode(eeepc, old_keycode)) clear_bit(old_keycode, dev->keybit); return 0; } @@ -1116,7 +1165,7 @@ static int eeepc_setkeycode(struct input_dev *dev, int scancode, int keycode) 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; @@ -1127,12 +1176,15 @@ static int eeepc_input_init(struct device *dev) return -ENOMEM; } eeepc->inputdev->name = "Asus EeePC extra buttons"; - eeepc->inputdev->dev.parent = dev; + 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: @@ -1150,10 +1202,12 @@ static int eeepc_input_init(struct device *dev) return 0; } -static void eeepc_input_exit(void) +static void eeepc_input_exit(struct eeepc_laptop *eeepc) { - if (eeepc->inputdev) + if (eeepc->inputdev) { input_unregister_device(eeepc->inputdev); + kfree(eeepc->keymap); + } } /* @@ -1161,21 +1215,22 @@ static void eeepc_input_exit(void) */ 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(eeepc->device, event, count); - acpi_bus_generate_netlink_event(eeepc->device->pnp.device_class, - dev_name(&eeepc->device->dev), event, + acpi_bus_generate_proc_event(device, event, count); + acpi_bus_generate_netlink_event(device->pnp.device_class, + dev_name(&device->dev), event, count); if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { int old_brightness, new_brightness; /* Update backlight device. */ - old_brightness = eeepc_backlight_notify(); + old_brightness = eeepc_backlight_notify(eeepc); /* Convert brightness event to keypress (obsolescent hack). */ new_brightness = event - NOTIFY_BRN_MIN; @@ -1191,10 +1246,10 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) */ } } - eeepc_input_notify(event); + eeepc_input_notify(eeepc, event); } -static void cmsg_quirk(int cm, const char *name) +static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) { int dummy; @@ -1208,23 +1263,23 @@ static void cmsg_quirk(int cm, const char *name) } } -static void cmsg_quirks(void) +static void cmsg_quirks(struct eeepc_laptop *eeepc) { - cmsg_quirk(CM_ASL_LID, "LID"); - cmsg_quirk(CM_ASL_TYPE, "TYPE"); - cmsg_quirk(CM_ASL_PANELPOWER, "PANELPOWER"); - cmsg_quirk(CM_ASL_TPD, "TPD"); + 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(void) +static int eeepc_acpi_init(struct eeepc_laptop *eeepc, struct acpi_device *device) { unsigned int init_flags; int result; - result = acpi_bus_get_status(eeepc->device); + result = acpi_bus_get_status(device); if (result) return result; - if (!eeepc->device->status.present) { + if (!device->status.present) { pr_err("Hotkey device not present, aborting\n"); return -ENODEV; } @@ -1242,25 +1297,27 @@ static int eeepc_acpi_init(void) pr_err("Get control methods supported failed\n"); return -ENODEV; } - cmsg_quirks(); + cmsg_quirks(eeepc); pr_info("Get control methods supported: 0x%x\n", eeepc->cm_supported); return 0; } -static void __devinit eeepc_enable_camera(void) +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(CM_ASL_CAMERA) == 0) - set_acpi(CM_ASL_CAMERA, 1); + 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 device *dev; + struct eeepc_laptop *eeepc; int result; pr_notice(EEEPC_LAPTOP_NAME "\n"); @@ -1271,53 +1328,64 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device) strcpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strcpy(acpi_device_class(device), EEEPC_ACPI_CLASS); device->driver_data = eeepc; - eeepc->device = device; - result = eeepc_acpi_init(); + result = eeepc_acpi_init(eeepc, device); if (result) goto fail_platform; - eeepc_enable_camera(); + eeepc_enable_camera(eeepc); - result = eeepc_platform_init(); + /* + * 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; - dev = &platform_device->dev; 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"); - 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_led_init(dev); + result = eeepc_led_init(eeepc); if (result) goto fail_led; - result = eeepc_rfkill_init(dev); + result = eeepc_rfkill_init(eeepc); if (result) goto fail_rfkill; + eeepc_device_present = true; return 0; fail_rfkill: - eeepc_led_exit(); + eeepc_led_exit(eeepc); fail_led: - eeepc_hwmon_exit(); + eeepc_hwmon_exit(eeepc); fail_hwmon: - eeepc_input_exit(); + eeepc_input_exit(eeepc); fail_input: - eeepc_backlight_exit(); + eeepc_backlight_exit(eeepc); fail_backlight: - eeepc_platform_exit(); + eeepc_platform_exit(eeepc); fail_platform: kfree(eeepc); @@ -1326,12 +1394,14 @@ fail_platform: static int eeepc_acpi_remove(struct acpi_device *device, int type) { - eeepc_backlight_exit(); - eeepc_rfkill_exit(); - eeepc_input_exit(); - eeepc_hwmon_exit(); - eeepc_led_exit(); - eeepc_platform_exit(); + struct eeepc_laptop *eeepc = acpi_driver_data(device); + + 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(eeepc); return 0; @@ -1369,7 +1439,7 @@ static int __init eeepc_laptop_init(void) result = acpi_bus_register_driver(&eeepc_acpi_driver); if (result < 0) goto fail_acpi_driver; - if (!eeepc) { + if (!eeepc_device_present) { result = -ENODEV; goto fail_no_device; } From 16851f92a5998bf8880a7401898ecfe351913854 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:45:10 +0000 Subject: [PATCH 043/117] asus-laptop: use KEY_F13 to map "Disable Touchpad" event The same key is used in toshiba-laptop, and there is no reserved key for that. Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 6c05172bf3d..e931e800423 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -313,7 +313,7 @@ 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, 0x6B, KEY_F13}, /* Lock Touchpad */ {KE_KEY, 0x82, KEY_CAMERA}, {KE_KEY, 0x8A, KEY_PROG1}, {KE_KEY, 0x95, KEY_MEDIA}, From 14f8af311e7d3e4198cbaade84a34f86505dcb37 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Thu, 3 Dec 2009 07:45:11 +0000 Subject: [PATCH 044/117] asus-laptop: add Lenovo SL hotkey support Lenovo SL series laptop has a very similar DSDT with Asus laptops. We can easily have the extra ACPI function support with little modification in asus-laptop.c Here is the hotkey enablement for Lenovo SL series laptop. This patch will enable the following hotkey: - Volumn Up - Volumn Down - Mute - Screen Lock (Fn+F2) - Battery Status (Fn+F3) - WLAN switch (Fn+F5) - Video output switch (Fn+F7) - Touchpad switch (Fn+F8) - Screen Magnifier (Fn+Space) The following function of Lenovo SL laptop is still need to be enabled: - Hotkey: KEY_SUSPEND (Fn+F4), KEY_SLEEP (Fn+F12), Dock Eject (Fn+F9) - Rfkill for bluetooth and wlan - LenovoCare LED - Hwmon for fan speed - Fingerprint scanner - Active Protection System Signed-off-by: Ike Panhc Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index e931e800423..83948dff241 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); @@ -294,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}, @@ -313,6 +319,8 @@ static struct key_entry asus_keymap[] = { {KE_KEY, 0x5F, KEY_WLAN}, {KE_KEY, 0x60, KEY_SWITCHVIDEOMODE}, {KE_KEY, 0x61, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x62, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x63, KEY_SWITCHVIDEOMODE}, {KE_KEY, 0x6B, KEY_F13}, /* Lock Touchpad */ {KE_KEY, 0x82, KEY_CAMERA}, {KE_KEY, 0x8A, KEY_PROG1}, From 1f0233eee9b385d78fda78626136c43e045082f7 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:45:12 +0000 Subject: [PATCH 045/117] asus-laptop: Add wlan switch found on V6V Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 83948dff241..3348cc6731d 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -323,6 +323,7 @@ static struct key_entry asus_keymap[] = { {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}, From b39b85e74acfd62a22afc33a88a7bda36beb3367 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:45:13 +0000 Subject: [PATCH 046/117] eeepc-laptop: map keys found on newer eeepc Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 935ec4404f0..920d9d9f1f9 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -136,6 +136,7 @@ static const 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 }, @@ -145,6 +146,8 @@ static const struct key_entry eeepc_keymap[] = { {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}, }; From f90be874303eddc53c199083a37bc44d65ab8351 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:45:14 +0000 Subject: [PATCH 047/117] eeepc-laptop: fix coding style fix styles problems introduced by commit e86bda235a08b6a8e64c1e8bb9d175f6961554e3 Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 920d9d9f1f9..5f0eb767e8a 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -244,7 +244,8 @@ static int get_acpi(struct eeepc_laptop *eeepc, int cm) return value; } -static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, acpi_handle *handle) +static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, + acpi_handle *handle) { const char *method = cm_setv[cm]; acpi_status status; @@ -255,7 +256,7 @@ static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, acpi_handle *h return -ENODEV; status = acpi_get_handle(eeepc->handle, (char *)method, - handle); + handle); if (status != AE_OK) { pr_warning("Error finding %s\n", method); return -ENODEV; @@ -1274,7 +1275,8 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc) cmsg_quirk(eeepc, CM_ASL_TPD, "TPD"); } -static int eeepc_acpi_init(struct eeepc_laptop *eeepc, struct acpi_device *device) +static int eeepc_acpi_init(struct eeepc_laptop *eeepc, + struct acpi_device *device) { unsigned int init_flags; int result; From 325fb8e9aeddf7bf8a7a892869dca00e7305c41e Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Thu, 3 Dec 2009 07:45:15 +0000 Subject: [PATCH 048/117] eeepc-laptop: re-add check for eeepc->backlight == NULL As Corentin points out, we do not create a backlight device if the ACPI video driver is able to provide equivalent functionality. So we do need to check before we try to update the backlight device. We now ignore brightness events completely if we have not created a backlight device. This is slightly more cautious than the original check. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/platform/x86/eeepc-laptop.c | 38 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 5f0eb767e8a..d07a4c0ec7e 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1230,27 +1230,35 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) dev_name(&device->dev), event, count); + /* Brightness events are special */ if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) { - int old_brightness, new_brightness; - /* Update backlight device. */ - old_brightness = eeepc_backlight_notify(eeepc); + /* Ignore them completely if the acpi video driver is used */ + if (eeepc->backlight_device != NULL) { + int old_brightness, new_brightness; - /* Convert brightness event to keypress (obsolescent hack). */ - new_brightness = event - NOTIFY_BRN_MIN; + /* Update the backlight device. */ + old_brightness = eeepc_backlight_notify(eeepc); - 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). - */ + /* 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); } - eeepc_input_notify(eeepc, event); } static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) From 728900f6fa7142e07a67d10d862bcb774d7a3493 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 3 Dec 2009 07:45:17 +0000 Subject: [PATCH 049/117] asus-laptop: schedule display_get and lcd_switch for removal Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- Documentation/feature-removal-schedule.txt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index bc693fffabe..d1c9e1763cf 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -489,3 +489,23 @@ Why: With the recent innovations in CPU hardware acceleration technologies Who: Alok N Kataria ---------------------------- + +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 + +---------------------------- From e5fbba85a7acc2626d4fe14501816811d702f3e9 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Tue, 21 Jul 2009 12:14:01 +0100 Subject: [PATCH 050/117] hp-wmi: improve rfkill support 1) Add support for reading the hardware blocked state. Previously we read a combination of the hardware and software blocked states, reporting it as the software blocked state. This caused some confusing behaviour. 2) The software state is persistent, mark it as such. 3) Check rfkill in the resume handler. Both the hard and soft blocked states may change over hibernation. Signed-off-by: Alan Jenkins Acked-by: Matthew Garrett Tested-by: Maciej Rutecki Signed-off-by: Len Brown --- drivers/platform/x86/hp-wmi.c | 141 ++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index c2842171cec..804cfdb8828 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) +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 & 0x100) + if (wireless & mask) return false; else return true; } -static bool hp_wmi_bluetooth_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 & 0x10000) - return false; - else - return true; -} - -static bool hp_wmi_wwan_state(void) -{ - int wireless = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, 0); - - 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()); - 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); - } else + 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); + 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 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; } From e1fbf346c7c56d6b2f9d835d297bcb088baaff3a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 31 Jul 2009 03:25:38 +0100 Subject: [PATCH 051/117] dell-laptop: Fix rfkill state queries The current code in dell-laptop is confused about the hardware rfkill state. Fix it up such that it's always reported correctly. Signed-off-by: Matthew Garrett Tested-by: Tim Gardner Signed-off-by: Len Brown --- drivers/platform/x86/dell-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 74909c4aaee..906111152b0 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -197,8 +197,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 = { From 71e9dc73cb6b1351bdd5f732cef5dbef411b3219 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Wed, 19 Aug 2009 15:06:47 +0100 Subject: [PATCH 052/117] dell-laptop: fix a use-after-free error on the failure path dell_setup_rfkill() already cleans up the rfkill devices on failure. So if it returns an error, we should not try to unregister the rfkill devices. Signed-off-by: Alan Jenkins Acked-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/platform/x86/dell-laptop.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 906111152b0..d791ef93c1f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -330,7 +330,7 @@ static int __init dell_init(void) if (ret) { printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n"); - goto out; + goto fail_rfkill; } #ifdef CONFIG_ACPI @@ -358,7 +358,7 @@ static int __init dell_init(void) 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 +368,15 @@ static int __init dell_init(void) } return 0; -out: + +fail_backlight: if (wifi_rfkill) rfkill_unregister(wifi_rfkill); if (bluetooth_rfkill) rfkill_unregister(bluetooth_rfkill); if (wwan_rfkill) rfkill_unregister(wwan_rfkill); +fail_rfkill: kfree(da_tokens); return ret; } From 4311bb230e0f7e4daa5fd5bc0cc536e2bd1eff20 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Wed, 19 Aug 2009 15:06:48 +0100 Subject: [PATCH 053/117] dell-laptop: fix rfkill memory leak on unload and failure paths rfkill_unregister() should always be followed by rfkill_destroy(). Signed-off-by: Alan Jenkins Acked-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/platform/x86/dell-laptop.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d791ef93c1f..94139d530c7 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -268,6 +268,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; @@ -370,12 +386,7 @@ static int __init dell_init(void) return 0; fail_backlight: - if (wifi_rfkill) - rfkill_unregister(wifi_rfkill); - if (bluetooth_rfkill) - rfkill_unregister(bluetooth_rfkill); - if (wwan_rfkill) - rfkill_unregister(wwan_rfkill); + dell_cleanup_rfkill(); fail_rfkill: kfree(da_tokens); return ret; @@ -384,12 +395,7 @@ fail_rfkill: 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); From ada3248a5d38654b33b0ae2eabe1d7e3d9a9ffce Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Wed, 19 Aug 2009 15:06:49 +0100 Subject: [PATCH 054/117] dell-laptop: create a platform device as a parent for the rfkill devices etc. dell-laptop may not need to export any sysfs files, but it should still create a platform device as a parent for the rfkill and backlight devices. Otherwise sysfs will display these as "virtual" devices, with no connection to either physical hardware or the dell-laptop module. Apparently this is useful for hardware detection. Signed-off-by: Alan Jenkins Acked-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/platform/x86/dell-laptop.c | 38 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 94139d530c7..a4ce64244f6 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; @@ -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; @@ -342,6 +354,18 @@ 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) { @@ -368,7 +392,7 @@ 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)) { @@ -388,6 +412,12 @@ static int __init dell_init(void) 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; } From 4788df4cf44fd14847d37f88daea649f01c01017 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Wed, 19 Aug 2009 15:06:50 +0100 Subject: [PATCH 055/117] dell-laptop: add __init to init functions Signed-off-by: Alan Jenkins Acked-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/platform/x86/dell-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index a4ce64244f6..3780994dc8f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -82,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; @@ -111,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 */ @@ -214,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; From 8c5d30e590593495c5bb8bd4a2519ce1ac909a22 Mon Sep 17 00:00:00 2001 From: Alan Jenkins Date: Wed, 19 Aug 2009 15:44:10 +0100 Subject: [PATCH 056/117] dell-laptop: remove duplicate Kconfig entry under drivers/misc This showed up as an unselectable option when using xconfig and searching for "dell". It must been overlooked when dell-laptop was moved to drivers/platform/x86. Signed-off-by: Alan Jenkins Signed-off-by: Len Brown --- drivers/misc/Kconfig | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index df1f86b5c83..ba137c8b263 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -210,19 +210,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 From 5ea2559726b786283236835dc2905c23b36ac91c Mon Sep 17 00:00:00 2001 From: Rezwanul Kabir Date: Mon, 2 Nov 2009 12:00:42 -0500 Subject: [PATCH 057/117] dell-wmi: Add support for new Dell systems Newer Dell systems support HotKey features differently from legacy systems. A new vendor specifc HotKey SMBIOS table (Type 0xB2) is defined. This table contains a mapping between scancode and the corresponding predefined keyfunction ( i.e. keycode).. Also, a new ACPI-WMI event type (called KeyIDList) with a value of 0x0010 is defined. Any BIOS containing 0xB2 table will send hotkey notifications using KeyIDList event. This is Rezwanul's patch, updated to ensure that brightness events are not sent if the backlight is controlled via ACPI and with the default keycode for the display output switching altered to match desktop expectations. Signed-off-by: Rezwanul Kabir Signed-off-by: Matthew Garrett Signed-off-by: Len Brown --- drivers/platform/x86/dell-wmi.c | 129 ++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 0f900cc9fa7..67f3fe71c50 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -31,6 +31,7 @@ #include #include #include +#include MODULE_AUTHOR("Matthew Garrett "); 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"); From c4da6940a7a41c72781ff2d62ebd4b99f3749f14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 17 Nov 2009 17:05:14 -0700 Subject: [PATCH 058/117] PNPACPI: save struct acpi_device, not just acpi_handle Some drivers need to look at things in the acpi_device structure besides the handle. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/pnp/pnpacpi/core.c | 17 ++++++++++++----- drivers/pnp/pnpacpi/rsparser.c | 9 ++++++--- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 83b8b5ac49c..b2348fc2378 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,12 +132,15 @@ 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 @@ -168,7 +175,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 ef3a2cd3a7a..5702b2c8691 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; From a1a541d86f50a9957beeedb122a035870d602647 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 2 Dec 2009 13:31:00 +0800 Subject: [PATCH 059/117] ACPI: support customizing ACPI control methods at runtime Introduce a new debugfs I/F (/sys/kernel/debug/acpi/custom_method) for ACPI, which can be used to customize the ACPI control methods at runtime. We can use this to debug the AML code level bugs instead of overriding the whole DSDT table, without rebuilding/rebooting kernel any more. Detailed description about how to use this debugfs I/F is stated in Documentation/acpi/method-customizing.txt Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- Documentation/acpi/method-customizing.txt | 66 ++++++++++++++++++ drivers/acpi/debug.c | 83 ++++++++++++++++++++++- 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 Documentation/acpi/method-customizing.txt diff --git a/Documentation/acpi/method-customizing.txt b/Documentation/acpi/method-customizing.txt new file mode 100644 index 00000000000..e628cd23ca8 --- /dev/null +++ b/Documentation/acpi/method-customizing.txt @@ -0,0 +1,66 @@ +Linux ACPI Custom Control Method How To +======================================= + +Written by Zhang Rui + + +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/drivers/acpi/debug.c b/drivers/acpi/debug.c index 8a690c3b8e2..0bedd1554f2 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -195,6 +196,79 @@ static int param_get_trace_state(char *buffer, struct kernel_param *kp) module_param_call(trace_state, param_set_trace_state, param_get_trace_state, NULL, 0644); +/* -------------------------------------------------------------------------- + DebugFS Interface + -------------------------------------------------------------------------- */ + +static ssize_t cm_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + static char *buf; + static int uncopied_bytes; + struct acpi_table_header table; + acpi_status status; + + if (!(*ppos)) { + /* parse the table header to get the table length */ + if (count <= sizeof(struct acpi_table_header)) + return -EINVAL; + if (copy_from_user(&table, user_buf, + sizeof(struct acpi_table_header))) + return -EFAULT; + uncopied_bytes = table.length; + buf = kzalloc(uncopied_bytes, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + + if (uncopied_bytes < count) { + kfree(buf); + return -EINVAL; + } + + if (copy_from_user(buf + (*ppos), user_buf, count)) { + kfree(buf); + return -EFAULT; + } + + uncopied_bytes -= count; + *ppos += count; + + if (!uncopied_bytes) { + status = acpi_install_method(buf); + kfree(buf); + if (ACPI_FAILURE(status)) + return -EINVAL; + } + + return count; +} + +static const struct file_operations cm_fops = { + .write = cm_write, +}; + +static int acpi_debugfs_init(void) +{ + struct dentry *acpi_dir, *cm_dentry; + + acpi_dir = debugfs_create_dir("acpi", NULL); + if (!acpi_dir) + goto err; + + cm_dentry = debugfs_create_file("custom_method", S_IWUGO, + acpi_dir, NULL, &cm_fops); + if (!cm_dentry) + goto err; + + return 0; + +err: + if (acpi_dir) + debugfs_remove(acpi_dir); + return -EINVAL; +} + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -286,7 +360,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 +395,10 @@ int __init acpi_debug_init(void) return 0; #endif } + +int __init acpi_debug_init(void) +{ + acpi_debugfs_init(); + acpi_procfs_init(); + return 0; +} From 03a05ed1152944000151d57b71000de287a1eb02 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Fri, 11 Dec 2009 15:17:20 +0800 Subject: [PATCH 060/117] ACPI: Use the ARB_DISABLE for the CPU which model id is less than 0x0f. Currently, ARB_DISABLE is a NOP on all of the recent Intel platforms. For such platforms, reduce contention on c3_lock by skipping the fake ARB_DISABLE. The cpu model id on one laptop is 14. If we disable ARB_DISABLE on this box, the box can't be booted correctly. But if we still enable ARB_DISABLE on this box, the box can be booted correctly. So we still use the ARB_DISABLE for the cpu which mode id is less than 0x0f. http://bugzilla.kernel.org/show_bug.cgi?id=14700 Signed-off-by: Zhao Yakui Acked-by: Pallipadi, Venkatesh cc: stable@kernel.org Signed-off-by: Len Brown --- arch/x86/kernel/acpi/cstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/acpi/cstate.c b/arch/x86/kernel/acpi/cstate.c index 59cdfa4686b..2e837f5080f 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); From 583061c59ffea1ea14f6dca735ab0f99238a7a92 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 11 Dec 2009 14:36:47 +0800 Subject: [PATCH 061/117] ACPICA: Fix possible memory leak for module-level code execution An object can be leaked for each block of executed module-level code if the interpreter slack mode is enabled. The change deletes any implicitly returned object in this case. Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/nseval.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index f771e978c40..65e0d1a260e 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -415,6 +415,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); From f2cb12510836da63cf459dca452fc0770da155f1 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 14:38:08 +0800 Subject: [PATCH 062/117] ACPICA: Fix two Scope type error messages Both messages incorrectly used the internal Path string instead of the node name. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/dswload.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 10fc7851784..1bab934e99f 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); } From ebdca3edd5ac61e37f2f5e7c11006c1a68d3cc7e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 14:40:17 +0800 Subject: [PATCH 063/117] ACPICA: Fix two additional Scope override error messages These messages were using the internal path for the message instead of using the node name. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/dswload.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 1bab934e99f..9cc2289f872 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -700,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; @@ -720,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); } From 34c39c755347c1ca3d06284bad2273c6a9c3108a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 14:53:11 +0800 Subject: [PATCH 064/117] ACPICA: Add repair for bad _FDE/_GTM buffers The expected return value for both names is a Buffer of 5 DWORDS. This repair fixes two possible problems (both seen in the field): A package of integers is returned, or a buffer of BYTEs is returned. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nspredef.c | 14 ++- drivers/acpi/acpica/nsrepair2.c | 149 +++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index b05f42903c8..1782c3d85ba 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -222,23 +222,21 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, 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; - } + if (ACPI_SUCCESS(status)) { - /* For returned Package objects, check the type of all sub-objects */ + /* For returned Package objects, check the type of all sub-objects */ - if (return_object->common.type == ACPI_TYPE_PACKAGE) { - status = acpi_ns_check_package(data, return_object_ptr); + if (return_object->common.type == ACPI_TYPE_PACKAGE) { + status = acpi_ns_check_package(data, return_object_ptr); + } } /* * Perform additional, more complicated repairs on a per-name - * basis. + * basis. Do this regardless of the status from above. */ status = acpi_ns_complex_repairs(data, node, status, return_object_ptr); -check_validation_status: /* * If the object validation failed or if we successfully repaired one * or more objects, mark the parent node to suppress further warning diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index d07b6861381..6c35b57a7fd 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -73,6 +73,10 @@ static acpi_status acpi_ns_repair_ALR(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); +static acpi_status +acpi_ns_repair_FDE(struct acpi_predefined_data *data, + union acpi_operand_object **return_object_ptr); + static acpi_status acpi_ns_repair_PSS(struct acpi_predefined_data *data, union acpi_operand_object **return_object_ptr); @@ -104,17 +108,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 a Package or Buffer of BYTEs to a Buffer of DWORDs + * _GTM: Convert a Package or Buffer of BYTEs to a Buffer of DWORDs + * _PSS: Sort the list descending by Power + * _TSS: Sort the list descending by Power */ static const struct acpi_repair_info acpi_ns_repairable_names[] = { {"_ALR", acpi_ns_repair_ALR}, + {"_FDE", acpi_ns_repair_FDE}, + {"_GTM", acpi_ns_repair_FDE}, /* _GTM has same repair as _FDE */ {"_PSS", acpi_ns_repair_PSS}, {"_TSS", acpi_ns_repair_TSS}, {{0, 0, 0, 0}, NULL} /* Table terminator */ }; +#define ACPI_FDE_FIELD_COUNT 5 +#define ACPI_FDE_BYTE_BUFFER_SIZE 5 +#define ACPI_FDE_DWORD_BUFFER_SIZE (ACPI_FDE_FIELD_COUNT * sizeof (u32)) + /****************************************************************************** * * FUNCTION: acpi_ns_complex_repairs @@ -213,6 +227,135 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, return (status); } +/****************************************************************************** + * + * FUNCTION: acpi_ns_repair_FDE + * + * PARAMETERS: Data - Pointer to validation data structure + * return_object_ptr - Pointer to the object returned from the + * evaluation of a method or object + * + * RETURN: Status. AE_OK if object is OK or was repaired successfully + * + * DESCRIPTION: Repair for the _FDE and _GTM objects. The expected return + * value is a Buffer of 5 DWORDs. This function repairs two + * possible problems: + * 1) The return value is a Buffer of BYTEs, not DWORDs + * 2) The return value is a Package of Integer objects + * + *****************************************************************************/ + +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 **elements; + union acpi_operand_object *buffer_object; + u8 *byte_buffer; + u32 *dword_buffer; + u32 count; + u32 i; + + 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_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Expanded Byte Buffer to expected DWord Buffer")); + break; + + case ACPI_TYPE_PACKAGE: + + /* All elements of the Package must be integers */ + + elements = return_object->package.elements; + count = + ACPI_MIN(ACPI_FDE_FIELD_COUNT, + return_object->package.count); + + for (i = 0; i < count; i++) { + if ((!*elements) || + ((*elements)->common.type != ACPI_TYPE_INTEGER)) { + return (AE_AML_OPERAND_TYPE); + } + elements++; + } + + /* Create the new buffer object to replace the Package */ + + buffer_object = + acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE); + if (!buffer_object) { + return (AE_NO_MEMORY); + } + + /* Copy the package elements (integers) to the buffer */ + + elements = return_object->package.elements; + dword_buffer = + ACPI_CAST_PTR(u32, buffer_object->buffer.pointer); + + for (i = 0; i < count; i++) { + *dword_buffer = (u32) (*elements)->integer.value; + dword_buffer++; + elements++; + } + + ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, + "Converted Package to expected Buffer")); + 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 From f24b664dc44a4ab4df61db2258cea298eeb43a8e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 14:57:00 +0800 Subject: [PATCH 065/117] ACPICA: Update internal namespace node/handle interfaces This change deletes the unnecessary acpi_ns_convert_entry_to_handle interface and renames the acpi_ns_map_handle_to_node interface to acpi_ns_validate_handle. ACPICA BZ 798. http://www.acpica.org/bugzilla/show_bug.cgi?id=798 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acnamesp.h | 4 +-- drivers/acpi/acpica/evregion.c | 4 +-- drivers/acpi/acpica/evxface.c | 4 +-- drivers/acpi/acpica/evxfevnt.c | 4 +-- drivers/acpi/acpica/evxfregn.c | 4 +-- drivers/acpi/acpica/nsdump.c | 2 +- drivers/acpi/acpica/nsnames.c | 2 +- drivers/acpi/acpica/nsutils.c | 57 +++++++--------------------------- drivers/acpi/acpica/nsxfeval.c | 10 +++--- drivers/acpi/acpica/nsxfname.c | 10 +++--- drivers/acpi/acpica/nsxfobj.c | 14 ++++----- drivers/acpi/acpica/rsxface.c | 2 +- 12 files changed, 40 insertions(+), 77 deletions(-) diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index ab83919dda6..14cef45bd6c 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -354,9 +354,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/evregion.c b/drivers/acpi/acpica/evregion.c index 0bc807c33a5..5336d911fbf 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/evxface.c b/drivers/acpi/acpica/evxface.c index 10b8543dd46..2fe0809d4eb 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 4721f58fe42..eed7a38d25f 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 7c3d2d356ff..c98aa7c2d67 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/nsdump.c b/drivers/acpi/acpica/nsdump.c index 2deb986861c..e37836e27e2 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/nsnames.c b/drivers/acpi/acpica/nsnames.c index af8e6bcee07..8f9a4875ce2 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/nsutils.c b/drivers/acpi/acpica/nsutils.c index ea55ab4f984..47d91e668a1 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(); @@ -708,42 +709,6 @@ struct acpi_namespace_node *acpi_ns_map_handle_to_node(acpi_handle handle) return (ACPI_CAST_PTR(struct acpi_namespace_node, handle)); } -/******************************************************************************* - * - * FUNCTION: acpi_ns_convert_entry_to_handle - * - * PARAMETERS: Node - Node to be converted to a Handle - * - * RETURN: A user handle - * - * DESCRIPTION: Convert a real Node to a namespace handle - * - ******************************************************************************/ - -acpi_handle acpi_ns_convert_entry_to_handle(struct acpi_namespace_node *node) -{ - - /* - * Simple implementation for now; - */ - return ((acpi_handle) node); - -/* Example future implementation --------------------- - - if (!Node) - { - return (NULL); - } - - if (Node == acpi_gbl_root_node) - { - return (ACPI_ROOT_OBJECT); - } - - return ((acpi_handle) Node); -------------------------------------------------------*/ -} - /******************************************************************************* * * FUNCTION: acpi_ns_terminate diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index f2bd1da7700..f0c0892bc7e 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 ddc84af6336..e611dd961b2 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 4071bad4458..0cc6ba01a49 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/rsxface.c b/drivers/acpi/acpica/rsxface.c index 395212bcd19..f27feb4772f 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); } From 47e11d54d2a516461d89cbb3f2fb87084056af72 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:01:12 +0800 Subject: [PATCH 066/117] ACPICA: Add more conversions to predefined name repair module This change enhances the automatic repairs/conversions for predefined name return values to make Integers, Strings, and Buffers fully interchangeable. Also, a Buffer can be converted to a Package of Integers if necessary. The nsrepair.c module was completely restructured. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nsrepair.c | 421 ++++++++++++++++++++++++++------- 1 file changed, 336 insertions(+), 85 deletions(-) diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index d563f1a564a..10629fa55d8 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -45,11 +45,50 @@ #include "accommon.h" #include "acnamesp.h" #include "acinterp.h" -#include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsrepair") +/******************************************************************************* + * + * This module attempts to repair or convert objects returned by the + * predefined methods to an object type that is expected, as per the ACPI + * specification. The need for this code is dictated by the many machines that + * return incorrect types for the standard predefined methods. Performing these + * conversions here, in one place, eliminates the need for individual ACPI + * device drivers to do the same. Note: Most of these conversions are different + * than the internal object conversion routines used for implicit object + * conversion. + * + * The following conversions can be performed as necessary: + * + * Integer -> String + * Integer -> Buffer + * String -> Integer + * String -> Buffer + * Buffer -> Integer + * Buffer -> String + * Buffer -> Package of Integers + * Package -> Package of one Package + * + ******************************************************************************/ +/* Local prototypes */ +static acpi_status +acpi_ns_convert_to_integer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_string(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + +static acpi_status +acpi_ns_convert_to_package(union acpi_operand_object *original_object, + union acpi_operand_object **return_object); + /******************************************************************************* * * FUNCTION: acpi_ns_repair_object @@ -68,6 +107,7 @@ ACPI_MODULE_NAME("nsrepair") * not expected. * ******************************************************************************/ + acpi_status acpi_ns_repair_object(struct acpi_predefined_data *data, u32 expected_btypes, @@ -76,98 +116,44 @@ 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; /* * 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) { - case ACPI_TYPE_BUFFER: - - /* Does the method/object legally return a string? */ - - if (!(expected_btypes & ACPI_RTYPE_STRING)) { - return (AE_AML_OPERAND_TYPE); + if (expected_btypes & ACPI_RTYPE_INTEGER) { + status = acpi_ns_convert_to_integer(return_object, &new_object); + if (ACPI_SUCCESS(status)) { + goto object_repaired; } - - /* - * Have a Buffer, expected a String, convert. 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])) { - length++; - } - - /* Allocate a new string object */ - - new_object = acpi_ut_create_string_object(length); - if (!new_object) { - return (AE_NO_MEMORY); - } - - /* - * Copy the raw buffer data with no transform. String is already NULL - * terminated at Length+1. - */ - ACPI_MEMCPY(new_object->string.pointer, - return_object->buffer.pointer, length); - break; - - case ACPI_TYPE_INTEGER: - - /* 1) Does the method/object legally return a buffer? */ - - 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); - } - } - - /* 2) Does the method/object legally return a string? */ - - 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) { - return (AE_AML_OPERAND_TYPE); - } - - /* Allocate a new NULL string object */ - - new_object = acpi_ut_create_string_object(0); - if (!new_object) { - return (AE_NO_MEMORY); - } - } else { - return (AE_AML_OPERAND_TYPE); - } - break; - - default: - - /* We cannot repair this object */ - - return (AE_AML_OPERAND_TYPE); } + 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 */ @@ -208,6 +194,271 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, 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: + + /* Buffer-to-Integer conversion. Max buffer size is 64 bits. */ + + 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: + /* + * 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 < original_object->buffer.length) && + (original_object->buffer.pointer[length])) { + length++; + } + + /* Allocate a new string object */ + + new_object = acpi_ut_create_string_object(length); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* + * Copy the raw buffer data with no transform. String is already NULL + * terminated at Length+1. + */ + ACPI_MEMCPY(new_object->string.pointer, + 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 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; + + 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; + + case ACPI_TYPE_STRING: + + /* 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); + } + + ACPI_MEMCPY(new_object->buffer.pointer, + original_object->string.pointer, + original_object->string.length); + break; + + default: + return (AE_AML_OPERAND_TYPE); + } + + *return_object = new_object; + return (AE_OK); +} + +/******************************************************************************* + * + * 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. + * + ******************************************************************************/ + +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); + } + + /* Convert each buffer byte to an integer package element */ + + elements = new_object->package.elements; + buffer = original_object->buffer.pointer; + + while (length--) { + *elements = acpi_ut_create_integer_object(*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); +} + /******************************************************************************* * * FUNCTION: acpi_ns_repair_package_list From 90434c1c7c61ce7dd349f17f003b8813421aeadc Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:02:15 +0800 Subject: [PATCH 067/117] ACPICA: Update function headers, no functional change Fixed a few errors with the headers in utcopy.c Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/utcopy.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index 0f0c64bf8ac..f857c5efb79 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 * From d97659112044c0c77b93c6199eee7ee884eb3cca Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:16:38 +0800 Subject: [PATCH 068/117] ACPICA: Fix mutex errors when running _REG methods Fixes a problem where mutex errors can occur when running a _REG method that is in the same scope as a method-defined operation region or an operation region under a module-level IF block. This is rare, so the problem has not been seen before. ACPICA BZ 826. http://www.acpica.org/bugzilla/show_bug.cgi?id=826 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/dswload.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 9cc2289f872..b40513dd6a6 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -1052,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, @@ -1063,21 +1076,17 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { return (status); } + + acpi_ex_exit_interpreter(); } - /* - * 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) - */ - - /* - * 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 From ea7c5ec148044776d5e134e52a3e1aca8d662dbe Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:18:52 +0800 Subject: [PATCH 069/117] ACPICA: Move Package-to-Buffer repair code into common ToBuffer function Move code specific to _FDE and _GTM into the generic repair code. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nspredef.c | 11 ++++++--- drivers/acpi/acpica/nsrepair.c | 43 +++++++++++++++++++++++++++++++-- drivers/acpi/acpica/nsrepair2.c | 43 --------------------------------- 3 files changed, 48 insertions(+), 49 deletions(-) diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 1782c3d85ba..c6297f57fea 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -215,6 +215,8 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, data->node_flags = node->flags; data->pathname = pathname; + /* TBD: For variable-length Packages, remove NULL elements here */ + /* * Check that the type of the return object is what is expected for * this predefined name @@ -223,10 +225,11 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, predefined->info.expected_btypes, ACPI_NOT_PACKAGE_ELEMENT); if (ACPI_SUCCESS(status)) { - - /* 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 created by call above. + */ + if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) { status = acpi_ns_check_package(data, return_object_ptr); } } diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 10629fa55d8..062a016d455 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -350,7 +350,7 @@ acpi_ns_convert_to_string(union acpi_operand_object *original_object, * * RETURN: Status. AE_OK if conversion was successful. * - * DESCRIPTION: Attempt to convert a Integer/String object to a Buffer. + * DESCRIPTION: Attempt to convert a Integer/String/Package object to a Buffer. * ******************************************************************************/ @@ -360,6 +360,10 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_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: @@ -393,6 +397,40 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, original_object->string.length); break; + case ACPI_TYPE_PACKAGE: + + /* All elements of the Package must be integers */ + + 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++; + } + + /* Create the new buffer object to replace the Package */ + + new_object = acpi_ut_create_buffer_object(ACPI_MUL_4(count)); + if (!new_object) { + return (AE_NO_MEMORY); + } + + /* Copy the package elements (integers) to the buffer as DWORDs */ + + 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); } @@ -441,7 +479,8 @@ acpi_ns_convert_to_package(union acpi_operand_object *original_object, buffer = original_object->buffer.pointer; while (length--) { - *elements = acpi_ut_create_integer_object(*buffer); + *elements = + acpi_ut_create_integer_object((u64) *buffer); if (!*elements) { acpi_ut_remove_reference(new_object); return (AE_NO_MEMORY); diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 6c35b57a7fd..846df7047a8 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -250,11 +250,9 @@ 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 **elements; union acpi_operand_object *buffer_object; u8 *byte_buffer; u32 *dword_buffer; - u32 count; u32 i; switch (return_object->common.type) { @@ -302,47 +300,6 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, "Expanded Byte Buffer to expected DWord Buffer")); break; - case ACPI_TYPE_PACKAGE: - - /* All elements of the Package must be integers */ - - elements = return_object->package.elements; - count = - ACPI_MIN(ACPI_FDE_FIELD_COUNT, - return_object->package.count); - - for (i = 0; i < count; i++) { - if ((!*elements) || - ((*elements)->common.type != ACPI_TYPE_INTEGER)) { - return (AE_AML_OPERAND_TYPE); - } - elements++; - } - - /* Create the new buffer object to replace the Package */ - - buffer_object = - acpi_ut_create_buffer_object(ACPI_FDE_DWORD_BUFFER_SIZE); - if (!buffer_object) { - return (AE_NO_MEMORY); - } - - /* Copy the package elements (integers) to the buffer */ - - elements = return_object->package.elements; - dword_buffer = - ACPI_CAST_PTR(u32, buffer_object->buffer.pointer); - - for (i = 0; i < count; i++) { - *dword_buffer = (u32) (*elements)->integer.value; - dword_buffer++; - elements++; - } - - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Converted Package to expected Buffer")); - break; - default: return (AE_AML_OPERAND_TYPE); } From 3a58176e4fa47d8232e04131b023f3f2ecd7084b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:23:22 +0800 Subject: [PATCH 070/117] ACPICA: Remove messages if predefined repair(s) are successful Repair mechanism was considered too wordy. Now, messages are only unconditionally emitted if the return object cannot be repaired. Existing messages for successful repairs were converted to ACPI_DEBUG_PRINT messages for now. ACPICA BZ 827. http://www.acpica.org/bugzilla/show_bug.cgi?id=827 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nspredef.c | 10 +++++----- drivers/acpi/acpica/nsrepair.c | 32 ++++++++++++++++++-------------- drivers/acpi/acpica/nsrepair2.c | 21 +++++++++++++-------- include/acpi/acoutput.h | 8 +++++--- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index c6297f57fea..e352467413f 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -462,11 +462,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 */ diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 062a016d455..907f60c9c9e 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -118,6 +118,8 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, union acpi_operand_object *new_object; 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 @@ -171,19 +173,18 @@ acpi_ns_repair_object(struct acpi_predefined_data *data, return_object->common.reference_count--; } - 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)); + 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_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))); + 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 */ @@ -528,6 +529,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. @@ -544,8 +547,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 846df7047a8..199333880c8 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -255,6 +255,8 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, u32 *dword_buffer; u32 i; + ACPI_FUNCTION_NAME(ns_repair_FDE); + switch (return_object->common.type) { case ACPI_TYPE_BUFFER: @@ -296,8 +298,9 @@ acpi_ns_repair_FDE(struct acpi_predefined_data *data, byte_buffer++; } - ACPI_INFO_PREDEFINED((AE_INFO, data->pathname, data->node_flags, - "Expanded Byte Buffer to expected DWord Buffer")); + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s Expanded Byte Buffer to expected DWord Buffer\n", + data->pathname)); break; default: @@ -445,6 +448,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) { @@ -460,8 +465,9 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, */ 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")); + ACPI_DEBUG_PRINT((ACPI_DB_REPAIR, + "%s: NULL elements removed from package\n", + data->pathname)); /* Exit if package is now zero length */ @@ -522,10 +528,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); } diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 5c823d5ab78..d814da4b536 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) From 43420bbb892268b5fc42cb80c3bc31dedbad3ac9 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:24:27 +0800 Subject: [PATCH 071/117] ACPICA: Update function headers and comments, no functional change Update comments for repair of _FDE and _GTM methods. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nsrepair.c | 5 +++++ drivers/acpi/acpica/nsrepair2.c | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 907f60c9c9e..4fd1bdb056b 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -399,6 +399,11 @@ acpi_ns_convert_to_buffer(union acpi_operand_object *original_object, 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 */ diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index 199333880c8..e7373eb6cfa 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -111,8 +111,8 @@ acpi_ns_sort_list(union acpi_operand_object **elements, * As necessary: * * _ALR: Sort the list ascending by ambient_illuminance - * _FDE: Convert a Package or Buffer of BYTEs to a Buffer of DWORDs - * _GTM: Convert a Package or Buffer of BYTEs to a Buffer of DWORDs + * _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 */ @@ -238,10 +238,9 @@ acpi_ns_repair_ALR(struct acpi_predefined_data *data, * 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 two - * possible problems: - * 1) The return value is a Buffer of BYTEs, not DWORDs - * 2) The return value is a Package of Integer objects + * value is a Buffer of 5 DWORDs. This function repairs a common + * problem where the return value is a Buffer of BYTEs, not + * DWORDs. * *****************************************************************************/ From 465da9eb75665203a826f961de74a817b870850a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:26:13 +0800 Subject: [PATCH 072/117] ACPICA: Conditionally perform complex per-predefined-name repairs Only attempt the "complex" repairs (package sorting, buffer expansion) if the previous "generic" validation and repair was successful. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/nspredef.c | 37 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index e352467413f..239fa2f731c 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -215,31 +215,42 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, data->node_flags = node->flags; data->pathname = pathname; - /* TBD: For variable-length Packages, remove NULL elements here */ - /* - * 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_SUCCESS(status)) { - /* - * For returned Package objects, check the type of all sub-objects. - * Note: Package may have been 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; + } + + /* + * 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) { + + /* TBD: For variable-length Packages, remove NULL elements here */ + + 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. Do this regardless of the status from above. + * 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); +exit: /* * If the object validation failed or if we successfully repaired one * or more objects, mark the parent node to suppress further warning From e31c32cfe52e98344dad28853c3331879f72c4b0 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Fri, 11 Dec 2009 15:28:27 +0800 Subject: [PATCH 073/117] ACPICA: Module-level code: enable _REG execution in same scope This change enables the execution of _REG methods that appear in the same scope as the module-level code, in resonse to an operation region declaration within the module-level code. Signed-off-by: Lin Ming Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/acobject.h | 6 +++++- drivers/acpi/acpica/dsmethod.c | 2 +- drivers/acpi/acpica/evrgnini.c | 15 +++++++++++++++ drivers/acpi/acpica/nsaccess.c | 2 +- drivers/acpi/acpica/nseval.c | 12 ++++++++++++ drivers/acpi/acpica/psxface.c | 3 ++- 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index b39d682a214..64062b1be3e 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 567a4899a01..e786f9fd767 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/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index cf29c495302..ff168052a33 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/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index 9c3cdbe2d82..d622ba77000 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/nseval.c b/drivers/acpi/acpica/nseval.c index 65e0d1a260e..af9fe910373 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; diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 12934ad6da8..d0c1b91eb8c 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 */ From d4085a3ffee8828f7f1bae7fa3cf5e58f59ba627 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 11 Dec 2009 15:29:44 +0800 Subject: [PATCH 074/117] ACPICA: Predefined name repair: automatically remove null package elements This change will automatically remove embedded and trailing NULL package elements from returned package objects that are defined to containe a variable number of sub-packages. The driver is then presented with a package with no null elements to deal with. ACPICA BZ 819. http://www.acpica.org/bugzilla/show_bug.cgi?id=819 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/acnamesp.h | 5 +++ drivers/acpi/acpica/nspredef.c | 59 ++++++------------------ drivers/acpi/acpica/nsrepair2.c | 79 +++++++++++++++++++-------------- 3 files changed, 65 insertions(+), 78 deletions(-) diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index 14cef45bd6c..61edb156e8d 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 */ diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 239fa2f731c..d34fa59548f 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -231,9 +231,6 @@ acpi_ns_check_predefined_names(struct acpi_namespace_node *node, * Note: Package may have been newly created by call above. */ if ((*return_object_ptr)->common.type == ACPI_TYPE_PACKAGE) { - - /* TBD: For variable-length Packages, remove NULL elements here */ - status = acpi_ns_check_package(data, return_object_ptr); if (ACPI_FAILURE(status)) { goto exit; @@ -439,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; @@ -692,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/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index e7373eb6cfa..f13691c1cca 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -45,6 +45,7 @@ #include #include "accommon.h" #include "acnamesp.h" +#include "acpredef.h" #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsrepair2") @@ -92,9 +93,6 @@ acpi_ns_check_sorted_list(struct acpi_predefined_data *data, u32 sort_index, u8 sort_direction, char *sort_key_name); -static acpi_status -acpi_ns_remove_null_elements(union acpi_operand_object *package); - static acpi_status acpi_ns_sort_list(union acpi_operand_object **elements, u32 count, u32 index, u8 sort_direction); @@ -456,25 +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_DEBUG_PRINT((ACPI_DB_REPAIR, - "%s: NULL elements removed from package\n", - data->pathname)); - - /* 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) { @@ -544,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; @@ -582,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); } /****************************************************************************** From fbc3be2af3e8beb581a20c2a0cfda5692f5a1c08 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 11 Dec 2009 15:31:40 +0800 Subject: [PATCH 075/117] ACPICA: Move check for valid Thread ID structure This change moves the check for a valid Thread ID structure up a few lines to insure that the check is made before the structure is actually used. Signed-off-by: Dan Carpenter Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/exmutex.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index 2f0114202b0..3c456bd575d 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 From 88e5071525ad6814be3a8a2792ce9e81a0cca22a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 15 Dec 2009 12:45:15 +0800 Subject: [PATCH 076/117] ACPICA: Update version to 20091214 Version 20091214. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 5e1ad3cd1bb..86e9735a96b 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" From 9065ce4500085b9ca66b19d3c4d21a73cb410173 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 17 Nov 2009 17:05:19 -0700 Subject: [PATCH 077/117] PNP: add interface to retrieve ACPI device from a PNPACPI device Add pnp_acpi_device(pnp_dev), which takes a PNP device and returns the associated ACPI device (or NULL, if the device is not a PNPACPI device). This allows us to write a PNP driver that can manage both traditional PNPBIOS and ACPI devices, treating ACPI-only functionality as an optional extension. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/pnp/pnpacpi/core.c | 3 ++- include/linux/pnp.h | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index b2348fc2378..5314bf630bc 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -144,7 +144,7 @@ static int pnpacpi_resume(struct pnp_dev *dev) } #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, @@ -154,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) { diff --git a/include/linux/pnp.h b/include/linux/pnp.h index fddfafaed02..7c4193eb007 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 From ad497680a5ff646e645752e3e065a752f32f12f8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 17 Nov 2009 17:05:24 -0700 Subject: [PATCH 078/117] ipmi: remove unused PCI probe code Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/char/ipmi/ipmi_si_intf.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index d2e698096ac..aaf6eadaa4f 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2202,7 +2202,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 +2240,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; From 18a3e0bfbcd589599d0affbfd484ba9a97e5f122 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 17 Nov 2009 17:05:29 -0700 Subject: [PATCH 079/117] ipmi: refer to table as "SPMI", not "ACPI" This discovery method uses the SPMI table, not the ACPI namespace. In the future, we will look in the namespace, so let's refer to the table as "SPMI" and save "ACPI" for the namespace. Signed-off-by: Bjorn Helgaas Signed-off-by: Len Brown --- drivers/char/ipmi/ipmi_si_intf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index aaf6eadaa4f..b58e587d299 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1919,7 +1919,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 +1940,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 +2002,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,7 +2020,7 @@ static __devinit void acpi_find_bmc(void) if (status != AE_OK) return; - try_init_acpi(spmi); + try_init_spmi(spmi); } } #endif @@ -3104,7 +3104,7 @@ static __devinit int init_ipmi_si(void) #endif #ifdef CONFIG_ACPI - acpi_find_bmc(); + spmi_find_bmc(); #endif #ifdef CONFIG_PCI From 9e368fa011d4e0aa050db348d69514900520e40b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 17 Nov 2009 17:05:34 -0700 Subject: [PATCH 080/117] ipmi: add PNP discovery (ACPI namespace via PNPACPI) This allows ipmi_si_intf.c to claim IPMI devices described in the ACPI namespace. Using PNP makes it simpler to parse the IRQ/IO/memory resources of the device. We look at any SPMI tables before looking for devices in the namespace. This is based on ipmi_pci_probe(). Signed-off-by: Bjorn Helgaas Signed-off-by: Myron Stowe Signed-off-by: Len Brown --- drivers/char/ipmi/ipmi_si_intf.c | 104 +++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index b58e587d299..679cd08b80b 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -64,6 +64,7 @@ #include #include #include +#include #ifdef CONFIG_PPC_OF #include @@ -2023,6 +2024,103 @@ static __devinit void spmi_find_bmc(void) 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 @@ -3106,6 +3204,9 @@ static __devinit int init_ipmi_si(void) #ifdef CONFIG_ACPI spmi_find_bmc(); #endif +#ifdef CONFIG_PNP + pnp_register_driver(&ipmi_pnp_driver); +#endif #ifdef CONFIG_PCI rv = pci_register_driver(&ipmi_pci_driver); @@ -3229,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); From d951d4cc84e8b5ddb8e0ab81cf6a72cc73fdd668 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 7 Dec 2009 22:05:50 +0100 Subject: [PATCH 081/117] asus-laptop: change light sens default values. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The light sensor disable brightness key and /sys/class/backlight/ control. There was a lot of report from users who didn't understand why they couldn't change their brightness, including: https://bugs.launchpad.net/bugs/222171 https://bugzilla.novell.com/show_bug.cgi?id=514747 http://bugzilla.kernel.org/show_bug.cgi?id=13671 http://bugzilla.kernel.org/show_bug.cgi?id=14432 Now the light sensor is disabled, and if the user want to enable it, the level should be ok. The funny thing is that comments where ok, not code. Cc: stable@kernel.org Cc: Thomas Renninger Cc: Peter Küppers Cc: Michael Franzl Cc: Ian Turner Signed-off-by: Corentin Chary Signed-off-by: Len Brown --- drivers/platform/x86/asus-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 3348cc6731d..61a1c750365 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1290,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); From 4bab30a041a2f27807b0b2a7fd36c8c8695c90c8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 14 Dec 2009 11:24:18 +0800 Subject: [PATCH 082/117] ACPI: add kernel tainting after overriding an ACPI control method Add kernel tainting after overriding an ACPI control method successfully. Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index 0bedd1554f2..cc421b7ae16 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -239,6 +239,7 @@ static ssize_t cm_write(struct file *file, const char __user *user_buf, kfree(buf); if (ACPI_FAILURE(status)) return -EINVAL; + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); } return count; From d89a727aff649f6768f7a34ee57f031ebf8bab4c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:06 -0200 Subject: [PATCH 083/117] thinkpad-acpi: sync input device EV_SW initial state Before we register the input device, sync the input layer EV_SW state through a call to input_report_switch(), to avoid issuing a gratuitous event for the initial state of these switches. This fixes some annoyances caused by the interaction with rfkill and EV_SW SW_RFKILL_ALL events. Reported-by: Kevin Locke Signed-off-by: Henrique de Moraes Holschuh Cc: Alan Jenkins Cc: Johannes Berg Cc: Dmitry Torokhov Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 53d6c331806..3daf349ddc9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3186,6 +3186,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; @@ -3291,6 +3293,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 @@ -3298,6 +3301,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)); @@ -3309,11 +3313,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); } @@ -3364,9 +3368,13 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (tp_features.hotkey_wlsw) { 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) { 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 +3441,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; From 5451a923bbdcff6ae665947e120af7238b21a9d2 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:07 -0200 Subject: [PATCH 084/117] thinkpad-acpi: log initial state of rfkill switches We already log the initial state of the hardware rfkill switch (WLSW), might as well log the state of the softswitches as well. Signed-off-by: Henrique de Moraes Holschuh Cc: Josip Rodin Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3daf349ddc9..05714abf5a8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1264,6 +1264,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]); @@ -1298,7 +1299,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) { @@ -1311,6 +1313,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; } From 329e4e18dfdc552f36b0642a3de5ebfa96063666 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:08 -0200 Subject: [PATCH 085/117] thinkpad-acpi: volume subdriver rewrite I don't trust the coupled EC writes and SMI calls the current volume control code does very much, although it is exactly what the IBM DSDTs seem to do (they never do more than a single step though). Change the driver to stop issuing SMIs, and just drive the EC directly to the desired level (DSDTs seem to confirm this will work even on very old models like the 570 and 600e/x). We checkpoint directly to NVRAM (this can be turned off) at suspend/shutdown/driver unload, which from what I can see in tbp, should also work on every ThinkPad. Signed-off-by: Henrique de Moraes Holschuh Cc: Lorne Applebaum Cc: Matthew Garrett Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 22 +- drivers/platform/x86/thinkpad_acpi.c | 342 ++++++++++++++++++++---- 2 files changed, 300 insertions(+), 64 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index f5056c7fb5b..96687d0106a 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1092,22 +1092,33 @@ WARNING: its level up and down at every change. -Volume control -- /proc/acpi/ibm/volume ---------------------------------------- +Volume control +-------------- -This feature allows volume control on ThinkPad models which don't have -a hardware volume knob. The available commands are: +procfs: /proc/acpi/ibm/volume + +This feature allows volume control on ThinkPad models with a digital +volume knob, 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 'level ' >/proc/acpi/ibm/volume -The number range is 0 to 15 although not all of them may be +The 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). The current volume level and mute state is shown in the file. +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 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. @@ -1376,6 +1387,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. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 05714abf5a8..a2f5312c6a4 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -231,6 +231,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") @@ -6375,21 +6376,260 @@ 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. + */ + +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 +}; + +static enum tpacpi_volume_access_mode volume_mode = + TPACPI_VOL_MODE_MAX; + + +/* + * 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) +{ + u8 lec = 0; + u8 b_nvram; + const u8 ec_mask = TP_EC_AUDIO_LVL_MSK | TP_EC_AUDIO_MUTESW_MSK; + + if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) + return; + + vdbg_printk(TPACPI_DBG_MIXER, + "trying to checkpoint mixer state to NVRAM...\n"); + + 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 { + vdbg_printk(TPACPI_DBG_MIXER, + "NVRAM mixer status already is 0x%02x (0x%02x)\n", + (unsigned int) lec, (unsigned int) b_nvram); + } + +unlock: + mutex_unlock(&volume_mutex); +} + +static int volume_get_status_ec(u8 *status) +{ + u8 s; + + if (!acpi_ec_read(TP_EC_AUDIO, &s)) + return -EIO; + + *status = s; + + dbg_printk(TPACPI_DBG_MIXER, "status 0x%02x\n", s); + + return 0; +} + +static int volume_get_status(u8 *status) +{ + return volume_get_status_ec(status); +} + +static int volume_set_status_ec(const u8 status) +{ + if (!acpi_ec_write(TP_EC_AUDIO, status)) + 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_suspend(pm_message_t state) +{ + tpacpi_volume_checkpoint_nvram(); +} + +static void volume_shutdown(void) +{ + tpacpi_volume_checkpoint_nvram(); +} + +static void volume_exit(void) +{ + tpacpi_volume_checkpoint_nvram(); +} + +static int __init volume_init(struct ibm_init_struct *iibm) +{ + 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_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, + "volume is supported\n"); + + return 0; +} static int volume_read(char *p) { int len = 0; - u8 level; + u8 status; - if (!acpi_ec_read(volume_offset, &level)) { + if (volume_get_status(&status) < 0) { len += sprintf(p + len, "level:\t\tunreadable\n"); } 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, "level:\t\t%d\n", + status & TP_EC_AUDIO_LVL_MSK); + len += sprintf(p + len, "mute:\t\t%s\n", + onoff(status, TP_EC_AUDIO_MUTESW)); len += sprintf(p + len, "commands:\tup, down, mute\n"); len += sprintf(p + len, "commands:\tlevel " - " ( is 0-15)\n"); + " ( is 0-%d)\n", TP_EC_VOLUME_MAX); } return len; @@ -6397,77 +6637,55 @@ static int volume_read(char *p) static int volume_write(char *buf) { - int cmos_cmd, inc, i; - u8 level, mute; - int new_level, new_mute; + u8 s; + u8 new_level, new_mute; + int l; char *cmd; + int rc; + + 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 (!acpi_ec_read(volume_offset, &level)) - return -EIO; - new_mute = mute = level & 0x40; - new_level = level = level & 0xf; - if (strlencmp(cmd, "up") == 0) { - if (mute) + if (new_mute) new_mute = 0; - else - new_level = level == 15 ? 15 : level + 1; + else if (new_level < TP_EC_VOLUME_MAX) + new_level++; } else if (strlencmp(cmd, "down") == 0) { - if (mute) + if (new_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 (new_level > 0) + new_level--; + } else if (sscanf(cmd, "level %u", &l) == 1 && + l >= 0 && l <= TP_EC_VOLUME_MAX) { + new_level = l; } else if (strlencmp(cmd, "mute") == 0) { - new_mute = 0x40; + new_mute = TP_EC_AUDIO_MUTESW_MSK; } else return -EINVAL; - - if (new_level != level) { - /* mute doesn't change */ - - cmos_cmd = (new_level > level) ? - TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; - inc = new_level > level ? 1 : -1; - - if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, level))) - return -EIO; - - for (i = level; i != new_level; i += inc) - if (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, i + inc)) - return -EIO; - - if (mute && - (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || - !acpi_ec_write(volume_offset, new_level + mute))) { - return -EIO; - } - } - - if (new_mute != mute) { - /* level doesn't change */ - - cmos_cmd = (new_mute) ? - TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; - - if (issue_thinkpad_cmos_command(cmos_cmd) || - !acpi_ec_write(volume_offset, level + new_mute)) - return -EIO; - } } - return 0; + 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); + + 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, + .shutdown = volume_shutdown, }; /************************************************************************* @@ -8121,6 +8339,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { .data = &brightness_driver_data, }, { + .init = volume_init, .data = &volume_driver_data, }, { @@ -8186,6 +8405,11 @@ 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"); + #define TPACPI_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ From a112ceee673629afc204bf6b4a4828a6143a083f Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:09 -0200 Subject: [PATCH 086/117] thinkpad-acpi: support MUTE-only ThinkPads Lenovo removed the extra mixer since the T61 and thereabouts. Newer Lenovo models only have the mute gate function, and leave the volume control to the HDA mixer. Until a way to automatically query the firmware about its audio control capabilities is discovered (there might not be any), use a white/black list. We will likely need to ask T60 (old and new model) and Z60/Z61 users whether they have volume control to populate the black/white list. Meanwhile, provide a volume_capabilities parameter that can be used to override the defaults. Signed-off-by: Henrique de Moraes Holschuh Cc: Lorne Applebaum Cc: Matthew Garrett Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 19 ++- drivers/platform/x86/thinkpad_acpi.c | 159 ++++++++++++++++++++---- 2 files changed, 149 insertions(+), 29 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 96687d0106a..bd87682103c 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1098,18 +1098,31 @@ Volume control procfs: /proc/acpi/ibm/volume This feature allows volume control on ThinkPad models with a digital -volume knob, as well as mute/unmute control. The available commands are: +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 ' >/proc/acpi/ibm/volume The 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. +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 @@ -1450,3 +1463,5 @@ Sysfs interface changelog: is deprecated and marked for removal. 0x020600: Marker for backlight change event support. + +0x020700: Support for mute-only mixers. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a2f5312c6a4..4d909d5a034 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define TPACPI_VERSION "0.23" -#define TPACPI_SYSFS_VERSION 0x020600 +#define TPACPI_SYSFS_VERSION 0x020700 /* * Changelog: @@ -299,6 +299,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; @@ -426,6 +427,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; @@ -6416,9 +6423,17 @@ enum tpacpi_volume_access_mode { 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; /* * Used to syncronize writers to TP_EC_AUDIO and @@ -6430,7 +6445,7 @@ static void tpacpi_volume_checkpoint_nvram(void) { u8 lec = 0; u8 b_nvram; - const u8 ec_mask = TP_EC_AUDIO_LVL_MSK | TP_EC_AUDIO_MUTESW_MSK; + u8 ec_mask; if (volume_mode != TPACPI_VOL_MODE_ECNVRAM) return; @@ -6438,6 +6453,11 @@ static void tpacpi_volume_checkpoint_nvram(void) vdbg_printk(TPACPI_DBG_MIXER, "trying to checkpoint mixer state to NVRAM...\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; @@ -6575,8 +6595,36 @@ static void volume_exit(void) tpacpi_volume_checkpoint_nvram(); } +#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; + vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); mutex_init(&volume_mutex); @@ -6596,6 +6644,36 @@ static int __init volume_init(struct ibm_init_struct *iibm) return 1; } + if (volume_capabilities >= TPACPI_VOL_CAP_MAX) + return -EINVAL; + + 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; @@ -6610,7 +6688,8 @@ static int __init volume_init(struct ibm_init_struct *iibm) } vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER, - "volume is supported\n"); + "mute is supported, volume control is %s\n", + str_supported(!tp_features.mixer_no_level_control)); return 0; } @@ -6623,13 +6702,21 @@ static int volume_read(char *p) if (volume_get_status(&status) < 0) { len += sprintf(p + len, "level:\t\tunreadable\n"); } else { - len += sprintf(p + len, "level:\t\t%d\n", - status & TP_EC_AUDIO_LVL_MSK); + if (tp_features.mixer_no_level_control) + len += sprintf(p + len, "level:\t\tunsupported\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", + status & TP_EC_AUDIO_LVL_MSK); + len += sprintf(p + len, "mute:\t\t%s\n", onoff(status, TP_EC_AUDIO_MUTESW)); - len += sprintf(p + len, "commands:\tup, down, mute\n"); - len += sprintf(p + len, "commands:\tlevel " + + len += sprintf(p + len, "commands:\tunmute, mute\n"); + if (!tp_features.mixer_no_level_control) { + len += sprintf(p + len, "commands:\tup, down\n"); + len += sprintf(p + len, "commands:\tlevel " " ( is 0-%d)\n", TP_EC_VOLUME_MAX); + } } return len; @@ -6651,30 +6738,43 @@ static int volume_write(char *buf) new_mute = s & TP_EC_AUDIO_MUTESW_MSK; while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "up") == 0) { - if (new_mute) - new_mute = 0; - else if (new_level < TP_EC_VOLUME_MAX) - new_level++; - } else if (strlencmp(cmd, "down") == 0) { - if (new_mute) - new_mute = 0; - else if (new_level > 0) - new_level--; - } else if (sscanf(cmd, "level %u", &l) == 1 && - l >= 0 && l <= TP_EC_VOLUME_MAX) { - new_level = l; - } else if (strlencmp(cmd, "mute") == 0) { + 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 + else if (strlencmp(cmd, "unmute") == 0) + new_mute = 0; + else return -EINVAL; } - 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); + 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); + } return (rc == -EINTR) ? -ERESTARTSYS : rc; } @@ -8410,6 +8510,11 @@ 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"); + #define TPACPI_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ From c7ac6291ea7ebc568a1fce16fed87d102898f264 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:10 -0200 Subject: [PATCH 087/117] thinkpad-acpi: disable volume control Disable volume control by default. It can be enabled at module load time by a module parameter (volume_control=1). The audio control mixer that thinkpad-acpi interacts with is fully functional without any drivers, and operated by hotkeys. The idea behind the console audio control is that the human operator is the only one that can interact with it. The ThinkVantage suite in Windows does not allow any software-based overrides, and only does OSD (on-screen-display) functions. The Linux driver will, with the addition of the ALSA interface, try to follow and enforce the ThinkVantage UI design: The user is supposed to use the keyboard hotkeys to interact with the console audio control. The kernel and the desktop environment is supposed to cooperate to provide proper user feedback through on-screen-display functions. Distros are urged to not to enable volume control by default. Enabling this must be a local admin's decision. This is the reason why there is no Kconfig option. Keep in mind that all ThinkPads have a normal, main mixer (AC97 or HDA) for regular software-based audio control. We are not talking about that mixer here. Advanced users are, of course, free to enable volume control and do as they please. Signed-off-by: Henrique de Moraes Holschuh Cc: Lorne Applebaum Cc: Matthew Garrett Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 13 +++++++ drivers/platform/x86/thinkpad_acpi.c | 47 ++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index bd87682103c..6a5814330e5 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1097,6 +1097,18 @@ Volume control procfs: /proc/acpi/ibm/volume +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. + +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: @@ -1465,3 +1477,4 @@ Sysfs interface changelog: 0x020600: Marker for backlight change event support. 0x020700: Support for mute-only mixers. + Volume control in read-only mode by default. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 4d909d5a034..2d74926913d 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -311,6 +311,7 @@ static struct { static struct { u16 hotkey_mask_ff:1; + u16 volume_ctrl_forbidden:1; } tp_warned; struct thinkpad_id_data { @@ -6434,6 +6435,7 @@ static enum tpacpi_volume_access_mode volume_mode = TPACPI_VOL_MODE_MAX; static enum tpacpi_volume_capabilities volume_capabilities; +static int volume_control_allowed; /* * Used to syncronize writers to TP_EC_AUDIO and @@ -6449,6 +6451,8 @@ static void tpacpi_volume_checkpoint_nvram(void) 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"); @@ -6691,6 +6695,12 @@ static int __init volume_init(struct ibm_init_struct *iibm) "mute is supported, volume control is %s\n", str_supported(!tp_features.mixer_no_level_control)); + printk(TPACPI_INFO + "Console audio control enabled, mode: %s\n", + (volume_control_allowed) ? + "override (read/write)" : + "monitor (read only)"); + return 0; } @@ -6711,11 +6721,16 @@ static int volume_read(char *p) len += sprintf(p + len, "mute:\t\t%s\n", onoff(status, TP_EC_AUDIO_MUTESW)); - len += sprintf(p + len, "commands:\tunmute, mute\n"); - if (!tp_features.mixer_no_level_control) { - len += sprintf(p + len, "commands:\tup, down\n"); - len += sprintf(p + len, "commands:\tlevel " - " ( is 0-%d)\n", TP_EC_VOLUME_MAX); + if (volume_control_allowed) { + len += sprintf(p + len, "commands:\tunmute, mute\n"); + if (!tp_features.mixer_no_level_control) { + len += sprintf(p + len, + "commands:\tup, down\n"); + len += sprintf(p + len, + "commands:\tlevel " + " ( is 0-%d)\n", + TP_EC_VOLUME_MAX); + } } } @@ -6730,6 +6745,23 @@ static int volume_write(char *buf) 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; @@ -8515,6 +8547,11 @@ 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"); + #define TPACPI_PARAM(feature) \ module_param_call(feature, set_ibm_param, NULL, NULL, 0); \ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \ From 0d204c34e85d1d63e5fdd3e3192747daf0ee7ec1 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:11 -0200 Subject: [PATCH 088/117] thinkpad-acpi: basic ALSA mixer support (v2) Add the basic ALSA mixer functionality. The mixer is event-driven, and will work fine on IBM ThinkPads. I expect Lenovo ThinkPads will cause some trouble with the event interface. Heavily based on work by Lorne Applebaum and ideas from Matthew Garrett . Signed-off-by: Henrique de Moraes Holschuh Cc: Lorne Applebaum Cc: Matthew Garrett Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 7 +- drivers/platform/x86/thinkpad_acpi.c | 237 +++++++++++++++++++++++- 2 files changed, 239 insertions(+), 5 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 6a5814330e5..b4ed30cb137 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1096,6 +1096,7 @@ 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. @@ -1144,9 +1145,8 @@ 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 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. +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 @@ -1478,3 +1478,4 @@ Sysfs interface changelog: 0x020700: Support for mute-only mixers. Volume control in read-only mode by default. + Marker for ALSA mixer support. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 2d74926913d..e0fbe73b8df 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -76,6 +76,10 @@ #include #include +#include +#include +#include + #include #include @@ -6402,6 +6406,22 @@ static struct ibm_struct brightness_driver_data = { * 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, @@ -6584,11 +6604,104 @@ static int volume_set_volume(const u8 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(); @@ -6596,9 +6709,87 @@ static void volume_shutdown(void) 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; + } + + 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; + + snd_card_set_dev(card, &tpacpi_pdev->dev); + rc = snd_card_register(card); + +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 */ @@ -6628,6 +6819,7 @@ static const struct tpacpi_quirk volume_quirk_table[] __initconst = { static int __init volume_init(struct ibm_init_struct *iibm) { unsigned long quirks; + int rc; vdbg_printk(TPACPI_DBG_INIT, "initializing volume subdriver\n"); @@ -6651,6 +6843,17 @@ static int __init volume_init(struct ibm_init_struct *iibm) 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)); @@ -6695,12 +6898,26 @@ static int __init volume_init(struct ibm_init_struct *iibm) "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; } @@ -6807,6 +7024,7 @@ static int volume_write(char *buf) new_mute ? "" : "un", new_level); rc = volume_set_status(new_mute | new_level); } + volume_alsa_notify_change(); return (rc == -EINTR) ? -ERESTARTSYS : rc; } @@ -6817,6 +7035,7 @@ static struct ibm_struct volume_driver_data = { .write = volume_write, .exit = volume_exit, .suspend = volume_suspend, + .resume = volume_resume, .shutdown = volume_shutdown, }; @@ -8115,10 +8334,16 @@ static void tpacpi_driver_event(const unsigned int hkey_event) 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); @@ -8552,6 +8777,14 @@ 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 " \ From 887965e6576a78f71b9b98dec43fd1c73becd2e8 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 15 Dec 2009 21:51:12 -0200 Subject: [PATCH 089/117] thinkpad-acpi: convert to seq_file Signed-off-by: Alexey Dobriyan Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 289 +++++++++++++-------------- 1 file changed, 134 insertions(+), 155 deletions(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e0fbe73b8df..c21c35e2dcc 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -61,6 +61,7 @@ #include #include +#include #include #include #include @@ -261,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); @@ -789,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; - - 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; + return ibm->read(m); } -static int dispatch_procfs_write(struct file *file, - const char __user *userbuf, - unsigned long count, void *data) +static int dispatch_proc_open(struct inode *inode, struct file *file) { - struct ibm_struct *ibm = data; + return single_open(file, dispatch_proc_show, PDE(inode)->data); +} + +static ssize_t dispatch_proc_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *pos) +{ + struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; char *kernbuf; int ret; @@ -847,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; @@ -1401,12 +1400,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; @@ -1420,13 +1417,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) @@ -1904,14 +1901,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 = { @@ -3759,14 +3753,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)) @@ -3778,17 +3771,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, \n"); + seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); + seq_printf(m, "commands:\tenable, disable, reset, \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) @@ -4054,9 +4046,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) @@ -4244,9 +4236,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) @@ -4621,14 +4613,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(); @@ -4639,20 +4630,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) @@ -4844,25 +4835,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) @@ -4940,20 +4930,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 ( is 0-21)\n"); + seq_printf(m, "status:\t\tsupported\n"); + seq_printf(m, "commands:\t ( is 0-21)\n"); } - return len; + return 0; } static int cmos_write(char *buf) @@ -5328,15 +5316,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 */ @@ -5345,15 +5331,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" " on, off, blink ( is 0-15)\n"); - return len; + return 0; } static int led_write(char *buf) @@ -5426,18 +5412,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 ( is 0-17)\n"); + seq_printf(m, "status:\t\tsupported\n"); + seq_printf(m, "commands:\t ( is 0-17)\n"); } - return len; + return 0; } static int beep_write(char *buf) @@ -5798,9 +5782,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; @@ -5808,16 +5791,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 = { @@ -5832,39 +5815,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 0x" + seq_printf(m, "commands:\t0x 0x" " ( is 00-ff, is 00-ff)\n"); - len += sprintf(p + len, "commands:\t0x " + seq_printf(m, "commands:\t0x " " ( is 00-ff, is 0-255)\n"); #endif - return len; + return 0; } static int ecdump_write(char *buf) @@ -6317,23 +6299,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 " + seq_printf(m, "level:\t\t%d\n", level); + seq_printf(m, "commands:\tup, down\n"); + seq_printf(m, "commands:\tlevel " " ( is 0-%d)\n", (tp_features.bright_16levels) ? 15 : 7); } - return len; + return 0; } static int brightness_write(char *buf) @@ -6921,29 +6902,28 @@ static int __init volume_init(struct ibm_init_struct *iibm) return 0; } -static int volume_read(char *p) +static int volume_read(struct seq_file *m) { - int len = 0; u8 status; if (volume_get_status(&status) < 0) { - len += sprintf(p + len, "level:\t\tunreadable\n"); + seq_printf(m, "level:\t\tunreadable\n"); } else { if (tp_features.mixer_no_level_control) - len += sprintf(p + len, "level:\t\tunsupported\n"); + seq_printf(m, "level:\t\tunsupported\n"); else - len += sprintf(p + len, "level:\t\t%d\n", + seq_printf(m, "level:\t\t%d\n", status & TP_EC_AUDIO_LVL_MSK); - len += sprintf(p + len, "mute:\t\t%s\n", + seq_printf(m, "mute:\t\t%s\n", onoff(status, TP_EC_AUDIO_MUTESW)); if (volume_control_allowed) { - len += sprintf(p + len, "commands:\tunmute, mute\n"); + seq_printf(m, "commands:\tunmute, mute\n"); if (!tp_features.mixer_no_level_control) { - len += sprintf(p + len, + seq_printf(m, "commands:\tup, down\n"); - len += sprintf(p + len, + seq_printf(m, "commands:\tlevel " " ( is 0-%d)\n", TP_EC_VOLUME_MAX); @@ -6951,7 +6931,7 @@ static int volume_read(char *p) } } - return len; + return 0; } static int volume_write(char *buf) @@ -8113,9 +8093,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; @@ -8127,7 +8106,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; @@ -8138,54 +8117,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 "); + seq_printf(m, "commands:\tlevel "); switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: - len += sprintf(p + len, " ( is 0-7)\n"); + seq_printf(m, " ( is 0-7)\n"); break; default: - len += sprintf(p + len, " ( is 0-7, " + seq_printf(m, " ( 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 ( " "is 0 (off), 1-120 (seconds))\n"); if (fan_control_commands & TPACPI_FAN_CMD_SPEED) - len += sprintf(p + len, "commands:\tspeed " + seq_printf(m, "commands:\tspeed " " ( is 0-65535)\n"); - return len; + return 0; } static int fan_write_cmd_level(const char *cmd, int *rc) @@ -8472,19 +8451,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; } From 5d2eb14d36723eba0b31ae208bc346835751e944 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Tue, 15 Dec 2009 21:51:13 -0200 Subject: [PATCH 090/117] thinkpad-acpi: bump version to 0.24 Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 4 ++-- drivers/platform/x86/thinkpad_acpi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index b4ed30cb137..169091f75e6 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 Henrique de Moraes Holschuh diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c21c35e2dcc..af2352517c9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -21,7 +21,7 @@ * 02110-1301, USA. */ -#define TPACPI_VERSION "0.23" +#define TPACPI_VERSION "0.24" #define TPACPI_SYSFS_VERSION 0x020700 /* From 13c41157096f05d5037a99152b4cf9d68ef00ef7 Mon Sep 17 00:00:00 2001 From: Mike Travis Date: Mon, 14 Dec 2009 13:38:30 -0800 Subject: [PATCH 091/117] ACPI: Remove repeated registered as cooling_device messages This message shows up for each cpu. Print as debug messages. [ 12.893967] processor ACPI0007:00: registered as cooling_device0 [ 12.907838] processor ACPI0007:01: registered as cooling_device1 Signed-off-by: Mike Travis Cc: Ingo Molnar Cc: linux-acpi@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Len Brown --- drivers/acpi/fan.c | 2 +- drivers/acpi/processor_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index f419849a0d3..acf2ab24984 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/processor_core.c b/drivers/acpi/processor_core.c index ec742a4e563..ecb2c512204 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -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, From edb949185210365f107ddc5efa60df105d25e1d7 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 15 Dec 2009 22:46:50 +0100 Subject: [PATCH 092/117] thermal: Fix test of unsigned in thermal_cooling_device_cur_state_store() state is unsigned long so the test did not work. Signed-off-by: Roel Kluin Acked-by: Zhang Rui Signed-off-by: Len Brown --- drivers/thermal/thermal_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 6f8d8f97121..800e0c56cc8 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -374,7 +374,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); From 2345baf4a8a0ac2e7e83f932048bb2d8ec1287e7 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Sun, 13 Dec 2009 14:42:36 -0800 Subject: [PATCH 093/117] battery: fix typo in comment Signed-off-by: Justin P. Mattock Signed-off-by: Len Brown --- drivers/acpi/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 3f4602b8f28..cada73ffdfa 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 From 13c199c0d0cf78b27592991129fb8cbcfc5164de Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Tue, 15 Dec 2009 22:01:57 +0800 Subject: [PATCH 094/117] ACPI: Use the return result of ACPI lid notifier chain correctly On some laptops it will return NOTIFY_OK(non-zero) when calling the ACPI LID notifier. Then it is used as the result of ACPI LID resume function, which will complain the following warning message in course of suspend/resume: >PM: Device PNP0C0D:00 failed to resume: error 1 This patch is to eliminate the above warning message. http://bugzilla.kernel.org/show_bug.cgi?id=14782 Signed-off-by: Zhao Yakui Signed-off-by: Len Brown --- drivers/acpi/button.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 0c9c6a9a002..8a95e8329df 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; } From b552a8c56db8a74ceffdcbb9269664009a81d536 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 17 Nov 2009 14:27:32 -0800 Subject: [PATCH 095/117] ACPI: remove NID_INVAL NUMA_NO_NODE has been exported globally and thus it can replace NID_INVAL in the acpi code. Also removes the unused acpi_unmap_pxm_to_node() function. [akpm@linux-foundation.org: coding-style fixes] Cc: Cyrill Gorcunov Cc: Lee Schermerhorn Reviewed-by: Christoph Lameter Signed-off-by: David Rientjes Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/numa.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 202dd0c976a..90f1884e8a5 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #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) { From bf2abdd79ac81c52957bf0d0d5430cec5433cfb7 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Tue, 17 Nov 2009 14:27:33 -0800 Subject: [PATCH 096/117] acpi: thermal: display forced passive trip points in proc Users can force a passive trip point for a thermal zone that does not have _PSV defined in ACPI by setting the passive attribute in sysfs. It's useful to display such trip points in /proc/acpi/thermal_zone. .../TZ1/cooling_mode: .../TZ1/polling_frequency:polling frequency: 10 seconds .../TZ1/state:state: ok .../TZ1/temperature:temperature: 53 C .../TZ1/trip_points:critical (S5): 110 C .../TZ1/trip_points:passive (forced): 95 C And if not set (passive is 0): .../TZ1/trip_points:passive (forced): Signed-off-by: Frans Pop Acked-by: Zhang Rui Cc: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/thermal.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 65f67815902..9073ada8883 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, "\n"); } for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { From 360657463679dee44f0b167ffa61f563b4fee101 Mon Sep 17 00:00:00 2001 From: Peter Feuerer Date: Tue, 17 Nov 2009 14:27:37 -0800 Subject: [PATCH 097/117] acerhdf: add new BIOS versions Added new BIOS versions for following netbooks: Acer 1410, Gateway LT31, Packard Bell DOA150. As the Gateway LT31 machines have different register values for setting and checking the off-state, the "cmd_off" variable has been splitted up to "cmd_off" and "chk_off". Signed-off-by: Peter Feuerer Cc: Borislav Petkov Cc: Andreas Mohr Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/platform/x86/acerhdf.c | 63 ++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index ab64522aaa6..d8b4229108b 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; From f69cfdd24ac40ab7bbd00d6ad85e003da755e3a0 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:29 -0600 Subject: [PATCH 098/117] ACPI: dock: combine add|alloc_dock_dependent_device (v2) There's no real need to have a separate allocation step when adding a dock dependent device. Combining the two functions is both logical and helps with legibility. Signed-off-by: Alex Chiang Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 55 ++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 4f2aa98e2ce..ee9829b47e2 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -93,40 +93,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; } /** @@ -826,7 +816,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 +829,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; } @@ -959,7 +946,6 @@ static struct attribute_group dock_attribute_group = { static int dock_add(acpi_handle handle) { int ret; - struct dock_dependent_device *dd; struct dock_station *dock_station; struct platform_device *dock_device; @@ -1008,12 +994,9 @@ static int dock_add(acpi_handle handle) NULL); /* add the dock station as a device dependent on itself */ - dd = alloc_dock_dependent_device(handle); - if (!dd) { - ret = -ENOMEM; + ret = add_dock_dependent_device(dock_station, handle); + if (ret) goto err_rmgroup; - } - add_dock_dependent_device(dock_station, dd); dock_station_count++; list_add(&dock_station->sibling, &dock_stations); From c6f1905ea91bf56a921c6eb574488869c8c3eeff Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:35 -0600 Subject: [PATCH 099/117] ACPI: dock: remove global 'dock_device_name' We only use it in one spot, so it probably gets optimized out, but there's still no need to use a global variable for this. Signed-off-by: Alex Chiang Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index ee9829b47e2..0a1221854a2 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}, @@ -964,7 +963,7 @@ static int dock_add(acpi_handle handle) /* initialize platform device stuff */ dock_station->dock_device = - platform_device_register_simple(dock_device_name, + platform_device_register_simple("dock", dock_station_count, NULL, 0); dock_device = dock_station->dock_device; if (IS_ERR(dock_device)) { From 9751cb721ef650729e981c7b39abb7676cd646e1 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:40 -0600 Subject: [PATCH 100/117] ACPI: dock: dock_add - hoist up platform_device_register_simple() Move the call to platform_device_register_simple so that we do it before allocating and initializing our struct dock_station. Signed-off-by: Alex Chiang Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 0a1221854a2..3bd89823402 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -948,11 +948,18 @@ static int dock_add(acpi_handle handle) struct dock_station *dock_station; struct platform_device *dock_device; + dock_device = + platform_device_register_simple("dock", + dock_station_count, NULL, 0); + if (IS_ERR(dock_device)) + return PTR_ERR(dock_device); + /* 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 = dock_device; dock_station->last_dock_time = jiffies - HZ; INIT_LIST_HEAD(&dock_station->dependent_devices); INIT_LIST_HEAD(&dock_station->hotplug_devices); @@ -961,15 +968,6 @@ static int dock_add(acpi_handle handle) mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); - /* initialize platform device stuff */ - dock_station->dock_device = - platform_device_register_simple("dock", - dock_station_count, NULL, 0); - dock_device = dock_station->dock_device; - if (IS_ERR(dock_device)) { - ret = PTR_ERR(dock_device); - goto out; - } platform_device_add_data(dock_device, &dock_station, sizeof(struct dock_station *)); From fe06fba292af5ed5c1c6ad9af3a9ef68da7a5088 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:45 -0600 Subject: [PATCH 101/117] ACPI: dock: add struct dock_station * directly to platform device data Instead of adding a (struct dock_station **) to our dock device's platform data, we can add the (struct dock_station *) directly. This change saves us some ugly casting and improves readability. The cost of making this change is an extra 290 bytes of stack usage, but this is an infrequently called code-path and unlikely to cause the kernel to blow up. Signed-off-by: Alex Chiang Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 3bd89823402..cfcd8eeb3b2 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -843,8 +843,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"); @@ -858,8 +857,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); } @@ -872,8 +870,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; @@ -891,8 +888,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)) @@ -905,8 +901,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) @@ -944,20 +939,18 @@ static struct attribute_group dock_attribute_group = { */ static int dock_add(acpi_handle handle) { - int ret; - struct dock_station *dock_station; + int ret, id; + struct dock_station ds, *dock_station; struct platform_device *dock_device; + id = dock_station_count; dock_device = - platform_device_register_simple("dock", - dock_station_count, NULL, 0); + platform_device_register_data(NULL, "dock", + id, &ds, sizeof(ds)); if (IS_ERR(dock_device)) return PTR_ERR(dock_device); - /* allocate & initialize the dock_station private data */ - dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); - if (!dock_station) - return -ENOMEM; + dock_station = dock_device->dev.platform_data; dock_station->handle = handle; dock_station->dock_device = dock_device; dock_station->last_dock_time = jiffies - HZ; @@ -968,9 +961,6 @@ static int dock_add(acpi_handle handle) mutex_init(&dock_station->hp_lock); ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); - platform_device_add_data(dock_device, &dock_station, - sizeof(struct dock_station *)); - /* we want the dock device to send uevents */ dev_set_uevent_suppress(&dock_device->dev, 0); @@ -1003,9 +993,6 @@ err_rmgroup: sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group); err_unregister: platform_device_unregister(dock_device); -out: - kfree(dock_station); - dock_station = NULL; printk(KERN_ERR "%s encountered error %d\n", __func__, ret); return ret; } @@ -1026,13 +1013,12 @@ static int dock_remove(struct dock_station *dock_station) list) kfree(dd); + list_del(&dock_station->sibling); + /* cleanup sysfs */ 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; } From 747479a3fb42849fe73b9b1f7545d751bde7d632 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 19 Oct 2009 15:14:50 -0600 Subject: [PATCH 102/117] ACPI: dock: minor whitespace and style cleanups Removed some stray whitespaces Added whitespace when needed for legibility Removed unneeded curly braces Removed useless void casts Removed unnecessary local variable initialization Renamed variables to help out with 80-column fixes Signed-off-by: Alex Chiang Acked-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/dock.c | 103 +++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index cfcd8eeb3b2..d4c3d82bef3 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -238,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)) @@ -264,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); /** @@ -294,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 @@ -309,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; @@ -326,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; } } @@ -353,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 @@ -373,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 @@ -415,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); } @@ -445,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"); } @@ -566,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); /** @@ -580,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); /** @@ -625,7 +619,6 @@ register_hotplug_dock_device(acpi_handle handle, struct acpi_dock_ops *ops, return ret; } - EXPORT_SYMBOL_GPL(register_hotplug_dock_device); /** @@ -646,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); /** @@ -761,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); @@ -771,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 ; } } @@ -941,28 +932,28 @@ static int dock_add(acpi_handle handle) { int ret, id; struct dock_station ds, *dock_station; - struct platform_device *dock_device; + struct platform_device *dd; id = dock_station_count; - dock_device = - platform_device_register_data(NULL, "dock", - id, &ds, sizeof(ds)); - if (IS_ERR(dock_device)) - return PTR_ERR(dock_device); + 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; - dock_station = dock_device->dev.platform_data; dock_station->handle = handle; - dock_station->dock_device = dock_device; + 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); + 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; @@ -971,14 +962,13 @@ static int dock_add(acpi_handle handle) if (is_battery(handle)) dock_station->flags |= DOCK_IS_BAT; - ret = sysfs_create_group(&dock_device->dev.kobj, &dock_attribute_group); + ret = sysfs_create_group(&dd->dev.kobj, &dock_attribute_group); if (ret) goto err_unregister; /* Find dependent devices */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_devices, dock_station, - NULL); + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + find_dock_devices, dock_station, NULL); /* add the dock station as a device dependent on itself */ ret = add_dock_dependent_device(dock_station, handle); @@ -990,9 +980,9 @@ static int dock_add(acpi_handle handle) return 0; err_rmgroup: - sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group); + sysfs_remove_group(&dd->dev.kobj, &dock_attribute_group); err_unregister: - platform_device_unregister(dock_device); + platform_device_unregister(dd); printk(KERN_ERR "%s encountered error %d\n", __func__, ret); return ret; } @@ -1000,20 +990,19 @@ err_unregister: /** * 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(&dock_station->sibling); + list_del(&ds->sibling); /* cleanup sysfs */ sysfs_remove_group(&dock_device->dev.kobj, &dock_attribute_group); @@ -1036,11 +1025,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; } @@ -1078,8 +1066,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) From f67538f81e6b8da9175c82807d649fbdb0055844 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 13 Nov 2009 19:55:30 -0800 Subject: [PATCH 103/117] acpi_pad: squish warning drivers/acpi/acpi_pad.c: In function 'power_saving_thread': drivers/acpi/acpi_pad.c:103: warning: 'preferred_cpu' may be used uninitialized in this function Cc: Shaohua Li Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/acpi_pad.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index 0d2cdb86158..97991ac6f5f 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; From 918aae42aa9b611a3663b16ae849fdedc67c2292 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 14 Dec 2009 17:10:06 +0900 Subject: [PATCH 104/117] ACPI: fix for lapic_timer_propagate_broadcast() I got following warning on ia64 box: In function 'acpi_processor_power_verify': 642: warning: passing argument 2 of 'smp_call_function_single' from incompatible pointer type This smp_call_function_single() was introduced by a commit f833bab87fca5c3ce13778421b1365845843b976: > @@ -162,8 +162,9 @@ > pr->power.timer_broadcast_on_state = state; > } > > -static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) > +static void lapic_timer_propagate_broadcast(void *arg) > { > + struct acpi_processor *pr = (struct acpi_processor *) arg; > unsigned long reason; > > reason = pr->power.timer_broadcast_on_state < INT_MAX ? > @@ -635,7 +636,8 @@ > working++; > } > > - lapic_timer_propagate_broadcast(pr); > + smp_call_function_single(pr->id, lapic_timer_propagate_broadcast, > + pr, 1); > > return (working); > } The problem is that the lapic_timer_propagate_broadcast() has 2 versions: One is real code that modified in the above commit, and the other is NOP code that used when !ARCH_APICTIMER_STOPS_ON_C3: static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } So I got warning because of !ARCH_APICTIMER_STOPS_ON_C3. We really want to do nothing here on !ARCH_APICTIMER_STOPS_ON_C3, so modify lapic_timer_propagate_broadcast() of real version to use smp_call_function_single() in it. Signed-off-by: Hidetoshi Seto Acked-by: Suresh Siddha Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index bbd066e7f85..d1676b1754d 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); } From 42b4e9ee3d1d3b691bcae37f217f08740320c58c Mon Sep 17 00:00:00 2001 From: Jes Sorensen Date: Wed, 16 Dec 2009 12:08:15 -0500 Subject: [PATCH 105/117] Toshiba Bluetooth Enabling driver (RFKill handler v3) This patch adds support for the ACPI events generated by the RFKill switch on modern Toshiba laptops, and re-enables the Bluetooth USB device when the switch is flipped back to the 'on' position. The RFKill switch brute force pulls out the USB device when flipped to 'off', but it doesn't automatically re-enable it. Without this driver, the Bluetooth is gone until after a reboot on my Portege R500. Signed-off-by: Jes Sorensen Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 15 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/toshiba_bluetooth.c | 144 +++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 drivers/platform/x86/toshiba_bluetooth.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39dea42..8dd2b3a4c47 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -435,4 +435,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 d1c16210a51..394258bd77c 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -21,3 +21,4 @@ obj-$(CONFIG_ACPI_WMI) += 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/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c new file mode 100644 index 00000000000..a350418e87e --- /dev/null +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -0,0 +1,144 @@ +/* + * Toshiba Bluetooth Enable Driver + * + * Copyright (C) 2009 Jes Sorensen + * + * 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 +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jes Sorensen "); +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); From d12d8baff927a31b7e13b72ed9549be6f296a6ef Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 10 Dec 2009 14:18:13 +0100 Subject: [PATCH 106/117] X86 drivers: Introduce msi-wmi driver This driver serves backlight (including switching) and volume up/down keys for MSI machines providing a specific wmi interface: 551A1F84-FBDD-4125-91DB-3EA8F44F1D45 B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2 Signed-off-by: Thomas Renninger CC: Carlos Corbacho CC: Matthew Garrett Tested-by: Matt Chen Reviewed-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 10 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/msi-wmi.c | 369 +++++++++++++++++++++++++++++++++ 3 files changed, 380 insertions(+) create mode 100644 drivers/platform/x86/msi-wmi.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 55ca39dea42..98ec6bd9226 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -365,6 +365,16 @@ 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 + 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 diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d1c16210a51..13aa37310f3 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -18,6 +18,7 @@ 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 diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c new file mode 100644 index 00000000000..7e0dab65975 --- /dev/null +++ b/drivers/platform/x86/msi-wmi.c @@ -0,0 +1,369 @@ +/* + * MSI WMI hotkeys + * + * Copyright (C) 2009 Novell + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Thomas Renninger "); +MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Set this to 1 to let the driver be more verbose"); + +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...) do { \ + if (debug) \ + printk(KERN_INFO DRV_PFX msg); \ + } while (0) + +struct key_entry { + char type; /* See KE_* below */ + u16 code; + u16 keycode; + int instance; + ktime_t last_pressed; +}; + +/* + * KE_KEY the only used key type, but keep this, others might also + * show up in the future. Compare with hp-wmi.c + */ +enum { KE_KEY, KE_END }; + +static struct key_entry msi_wmi_keymap[] = { + { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, 0, {0, } }, + { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, 1, {0, } }, + { KE_KEY, 0xd2, KEY_VOLUMEUP, 2, {0, } }, + { KE_KEY, 0xd3, KEY_VOLUMEDOWN, 3, {0, } }, + { KE_END, 0} +}; + +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 = 0; + + /* 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); + 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 struct key_entry *msi_wmi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = msi_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *msi_wmi_get_entry_by_keycode(int keycode) +{ + struct key_entry *key; + + for (key = msi_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int msi_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) +{ + struct key_entry *key = msi_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int msi_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = msi_wmi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!msi_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +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 = msi_wmi_get_entry_by_scancode(eventcode); + if (key) { + cur = ktime_get_real(); + /* Ignore event if the same event happened in a 50 ms + timeframe -> Key press may result in 10-20 GPEs */ + if (ktime_to_us(ktime_sub(cur, key->last_pressed)) + < 1000 * 50) { + dprintk("Suppressed key event 0x%X - " + "Last press was %lld us ago\n", + key->code, + ktime_to_us(ktime_sub(cur, + key->last_pressed))); + return; + } + key->last_pressed = cur; + + switch (key->type) { + case KE_KEY: + /* Brightness is served via acpi video driver */ + if (!backlight && + (key->keycode == KEY_BRIGHTNESSUP || + key->keycode == KEY_BRIGHTNESSDOWN)) + break; + + dprintk("Send key: 0x%X - " + "Input layer keycode: %d\n", key->code, + key->keycode); + input_report_key(msi_wmi_input_dev, + key->keycode, 1); + input_sync(msi_wmi_input_dev); + input_report_key(msi_wmi_input_dev, + key->keycode, 0); + input_sync(msi_wmi_input_dev); + break; + } + } 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) +{ + struct key_entry *key; + int err; + + msi_wmi_input_dev = input_allocate_device(); + + msi_wmi_input_dev->name = "MSI WMI hotkeys"; + msi_wmi_input_dev->phys = "wmi/input0"; + msi_wmi_input_dev->id.bustype = BUS_HOST; + msi_wmi_input_dev->getkeycode = msi_wmi_getkeycode; + msi_wmi_input_dev->setkeycode = msi_wmi_setkeycode; + + for (key = msi_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, msi_wmi_input_dev->evbit); + set_bit(key->keycode, msi_wmi_input_dev->keybit); + break; + } + } + + err = input_register_device(msi_wmi_input_dev); + + if (err) { + input_free_device(msi_wmi_input_dev); + return err; + } + + return 0; +} + +static int __init msi_wmi_init(void) +{ + int err; + + if (wmi_has_guid(MSIWMI_EVENT_GUID)) { + err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, + msi_wmi_notify, NULL); + if (err) + return -EINVAL; + + err = msi_wmi_input_setup(); + if (err) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + return -EINVAL; + } + + if (!acpi_video_backlight_support()) { + backlight = backlight_device_register(DRV_NAME, + NULL, NULL, &msi_backlight_ops); + if (IS_ERR(backlight)) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + input_unregister_device(msi_wmi_input_dev); + return -EINVAL; + } + + backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1; + err = bl_get(NULL); + if (err < 0) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + input_unregister_device(msi_wmi_input_dev); + backlight_device_unregister(backlight); + return -EINVAL; + } + backlight->props.brightness = err; + } + } + printk(KERN_INFO DRV_PFX "Event handler installed\n"); + return 0; +} + +static void __exit msi_wmi_exit(void) +{ + if (wmi_has_guid(MSIWMI_EVENT_GUID)) { + wmi_remove_notify_handler(MSIWMI_EVENT_GUID); + input_unregister_device(msi_wmi_input_dev); + backlight_device_unregister(backlight); + } +} + +module_init(msi_wmi_init); +module_exit(msi_wmi_exit); From addd65aac7bcfed7348048b3ce24774718fc44c3 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:14 +0100 Subject: [PATCH 107/117] msi-wmi: remove useless includes Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/msi-wmi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 7e0dab65975..fb988d8f426 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -23,14 +23,8 @@ #include -#include -#include -#include #include -#include #include -#include -#include #include MODULE_AUTHOR("Thomas Renninger "); From 46b51eb9e14afb3bde4bc2fe3bbc22ce012647d4 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:15 +0100 Subject: [PATCH 108/117] msi-wmi: rework init There should be less code duplication with usage of gotos Driver won't load if there's no hardware to control Safer error handling at input driver allocation Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/msi-wmi.c | 62 +++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index fb988d8f426..dcb048c0793 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -284,6 +284,8 @@ 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"; @@ -314,40 +316,44 @@ static int __init msi_wmi_init(void) { int err; - if (wmi_has_guid(MSIWMI_EVENT_GUID)) { - err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, - msi_wmi_notify, NULL); - if (err) - return -EINVAL; + 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) { - wmi_remove_notify_handler(MSIWMI_EVENT_GUID); - 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)) { - wmi_remove_notify_handler(MSIWMI_EVENT_GUID); - input_unregister_device(msi_wmi_input_dev); - return -EINVAL; - } + 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) { - wmi_remove_notify_handler(MSIWMI_EVENT_GUID); - input_unregister_device(msi_wmi_input_dev); - backlight_device_unregister(backlight); - return -EINVAL; - } - backlight->props.brightness = err; - } + backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1; + err = bl_get(NULL); + if (err < 0) + goto err_free_backlight; + + backlight->props.brightness = err; } printk(KERN_INFO DRV_PFX "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) From 822ddc042a12aa2a8c2030ad4ebc660bc0e66c3f Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:16 +0100 Subject: [PATCH 109/117] msi-wmi: remove custom runtime debug implementation Rely on DYNAMIC_DEBUG instead if needed Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/msi-wmi.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index dcb048c0793..75e422b2753 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -31,10 +31,6 @@ MODULE_AUTHOR("Thomas Renninger "); MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); MODULE_LICENSE("GPL"); -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Set this to 1 to let the driver be more verbose"); - MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); @@ -54,10 +50,7 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*"); #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" -#define dprintk(msg...) do { \ - if (debug) \ - printk(KERN_INFO DRV_PFX msg); \ - } while (0) +#define dprintk(msg...) pr_debug(DRV_PFX msg) struct key_entry { char type; /* See KE_* below */ @@ -343,7 +336,7 @@ static int __init msi_wmi_init(void) backlight->props.brightness = err; } - printk(KERN_INFO DRV_PFX "Event handler installed\n"); + dprintk("Event handler installed\n"); return 0; From 977f9b921c82726745a8b7281dc679edb32b4906 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:17 +0100 Subject: [PATCH 110/117] msi-wmi: remove unused field 'instance' in key_entry structure Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/msi-wmi.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 75e422b2753..d7579359a2c 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -56,7 +56,6 @@ struct key_entry { char type; /* See KE_* below */ u16 code; u16 keycode; - int instance; ktime_t last_pressed; }; @@ -67,10 +66,10 @@ struct key_entry { enum { KE_KEY, KE_END }; static struct key_entry msi_wmi_keymap[] = { - { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, 0, {0, } }, - { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, 1, {0, } }, - { KE_KEY, 0xd2, KEY_VOLUMEUP, 2, {0, } }, - { KE_KEY, 0xd3, KEY_VOLUMEDOWN, 3, {0, } }, + { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, {0, } }, + { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, {0, } }, + { KE_KEY, 0xd2, KEY_VOLUMEUP, {0, } }, + { KE_KEY, 0xd3, KEY_VOLUMEDOWN, {0, } }, { KE_END, 0} }; From d607af93006594f7da1d4b7d44724c5308f4e892 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:18 +0100 Subject: [PATCH 111/117] msi-wmi: replace one-condition switch-case with if statement Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/msi-wmi.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index d7579359a2c..2c2afc1828b 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -243,14 +243,10 @@ static void msi_wmi_notify(u32 value, void *context) } key->last_pressed = cur; - switch (key->type) { - case KE_KEY: - /* Brightness is served via acpi video driver */ - if (!backlight && - (key->keycode == KEY_BRIGHTNESSUP || - key->keycode == KEY_BRIGHTNESSDOWN)) - break; - + if (key->type == KE_KEY && + /* Brightness is served via acpi video driver */ + (backlight || (key->keycode != KEY_BRIGHTNESSUP && + key->keycode != KEY_BRIGHTNESSDOWN))) { dprintk("Send key: 0x%X - " "Input layer keycode: %d\n", key->code, key->keycode); @@ -260,7 +256,6 @@ static void msi_wmi_notify(u32 value, void *context) input_report_key(msi_wmi_input_dev, key->keycode, 0); input_sync(msi_wmi_input_dev); - break; } } else printk(KERN_INFO "Unknown key pressed - %x\n", From c30116c6f0d26cd6e46dfa578163d573ef4730b2 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Thu, 10 Dec 2009 14:18:19 +0100 Subject: [PATCH 112/117] msi-wmi: switch to using input sparse keymap library Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/msi-wmi.c | 132 ++++++++------------------------- 2 files changed, 32 insertions(+), 101 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 98ec6bd9226..1f82d6df96e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -369,6 +369,7 @@ config MSI_WMI tristate "MSI WMI extras" depends on ACPI_WMI depends on INPUT + select INPUT_SPARSEKMAP help Say Y here if you want to support WMI-based hotkeys on MSI laptops. diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 2c2afc1828b..e25b80c530f 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -21,9 +21,9 @@ */ - #include #include +#include #include #include @@ -52,26 +52,15 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*"); #define dprintk(msg...) pr_debug(DRV_PFX msg) -struct key_entry { - char type; /* See KE_* below */ - u16 code; - u16 keycode; - ktime_t last_pressed; -}; - -/* - * KE_KEY the only used key type, but keep this, others might also - * show up in the future. Compare with hp-wmi.c - */ -enum { KE_KEY, KE_END }; - +#define KEYCODE_BASE 0xD0 static struct key_entry msi_wmi_keymap[] = { - { KE_KEY, 0xd0, KEY_BRIGHTNESSUP, {0, } }, - { KE_KEY, 0xd1, KEY_BRIGHTNESSDOWN, {0, } }, - { KE_KEY, 0xd2, KEY_VOLUMEUP, {0, } }, - { KE_KEY, 0xd3, KEY_VOLUMEDOWN, {0, } }, + { KE_KEY, KEYCODE_BASE, {KEY_BRIGHTNESSUP} }, + { KE_KEY, KEYCODE_BASE + 1, {KEY_BRIGHTNESSDOWN} }, + { KE_KEY, KEYCODE_BASE + 2, {KEY_VOLUMEUP} }, + { KE_KEY, KEYCODE_BASE + 3, {KEY_VOLUMEDOWN} }, { KE_END, 0} }; +static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; struct backlight_device *backlight; @@ -158,61 +147,6 @@ static struct backlight_ops msi_backlight_ops = { .update_status = bl_set_status, }; -static struct key_entry *msi_wmi_get_entry_by_scancode(int code) -{ - struct key_entry *key; - - for (key = msi_wmi_keymap; key->type != KE_END; key++) - if (code == key->code) - return key; - - return NULL; -} - -static struct key_entry *msi_wmi_get_entry_by_keycode(int keycode) -{ - struct key_entry *key; - - for (key = msi_wmi_keymap; key->type != KE_END; key++) - if (key->type == KE_KEY && keycode == key->keycode) - return key; - - return NULL; -} - -static int msi_wmi_getkeycode(struct input_dev *dev, int scancode, int *keycode) -{ - struct key_entry *key = msi_wmi_get_entry_by_scancode(scancode); - - if (key && key->type == KE_KEY) { - *keycode = key->keycode; - return 0; - } - - return -EINVAL; -} - -static int msi_wmi_setkeycode(struct input_dev *dev, int scancode, int keycode) -{ - struct key_entry *key; - int old_keycode; - - if (keycode < 0 || keycode > KEY_MAX) - return -EINVAL; - - key = msi_wmi_get_entry_by_scancode(scancode); - if (key && key->type == KE_KEY) { - old_keycode = key->keycode; - key->keycode = keycode; - set_bit(keycode, dev->keybit); - if (!msi_wmi_get_entry_by_keycode(old_keycode)) - clear_bit(old_keycode, dev->keybit); - return 0; - } - - return -EINVAL; -} - static void msi_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -227,21 +161,22 @@ static void msi_wmi_notify(u32 value, void *context) if (obj && obj->type == ACPI_TYPE_INTEGER) { int eventcode = obj->integer.value; dprintk("Eventcode: 0x%x\n", eventcode); - key = msi_wmi_get_entry_by_scancode(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(ktime_sub(cur, key->last_pressed)) - < 1000 * 50) { + 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(ktime_sub(cur, - key->last_pressed))); + key->code, ktime_to_us(diff)); return; } - key->last_pressed = cur; + last_pressed[key->code - KEYCODE_BASE] = cur; if (key->type == KE_KEY && /* Brightness is served via acpi video driver */ @@ -250,12 +185,8 @@ static void msi_wmi_notify(u32 value, void *context) dprintk("Send key: 0x%X - " "Input layer keycode: %d\n", key->code, key->keycode); - input_report_key(msi_wmi_input_dev, - key->keycode, 1); - input_sync(msi_wmi_input_dev); - input_report_key(msi_wmi_input_dev, - key->keycode, 0); - input_sync(msi_wmi_input_dev); + sparse_keymap_report_entry(msi_wmi_input_dev, + key, 1, true); } } else printk(KERN_INFO "Unknown key pressed - %x\n", @@ -267,7 +198,6 @@ static void msi_wmi_notify(u32 value, void *context) static int __init msi_wmi_input_setup(void) { - struct key_entry *key; int err; msi_wmi_input_dev = input_allocate_device(); @@ -277,26 +207,25 @@ static int __init msi_wmi_input_setup(void) msi_wmi_input_dev->name = "MSI WMI hotkeys"; msi_wmi_input_dev->phys = "wmi/input0"; msi_wmi_input_dev->id.bustype = BUS_HOST; - msi_wmi_input_dev->getkeycode = msi_wmi_getkeycode; - msi_wmi_input_dev->setkeycode = msi_wmi_setkeycode; - for (key = msi_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_KEY: - set_bit(EV_KEY, msi_wmi_input_dev->evbit); - set_bit(key->keycode, msi_wmi_input_dev->keybit); - break; - } - } + 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) { - input_free_device(msi_wmi_input_dev); - return err; - } + 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) @@ -347,6 +276,7 @@ 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); } From de078e5747fa3a95efac04fd6725dcceb4520416 Mon Sep 17 00:00:00 2001 From: Anisse Astier Date: Mon, 14 Dec 2009 10:21:39 +0100 Subject: [PATCH 113/117] msi-wmi: depend on backlight and fix corner-cases problems Now depends on BACKLIGHT_CLASS_DEVICE. Driver will return an error if it can't get actual backlight value Fix remapping of brightness keys when backlight is not controlled by ACPI. Signed-off-by: Anisse Astier Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/msi-wmi.c | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1f82d6df96e..a006dec3b3e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -369,6 +369,7 @@ 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. diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index e25b80c530f..0c8fe145c4a 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -53,11 +53,15 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*"); #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, KEYCODE_BASE, {KEY_BRIGHTNESSUP} }, - { KE_KEY, KEYCODE_BASE + 1, {KEY_BRIGHTNESSDOWN} }, - { KE_KEY, KEYCODE_BASE + 2, {KEY_VOLUMEUP} }, - { KE_KEY, KEYCODE_BASE + 3, {KEY_VOLUMEDOWN} }, + { 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]; @@ -110,12 +114,14 @@ static int msi_wmi_set_block(int instance, int value) static int bl_get(struct backlight_device *bd) { - int level, err, ret = 0; + int level, err, ret; /* Instance 1 is "get backlight", cmp with DSDT */ err = msi_wmi_query_block(1, &ret); - if (err) + 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) { @@ -180,8 +186,9 @@ static void msi_wmi_notify(u32 value, void *context) if (key->type == KE_KEY && /* Brightness is served via acpi video driver */ - (backlight || (key->keycode != KEY_BRIGHTNESSUP && - key->keycode != KEY_BRIGHTNESSDOWN))) { + (!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); From 70023de88c58a81a730ab4d13c51a30e537ec76e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 29 Oct 2009 11:04:28 +0800 Subject: [PATCH 114/117] ACPI: Add a generic API for _OSC -v2 v2->v1: .improve debug info as suggedted by Bjorn,Kenji .API is using uuid string as suggested by Alexey Add an API to execute _OSC. A lot of devices can have this method, so add a generic API. Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/bus.c | 122 +++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 9 ++++ 2 files changed, 131 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 74119152435..12240be58f2 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -344,6 +344,128 @@ 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); + /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index dfcd920c3e5..3247e09db20 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -253,6 +253,13 @@ 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 @@ -265,6 +272,8 @@ 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); + /* _OSC DW1 Definition (OS Support Fields) */ #define OSC_EXT_PCI_CONFIG_SUPPORT 1 #define OSC_ACTIVE_STATE_PWR_SUPPORT 2 From 3a9622dc4659af44a8098a233f65c51e495ff0a5 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 29 Oct 2009 11:04:50 +0800 Subject: [PATCH 115/117] ACPI: cleanup pci_root _OSC code. Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/pci_root.c | 76 ++++++++--------------------------------- include/linux/acpi.h | 5 +-- 2 files changed, 17 insertions(+), 64 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1af808171d4..101cce3681d 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; + status = acpi_run_osc(handle, &context); + if (ACPI_SUCCESS(status)) { + *retval = *((u32 *)(context.ret.pointer + 8)); + kfree(context.ret.pointer); } - /* 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; - } -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/include/linux/acpi.h b/include/linux/acpi.h index 3247e09db20..535beecc37c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -263,7 +263,6 @@ struct acpi_osc_context { #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 @@ -274,12 +273,14 @@ struct acpi_osc_context { acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); +/* 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 @@ -288,7 +289,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #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 | \ From 3563ff964fdc36358cef0330936fdac28e65142a Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 29 Oct 2009 11:05:05 +0800 Subject: [PATCH 116/117] ACPI: Add platform-wide _OSC support. Signed-off-by: Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/bus.c | 26 ++++++++++++++++++++++++++ include/linux/acpi.h | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 12240be58f2..65f7e335f12 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -466,6 +466,30 @@ out_kfree: } 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 -------------------------------------------------------------------------- */ @@ -856,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/include/linux/acpi.h b/include/linux/acpi.h index 535beecc37c..e11090d462d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -273,6 +273,13 @@ struct acpi_osc_context { 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 From bf8b4542f92c4d8222941b1cab055fa350ab2fb4 Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Mon, 26 Oct 2009 17:44:18 +0100 Subject: [PATCH 117/117] ACPI processor: Fix section mismatch for processor_add() Due to the merge of processor_start() (declared with __cpuinit) into processor_add(), a section mismatch warning appears: WARNING: drivers/built-in.o(.text+0x4d59d): Section mismatch in reference from the function acpi_processor_add() to the function .cpuinit.text:acpi_processor_power_init() ... This patch fixes the warning by declaring processor_add() as __cpuinit and also declares acpi_processor_add_fs() as __cpuinit as it is only used in acpi_processor_add(). Signed-off-by: Thomas Renninger Signed-off-by: Len Brown --- drivers/acpi/processor_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index ecb2c512204..709b590c2fb 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;