From 6503e5df08008b9a47022b5e9ebba658c8fa69af Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 27 Nov 2008 17:48:13 +0000 Subject: thermal: use integers rather than strings for thermal values The thermal API currently uses strings to pass values to userspace. This makes it difficult to use from within the kernel. Change the interface to use integers and fix up the consumers. Signed-off-by: Matthew Garrett Acked-by: Zhang Rui Acked-by: Thomas Renninger Signed-off-by: Len Brown --- drivers/platform/x86/intel_menlow.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 27b7662955bb..29432a50be45 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -57,8 +57,8 @@ MODULE_LICENSE("GPL"); * In that case max_cstate would be n-1 * GTHS returning '0' would mean that no bandwidth control states are supported */ -static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, - unsigned long *max_state) +static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev, + unsigned long *max_state) { struct acpi_device *device = cdev->devdata; acpi_handle handle = device->handle; @@ -83,22 +83,12 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, return 0; } -static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev, - char *buf) -{ - unsigned long value; - if (memory_get_int_max_bandwidth(cdev, &value)) - return -EINVAL; - - return sprintf(buf, "%ld\n", value); -} - static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev, - char *buf) + unsigned long *value) { struct acpi_device *device = cdev->devdata; acpi_handle handle = device->handle; - unsigned long long value; + unsigned long long result; struct acpi_object_list arg_list; union acpi_object arg; acpi_status status = AE_OK; @@ -108,15 +98,16 @@ static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev, arg.type = ACPI_TYPE_INTEGER; arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH; status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH, - &arg_list, &value); + &arg_list, &result); if (ACPI_FAILURE(status)) return -EFAULT; - return sprintf(buf, "%llu\n", value); + *value = result; + return 0; } static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, - unsigned int state) + unsigned long state) { struct acpi_device *device = cdev->devdata; acpi_handle handle = device->handle; @@ -126,7 +117,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, unsigned long long temp; unsigned long max_state; - if (memory_get_int_max_bandwidth(cdev, &max_state)) + if (memory_get_max_bandwidth(cdev, &max_state)) return -EFAULT; if (state > max_state) @@ -142,7 +133,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, &temp); printk(KERN_INFO - "Bandwidth value was %d: status is %d\n", state, status); + "Bandwidth value was %ld: status is %d\n", state, status); if (ACPI_FAILURE(status)) return -EFAULT; -- cgit v1.2.3 From 4c395bdd3f2ca8f7e8efad881e16071182c3b8ca Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Wed, 4 Mar 2009 11:55:28 -0800 Subject: hp-wmi: notify of a potential docking state change on resume It is possible that the system gets docked or undocked while it's suspended. Generate an input event on resume to notify user space if there was a state change. As it is a switch, we can generate the event unconditionally; the input layer will only pass it on if there is an actual change. Signed-off-by: Frans Pop Cc: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/platform/x86/hp-wmi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index f41135f2fb29..50d9019de2be 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,6 +53,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); 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 platform_device *device); struct bios_args { u32 signature; @@ -101,6 +102,7 @@ static struct platform_driver hp_wmi_driver = { }, .probe = hp_wmi_bios_setup, .remove = hp_wmi_bios_remove, + .resume = hp_wmi_resume_handler, }; static int hp_wmi_perform_query(int query, int write, int value) @@ -487,6 +489,29 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) return 0; } +static int hp_wmi_resume_handler(struct platform_device *device) +{ + struct key_entry *key; + + /* + * Docking state may have changed while suspended, so trigger + * an input event for the current state. As this is a switch, + * the input layer will only actually pass it on if the state + * changed. + */ + for (key = hp_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_SW: + input_report_switch(hp_wmi_input_dev, key->keycode, + hp_wmi_dock_state()); + input_sync(hp_wmi_input_dev); + break; + } + } + + return 0; +} + static int __init hp_wmi_init(void) { int err; -- cgit v1.2.3 From badf26f00f2ed80615206d07bcfc2e3b78af5441 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 26 Mar 2009 21:58:12 +0900 Subject: sony-laptop: Add support for new Sony platform API Newer Sony Vaios provide a new API for accessing platform functionality. It consists of a set of standardised methods for enabling events and performing queries. These are each identified by a unique handle. This patch adds support for calling functions based on their handle and ports the existing code for these machines over to it. Signed-off-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 146 +++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 81 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 537959d07148..3c52ec9548ab 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -689,6 +689,31 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, return -1; } +static int sony_find_snc_handle(int handle) +{ + int i; + int result; + + for (i = 0x20; i < 0x30; i++) { + acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); + if (result == handle) + return i-0x20; + } + + return -1; +} + +static int sony_call_snc_handle(int handle, int argument, int *result) +{ + int offset = sony_find_snc_handle(handle); + + if (offset < 0) + return -1; + + return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, + result); +} + /* * sony_nc_values input/output validate functions */ @@ -809,32 +834,6 @@ struct sony_nc_event { u8 event; }; -static struct sony_nc_event *sony_nc_events; - -/* Vaio C* --maybe also FE*, N* and AR* ?-- special init sequence - * for Fn keys - */ -static int sony_nc_C_enable(const struct dmi_system_id *id) -{ - int result = 0; - - printk(KERN_NOTICE DRV_PFX "detected %s\n", id->ident); - - sony_nc_events = id->driver_data; - - if (acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x4, &result) < 0 - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x2, &result) < 0 - || acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0x10, &result) < 0 - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x0, &result) < 0 - || acpi_callsetfunc(sony_nc_acpi_handle, "SN03", 0x2, &result) < 0 - || acpi_callsetfunc(sony_nc_acpi_handle, "SN07", 0x101, &result) < 0) { - printk(KERN_WARNING DRV_PFX "failed to initialize SNC, some " - "functionalities may be missing\n"); - return 1; - } - return 0; -} - static struct sony_nc_event sony_C_events[] = { { 0x81, SONYPI_EVENT_FNKEY_F1 }, { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, @@ -851,57 +850,17 @@ static struct sony_nc_event sony_C_events[] = { { 0, 0 }, }; -/* SNC-only model map */ -static const struct dmi_system_id sony_nc_ids[] = { - { - .ident = "Sony Vaio FE Series", - .callback = sony_nc_C_enable, - .driver_data = sony_C_events, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FE"), - }, - }, - { - .ident = "Sony Vaio FZ Series", - .callback = sony_nc_C_enable, - .driver_data = sony_C_events, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ"), - }, - }, - { - .ident = "Sony Vaio C Series", - .callback = sony_nc_C_enable, - .driver_data = sony_C_events, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-C"), - }, - }, - { - .ident = "Sony Vaio N Series", - .callback = sony_nc_C_enable, - .driver_data = sony_C_events, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-N"), - }, - }, - { } -}; - /* * ACPI callbacks */ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct sony_nc_event *evmap; + int i; u32 ev = event; int result; - if (ev == 0x92) { + if (ev == 0x92 || ev == 0x90) { + int origev = ev; /* read the key pressed from EC.GECR * A call to SN07 with 0x0202 will do it as well respecting * the current protocol on different OSes @@ -913,20 +872,23 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) * TODO: we may want to do the same for the older GHKE -need * dmi list- so this snippet may become one more callback. */ - if (acpi_callsetfunc(handle, "SN07", 0x0202, &result) < 0) + if (sony_call_snc_handle(0x100, 0x200, &result)) dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); else ev = result & 0xFF; - } - if (sony_nc_events) - for (evmap = sony_nc_events; evmap->event; evmap++) { - if (evmap->data == ev) { - ev = evmap->event; + for (i = 0; sony_C_events[i].data; i++) { + if (sony_C_events[i].data == ev) { + ev = sony_C_events[i].event; break; } } + if (!sony_C_events[i].data) + printk(KERN_INFO DRV_PFX "Unknown event: %x %x\n", + origev, ev); + } + dprintk("sony_acpi_notify, event: 0x%.2x\n", ev); sony_laptop_report_input_event(ev); acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); @@ -953,9 +915,25 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, /* * ACPI device */ +static int sony_nc_function_setup(struct acpi_device *device) +{ + int result; + + /* Enable all events */ + acpi_callsetfunc(sony_nc_acpi_handle, "SN02", 0xffff, &result); + + /* Setup hotkeys */ + sony_call_snc_handle(0x0100, 0, &result); + sony_call_snc_handle(0x0101, 0, &result); + sony_call_snc_handle(0x0102, 0x100, &result); + + return 0; +} + static int sony_nc_resume(struct acpi_device *device) { struct sony_nc_value *item; + acpi_handle handle; for (item = sony_nc_values; item->name; item++) { int ret; @@ -970,14 +948,17 @@ static int sony_nc_resume(struct acpi_device *device) } } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", + &handle))) { + dprintk("Doing SNC setup\n"); + sony_nc_function_setup(device); + } + /* set the last requested brightness level */ if (sony_backlight_device && !sony_backlight_update_status(sony_backlight_device)) printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); - /* re-initialize models with specific requirements */ - dmi_check_system(sony_nc_ids); - return 0; } @@ -1024,6 +1005,12 @@ static int sony_nc_add(struct acpi_device *device) dprintk("_INI Method failed\n"); } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", + &handle))) { + dprintk("Doing SNC setup\n"); + sony_nc_function_setup(device); + } + /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { @@ -1063,9 +1050,6 @@ static int sony_nc_add(struct acpi_device *device) } - /* initialize models with specific requirements */ - dmi_check_system(sony_nc_ids); - result = sony_pf_add(); if (result) goto outbacklight; -- cgit v1.2.3 From 82734bfc8622f3e8f015a5783b739739f97f89f9 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 26 Mar 2009 21:58:13 +0900 Subject: sony-laptop: Enable EC on newer hardware The latest Vaios can execute certain codepaths in two ways - either using system management mode or using pure ACPI methods. The latter is only used if the OS has called the ECON method. Ensure that this is done where the method is available. Signed-off-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3c52ec9548ab..04deed826180 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -948,6 +948,12 @@ static int sony_nc_resume(struct acpi_device *device) } } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", + &handle))) { + if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + dprintk("ECON Method failed\n"); + } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); @@ -1005,6 +1011,12 @@ static int sony_nc_add(struct acpi_device *device) dprintk("_INI Method failed\n"); } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", + &handle))) { + if (acpi_callsetfunc(sony_nc_acpi_handle, "ECON", 1, NULL)) + dprintk("ECON Method failed\n"); + } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); -- cgit v1.2.3 From 9b57896e62bfa752ee7435e6cfe57fb210c0db8c Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 26 Mar 2009 21:58:14 +0900 Subject: sony-laptop: Add support for extra keyboard events The current sony-laptop code assumes that the keyboard event method is always located at slot 2 in the platform code. Remove this assumption and add support for some additional hotkeys. Signed-off-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 59 +++++++++++++++++++++----------------- include/linux/sonypi.h | 1 + 2 files changed, 33 insertions(+), 27 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 04deed826180..f6cdc8929fd7 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -211,6 +211,7 @@ static int sony_laptop_input_index[] = { 48, /* 61 SONYPI_EVENT_WIRELESS_OFF */ 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */ 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */ + 51, /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -264,7 +265,8 @@ static int sony_laptop_input_keycode_map[] = { KEY_WLAN, /* 47 SONYPI_EVENT_WIRELESS_ON */ KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */ - KEY_ZOOMOUT /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ + KEY_ZOOMOUT, /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ + KEY_EJECTCD /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -834,7 +836,11 @@ struct sony_nc_event { u8 event; }; -static struct sony_nc_event sony_C_events[] = { +static struct sony_nc_event sony_nc_events[] = { + { 0x90, SONYPI_EVENT_PKEY_P1 }, + { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x91, SONYPI_EVENT_PKEY_P1 }, + { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x81, SONYPI_EVENT_FNKEY_F1 }, { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, { 0x85, SONYPI_EVENT_FNKEY_F5 }, @@ -843,10 +849,14 @@ static struct sony_nc_event sony_C_events[] = { { 0x06, SONYPI_EVENT_FNKEY_RELEASED }, { 0x87, SONYPI_EVENT_FNKEY_F7 }, { 0x07, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x89, SONYPI_EVENT_FNKEY_F9 }, + { 0x09, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8A, SONYPI_EVENT_FNKEY_F10 }, { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, + { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0, 0 }, }; @@ -855,38 +865,33 @@ static struct sony_nc_event sony_C_events[] = { */ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) { - int i; u32 ev = event; int result; - if (ev == 0x92 || ev == 0x90) { + if (ev >= 0x90) { + /* New-style event */ int origev = ev; - /* read the key pressed from EC.GECR - * A call to SN07 with 0x0202 will do it as well respecting - * the current protocol on different OSes - * - * Note: the path for GECR may be - * \_SB.PCI0.LPCB.EC (C, FE, AR, N and friends) - * \_SB.PCI0.PIB.EC0 (VGN-FR notifications are sent directly, no GECR) - * - * TODO: we may want to do the same for the older GHKE -need - * dmi list- so this snippet may become one more callback. - */ - if (sony_call_snc_handle(0x100, 0x200, &result)) - dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); - else - ev = result & 0xFF; + ev -= 0x90; - for (i = 0; sony_C_events[i].data; i++) { - if (sony_C_events[i].data == ev) { - ev = sony_C_events[i].event; - break; + if (sony_find_snc_handle(0x100) == ev) { + int i; + + if (sony_call_snc_handle(0x100, 0x200, &result)) + dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); + else + ev = result & 0xFF; + + for (i = 0; sony_nc_events[i].event; i++) { + if (sony_nc_events[i].data == ev) { + ev = sony_nc_events[i].event; + break; + } } - } - if (!sony_C_events[i].data) - printk(KERN_INFO DRV_PFX "Unknown event: %x %x\n", - origev, ev); + if (!sony_nc_events[i].data) + printk(KERN_INFO DRV_PFX + "Unknown event: %x %x\n", origev, ev); + } } dprintk("sony_acpi_notify, event: 0x%.2x\n", ev); diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index f41ffd7c2dd9..8458dbe95862 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -103,6 +103,7 @@ #define SONYPI_EVENT_WIRELESS_OFF 61 #define SONYPI_EVENT_ZOOM_IN_PRESSED 62 #define SONYPI_EVENT_ZOOM_OUT_PRESSED 63 +#define SONYPI_EVENT_CD_EJECT_PRESSED 64 /* get/set brightness */ #define SONYPI_IOCGBRT _IOR('v', 0, __u8) -- cgit v1.2.3 From 6cc056bc31ea9910afb01adc0848bb6ae68e0205 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 26 Mar 2009 21:58:15 +0900 Subject: sony-laptop: Add rfkill support on new models Newer Vaios provide a full featured rfkill implementation via their platform methods. Add support for enumerating the available devices and providing rfkill access to them. Support for the physical kill switch is added, with the devices moving into the HARD_BLOCKED state when toggled. Signed-off-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 186 +++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f6cdc8929fd7..f458870c30b6 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -64,6 +64,7 @@ #include #include #include +#include #ifdef CONFIG_SONYPI_COMPAT #include #include @@ -123,6 +124,18 @@ MODULE_PARM_DESC(minor, "default is -1 (automatic)"); #endif +enum sony_nc_rfkill { + SONY_WIFI, + SONY_BLUETOOTH, + SONY_WWAN, + SONY_WIMAX, + SONY_RFKILL_MAX, +}; + +static struct rfkill *sony_rfkill_devices[SONY_RFKILL_MAX]; +static int sony_rfkill_address[SONY_RFKILL_MAX] = {0x300, 0x500, 0x700, 0x900}; +static void sony_nc_rfkill_update(void); + /*********** Input Devices ***********/ #define SONY_LAPTOP_BUF_SIZE 128 @@ -134,6 +147,7 @@ struct sony_laptop_input_s { spinlock_t fifo_lock; struct workqueue_struct *wq; }; + static struct sony_laptop_input_s sony_laptop_input = { .users = ATOMIC_INIT(0), }; @@ -891,6 +905,9 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) if (!sony_nc_events[i].data) printk(KERN_INFO DRV_PFX "Unknown event: %x %x\n", origev, ev); + } else if (sony_find_snc_handle(0x124) == ev) { + sony_nc_rfkill_update(); + return; } } @@ -973,6 +990,172 @@ static int sony_nc_resume(struct acpi_device *device) return 0; } +static void sony_nc_rfkill_cleanup(void) +{ + int i; + + for (i = 0; i < SONY_RFKILL_MAX; i++) { + if (sony_rfkill_devices[i]) + rfkill_unregister(sony_rfkill_devices[i]); + } +} + +static int sony_nc_rfkill_get(void *data, enum rfkill_state *state) +{ + int result; + int argument = sony_rfkill_address[(long) data]; + + sony_call_snc_handle(0x124, 0x200, &result); + if (result & 0x1) { + sony_call_snc_handle(0x124, argument, &result); + if (result & 0xf) + *state = RFKILL_STATE_UNBLOCKED; + else + *state = RFKILL_STATE_SOFT_BLOCKED; + } else { + *state = RFKILL_STATE_HARD_BLOCKED; + } + + return 0; +} + +static int sony_nc_rfkill_set(void *data, enum rfkill_state state) +{ + int result; + int argument = sony_rfkill_address[(long) data] + 0x100; + + if (state == RFKILL_STATE_UNBLOCKED) + argument |= 0xff0000; + + return sony_call_snc_handle(0x124, argument, &result); +} + +static int sony_nc_setup_wifi_rfkill(struct acpi_device *device) +{ + int err = 0; + struct rfkill *sony_wifi_rfkill; + + sony_wifi_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WLAN); + if (!sony_wifi_rfkill) + return -1; + sony_wifi_rfkill->name = "sony-wifi"; + sony_wifi_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wifi_rfkill->get_state = sony_nc_rfkill_get; + sony_wifi_rfkill->user_claim_unsupported = 1; + sony_wifi_rfkill->data = (void *)SONY_WIFI; + err = rfkill_register(sony_wifi_rfkill); + if (err) + rfkill_free(sony_wifi_rfkill); + else + sony_rfkill_devices[SONY_WIFI] = sony_wifi_rfkill; + return err; +} + +static int sony_nc_setup_bluetooth_rfkill(struct acpi_device *device) +{ + int err = 0; + struct rfkill *sony_bluetooth_rfkill; + + sony_bluetooth_rfkill = rfkill_allocate(&device->dev, + RFKILL_TYPE_BLUETOOTH); + if (!sony_bluetooth_rfkill) + return -1; + sony_bluetooth_rfkill->name = "sony-bluetooth"; + sony_bluetooth_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_bluetooth_rfkill->get_state = sony_nc_rfkill_get; + sony_bluetooth_rfkill->user_claim_unsupported = 1; + sony_bluetooth_rfkill->data = (void *)SONY_BLUETOOTH; + err = rfkill_register(sony_bluetooth_rfkill); + if (err) + rfkill_free(sony_bluetooth_rfkill); + else + sony_rfkill_devices[SONY_BLUETOOTH] = sony_bluetooth_rfkill; + return err; +} + +static int sony_nc_setup_wwan_rfkill(struct acpi_device *device) +{ + int err = 0; + struct rfkill *sony_wwan_rfkill; + + sony_wwan_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WWAN); + if (!sony_wwan_rfkill) + return -1; + sony_wwan_rfkill->name = "sony-wwan"; + sony_wwan_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wwan_rfkill->get_state = sony_nc_rfkill_get; + sony_wwan_rfkill->user_claim_unsupported = 1; + sony_wwan_rfkill->data = (void *)SONY_WWAN; + err = rfkill_register(sony_wwan_rfkill); + if (err) + rfkill_free(sony_wwan_rfkill); + else + sony_rfkill_devices[SONY_WWAN] = sony_wwan_rfkill; + return err; +} + +static int sony_nc_setup_wimax_rfkill(struct acpi_device *device) +{ + int err = 0; + struct rfkill *sony_wimax_rfkill; + + sony_wimax_rfkill = rfkill_allocate(&device->dev, RFKILL_TYPE_WIMAX); + if (!sony_wimax_rfkill) + return -1; + sony_wimax_rfkill->name = "sony-wimax"; + sony_wimax_rfkill->toggle_radio = sony_nc_rfkill_set; + sony_wimax_rfkill->get_state = sony_nc_rfkill_get; + sony_wimax_rfkill->user_claim_unsupported = 1; + sony_wimax_rfkill->data = (void *)SONY_WIMAX; + err = rfkill_register(sony_wimax_rfkill); + if (err) + rfkill_free(sony_wimax_rfkill); + else + sony_rfkill_devices[SONY_WIMAX] = sony_wimax_rfkill; + return err; +} + +static void sony_nc_rfkill_update() +{ + int i; + enum rfkill_state state; + + for (i = 0; i < SONY_RFKILL_MAX; i++) { + if (sony_rfkill_devices[i]) { + sony_rfkill_devices[i]-> + get_state(sony_rfkill_devices[i]->data, + &state); + rfkill_force_state(sony_rfkill_devices[i], state); + } + } +} + +static int sony_nc_rfkill_setup(struct acpi_device *device) +{ + int result, ret; + + if (sony_find_snc_handle(0x124) == -1) + return -1; + + ret = sony_call_snc_handle(0x124, 0xb00, &result); + if (ret) { + printk(KERN_INFO DRV_PFX + "Unable to enumerate rfkill devices: %x\n", ret); + return ret; + } + + if (result & 0x1) + sony_nc_setup_wifi_rfkill(device); + if (result & 0x2) + sony_nc_setup_bluetooth_rfkill(device); + if (result & 0x1c) + sony_nc_setup_wwan_rfkill(device); + if (result & 0x20) + sony_nc_setup_wimax_rfkill(device); + + return 0; +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1026,6 +1209,7 @@ static int sony_nc_add(struct acpi_device *device) &handle))) { dprintk("Doing SNC setup\n"); sony_nc_function_setup(device); + sony_nc_rfkill_setup(device); } /* setup input devices and helper fifo */ @@ -1132,6 +1316,7 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); outwalk: + sony_nc_rfkill_cleanup(); return result; } @@ -1157,6 +1342,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) sony_pf_remove(); sony_laptop_remove_input(); + sony_nc_rfkill_cleanup(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); return 0; -- cgit v1.2.3 From 45c7942ba8f6b7d5d1147c10f84f0cbf5fa3a2b8 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 26 Mar 2009 21:58:16 +0900 Subject: sony-laptop: Add support for extended hotkeys Recent Sony SR-series machines have an additional set of buttons accessed via the 0x127 method rather than the 0x100 method. Add support for these. Signed-off-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 65 +++++++++++++++++++++++++++++++------- include/linux/sonypi.h | 4 +++ 2 files changed, 57 insertions(+), 12 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f458870c30b6..e000c9f6cdf5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -226,6 +226,10 @@ static int sony_laptop_input_index[] = { 49, /* 62 SONYPI_EVENT_ZOOM_IN_PRESSED */ 50, /* 63 SONYPI_EVENT_ZOOM_OUT_PRESSED */ 51, /* 64 SONYPI_EVENT_CD_EJECT_PRESSED */ + 52, /* 65 SONYPI_EVENT_MODEKEY_PRESSED */ + 53, /* 66 SONYPI_EVENT_PKEY_P4 */ + 54, /* 67 SONYPI_EVENT_PKEY_P5 */ + 55, /* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -280,7 +284,11 @@ static int sony_laptop_input_keycode_map[] = { KEY_WLAN, /* 48 SONYPI_EVENT_WIRELESS_OFF */ KEY_ZOOMIN, /* 49 SONYPI_EVENT_ZOOM_IN_PRESSED */ KEY_ZOOMOUT, /* 50 SONYPI_EVENT_ZOOM_OUT_PRESSED */ - KEY_EJECTCD /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */ + KEY_EJECTCD, /* 51 SONYPI_EVENT_CD_EJECT_PRESSED */ + KEY_F13, /* 52 SONYPI_EVENT_MODEKEY_PRESSED */ + KEY_PROG4, /* 53 SONYPI_EVENT_PKEY_P4 */ + KEY_F14, /* 54 SONYPI_EVENT_PKEY_P5 */ + KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -850,7 +858,7 @@ struct sony_nc_event { u8 event; }; -static struct sony_nc_event sony_nc_events[] = { +static struct sony_nc_event sony_100_events[] = { { 0x90, SONYPI_EVENT_PKEY_P1 }, { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x91, SONYPI_EVENT_PKEY_P1 }, @@ -874,6 +882,25 @@ static struct sony_nc_event sony_nc_events[] = { { 0, 0 }, }; +static struct sony_nc_event sony_127_events[] = { + { 0x81, SONYPI_EVENT_MODEKEY_PRESSED }, + { 0x01, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x82, SONYPI_EVENT_PKEY_P1 }, + { 0x02, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x83, SONYPI_EVENT_PKEY_P2 }, + { 0x03, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x84, SONYPI_EVENT_PKEY_P3 }, + { 0x04, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x85, SONYPI_EVENT_PKEY_P4 }, + { 0x05, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x86, SONYPI_EVENT_PKEY_P5 }, + { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x06, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0x87, SONYPI_EVENT_SETTINGKEY_PRESSED }, + { 0x07, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0, 0 }, +}; + /* * ACPI callbacks */ @@ -884,27 +911,41 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) if (ev >= 0x90) { /* New-style event */ - int origev = ev; + int key_handle = 0; ev -= 0x90; - if (sony_find_snc_handle(0x100) == ev) { - int i; + if (sony_find_snc_handle(0x100) == ev) + key_handle = 0x100; + if (sony_find_snc_handle(0x127) == ev) + key_handle = 0x127; + + if (handle) { + struct sony_nc_event *key_event; - if (sony_call_snc_handle(0x100, 0x200, &result)) - dprintk("sony_acpi_notify, unable to decode event 0x%.2x\n", ev); + if (sony_call_snc_handle(key_handle, 0x200, &result)) + dprintk("sony_acpi_notify, unable to decode" + " event 0x%.2x 0x%.2x\n", key_handle, + ev); else ev = result & 0xFF; - for (i = 0; sony_nc_events[i].event; i++) { - if (sony_nc_events[i].data == ev) { - ev = sony_nc_events[i].event; + if (key_handle == 0x100) + key_event = sony_100_events; + else + key_event = sony_127_events; + + for (; key_event->data; key_event++) { + if (key_event->data == ev) { + ev = key_event->event; break; } } - if (!sony_nc_events[i].data) + if (!key_event->data) { printk(KERN_INFO DRV_PFX - "Unknown event: %x %x\n", origev, ev); + "Unknown event: 0x%x 0x%x\n", key_handle, + ev); + } } else if (sony_find_snc_handle(0x124) == ev) { sony_nc_rfkill_update(); return; diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 8458dbe95862..bb835019ac7f 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -104,6 +104,10 @@ #define SONYPI_EVENT_ZOOM_IN_PRESSED 62 #define SONYPI_EVENT_ZOOM_OUT_PRESSED 63 #define SONYPI_EVENT_CD_EJECT_PRESSED 64 +#define SONYPI_EVENT_MODEKEY_PRESSED 65 +#define SONYPI_EVENT_PKEY_P4 66 +#define SONYPI_EVENT_PKEY_P5 67 +#define SONYPI_EVENT_SETTINGKEY_PRESSED 68 /* get/set brightness */ #define SONYPI_IOCGBRT _IOR('v', 0, __u8) -- cgit v1.2.3 From e93c8a6819b217f4f4a490f67f26e02ff6b23b44 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 26 Mar 2009 21:58:17 +0900 Subject: sony-laptop: merge Type4 into Type3 Creating Type4 was a mistake in the first place. Some users report that also Type3 vaios require the same extra hotkey handling which the Type4 for was menat to guard from. Merging down Type4 into Type3 will just remove a useless distinction. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e000c9f6cdf5..3e45c65b8f81 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1423,7 +1423,6 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_TYPE1_OFFSET 0x04 #define SONYPI_TYPE2_OFFSET 0x12 #define SONYPI_TYPE3_OFFSET 0x12 -#define SONYPI_TYPE4_OFFSET 0x12 struct sony_pic_ioport { struct acpi_resource_io io1; @@ -1666,14 +1665,6 @@ static struct sonypi_eventtypes type3_events[] = { { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, { 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, - { 0 }, -}; -static struct sonypi_eventtypes type4_events[] = { - { 0, 0xffffffff, sonypi_releaseev }, - { 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, - { 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, - { 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, - { 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev }, { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev }, { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev }, @@ -1739,11 +1730,11 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) /* * minidrivers for SPIC models */ -static int type4_handle_irq(const u8 data_mask, const u8 ev) +static int type3_handle_irq(const u8 data_mask, const u8 ev) { /* * 0x31 could mean we have to take some extra action and wait for - * the next irq for some Type4 models, it will generate a new + * the next irq for some Type3 models, it will generate a new * irq and we can read new data from the device: * - 0x5c and 0x5f requires 0xA0 * - 0x61 requires 0xB3 @@ -1773,16 +1764,10 @@ static struct device_ctrl spic_types[] = { }, { .model = SONYPI_DEVICE_TYPE3, - .handle_irq = NULL, + .handle_irq = type3_handle_irq, .evport_offset = SONYPI_TYPE3_OFFSET, .event_types = type3_events, }, - { - .model = SONYPI_DEVICE_TYPE4, - .handle_irq = type4_handle_irq, - .evport_offset = SONYPI_TYPE4_OFFSET, - .event_types = type4_events, - }, }; static void sony_pic_detect_device_type(struct sony_pic_dev *dev) @@ -1806,14 +1791,14 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev) pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, NULL); if (pcidev) { - dev->control = &spic_types[3]; + dev->control = &spic_types[2]; goto out; } pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, NULL); if (pcidev) { - dev->control = &spic_types[3]; + dev->control = &spic_types[2]; goto out; } @@ -1826,8 +1811,7 @@ out: printk(KERN_INFO DRV_PFX "detected Type%d model\n", dev->control->model == SONYPI_DEVICE_TYPE1 ? 1 : - dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 : - dev->control->model == SONYPI_DEVICE_TYPE3 ? 3 : 4); + dev->control->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); } /* camera tests and poweron/poweroff */ -- cgit v1.2.3 From 1cae71032183776e833036fe828315dcd3444df1 Mon Sep 17 00:00:00 2001 From: Harald Jenny Date: Thu, 26 Mar 2009 21:58:18 +0900 Subject: sony-laptop: VGN-A317M hotkey support This laptop has 5 SPIC managed buttons above the keyboard: sound + and - as well as brightness, zoom and S1. Possibly the entire VGN-A serie behaves the same. Signed-off-by: Harald Jenny Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 22 ++++++++++++++++++++++ include/linux/sonypi.h | 3 +++ 2 files changed, 25 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3e45c65b8f81..4f932889569b 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -230,6 +230,9 @@ static int sony_laptop_input_index[] = { 53, /* 66 SONYPI_EVENT_PKEY_P4 */ 54, /* 67 SONYPI_EVENT_PKEY_P5 */ 55, /* 68 SONYPI_EVENT_SETTINGKEY_PRESSED */ + 56, /* 69 SONYPI_EVENT_VOLUME_INC_PRESSED */ + 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ + -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -289,6 +292,8 @@ static int sony_laptop_input_keycode_map[] = { KEY_PROG4, /* 53 SONYPI_EVENT_PKEY_P4 */ KEY_F14, /* 54 SONYPI_EVENT_PKEY_P5 */ KEY_F15, /* 55 SONYPI_EVENT_SETTINGKEY_PRESSED */ + KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ + KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -1555,6 +1560,7 @@ static struct sonypi_event sonypi_pkeyev[] = { { 0x01, SONYPI_EVENT_PKEY_P1 }, { 0x02, SONYPI_EVENT_PKEY_P2 }, { 0x04, SONYPI_EVENT_PKEY_P3 }, + { 0x20, SONYPI_EVENT_PKEY_P1 }, { 0, 0 } }; @@ -1598,6 +1604,7 @@ static struct sonypi_event sonypi_zoomev[] = { { 0x39, SONYPI_EVENT_ZOOM_PRESSED }, { 0x10, SONYPI_EVENT_ZOOM_IN_PRESSED }, { 0x20, SONYPI_EVENT_ZOOM_OUT_PRESSED }, + { 0x04, SONYPI_EVENT_ZOOM_PRESSED }, { 0, 0 } }; @@ -1628,6 +1635,19 @@ static struct sonypi_event sonypi_batteryev[] = { { 0, 0 } }; +/* The set of possible volume events */ +static struct sonypi_event sonypi_volumeev[] = { + { 0x01, SONYPI_EVENT_VOLUME_INC_PRESSED }, + { 0x02, SONYPI_EVENT_VOLUME_DEC_PRESSED }, + { 0, 0 } +}; + +/* The set of possible brightness events */ +static struct sonypi_event sonypi_brightnessev[] = { + { 0x80, SONYPI_EVENT_BRIGHTNESS_PRESSED }, + { 0, 0 } +}; + static struct sonypi_eventtypes type1_events[] = { { 0, 0xffffffff, sonypi_releaseev }, { 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, @@ -1668,6 +1688,8 @@ static struct sonypi_eventtypes type3_events[] = { { 0x05, SONYPI_PKEY_MASK, sonypi_pkeyev }, { 0x05, SONYPI_ZOOM_MASK, sonypi_zoomev }, { 0x05, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { 0x05, SONYPI_PKEY_MASK, sonypi_volumeev }, + { 0x05, SONYPI_PKEY_MASK, sonypi_brightnessev }, { 0 }, }; diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index bb835019ac7f..34c4475ac4a2 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -108,6 +108,9 @@ #define SONYPI_EVENT_PKEY_P4 66 #define SONYPI_EVENT_PKEY_P5 67 #define SONYPI_EVENT_SETTINGKEY_PRESSED 68 +#define SONYPI_EVENT_VOLUME_INC_PRESSED 69 +#define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 +#define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 /* get/set brightness */ #define SONYPI_IOCGBRT _IOR('v', 0, __u8) -- cgit v1.2.3 From 2b24ef093aec6d7b9c18af75644ec22b4069b283 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 26 Mar 2009 21:58:19 +0900 Subject: sony-laptop: Eliminate BKL in ioctls Signed-off-by: Alan Cox Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 4f932889569b..813d0e03d9c5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2224,8 +2224,8 @@ static int ec_read16(u8 addr, u16 *value) return 0; } -static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, - unsigned int cmd, unsigned long arg) +static long sonypi_misc_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) { int ret = 0; void __user *argp = (void __user *)arg; @@ -2359,7 +2359,7 @@ static const struct file_operations sonypi_misc_fops = { .open = sonypi_misc_open, .release = sonypi_misc_release, .fasync = sonypi_misc_fasync, - .ioctl = sonypi_misc_ioctl, + .unlocked_ioctl = sonypi_misc_ioctl, }; static struct miscdevice sonypi_misc_device = { -- cgit v1.2.3 From d5b02695d5471b38064efeba2b102bd5ead55297 Mon Sep 17 00:00:00 2001 From: ISHIKAWA Mutsumi Date: Thu, 26 Mar 2009 21:58:20 +0900 Subject: sony-laptop: detect the ICH9 chipset as Type3 Signed-off-by: ISHIKAWA Mutsumi Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 813d0e03d9c5..5030f991906a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1824,6 +1824,13 @@ static void sony_pic_detect_device_type(struct sony_pic_dev *dev) goto out; } + pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH9_1, NULL); + if (pcidev) { + dev->control = &spic_types[2]; + goto out; + } + /* default */ dev->control = &spic_types[1]; -- cgit v1.2.3 From 3ad1b7619959c0387a30e05c6f36537190b6da7b Mon Sep 17 00:00:00 2001 From: Sergey Yanovich Date: Thu, 26 Mar 2009 21:58:21 +0900 Subject: sony-laptop: notify the hardware of a state change in wwanpower Signed-off-by: Sergey Yanovich Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5030f991906a..74df72321aeb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2004,6 +2004,7 @@ static void sony_pic_set_wwanpower(u8 state) return; } sony_pic_call2(0xB0, state); + sony_pic_call1(0x82); spic_dev.wwan_power = state; mutex_unlock(&spic_dev.lock); } -- cgit v1.2.3 From f5acf5e898618295895b61d81681ae3115d94b96 Mon Sep 17 00:00:00 2001 From: Anton Veretenenko Date: Thu, 26 Mar 2009 22:44:26 +0900 Subject: sony-laptop: Add FW specific hotkey events Signed-off-by: Anton Veretenenko Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 74df72321aeb..3575a2d8f1ce 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -870,6 +870,12 @@ static struct sony_nc_event sony_100_events[] = { { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x81, SONYPI_EVENT_FNKEY_F1 }, { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x82, SONYPI_EVENT_FNKEY_F2 }, + { 0x02, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x83, SONYPI_EVENT_FNKEY_F3 }, + { 0x03, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x84, SONYPI_EVENT_FNKEY_F4 }, + { 0x04, SONYPI_EVENT_FNKEY_RELEASED }, { 0x85, SONYPI_EVENT_FNKEY_F5 }, { 0x05, SONYPI_EVENT_FNKEY_RELEASED }, { 0x86, SONYPI_EVENT_FNKEY_F6 }, -- cgit v1.2.3 From c9f1e6f67ad6d16c11586a4550fd054d7538eb92 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 26 Mar 2009 21:58:23 +0900 Subject: sony-laptop: Make sony_pic_set_wwanpower not take mutexes For consistency with __sony_pic_set_bluetoothpower, this is also needed later to allow setting the wwanpower attribute from the resume path and only lock the mutex once. Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 3575a2d8f1ce..8796dc69950e 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2001,18 +2001,14 @@ int sony_pic_camera_command(int command, u8 value) EXPORT_SYMBOL(sony_pic_camera_command); /* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */ -static void sony_pic_set_wwanpower(u8 state) +static void __sony_pic_set_wwanpower(u8 state) { state = !!state; - mutex_lock(&spic_dev.lock); - if (spic_dev.wwan_power == state) { - mutex_unlock(&spic_dev.lock); + if (spic_dev.wwan_power == state) return; - } sony_pic_call2(0xB0, state); sony_pic_call1(0x82); spic_dev.wwan_power = state; - mutex_unlock(&spic_dev.lock); } static ssize_t sony_pic_wwanpower_store(struct device *dev, @@ -2024,7 +2020,9 @@ static ssize_t sony_pic_wwanpower_store(struct device *dev, return -EINVAL; value = simple_strtoul(buffer, NULL, 10); - sony_pic_set_wwanpower(value); + mutex_lock(&spic_dev.lock); + __sony_pic_set_wwanpower(value); + mutex_unlock(&spic_dev.lock); return count; } -- cgit v1.2.3 From 3741834775de035bdf0cd1ce44862bb8c775a4fa Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 26 Mar 2009 21:58:24 +0900 Subject: sony-laptop: update copyright Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8796dc69950e..fc0dd63e672d 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2,7 +2,7 @@ * ACPI Sony Notebook Control Driver (SNC and SPIC) * * Copyright (C) 2004-2005 Stelian Pop - * Copyright (C) 2007 Mattia Dongili + * Copyright (C) 2007-2009 Mattia Dongili * * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c * which are copyrighted by their respective authors. -- cgit v1.2.3 From 4ef4cbb3686f9265da87bc6e482162c96f415427 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Thu, 26 Mar 2009 21:58:25 +0900 Subject: sony-laptop: Kill the BKL Signed-off-by: Alessio Igor Bogani Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index fc0dd63e672d..0f710317a94e 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -2180,10 +2179,15 @@ static int sonypi_misc_release(struct inode *inode, struct file *file) static int sonypi_misc_open(struct inode *inode, struct file *file) { /* Flush input queue on first open */ - lock_kernel(); + unsigned long flags; + + spin_lock_irqsave(sonypi_compat.fifo->lock, flags); + if (atomic_inc_return(&sonypi_compat.open_count) == 1) - kfifo_reset(sonypi_compat.fifo); - unlock_kernel(); + __kfifo_reset(sonypi_compat.fifo); + + spin_unlock_irqrestore(sonypi_compat.fifo->lock, flags); + return 0; } -- cgit v1.2.3 From 07de5bdb7bad607e29dc17c345717403a76a334c Mon Sep 17 00:00:00 2001 From: Krzysztof Kosiński Date: Thu, 19 Mar 2009 23:22:31 +0100 Subject: tc1100-wmi: Fix state reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tc1100-wmi driver should print the current states of wireless LAN and jogdial brightness control when "cat /sys/devices/platform/tc1100-wmi/wireless" and "cat /sys/devices/platform/tc1100-wmi/jogdial" are executed, respectively. What actually happens is that both of those commands print 0 regardless of the hardware state. The cause is that wmi_query_block returns an ACPI_TYPE_INTEGER rather than ACPI_TYPE_BUFFER as the driver assumes. Additionally, the driver intends to return a jogdial state that is inverted with respect to the commands required to set it (e.g. it intends to return 1 after the jogdial file was written with 0). This patch fixes both of those issues - the commands to query the state now work, and should return the same state that was written. http://bugzilla.kernel.org/show_bug.cgi?id=12286 Signed-off-by: Krzysztof Kosiński Acked-by: Carlos Corbacho Signed-off-by: Len Brown --- drivers/platform/x86/tc1100-wmi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index b4a4aa9ee482..44166003d4ef 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -94,9 +94,8 @@ static int get_state(u32 *out, u8 instance) return -ENODEV; obj = (union acpi_object *) result.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER && - obj->buffer.length == sizeof(u32)) { - tmp = *((u32 *) obj->buffer.pointer); + if (obj && obj->type == ACPI_TYPE_INTEGER) { + tmp = obj->integer.value; } else { tmp = 0; } @@ -109,7 +108,7 @@ static int get_state(u32 *out, u8 instance) *out = (tmp == 3) ? 1 : 0; return 0; case TC1100_INSTANCE_JOGDIAL: - *out = (tmp == 1) ? 1 : 0; + *out = (tmp == 1) ? 0 : 1; return 0; default: return -ENODEV; -- cgit v1.2.3 From ff07a53a33cff2733c2c20fc75e84e75c2572889 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Wed, 1 Apr 2009 22:10:44 +0900 Subject: sony-laptop: Fix some typos in log messages (Unabe/Unable) Signed-off-by: Alessio Igor Bogani Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 0f710317a94e..5837156f09fd 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -397,7 +397,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); if (!sony_laptop_input.wq) { printk(KERN_ERR DRV_PFX - "Unabe to create workqueue.\n"); + "Unable to create workqueue.\n"); error = -ENXIO; goto err_free_kfifo; } @@ -1267,7 +1267,7 @@ static int sony_nc_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { printk(KERN_ERR DRV_PFX - "Unabe to create input devices.\n"); + "Unable to create input devices.\n"); goto outwalk; } @@ -2816,7 +2816,7 @@ static int sony_pic_add(struct acpi_device *device) result = sony_pic_possible_resources(device); if (result) { printk(KERN_ERR DRV_PFX - "Unabe to read possible resources.\n"); + "Unable to read possible resources.\n"); goto err_free_resources; } @@ -2824,7 +2824,7 @@ static int sony_pic_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { printk(KERN_ERR DRV_PFX - "Unabe to create input devices.\n"); + "Unable to create input devices.\n"); goto err_free_resources; } -- cgit v1.2.3 From 6479efb68dce48fa52aed6ff876d8d102f200527 Mon Sep 17 00:00:00 2001 From: Matthias Welwarsky Date: Wed, 1 Apr 2009 22:10:45 +0900 Subject: sony-laptop: new style events typo fixes Signed-off-by: Matthias Welwarsky Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5837156f09fd..011c03596b2c 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -865,7 +865,7 @@ struct sony_nc_event { static struct sony_nc_event sony_100_events[] = { { 0x90, SONYPI_EVENT_PKEY_P1 }, { 0x10, SONYPI_EVENT_ANYBUTTON_RELEASED }, - { 0x91, SONYPI_EVENT_PKEY_P1 }, + { 0x91, SONYPI_EVENT_PKEY_P2 }, { 0x11, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x81, SONYPI_EVENT_FNKEY_F1 }, { 0x01, SONYPI_EVENT_FNKEY_RELEASED }, @@ -929,7 +929,7 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) if (sony_find_snc_handle(0x127) == ev) key_handle = 0x127; - if (handle) { + if (key_handle) { struct sony_nc_event *key_event; if (sony_call_snc_handle(key_handle, 0x200, &result)) -- cgit v1.2.3 From 14bd31365fbeeccee72b0aead3baa4e5da208281 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Wed, 1 Apr 2009 22:10:46 +0900 Subject: sony-laptop should depend on RFKILL Fixes this build error when RFKILL is not set: drivers/platform/x86/sony-laptop.c:1050: undefined reference to `rfkill_unregister' and so on.. Signed-off-by: Alexander Beregalov Acked-by: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 3608081bc3e0..36b1628d19c6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -165,6 +165,7 @@ config SONY_LAPTOP depends on ACPI select BACKLIGHT_CLASS_DEVICE depends on INPUT + depends on RFKILL ---help--- This mini-driver drives the SNC and SPIC devices present in the ACPI BIOS of the Sony Vaio laptops. -- cgit v1.2.3 From 16dd55f309cf69a648ca3b1fc04b3b6f079c8be0 Mon Sep 17 00:00:00 2001 From: Matthias Welwarsky Date: Wed, 1 Apr 2009 22:10:47 +0900 Subject: sony-laptop: fix event reporting for new style events In short Fn key events are always reported through acpi. The input layer gets all the old style events and only those new style events that, after being decoded, are mapped to an locally represented events. rfkill only update the rfkill device status. Signed-off-by: Matthias Welwarsky Signed-off-by: Mattia Dongili Signed-off-by: Len Brown --- drivers/platform/x86/sony-laptop.c | 41 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 011c03596b2c..e02edf68a68e 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -917,10 +917,10 @@ static struct sony_nc_event sony_127_events[] = { static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) { u32 ev = event; - int result; if (ev >= 0x90) { /* New-style event */ + int result; int key_handle = 0; ev -= 0x90; @@ -932,38 +932,43 @@ static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) if (key_handle) { struct sony_nc_event *key_event; - if (sony_call_snc_handle(key_handle, 0x200, &result)) + if (sony_call_snc_handle(key_handle, 0x200, &result)) { dprintk("sony_acpi_notify, unable to decode" " event 0x%.2x 0x%.2x\n", key_handle, ev); - else + /* restore the original event */ + ev = event; + } else { ev = result & 0xFF; - if (key_handle == 0x100) - key_event = sony_100_events; - else - key_event = sony_127_events; + if (key_handle == 0x100) + key_event = sony_100_events; + else + key_event = sony_127_events; - for (; key_event->data; key_event++) { - if (key_event->data == ev) { - ev = key_event->event; - break; + for (; key_event->data; key_event++) { + if (key_event->data == ev) { + ev = key_event->event; + break; + } } - } - if (!key_event->data) { - printk(KERN_INFO DRV_PFX - "Unknown event: 0x%x 0x%x\n", key_handle, - ev); + if (!key_event->data) + printk(KERN_INFO DRV_PFX + "Unknown event: 0x%x 0x%x\n", + key_handle, + ev); + else + sony_laptop_report_input_event(ev); } } else if (sony_find_snc_handle(0x124) == ev) { sony_nc_rfkill_update(); return; } - } + } else + sony_laptop_report_input_event(ev); dprintk("sony_acpi_notify, event: 0x%.2x\n", ev); - sony_laptop_report_input_event(ev); acpi_bus_generate_proc_event(sony_nc_acpi_device, 1, ev); } -- cgit v1.2.3 From 0b3f6109f0c9ff926b5ffc80dc1cebb24f192b35 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Jan 2009 20:17:11 +0000 Subject: dell-wmi: new driver for hotkey control Add a WMI driver for Dell laptops. Currently it does nothing but send a generic input event when a button with a picture of a battery on it is pressed, but maybe other uses will appear over time. Signed-off-by: Matthew Garrett Cc: Dmitry Torokhov Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- MAINTAINERS | 5 + drivers/platform/x86/Kconfig | 10 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/dell-wmi.c | 210 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 drivers/platform/x86/dell-wmi.c (limited to 'drivers/platform') diff --git a/MAINTAINERS b/MAINTAINERS index 5d460c9d1c2c..4ee59641c16c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1393,6 +1393,11 @@ P: Doug Warzecha M: Douglas_Warzecha@dell.com S: Maintained +DELL WMI EXTRAS DRIVER +P: Matthew Garrett +M: mjg59@srcf.ucam.org +S: Maintained + DEVICE NUMBER REGISTRY P: Torben Mathiasen M: device@lanana.org diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 3608081bc3e0..a27bff3007c8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -67,6 +67,16 @@ config DELL_LAPTOP This driver adds support for rfkill and backlight control to Dell laptops. +config DELL_WMI + tristate "Dell WMI extras" + depends on ACPI_WMI + depends on INPUT + ---help--- + Say Y here if you want to support WMI-based hotkeys on Dell laptops. + + To compile this driver as a module, choose M here: the module will + be called dell-wmi. + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e29065120be9..e40c7bd1b87e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o +obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c new file mode 100644 index 000000000000..2fab94162147 --- /dev/null +++ b/drivers/platform/x86/dell-wmi.c @@ -0,0 +1,210 @@ +/* + * Dell WMI hotkeys + * + * Copyright (C) 2008 Red Hat + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * 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 + +MODULE_AUTHOR("Matthew Garrett "); +MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); +MODULE_LICENSE("GPL"); + +#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" + +MODULE_ALIAS("wmi:"DELL_EVENT_GUID); + +struct key_entry { + char type; /* See KE_* below */ + u16 code; + u16 keycode; +}; + +enum { KE_KEY, KE_SW, KE_END }; + +static struct key_entry dell_wmi_keymap[] = { + {KE_KEY, 0xe045, KEY_PROG1}, + {KE_END, 0} +}; + +static struct input_dev *dell_wmi_input_dev; + +static struct key_entry *dell_wmi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *dell_wmi_get_entry_by_keycode(int keycode) +{ + struct key_entry *key; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) + if (key->type == KE_KEY && keycode == key->keycode) + return key; + + return NULL; +} + +static int dell_wmi_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct key_entry *key = dell_wmi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int dell_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 = dell_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 (!dell_wmi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + return -EINVAL; +} + +static void dell_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + static struct key_entry *key; + union acpi_object *obj; + + wmi_get_event_data(value, &response); + + obj = (union acpi_object *)response.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER) { + int *buffer = (int *)obj->buffer.pointer; + key = dell_wmi_get_entry_by_scancode(buffer[1]); + if (key) { + 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 + printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", + buffer[1]); + } +} + +static int __init dell_wmi_input_setup(void) +{ + struct key_entry *key; + int err; + + dell_wmi_input_dev = input_allocate_device(); + + if (!dell_wmi_input_dev) + return -ENOMEM; + + dell_wmi_input_dev->name = "Dell WMI hotkeys"; + dell_wmi_input_dev->phys = "wmi/input0"; + dell_wmi_input_dev->id.bustype = BUS_HOST; + dell_wmi_input_dev->getkeycode = dell_wmi_getkeycode; + dell_wmi_input_dev->setkeycode = dell_wmi_setkeycode; + + for (key = dell_wmi_keymap; key->type != KE_END; key++) { + switch (key->type) { + case KE_KEY: + set_bit(EV_KEY, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->keybit); + break; + case KE_SW: + set_bit(EV_SW, dell_wmi_input_dev->evbit); + set_bit(key->keycode, dell_wmi_input_dev->swbit); + break; + } + } + + err = input_register_device(dell_wmi_input_dev); + + if (err) { + input_free_device(dell_wmi_input_dev); + return err; + } + + return 0; +} + +static int __init dell_wmi_init(void) +{ + int err; + + if (wmi_has_guid(DELL_EVENT_GUID)) { + err = dell_wmi_input_setup(); + + if (err) + return err; + + err = wmi_install_notify_handler(DELL_EVENT_GUID, + dell_wmi_notify, NULL); + if (err) { + input_unregister_device(dell_wmi_input_dev); + printk(KERN_ERR "dell-wmi: Unable to register" + " notify handler - %d\n", err); + return err; + } + + } else + printk(KERN_WARNING "dell-wmi: No known WMI GUID found\n"); + + return 0; +} + +static void __exit dell_wmi_exit(void) +{ + if (wmi_has_guid(DELL_EVENT_GUID)) { + wmi_remove_notify_handler(DELL_EVENT_GUID); + input_unregister_device(dell_wmi_input_dev); + } +} + +module_init(dell_wmi_init); +module_exit(dell_wmi_exit); -- cgit v1.2.3 From c542aadeb4700bc316834d862d52ba3d2664f13a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 14 Jan 2009 13:59:50 +0800 Subject: panasonic-laptop: Fix autoloading This patch adds MODULE_DEVICE_TABLE() to panasonic-laptop.c in order to ensure automatic loading of the module on systems with the respective "MAT*" ACPI devices. Signed-off-by: Martin Lucina Signed-off-by: Harald Welte Signed-off-by: Len Brown --- drivers/platform/x86/panasonic-laptop.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index c47a44dcb702..ecaca412a2a1 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -184,6 +184,7 @@ static const struct acpi_device_id pcc_device_ids[] = { { "MAT0019", 0}, { "", 0}, }; +MODULE_DEVICE_TABLE(acpi, pcc_device_ids); static struct acpi_driver acpi_pcc_driver = { .name = ACPI_PCC_DRIVER_NAME, -- cgit v1.2.3 From ba256b41bd58f0821c9c0e203982fb260f09bdeb Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 14 Jan 2009 14:01:17 +0800 Subject: panasonic-laptop: use snprintf with PAGE_SIZE in sysfs attributes Instead of just sprintf() into the page-sized buffer provided by the sysfs/device_attribute API, we use snprintf with PAGE_SIZE as an additional safeguard. Signed-off-by: Martin Lucina Signed-off-by: Harald Welte Signed-off-by: Len Brown --- drivers/platform/x86/panasonic-laptop.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ecaca412a2a1..a5ce4bc202e3 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -367,7 +367,7 @@ static ssize_t show_numbatt(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; - return sprintf(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); } static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, @@ -379,7 +379,7 @@ static ssize_t show_lcdtype(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; - return sprintf(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]); + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); } static ssize_t show_mute(struct device *dev, struct device_attribute *attr, @@ -391,7 +391,7 @@ static ssize_t show_mute(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; - return sprintf(buf, "%u\n", pcc->sinf[SINF_MUTE]); + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); } static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, @@ -403,7 +403,7 @@ static ssize_t show_sticky(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc, pcc->sinf)) return -EIO; - return sprintf(buf, "%u\n", pcc->sinf[SINF_STICKY_KEY]); + return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_STICKY_KEY]); } static ssize_t set_sticky(struct device *dev, struct device_attribute *attr, -- cgit v1.2.3 From 1c762ca438447fa3525d84f4a0784a2021a66200 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:42 +0000 Subject: thinkpad-acpi: update copyright notices It is that time of the year again... Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d2433204a40c..792d22e958cb 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3,7 +3,7 @@ * * * Copyright (C) 2004-2005 Borislav Deianov - * Copyright (C) 2006-2008 Henrique de Moraes Holschuh + * Copyright (C) 2006-2009 Henrique de Moraes Holschuh * * 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 -- cgit v1.2.3 From 257bc1cb3e29c8da62b9c9e0a4505011776c7040 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:43 +0000 Subject: thinkpad-acpi: drop ibm-acpi alias The driver was renamed two years ago, on 2.6.21. Drop the old compatibility alias, we have given everybody quite enough time to update their configs to the new name. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 792d22e958cb..c83ec94eb8d9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -7517,9 +7517,6 @@ static int __init thinkpad_acpi_module_init(void) return 0; } -/* Please remove this in year 2009 */ -MODULE_ALIAS("ibm_acpi"); - MODULE_ALIAS(TPACPI_DRVR_SHORTNAME); /* -- cgit v1.2.3 From 3dcc2c3b00cad01a0e3667607f8644e891e4dc8b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:45 +0000 Subject: thinkpad-acpi: cleanup debug helpers Fix the vdbg_printk macro definition to be sane when CONFIG_THINKPAD_ACPI_DEBUG is undefined, and move the mess into a file section of its own. This doesn't change anything in the current code, but future code will need the proper behaviour. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index c83ec94eb8d9..3367df9d4f99 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -184,17 +184,6 @@ enum { #define TPACPI_DBG_ALL 0xffff #define TPACPI_DBG_INIT 0x0001 #define TPACPI_DBG_EXIT 0x0002 -#define dbg_printk(a_dbg_level, format, arg...) \ - do { if (dbg_level & a_dbg_level) \ - printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \ - } while (0) -#ifdef CONFIG_THINKPAD_ACPI_DEBUG -#define vdbg_printk(a_dbg_level, format, arg...) \ - dbg_printk(a_dbg_level, format, ## arg) -static const char *str_supported(int is_supported); -#else -#define vdbg_printk(a_dbg_level, format, arg...) -#endif #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -326,6 +315,24 @@ static int tpacpi_uwb_emulstate; #endif +/************************************************************************* + * Debugging helpers + */ + +#define dbg_printk(a_dbg_level, format, arg...) \ + do { if (dbg_level & (a_dbg_level)) \ + printk(TPACPI_DEBUG "%s: " format, __func__ , ## arg); \ + } while (0) + +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +#define vdbg_printk dbg_printk +static const char *str_supported(int is_supported); +#else +#define vdbg_printk(a_dbg_level, format, arg...) \ + do { } while (0) +#endif + + /**************************************************************************** **************************************************************************** * -- cgit v1.2.3 From 7ff8d62f7f055aaffbeb493863136c1b876bbe2e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:46 +0000 Subject: thinkpad-acpi: add missing log levels Add missing log levels in a standalone commit, to avoid dependencies in future unrelated changes, just because they wanted to use one of the missing log levels. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- drivers/platform/x86/thinkpad_acpi.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3367df9d4f99..6331b88d77d9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -172,15 +172,18 @@ enum { TPACPI_RFK_UWB_SW_ID, }; -/* Debugging */ +/* printk headers */ #define TPACPI_LOG TPACPI_FILE ": " -#define TPACPI_ALERT KERN_ALERT TPACPI_LOG -#define TPACPI_CRIT KERN_CRIT TPACPI_LOG -#define TPACPI_ERR KERN_ERR TPACPI_LOG -#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG -#define TPACPI_INFO KERN_INFO TPACPI_LOG -#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG - +#define TPACPI_EMERG KERN_EMERG TPACPI_LOG +#define TPACPI_ALERT KERN_ALERT TPACPI_LOG +#define TPACPI_CRIT KERN_CRIT TPACPI_LOG +#define TPACPI_ERR KERN_ERR TPACPI_LOG +#define TPACPI_WARN KERN_WARNING TPACPI_LOG +#define TPACPI_NOTICE KERN_NOTICE TPACPI_LOG +#define TPACPI_INFO KERN_INFO TPACPI_LOG +#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG + +/* Debugging printk groups */ #define TPACPI_DBG_ALL 0xffff #define TPACPI_DBG_INIT 0x0001 #define TPACPI_DBG_EXIT 0x0002 -- cgit v1.2.3 From 73a94d86a8625371f76de0ee12dc5bacd3ed42c0 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:47 +0000 Subject: thinkpad-acpi: add new debug helpers and warn of deprecated atts Add a debug helper that discloses the TGID of the userspace task attempting to access the driver. This is highly useful when dealing with bug reports, since often the user has no idea that some userspace application is accessing thinkpad-acpi... Also add a helper to log warnings about sysfs attributes that are deprecated. Use the new helpers to issue deprecation warnings for bluetooth_enable and wwan_enabled, that have been deprecated for a while, now. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 2 ++ drivers/platform/x86/thinkpad_acpi.c | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index f6e6bc66eed3..e8f52fbadfe0 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1484,6 +1484,8 @@ will enable all debugging output classes. It takes a bitmask, so to enable more than one output class, just add their values. Debug bitmask Description + 0x8000 Disclose PID of userspace programs + accessing some functions of the driver 0x0001 Initialization and probing 0x0002 Removal diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 6331b88d77d9..852be7c1a172 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -185,6 +186,7 @@ enum { /* Debugging printk groups */ #define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_DISCLOSETASK 0x8000 #define TPACPI_DBG_INIT 0x0001 #define TPACPI_DBG_EXIT 0x0002 @@ -335,6 +337,21 @@ static const char *str_supported(int is_supported); do { } while (0) #endif +static void tpacpi_log_usertask(const char * const what) +{ + printk(TPACPI_DEBUG "%s: access by process with PID %d\n", + what, task_tgid_vnr(current)); +} + +#define tpacpi_disclose_usertask(what, format, arg...) \ + do { \ + if (unlikely( \ + (dbg_level & TPACPI_DBG_DISCLOSETASK) && \ + (tpacpi_lifecycle == TPACPI_LIFE_RUNNING))) { \ + printk(TPACPI_DEBUG "%s: PID %d: " format, \ + what, task_tgid_vnr(current), ## arg); \ + } \ + } while (0) /**************************************************************************** **************************************************************************** @@ -1030,6 +1047,21 @@ static int __init tpacpi_new_rfkill(const unsigned int id, return 0; } +static void printk_deprecated_attribute(const char * const what, + const char * const details) +{ + tpacpi_log_usertask("deprecated sysfs attribute"); + printk(TPACPI_WARN "WARNING: sysfs attribute %s is deprecated and " + "will be removed. %s\n", + what, details); +} + +static void printk_deprecated_rfkill_attribute(const char * const what) +{ + printk_deprecated_attribute(what, + "Please switch to generic rfkill before year 2010"); +} + /************************************************************************* * thinkpad-acpi driver attributes */ @@ -3070,6 +3102,8 @@ static ssize_t bluetooth_enable_show(struct device *dev, { int status; + printk_deprecated_rfkill_attribute("bluetooth_enable"); + status = bluetooth_get_radiosw(); if (status < 0) return status; @@ -3085,6 +3119,8 @@ static ssize_t bluetooth_enable_store(struct device *dev, unsigned long t; int res; + printk_deprecated_rfkill_attribute("bluetooth_enable"); + if (parse_strtoul(buf, 1, &t)) return -EINVAL; @@ -3347,6 +3383,8 @@ static ssize_t wan_enable_show(struct device *dev, { int status; + printk_deprecated_rfkill_attribute("wwan_enable"); + status = wan_get_radiosw(); if (status < 0) return status; @@ -3362,6 +3400,8 @@ static ssize_t wan_enable_store(struct device *dev, unsigned long t; int res; + printk_deprecated_rfkill_attribute("wwan_enable"); + if (parse_strtoul(buf, 1, &t)) return -EINVAL; -- cgit v1.2.3 From 2586d5663d0a17d69383acf6110f16a979a07c4e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:48 +0000 Subject: thinkpad-acpi: remove HKEY disable functionality The HKEY disable functionality basically cripples the entire event model of the ThinkPad firmware and of the thinkpad-acpi driver. Remove this functionality from the driver. HKEY must be enabled at all times while thinkpad-acpi is loaded, and disabled otherwise. For sysfs, according to the sysfs ABI and the thinkpad-acpi sysfs rules of engagement, we will just remove the attributes. This will be done in two stages: disable their function now, after two kernel releases, remove the attributes. For procfs, we call WARN(). If nothing triggers it, I will simply remove the enable/disable commands entirely in the future along with the sysfs attributes. I don't expect much, if any fallout from this. There really isn't any reason to mess with hotkey_enable or with the enable/disable commands to /proc/acpi/ibm/hotkey, and this has been true for years... Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 39 +++++++++++++------------- drivers/platform/x86/thinkpad_acpi.c | 49 ++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 42 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index e8f52fbadfe0..de6f14c79070 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -179,17 +179,14 @@ system. Enabling the hotkey functionality of thinkpad-acpi signals the firmware that such a driver is present, and modifies how the ThinkPad firmware will behave in many situations. -The driver enables the hot key feature automatically when loaded. The -feature can later be disabled and enabled back at runtime. The driver -will also restore the hot key feature to its previous state and mask -when it is unloaded. +The driver enables the HKEY ("hot key") event reporting automatically +when loaded, and disables it when it is removed. -When the hotkey feature is enabled and the hot key mask is set (see -below), the driver will report HKEY events in the following format: +The driver will report HKEY events in the following format: ibm/hotkey HKEY 00000080 0000xxxx -Some of these events refer to hot key presses, but not all. +Some of these events refer to hot key presses, but not all of them. The driver will generate events over the input layer for hot keys and radio switches, and over the ACPI netlink layer for other events. The @@ -221,13 +218,17 @@ procfs notes: The following commands can be written to the /proc/acpi/ibm/hotkey file: - echo enable > /proc/acpi/ibm/hotkey -- enable the hot keys feature - echo disable > /proc/acpi/ibm/hotkey -- disable the hot keys feature echo 0xffffffff > /proc/acpi/ibm/hotkey -- enable all hot keys echo 0 > /proc/acpi/ibm/hotkey -- disable all possible hot keys ... any other 8-hex-digit mask ... echo reset > /proc/acpi/ibm/hotkey -- restore the original mask +The following commands have been deprecated and will cause the kernel +to log a warning: + + echo enable > /proc/acpi/ibm/hotkey -- does nothing + echo disable > /proc/acpi/ibm/hotkey -- returns an error + The procfs interface does not support NVRAM polling control. So as to maintain maximum bug-to-bug compatibility, it does not report any masks, nor does it allow one to manipulate the hot key mask when the firmware @@ -236,12 +237,9 @@ does not support masks at all, even if NVRAM polling is in use. sysfs notes: hotkey_bios_enabled: - Returns the status of the hot keys feature when - thinkpad-acpi was loaded. Upon module unload, the hot - key feature status will be restored to this value. + DEPRECATED, WILL BE REMOVED SOON. - 0: hot keys were disabled - 1: hot keys were enabled (unusual) + Returns 0. hotkey_bios_mask: Returns the hot keys mask when thinkpad-acpi was loaded. @@ -249,13 +247,10 @@ sysfs notes: to this value. hotkey_enable: - Enables/disables the hot keys feature in the ACPI - firmware, and reports current status of the hot keys - feature. Has no effect on the NVRAM hot key polling - functionality. + DEPRECATED, WILL BE REMOVED SOON. - 0: disables the hot keys feature / feature disabled - 1: enables the hot keys feature / feature enabled + 0: returns -EPERM + 1: does nothing hotkey_mask: bit mask to enable driver-handling (and depending on @@ -1535,3 +1530,7 @@ Sysfs interface changelog: 0x020200: Add poll()/select() support to the following attributes: hotkey_radio_sw, wakeup_hotunplug_complete, wakeup_reason + +0x020300: hotkey enable/disable support removed, attributes + hotkey_bios_enabled and hotkey_enable deprecated and + marked for removal. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 852be7c1a172..f003fb7c79ca 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -22,7 +22,7 @@ */ #define TPACPI_VERSION "0.22" -#define TPACPI_SYSFS_VERSION 0x020200 +#define TPACPI_SYSFS_VERSION 0x020300 /* * Changelog: @@ -1424,7 +1424,6 @@ static enum { /* Reasons for waking up */ static int hotkey_autosleep_ack; -static int hotkey_orig_status; static u32 hotkey_orig_mask; static u32 hotkey_all_mask; static u32 hotkey_reserved_mask; @@ -1571,9 +1570,9 @@ static int hotkey_status_get(int *status) return 0; } -static int hotkey_status_set(int status) +static int hotkey_status_set(bool enable) { - if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) + if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", enable ? 1 : 0)) return -EIO; return 0; @@ -1889,6 +1888,9 @@ static ssize_t hotkey_enable_show(struct device *dev, { int res, status; + printk_deprecated_attribute("hotkey_enable", + "Hotkey reporting is always enabled"); + res = hotkey_status_get(&status); if (res) return res; @@ -1901,14 +1903,17 @@ static ssize_t hotkey_enable_store(struct device *dev, const char *buf, size_t count) { unsigned long t; - int res; + + printk_deprecated_attribute("hotkey_enable", + "Hotkeys can be disabled through hotkey_mask"); if (parse_strtoul(buf, 1, &t)) return -EINVAL; - res = hotkey_status_set(t); + if (t == 0) + return -EPERM; - return (res) ? res : count; + return count; } static struct device_attribute dev_attr_hotkey_enable = @@ -1964,7 +1969,7 @@ static ssize_t hotkey_bios_enabled_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status); + return sprintf(buf, "0\n"); } static struct device_attribute dev_attr_hotkey_bios_enabled = @@ -2243,7 +2248,7 @@ static void hotkey_exit(void) "restoring original hot key mask\n"); /* no short-circuit boolean operator below! */ if ((hotkey_mask_set(hotkey_orig_mask) | - hotkey_status_set(hotkey_orig_status)) != 0) + hotkey_status_set(false)) != 0) printk(TPACPI_ERR "failed to restore hot key mask " "to BIOS defaults\n"); @@ -2438,10 +2443,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* hotkey_source_mask *must* be zero for * the first hotkey_mask_get */ - res = hotkey_status_get(&hotkey_orig_status); - if (res) - goto err_exit; - if (tp_features.hotkey_mask) { res = hotkey_mask_get(); if (res) @@ -2581,7 +2582,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n"); - res = hotkey_status_set(1); + res = hotkey_status_set(true); if (res) { hotkey_exit(); return res; @@ -2926,9 +2927,17 @@ static int hotkey_read(char *p) return len; } +static void hotkey_enabledisable_warn(void) +{ + tpacpi_log_usertask("procfs hotkey enable/disable"); + WARN(1, TPACPI_WARN + "hotkey enable/disable functionality has been " + "removed from the driver. Hotkeys are always enabled.\n"); +} + static int hotkey_write(char *buf) { - int res, status; + int res; u32 mask; char *cmd; @@ -2938,17 +2947,16 @@ static int hotkey_write(char *buf) if (mutex_lock_killable(&hotkey_mutex)) return -ERESTARTSYS; - status = -1; mask = hotkey_mask; res = 0; while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - status = 1; + hotkey_enabledisable_warn(); } else if (strlencmp(cmd, "disable") == 0) { - status = 0; + hotkey_enabledisable_warn(); + res = -EPERM; } else if (strlencmp(cmd, "reset") == 0) { - status = hotkey_orig_status; mask = hotkey_orig_mask; } else if (sscanf(cmd, "0x%x", &mask) == 1) { /* mask set */ @@ -2959,9 +2967,6 @@ static int hotkey_write(char *buf) goto errexit; } } - if (status != -1) - res = hotkey_status_set(status); - if (!res && mask != hotkey_mask) res = hotkey_mask_set(mask); -- cgit v1.2.3 From a4d5effcc73749ee3ebbf578d162905e6fa4e07d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:49 +0000 Subject: thinkpad-acpi: restrict access to some firmware LEDs Some of the ThinkPad LEDs indicate critical conditions that can cause data loss or cause hardware damage when ignored (e.g. force-ejecting a powered up bay; ignoring a failing battery, or empty battery; force- undocking with the dock buses still active, etc). On almost all ThinkPads, LED access is write-only, and the firmware usually does fire-and-forget signaling on them, so you effectively lose whatever message the firmware was trying to convey to the user when you override the LED state, without any chance to restore it. Restrict access to all LEDs that can convey important alarms, or that could mislead the user into incorrectly operating the hardware. This will make the Lenovo engineers less unhappy about the whole issue. Allow users that really want it to still control all LEDs, it is the unaware user that we have to worry about. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 11 +++++ drivers/platform/x86/Kconfig | 24 +++++++++++ drivers/platform/x86/thinkpad_acpi.c | 76 ++++++++++++++++++++++++--------- 3 files changed, 91 insertions(+), 20 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index de6f14c79070..bce1d959b703 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -901,6 +901,17 @@ some older ThinkPad models, it is possible to query the status of the LED indicators as well. Newer ThinkPads cannot query the real status of the LED indicators. +Because misuse of the LEDs could induce an unaware user to perform +dangerous actions (like undocking or ejecting a bay device while the +buses are still active), or mask an important alarm (such as a nearly +empty battery, or a broken battery), access to most LEDs is +restricted. + +Unrestricted access to all LEDs requires that thinkpad-acpi be +compiled with the CONFIG_THINKPAD_ACPI_UNSAFE_LEDS option enabled. +Distributions must never enable this option. Individual users that +are aware of the consequences are welcome to enabling it. + procfs notes: The available commands are: diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 3608081bc3e0..d45c6ab729f8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -226,6 +226,30 @@ config THINKPAD_ACPI_DEBUG If you are not sure, say N here. +config THINKPAD_ACPI_UNSAFE_LEDS + bool "Allow control of important LEDs (unsafe)" + depends on THINKPAD_ACPI + default n + ---help--- + Overriding LED state on ThinkPads can mask important + firmware alerts (like critical battery condition), or misled + the user into damaging the hardware (undocking or ejecting + the bay while buses are still active), etc. + + LED control on the ThinkPad is write-only (with very few + exceptions on very ancient models), which makes it + impossible to know beforehand if important information will + be lost when one changes LED state. + + Users that know what they are doing can enable this option + and the driver will allow control of every LED, including + the ones on the dock stations. + + Never enable this option on a distribution kernel. + + Say N here, unless you are building a kernel for your own + use, and need to control the important firmware LEDs. + config THINKPAD_ACPI_DOCK bool "Legacy Docking Station Support" depends on THINKPAD_ACPI diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f003fb7c79ca..38c34c79ff35 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -4657,6 +4657,16 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { "tpacpi::unknown_led", "tpacpi::standby", }; +#define TPACPI_SAFE_LEDS 0x0081U + +static inline bool tpacpi_is_led_restricted(const unsigned int led) +{ +#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS + return false; +#else + return (TPACPI_SAFE_LEDS & (1 << led)) == 0; +#endif +} static int led_get_status(const unsigned int led) { @@ -4694,16 +4704,20 @@ static int led_set_status(const unsigned int led, switch (led_supported) { case TPACPI_LED_570: /* 570 */ - if (led > 7) + if (unlikely(led > 7)) return -EINVAL; + if (unlikely(tpacpi_is_led_restricted(led))) + return -EPERM; if (!acpi_evalf(led_handle, NULL, NULL, "vdd", (1 << led), led_sled_arg1[ledstatus])) rc = -EIO; break; case TPACPI_LED_OLD: /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ - if (led > 7) + if (unlikely(led > 7)) return -EINVAL; + if (unlikely(tpacpi_is_led_restricted(led))) + return -EPERM; rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led)); if (rc >= 0) rc = ec_write(TPACPI_LED_EC_HLBL, @@ -4714,6 +4728,10 @@ static int led_set_status(const unsigned int led, break; case TPACPI_LED_NEW: /* all others */ + if (unlikely(led >= TPACPI_LED_NUMLEDS)) + return -EINVAL; + if (unlikely(tpacpi_is_led_restricted(led))) + return -EPERM; if (!acpi_evalf(led_handle, NULL, NULL, "vdd", led, led_led_arg1[ledstatus])) rc = -EIO; @@ -4806,6 +4824,30 @@ static void led_exit(void) kfree(tpacpi_leds); } +static int __init tpacpi_init_led(unsigned int led) +{ + int rc; + + tpacpi_leds[led].led = led; + + tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; + tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; + if (led_supported == TPACPI_LED_570) + tpacpi_leds[led].led_classdev.brightness_get = + &led_sysfs_get; + + tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led]; + + INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker); + + rc = led_classdev_register(&tpacpi_pdev->dev, + &tpacpi_leds[led].led_classdev); + if (rc < 0) + tpacpi_leds[led].led_classdev.name = NULL; + + return rc; +} + static int __init led_init(struct ibm_init_struct *iibm) { unsigned int i; @@ -4839,27 +4881,21 @@ static int __init led_init(struct ibm_init_struct *iibm) } for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { - tpacpi_leds[i].led = i; - - tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; - tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; - if (led_supported == TPACPI_LED_570) - tpacpi_leds[i].led_classdev.brightness_get = - &led_sysfs_get; - - tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i]; - - INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker); - - rc = led_classdev_register(&tpacpi_pdev->dev, - &tpacpi_leds[i].led_classdev); - if (rc < 0) { - tpacpi_leds[i].led_classdev.name = NULL; - led_exit(); - return rc; + if (!tpacpi_is_led_restricted(i)) { + rc = tpacpi_init_led(i); + if (rc < 0) { + led_exit(); + return rc; + } } } +#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS + if (led_supported != TPACPI_LED_NONE) + printk(TPACPI_NOTICE + "warning: userspace override of important " + "firmware LEDs is enabled\n"); +#endif return (led_supported != TPACPI_LED_NONE)? 0 : 1; } -- cgit v1.2.3 From bee4cd9b9eaa8c72832e1ee7f4940604e94beb27 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:50 +0000 Subject: thinkpad-acpi: enhanced debugging messages for rfkill subdrivers Enhance debugging messages for all rfkill subdrivers in thinkpad-acpi. Also, log a warning if the deprecated sysfs attributes are in use. These attributes are going to be removed sometime in 2010. There is an user-visible side-effect: we now coalesce attempts to enable/disable bluetooth or WWAN in the procfs interface, instead of hammering the firmware with multiple requests. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 2 + drivers/platform/x86/thinkpad_acpi.c | 117 ++++++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 21 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index bce1d959b703..7daca0553676 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1494,6 +1494,8 @@ to enable more than one output class, just add their values. accessing some functions of the driver 0x0001 Initialization and probing 0x0002 Removal + 0x0004 RF Transmitter control (RFKILL) + (bluetooth, WWAN, UWB...) 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 38c34c79ff35..57ab5512c796 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -189,6 +189,7 @@ enum { #define TPACPI_DBG_DISCLOSETASK 0x8000 #define TPACPI_DBG_INIT 0x0001 #define TPACPI_DBG_EXIT 0x0002 +#define TPACPI_DBG_RFKILL 0x0004 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -1016,10 +1017,13 @@ static int __init tpacpi_new_rfkill(const unsigned int id, /* try to set the initial state as the default for the rfkill * type, since we ask the firmware to preserve it across S5 in * NVRAM */ - rfkill_set_default(rfktype, + if (rfkill_set_default(rfktype, (initial_state == RFKILL_STATE_UNBLOCKED) ? RFKILL_STATE_UNBLOCKED : - RFKILL_STATE_SOFT_BLOCKED); + RFKILL_STATE_SOFT_BLOCKED) == -EPERM) + vdbg_printk(TPACPI_DBG_RFKILL, + "Default state for %s cannot be changed\n", + name); } *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype); @@ -3018,13 +3022,17 @@ enum { TP_ACPI_BLTH_SAVE_STATE = 0x05, /* Save state for S4/S5 */ }; +#define TPACPI_RFK_BLUETOOTH_SW_NAME "tpacpi_bluetooth_sw" + static struct rfkill *tpacpi_bluetooth_rfkill; static void bluetooth_suspend(pm_message_t state) { /* Try to make sure radio will resume powered off */ - acpi_evalf(NULL, NULL, "\\BLTH", "vd", - TP_ACPI_BLTH_PWR_OFF_ON_RESUME); + 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_radiosw(void) @@ -3062,6 +3070,10 @@ static void bluetooth_update_rfk(void) if (status < 0) return; rfkill_force_state(tpacpi_bluetooth_rfkill, status); + + vdbg_printk(TPACPI_DBG_RFKILL, + "forced rfkill state to %d\n", + status); } static int bluetooth_set_radiosw(int radio_on, int update_rfk) @@ -3077,6 +3089,9 @@ static int bluetooth_set_radiosw(int radio_on, int update_rfk) && radio_on) return -EPERM; + vdbg_printk(TPACPI_DBG_RFKILL, + "will %s bluetooth\n", radio_on ? "enable" : "disable"); + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_bluetoothemul) { tpacpi_bluetooth_emulstate = !!radio_on; @@ -3129,6 +3144,8 @@ static ssize_t bluetooth_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; + tpacpi_disclose_usertask("bluetooth_enable", "set to %ld\n", t); + res = bluetooth_set_radiosw(t, 1); return (res) ? res : count; @@ -3162,6 +3179,8 @@ static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state) static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state) { + dbg_printk(TPACPI_DBG_RFKILL, + "request to change radio state to %d\n", state); return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); } @@ -3172,6 +3191,9 @@ static void bluetooth_shutdown(void) TP_ACPI_BLTH_SAVE_STATE)) printk(TPACPI_NOTICE "failed to save bluetooth state to NVRAM\n"); + else + vdbg_printk(TPACPI_DBG_RFKILL, + "bluestooth state saved to NVRAM\n"); } static void bluetooth_exit(void) @@ -3190,7 +3212,8 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) int res; int status = 0; - vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "initializing bluetooth subdriver\n"); TPACPI_ACPIHANDLE_INIT(hkey); @@ -3199,7 +3222,8 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) tp_features.bluetooth = hkey_handle && acpi_evalf(hkey_handle, &status, "GBDC", "qd"); - vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "bluetooth is %s, status 0x%02x\n", str_supported(tp_features.bluetooth), status); @@ -3214,7 +3238,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { /* no bluetooth hardware present in system */ tp_features.bluetooth = 0; - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, "bluetooth hardware not installed\n"); } @@ -3229,7 +3253,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm) res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID, &tpacpi_bluetooth_rfkill, RFKILL_TYPE_BLUETOOTH, - "tpacpi_bluetooth_sw", + TPACPI_RFK_BLUETOOTH_SW_NAME, true, tpacpi_bluetooth_rfk_set, tpacpi_bluetooth_rfk_get); @@ -3262,19 +3286,27 @@ static int bluetooth_read(char *p) static int bluetooth_write(char *buf) { char *cmd; + int state = -1; if (!tp_features.bluetooth) return -ENODEV; while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - bluetooth_set_radiosw(1, 1); + state = 1; } else if (strlencmp(cmd, "disable") == 0) { - bluetooth_set_radiosw(0, 1); + state = 0; } else return -EINVAL; } + if (state != -1) { + tpacpi_disclose_usertask("procfs bluetooth", + "attempt to %s\n", + state ? "enable" : "disable"); + bluetooth_set_radiosw(state, 1); + } + return 0; } @@ -3299,13 +3331,17 @@ enum { off / last state */ }; +#define TPACPI_RFK_WWAN_SW_NAME "tpacpi_wwan_sw" + static struct rfkill *tpacpi_wan_rfkill; static void wan_suspend(pm_message_t state) { /* Try to make sure radio will resume powered off */ - acpi_evalf(NULL, NULL, "\\WGSV", "qvd", - TP_ACPI_WGSV_PWR_OFF_ON_RESUME); + 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_radiosw(void) @@ -3343,6 +3379,10 @@ static void wan_update_rfk(void) if (status < 0) return; rfkill_force_state(tpacpi_wan_rfkill, status); + + vdbg_printk(TPACPI_DBG_RFKILL, + "forced rfkill state to %d\n", + status); } static int wan_set_radiosw(int radio_on, int update_rfk) @@ -3358,6 +3398,9 @@ static int wan_set_radiosw(int radio_on, int update_rfk) && radio_on) return -EPERM; + vdbg_printk(TPACPI_DBG_RFKILL, + "will %s WWAN\n", radio_on ? "enable" : "disable"); + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wwanemul) { tpacpi_wwan_emulstate = !!radio_on; @@ -3410,6 +3453,8 @@ static ssize_t wan_enable_store(struct device *dev, if (parse_strtoul(buf, 1, &t)) return -EINVAL; + tpacpi_disclose_usertask("wwan_enable", "set to %ld\n", t); + res = wan_set_radiosw(t, 1); return (res) ? res : count; @@ -3443,6 +3488,8 @@ static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state) static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state) { + dbg_printk(TPACPI_DBG_RFKILL, + "request to change radio state to %d\n", state); return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); } @@ -3453,6 +3500,9 @@ static void wan_shutdown(void) TP_ACPI_WGSV_SAVE_STATE)) printk(TPACPI_NOTICE "failed to save WWAN state to NVRAM\n"); + else + vdbg_printk(TPACPI_DBG_RFKILL, + "WWAN state saved to NVRAM\n"); } static void wan_exit(void) @@ -3471,14 +3521,16 @@ static int __init wan_init(struct ibm_init_struct *iibm) int res; int status = 0; - vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "initializing wan subdriver\n"); TPACPI_ACPIHANDLE_INIT(hkey); tp_features.wan = hkey_handle && acpi_evalf(hkey_handle, &status, "GWAN", "qd"); - vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "wan is %s, status 0x%02x\n", str_supported(tp_features.wan), status); @@ -3493,7 +3545,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) !(status & TP_ACPI_WANCARD_HWPRESENT)) { /* no wan hardware present in system */ tp_features.wan = 0; - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, "wan hardware not installed\n"); } @@ -3508,7 +3560,7 @@ static int __init wan_init(struct ibm_init_struct *iibm) res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID, &tpacpi_wan_rfkill, RFKILL_TYPE_WWAN, - "tpacpi_wwan_sw", + TPACPI_RFK_WWAN_SW_NAME, true, tpacpi_wan_rfk_set, tpacpi_wan_rfk_get); @@ -3526,6 +3578,8 @@ static int wan_read(char *p) int len = 0; int status = wan_get_radiosw(); + tpacpi_disclose_usertask("procfs wan", "read"); + if (!tp_features.wan) len += sprintf(p + len, "status:\t\tnot supported\n"); else { @@ -3541,19 +3595,27 @@ static int wan_read(char *p) static int wan_write(char *buf) { char *cmd; + int state = -1; if (!tp_features.wan) return -ENODEV; while ((cmd = next_cmd(&buf))) { if (strlencmp(cmd, "enable") == 0) { - wan_set_radiosw(1, 1); + state = 1; } else if (strlencmp(cmd, "disable") == 0) { - wan_set_radiosw(0, 1); + state = 0; } else return -EINVAL; } + if (state != -1) { + tpacpi_disclose_usertask("procfs wan", + "attempt to %s\n", + state ? "enable" : "disable"); + wan_set_radiosw(state, 1); + } + return 0; } @@ -3576,6 +3638,8 @@ enum { TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */ }; +#define TPACPI_RFK_UWB_SW_NAME "tpacpi_uwb_sw" + static struct rfkill *tpacpi_uwb_rfkill; static int uwb_get_radiosw(void) @@ -3613,6 +3677,10 @@ static void uwb_update_rfk(void) if (status < 0) return; rfkill_force_state(tpacpi_uwb_rfkill, status); + + vdbg_printk(TPACPI_DBG_RFKILL, + "forced rfkill state to %d\n", + status); } static int uwb_set_radiosw(int radio_on, int update_rfk) @@ -3628,6 +3696,9 @@ static int uwb_set_radiosw(int radio_on, int update_rfk) && radio_on) return -EPERM; + vdbg_printk(TPACPI_DBG_RFKILL, + "will %s UWB\n", radio_on ? "enable" : "disable"); + #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_uwbemul) { tpacpi_uwb_emulstate = !!radio_on; @@ -3662,6 +3733,8 @@ static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state) static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state) { + dbg_printk(TPACPI_DBG_RFKILL, + "request to change radio state to %d\n", state); return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0); } @@ -3676,14 +3749,16 @@ static int __init uwb_init(struct ibm_init_struct *iibm) int res; int status = 0; - vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "initializing uwb subdriver\n"); TPACPI_ACPIHANDLE_INIT(hkey); tp_features.uwb = hkey_handle && acpi_evalf(hkey_handle, &status, "GUWB", "qd"); - vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_RFKILL, + "uwb is %s, status 0x%02x\n", str_supported(tp_features.uwb), status); @@ -3708,7 +3783,7 @@ static int __init uwb_init(struct ibm_init_struct *iibm) res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID, &tpacpi_uwb_rfkill, RFKILL_TYPE_UWB, - "tpacpi_uwb_sw", + TPACPI_RFK_UWB_SW_NAME, false, tpacpi_uwb_rfk_set, tpacpi_uwb_rfk_get); -- cgit v1.2.3 From 56e2c200945dafafb86169762eb1e88aed0ce69e Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:51 +0000 Subject: thinkpad-acpi: enhanced debugging messages for the hotkey subdriver Enhance debugging messages for the hotkey subdriver. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 1 + drivers/platform/x86/thinkpad_acpi.c | 39 ++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 10 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 7daca0553676..abbbe78a003b 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1496,6 +1496,7 @@ to enable more than one output class, just add their values. 0x0002 Removal 0x0004 RF Transmitter control (RFKILL) (bluetooth, WWAN, UWB...) + 0x0008 HKEY event interface, hotkeys 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 57ab5512c796..0a4796a246ea 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -190,6 +190,7 @@ enum { #define TPACPI_DBG_INIT 0x0001 #define TPACPI_DBG_EXIT 0x0002 #define TPACPI_DBG_RFKILL 0x0004 +#define TPACPI_DBG_HKEY 0x0008 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -1961,6 +1962,8 @@ static ssize_t hotkey_mask_store(struct device *dev, mutex_unlock(&hotkey_mutex); + tpacpi_disclose_usertask("hotkey_mask", "set to 0x%08lx\n", t); + return (res) ? res : count; } @@ -2047,6 +2050,8 @@ static ssize_t hotkey_source_mask_store(struct device *dev, mutex_unlock(&hotkey_mutex); + tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t); + return count; } @@ -2079,6 +2084,8 @@ static ssize_t hotkey_poll_freq_store(struct device *dev, hotkey_poll_setup(1); mutex_unlock(&hotkey_mutex); + tpacpi_disclose_usertask("hotkey_poll_freq", "set to %lu\n", t); + return count; } @@ -2248,7 +2255,7 @@ static void hotkey_exit(void) kfree(hotkey_keycode_map); if (tp_features.hotkey) { - dbg_printk(TPACPI_DBG_EXIT, + dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original hot key mask\n"); /* no short-circuit boolean operator below! */ if ((hotkey_mask_set(hotkey_orig_mask) | @@ -2378,7 +2385,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) int status; int hkeyv; - vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "initializing hotkey subdriver\n"); BUG_ON(!tpacpi_inputdev); BUG_ON(tpacpi_inputdev->open != NULL || @@ -2395,7 +2403,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* hotkey not supported on 570 */ tp_features.hotkey = hkey_handle != NULL; - vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "hotkeys are %s\n", str_supported(tp_features.hotkey)); if (!tp_features.hotkey) @@ -2427,10 +2436,14 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) * T4x, X31, and later */ tp_features.hotkey_mask = 1; + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "firmware HKEY interface version: 0x%x\n", + hkeyv); } } - vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "hotkey masks are %s\n", str_supported(tp_features.hotkey_mask)); if (tp_features.hotkey_mask) { @@ -2469,7 +2482,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK; } - vdbg_printk(TPACPI_DBG_INIT, + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "hotkey source mask 0x%08x, polling freq %d\n", hotkey_source_mask, hotkey_poll_freq); #endif @@ -2523,12 +2536,12 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) } if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using Lenovo default hot key map\n"); memcpy(hotkey_keycode_map, &lenovo_keycode_map, TPACPI_HOTKEY_MAP_SIZE); } else { - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using IBM default hot key map\n"); memcpy(hotkey_keycode_map, &ibm_keycode_map, TPACPI_HOTKEY_MAP_SIZE); @@ -2585,7 +2598,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | (1 << TP_ACPI_HOTKEYSCAN_FNEND); } - dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n"); + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "enabling firmware HKEY event interface...\n"); res = hotkey_status_set(true); if (res) { hotkey_exit(); @@ -2599,8 +2613,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) return res; } - dbg_printk(TPACPI_DBG_INIT, - "legacy hot key reporting over procfs %s\n", + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, + "legacy ibm/hotkey event reporting over procfs %s\n", (hotkey_report_mode < 2) ? "enabled" : "disabled"); @@ -2971,6 +2985,11 @@ static int hotkey_write(char *buf) goto errexit; } } + + if (!res) + tpacpi_disclose_usertask("procfs hotkey", + "set mask to 0x%08x\n", mask); + if (!res && mask != hotkey_mask) res = hotkey_mask_set(mask); -- cgit v1.2.3 From 74a60c0f828016456fc635feae388ffd12bb3bb9 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:52 +0000 Subject: thinkpad-acpi: enhanced debugging messages for the fan subdriver Enhance debugging messages for the fan subdriver. Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 1 + drivers/platform/x86/thinkpad_acpi.c | 46 +++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index abbbe78a003b..25ed43d0a21f 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1497,6 +1497,7 @@ to enable more than one output class, just add their values. 0x0004 RF Transmitter control (RFKILL) (bluetooth, WWAN, UWB...) 0x0008 HKEY event interface, hotkeys + 0x0010 Fan 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 0a4796a246ea..4eec77032a71 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -191,6 +191,7 @@ enum { #define TPACPI_DBG_EXIT 0x0002 #define TPACPI_DBG_RFKILL 0x0004 #define TPACPI_DBG_HKEY 0x0008 +#define TPACPI_DBG_FAN 0x0010 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -6271,6 +6272,9 @@ static int fan_set_level(int level) default: return -ENXIO; } + + vdbg_printk(TPACPI_DBG_FAN, + "fan control: set fan control register to 0x%02x\n", level); return 0; } @@ -6348,6 +6352,11 @@ static int fan_set_enable(void) } mutex_unlock(&fan_mutex); + + if (!rc) + vdbg_printk(TPACPI_DBG_FAN, + "fan control: set fan control register to 0x%02x\n", + s); return rc; } @@ -6384,6 +6393,9 @@ static int fan_set_disable(void) rc = -ENXIO; } + if (!rc) + vdbg_printk(TPACPI_DBG_FAN, + "fan control: set fan control register to 0\n"); mutex_unlock(&fan_mutex); return rc; @@ -6512,6 +6524,9 @@ static ssize_t fan_pwm1_enable_store(struct device *dev, if (parse_strtoul(buf, 2, &t)) return -EINVAL; + tpacpi_disclose_usertask("hwmon pwm1_enable", + "set fan mode to %lu\n", t); + switch (t) { case 0: level = TP_EC_FAN_FULLSPEED; @@ -6577,6 +6592,9 @@ static ssize_t fan_pwm1_store(struct device *dev, if (parse_strtoul(buf, 255, &s)) return -EINVAL; + tpacpi_disclose_usertask("hwmon pwm1", + "set fan speed to %lu\n", s); + /* scale down from 0-255 to 0-7 */ newlevel = (s >> 5) & 0x07; @@ -6643,6 +6661,8 @@ static ssize_t fan_fan_watchdog_store(struct device_driver *drv, fan_watchdog_maxinterval = t; fan_watchdog_reset(); + tpacpi_disclose_usertask("fan_watchdog", "set to %lu\n", t); + return count; } @@ -6664,7 +6684,8 @@ static int __init fan_init(struct ibm_init_struct *iibm) { int rc; - vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, + "initializing fan subdriver\n"); mutex_init(&fan_mutex); fan_status_access_mode = TPACPI_FAN_NONE; @@ -6723,7 +6744,8 @@ static int __init fan_init(struct ibm_init_struct *iibm) } } - vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n", + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, + "fan is %s, modes %d, %d\n", str_supported(fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE), fan_status_access_mode, fan_control_access_mode); @@ -6732,7 +6754,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (!fan_control_allowed) { fan_control_access_mode = TPACPI_FAN_WR_NONE; fan_control_commands = 0; - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, "fan control features disabled by parameter\n"); } @@ -6761,7 +6783,7 @@ static int __init fan_init(struct ibm_init_struct *iibm) static void fan_exit(void) { - vdbg_printk(TPACPI_DBG_EXIT, + vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_FAN, "cancelling any pending fan watchdog tasks\n"); /* FIXME: can we really do this unconditionally? */ @@ -6942,6 +6964,9 @@ static int fan_write_cmd_level(const char *cmd, int *rc) if (*rc == -ENXIO) printk(TPACPI_ERR "level command accepted for unsupported " "access mode %d", fan_control_access_mode); + else if (!*rc) + tpacpi_disclose_usertask("procfs fan", + "set level to %d\n", level); return 1; } @@ -6955,6 +6980,8 @@ static int fan_write_cmd_enable(const char *cmd, int *rc) if (*rc == -ENXIO) printk(TPACPI_ERR "enable command accepted for unsupported " "access mode %d", fan_control_access_mode); + else if (!*rc) + tpacpi_disclose_usertask("procfs fan", "enable\n"); return 1; } @@ -6968,6 +6995,8 @@ static int fan_write_cmd_disable(const char *cmd, int *rc) if (*rc == -ENXIO) printk(TPACPI_ERR "disable command accepted for unsupported " "access mode %d", fan_control_access_mode); + else if (!*rc) + tpacpi_disclose_usertask("procfs fan", "disable\n"); return 1; } @@ -6986,6 +7015,9 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) if (*rc == -ENXIO) printk(TPACPI_ERR "speed command accepted for unsupported " "access mode %d", fan_control_access_mode); + else if (!*rc) + tpacpi_disclose_usertask("procfs fan", + "set speed to %d\n", speed); return 1; } @@ -6999,8 +7031,12 @@ static int fan_write_cmd_watchdog(const char *cmd, int *rc) if (interval < 0 || interval > 120) *rc = -EINVAL; - else + else { fan_watchdog_maxinterval = interval; + tpacpi_disclose_usertask("procfs fan", + "set watchdog timer to %d\n", + interval); + } return 1; } -- cgit v1.2.3 From 0e501834f8c2ba7de2a56e332d346dcf4ac0b593 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Sat, 4 Apr 2009 04:25:53 +0000 Subject: thinkpad-acpi: rework brightness support Refactor and redesign the brightness control backend... In order to fix bugzilla #11750... Add a new brightness control mode: support direct NVRAM checkpointing of the backlight level (i.e. store directly to NVRAM without the need for UCMS calls), and use that together with the EC-based control. Disallow UCMS+EC, thus avoiding races with the SMM firmware. Switch the models that define HBRV (EC Brightness Value) in the DSDT to the new mode. These are: T40-T43, R50-R52, R50e, R51e, X31-X41. Change the default for all other IBM ThinkPads to UCMS-only. The Lenovo models already default to UCMS-only. Reported-by: Alexey Fisher Signed-off-by: Henrique de Moraes Holschuh Signed-off-by: Len Brown --- Documentation/laptops/thinkpad-acpi.txt | 12 +- drivers/platform/x86/thinkpad_acpi.c | 317 ++++++++++++++++++++++---------- 2 files changed, 227 insertions(+), 102 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt index 25ed43d0a21f..3d7650768bb5 100644 --- a/Documentation/laptops/thinkpad-acpi.txt +++ b/Documentation/laptops/thinkpad-acpi.txt @@ -1157,10 +1157,15 @@ display backlight brightness control methods have 16 levels, ranging from 0 to 15. There are two interfaces to the firmware for direct brightness control, -EC and CMOS. To select which one should be used, use the +EC and UCMS (or CMOS). To select which one should be used, use the brightness_mode module parameter: brightness_mode=1 selects EC mode, -brightness_mode=2 selects CMOS mode, brightness_mode=3 selects both EC -and CMOS. The driver tries to auto-detect which interface to use. +brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC +mode with NVRAM backing (so that brightness changes are remembered +across shutdown/reboot). + +The driver tries to select which interface to use from a table of +defaults for each ThinkPad model. If it makes a wrong choice, please +report this as a bug, so that we can fix it. When display backlight brightness controls are available through the standard ACPI interface, it is best to use it instead of this direct @@ -1498,6 +1503,7 @@ to enable more than one output class, just add their values. (bluetooth, WWAN, UWB...) 0x0008 HKEY event interface, hotkeys 0x0010 Fan control + 0x0020 Backlight brightness 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 4eec77032a71..ba3682c5cde0 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -192,6 +192,7 @@ enum { #define TPACPI_DBG_RFKILL 0x0004 #define TPACPI_DBG_HKEY 0x0008 #define TPACPI_DBG_FAN 0x0010 +#define TPACPI_DBG_BRGHT 0x0020 #define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") #define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") @@ -274,7 +275,6 @@ static struct { static struct { u16 hotkey_mask_ff:1; - u16 bright_cmos_ec_unsync:1; } tp_warned; struct thinkpad_id_data { @@ -5526,6 +5526,20 @@ static struct ibm_struct ecdump_driver_data = { #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" +/* + * ThinkPads can read brightness from two places: EC HBRV (0x31), or + * CMOS NVRAM byte 0x5E, bits 0-3. + * + * EC HBRV (0x31) has the following layout + * Bit 7: unknown function + * Bit 6: unknown function + * Bit 5: Z: honour scale changes, NZ: ignore scale changes + * Bit 4: must be set to zero to avoid problems + * Bit 3-0: backlight brightness level + * + * brightness_get_raw returns status data in the HBRV layout + */ + enum { TP_EC_BACKLIGHT = 0x31, @@ -5535,108 +5549,164 @@ enum { TP_EC_BACKLIGHT_MAPSW = 0x20, }; +enum tpacpi_brightness_access_mode { + TPACPI_BRGHT_MODE_AUTO = 0, /* Not implemented yet */ + TPACPI_BRGHT_MODE_EC, /* EC control */ + TPACPI_BRGHT_MODE_UCMS_STEP, /* UCMS step-based control */ + TPACPI_BRGHT_MODE_ECNVRAM, /* EC control w/ NVRAM store */ + TPACPI_BRGHT_MODE_MAX +}; + static struct backlight_device *ibm_backlight_device; -static int brightness_mode; + +static enum tpacpi_brightness_access_mode brightness_mode = + TPACPI_BRGHT_MODE_MAX; + static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ static struct mutex brightness_mutex; -/* - * ThinkPads can read brightness from two places: EC 0x31, or - * CMOS NVRAM byte 0x5E, bits 0-3. - * - * EC 0x31 has the following layout - * Bit 7: unknown function - * Bit 6: unknown function - * Bit 5: Z: honour scale changes, NZ: ignore scale changes - * Bit 4: must be set to zero to avoid problems - * Bit 3-0: backlight brightness level - * - * brightness_get_raw returns status data in the EC 0x31 layout - */ -static int brightness_get_raw(int *status) +/* NVRAM brightness access, + * call with brightness_mutex held! */ +static unsigned int tpacpi_brightness_nvram_get(void) { - u8 lec = 0, lcmos = 0, level = 0; + u8 lnvram; - if (brightness_mode & 1) { - if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec)) - return -EIO; - level = lec & TP_EC_BACKLIGHT_LVLMSK; - }; - if (brightness_mode & 2) { - lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) - & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) - >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; - lcmos &= (tp_features.bright_16levels)? 0x0f : 0x07; - level = lcmos; - } - - if (brightness_mode == 3) { - *status = lec; /* Prefer EC, CMOS is just a backing store */ - lec &= TP_EC_BACKLIGHT_LVLMSK; - if (lec == lcmos) - tp_warned.bright_cmos_ec_unsync = 0; - else { - if (!tp_warned.bright_cmos_ec_unsync) { - printk(TPACPI_ERR - "CMOS NVRAM (%u) and EC (%u) do not " - "agree on display brightness level\n", - (unsigned int) lcmos, - (unsigned int) lec); - tp_warned.bright_cmos_ec_unsync = 1; - } + lnvram = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) + & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS; + lnvram &= (tp_features.bright_16levels) ? 0x0f : 0x07; + + return lnvram; +} + +static void tpacpi_brightness_checkpoint_nvram(void) +{ + u8 lec = 0; + u8 b_nvram; + + if (brightness_mode != TPACPI_BRGHT_MODE_ECNVRAM) + return; + + vdbg_printk(TPACPI_DBG_BRGHT, + "trying to checkpoint backlight level to NVRAM...\n"); + + if (mutex_lock_killable(&brightness_mutex) < 0) + return; + + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec))) + goto unlock; + lec &= TP_EC_BACKLIGHT_LVLMSK; + b_nvram = nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS); + + if (lec != ((b_nvram & TP_NVRAM_MASK_LEVEL_BRIGHTNESS) + >> TP_NVRAM_POS_LEVEL_BRIGHTNESS)) { + /* NVRAM needs update */ + b_nvram &= ~(TP_NVRAM_MASK_LEVEL_BRIGHTNESS << + TP_NVRAM_POS_LEVEL_BRIGHTNESS); + b_nvram |= lec; + nvram_write_byte(b_nvram, TP_NVRAM_ADDR_BRIGHTNESS); + dbg_printk(TPACPI_DBG_BRGHT, + "updated NVRAM backlight level to %u (0x%02x)\n", + (unsigned int) lec, (unsigned int) b_nvram); + } else + vdbg_printk(TPACPI_DBG_BRGHT, + "NVRAM backlight level already is %u (0x%02x)\n", + (unsigned int) lec, (unsigned int) b_nvram); + +unlock: + mutex_unlock(&brightness_mutex); +} + + +/* call with brightness_mutex held! */ +static int tpacpi_brightness_get_raw(int *status) +{ + u8 lec = 0; + + switch (brightness_mode) { + case TPACPI_BRGHT_MODE_UCMS_STEP: + *status = tpacpi_brightness_nvram_get(); + return 0; + case TPACPI_BRGHT_MODE_EC: + case TPACPI_BRGHT_MODE_ECNVRAM: + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec))) return -EIO; - } - } else { - *status = level; + *status = lec; + return 0; + default: + return -ENXIO; } +} + +/* call with brightness_mutex held! */ +/* do NOT call with illegal backlight level value */ +static int tpacpi_brightness_set_ec(unsigned int value) +{ + u8 lec = 0; + + if (unlikely(!acpi_ec_read(TP_EC_BACKLIGHT, &lec))) + return -EIO; + + if (unlikely(!acpi_ec_write(TP_EC_BACKLIGHT, + (lec & TP_EC_BACKLIGHT_CMDMSK) | + (value & TP_EC_BACKLIGHT_LVLMSK)))) + return -EIO; + + return 0; +} + +/* call with brightness_mutex held! */ +static int tpacpi_brightness_set_ucmsstep(unsigned int value) +{ + int cmos_cmd, inc; + unsigned int current_value, i; + + current_value = tpacpi_brightness_nvram_get(); + + if (value == current_value) + return 0; + + cmos_cmd = (value > current_value) ? + TP_CMOS_BRIGHTNESS_UP : + TP_CMOS_BRIGHTNESS_DOWN; + inc = (value > current_value) ? 1 : -1; + + for (i = current_value; i != value; i += inc) + if (issue_thinkpad_cmos_command(cmos_cmd)) + return -EIO; return 0; } /* May return EINTR which can always be mapped to ERESTARTSYS */ -static int brightness_set(int value) +static int brightness_set(unsigned int value) { - int cmos_cmd, inc, i, res; - int current_value; - int command_bits; + int res; if (value > ((tp_features.bright_16levels)? 15 : 7) || value < 0) return -EINVAL; + vdbg_printk(TPACPI_DBG_BRGHT, + "set backlight level to %d\n", value); + res = mutex_lock_killable(&brightness_mutex); if (res < 0) return res; - res = brightness_get_raw(¤t_value); - if (res < 0) - goto errout; - - command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK; - current_value &= TP_EC_BACKLIGHT_LVLMSK; - - cmos_cmd = value > current_value ? - TP_CMOS_BRIGHTNESS_UP : - TP_CMOS_BRIGHTNESS_DOWN; - inc = (value > current_value)? 1 : -1; - - res = 0; - for (i = current_value; i != value; i += inc) { - if ((brightness_mode & 2) && - issue_thinkpad_cmos_command(cmos_cmd)) { - res = -EIO; - goto errout; - } - if ((brightness_mode & 1) && - !acpi_ec_write(TP_EC_BACKLIGHT, - (i + inc) | command_bits)) { - res = -EIO; - goto errout;; - } + switch (brightness_mode) { + case TPACPI_BRGHT_MODE_EC: + case TPACPI_BRGHT_MODE_ECNVRAM: + res = tpacpi_brightness_set_ec(value); + break; + case TPACPI_BRGHT_MODE_UCMS_STEP: + res = tpacpi_brightness_set_ucmsstep(value); + break; + default: + res = -ENXIO; } -errout: mutex_unlock(&brightness_mutex); return res; } @@ -5645,21 +5715,34 @@ errout: static int brightness_update_status(struct backlight_device *bd) { - /* it is the backlight class's job (caller) to handle - * EINTR and other errors properly */ - return brightness_set( + unsigned int level = (bd->props.fb_blank == FB_BLANK_UNBLANK && bd->props.power == FB_BLANK_UNBLANK) ? - bd->props.brightness : 0); + bd->props.brightness : 0; + + dbg_printk(TPACPI_DBG_BRGHT, + "backlight: attempt to set level to %d\n", + level); + + /* it is the backlight class's job (caller) to handle + * EINTR and other errors properly */ + return brightness_set(level); } static int brightness_get(struct backlight_device *bd) { int status, res; - res = brightness_get_raw(&status); + res = mutex_lock_killable(&brightness_mutex); if (res < 0) - return 0; /* FIXME: teach backlight about error handling */ + return 0; + + res = tpacpi_brightness_get_raw(&status); + + mutex_unlock(&brightness_mutex); + + if (res < 0) + return 0; return status & TP_EC_BACKLIGHT_LVLMSK; } @@ -5709,7 +5792,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) } if (!brightness_enable) { - dbg_printk(TPACPI_DBG_INIT, + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, "brightness support disabled by " "module parameter\n"); return 1; @@ -5724,20 +5807,38 @@ static int __init brightness_init(struct ibm_init_struct *iibm) if (b == 16) tp_features.bright_16levels = 1; - if (!brightness_mode) { - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) - brightness_mode = 2; - else - brightness_mode = 3; + /* + * Check for module parameter bogosity, note that we + * init brightness_mode to TPACPI_BRGHT_MODE_MAX in order to be + * able to detect "unspecified" + */ + if (brightness_mode > TPACPI_BRGHT_MODE_MAX) + return -EINVAL; - dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n", - brightness_mode); - } + /* TPACPI_BRGHT_MODE_AUTO not implemented yet, just use default */ + if (brightness_mode == TPACPI_BRGHT_MODE_AUTO || + brightness_mode == TPACPI_BRGHT_MODE_MAX) { + if (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) { + /* + * IBM models that define HBRV probably have + * EC-based backlight level control + */ + if (acpi_evalf(ec_handle, NULL, "HBRV", "qd")) + /* T40-T43, R50-R52, R50e, R51e, X31-X41 */ + brightness_mode = TPACPI_BRGHT_MODE_ECNVRAM; + else + /* all other IBM ThinkPads */ + brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP; + } else + /* All Lenovo ThinkPads */ + brightness_mode = TPACPI_BRGHT_MODE_UCMS_STEP; - if (brightness_mode > 3) - return -EINVAL; + dbg_printk(TPACPI_DBG_BRGHT, + "selected brightness_mode=%d\n", + brightness_mode); + } - if (brightness_get_raw(&b) < 0) + if (tpacpi_brightness_get_raw(&b) < 0) return 1; if (tp_features.bright_16levels) @@ -5751,7 +5852,8 @@ static int __init brightness_init(struct ibm_init_struct *iibm) printk(TPACPI_ERR "Could not register backlight device\n"); return PTR_ERR(ibm_backlight_device); } - vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); + vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT, + "brightness is supported\n"); ibm_backlight_device->props.max_brightness = (tp_features.bright_16levels)? 15 : 7; @@ -5761,13 +5863,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 0; } +static void brightness_suspend(pm_message_t state) +{ + tpacpi_brightness_checkpoint_nvram(); +} + +static void brightness_shutdown(void) +{ + tpacpi_brightness_checkpoint_nvram(); +} + static void brightness_exit(void) { if (ibm_backlight_device) { - vdbg_printk(TPACPI_DBG_EXIT, + vdbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_BRGHT, "calling backlight_device_unregister()\n"); backlight_device_unregister(ibm_backlight_device); } + + tpacpi_brightness_checkpoint_nvram(); } static int brightness_read(char *p) @@ -5814,6 +5928,9 @@ static int brightness_write(char *buf) return -EINVAL; } + tpacpi_disclose_usertask("procfs brightness", + "set level to %d\n", level); + /* * Now we know what the final level should be, so we try to set it. * Doing it this way makes the syscall restartable in case of EINTR @@ -5827,6 +5944,8 @@ static struct ibm_struct brightness_driver_data = { .read = brightness_read, .write = brightness_write, .exit = brightness_exit, + .suspend = brightness_suspend, + .shutdown = brightness_shutdown, }; /************************************************************************* @@ -7465,10 +7584,10 @@ module_param_named(fan_control, fan_control_allowed, bool, 0); MODULE_PARM_DESC(fan_control, "Enables setting fan parameters features when true"); -module_param_named(brightness_mode, brightness_mode, int, 0); +module_param_named(brightness_mode, brightness_mode, uint, 0); MODULE_PARM_DESC(brightness_mode, "Selects brightness control strategy: " - "0=auto, 1=EC, 2=CMOS, 3=both"); + "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM"); module_param(brightness_enable, uint, 0); MODULE_PARM_DESC(brightness_enable, -- cgit v1.2.3 From a74dd5fdabcd34c93e17e9c7024eeb503c92b048 Mon Sep 17 00:00:00 2001 From: Carlos Corbacho Date: Sat, 4 Apr 2009 09:33:29 +0100 Subject: acer-wmi: Blacklist Acer Aspire One The Aspire One's ACPI-WMI interface is a placeholder that does nothing, and the invalid results that we get from it are now causing userspace problems as acer-wmi always returns that the rfkill is enabled (i.e. the radio is off, when it isn't). As it's hardware controlled, acer-wmi isn't needed on the Aspire One either. Thanks to Andy Whitcroft at Canonical for tracking down Ubuntu's userspace issues to this. Signed-off-by: Carlos Corbacho Reported-by: Andy Whitcroft Cc: stable@kernel.org Signed-off-by: Len Brown --- drivers/platform/x86/acer-wmi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a6a42e8c060b..60fbef283e73 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -225,6 +225,25 @@ static struct quirk_entry quirk_fujitsu_amilo_li_1718 = { .wireless = 2, }; +/* The Aspire One has a dummy ACPI-WMI interface - disable it */ +static struct dmi_system_id __devinitdata acer_blacklist[] = { + { + .ident = "Acer Aspire One (SSD)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"), + }, + }, + { + .ident = "Acer Aspire One (HDD)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"), + }, + }, + {} +}; + static struct dmi_system_id acer_quirks[] = { { .callback = dmi_matched, @@ -1254,6 +1273,12 @@ static int __init acer_wmi_init(void) printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n"); + if (dmi_check_system(acer_blacklist)) { + printk(ACER_INFO "Blacklisted hardware detected - " + "not loading\n"); + return -ENODEV; + } + find_quirks(); /* -- cgit v1.2.3 From 350e32907cf72ef82bbe7d72525fa2d9dcf64682 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Sat, 4 Apr 2009 09:33:34 +0100 Subject: acer-wmi: Cleanup the failure cleanup handling Cleanup the failure cleanup handling for brightness and email led. [cc: Split out from another patch] Signed-off-by: Andy Whitcroft Signed-off-by: Carlos Corbacho Signed-off-by: Len Brown --- drivers/platform/x86/acer-wmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/platform') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 60fbef283e73..27837e8ae51d 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1136,11 +1136,17 @@ static int __devinit acer_platform_probe(struct platform_device *device) } err = acer_rfkill_init(&device->dev); + if (err) + goto error_rfkill; return err; +error_rfkill: + if (has_cap(ACER_CAP_BRIGHTNESS)) + acer_backlight_exit(); error_brightness: - acer_led_exit(); + if (has_cap(ACER_CAP_MAILLED)) + acer_led_exit(); error_mailled: return err; } -- cgit v1.2.3 From 4f0175dc13a57cb5d2abef44eb4394b0b964bdad Mon Sep 17 00:00:00 2001 From: Carlos Corbacho Date: Sat, 4 Apr 2009 09:33:39 +0100 Subject: acer-wmi: Update copyright notice & documentation Explicitly note in the documentation that the Acer Aspire One is not supported. Signed-off-by: Carlos Corbacho Signed-off-by: Len Brown --- Documentation/laptops/acer-wmi.txt | 10 +++++++--- drivers/platform/x86/acer-wmi.c | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/platform') diff --git a/Documentation/laptops/acer-wmi.txt b/Documentation/laptops/acer-wmi.txt index 2b3a6b5260bf..5ee2a02b3b40 100644 --- a/Documentation/laptops/acer-wmi.txt +++ b/Documentation/laptops/acer-wmi.txt @@ -1,9 +1,9 @@ Acer Laptop WMI Extras Driver http://code.google.com/p/aceracpi -Version 0.2 -18th August 2008 +Version 0.3 +4th April 2009 -Copyright 2007-2008 Carlos Corbacho +Copyright 2007-2009 Carlos Corbacho acer-wmi is a driver to allow you to control various parts of your Acer laptop hardware under Linux which are exposed via ACPI-WMI. @@ -36,6 +36,10 @@ not possible in kernel space from a 64 bit OS. Supported Hardware ****************** +NOTE: The Acer Aspire One is not supported hardware. It cannot work with +acer-wmi until Acer fix their ACPI-WMI implementation on them, so has been +blacklisted until that happens. + Please see the website for the current list of known working hardare: http://code.google.com/p/aceracpi/wiki/SupportedHardware diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 27837e8ae51d..0f6e43bf4fc2 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1,7 +1,7 @@ /* * Acer WMI Laptop Extras * - * Copyright (C) 2007-2008 Carlos Corbacho + * Copyright (C) 2007-2009 Carlos Corbacho * * Based on acer_acpi: * Copyright (C) 2005-2007 E.M. Smith -- cgit v1.2.3