From 5369c02d951afc72d68f5f85089160a63d31ca37 Mon Sep 17 00:00:00 2001 From: Sreedhara DS Date: Fri, 22 Oct 2010 15:43:55 +0100 Subject: [PATCH 01/36] intel_scu_ipc: Utility driver for intel scu ipc This driver implements ioctl and interfaces with intel scu ipc driver. It is used to access pmic/msic registers from user space and firmware update utility. Signed-off-by: Sreedhara DS [Extensive clean up and debug] Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 9 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_scu_ipc.c | 4 +- drivers/platform/x86/intel_scu_ipcutil.c | 133 +++++++++++++++++++++++ 4 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 drivers/platform/x86/intel_scu_ipcutil.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index faec777b1ed4..65ba62086ca8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -576,6 +576,15 @@ config INTEL_SCU_IPC some embedded Intel x86 platforms. This is not needed for PC-type machines. +config INTEL_SCU_IPC_UTIL + tristate "Intel SCU IPC utility driver" + depends on INTEL_SCU_IPC + default y + ---help--- + The IPC Util driver provides an interface with the SCU enabling + low level access for debug work and updating the firmware. Say + N unless you will be doing this on an Intel MID platform. + config GPIO_INTEL_PMIC bool "Intel PMIC GPIO support" depends on INTEL_SCU_IPC && GPIOLIB diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9950ccc940b5..4ec4ff8f9182 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o +obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index ca35b0ce944a..1752ef006d26 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -497,7 +497,7 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) "intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd); mutex_unlock(&ipclock); - return -1; + return -EIO; } mutex_unlock(&ipclock); return 0; @@ -642,7 +642,7 @@ int intel_scu_ipc_fw_update(u8 *buffer, u32 length) if (status == IPC_FW_UPDATE_SUCCESS) return 0; - return -1; + return -EIO; } EXPORT_SYMBOL(intel_scu_ipc_fw_update); diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c new file mode 100644 index 000000000000..ba3231d0819e --- /dev/null +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -0,0 +1,133 @@ +/* + * intel_scu_ipc.c: Driver for the Intel SCU IPC mechanism + * + * (C) Copyright 2008-2010 Intel Corporation + * Author: Sreedhara DS (sreedhara.ds@intel.com) + * + * 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; version 2 + * of the License. + * + * This driver provides ioctl interfaces to call intel scu ipc driver api + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 major; + +#define MAX_FW_SIZE 264192 + +/* ioctl commnds */ +#define INTE_SCU_IPC_REGISTER_READ 0 +#define INTE_SCU_IPC_REGISTER_WRITE 1 +#define INTE_SCU_IPC_REGISTER_UPDATE 2 +#define INTE_SCU_IPC_FW_UPDATE 0xA2 + +struct scu_ipc_data { + u32 count; /* No. of registers */ + u16 addr[5]; /* Register addresses */ + u8 data[5]; /* Register data */ + u8 mask; /* Valid for read-modify-write */ +}; + +/** + * scu_reg_access - implement register access ioctls + * @cmd: command we are doing (read/write/update) + * @data: kernel copy of ioctl data + * + * Allow the user to perform register accesses on the SCU via the + * kernel interface + */ + +static int scu_reg_access(u32 cmd, struct scu_ipc_data *data) +{ + int count = data->count; + + if (count == 0 || count == 3 || count > 4) + return -EINVAL; + + switch (cmd) { + case INTE_SCU_IPC_REGISTER_READ: + return intel_scu_ipc_readv(data->addr, data->data, count); + case INTE_SCU_IPC_REGISTER_WRITE: + return intel_scu_ipc_writev(data->addr, data->data, count); + case INTE_SCU_IPC_REGISTER_UPDATE: + return intel_scu_ipc_update_register(data->addr[0], + data->data[0], data->mask); + default: + return -ENOTTY; + } +} + +/** + * scu_ipc_ioctl - control ioctls for the SCU + * @fp: file handle of the SCU device + * @cmd: ioctl coce + * @arg: pointer to user passed structure + * + * Support the I/O and firmware flashing interfaces of the SCU + */ +static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + int ret; + struct scu_ipc_data data; + void __user *argp = (void __user *)arg; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (cmd == INTE_SCU_IPC_FW_UPDATE) { + u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); + if (fwbuf == NULL) + return -ENOMEM; + if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { + kfree(fwbuf); + return -EFAULT; + } + ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE); + kfree(fwbuf); + return ret; + } else { + if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) + return -EFAULT; + ret = scu_reg_access(cmd, &data); + if (ret < 0) + return ret; + if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) + return -EFAULT; + return 0; + } +} + +static const struct file_operations scu_ipc_fops = { + .unlocked_ioctl = scu_ipc_ioctl, +}; + +static int __init ipc_module_init(void) +{ + return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); +} + +static void __exit ipc_module_exit(void) +{ + unregister_chrdev(major, "intel_mid_scu"); +} + +module_init(ipc_module_init); +module_exit(ipc_module_exit); + +MODULE_LICENSE("GPL V2"); +MODULE_DESCRIPTION("Utility driver for intel scu ipc"); +MODULE_AUTHOR("Sreedhara "); From 3098064d3b4a9bf9d2855b2a89599ad77695e324 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sun, 14 Nov 2010 19:04:38 -0800 Subject: [PATCH 02/36] drivers/platform/x86: Remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Matthew Garrett --- drivers/platform/x86/classmate-laptop.c | 2 +- drivers/platform/x86/thinkpad_acpi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 341cbfef93ee..d2b7720c0e6f 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -653,7 +653,7 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) code = cmpc_keys_codes[event & 0x0F]; - inputdev = dev_get_drvdata(&dev->dev);; + inputdev = dev_get_drvdata(&dev->dev); input_report_key(inputdev, code, !(event & 0x10)); } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index e8c21994b36d..f372dc7ae14e 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6345,7 +6345,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) "as change notification\n"); tpacpi_hotkey_driver_mask_set(hotkey_driver_mask | TP_ACPI_HKEY_BRGHTUP_MASK - | TP_ACPI_HKEY_BRGHTDWN_MASK);; + | TP_ACPI_HKEY_BRGHTDWN_MASK); return 0; } From 58f6425eb92f54943878b0b3f9c1e51f99c2cb72 Mon Sep 17 00:00:00 2001 From: Colin King Date: Fri, 19 Nov 2010 15:40:02 +0000 Subject: [PATCH 03/36] WMI: Cater for multiple events with same GUID WMI data blocks can contain WMI events with the same GUID but with different notifiy_ids, for example volume up/down hotkeys. This patch enables a single event handler to be registered and unregistered against all events with same GUID but different notify_ids. Since an event handler is passed the notify_id of an event it can can differentiate between the different events. The patch also ensures we only register and unregister a device per unique GUID. Signed-off-by: Colin Ian King Signed-off-by: Matthew Garrett --- drivers/platform/x86/wmi.c | 137 ++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 63 deletions(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index aecd9a9b549f..b8e5383eab0c 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -549,21 +549,34 @@ acpi_status wmi_install_notify_handler(const char *guid, wmi_notify_handler handler, void *data) { struct wmi_block *block; - acpi_status status; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid || !handler) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (block->handler && block->handler != wmi_notify_debug) - return AE_ALREADY_ACQUIRED; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - block->handler = handler; - block->handler_data = data; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (block->handler && + block->handler != wmi_notify_debug) + return AE_ALREADY_ACQUIRED; - status = wmi_method_enable(block, 1); + block->handler = handler; + block->handler_data = data; + + wmi_status = wmi_method_enable(block, 1); + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) + status = wmi_status; + } + } return status; } @@ -577,24 +590,40 @@ EXPORT_SYMBOL_GPL(wmi_install_notify_handler); acpi_status wmi_remove_notify_handler(const char *guid) { struct wmi_block *block; - acpi_status status = AE_OK; + acpi_status status = AE_NOT_EXIST; + char tmp[16], guid_input[16]; + struct list_head *p; if (!guid) return AE_BAD_PARAMETER; - if (!find_guid(guid, &block)) - return AE_NOT_EXIST; + wmi_parse_guid(guid, tmp); + wmi_swap_bytes(tmp, guid_input); - if (!block->handler || block->handler == wmi_notify_debug) - return AE_NULL_ENTRY; + list_for_each(p, &wmi_block_list) { + acpi_status wmi_status; + block = list_entry(p, struct wmi_block, list); - if (debug_event) { - block->handler = wmi_notify_debug; - } else { - status = wmi_method_enable(block, 0); - block->handler = NULL; - block->handler_data = NULL; + if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (!block->handler || + block->handler == wmi_notify_debug) + return AE_NULL_ENTRY; + + if (debug_event) { + block->handler = wmi_notify_debug; + status = AE_OK; + } else { + wmi_status = wmi_method_enable(block, 0); + block->handler = NULL; + block->handler_data = NULL; + if ((wmi_status != AE_OK) || + ((wmi_status == AE_OK) && + (status == AE_NOT_EXIST))) + status = wmi_status; + } + } } + return status; } EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); @@ -705,22 +734,11 @@ static struct class wmi_class = { .dev_attrs = wmi_dev_attrs, }; -static struct wmi_block *wmi_create_device(const struct guid_block *gblock, - acpi_handle handle) +static int wmi_create_device(const struct guid_block *gblock, + struct wmi_block *wblock, acpi_handle handle) { - struct wmi_block *wblock; - int error; char guid_string[37]; - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); - if (!wblock) { - error = -ENOMEM; - goto err_out; - } - - wblock->handle = handle; - wblock->gblock = *gblock; - wblock->dev.class = &wmi_class; wmi_gtoa(gblock->guid, guid_string); @@ -728,17 +746,7 @@ static struct wmi_block *wmi_create_device(const struct guid_block *gblock, dev_set_drvdata(&wblock->dev, wblock); - error = device_register(&wblock->dev); - if (error) - goto err_free; - - list_add_tail(&wblock->list, &wmi_block_list); - return wblock; - -err_free: - kfree(wblock); -err_out: - return ERR_PTR(error); + return device_register(&wblock->dev); } static void wmi_free_devices(void) @@ -747,7 +755,8 @@ static void wmi_free_devices(void) /* Delete devices for all the GUIDs */ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) - device_unregister(&wblock->dev); + if (wblock->dev.class) + device_unregister(&wblock->dev); } static bool guid_already_parsed(const char *guid_string) @@ -770,7 +779,6 @@ static acpi_status parse_wdg(acpi_handle handle) union acpi_object *obj; const struct guid_block *gblock; struct wmi_block *wblock; - char guid_string[37]; acpi_status status; int retval; u32 i, total; @@ -792,29 +800,32 @@ static acpi_status parse_wdg(acpi_handle handle) total = obj->buffer.length / sizeof(struct guid_block); for (i = 0; i < total; i++) { - /* - Some WMI devices, like those for nVidia hooks, have a - duplicate GUID. It's not clear what we should do in this - case yet, so for now, we'll just ignore the duplicate. - Anyone who wants to add support for that device can come - up with a better workaround for the mess then. - */ - if (guid_already_parsed(gblock[i].guid) == true) { - wmi_gtoa(gblock[i].guid, guid_string); - pr_info("Skipping duplicate GUID %s\n", guid_string); - continue; - } - if (debug_dump_wdg) wmi_dump_wdg(&gblock[i]); - wblock = wmi_create_device(&gblock[i], handle); - if (IS_ERR(wblock)) { - retval = PTR_ERR(wblock); - wmi_free_devices(); - break; + wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + if (!wblock) + return AE_NO_MEMORY; + + wblock->handle = handle; + wblock->gblock = gblock[i]; + + /* + Some WMI devices, like those for nVidia hooks, have a + duplicate GUID. It's not clear what we should do in this + case yet, so for now, we'll just ignore the duplicate + for device creation. + */ + if (!guid_already_parsed(gblock[i].guid)) { + retval = wmi_create_device(&gblock[i], wblock, handle); + if (retval) { + wmi_free_devices(); + break; + } } + list_add_tail(&wblock->list, &wmi_block_list); + if (debug_event) { wblock->handler = wmi_notify_debug; wmi_method_enable(wblock, 1); From 698e1641a37f833dd26ee2fde5eed426cd97880b Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Tue, 30 Nov 2010 16:30:43 -0200 Subject: [PATCH 04/36] classmate-laptop: little optimization for cmpc_rfkill_block We don't need to call bios/acpi (cmpc_set_rfkill_wlan) if the blocked state is already set to the same value (little optimization). This can happen for example if we initialize the module with same initial hardware state (rfkill core always call cmpc_rfkill_block on initialization here). Also GWRI method only accepts 0 or 1 for setting rfkill block, as can be seen on AML code from acpidump->DSDT from a classmate sample I have, so should be fine setting state only to 0 or 1 directly. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Matthew Garrett Acked-by: Thadeu Lima de Souza Cascardo --- drivers/platform/x86/classmate-laptop.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index d2b7720c0e6f..3dabd00b1d5a 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -522,18 +522,20 @@ static int cmpc_rfkill_block(void *data, bool blocked) acpi_status status; acpi_handle handle; unsigned long long state; + bool is_blocked; handle = data; status = cmpc_get_rfkill_wlan(handle, &state); if (ACPI_FAILURE(status)) return -ENODEV; - if (blocked) - state &= ~1; - else - state |= 1; - status = cmpc_set_rfkill_wlan(handle, state); - if (ACPI_FAILURE(status)) - return -ENODEV; + /* Check if we really need to call cmpc_set_rfkill_wlan */ + is_blocked = state & 1 ? false : true; + if (is_blocked != blocked) { + state = blocked ? 0 : 1; + status = cmpc_set_rfkill_wlan(handle, state); + if (ACPI_FAILURE(status)) + return -ENODEV; + } return 0; } From 72135d21b587debcbcc57e0dbcc8bcfa4dacb661 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Mon, 6 Dec 2010 16:46:19 -0500 Subject: [PATCH 05/36] classmate-laptop: add missing input_sync call Add missing input_sync call in cmpc_keys_handler function. Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: Matthew Garrett Acked-by: Thadeu Lima de Souza Cascardo --- drivers/platform/x86/classmate-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 3dabd00b1d5a..911135425224 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -657,6 +657,7 @@ static void cmpc_keys_handler(struct acpi_device *dev, u32 event) code = cmpc_keys_codes[event & 0x0F]; inputdev = dev_get_drvdata(&dev->dev); input_report_key(inputdev, code, !(event & 0x10)); + input_sync(inputdev); } static void cmpc_keys_idev_init(struct input_dev *inputdev) From 27c136c8738f6bec10c26aaf0a486f19edef7bf7 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:05 +0100 Subject: [PATCH 06/36] eeepc-wmi: rework eeepc_wmi_init and eeepc_wmi_exit The old code was using platform_driver.probe to initialize eeepc_wmi context. That's a mistake because if probe fail, eeepc_platform_register() won't tell anyone, and chaos will happen. Wrap add and remove code inside eeepc_wmi_add() / eeepc_wmi_remove(), and try to use the static platform_device only in eeepc_wmi_init() and eeepc_wmi_exit() The code is now very similar to eeepc-laptop, except eeepc_laptop_add and eeepc_laptop_remove are called from acpi_driver, not module init/exit functions, but WMI doesn't provide such functionalities (yet ?). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 135 ++++++++++++++++++------------- 1 file changed, 79 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d50fbbe2478..dfbb295326be 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -86,8 +86,10 @@ struct bios_args { struct eeepc_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; + struct platform_device *platform_device; }; +/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ static struct platform_device *platform_device; static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) @@ -101,7 +103,7 @@ static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) eeepc->inputdev->name = "Eee PC WMI hotkeys"; eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; eeepc->inputdev->id.bustype = BUS_HOST; - eeepc->inputdev->dev.parent = &platform_device->dev; + eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); if (err) @@ -234,7 +236,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = 15; bd = backlight_device_register(EEEPC_WMI_FILE, - &platform_device->dev, eeepc, + &eeepc->platform_device->dev, eeepc, &eeepc_wmi_bl_ops, &props); if (IS_ERR(bd)) { pr_err("Could not register backlight device\n"); @@ -337,49 +339,99 @@ static int eeepc_wmi_sysfs_init(struct platform_device *device) return 0; error_sysfs: - eeepc_wmi_sysfs_exit(platform_device); + eeepc_wmi_sysfs_exit(device); return retval; } -static int __devinit eeepc_wmi_platform_probe(struct platform_device *device) +/* + * Platform device + */ +static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) +{ + int err; + + eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); + if (!eeepc->platform_device) + return -ENOMEM; + platform_set_drvdata(eeepc->platform_device, eeepc); + + err = platform_device_add(eeepc->platform_device); + if (err) + goto fail_platform_device; + + err = eeepc_wmi_sysfs_init(eeepc->platform_device); + if (err) + goto fail_sysfs; + return 0; + +fail_sysfs: + platform_device_del(eeepc->platform_device); +fail_platform_device: + platform_device_put(eeepc->platform_device); + return err; +} + +static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) +{ + eeepc_wmi_sysfs_exit(eeepc->platform_device); + platform_device_unregister(eeepc->platform_device); +} + +/* + * WMI Driver + */ +static struct platform_device * __init eeepc_wmi_add(void) { struct eeepc_wmi *eeepc; - int err; acpi_status status; + int err; - eeepc = platform_get_drvdata(device); + eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); + if (!eeepc) + return ERR_PTR(-ENOMEM); + + /* + * Register the platform device first. It is used as a parent for the + * sub-devices below. + */ + err = eeepc_wmi_platform_init(eeepc); + if (err) + goto fail_platform; err = eeepc_wmi_input_init(eeepc); if (err) - goto error_input; + goto fail_input; if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); if (err) - goto error_backlight; + goto fail_backlight; } else pr_info("Backlight controlled by ACPI video driver\n"); status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, - eeepc_wmi_notify, eeepc); + eeepc_wmi_notify, eeepc); if (ACPI_FAILURE(status)) { pr_err("Unable to register notify handler - %d\n", status); err = -ENODEV; - goto error_wmi; + goto fail_wmi_handler; } - return 0; + return eeepc->platform_device; -error_wmi: +fail_wmi_handler: eeepc_wmi_backlight_exit(eeepc); -error_backlight: +fail_backlight: eeepc_wmi_input_exit(eeepc); -error_input: - return err; +fail_input: + eeepc_wmi_platform_exit(eeepc); +fail_platform: + kfree(eeepc); + return ERR_PTR(err); } -static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) +static int eeepc_wmi_remove(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -387,7 +439,9 @@ static int __devexit eeepc_wmi_platform_remove(struct platform_device *device) wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_input_exit(eeepc); + eeepc_wmi_platform_exit(eeepc); + kfree(eeepc); return 0; } @@ -396,13 +450,10 @@ static struct platform_driver platform_driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, }, - .probe = eeepc_wmi_platform_probe, - .remove = __devexit_p(eeepc_wmi_platform_remove), }; static int __init eeepc_wmi_init(void) { - struct eeepc_wmi *eeepc; int err; if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || @@ -411,58 +462,30 @@ static int __init eeepc_wmi_init(void) return -ENODEV; } - eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); - if (!eeepc) - return -ENOMEM; - - platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1); - if (!platform_device) { - pr_warning("Unable to allocate platform device\n"); - err = -ENOMEM; - goto fail_platform; + platform_device = eeepc_wmi_add(); + if (IS_ERR(platform_device)) { + err = PTR_ERR(platform_device); + goto fail_eeepc_wmi; } - err = platform_device_add(platform_device); - if (err) { - pr_warning("Unable to add platform device\n"); - goto put_dev; - } - - platform_set_drvdata(platform_device, eeepc); - err = platform_driver_register(&platform_driver); if (err) { pr_warning("Unable to register platform driver\n"); - goto del_dev; + goto fail_platform_driver; } - err = eeepc_wmi_sysfs_init(platform_device); - if (err) - goto del_sysfs; - return 0; -del_sysfs: - eeepc_wmi_sysfs_exit(platform_device); -del_dev: - platform_device_del(platform_device); -put_dev: - platform_device_put(platform_device); -fail_platform: - kfree(eeepc); - +fail_platform_driver: + eeepc_wmi_remove(platform_device); +fail_eeepc_wmi: return err; } static void __exit eeepc_wmi_exit(void) { - struct eeepc_wmi *eeepc; - - eeepc_wmi_sysfs_exit(platform_device); - eeepc = platform_get_drvdata(platform_device); + eeepc_wmi_remove(platform_device); platform_driver_unregister(&platform_driver); - platform_device_unregister(platform_device); - kfree(eeepc); } module_init(eeepc_wmi_init); From 084fca63128849c0961b3cfdb0cd0345e8f51ad8 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:06 +0100 Subject: [PATCH 07/36] eeepc-wmi: add touchpad led support Most of the code comes from eeepc-laptop. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/eeepc-wmi.c | 113 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 65ba62086ca8..adcdbbd7d3fd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -426,6 +426,8 @@ config EEEPC_WMI depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE select INPUT_SPARSEKMAP + select LEDS_CLASS + select NEW_LEDS ---help--- Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index dfbb295326be..f12a25da4055 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_METHODID_CFVS 0x53564643 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_TPDLED 0x00100011 static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ @@ -87,6 +89,11 @@ struct eeepc_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; struct platform_device *platform_device; + + struct led_classdev tpd_led; + int tpd_led_wk; + struct workqueue_struct *led_workqueue; + struct work_struct tpd_led_work; }; /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ @@ -176,6 +183,105 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) return status; } +/* + * LEDs + */ +/* + * These functions actually update the LED's, and are called from a + * workqueue. By doing this as separate work rather than when the LED + * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a + * potentially bad time, such as a timer interrupt. + */ +static void tpd_led_update(struct work_struct *work) +{ + int ctrl_param; + struct eeepc_wmi *eeepc; + + eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); + + ctrl_param = eeepc->tpd_led_wk; + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param); +} + +static void tpd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + + eeepc->tpd_led_wk = !!value; + queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); +} + +static int read_tpd_state(struct eeepc_wmi *eeepc) +{ + static u32 ctrl_param; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + else if (!ctrl_param || ctrl_param == 0x00060000) + /* + * if touchpad led is present, DSTS will set some bits, + * usually 0x00020000. + * 0x00060000 means that the device is not supported + */ + return -ENODEV; + else + /* Status is stored in the first bit */ + return ctrl_param & 0x1; +} + +static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + + return read_tpd_state(eeepc); +} + +static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) +{ + int rv; + + if (read_tpd_state(eeepc) < 0) + return 0; + + eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!eeepc->led_workqueue) + return -ENOMEM; + INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); + + eeepc->tpd_led.name = "eeepc::touchpad"; + eeepc->tpd_led.brightness_set = tpd_led_set; + eeepc->tpd_led.brightness_get = tpd_led_get; + eeepc->tpd_led.max_brightness = 1; + + rv = led_classdev_register(&eeepc->platform_device->dev, + &eeepc->tpd_led); + if (rv) { + destroy_workqueue(eeepc->led_workqueue); + return rv; + } + + return 0; +} + +static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->tpd_led.dev) + led_classdev_unregister(&eeepc->tpd_led); + if (eeepc->led_workqueue) + destroy_workqueue(eeepc->led_workqueue); +} + +/* + * Backlight + */ static int read_brightness(struct backlight_device *bd) { static u32 ctrl_param; @@ -402,6 +508,10 @@ static struct platform_device * __init eeepc_wmi_add(void) if (err) goto fail_input; + err = eeepc_wmi_led_init(eeepc); + if (err) + goto fail_leds; + if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); if (err) @@ -423,6 +533,8 @@ static struct platform_device * __init eeepc_wmi_add(void) fail_wmi_handler: eeepc_wmi_backlight_exit(eeepc); fail_backlight: + eeepc_wmi_led_exit(eeepc); +fail_leds: eeepc_wmi_input_exit(eeepc); fail_input: eeepc_wmi_platform_exit(eeepc); @@ -439,6 +551,7 @@ static int eeepc_wmi_remove(struct platform_device *device) wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_input_exit(eeepc); + eeepc_wmi_led_exit(eeepc); eeepc_wmi_platform_exit(eeepc); kfree(eeepc); From ba48fdb969d0404d54f6fa0266373afecbbd19d7 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:07 +0100 Subject: [PATCH 08/36] eeepc-wmi: add rfkill support for wlan, bluetooth and 3g wimax support is missing because I don't have any DSDT with WMI and wimax support. Most of the code comes from eeepc-laptop. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/eeepc-wmi.c | 138 +++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index adcdbbd7d3fd..4c7f8b926103 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -425,6 +425,7 @@ config EEEPC_WMI depends on INPUT depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL || RFKILL = n select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index f12a25da4055..fb548f4b03e0 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,9 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DEVID_WLAN 0x00010011 +#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 +#define EEEPC_WMI_DEVID_WWAN3G 0x00010019 static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ @@ -94,6 +98,10 @@ struct eeepc_wmi { int tpd_led_wk; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; + + struct rfkill *wlan_rfkill; + struct rfkill *bluetooth_rfkill; + struct rfkill *wwan3g_rfkill; }; /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ @@ -279,6 +287,129 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) destroy_workqueue(eeepc->led_workqueue); } +/* + * Rfkill devices + */ +static int eeepc_rfkill_set(void *data, bool blocked) +{ + int dev_id = (unsigned long)data; + u32 ctrl_param = !blocked; + + return eeepc_wmi_set_devstate(dev_id, ctrl_param); +} + +static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) +{ + int dev_id = (unsigned long)data; + u32 ctrl_param; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &ctrl_param); + + if (ACPI_FAILURE(status)) + return ; + + rfkill_set_sw_state(rfkill, !(ctrl_param & 0x1)); +} + +static const struct rfkill_ops eeepc_rfkill_ops = { + .set_block = eeepc_rfkill_set, + .query = eeepc_rfkill_query, +}; + +static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, + struct rfkill **rfkill, + const char *name, + enum rfkill_type type, int dev_id) +{ + int result; + u32 ctrl_param; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &ctrl_param); + + if (ACPI_FAILURE(status)) + return -1; + + /* If the device is present, DSTS will always set some bits + * 0x00070000 - 1110000000000000000 - device supported + * 0x00060000 - 1100000000000000000 - not supported + * 0x00020000 - 0100000000000000000 - device supported + * 0x00010000 - 0010000000000000000 - not supported / special mode ? + */ + if (!ctrl_param || ctrl_param == 0x00060000) + return -ENODEV; + + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, (void *)(long)dev_id); + + if (!*rfkill) + return -EINVAL; + + rfkill_init_sw_state(*rfkill, !(ctrl_param & 0x1)); + result = rfkill_register(*rfkill); + if (result) { + rfkill_destroy(*rfkill); + *rfkill = NULL; + return result; + } + return 0; +} + +static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) +{ + if (eeepc->wlan_rfkill) { + rfkill_unregister(eeepc->wlan_rfkill); + rfkill_destroy(eeepc->wlan_rfkill); + eeepc->wlan_rfkill = NULL; + } + if (eeepc->bluetooth_rfkill) { + rfkill_unregister(eeepc->bluetooth_rfkill); + rfkill_destroy(eeepc->bluetooth_rfkill); + eeepc->bluetooth_rfkill = NULL; + } + if (eeepc->wwan3g_rfkill) { + rfkill_unregister(eeepc->wwan3g_rfkill); + rfkill_destroy(eeepc->wwan3g_rfkill); + eeepc->wwan3g_rfkill = NULL; + } +} + +static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) +{ + int result = 0; + + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, + "eeepc-wlan", RFKILL_TYPE_WLAN, + EEEPC_WMI_DEVID_WLAN); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, + "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, + EEEPC_WMI_DEVID_BLUETOOTH); + + if (result && result != -ENODEV) + goto exit; + + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, + "eeepc-wwan3g", RFKILL_TYPE_WWAN, + EEEPC_WMI_DEVID_WWAN3G); + + if (result && result != -ENODEV) + goto exit; + +exit: + if (result && result != -ENODEV) + eeepc_wmi_rfkill_exit(eeepc); + + if (result == -ENODEV) + result = 0; + + return result; +} + /* * Backlight */ @@ -512,6 +643,10 @@ static struct platform_device * __init eeepc_wmi_add(void) if (err) goto fail_leds; + err = eeepc_wmi_rfkill_init(eeepc); + if (err) + goto fail_rfkill; + if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); if (err) @@ -533,6 +668,8 @@ static struct platform_device * __init eeepc_wmi_add(void) fail_wmi_handler: eeepc_wmi_backlight_exit(eeepc); fail_backlight: + eeepc_wmi_rfkill_exit(eeepc); +fail_rfkill: eeepc_wmi_led_exit(eeepc); fail_leds: eeepc_wmi_input_exit(eeepc); @@ -552,6 +689,7 @@ static int eeepc_wmi_remove(struct platform_device *device) eeepc_wmi_backlight_exit(eeepc); eeepc_wmi_input_exit(eeepc); eeepc_wmi_led_exit(eeepc); + eeepc_wmi_rfkill_exit(eeepc); eeepc_wmi_platform_exit(eeepc); kfree(eeepc); From 4e37b42d5ac0d3f505bcdc09028f3fde82031593 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:08 +0100 Subject: [PATCH 09/36] eeepc-wmi: use attribute group to manage attributes Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index fb548f4b03e0..a70d76a463a6 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -560,24 +560,23 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); +static struct attribute *platform_attributes[] = { + &dev_attr_cpufv.attr, + NULL +}; + +static struct attribute_group platform_attribute_group = { + .attrs = platform_attributes +}; + static void eeepc_wmi_sysfs_exit(struct platform_device *device) { - device_remove_file(&device->dev, &dev_attr_cpufv); + sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); } static int eeepc_wmi_sysfs_init(struct platform_device *device) { - int retval = -ENOMEM; - - retval = device_create_file(&device->dev, &dev_attr_cpufv); - if (retval) - goto error_sysfs; - - return 0; - -error_sysfs: - eeepc_wmi_sysfs_exit(device); - return retval; + return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); } /* From 8c1b2d83e82e11b9447d7fb3715d6c9764f6f28d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:09 +0100 Subject: [PATCH 10/36] eeepc-wmi: add debugfs entries eeepc-wmi/ - debugfs root directory dev_id - current dev_id ctrl_param - current ctrl_param devs - call DEVS(dev_id, ctrl_param) and print result dsts - call DSTS(dev_id) and print result DEVS and DSTS are the main functions used in eeepc-wmi, this will allow to test new features without patching the drivers. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 170 +++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index a70d76a463a6..253070ab963c 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -89,6 +91,19 @@ struct bios_args { u32 ctrl_param; }; +/* + * eeepc-wmi/ - debugfs root directory + * dev_id - current dev_id + * ctrl_param - current ctrl_param + * devs - call DEVS(dev_id, ctrl_param) and print result + * dsts - call DSTS(dev_id) and print result + */ +struct eeepc_wmi_debug { + struct dentry *root; + u32 dev_id; + u32 ctrl_param; +}; + struct eeepc_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -102,6 +117,8 @@ struct eeepc_wmi { struct rfkill *wlan_rfkill; struct rfkill *bluetooth_rfkill; struct rfkill *wwan3g_rfkill; + + struct eeepc_wmi_debug debug; }; /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */ @@ -176,7 +193,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) } -static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) +static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, + u32 *retval) { struct bios_args args = { .dev_id = dev_id, @@ -185,8 +203,32 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param) struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; acpi_status status; - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_DEVS, &input, NULL); + if (!retval) { + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, + EEEPC_WMI_METHODID_DEVS, + &input, NULL); + } else { + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + u32 tmp; + + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, + EEEPC_WMI_METHODID_DEVS, + &input, &output); + + if (ACPI_FAILURE(status)) + return status; + + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32)obj->integer.value; + else + tmp = 0; + + *retval = tmp; + + kfree(obj); + } return status; } @@ -208,7 +250,7 @@ static void tpd_led_update(struct work_struct *work) eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); ctrl_param = eeepc->tpd_led_wk; - eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param); + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL); } static void tpd_led_set(struct led_classdev *led_cdev, @@ -295,7 +337,7 @@ static int eeepc_rfkill_set(void *data, bool blocked) int dev_id = (unsigned long)data; u32 ctrl_param = !blocked; - return eeepc_wmi_set_devstate(dev_id, ctrl_param); + return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); } static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) @@ -434,7 +476,8 @@ static int update_bl_status(struct backlight_device *bd) ctrl_param = bd->props.brightness; - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, ctrl_param); + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); if (ACPI_FAILURE(status)) return -1; @@ -613,6 +656,114 @@ static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) platform_device_unregister(eeepc->platform_device); } +/* + * debugfs + */ +struct eeepc_wmi_debugfs_node { + struct eeepc_wmi *eeepc; + char *name; + int (*show)(struct seq_file *m, void *data); +}; + +static int show_dsts(struct seq_file *m, void *data) +{ + struct eeepc_wmi *eeepc = m->private; + acpi_status status; + u32 retval = -1; + + status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + + seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); + + return 0; +} + +static int show_devs(struct seq_file *m, void *data) +{ + struct eeepc_wmi *eeepc = m->private; + acpi_status status; + u32 retval = -1; + + status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, + eeepc->debug.ctrl_param, &retval); + if (ACPI_FAILURE(status)) + return -EIO; + + seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, + eeepc->debug.ctrl_param, retval); + + return 0; +} + +static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { + { NULL, "devs", show_devs }, + { NULL, "dsts", show_dsts }, +}; + +static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) +{ + struct eeepc_wmi_debugfs_node *node = inode->i_private; + + return single_open(file, node->show, node->eeepc); +} + +static const struct file_operations eeepc_wmi_debugfs_io_ops = { + .owner = THIS_MODULE, + .open = eeepc_wmi_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) +{ + debugfs_remove_recursive(eeepc->debug.root); +} + +static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) +{ + struct dentry *dent; + int i; + + eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); + if (!eeepc->debug.root) { + pr_err("failed to create debugfs directory"); + goto error_debugfs; + } + + dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, + eeepc->debug.root, &eeepc->debug.dev_id); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, + eeepc->debug.root, &eeepc->debug.ctrl_param); + if (!dent) + goto error_debugfs; + + for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { + struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; + + node->eeepc = eeepc; + dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, + eeepc->debug.root, node, + &eeepc_wmi_debugfs_io_ops); + if (!dent) { + pr_err("failed to create debug file: %s\n", node->name); + goto error_debugfs; + } + } + + return 0; + +error_debugfs: + eeepc_wmi_debugfs_exit(eeepc); + return -ENOMEM; +} + /* * WMI Driver */ @@ -662,8 +813,14 @@ static struct platform_device * __init eeepc_wmi_add(void) goto fail_wmi_handler; } + err = eeepc_wmi_debugfs_init(eeepc); + if (err) + goto fail_debugfs; + return eeepc->platform_device; +fail_debugfs: + wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); fail_wmi_handler: eeepc_wmi_backlight_exit(eeepc); fail_backlight: @@ -689,6 +846,7 @@ static int eeepc_wmi_remove(struct platform_device *device) eeepc_wmi_input_exit(eeepc); eeepc_wmi_led_exit(eeepc); eeepc_wmi_rfkill_exit(eeepc); + eeepc_wmi_debugfs_exit(eeepc); eeepc_wmi_platform_exit(eeepc); kfree(eeepc); From 2a3f0064f2a06d5a31cddb0e452681d9f884bd8c Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:10 +0100 Subject: [PATCH 11/36] eeepc-wmi: fix confusion between ctrl_param and retval Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 253070ab963c..78306bea01f7 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -164,7 +164,7 @@ static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) eeepc->inputdev = NULL; } -static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) +static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) { struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -184,8 +184,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *ctrl_param) else tmp = 0; - if (ctrl_param) - *ctrl_param = tmp; + if (retval) + *retval = tmp; kfree(obj); @@ -266,14 +266,14 @@ static void tpd_led_set(struct led_classdev *led_cdev, static int read_tpd_state(struct eeepc_wmi *eeepc) { - static u32 ctrl_param; + static u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &ctrl_param); + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); if (ACPI_FAILURE(status)) return -1; - else if (!ctrl_param || ctrl_param == 0x00060000) + else if (!retval || retval == 0x00060000) /* * if touchpad led is present, DSTS will set some bits, * usually 0x00020000. @@ -282,7 +282,7 @@ static int read_tpd_state(struct eeepc_wmi *eeepc) return -ENODEV; else /* Status is stored in the first bit */ - return ctrl_param & 0x1; + return retval & 0x1; } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -343,15 +343,15 @@ static int eeepc_rfkill_set(void *data, bool blocked) static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) { int dev_id = (unsigned long)data; - u32 ctrl_param; + u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(dev_id, &ctrl_param); + status = eeepc_wmi_get_devstate(dev_id, &retval); if (ACPI_FAILURE(status)) return ; - rfkill_set_sw_state(rfkill, !(ctrl_param & 0x1)); + rfkill_set_sw_state(rfkill, !(retval & 0x1)); } static const struct rfkill_ops eeepc_rfkill_ops = { @@ -365,10 +365,10 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, enum rfkill_type type, int dev_id) { int result; - u32 ctrl_param; + u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(dev_id, &ctrl_param); + status = eeepc_wmi_get_devstate(dev_id, &retval); if (ACPI_FAILURE(status)) return -1; @@ -379,7 +379,7 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, * 0x00020000 - 0100000000000000000 - device supported * 0x00010000 - 0010000000000000000 - not supported / special mode ? */ - if (!ctrl_param || ctrl_param == 0x00060000) + if (!retval || retval == 0x00060000) return -ENODEV; *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, @@ -388,7 +388,7 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, if (!*rfkill) return -EINVAL; - rfkill_init_sw_state(*rfkill, !(ctrl_param & 0x1)); + rfkill_init_sw_state(*rfkill, !(retval & 0x1)); result = rfkill_register(*rfkill); if (result) { rfkill_destroy(*rfkill); @@ -457,15 +457,15 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) */ static int read_brightness(struct backlight_device *bd) { - static u32 ctrl_param; + static u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &ctrl_param); + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); if (ACPI_FAILURE(status)) return -1; else - return ctrl_param & 0xFF; + return retval & 0xFF; } static int update_bl_status(struct backlight_device *bd) From 4c4edfa3d375109c1360ce786b9df984ca65d727 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:11 +0100 Subject: [PATCH 12/36] eeepc-wmi: claim eeepc-wmi maintainership Since eeepc-wmi has currently no official maintainer, I claim maintainership of this driver, and add it to the acpi4asus project. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- MAINTAINERS | 8 ++++++++ drivers/platform/x86/eeepc-wmi.c | 1 + 2 files changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index bcb78306b62f..9a7817591876 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2271,6 +2271,14 @@ W: http://acpi4asus.sf.net S: Maintained F: drivers/platform/x86/eeepc-laptop.c +EEEPC WMI EXTRAS DRIVER +M: Corentin Chary +L: acpi4asus-user@lists.sourceforge.net +L: platform-driver-x86@vger.kernel.org +W: http://acpi4asus.sf.net +S: Maintained +F: drivers/platform/x86/eeepc-wmi.c + EFIFB FRAMEBUFFER DRIVER L: linux-fbdev@vger.kernel.org M: Peter Jones diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 78306bea01f7..5f3f42c8a989 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -2,6 +2,7 @@ * Eee PC WMI hotkey driver * * Copyright(C) 2010 Intel Corporation. + * Copyright(C) 2010 Corentin Chary * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac From dfed65d56f9a94466bb4afd44ec8e900a6709cda Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:12 +0100 Subject: [PATCH 13/36] eeepc-wmi: remove unneeded static Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 5f3f42c8a989..52274e21ee89 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -267,7 +267,7 @@ static void tpd_led_set(struct led_classdev *led_cdev, static int read_tpd_state(struct eeepc_wmi *eeepc) { - static u32 retval; + u32 retval; acpi_status status; status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); @@ -458,7 +458,7 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) */ static int read_brightness(struct backlight_device *bd) { - static u32 retval; + u32 retval; acpi_status status; status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); @@ -472,7 +472,7 @@ static int read_brightness(struct backlight_device *bd) static int update_bl_status(struct backlight_device *bd) { - static u32 ctrl_param; + u32 ctrl_param; acpi_status status; ctrl_param = bd->props.brightness; From 62a75d83131c8887237d26a36ffeabd53c3640fd Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:13 +0100 Subject: [PATCH 14/36] eeepc-laptop: add a getter for touchpad led Allow te get the current led state in a more accurate way. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-laptop.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index b2edfdcdcb84..e9fc530e7dc2 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -529,6 +529,15 @@ static void tpd_led_set(struct led_classdev *led_cdev, queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } +static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) +{ + struct eeepc_laptop *eeepc; + + eeepc = container_of(led_cdev, struct eeepc_laptop, tpd_led); + + return get_acpi(eeepc, CM_ASL_TPD); +} + static int eeepc_led_init(struct eeepc_laptop *eeepc) { int rv; @@ -543,6 +552,8 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) eeepc->tpd_led.name = "eeepc::touchpad"; eeepc->tpd_led.brightness_set = tpd_led_set; + if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ + eeepc->tpd_led.brightness_get = tpd_led_get; eeepc->tpd_led.max_brightness = 1; rv = led_classdev_register(&eeepc->platform_device->dev, From d358cb55a4cc83c37fbaebc0e4401a573777c6ac Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 29 Nov 2010 08:14:14 +0100 Subject: [PATCH 15/36] eeepc-wmi: never load if legacy device is enabled If legacy device (SB.ATKD - ASUS010) used by eeepc-laptop is enabled, don't allow eeepc-wmi to load because: - eeepc-laptop may be loaded, and can conflict with eeepc-wmi (they both try to register eeepc::touchpad led for example). - the WMI interface is inteded to be used when the OS is not detected as Win 7. And when this is the case, the ASUS010 device is disabled. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 52274e21ee89..4d38f98aa976 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -49,6 +49,8 @@ MODULE_AUTHOR("Yong Wang "); MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); MODULE_LICENSE("GPL"); +#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ + #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" @@ -861,6 +863,27 @@ static struct platform_driver platform_driver = { }, }; +static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, + void *context, void **retval) +{ + pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); + *(bool *)context = true; + return AE_CTRL_TERMINATE; +} + +static int __init eeepc_wmi_check_atkd(void) +{ + acpi_status status; + bool found = false; + + status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, + &found, NULL); + + if (ACPI_FAILURE(status) || !found) + return 0; + return -1; +} + static int __init eeepc_wmi_init(void) { int err; @@ -871,6 +894,16 @@ static int __init eeepc_wmi_init(void) return -ENODEV; } + if (eeepc_wmi_check_atkd()) { + pr_warning("WMI device present, but legacy ATKD device is also " + "present and enabled."); + pr_warning("You probably booted with acpi_osi=\"Linux\" or " + "acpi_osi=\"!Windows 2009\""); + pr_warning("Can't load eeepc-wmi, use default acpi_osi " + "(preferred) or eeepc-laptop"); + return -ENODEV; + } + platform_device = eeepc_wmi_add(); if (IS_ERR(platform_device)) { err = PTR_ERR(platform_device); From e1e0dacba5af2b0cd8f9043d0b937296c90bc990 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 6 Dec 2010 16:44:23 -0500 Subject: [PATCH 16/36] WMI: return error if wmi_create_device() fails The break resets the retval to 0 but we want to return an error code. This was introduced in c64eefd48c4 "WMI: embed struct device directly into wmi_block" Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett Acked-by: Dmitry Torokhov --- drivers/platform/x86/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index b8e5383eab0c..05cc79672a8b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -820,7 +820,7 @@ static acpi_status parse_wdg(acpi_handle handle) retval = wmi_create_device(&gblock[i], wblock, handle); if (retval) { wmi_free_devices(); - break; + goto out_free_pointer; } } From 9c23225006b695ceba31c035b287f5a7a0b0304e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 8 Dec 2010 00:04:14 +0900 Subject: [PATCH 17/36] fujitsu-laptop: fix compiler warning on pnp_ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Annotate pnp_ids as '__used' to fix following warning: CC drivers/platform/x86/fujitsu-laptop.o drivers/platform/x86/fujitsu-laptop.c:1243: warning: ‘pnp_ids’ defined but not used Signed-off-by: Namhyung Kim Signed-off-by: Matthew Garrett --- drivers/platform/x86/fujitsu-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index f44cd2620ff9..ad88b2ec34a1 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -1240,7 +1240,7 @@ MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); -static struct pnp_device_id pnp_ids[] = { +static struct pnp_device_id pnp_ids[] __used = { {.id = "FUJ02bf"}, {.id = "FUJ02B1"}, {.id = "FUJ02E3"}, From 98ee69191d3af68e2292528cbb16dcba3d8e2b81 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:00:15 +0800 Subject: [PATCH 18/36] ideapad: add platform driver for ideapad Create /sys/devices/platform/ideapad for nodes of ideapad landing. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 54 +++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5ff12205aa6b..5998ae14a220 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -27,6 +27,7 @@ #include #include #include +#include #define IDEAPAD_DEV_CAMERA 0 #define IDEAPAD_DEV_WLAN 1 @@ -37,6 +38,7 @@ struct ideapad_private { acpi_handle handle; struct rfkill *rfk[5]; + struct platform_device *platform_device; } *ideapad_priv; static struct { @@ -277,6 +279,35 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) rfkill_destroy(priv->rfk[dev]); } +/* + * Platform device + */ +static int __devinit ideapad_platform_init(void) +{ + int result; + + ideapad_priv->platform_device = platform_device_alloc("ideapad", -1); + if (!ideapad_priv->platform_device) + return -ENOMEM; + platform_set_drvdata(ideapad_priv->platform_device, ideapad_priv); + + result = platform_device_add(ideapad_priv->platform_device); + if (result) + goto fail_platform_device; + + return 0; + +fail_platform_device: + platform_device_put(ideapad_priv->platform_device); + return result; +} + +static void ideapad_platform_exit(void) +{ + platform_device_unregister(ideapad_priv->platform_device); +} +/* the above is platform device */ + static const struct acpi_device_id ideapad_device_ids[] = { { "VPC2004", 0}, { "", 0}, @@ -285,7 +316,7 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int ideapad_acpi_add(struct acpi_device *adevice) { - int i, cfg; + int ret, i, cfg; int devs_present[5]; struct ideapad_private *priv; @@ -305,18 +336,20 @@ static int ideapad_acpi_add(struct acpi_device *adevice) priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + ideapad_priv = priv; + + ret = ideapad_platform_init(); + if (ret) + goto platform_failed; if (devs_present[IDEAPAD_DEV_CAMERA]) { - int ret = device_create_file(&adevice->dev, &dev_attr_camera_power); - if (ret) { - kfree(priv); - return ret; - } + ret = device_create_file(&adevice->dev, &dev_attr_camera_power); + if (ret) + goto camera_failed; } priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); - ideapad_priv = priv; for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { if (!devs_present[i]) continue; @@ -325,6 +358,12 @@ static int ideapad_acpi_add(struct acpi_device *adevice) } ideapad_sync_rfk_state(adevice); return 0; + +camera_failed: + ideapad_platform_exit(); +platform_failed: + kfree(priv); + return ret; } static int ideapad_acpi_remove(struct acpi_device *adevice, int type) @@ -337,6 +376,7 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) ideapad_unregister_rfkill(adevice, i); + ideapad_platform_exit(); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); return 0; From c9f718d0c6b4cf8033aa0f5ac892d68ddfb865aa Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:00:27 +0800 Subject: [PATCH 19/36] ideapad: let camera power control entry under platform driver The entry was at /sys/devices/LNXSYSTM:00/../VPC2004:00/camera_power move to /sys/devices/platform/ideapad/camera_power Add document about usage of ideapad node in sysfs. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-platform-ideapad-laptop | 6 ++ drivers/platform/x86/ideapad-laptop.c | 55 +++++++++---------- 2 files changed, 31 insertions(+), 30 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-platform-ideapad-laptop diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop new file mode 100644 index 000000000000..807fca2ae2a4 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop @@ -0,0 +1,6 @@ +What: /sys/devices/platform/ideapad/camera_power +Date: Dec 2010 +KernelVersion: 2.6.37 +Contact: "Ike Panhc " +Description: + Control the power of camera module. 1 means on, 0 means off. diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5998ae14a220..49f207fc61d2 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -282,6 +282,15 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) /* * Platform device */ +static struct attribute *ideapad_attributes[] = { + &dev_attr_camera_power.attr, + NULL +}; + +static struct attribute_group ideapad_attribute_group = { + .attrs = ideapad_attributes +}; + static int __devinit ideapad_platform_init(void) { int result; @@ -295,8 +304,14 @@ static int __devinit ideapad_platform_init(void) if (result) goto fail_platform_device; + result = sysfs_create_group(&ideapad_priv->platform_device->dev.kobj, + &ideapad_attribute_group); + if (result) + goto fail_sysfs; return 0; +fail_sysfs: + platform_device_del(ideapad_priv->platform_device); fail_platform_device: platform_device_put(ideapad_priv->platform_device); return result; @@ -304,6 +319,8 @@ static int __devinit ideapad_platform_init(void) static void ideapad_platform_exit(void) { + sysfs_remove_group(&ideapad_priv->platform_device->dev.kobj, + &ideapad_attribute_group); platform_device_unregister(ideapad_priv->platform_device); } /* the above is platform device */ @@ -317,50 +334,30 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int ideapad_acpi_add(struct acpi_device *adevice) { int ret, i, cfg; - int devs_present[5]; struct ideapad_private *priv; if (read_method_int(adevice->handle, "_CFG", &cfg)) return -ENODEV; - for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) - devs_present[i] = 1; - else - devs_present[i] = 0; - } - - /* The hardware switch is always present */ - devs_present[IDEAPAD_DEV_KILLSW] = 1; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; ideapad_priv = priv; + priv->handle = adevice->handle; + dev_set_drvdata(&adevice->dev, priv); ret = ideapad_platform_init(); if (ret) goto platform_failed; - if (devs_present[IDEAPAD_DEV_CAMERA]) { - ret = device_create_file(&adevice->dev, &dev_attr_camera_power); - if (ret) - goto camera_failed; - } - - priv->handle = adevice->handle; - dev_set_drvdata(&adevice->dev, priv); - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) { - if (!devs_present[i]) - continue; - - ideapad_register_rfkill(adevice, i); + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { + if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) + ideapad_register_rfkill(adevice, i); } ideapad_sync_rfk_state(adevice); + return 0; -camera_failed: - ideapad_platform_exit(); platform_failed: kfree(priv); return ret; @@ -371,14 +368,12 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int i; - device_remove_file(&adevice->dev, &dev_attr_camera_power); - - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) ideapad_unregister_rfkill(adevice, i); - ideapad_platform_exit(); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); + return 0; } From f63409ae91ff94e2192dafbeb00c278ba299f80e Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:00:38 +0800 Subject: [PATCH 20/36] ideapad: add hotkey support Hotkey enabled by this patch: Fn+F3: Video mode switch Fn+F5: software rfkill for wifi For some ideapad when push Fn+F3, hardware generates Super-P keys, those key will not be enabled by this patch. Thanks for Dave Hansen report the problem. If CONFIG_INPUT_SPARSEKMAP is not set, when building, you will have error message: ERROR: "sparse_keymap_setup" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "sparse_keymap_free" [drivers/platform/x86/ideapad-laptop.ko] undefined! ERROR: "sparse_keymap_report_event" [drivers/platform/x86/ideapad-laptop.ko] undefined! To select INPUT_SPARSEKMAP solve this issue. Ref: http://lkml.org/lkml/2010/12/2/340 Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/ideapad-laptop.c | 72 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4c7f8b926103..254c4ef6be35 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -226,6 +226,7 @@ config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI depends on RFKILL + select INPUT_SPARSEKMAP help This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 49f207fc61d2..04b4f5b3114d 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #define IDEAPAD_DEV_CAMERA 0 #define IDEAPAD_DEV_WLAN 1 @@ -39,6 +41,7 @@ struct ideapad_private { acpi_handle handle; struct rfkill *rfk[5]; struct platform_device *platform_device; + struct input_dev *inputdev; } *ideapad_priv; static struct { @@ -325,6 +328,66 @@ static void ideapad_platform_exit(void) } /* the above is platform device */ +/* + * input device + */ +static const struct key_entry ideapad_keymap[] = { + { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x0D, { KEY_WLAN } }, + { KE_END, 0 }, +}; + +static int __devinit ideapad_input_init(void) +{ + struct input_dev *inputdev; + int error; + + inputdev = input_allocate_device(); + if (!inputdev) { + pr_info("Unable to allocate input device\n"); + return -ENOMEM; + } + + inputdev->name = "Ideapad extra buttons"; + inputdev->phys = "ideapad/input0"; + inputdev->id.bustype = BUS_HOST; + inputdev->dev.parent = &ideapad_priv->platform_device->dev; + + error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); + if (error) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; + } + + error = input_register_device(inputdev); + if (error) { + pr_err("Unable to register input device\n"); + goto err_free_keymap; + } + + ideapad_priv->inputdev = inputdev; + return 0; + +err_free_keymap: + sparse_keymap_free(inputdev); +err_free_dev: + input_free_device(inputdev); + return error; +} + +static void __devexit ideapad_input_exit(void) +{ + sparse_keymap_free(ideapad_priv->inputdev); + input_unregister_device(ideapad_priv->inputdev); + ideapad_priv->inputdev = NULL; +} + +static void ideapad_input_report(unsigned long scancode) +{ + sparse_keymap_report_event(ideapad_priv->inputdev, scancode, 1, true); +} +/* the above is input device */ + static const struct acpi_device_id ideapad_device_ids[] = { { "VPC2004", 0}, { "", 0}, @@ -350,6 +413,10 @@ static int ideapad_acpi_add(struct acpi_device *adevice) if (ret) goto platform_failed; + ret = ideapad_input_init(); + if (ret) + goto input_failed; + for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) ideapad_register_rfkill(adevice, i); @@ -358,6 +425,8 @@ static int ideapad_acpi_add(struct acpi_device *adevice) return 0; +input_failed: + ideapad_platform_exit(); platform_failed: kfree(priv); return ret; @@ -370,6 +439,7 @@ static int ideapad_acpi_remove(struct acpi_device *adevice, int type) for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) ideapad_unregister_rfkill(adevice, i); + ideapad_input_exit(); ideapad_platform_exit(); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); @@ -392,6 +462,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (test_bit(vpc_bit, &vpc1)) { if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); + else + ideapad_input_report(vpc_bit); } } } From a4b5a2794a27da870c2e16db390778a4683f95f8 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:00:48 +0800 Subject: [PATCH 21/36] ideapad: add markups, unify comments and return result when init 1. Add markups on init and exit functions 2. Unify the comments in the same style 3. Return result when module initial Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 31 +++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 04b4f5b3114d..3c6c5b5e1d53 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1,5 +1,5 @@ /* - * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras + * ideapad-laptop.c - Lenovo IdeaPad ACPI Extras * * Copyright © 2010 Intel Corporation * Copyright © 2010 David Woodhouse @@ -168,8 +168,10 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) pr_err("timeout in write_ec_cmd\n"); return -1; } -/* the above is ACPI helpers */ +/* + * camera power + */ static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) @@ -203,6 +205,9 @@ static ssize_t store_ideapad_cam(struct device *dev, static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); +/* + * Rfkill + */ static int ideapad_rfk_set(void *data, bool blocked) { int device = (unsigned long)data; @@ -235,7 +240,8 @@ static void ideapad_sync_rfk_state(struct acpi_device *adevice) rfkill_set_hw_state(priv->rfk[i], hw_blocked); } -static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) +static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, + int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int ret; @@ -271,7 +277,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) return 0; } -static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) +static void __devexit ideapad_unregister_rfkill(struct acpi_device *adevice, + int dev) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); @@ -326,7 +333,6 @@ static void ideapad_platform_exit(void) &ideapad_attribute_group); platform_device_unregister(ideapad_priv->platform_device); } -/* the above is platform device */ /* * input device @@ -386,15 +392,17 @@ static void ideapad_input_report(unsigned long scancode) { sparse_keymap_report_event(ideapad_priv->inputdev, scancode, 1, true); } -/* the above is input device */ +/* + * module init/exit + */ static const struct acpi_device_id ideapad_device_ids[] = { { "VPC2004", 0}, { "", 0}, }; MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); -static int ideapad_acpi_add(struct acpi_device *adevice) +static int __devinit ideapad_acpi_add(struct acpi_device *adevice) { int ret, i, cfg; struct ideapad_private *priv; @@ -432,7 +440,7 @@ static int ideapad_acpi_add(struct acpi_device *adevice) return ret; } -static int ideapad_acpi_remove(struct acpi_device *adevice, int type) +static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int i; @@ -478,19 +486,14 @@ static struct acpi_driver ideapad_acpi_driver = { .owner = THIS_MODULE, }; - static int __init ideapad_acpi_module_init(void) { - acpi_bus_register_driver(&ideapad_acpi_driver); - - return 0; + return acpi_bus_register_driver(&ideapad_acpi_driver); } - static void __exit ideapad_acpi_module_exit(void) { acpi_bus_unregister_driver(&ideapad_acpi_driver); - } MODULE_AUTHOR("David Woodhouse "); From 8693ae846cad00e6c2c40e116ec1fc50c145b559 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:01:01 +0800 Subject: [PATCH 22/36] ideapad: pass ideapad_priv as argument (part 1) Passing ideapad_priv as argument and try not to using too much global variable. This is part 1 for platform driver and input device. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 54 ++++++++++++++------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 3c6c5b5e1d53..37fe0d0448c9 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -301,37 +301,37 @@ static struct attribute_group ideapad_attribute_group = { .attrs = ideapad_attributes }; -static int __devinit ideapad_platform_init(void) +static int __devinit ideapad_platform_init(struct ideapad_private *priv) { int result; - ideapad_priv->platform_device = platform_device_alloc("ideapad", -1); - if (!ideapad_priv->platform_device) + priv->platform_device = platform_device_alloc("ideapad", -1); + if (!priv->platform_device) return -ENOMEM; - platform_set_drvdata(ideapad_priv->platform_device, ideapad_priv); + platform_set_drvdata(priv->platform_device, priv); - result = platform_device_add(ideapad_priv->platform_device); + result = platform_device_add(priv->platform_device); if (result) goto fail_platform_device; - result = sysfs_create_group(&ideapad_priv->platform_device->dev.kobj, + result = sysfs_create_group(&priv->platform_device->dev.kobj, &ideapad_attribute_group); if (result) goto fail_sysfs; return 0; fail_sysfs: - platform_device_del(ideapad_priv->platform_device); + platform_device_del(priv->platform_device); fail_platform_device: - platform_device_put(ideapad_priv->platform_device); + platform_device_put(priv->platform_device); return result; } -static void ideapad_platform_exit(void) +static void ideapad_platform_exit(struct ideapad_private *priv) { - sysfs_remove_group(&ideapad_priv->platform_device->dev.kobj, + sysfs_remove_group(&priv->platform_device->dev.kobj, &ideapad_attribute_group); - platform_device_unregister(ideapad_priv->platform_device); + platform_device_unregister(priv->platform_device); } /* @@ -343,7 +343,7 @@ static const struct key_entry ideapad_keymap[] = { { KE_END, 0 }, }; -static int __devinit ideapad_input_init(void) +static int __devinit ideapad_input_init(struct ideapad_private *priv) { struct input_dev *inputdev; int error; @@ -357,7 +357,7 @@ static int __devinit ideapad_input_init(void) inputdev->name = "Ideapad extra buttons"; inputdev->phys = "ideapad/input0"; inputdev->id.bustype = BUS_HOST; - inputdev->dev.parent = &ideapad_priv->platform_device->dev; + inputdev->dev.parent = &priv->platform_device->dev; error = sparse_keymap_setup(inputdev, ideapad_keymap, NULL); if (error) { @@ -371,7 +371,7 @@ static int __devinit ideapad_input_init(void) goto err_free_keymap; } - ideapad_priv->inputdev = inputdev; + priv->inputdev = inputdev; return 0; err_free_keymap: @@ -381,16 +381,17 @@ static int __devinit ideapad_input_init(void) return error; } -static void __devexit ideapad_input_exit(void) +static void __devexit ideapad_input_exit(struct ideapad_private *priv) { - sparse_keymap_free(ideapad_priv->inputdev); - input_unregister_device(ideapad_priv->inputdev); - ideapad_priv->inputdev = NULL; + sparse_keymap_free(priv->inputdev); + input_unregister_device(priv->inputdev); + priv->inputdev = NULL; } -static void ideapad_input_report(unsigned long scancode) +static void ideapad_input_report(struct ideapad_private *priv, + unsigned long scancode) { - sparse_keymap_report_event(ideapad_priv->inputdev, scancode, 1, true); + sparse_keymap_report_event(priv->inputdev, scancode, 1, true); } /* @@ -417,11 +418,11 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); - ret = ideapad_platform_init(); + ret = ideapad_platform_init(priv); if (ret) goto platform_failed; - ret = ideapad_input_init(); + ret = ideapad_input_init(priv); if (ret) goto input_failed; @@ -434,7 +435,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) return 0; input_failed: - ideapad_platform_exit(); + ideapad_platform_exit(priv); platform_failed: kfree(priv); return ret; @@ -447,8 +448,8 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) ideapad_unregister_rfkill(adevice, i); - ideapad_input_exit(); - ideapad_platform_exit(); + ideapad_input_exit(priv); + ideapad_platform_exit(priv); dev_set_drvdata(&adevice->dev, NULL); kfree(priv); @@ -457,6 +458,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) { + struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); acpi_handle handle = adevice->handle; unsigned long vpc1, vpc2, vpc_bit; @@ -471,7 +473,7 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) if (vpc_bit == 9) ideapad_sync_rfk_state(adevice); else - ideapad_input_report(vpc_bit); + ideapad_input_report(priv, vpc_bit); } } } From c1f73658edc8ac6f624968b47a276361ce032ca9 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Mon, 13 Dec 2010 18:01:12 +0800 Subject: [PATCH 23/36] ideapad: pass ideapad_priv as argument (part 2) Passing ideapad_priv as argument and try not to using too much global variable. This is part 2 for rfkill. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 71 +++++++++++---------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 37fe0d0448c9..114d95247cdf 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -31,32 +31,15 @@ #include #include -#define IDEAPAD_DEV_CAMERA 0 -#define IDEAPAD_DEV_WLAN 1 -#define IDEAPAD_DEV_BLUETOOTH 2 -#define IDEAPAD_DEV_3G 3 -#define IDEAPAD_DEV_KILLSW 4 +#define IDEAPAD_RFKILL_DEV_NUM (3) struct ideapad_private { - acpi_handle handle; - struct rfkill *rfk[5]; + struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; struct platform_device *platform_device; struct input_dev *inputdev; -} *ideapad_priv; - -static struct { - char *name; - int cfgbit; - int opcode; - int type; -} ideapad_rfk_data[] = { - { "ideapad_camera", 19, 0x1E, NUM_RFKILL_TYPES }, - { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, - { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, - { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, - { "ideapad_killsw", 0, 0, RFKILL_TYPE_WLAN } }; +static acpi_handle ideapad_handle; static bool no_bt_rfkill; module_param(no_bt_rfkill, bool, 0444); MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); @@ -176,11 +159,9 @@ static ssize_t show_ideapad_cam(struct device *dev, struct device_attribute *attr, char *buf) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; unsigned long result; - if (read_ec_data(handle, 0x1D, &result)) + if (read_ec_data(ideapad_handle, 0x1D, &result)) return sprintf(buf, "-1\n"); return sprintf(buf, "%lu\n", result); } @@ -189,15 +170,13 @@ static ssize_t store_ideapad_cam(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct ideapad_private *priv = dev_get_drvdata(dev); - acpi_handle handle = priv->handle; int ret, state; if (!count) return 0; if (sscanf(buf, "%i", &state) != 1) return -EINVAL; - ret = write_ec_cmd(handle, 0x1E, state); + ret = write_ec_cmd(ideapad_handle, 0x1E, state); if (ret < 0) return ret; return count; @@ -208,16 +187,24 @@ static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); /* * Rfkill */ +struct ideapad_rfk_data { + char *name; + int cfgbit; + int opcode; + int type; +}; + +const struct ideapad_rfk_data ideapad_rfk_data[] = { + { "ideapad_wlan", 18, 0x15, RFKILL_TYPE_WLAN }, + { "ideapad_bluetooth", 16, 0x17, RFKILL_TYPE_BLUETOOTH }, + { "ideapad_3g", 17, 0x20, RFKILL_TYPE_WWAN }, +}; + static int ideapad_rfk_set(void *data, bool blocked) { - int device = (unsigned long)data; + unsigned long opcode = (unsigned long)data; - if (device == IDEAPAD_DEV_KILLSW) - return -EINVAL; - - return write_ec_cmd(ideapad_priv->handle, - ideapad_rfk_data[device].opcode, - !blocked); + return write_ec_cmd(ideapad_handle, opcode, !blocked); } static struct rfkill_ops ideapad_rfk_ops = { @@ -227,15 +214,14 @@ static struct rfkill_ops ideapad_rfk_ops = { static void ideapad_sync_rfk_state(struct acpi_device *adevice) { struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); - acpi_handle handle = priv->handle; unsigned long hw_blocked; int i; - if (read_ec_data(handle, 0x23, &hw_blocked)) + if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) return; hw_blocked = !hw_blocked; - for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) if (priv->rfk[i]) rfkill_set_hw_state(priv->rfk[i], hw_blocked); } @@ -250,7 +236,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, if (no_bt_rfkill && (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) { /* Force to enable bluetooth when no_bt_rfkill=1 */ - write_ec_cmd(ideapad_priv->handle, + write_ec_cmd(ideapad_handle, ideapad_rfk_data[dev].opcode, 1); return 0; } @@ -261,7 +247,7 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, if (!priv->rfk[dev]) return -ENOMEM; - if (read_ec_data(ideapad_priv->handle, ideapad_rfk_data[dev].opcode-1, + if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, &sw_blocked)) { rfkill_init_sw_state(priv->rfk[dev], 0); } else { @@ -414,9 +400,8 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - ideapad_priv = priv; - priv->handle = adevice->handle; dev_set_drvdata(&adevice->dev, priv); + ideapad_handle = adevice->handle; ret = ideapad_platform_init(priv); if (ret) @@ -426,9 +411,11 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) if (ret) goto input_failed; - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) { + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { if (test_bit(ideapad_rfk_data[i].cfgbit, (unsigned long *)&cfg)) ideapad_register_rfkill(adevice, i); + else + priv->rfk[i] = NULL; } ideapad_sync_rfk_state(adevice); @@ -446,7 +433,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); int i; - for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++) + for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) ideapad_unregister_rfkill(adevice, i); ideapad_input_exit(priv); ideapad_platform_exit(priv); From e98062ed6dc46ed3270350e1040e19d44150d1d1 Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Wed, 8 Dec 2010 11:20:26 +0100 Subject: [PATCH 24/36] platform/x86: Kconfig: Replace select by depends on ACPI_WMI With 'make oldnoconfig' I see these warnings in linux-next (next-20101208): drivers/platform/x86/Kconfig:422:error: recursive dependency detected! drivers/platform/x86/Kconfig:422: symbol EEEPC_WMI depends on ACPI_WMI drivers/platform/x86/Kconfig:438: symbol ACPI_WMI is selected by ACER_WMI drivers/platform/x86/Kconfig:18: symbol ACER_WMI depends on LEDS_CLASS drivers/leds/Kconfig:10: symbol LEDS_CLASS is selected by EEEPC_WMI This patch replaces all "select on ACPI_WMI" by "depends on ACPI_WMI". Quote from David Woodhouse: "A better policy is: "NEVER USE SELECT"." Reported-and-tested-by: Sedat Dilek Signed-off-by: Sedat Dilek Acked-by: Randy Dunlap Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 254c4ef6be35..fbee997d2c24 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -23,7 +23,7 @@ config ACER_WMI depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 depends on RFKILL || RFKILL = n - select ACPI_WMI + depends on ACPI_WMI ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, @@ -131,7 +131,7 @@ config TC1100_WMI depends on !X86_64 depends on EXPERIMENTAL depends on ACPI - select ACPI_WMI + depends on ACPI_WMI ---help--- This is a driver for the WMI extensions (wireless and bluetooth power control) of the HP Compaq TC1100 tablet. From 3fdca87d10f1d45b1c034da343e68beb082f9b84 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Tue, 7 Dec 2010 10:29:20 +0800 Subject: [PATCH 25/36] acer-wmi: Add acer wmi hotkey events support Add acer wmi hotkey event support. Install a wmi notify handler to transfer wmi event key to key code, then send out keycode through acer wmi input device to userland. Signed-off-by: Lee, Chun-Yi Acked-by: Dmitry Torokhov Acked-by: Thomas Renninger Acked-by: Jiri Benc Signed-off-by: Carlos Corbacho Cc: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/acer-wmi.c | 139 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index fbee997d2c24..a122a0dac981 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -22,8 +22,10 @@ config ACER_WMI depends on NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 + depends on INPUT depends on RFKILL || RFKILL = n depends on ACPI_WMI + select INPUT_SPARSEKMAP ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c8c65375bfe2..a1c6141f463b 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include @@ -48,6 +50,7 @@ MODULE_LICENSE("GPL"); #define ACER_ERR KERN_ERR ACER_LOGPREFIX #define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX #define ACER_INFO KERN_INFO ACER_LOGPREFIX +#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX /* * Magic Number @@ -83,8 +86,39 @@ MODULE_LICENSE("GPL"); #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" +/* + * Acer ACPI event GUIDs + */ +#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026" + MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); +MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); + +enum acer_wmi_event_ids { + WMID_HOTKEY_EVENT = 0x1, +}; + +static const struct key_entry acer_wmi_keymap[] = { + {KE_KEY, 0x01, {KEY_WLAN} }, /* WiFi */ + {KE_KEY, 0x12, {KEY_BLUETOOTH} }, /* BT */ + {KE_KEY, 0x21, {KEY_PROG1} }, /* Backup */ + {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ + {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ + {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ + {KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */ + {KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */ + {KE_END, 0} +}; + +static struct input_dev *acer_wmi_input_dev; + +struct event_return_value { + u8 function; + u8 key_num; + u16 device_state; + u32 reserved; +} __attribute__((packed)); /* * Interface capability flags @@ -1085,6 +1119,99 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL); +static void acer_wmi_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + struct event_return_value return_value; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + printk(ACER_WARNING "bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + + if (!obj) + return; + if (obj->type != ACPI_TYPE_BUFFER) { + printk(ACER_WARNING "Unknown response received %d\n", + obj->type); + kfree(obj); + return; + } + if (obj->buffer.length != 8) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return; + } + + return_value = *((struct event_return_value *)obj->buffer.pointer); + kfree(obj); + + switch (return_value.function) { + case WMID_HOTKEY_EVENT: + if (!sparse_keymap_report_event(acer_wmi_input_dev, + return_value.key_num, 1, true)) + printk(ACER_WARNING "Unknown key number - 0x%x\n", + return_value.key_num); + break; + default: + printk(ACER_WARNING "Unknown function number - %d - %d\n", + return_value.function, return_value.key_num); + break; + } +} + +static int __init acer_wmi_input_setup(void) +{ + acpi_status status; + int err; + + acer_wmi_input_dev = input_allocate_device(); + if (!acer_wmi_input_dev) + return -ENOMEM; + + acer_wmi_input_dev->name = "Acer WMI hotkeys"; + acer_wmi_input_dev->phys = "wmi/input0"; + acer_wmi_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(acer_wmi_input_dev, acer_wmi_keymap, NULL); + if (err) + goto err_free_dev; + + status = wmi_install_notify_handler(ACERWMID_EVENT_GUID, + acer_wmi_notify, NULL); + if (ACPI_FAILURE(status)) { + err = -EIO; + goto err_free_keymap; + } + + err = input_register_device(acer_wmi_input_dev); + if (err) + goto err_uninstall_notifier; + + return 0; + +err_uninstall_notifier: + wmi_remove_notify_handler(ACERWMID_EVENT_GUID); +err_free_keymap: + sparse_keymap_free(acer_wmi_input_dev); +err_free_dev: + input_free_device(acer_wmi_input_dev); + return err; +} + +static void acer_wmi_input_destroy(void) +{ + wmi_remove_notify_handler(ACERWMID_EVENT_GUID); + sparse_keymap_free(acer_wmi_input_dev); + input_unregister_device(acer_wmi_input_dev); +} + /* * debugfs functions */ @@ -1327,6 +1454,12 @@ static int __init acer_wmi_init(void) "generic video driver\n"); } + if (wmi_has_guid(ACERWMID_EVENT_GUID)) { + err = acer_wmi_input_setup(); + if (err) + return err; + } + err = platform_driver_register(&acer_platform_driver); if (err) { printk(ACER_ERR "Unable to register platform driver.\n"); @@ -1368,11 +1501,17 @@ static int __init acer_wmi_init(void) error_device_alloc: platform_driver_unregister(&acer_platform_driver); error_platform_register: + if (wmi_has_guid(ACERWMID_EVENT_GUID)) + acer_wmi_input_destroy(); + return err; } static void __exit acer_wmi_exit(void) { + if (wmi_has_guid(ACERWMID_EVENT_GUID)) + acer_wmi_input_destroy(); + remove_sysfs(acer_platform_device); remove_debugfs(); platform_device_unregister(acer_platform_device); From b3c9092b2fed427d45117d23ceb577ad8dc46a9a Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Tue, 7 Dec 2010 10:29:22 +0800 Subject: [PATCH 26/36] acer-wmi: Add 3G rfkill sysfs file Add 3G rfkill sysfs file for provide userland to control 3G device on/off by using WMI method. Signed-off-by: Lee, Chun-Yi Acked-by: Thomas Renninger Acked-by: Jiri Benc Acked-by: Dmitry Torokhov Signed-off-by: Carlos Corbaho Cc: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 100 +++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a1c6141f463b..85d263349aac 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -85,6 +85,7 @@ MODULE_LICENSE("GPL"); #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" #define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" +#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" /* * Acer ACPI event GUIDs @@ -120,6 +121,24 @@ struct event_return_value { u32 reserved; } __attribute__((packed)); +/* + * GUID3 Get Device Status device flags + */ +#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ + +struct wmid3_gds_input_param { /* Get Device Status input parameter */ + u8 function_num; /* Function Number */ + u8 hotkey_number; /* Hotkey Number */ + u16 devices; /* Get Device */ +} __attribute__((packed)); + +struct wmid3_gds_return_value { /* Get Device Status return value*/ + u8 error_code; /* Error Code */ + u8 ec_return_value; /* EC Return Value */ + u16 devices; /* Current Device Status */ + u32 reserved; +} __attribute__((packed)); + /* * Interface capability flags */ @@ -174,6 +193,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; +static struct rfkill *threeg_rfkill; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -982,6 +1002,54 @@ static void acer_backlight_exit(void) backlight_device_unregister(acer_backlight_device); } +static acpi_status wmid3_get_device_status(u32 *value, u16 device) +{ + struct wmid3_gds_return_value return_value; + acpi_status status; + union acpi_object *obj; + struct wmid3_gds_input_param params = { + .function_num = 0x1, + .hotkey_number = 0x01, + .devices = device, + }; + struct acpi_buffer input = { + sizeof(struct wmid3_gds_input_param), + ¶ms + }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 8) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); + kfree(obj); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Get Device Status failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + else + *value = !!(return_value.devices & device); + + return status; +} + /* * Rfkill devices */ @@ -1002,6 +1070,13 @@ static void acer_rfkill_update(struct work_struct *ignored) rfkill_set_sw_state(bluetooth_rfkill, !state); } + if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { + status = wmid3_get_device_status(&state, + ACER_WMID3_GDS_THREEG); + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(threeg_rfkill, !state); + } + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); } @@ -1058,6 +1133,19 @@ static int acer_rfkill_init(struct device *dev) } } + if (has_cap(ACER_CAP_THREEG)) { + threeg_rfkill = acer_rfkill_register(dev, + RFKILL_TYPE_WWAN, "acer-threeg", + ACER_CAP_THREEG); + if (IS_ERR(threeg_rfkill)) { + rfkill_unregister(wireless_rfkill); + rfkill_destroy(wireless_rfkill); + rfkill_unregister(bluetooth_rfkill); + rfkill_destroy(bluetooth_rfkill); + return PTR_ERR(threeg_rfkill); + } + } + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; @@ -1074,6 +1162,11 @@ static void acer_rfkill_exit(void) rfkill_unregister(bluetooth_rfkill); rfkill_destroy(bluetooth_rfkill); } + + if (has_cap(ACER_CAP_THREEG)) { + rfkill_unregister(threeg_rfkill); + rfkill_destroy(threeg_rfkill); + } return; } @@ -1084,7 +1177,12 @@ static ssize_t show_bool_threeg(struct device *dev, struct device_attribute *attr, char *buf) { u32 result; \ - acpi_status status = get_u32(&result, ACER_CAP_THREEG); + acpi_status status; + if (wmi_has_guid(WMID_GUID3)) + status = wmid3_get_device_status(&result, + ACER_WMID3_GDS_THREEG); + else + status = get_u32(&result, ACER_CAP_THREEG); if (ACPI_SUCCESS(status)) return sprintf(buf, "%u\n", result); return sprintf(buf, "Read error\n"); From 6c3df88f19375217f0dbfc6160e8c2a635f56c53 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Tue, 7 Dec 2010 10:29:23 +0800 Subject: [PATCH 27/36] acer-wmi: Detect the WiFi/Bluetooth/3G devices available Check the Acer OEM-specific Type AA to detect the WiFi/Bluetooth/3G devices available or not, and set the devices capability flag. Signed-off-by: Lee, Chun-Yi Reviewed-by: Jean Delvare Reviewed-by: Dmitry Torokhov Acked-by: Thomas Renninger Acked-by: Jiri Benc Cc: Carlos Corbacho Cc: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 49 ++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 85d263349aac..583565a8bbce 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -124,7 +125,9 @@ struct event_return_value { /* * GUID3 Get Device Status device flags */ +#define ACER_WMID3_GDS_WIRELESS (1<<0) /* WiFi */ #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ +#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ struct wmid3_gds_input_param { /* Get Device Status input parameter */ u8 function_num; /* Function Number */ @@ -139,6 +142,13 @@ struct wmid3_gds_return_value { /* Get Device Status return value*/ u32 reserved; } __attribute__((packed)); +struct hotkey_function_type_aa { + u8 type; + u8 length; + u16 handle; + u16 commun_func_bitmap; +} __attribute__((packed)); + /* * Interface capability flags */ @@ -169,6 +179,7 @@ static int mailled = -1; static int brightness = -1; static int threeg = -1; static int force_series; +static bool has_type_aa; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -807,6 +818,28 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) return WMI_execute_u32(method_id, (u32)value, NULL); } +static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) +{ + struct hotkey_function_type_aa *type_aa; + + /* We are looking for OEM-specific Type AAh */ + if (header->type != 0xAA) + return; + + has_type_aa = true; + type_aa = (struct hotkey_function_type_aa *) header; + + printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n", + type_aa->commun_func_bitmap); + + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) + interface->capability |= ACER_CAP_WIRELESS; + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_THREEG) + interface->capability |= ACER_CAP_THREEG; + if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) + interface->capability |= ACER_CAP_BLUETOOTH; +} + static acpi_status WMID_set_capabilities(void) { struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -827,16 +860,17 @@ static acpi_status WMID_set_capabilities(void) return AE_ERROR; } - /* Not sure on the meaning of the relevant bits yet to detect these */ - interface->capability |= ACER_CAP_WIRELESS; - interface->capability |= ACER_CAP_THREEG; + dmi_walk(type_aa_dmi_decode, NULL); + if (!has_type_aa) { + interface->capability |= ACER_CAP_WIRELESS; + interface->capability |= ACER_CAP_THREEG; + if (devices & 0x10) + interface->capability |= ACER_CAP_BLUETOOTH; + } /* WMID always provides brightness methods */ interface->capability |= ACER_CAP_BRIGHTNESS; - if (devices & 0x10) - interface->capability |= ACER_CAP_BLUETOOTH; - if (!(devices & 0x20)) max_brightness = 0x9; @@ -915,7 +949,8 @@ static void __init acer_commandline_init(void) * capability isn't available on the given interface */ set_u32(mailled, ACER_CAP_MAILLED); - set_u32(threeg, ACER_CAP_THREEG); + if (!has_type_aa) + set_u32(threeg, ACER_CAP_THREEG); set_u32(brightness, ACER_CAP_BRIGHTNESS); } From 466449cfe797b8a5d82d25d0e0e08426d8dfba19 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Mon, 13 Dec 2010 10:02:41 +0800 Subject: [PATCH 28/36] acer-wmi: Initialize wlan/bluetooth/wwan rfkill software block state Initial wlan/bluetooth/wwan rfkill software block state when acer-wmi driver probe. Acer notebook can save the devices state and this patch can use it to initial the devices' rfkill state. Signed-off-by: Lee, Chun-Yi Acked-by: Thomas Renninger Cc: Carlos Corbacho Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 583565a8bbce..d80db0899382 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1085,6 +1085,31 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device) return status; } +static acpi_status get_device_status(u32 *value, u32 cap) +{ + if (wmi_has_guid(WMID_GUID3)) { + u16 device; + + switch (cap) { + case ACER_CAP_WIRELESS: + device = ACER_WMID3_GDS_WIRELESS; + break; + case ACER_CAP_BLUETOOTH: + device = ACER_WMID3_GDS_BLUETOOTH; + break; + case ACER_CAP_THREEG: + device = ACER_WMID3_GDS_THREEG; + break; + default: + return AE_ERROR; + } + return wmid3_get_device_status(value, device); + + } else { + return get_u32(value, cap); + } +} + /* * Rfkill devices */ @@ -1135,6 +1160,8 @@ static struct rfkill *acer_rfkill_register(struct device *dev, { int err; struct rfkill *rfkill_dev; + u32 state; + acpi_status status; rfkill_dev = rfkill_alloc(name, dev, type, &acer_rfkill_ops, @@ -1142,6 +1169,10 @@ static struct rfkill *acer_rfkill_register(struct device *dev, if (!rfkill_dev) return ERR_PTR(-ENOMEM); + status = get_device_status(&state, cap); + if (ACPI_SUCCESS(status)) + rfkill_init_sw_state(rfkill_dev, !state); + err = rfkill_register(rfkill_dev); if (err) { rfkill_destroy(rfkill_dev); From 213658516fd5e125eb7a97995f6cae8996f8015b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Fri, 24 Dec 2010 19:56:28 +0100 Subject: [PATCH 29/36] ACPI Thinkpad: We must always call va_end() after va_start() but do not do so in thinkpad_acpi.c::acpi_evalf() Hi, In drivers/platform/x86/thinkpad_acpi.c::acpi_evalf() we don't always call va_end() after va_start(). This patch corrects that. Signed-off-by: Jesper Juhl Acked-by: Henrique de Moraes Holschuh Signed-off-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f372dc7ae14e..a974ca383cb9 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -589,6 +589,7 @@ static int acpi_evalf(acpi_handle handle, default: printk(TPACPI_ERR "acpi_evalf() called " "with invalid format character '%c'\n", c); + va_end(ap); return 0; } } From 456dc301cc3b547b2a674de3028f53fb1453e532 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 31 Dec 2010 09:48:20 +0800 Subject: [PATCH 30/36] [PATCH] intel_pmic_gpio: modify EOI handling following change of kernel irq subsystem Latest kernel has many changes in IRQ subsystem and its interfaces, like adding "irq_eoi" for struct irq_chip, this patch will make it support both the new and old interface. Cc: Alek Du Signed-off-by: Feng Tang Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_pmic_gpio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index e61db9dfebef..930e62762365 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -244,7 +244,11 @@ static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) generic_handle_irq(pg->irq_base + gpio); } } - desc->chip->eoi(irq); + + if (desc->chip->irq_eoi) + desc->chip->irq_eoi(irq_get_irq_data(irq)); + else + dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq); } static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) From 59ccf2f3d55c06fd34613f1f78de0279436a7b35 Mon Sep 17 00:00:00 2001 From: "From: Lee, Chun-Yi" Date: Fri, 7 Jan 2011 17:25:14 -0500 Subject: [PATCH 31/36] acer-wmi: Enabled Acer Launch Manager mode Enabled Acer Launch Manager mode to disable the EC raw behavior for communication devices when WMID3 method available. And, we also add a ec_raw_mode kernel module option for enable The EC raw behavior mode when anyone what reset it back. When Acer Launch Manager mode enabled, EC will stop to touch any communication devices' RF state or power state that causes conflict with rfkill_input or any userland daemon to charge the rfkill rules. Signed-off-by: Lee, Chun-Yi Acked-by: Thomas Renninger Acked-by: Jiri Benc Acked-by: Dmitry Torokhov Signed-off-by: Carlos Corbacho Cc: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 110 ++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index d80db0899382..ee40d681edd0 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -129,6 +129,20 @@ struct event_return_value { #define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */ #define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */ +struct lm_input_params { + u8 function_num; /* Function Number */ + u16 commun_devices; /* Communication type devices default status */ + u16 devices; /* Other type devices default status */ + u8 lm_status; /* Launch Manager Status */ + u16 reserved; +} __attribute__((packed)); + +struct lm_return_value { + u8 error_code; /* Error Code */ + u8 ec_return_value; /* EC Return Value */ + u16 reserved; +} __attribute__((packed)); + struct wmid3_gds_input_param { /* Get Device Status input parameter */ u8 function_num; /* Function Number */ u8 hotkey_number; /* Hotkey Number */ @@ -179,16 +193,19 @@ static int mailled = -1; static int brightness = -1; static int threeg = -1; static int force_series; +static bool ec_raw_mode; static bool has_type_aa; module_param(mailled, int, 0444); module_param(brightness, int, 0444); module_param(threeg, int, 0444); module_param(force_series, int, 0444); +module_param(ec_raw_mode, bool, 0444); MODULE_PARM_DESC(mailled, "Set initial state of Mail LED"); MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness"); MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware"); MODULE_PARM_DESC(force_series, "Force a different laptop series"); +MODULE_PARM_DESC(ec_raw_mode, "Enable EC raw mode"); struct acer_data { int mailled; @@ -1330,6 +1347,85 @@ static void acer_wmi_notify(u32 value, void *context) } } +static acpi_status +wmid3_set_lm_mode(struct lm_input_params *params, + struct lm_return_value *return_value) +{ + acpi_status status; + union acpi_object *obj; + + struct acpi_buffer input = { sizeof(struct lm_input_params), params }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + + status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output); + if (ACPI_FAILURE(status)) + return status; + + obj = output.pointer; + + if (!obj) + return AE_ERROR; + else if (obj->type != ACPI_TYPE_BUFFER) { + kfree(obj); + return AE_ERROR; + } + if (obj->buffer.length != 4) { + printk(ACER_WARNING "Unknown buffer length %d\n", + obj->buffer.length); + kfree(obj); + return AE_ERROR; + } + + *return_value = *((struct lm_return_value *)obj->buffer.pointer); + kfree(obj); + + return status; +} + +static int acer_wmi_enable_ec_raw(void) +{ + struct lm_return_value return_value; + acpi_status status; + struct lm_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .lm_status = 0x00, /* Launch Manager Deactive */ + }; + + status = wmid3_set_lm_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Enabling EC raw mode failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + else + printk(ACER_INFO "Enabled EC raw mode"); + + return status; +} + +static int acer_wmi_enable_lm(void) +{ + struct lm_return_value return_value; + acpi_status status; + struct lm_input_params params = { + .function_num = 0x1, + .commun_devices = 0xFFFF, + .devices = 0xFFFF, + .lm_status = 0x01, /* Launch Manager Active */ + }; + + status = wmid3_set_lm_mode(¶ms, &return_value); + + if (return_value.error_code || return_value.ec_return_value) + printk(ACER_WARNING "Enabling Launch Manager failed: " + "0x%x - 0x%x\n", return_value.error_code, + return_value.ec_return_value); + + return status; +} + static int __init acer_wmi_input_setup(void) { acpi_status status; @@ -1618,6 +1714,20 @@ static int __init acer_wmi_init(void) "generic video driver\n"); } + if (wmi_has_guid(WMID_GUID3)) { + if (ec_raw_mode) { + if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) { + printk(ACER_ERR "Cannot enable EC raw mode\n"); + return -ENODEV; + } + } else if (ACPI_FAILURE(acer_wmi_enable_lm())) { + printk(ACER_ERR "Cannot enable Launch Manager mode\n"); + return -ENODEV; + } + } else if (ec_raw_mode) { + printk(ACER_INFO "No WMID EC raw mode enable method\n"); + } + if (wmi_has_guid(ACERWMID_EVENT_GUID)) { err = acer_wmi_input_setup(); if (err) From 4a198be7f072190a44033b7de6084b090b9885ee Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 7 Jan 2011 17:29:44 -0500 Subject: [PATCH 32/36] Support KHLB2 in the compal laptop driver Add the KHLB2 model identifier to the list of supported models Signed-off-by: Albert Astals Cid Signed-off-by: Matthew Garrett --- drivers/platform/x86/compal-laptop.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 097083cac413..034572b980c9 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -872,6 +872,14 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }, .callback = dmi_check_cb_extra }, + { + .ident = "KHLB2", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "KHLB2"), + DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"), + }, + .callback = dmi_check_cb_extra + }, { } }; From 7027d8b570244f0fa3aaebccf0bcd8e95e172631 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 8 Jan 2011 19:55:40 -0800 Subject: [PATCH 33/36] intel_ips: fix sparse non-ANSI function warning Fix sparse warning for non-ANSI function declaration: drivers/platform/x86/intel_ips.c:1477:25: warning: non-ANSI function declaration of function 'ips_link_to_i915_driver' Signed-off-by: Randy Dunlap Cc: Matthew Garrett Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_ips.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index f0b3ad13c273..1294a39373ba 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1474,7 +1474,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) } void -ips_link_to_i915_driver() +ips_link_to_i915_driver(void) { /* We can't cleanly get at the various ips_driver structs from * this caller (the i915 driver), so just set a flag saying From a46a780835f394869e1fbbef8b528a1e02193e78 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 8 Jan 2011 19:56:44 -0800 Subject: [PATCH 34/36] sony-laptop: fix sparse non-ANSI function warning Fix sparse warning for non-ANSI function declaration: drivers/platform/x86/sony-laptop.c:1134:35: warning: non-ANSI function declaration of function 'sony_nc_rfkill_update' Signed-off-by: Randy Dunlap Cc: Matthew Garrett Cc: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f200677851b8..68edc97f1926 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1131,7 +1131,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, return err; } -static void sony_nc_rfkill_update() +static void sony_nc_rfkill_update(void) { enum sony_nc_rfkill i; int result; From 0c51a4d8abd6ed5ba55f828840c6b78ab672644b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 10 Jan 2011 14:37:02 +0000 Subject: [PATCH 35/36] platform/x86: Consistently select LEDS Kconfig options Currently the x86 platform devices are not consistent about selecting or depending on the LEDs Kconfig variables, and this inconsistency leads to Kconfig getting upset and refusing to offer LEDs (even on non-x86 platforms): drivers/platform/x86/Kconfig:422:error: recursive dependency detected! drivers/platform/x86/Kconfig:422: symbol EEEPC_WMI depends on ACPI_WMI drivers/platform/x86/Kconfig:438: symbol ACPI_WMI is selected by ACER_WMI drivers/platform/x86/Kconfig:18: symbol ACER_WMI depends on LEDS_CLASS drivers/leds/Kconfig:10: symbol LEDS_CLASS is selected by EEEPC_WMI Fix this by always selecting rather than depending on the symbols as slightly more drivers use this approach already and it seems more user friendly. Signed-off-by: Mark Brown Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index a122a0dac981..d163bc2e2b9e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -18,8 +18,8 @@ if X86_PLATFORM_DEVICES config ACER_WMI tristate "Acer WMI Laptop Extras" depends on ACPI - depends on LEDS_CLASS - depends on NEW_LEDS + select LEDS_CLASS + select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 depends on INPUT @@ -516,8 +516,8 @@ config TOPSTAR_LAPTOP config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on ACPI - depends on LEDS_CLASS - depends on NEW_LEDS + select LEDS_CLASS + select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on INPUT depends on RFKILL || RFKILL = n From 1a7d946993aaf2a79e9c65abbe169a108e351bcb Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 8 Jan 2011 18:47:29 +0900 Subject: [PATCH 36/36] sony-laptop: support new hotkeys on the P, Z and EC series Add new mappings for assist, VAIO, zoom and eject buttons present on refurbished P, Z and EC models. Reported-by: Gyorgy Jeney Reported-by: Stephan Mueller Cc: Dmitry Torokhov Cc: Matthew Garrett Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 10 ++++++++++ include/linux/sonypi.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 68edc97f1926..b4a95bb2f232 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -235,6 +235,7 @@ static int sony_laptop_input_index[] = { 57, /* 70 SONYPI_EVENT_VOLUME_DEC_PRESSED */ -1, /* 71 SONYPI_EVENT_BRIGHTNESS_PRESSED */ 58, /* 72 SONYPI_EVENT_MEDIA_PRESSED */ + 59, /* 72 SONYPI_EVENT_VENDOR_PRESSED */ }; static int sony_laptop_input_keycode_map[] = { @@ -297,6 +298,7 @@ static int sony_laptop_input_keycode_map[] = { KEY_VOLUMEUP, /* 56 SONYPI_EVENT_VOLUME_INC_PRESSED */ KEY_VOLUMEDOWN, /* 57 SONYPI_EVENT_VOLUME_DEC_PRESSED */ KEY_MEDIA, /* 58 SONYPI_EVENT_MEDIA_PRESSED */ + KEY_VENDOR, /* 59 SONYPI_EVENT_VENDOR_PRESSED */ }; /* release buttons after a short delay if pressed */ @@ -894,10 +896,18 @@ static struct sony_nc_event sony_100_events[] = { { 0x0A, SONYPI_EVENT_FNKEY_RELEASED }, { 0x8C, SONYPI_EVENT_FNKEY_F12 }, { 0x0C, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x9d, SONYPI_EVENT_ZOOM_PRESSED }, + { 0x1d, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0x9f, SONYPI_EVENT_CD_EJECT_PRESSED }, { 0x1f, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0xa1, SONYPI_EVENT_MEDIA_PRESSED }, { 0x21, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa4, SONYPI_EVENT_CD_EJECT_PRESSED }, + { 0x24, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa5, SONYPI_EVENT_VENDOR_PRESSED }, + { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa6, SONYPI_EVENT_HELP_PRESSED }, + { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0, 0 }, }; diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 4f95c1aac2fd..0e6dc3891942 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -112,6 +112,7 @@ #define SONYPI_EVENT_VOLUME_DEC_PRESSED 70 #define SONYPI_EVENT_BRIGHTNESS_PRESSED 71 #define SONYPI_EVENT_MEDIA_PRESSED 72 +#define SONYPI_EVENT_VENDOR_PRESSED 73 /* get/set brightness */ #define SONYPI_IOCGBRT _IOR('v', 0, __u8)