From 883ae7992c09435927dda642b61f1455fceb5b85 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Wed, 23 Feb 2011 21:39:59 +0800 Subject: [PATCH 01/81] ideapad: read brightness setting on brightness key notify BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=25922 On ideapad Y530, the brightness key notify will be blocked if the last notify is not responsed by getting the brightness value. Read value when we get the notify shall fix the problem and will not have any difference on other ideapads. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 114d95247cd..21b101899ba 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -459,6 +459,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 if (vpc_bit == 4) + read_ec_data(handle, 0x12, &vpc2); else ideapad_input_report(priv, vpc_bit); } From 89ca11771a4b50ed616ab6c37e0ef333d02f1d47 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Thu, 3 Feb 2011 16:27:55 +0000 Subject: [PATCH 02/81] OLPC XO-1.5 ebook switch driver The OLPC XO-1.5 has an ebook switch, triggered when the laptop screen is rotated then folding down, converting the device into ebook form. This switch is exposed through ACPI. Add a driver that exposes it to userspace as an input device. Signed-off-by: Daniel Drake Acked-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 9 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/xo15-ebook.c | 181 ++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 drivers/platform/x86/xo15-ebook.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 222dfb737b1..2f6a37a1d2e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -672,4 +672,13 @@ config XO1_RFKILL Support for enabling/disabling the WLAN interface on the OLPC XO-1 laptop. +config XO15_EBOOK + tristate "OLPC XO-1.5 ebook switch" + depends on ACPI && INPUT + ---help--- + Support for the ebook switch on the OLPC XO-1.5 laptop. + + This switch is triggered as the screen is rotated and folded down to + convert the device into ebook form. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 299aefb3e74..daa286c9d53 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -34,4 +34,5 @@ obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o +obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c new file mode 100644 index 00000000000..2343bb36e56 --- /dev/null +++ b/drivers/platform/x86/xo15-ebook.c @@ -0,0 +1,181 @@ +/* + * OLPC XO-1.5 ebook switch driver + * (based on generic ACPI button driver) + * + * Copyright (C) 2009 Paul Fox + * Copyright (C) 2010 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "xo15-ebook" +#define PREFIX MODULE_NAME ": " + +#define XO15_EBOOK_CLASS MODULE_NAME +#define XO15_EBOOK_TYPE_UNKNOWN 0x00 +#define XO15_EBOOK_NOTIFY_STATUS 0x80 + +#define XO15_EBOOK_SUBCLASS "ebook" +#define XO15_EBOOK_HID "XO15EBK" +#define XO15_EBOOK_DEVICE_NAME "EBook Switch" + +ACPI_MODULE_NAME(MODULE_NAME); + +MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver"); +MODULE_LICENSE("GPL"); + +static const struct acpi_device_id ebook_device_ids[] = { + { XO15_EBOOK_HID, 0 }, + { "", 0 }, +}; +MODULE_DEVICE_TABLE(acpi, ebook_device_ids); + +struct ebook_switch { + struct input_dev *input; + char phys[32]; /* for input device */ +}; + +static int ebook_send_state(struct acpi_device *device) +{ + struct ebook_switch *button = acpi_driver_data(device); + unsigned long long state; + acpi_status status; + + status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); + if (ACPI_FAILURE(status)) + return -EIO; + + /* input layer checks if event is redundant */ + input_report_switch(button->input, SW_TABLET_MODE, !state); + input_sync(button->input); + return 0; +} + +static void ebook_switch_notify(struct acpi_device *device, u32 event) +{ + switch (event) { + case ACPI_FIXED_HARDWARE_EVENT: + case XO15_EBOOK_NOTIFY_STATUS: + ebook_send_state(device); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } +} + +static int ebook_switch_resume(struct acpi_device *device) +{ + return ebook_send_state(device); +} + +static int ebook_switch_add(struct acpi_device *device) +{ + struct ebook_switch *button; + struct input_dev *input; + const char *hid = acpi_device_hid(device); + char *name, *class; + int error; + + button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_button; + } + + name = acpi_device_name(device); + class = acpi_device_class(device); + + if (strcmp(hid, XO15_EBOOK_HID)) { + printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); + error = -ENODEV; + goto err_free_input; + } + + strcpy(name, XO15_EBOOK_DEVICE_NAME); + sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS); + + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid); + + input->name = name; + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->dev.parent = &device->dev; + + input->evbit[0] = BIT_MASK(EV_SW); + set_bit(SW_TABLET_MODE, input->swbit); + + error = input_register_device(input); + if (error) + goto err_free_input; + + ebook_send_state(device); + + if (device->wakeup.flags.valid) { + /* Button's GPE is run-wake GPE */ + acpi_enable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + device->wakeup.run_wake_count++; + device_set_wakeup_enable(&device->dev, true); + } + + return 0; + + err_free_input: + input_free_device(input); + err_free_button: + kfree(button); + return error; +} + +static int ebook_switch_remove(struct acpi_device *device, int type) +{ + struct ebook_switch *button = acpi_driver_data(device); + + input_unregister_device(button->input); + kfree(button); + return 0; +} + +static struct acpi_driver xo15_ebook_driver = { + .name = MODULE_NAME, + .class = XO15_EBOOK_CLASS, + .ids = ebook_device_ids, + .ops = { + .add = ebook_switch_add, + .resume = ebook_switch_resume, + .remove = ebook_switch_remove, + .notify = ebook_switch_notify, + }, +}; + +static int __init xo15_ebook_init(void) +{ + return acpi_bus_register_driver(&xo15_ebook_driver); +} + +static void __exit xo15_ebook_exit(void) +{ + acpi_bus_unregister_driver(&xo15_ebook_driver); +} + +module_init(xo15_ebook_init); +module_exit(xo15_ebook_exit); From 5628e5aa12d6147f4174ed7c42da146ef57fd125 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:26 +0100 Subject: [PATCH 03/81] eeepc-wmi: reorder keymap Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 4d38f98aa97..350b7bcfc32 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -74,18 +74,18 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ - { KE_KEY, 0x5d, { KEY_WLAN } }, - { KE_KEY, 0x32, { KEY_MUTE } }, - { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, - { KE_KEY, 0x30, { KEY_VOLUMEUP } }, { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x5c, { KEY_F15 } }, + { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, - { KE_KEY, 0x5c, { KEY_F15 } }, { KE_END, 0}, }; From bc40cce201b69ab25178565e298d9482a5876306 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:27 +0100 Subject: [PATCH 04/81] eeepc-wmi: add wlan key found on 1015P Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 350b7bcfc32..0d8217d1478 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -82,6 +82,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x5c, { KEY_F15 } }, { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, From afa7c886578ce264d9b66d4bcb1fea51fac47925 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:28 +0100 Subject: [PATCH 05/81] eeepc-wmi: add hotplug code for Eeepc 1000H Implement wireless like hotplug handling (code stolen from eeepc-laptop). Reminder: on some models rfkill is implemented by logically unplugging the wireless card from the PCI bus. Despite sending ACPI notifications, this does not appear to be implemented using standard ACPI hotplug - nor does the firmware provide the _OSC method required to support native PCIe hotplug. The only sensible choice appears to be to handle the hotplugging directly in the platform driver. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 274 ++++++++++++++++++++++++++++++- 1 file changed, 273 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0d8217d1478..01bc2b3da98 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -37,9 +37,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -72,6 +75,14 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 +static bool hotplug_wireless; + +module_param(hotplug_wireless, bool, 0444); +MODULE_PARM_DESC(hotplug_wireless, + "Enable hotplug for wireless device. " + "If your laptop needs that, please report to " + "acpi4asus-user@lists.sourceforge.net."); + static const struct key_entry eeepc_wmi_keymap[] = { /* Sleep already handled via generic ACPI code */ { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, @@ -109,6 +120,8 @@ struct eeepc_wmi_debug { }; struct eeepc_wmi { + bool hotplug_wireless; + struct input_dev *inputdev; struct backlight_device *backlight_device; struct platform_device *platform_device; @@ -122,6 +135,9 @@ struct eeepc_wmi { struct rfkill *bluetooth_rfkill; struct rfkill *wwan3g_rfkill; + struct hotplug_slot *hotplug_slot; + struct mutex hotplug_lock; + struct eeepc_wmi_debug debug; }; @@ -177,7 +193,8 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) u32 tmp; status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_DSTS, &input, &output); + 1, EEEPC_WMI_METHODID_DSTS, + &input, &output); if (ACPI_FAILURE(status)) return status; @@ -333,6 +350,206 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) destroy_workqueue(eeepc->led_workqueue); } +/* + * PCI hotplug (for wlan rfkill) + */ +static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) +{ + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); + + if (ACPI_FAILURE(status)) + return false; + + return !(retval & 0x1); +} + +static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) +{ + struct pci_dev *dev; + struct pci_bus *bus; + bool blocked = eeepc_wlan_rfkill_blocked(eeepc); + bool absent; + u32 l; + + if (eeepc->wlan_rfkill) + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + + mutex_lock(&eeepc->hotplug_lock); + + if (eeepc->hotplug_slot) { + bus = pci_find_bus(0, 1); + if (!bus) { + pr_warning("Unable to find PCI bus 1?\n"); + goto out_unlock; + } + + if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { + pr_err("Unable to read PCI config space?\n"); + goto out_unlock; + } + absent = (l == 0xffffffff); + + if (blocked != absent) { + pr_warning("BIOS says wireless lan is %s, " + "but the pci device is %s\n", + blocked ? "blocked" : "unblocked", + absent ? "absent" : "present"); + pr_warning("skipped wireless hotplug as probably " + "inappropriate for this model\n"); + goto out_unlock; + } + + if (!blocked) { + dev = pci_get_slot(bus, 0); + if (dev) { + /* Device already present */ + pci_dev_put(dev); + goto out_unlock; + } + dev = pci_scan_single_device(bus, 0); + if (dev) { + pci_bus_assign_resources(bus); + if (pci_bus_add_device(dev)) + pr_err("Unable to hotplug wifi\n"); + } + } else { + dev = pci_get_slot(bus, 0); + if (dev) { + pci_remove_bus_device(dev); + pci_dev_put(dev); + } + } + } + +out_unlock: + mutex_unlock(&eeepc->hotplug_lock); +} + +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +{ + struct eeepc_wmi *eeepc = data; + + if (event != ACPI_NOTIFY_BUS_CHECK) + return; + + eeepc_rfkill_hotplug(eeepc); +} + +static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, + char *node) +{ + acpi_status status; + acpi_handle handle; + + status = acpi_get_handle(NULL, node, &handle); + + if (ACPI_SUCCESS(status)) { + status = acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify, + eeepc); + if (ACPI_FAILURE(status)) + pr_warning("Failed to register notify on %s\n", node); + } else + return -ENODEV; + + return 0; +} + +static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, + char *node) +{ + acpi_status status = AE_OK; + acpi_handle handle; + + status = acpi_get_handle(NULL, node, &handle); + + if (ACPI_SUCCESS(status)) { + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify); + if (ACPI_FAILURE(status)) + pr_err("Error removing rfkill notify handler %s\n", + node); + } +} + +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, + u8 *value) +{ + u32 retval; + acpi_status status; + + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (!retval || retval == 0x00060000) + return -ENODEV; + else + *value = (retval & 0x1); + + return 0; +} + +static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) +{ + kfree(hotplug_slot->info); + kfree(hotplug_slot); +} + +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { + .owner = THIS_MODULE, + .get_adapter_status = eeepc_get_adapter_status, + .get_power_status = eeepc_get_adapter_status, +}; + +static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) +{ + int ret = -ENOMEM; + struct pci_bus *bus = pci_find_bus(0, 1); + + if (!bus) { + pr_err("Unable to find wifi PCI bus\n"); + return -ENODEV; + } + + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!eeepc->hotplug_slot) + goto error_slot; + + eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), + GFP_KERNEL); + if (!eeepc->hotplug_slot->info) + goto error_info; + + eeepc->hotplug_slot->private = eeepc; + eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; + eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; + eeepc_get_adapter_status(eeepc->hotplug_slot, + &eeepc->hotplug_slot->info->adapter_status); + + ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); + if (ret) { + pr_err("Unable to register hotplug slot - %d\n", ret); + goto error_register; + } + + return 0; + +error_register: + kfree(eeepc->hotplug_slot->info); +error_info: + kfree(eeepc->hotplug_slot); + eeepc->hotplug_slot = NULL; +error_slot: + return ret; +} + /* * Rfkill devices */ @@ -404,11 +621,22 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) { + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); if (eeepc->wlan_rfkill) { rfkill_unregister(eeepc->wlan_rfkill); rfkill_destroy(eeepc->wlan_rfkill); eeepc->wlan_rfkill = NULL; } + /* + * Refresh pci hotplug in case the rfkill state was changed after + * eeepc_unregister_rfkill_notifier() + */ + eeepc_rfkill_hotplug(eeepc); + if (eeepc->hotplug_slot) + pci_hp_deregister(eeepc->hotplug_slot); + if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); rfkill_destroy(eeepc->bluetooth_rfkill); @@ -425,6 +653,8 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) { int result = 0; + mutex_init(&eeepc->hotplug_lock); + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, "eeepc-wlan", RFKILL_TYPE_WLAN, EEEPC_WMI_DEVID_WLAN); @@ -446,6 +676,23 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + result = eeepc_setup_pci_hotplug(eeepc); + /* + * If we get -EBUSY then something else is handling the PCI hotplug - + * don't fail in this case + */ + if (result == -EBUSY) + result = 0; + + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); + /* + * Refresh pci hotplug in case the rfkill state was changed during + * setup. + */ + eeepc_rfkill_hotplug(eeepc); + exit: if (result && result != -ENODEV) eeepc_wmi_rfkill_exit(eeepc); @@ -771,6 +1018,28 @@ error_debugfs: /* * WMI Driver */ +static void eeepc_dmi_check(struct eeepc_wmi *eeepc) +{ + const char *model; + + model = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!model) + return; + + /* + * Whitelist for wlan hotplug + * + * Eeepc 1000H needs the current hotplug code to handle + * Fn+F2 correctly. We may add other Eeepc here later, but + * it seems that most of the laptops supported by eeepc-wmi + * don't need to be on this list + */ + if (strcmp(model, "1000H") == 0) { + eeepc->hotplug_wireless = true; + pr_info("wlan hotplug enabled\n"); + } +} + static struct platform_device * __init eeepc_wmi_add(void) { struct eeepc_wmi *eeepc; @@ -781,6 +1050,9 @@ static struct platform_device * __init eeepc_wmi_add(void) if (!eeepc) return ERR_PTR(-ENOMEM); + eeepc->hotplug_wireless = hotplug_wireless; + eeepc_dmi_check(eeepc); + /* * Register the platform device first. It is used as a parent for the * sub-devices below. From 279f8f95493c9aaa0a85520c863ccba87c4bf930 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:29 +0100 Subject: [PATCH 06/81] eeepc-wmi: serialize access to wmi method \AMW0.WMBC, which is the main method that we use, is not reentrant. When wireless hotpluging is enabled, toggling the status of the wireless device using WMBC will trigger a notification and the notification handler need to call WMBC again to get the new status of the device, this will trigger the following error: ACPI Error (dswload-0802): [_T_0] Namespace lookup failure, AE_ALREADY_EXISTS ACPI Exception: AE_ALREADY_EXISTS, During name lookup/catalog (20100428/psloop-231) ACPI Error (psparse-0537): Method parse/execution failed [\AMW0.WMBC] (Node f7023b88), AE_ALREADY_EXISTS ACPI: Marking method WMBC as Serialized because of AE_ALREADY_EXISTS error Since there is currently no way to tell the acpi subsystem to mark a method as serialized, we do it in eeepc-wmi. Of course, we could let the first call fail, and then it would work, but it doesn't seems really clean, and it will make the first WMBC call return a random value. This patch was tested on EeePc 1000H with a RaLink RT2860 wireless card using the rt2800pci driver. rt2860sta driver seems to deadlock when we remove the pci device... Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 78 +++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 01bc2b3da98..eb4c0ce88ac 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -137,6 +137,9 @@ struct eeepc_wmi { struct hotplug_slot *hotplug_slot; struct mutex hotplug_lock; + struct mutex wmi_lock; + struct workqueue_struct *hotplug_workqueue; + struct work_struct hotplug_work; struct eeepc_wmi_debug debug; }; @@ -370,15 +373,19 @@ static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) { struct pci_dev *dev; struct pci_bus *bus; - bool blocked = eeepc_wlan_rfkill_blocked(eeepc); + bool blocked; bool absent; u32 l; - if (eeepc->wlan_rfkill) - rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + mutex_lock(&eeepc->wmi_lock); + blocked = eeepc_wlan_rfkill_blocked(eeepc); + mutex_unlock(&eeepc->wmi_lock); mutex_lock(&eeepc->hotplug_lock); + if (eeepc->wlan_rfkill) + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + if (eeepc->hotplug_slot) { bus = pci_find_bus(0, 1); if (!bus) { @@ -435,7 +442,14 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) if (event != ACPI_NOTIFY_BUS_CHECK) return; - eeepc_rfkill_hotplug(eeepc); + /* + * We can't call directly eeepc_rfkill_hotplug because most + * of the time WMBC is still being executed and not reetrant. + * There is currently no way to tell ACPICA that we want this + * method to be serialized, we schedule a eeepc_rfkill_hotplug + * call later, in a safer context. + */ + queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work); } static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, @@ -508,6 +522,14 @@ static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { .get_power_status = eeepc_get_adapter_status, }; +static void eeepc_hotplug_work(struct work_struct *work) +{ + struct eeepc_wmi *eeepc; + + eeepc = container_of(work, struct eeepc_wmi, hotplug_work); + eeepc_rfkill_hotplug(eeepc); +} + static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) { int ret = -ENOMEM; @@ -518,6 +540,13 @@ static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) return -ENODEV; } + eeepc->hotplug_workqueue = + create_singlethread_workqueue("hotplug_workqueue"); + if (!eeepc->hotplug_workqueue) + goto error_workqueue; + + INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work); + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); if (!eeepc->hotplug_slot) goto error_slot; @@ -547,6 +576,8 @@ error_info: kfree(eeepc->hotplug_slot); eeepc->hotplug_slot = NULL; error_slot: + destroy_workqueue(eeepc->hotplug_workqueue); +error_workqueue: return ret; } @@ -575,6 +606,34 @@ static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) rfkill_set_sw_state(rfkill, !(retval & 0x1)); } +static int eeepc_rfkill_wlan_set(void *data, bool blocked) +{ + struct eeepc_wmi *eeepc = data; + int ret; + + /* + * This handler is enabled only if hotplug is enabled. + * In this case, the eeepc_wmi_set_devstate() will + * trigger a wmi notification and we need to wait + * this call to finish before being able to call + * any wmi method + */ + mutex_lock(&eeepc->wmi_lock); + ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked); + mutex_unlock(&eeepc->wmi_lock); + return ret; +} + +static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data) +{ + eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN); +} + +static const struct rfkill_ops eeepc_rfkill_wlan_ops = { + .set_block = eeepc_rfkill_wlan_set, + .query = eeepc_rfkill_wlan_query, +}; + static const struct rfkill_ops eeepc_rfkill_ops = { .set_block = eeepc_rfkill_set, .query = eeepc_rfkill_query, @@ -603,8 +662,12 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, if (!retval || retval == 0x00060000) return -ENODEV; - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, - &eeepc_rfkill_ops, (void *)(long)dev_id); + if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_wlan_ops, eeepc); + else + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, + &eeepc_rfkill_ops, (void *)(long)dev_id); if (!*rfkill) return -EINVAL; @@ -636,6 +699,8 @@ static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) eeepc_rfkill_hotplug(eeepc); if (eeepc->hotplug_slot) pci_hp_deregister(eeepc->hotplug_slot); + if (eeepc->hotplug_workqueue) + destroy_workqueue(eeepc->hotplug_workqueue); if (eeepc->bluetooth_rfkill) { rfkill_unregister(eeepc->bluetooth_rfkill); @@ -654,6 +719,7 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) int result = 0; mutex_init(&eeepc->hotplug_lock); + mutex_init(&eeepc->wmi_lock); result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, "eeepc-wlan", RFKILL_TYPE_WLAN, From 7898cf1a3665d22c4d16308f73e981c6464be81b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:30 +0100 Subject: [PATCH 07/81] eeepc-wmi: return proper error code in eeepc_rfkill_set() Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index eb4c0ce88ac..d8234582b54 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -588,8 +588,14 @@ static int eeepc_rfkill_set(void *data, bool blocked) { int dev_id = (unsigned long)data; u32 ctrl_param = !blocked; + acpi_status status; - return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); + status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; } static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) From 5c95638d115f9c6661fff254b3beb14b19f88e41 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:31 +0100 Subject: [PATCH 08/81] eeepc-wmi: add an helper using simple return codes eeepc_wmi_get_devstate returns an acpi_status, so each call need extra logic to handle the return code. This patch add a simple getter, returning a boolean (or a negative error code). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 96 +++++++++++++------------------- 1 file changed, 39 insertions(+), 57 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index d8234582b54..de501fb3d1b 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -257,6 +257,29 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, return status; } +/* Helper for special devices with magic return codes */ +static int eeepc_wmi_get_devstate_simple(u32 dev_id) +{ + u32 retval = 0; + acpi_status status; + + status = eeepc_wmi_get_devstate(dev_id, &retval); + + if (ACPI_FAILURE(status)) + return -EINVAL; + + /* 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 (!retval || retval == 0x00060000) + return -ENODEV; + + return retval & 0x1; +} + /* * LEDs */ @@ -290,23 +313,7 @@ static void tpd_led_set(struct led_classdev *led_cdev, static int read_tpd_state(struct eeepc_wmi *eeepc) { - u32 retval; - acpi_status status; - - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval); - - if (ACPI_FAILURE(status)) - return -1; - else if (!retval || retval == 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 retval & 0x1; + return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TPDLED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -358,15 +365,11 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) */ static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) { - u32 retval; - acpi_status status; + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); - - if (ACPI_FAILURE(status)) + if (result < 0) return false; - - return !(retval & 0x1); + return !result; } static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) @@ -494,19 +497,12 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - u32 retval; - acpi_status status; + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval); - - if (ACPI_FAILURE(status)) - return -EIO; - - if (!retval || retval == 0x00060000) - return -ENODEV; - else - *value = (retval & 0x1); + if (result < 0) + return result; + *value = !!result; return 0; } @@ -601,15 +597,14 @@ 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 retval; - acpi_status status; + int result; - status = eeepc_wmi_get_devstate(dev_id, &retval); + result = eeepc_wmi_get_devstate_simple(dev_id); - if (ACPI_FAILURE(status)) + if (result < 0) return ; - rfkill_set_sw_state(rfkill, !(retval & 0x1)); + rfkill_set_sw_state(rfkill, !result); } static int eeepc_rfkill_wlan_set(void *data, bool blocked) @@ -650,23 +645,10 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, const char *name, enum rfkill_type type, int dev_id) { - int result; - u32 retval; - acpi_status status; + int result = eeepc_wmi_get_devstate_simple(dev_id); - status = eeepc_wmi_get_devstate(dev_id, &retval); - - 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 (!retval || retval == 0x00060000) - return -ENODEV; + if (result < 0) + return result; if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, @@ -678,7 +660,7 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, if (!*rfkill) return -EINVAL; - rfkill_init_sw_state(*rfkill, !(retval & 0x1)); + rfkill_init_sw_state(*rfkill, !result); result = rfkill_register(*rfkill); if (result) { rfkill_destroy(*rfkill); From 0773d7f9f1c0ad5fb86d23ad7e4ef5bfd2f48b5e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:32 +0100 Subject: [PATCH 09/81] eeepc-wmi: add hibernate/resume callbacks Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index de501fb3d1b..583ba78a7f7 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -1183,10 +1183,59 @@ static int eeepc_wmi_remove(struct platform_device *device) return 0; } +/* + * Platform driver - hibernate/resume callbacks + */ +static int eeepc_hotk_thaw(struct device *device) +{ + struct eeepc_wmi *eeepc = dev_get_drvdata(device); + + if (eeepc->wlan_rfkill) { + bool wlan; + + /* + * Work around bios bug - acpi _PTS turns off the wireless led + * during suspend. Normally it restores it on resume, but + * we should kick it ourselves in case hibernation is aborted. + */ + wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL); + } + + return 0; +} + +static int eeepc_hotk_restore(struct device *device) +{ + struct eeepc_wmi *eeepc = dev_get_drvdata(device); + int bl; + + /* Refresh both wlan rfkill state and pci hotplug */ + if (eeepc->wlan_rfkill) + eeepc_rfkill_hotplug(eeepc); + + if (eeepc->bluetooth_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); + rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); +} + if (eeepc->wwan3g_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); + rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); + } + + return 0; +} + +static const struct dev_pm_ops eeepc_pm_ops = { + .thaw = eeepc_hotk_thaw, + .restore = eeepc_hotk_restore, +}; + static struct platform_driver platform_driver = { .driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, + .pm = &eeepc_pm_ops, }, }; From a04ce290bffe6b39edf18bac0fdb302503a53a4e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:33 +0100 Subject: [PATCH 10/81] eeepc-wmi: switch to platform_create_bundle() This allow to remove ~30 lines of code. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 75 ++++++++++---------------------- 1 file changed, 22 insertions(+), 53 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 583ba78a7f7..1fc191bc0a3 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -144,9 +144,6 @@ struct eeepc_wmi { struct eeepc_wmi_debug debug; }; -/* 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) { int err; @@ -932,33 +929,12 @@ static int eeepc_wmi_sysfs_init(struct platform_device *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; + return eeepc_wmi_sysfs_init(eeepc->platform_device); } static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) { eeepc_wmi_sysfs_exit(eeepc->platform_device); - platform_device_unregister(eeepc->platform_device); } /* @@ -1094,7 +1070,7 @@ static void eeepc_dmi_check(struct eeepc_wmi *eeepc) } } -static struct platform_device * __init eeepc_wmi_add(void) +static int __init eeepc_wmi_add(struct platform_device *pdev) { struct eeepc_wmi *eeepc; acpi_status status; @@ -1102,15 +1078,14 @@ static struct platform_device * __init eeepc_wmi_add(void) eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); if (!eeepc) - return ERR_PTR(-ENOMEM); + return -ENOMEM; + + eeepc->platform_device = pdev; + platform_set_drvdata(eeepc->platform_device, eeepc); eeepc->hotplug_wireless = hotplug_wireless; eeepc_dmi_check(eeepc); - /* - * 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; @@ -1147,7 +1122,7 @@ static struct platform_device * __init eeepc_wmi_add(void) if (err) goto fail_debugfs; - return eeepc->platform_device; + return 0; fail_debugfs: wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); @@ -1163,10 +1138,10 @@ fail_input: eeepc_wmi_platform_exit(eeepc); fail_platform: kfree(eeepc); - return ERR_PTR(err); + return err; } -static int eeepc_wmi_remove(struct platform_device *device) +static int __exit eeepc_wmi_remove(struct platform_device *device) { struct eeepc_wmi *eeepc; @@ -1232,6 +1207,7 @@ static const struct dev_pm_ops eeepc_pm_ops = { }; static struct platform_driver platform_driver = { + .remove = __exit_p(eeepc_wmi_remove), .driver = { .name = EEEPC_WMI_FILE, .owner = THIS_MODULE, @@ -1260,10 +1236,8 @@ static int __init eeepc_wmi_check_atkd(void) return -1; } -static int __init eeepc_wmi_init(void) +static int __init eeepc_wmi_probe(struct platform_device *pdev) { - int err; - if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { pr_warning("No known WMI GUID found\n"); @@ -1280,29 +1254,24 @@ static int __init eeepc_wmi_init(void) return -ENODEV; } - platform_device = eeepc_wmi_add(); - if (IS_ERR(platform_device)) { - err = PTR_ERR(platform_device); - goto fail_eeepc_wmi; - } + return eeepc_wmi_add(pdev); +} - err = platform_driver_register(&platform_driver); - if (err) { - pr_warning("Unable to register platform driver\n"); - goto fail_platform_driver; - } +static struct platform_device *platform_device; +static int __init eeepc_wmi_init(void) +{ + platform_device = platform_create_bundle(&platform_driver, + eeepc_wmi_probe, + NULL, 0, NULL, 0); + if (IS_ERR(platform_device)) + return PTR_ERR(platform_device); return 0; - -fail_platform_driver: - eeepc_wmi_remove(platform_device); -fail_eeepc_wmi: - return err; } static void __exit eeepc_wmi_exit(void) { - eeepc_wmi_remove(platform_device); + platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); } From 33e0e6fed04dc6067562c65e3e6d551dc0eb11fc Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:34 +0100 Subject: [PATCH 11/81] eeepc-wmi: reorder defines Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 1fc191bc0a3..aa9e1d1719d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -60,20 +60,20 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); -#define NOTIFY_BRNUP_MIN 0x11 -#define NOTIFY_BRNUP_MAX 0x1f -#define NOTIFY_BRNDOWN_MIN 0x20 -#define NOTIFY_BRNDOWN_MAX 0x2e +#define NOTIFY_BRNUP_MIN 0x11 +#define NOTIFY_BRNUP_MAX 0x1f +#define NOTIFY_BRNDOWN_MIN 0x20 +#define NOTIFY_BRNDOWN_MAX 0x2e -#define EEEPC_WMI_METHODID_DEVS 0x53564544 -#define EEEPC_WMI_METHODID_DSTS 0x53544344 -#define EEEPC_WMI_METHODID_CFVS 0x53564643 +#define EEEPC_WMI_METHODID_DSTS 0x53544344 +#define EEEPC_WMI_METHODID_DEVS 0x53564544 +#define EEEPC_WMI_METHODID_CFVS 0x53564643 -#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 +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_TPDLED 0x00100011 static bool hotplug_wireless; From aafa719dcd0cb0c05bb0690c816b13263c8b36e6 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:35 +0100 Subject: [PATCH 12/81] eeepc-wmi: use the presence bit correctly I checked some more DSDT, and it seems that I wasn't totally right about the meaning of DSTS return value. Bit 0 is clearly the status of the device, and I discovered that bit 16 is set when the device is present. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index aa9e1d1719d..391c32bd703 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -75,6 +75,9 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 +#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 + static bool hotplug_wireless; module_param(hotplug_wireless, bool, 0444); @@ -265,16 +268,10 @@ static int eeepc_wmi_get_devstate_simple(u32 dev_id) if (ACPI_FAILURE(status)) return -EINVAL; - /* 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 (!retval || retval == 0x00060000) + if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; - return retval & 0x1; + return retval & EEEPC_WMI_DSTS_STATUS_BIT; } /* From 9e1565bc390123d3c74b940ba3466faf196970ec Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:36 +0100 Subject: [PATCH 13/81] eeepc-wmi: add camera and card reader support Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-platform-eeepc-wmi | 14 +++ drivers/platform/x86/eeepc-wmi.c | 89 ++++++++++++++++++- 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi index e4b5fef5fad..9fc8d33e280 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi @@ -8,3 +8,17 @@ Description: * 0 -> Super Performance Mode * 1 -> High Performance Mode * 2 -> Power Saving Mode + +What: /sys/devices/platform/eeepc-wmi/camera +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the camera. 1 means on, 0 means off. + +What: /sys/devices/platform/eeepc-wmi/cardr +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the card reader. 1 means on, 0 means off. diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 391c32bd703..83415dd42d0 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -73,6 +73,8 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_CAMERA 0x00060013 +#define EEEPC_WMI_DEVID_CARDREADER 0x00080013 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 @@ -879,6 +881,70 @@ static void eeepc_wmi_notify(u32 value, void *context) kfree(obj); } +/* + * Sys helpers + */ +static int parse_arg(const char *buf, unsigned long count, int *val) +{ + if (!count) + return 0; + if (sscanf(buf, "%i", val) != 1) + return -EINVAL; + return count; +} + +static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) +{ + acpi_status status; + u32 retval; + int rv, value; + + value = eeepc_wmi_get_devstate_simple(devid); + if (value == -ENODEV) /* Check device presence */ + return value; + + rv = parse_arg(buf, count, &value); + status = eeepc_wmi_set_devstate(devid, value, &retval); + + if (ACPI_FAILURE(status)) + return -EIO; + return rv; +} + +static ssize_t show_sys_wmi(int devid, char *buf) +{ + int value = eeepc_wmi_get_devstate_simple(devid); + + if (value < 0) + return value; + + return sprintf(buf, "%d\n", value); +} + +#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ + static ssize_t show_##_name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + return show_sys_wmi(_cm, buf); \ + } \ + static ssize_t store_##_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return store_sys_wmi(_cm, buf, count); \ + } \ + static struct device_attribute dev_attr_##_name = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = _mode }, \ + .show = show_##_name, \ + .store = store_##_name, \ + } + +EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); +EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); + static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -904,11 +970,32 @@ static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, + &dev_attr_camera.attr, + &dev_attr_cardr.attr, NULL }; +static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, + int idx) +{ + bool supported = true; + int devid = -1; + + if (attr == &dev_attr_camera.attr) + devid = EEEPC_WMI_DEVID_CAMERA; + else if (attr == &dev_attr_cardr.attr) + devid = EEEPC_WMI_DEVID_CARDREADER; + + if (devid != -1) + supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; + + return supported ? attr->mode : 0; +} + static struct attribute_group platform_attribute_group = { - .attrs = platform_attributes + .is_visible = eeepc_sysfs_is_visible, + .attrs = platform_attributes }; static void eeepc_wmi_sysfs_exit(struct platform_device *device) From 2e9e159d8e18c37d60a7d5040314f579a40f4c63 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:37 +0100 Subject: [PATCH 14/81] eeepc-wmi: add wimax support Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 83415dd42d0..0db70093943 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -71,6 +71,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_WLAN 0x00010011 #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 +#define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 @@ -138,6 +139,7 @@ struct eeepc_wmi { struct rfkill *wlan_rfkill; struct rfkill *bluetooth_rfkill; + struct rfkill *wimax_rfkill; struct rfkill *wwan3g_rfkill; struct hotplug_slot *hotplug_slot; @@ -691,6 +693,11 @@ static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) rfkill_destroy(eeepc->bluetooth_rfkill); eeepc->bluetooth_rfkill = NULL; } + if (eeepc->wimax_rfkill) { + rfkill_unregister(eeepc->wimax_rfkill); + rfkill_destroy(eeepc->wimax_rfkill); + eeepc->wimax_rfkill = NULL; + } if (eeepc->wwan3g_rfkill) { rfkill_unregister(eeepc->wwan3g_rfkill); rfkill_destroy(eeepc->wwan3g_rfkill); @@ -719,6 +726,13 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, + "eeepc-wimax", RFKILL_TYPE_WIMAX, + EEEPC_WMI_DEVID_WIMAX); + + if (result && result != -ENODEV) + goto exit; + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, "eeepc-wwan3g", RFKILL_TYPE_WWAN, EEEPC_WMI_DEVID_WWAN3G); @@ -1276,7 +1290,11 @@ static int eeepc_hotk_restore(struct device *device) if (eeepc->bluetooth_rfkill) { bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); -} + } + if (eeepc->wimax_rfkill) { + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX); + rfkill_set_sw_state(eeepc->wimax_rfkill, bl); + } if (eeepc->wwan3g_rfkill) { bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); From 54c799a50f58285f5f6a93d87470cba1847943a3 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:38 +0100 Subject: [PATCH 15/81] eeepc-wmi: set the right key code for 0xe9 This key should power off the backlight, not the display, it is also used in acpi/video.c to do the same thing. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0db70093943..75dd692d31e 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -103,7 +103,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, { KE_KEY, 0xe1, { KEY_F14 } }, - { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } }, + { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, { KE_END, 0}, }; From b71872650fe967eb0a38aa0d7dcbe9c60d160032 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:39 +0100 Subject: [PATCH 16/81] eeepc-wmi: support backlight power (bl_power) attribute Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 75 ++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 75dd692d31e..910eb00fb13 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -73,13 +73,16 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 -#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012 +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 +#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 #define EEEPC_WMI_DEVID_TPDLED 0x00100011 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 +#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF +#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 static bool hotplug_wireless; @@ -262,7 +265,7 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, } /* Helper for special devices with magic return codes */ -static int eeepc_wmi_get_devstate_simple(u32 dev_id) +static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask) { u32 retval = 0; acpi_status status; @@ -275,7 +278,12 @@ static int eeepc_wmi_get_devstate_simple(u32 dev_id) if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; - return retval & EEEPC_WMI_DSTS_STATUS_BIT; + return retval & mask; +} + +static int eeepc_wmi_get_devstate_simple(u32 dev_id) +{ + return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT); } /* @@ -770,34 +778,53 @@ exit: /* * Backlight */ +static int read_backlight_power(void) +{ + int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT); + + if (ret < 0) + return ret; + + return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; +} + static int read_brightness(struct backlight_device *bd) { u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval); + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval); if (ACPI_FAILURE(status)) - return -1; + return -EIO; else - return retval & 0xFF; + return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK; } static int update_bl_status(struct backlight_device *bd) { - u32 ctrl_param; acpi_status status; + int power; ctrl_param = bd->props.brightness; - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, ctrl_param, NULL); if (ACPI_FAILURE(status)) - return -1; - else - return 0; + return -EIO; + + power = read_backlight_power(); + if (power != -ENODEV && bd->props.power != power) { + ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); + + if (ACPI_FAILURE(status)) + return -EIO; + } + return 0; } static const struct backlight_ops eeepc_wmi_bl_ops = { @@ -827,9 +854,29 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) { struct backlight_device *bd; struct backlight_properties props; + int max; + int power; + + max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS, + EEEPC_WMI_DSTS_MAX_BRIGTH_MASK); + power = read_backlight_power(); + + if (max < 0 && power < 0) { + /* Try to keep the original error */ + if (max == -ENODEV && power == -ENODEV) + return -ENODEV; + if (max != -ENODEV) + return max; + else + return power; + } + if (max == -ENODEV) + max = 0; + if (power == -ENODEV) + power = FB_BLANK_UNBLANK; memset(&props, 0, sizeof(struct backlight_properties)); - props.max_brightness = 15; + props.max_brightness = max; bd = backlight_device_register(EEEPC_WMI_FILE, &eeepc->platform_device->dev, eeepc, &eeepc_wmi_bl_ops, &props); @@ -841,7 +888,7 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) eeepc->backlight_device = bd; bd->props.brightness = read_brightness(bd); - bd->props.power = FB_BLANK_UNBLANK; + bd->props.power = power; backlight_update_status(bd); return 0; @@ -1202,7 +1249,7 @@ static int __init eeepc_wmi_add(struct platform_device *pdev) if (!acpi_video_backlight_support()) { err = eeepc_wmi_backlight_init(eeepc); - if (err) + if (err && err != -ENODEV) goto fail_backlight; } else pr_info("Backlight controlled by ACPI video driver\n"); From c14d4b8ea799515cd44134b8eddd8f789f0b6286 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:40 +0100 Subject: [PATCH 17/81] eeepc-wmi: respect wireless_hotplug setting Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 910eb00fb13..bc713334567 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -748,6 +748,9 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result && result != -ENODEV) goto exit; + if (!eeepc->hotplug_wireless) + goto exit; + result = eeepc_setup_pci_hotplug(eeepc); /* * If we get -EBUSY then something else is handling the PCI hotplug - From 8571d75d614702e3e0278c92892012dbf7c2e65b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:41 +0100 Subject: [PATCH 18/81] eeepc-wmi: real touchpad led device id is 0x001000012 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index bc713334567..89847671624 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -77,7 +77,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 -#define EEEPC_WMI_DEVID_TPDLED 0x00100011 +#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 @@ -303,7 +303,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, NULL); + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); } static void tpd_led_set(struct led_classdev *led_cdev, @@ -317,9 +317,9 @@ static void tpd_led_set(struct led_classdev *led_cdev, queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); } -static int read_tpd_state(struct eeepc_wmi *eeepc) +static int read_tpd_led_state(struct eeepc_wmi *eeepc) { - return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TPDLED); + return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -328,14 +328,14 @@ static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); - return read_tpd_state(eeepc); + return read_tpd_led_state(eeepc); } static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) { int rv; - if (read_tpd_state(eeepc) < 0) + if (read_tpd_led_state(eeepc) < 0) return 0; eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); From 77ca5b0197138db1260bbbb95ce3fd015dd10437 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:30:48 +0100 Subject: [PATCH 19/81] eeepc-wmi: comments keymap to clarify the meaning of some keys Found while checking PDF manuals... 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 89847671624..a78a90d886d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -99,13 +99,13 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x30, { KEY_VOLUMEUP } }, { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, { KE_KEY, 0x32, { KEY_MUTE } }, - { KE_KEY, 0x5c, { KEY_F15 } }, + { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, - { KE_KEY, 0xe1, { KEY_F14 } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ + { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, { KE_END, 0}, }; From af96f87703f33a4dba4b51c7b3f0d6f874aa4853 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:27:30 +0100 Subject: [PATCH 20/81] asus-laptop: let WLED alone on L1400B Asus took the DSDT from another model (L84F), made some change to make it work, but forgot to remove WLED method (the laptop doesn't have a wireless card). They even didn't change the model name. ref: https://bugzilla.kernel.org/show_bug.cgi?id=25712 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 5a6f7d7575d..2b9d7b8636e 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -1557,6 +1558,20 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) return result; } +static void __devinit asus_dmi_check(void) +{ + const char *model; + + model = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!model) + return; + + /* On L1400B WLED control the sound card, don't mess with it ... */ + if (strncmp(model, "L1400B", 6) == 0) { + wlan_status = -1; + } +} + static bool asus_device_present; static int __devinit asus_acpi_add(struct acpi_device *device) @@ -1575,6 +1590,8 @@ static int __devinit asus_acpi_add(struct acpi_device *device) device->driver_data = asus; asus->device = device; + asus_dmi_check(); + result = asus_acpi_init(asus); if (result) goto fail_platform; From 3b81cf9d558c57406b4ed9b0d2639113d1d428b6 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:27:31 +0100 Subject: [PATCH 21/81] asus-laptop: remove deprecated interfaces (lcd_switch and display_get) I should have done that one year ago, so it's more than time to do it. These two features use non-standard interfaces. There are the only features that really need multiple path to guess what's the right method name on a specific laptop. Removing them allow to remove a lot of code an significantly clean the driver. This will affect the backlight code which won't be able to know if the backlight is on or off. The platform display file will also be write only (like the one in eeepc-laptop). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 163 +---------------------------- 1 file changed, 3 insertions(+), 160 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 2b9d7b8636e..62ef43ef06d 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -158,46 +158,9 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " #define METHOD_BRIGHTNESS_SET "SPLV" #define METHOD_BRIGHTNESS_GET "GPLV" -/* Backlight */ -static acpi_handle lcd_switch_handle; -static char *lcd_switch_paths[] = { - "\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */ - "\\_SB.PCI0.ISA.EC0._Q10", /* A1x */ - "\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */ - "\\_SB.PCI0.PX40.EC0.Q10", /* M1A */ - "\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */ - "\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */ - "\\_SB.PCI0.PX40.Q10", /* S1x */ - "\\Q10"}; /* A2x, L2D, L3D, M2E */ - /* Display */ #define METHOD_SWITCH_DISPLAY "SDSP" -static acpi_handle display_get_handle; -static char *display_get_paths[] = { - /* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */ - "\\_SB.PCI0.P0P1.VGA.GETD", - /* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */ - "\\_SB.PCI0.P0P2.VGA.GETD", - /* A6V A6Q */ - "\\_SB.PCI0.P0P3.VGA.GETD", - /* A6T, A6M */ - "\\_SB.PCI0.P0PA.VGA.GETD", - /* L3C */ - "\\_SB.PCI0.PCI1.VGAC.NMAP", - /* Z96F */ - "\\_SB.PCI0.VGA.GETD", - /* A2D */ - "\\ACTD", - /* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */ - "\\ADVG", - /* P30 */ - "\\DNXT", - /* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */ - "\\INFB", - /* A3F A6F A3N A3L M6N W3N W6A */ - "\\SSTE"}; - #define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */ #define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */ @@ -247,7 +210,6 @@ struct asus_laptop { int wireless_status; bool have_rsts; - int lcd_state; struct rfkill *gps_rfkill; @@ -560,48 +522,6 @@ error: /* * Backlight device */ -static int asus_lcd_status(struct asus_laptop *asus) -{ - return asus->lcd_state; -} - -static int asus_lcd_set(struct asus_laptop *asus, int value) -{ - int lcd = 0; - acpi_status status = 0; - - lcd = !!value; - - if (lcd == asus_lcd_status(asus)) - return 0; - - if (!lcd_switch_handle) - return -ENODEV; - - status = acpi_evaluate_object(lcd_switch_handle, - NULL, NULL, NULL); - - if (ACPI_FAILURE(status)) { - pr_warning("Error switching LCD\n"); - return -ENODEV; - } - - asus->lcd_state = lcd; - return 0; -} - -static void lcd_blank(struct asus_laptop *asus, int blank) -{ - struct backlight_device *bd = asus->backlight_device; - - asus->lcd_state = (blank == FB_BLANK_UNBLANK); - - if (bd) { - bd->props.power = blank; - backlight_update_status(bd); - } -} - static int asus_read_brightness(struct backlight_device *bd) { struct asus_laptop *asus = bl_get_data(bd); @@ -629,16 +549,9 @@ static int asus_set_brightness(struct backlight_device *bd, int value) static int update_bl_status(struct backlight_device *bd) { - struct asus_laptop *asus = bl_get_data(bd); - int rv; int value = bd->props.brightness; - rv = asus_set_brightness(bd, value); - if (rv) - return rv; - - value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; - return asus_lcd_set(asus, value); + return asus_set_brightness(bd, value); } static const struct backlight_ops asusbl_ops = { @@ -662,8 +575,7 @@ static int asus_backlight_init(struct asus_laptop *asus) struct backlight_properties props; if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) || - acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) || - !lcd_switch_handle) + acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL)) return 0; memset(&props, 0, sizeof(struct backlight_properties)); @@ -972,41 +884,6 @@ static void asus_set_display(struct asus_laptop *asus, int value) return; } -static int read_display(struct asus_laptop *asus) -{ - unsigned long long value = 0; - acpi_status rv = AE_OK; - - /* - * In most of the case, we know how to set the display, but sometime - * we can't read it - */ - if (display_get_handle) { - rv = acpi_evaluate_integer(display_get_handle, NULL, - NULL, &value); - if (ACPI_FAILURE(rv)) - pr_warning("Error reading display status\n"); - } - - value &= 0x0F; /* needed for some models, shouldn't hurt others */ - - return value; -} - -/* - * Now, *this* one could be more user-friendly, but so far, no-one has - * complained. The significance of bits is the same as in store_disp() - */ -static ssize_t show_disp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct asus_laptop *asus = dev_get_drvdata(dev); - - if (!display_get_handle) - return -ENODEV; - return sprintf(buf, "%d\n", read_display(asus)); -} - /* * Experimental support for display switching. As of now: 1 should activate * the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI. @@ -1248,15 +1125,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) struct asus_laptop *asus = acpi_driver_data(device); u16 count; - /* - * We need to tell the backlight device when the backlight power is - * switched - */ - if (event == ATKD_LCD_ON) - lcd_blank(asus, FB_BLANK_UNBLANK); - else if (event == ATKD_LCD_OFF) - lcd_blank(asus, FB_BLANK_POWERDOWN); - /* TODO Find a better way to handle events count. */ count = asus->event_count[event % 128]++; acpi_bus_generate_proc_event(asus->device, event, count); @@ -1283,7 +1151,7 @@ static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR, show_bluetooth, store_bluetooth); static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); -static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp); +static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp); static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); @@ -1394,26 +1262,6 @@ static struct platform_driver platform_driver = { } }; -static int asus_handle_init(char *name, acpi_handle * handle, - char **paths, int num_paths) -{ - int i; - acpi_status status; - - for (i = 0; i < num_paths; i++) { - status = acpi_get_handle(NULL, paths[i], handle); - if (ACPI_SUCCESS(status)) - return 0; - } - - *handle = NULL; - return -ENODEV; -} - -#define ASUS_HANDLE_INIT(object) \ - asus_handle_init(#object, &object##_handle, object##_paths, \ - ARRAY_SIZE(object##_paths)) - /* * This function is used to initialize the context with right values. In this * method, we can make all the detection we want, and modify the asus_laptop @@ -1499,10 +1347,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus) if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL)) asus->have_rsts = true; - /* Scheduled for removal */ - ASUS_HANDLE_INIT(lcd_switch); - ASUS_HANDLE_INIT(display_get); - kfree(model); return AE_OK; @@ -1554,7 +1398,6 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) asus_als_level(asus, asus->light_level); } - asus->lcd_state = 1; /* LCD should be on when the module load */ return result; } From 4615bb661352acb7032796185c8c5573e47dfa1d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:42 +0100 Subject: [PATCH 22/81] eeepc-wmi: add touchpad sysfs file Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- Documentation/ABI/testing/sysfs-platform-eeepc-wmi | 7 +++++++ drivers/platform/x86/eeepc-wmi.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi index 9fc8d33e280..26acb6842c4 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi +++ b/Documentation/ABI/testing/sysfs-platform-eeepc-wmi @@ -22,3 +22,10 @@ KernelVersion: 2.6.39 Contact: "Corentin Chary" Description: Control the card reader. 1 means on, 0 means off. + +What: /sys/devices/platform/eeepc-wmi/touchpad +Date: Jan 2010 +KernelVersion: 2.6.39 +Contact: "Corentin Chary" +Description: + Control the card touchpad. 1 means on, 0 means off. diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index a78a90d886d..16c7f2d6251 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -77,6 +77,7 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 #define EEEPC_WMI_DEVID_CAMERA 0x00060013 #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 +#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 #define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 @@ -1006,6 +1007,7 @@ static ssize_t show_sys_wmi(int devid, char *buf) .store = store_##_name, \ } +EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD); EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); @@ -1036,6 +1038,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_cpufv.attr, &dev_attr_camera.attr, &dev_attr_cardr.attr, + &dev_attr_touchpad.attr, NULL }; @@ -1050,6 +1053,8 @@ static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, devid = EEEPC_WMI_DEVID_CAMERA; else if (attr == &dev_attr_cardr.attr) devid = EEEPC_WMI_DEVID_CARDREADER; + else if (attr == &dev_attr_touchpad.attr) + devid = EEEPC_WMI_DEVID_TOUCHPAD; if (devid != -1) supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; From 43815941efb1374c2f71eb1fb0a2814b049030eb Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:28:43 +0100 Subject: [PATCH 23/81] eeepc-wmi: reorder device ids Each device seems to be in a "group" (devid >> 16 & 0xFF). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 16c7f2d6251..eea8c942653 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -65,21 +65,32 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define NOTIFY_BRNDOWN_MIN 0x20 #define NOTIFY_BRNDOWN_MAX 0x2e +/* WMI Methods */ #define EEEPC_WMI_METHODID_DSTS 0x53544344 #define EEEPC_WMI_METHODID_DEVS 0x53564544 #define EEEPC_WMI_METHODID_CFVS 0x53564643 +/* Wireless */ #define EEEPC_WMI_DEVID_WLAN 0x00010011 #define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 #define EEEPC_WMI_DEVID_WIMAX 0x00010017 #define EEEPC_WMI_DEVID_WWAN3G 0x00010019 + +/* Backlight and Brightness */ #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 #define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 + +/* Misc */ #define EEEPC_WMI_DEVID_CAMERA 0x00060013 + +/* Storage */ #define EEEPC_WMI_DEVID_CARDREADER 0x00080013 + +/* Input */ #define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 #define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 +/* DSTS masks */ #define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 #define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 #define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF From e2d3d44b9a6efe4f3968252d4b680397a9640268 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sun, 6 Feb 2011 13:30:47 +0100 Subject: [PATCH 24/81] eeepc-wmi: add camera keys These keys are supposed to be handled by any software using the camera (like webKam or cheese...). They can also be used to actually move the camera when possible. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 7 +++++++ include/linux/input.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index eea8c942653..d3997757e79 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -114,11 +114,18 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, + { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, + { KE_KEY, 0xec, { KEY_CAMERA_UP } }, + { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, + { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, + { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, { KE_END, 0}, }; diff --git a/include/linux/input.h b/include/linux/input.h index 056ae8a5bd9..f3a7794a18c 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -664,6 +664,13 @@ struct input_keymap_entry { #define KEY_TOUCHPAD_ON 0x213 #define KEY_TOUCHPAD_OFF 0x214 +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 From 8eec8a1167b5912c19fec2cdad5b968dd0f8690d Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Mon, 7 Feb 2011 14:45:55 -0500 Subject: [PATCH 25/81] intel_mid_powerbtn: add power button driver for Medfield platform (#3) The power button is connected to MSIC on Medfield, we will get two interrupts from IOAPIC when pressing or releasing the power button. Signed-off-by: Hong Liu [Minor fixes as noted by Dmitry] Signed-off-by: Alan Cox Acked-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 8 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/intel_mid_powerbtn.c | 148 ++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 drivers/platform/x86/intel_mid_powerbtn.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2f6a37a1d2e..2822d8a8cdd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -616,6 +616,14 @@ config GPIO_INTEL_PMIC Say Y here to support GPIO via the SCU IPC interface on Intel MID platforms. +config INTEL_MID_POWER_BUTTON + tristate "power button driver for Intel MID platforms" + depends on INTEL_SCU_IPC + help + This driver handles the power button on the Intel MID platforms. + + If unsure, say N. + config RAR_REGISTER bool "Restricted Access Region Register Driver" depends on PCI && X86_MRST diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index daa286c9d53..3e14b18403e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -36,3 +36,4 @@ obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o +obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c new file mode 100644 index 00000000000..213e79ba68d --- /dev/null +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -0,0 +1,148 @@ +/* + * Power button driver for Medfield. + * + * Copyright (C) 2010 Intel Corp + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "msic_power_btn" + +#define MSIC_IRQ_STAT 0x02 + #define MSIC_IRQ_PB (1 << 0) +#define MSIC_PB_CONFIG 0x3e +#define MSIC_PB_STATUS 0x3f + #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ + +struct mfld_pb_priv { + struct input_dev *input; + unsigned int irq; +}; + +static irqreturn_t mfld_pb_isr(int irq, void *dev_id) +{ + struct mfld_pb_priv *priv = dev_id; + int ret; + u8 pbstat; + + ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat); + if (ret < 0) + return IRQ_HANDLED; + + input_event(priv->input, EV_KEY, KEY_POWER, !(pbstat & MSIC_PB_LEVEL)); + input_sync(priv->input); + + return IRQ_HANDLED; +} + +static int __devinit mfld_pb_probe(struct platform_device *pdev) +{ + struct mfld_pb_priv *priv; + struct input_dev *input; + int irq; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + priv = kzalloc(sizeof(struct mfld_pb_priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->input = input; + priv->irq = irq; + + input->name = pdev->name; + input->phys = "power-button/input0"; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = request_threaded_irq(priv->irq, NULL, mfld_pb_isr, + 0, DRIVER_NAME, priv); + if (error) { + dev_err(&pdev->dev, + "unable to request irq %d for mfld power button\n", + irq); + goto err_free_mem; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, + "unable to register input dev, error %d\n", error); + goto err_free_irq; + } + + platform_set_drvdata(pdev, priv); + return 0; + +err_free_irq: + free_irq(priv->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit mfld_pb_remove(struct platform_device *pdev) +{ + struct mfld_pb_priv *priv = platform_get_drvdata(pdev); + + free_irq(priv->irq, priv); + input_unregister_device(priv->input); + kfree(priv); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver mfld_pb_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mfld_pb_probe, + .remove = __devexit_p(mfld_pb_remove), +}; + +static int __init mfld_pb_init(void) +{ + return platform_driver_register(&mfld_pb_driver); +} +module_init(mfld_pb_init); + +static void __exit mfld_pb_exit(void) +{ + platform_driver_unregister(&mfld_pb_driver); +} +module_exit(mfld_pb_exit); + +MODULE_AUTHOR("Hong Liu "); +MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); From 820787fceb3e62c29a36423eb30e2f9f198547f7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 17 Feb 2011 18:44:09 +0000 Subject: [PATCH 26/81] Enable Dell All-In-One volume up/down keys Enable volume up and down hotkeys on WMI events GUID 284A0E6B-380E-472A-921F-E52786257FB4 and GUID 02314822-307C-4F66-bf0E-48AEAEB26CC8. Also works around a firmware bug where the _WED method should return an integer containing the key code and in fact the method returns the key code in element zero of a buffer. BugLink: http://bugs.launchpad.net/bugs/701530 BugLink: http://bugs.launchpad.net/bugs/676997 Signed-off-by: Colin Ian King Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 13 +++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/dell-wmi-aio.c | 171 ++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 drivers/platform/x86/dell-wmi-aio.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2822d8a8cdd..f0ef757d9fd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -101,6 +101,19 @@ config DELL_WMI To compile this driver as a module, choose M here: the module will be called dell-wmi. +config DELL_WMI_AIO + tristate "WMI Hotkeys for Dell All-In-One series" + depends on ACPI_WMI + depends on INPUT + select INPUT_SPARSEKMAP + ---help--- + Say Y here if you want to support WMI-based hotkeys on Dell + All-In-One machines. + + To compile this driver as a module, choose M here: the module will + be called dell-wmi. + + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 3e14b18403e..46f3fd2c885 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o +obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_ACCEL) += hp_accel.o diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c new file mode 100644 index 00000000000..0ed84573ae1 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-aio.c @@ -0,0 +1,171 @@ +/* + * WMI hotkeys support for Dell All-In-One series + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series"); +MODULE_LICENSE("GPL"); + +#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4" +#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8" + +static const char *dell_wmi_aio_guids[] = { + EVENT_GUID1, + EVENT_GUID2, + NULL +}; + +MODULE_ALIAS("wmi:"EVENT_GUID1); +MODULE_ALIAS("wmi:"EVENT_GUID2); + +static const struct key_entry dell_wmi_aio_keymap[] = { + { KE_KEY, 0xc0, { KEY_VOLUMEUP } }, + { KE_KEY, 0xc1, { KEY_VOLUMEDOWN } }, + { KE_END, 0 } +}; + +static struct input_dev *dell_wmi_aio_input_dev; + +static void dell_wmi_aio_notify(u32 value, void *context) +{ + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (status != AE_OK) { + pr_info("bad event status 0x%x\n", status); + return; + } + + obj = (union acpi_object *)response.pointer; + if (obj) { + unsigned int scancode; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + /* Most All-In-One correctly return integer scancode */ + scancode = obj->integer.value; + sparse_keymap_report_event(dell_wmi_aio_input_dev, + scancode, 1, true); + break; + case ACPI_TYPE_BUFFER: + /* Broken machines return the scancode in a buffer */ + if (obj->buffer.pointer && obj->buffer.length > 0) { + scancode = obj->buffer.pointer[0]; + sparse_keymap_report_event( + dell_wmi_aio_input_dev, + scancode, 1, true); + } + break; + } + } + kfree(obj); +} + +static int __init dell_wmi_aio_input_setup(void) +{ + int err; + + dell_wmi_aio_input_dev = input_allocate_device(); + + if (!dell_wmi_aio_input_dev) + return -ENOMEM; + + dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys"; + dell_wmi_aio_input_dev->phys = "wmi/input0"; + dell_wmi_aio_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(dell_wmi_aio_input_dev, + dell_wmi_aio_keymap, NULL); + if (err) { + pr_err("Unable to setup input device keymap\n"); + goto err_free_dev; + } + err = input_register_device(dell_wmi_aio_input_dev); + if (err) { + pr_info("Unable to register input device\n"); + goto err_free_keymap; + } + return 0; + +err_free_keymap: + sparse_keymap_free(dell_wmi_aio_input_dev); +err_free_dev: + input_free_device(dell_wmi_aio_input_dev); + return err; +} + +static const char *dell_wmi_aio_find(void) +{ + int i; + + for (i = 0; dell_wmi_aio_guids[i] != NULL; i++) + if (wmi_has_guid(dell_wmi_aio_guids[i])) + return dell_wmi_aio_guids[i]; + + return NULL; +} + +static int __init dell_wmi_aio_init(void) +{ + int err; + const char *guid; + + guid = dell_wmi_aio_find(); + if (!guid) { + pr_warning("No known WMI GUID found\n"); + return -ENXIO; + } + + err = dell_wmi_aio_input_setup(); + if (err) + return err; + + err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); + if (err) { + pr_err("Unable to register notify handler - %d\n", err); + sparse_keymap_free(dell_wmi_aio_input_dev); + input_unregister_device(dell_wmi_aio_input_dev); + return err; + } + + return 0; +} + +static void __exit dell_wmi_aio_exit(void) +{ + const char *guid; + + guid = dell_wmi_aio_find(); + wmi_remove_notify_handler(guid); + sparse_keymap_free(dell_wmi_aio_input_dev); + input_unregister_device(dell_wmi_aio_input_dev); +} + +module_init(dell_wmi_aio_init); +module_exit(dell_wmi_aio_exit); From 80887757e304d1f973330d9398024cffcd82d00d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 16 Feb 2011 14:31:02 -0800 Subject: [PATCH 27/81] platform/x86: intel_mid_powerbutton needs INPUT intel_mid_powerbtn.c uses input interfaces, so it should depend on INPUT to fix build errors when CONFIG_INPUT is not enabled: intel_mid_powerbtn.c:(.text+0x56ca8f): undefined reference to `input_event' intel_mid_powerbtn.c:(.devinit.text+0x2e7b4): undefined reference to `input_allocate_device' intel_mid_powerbtn.c:(.devinit.text+0x2e7ff): undefined reference to `input_set_capability' intel_mid_powerbtn.c:(.devinit.text+0x2e84a): undefined reference to `input_register_device' intel_mid_powerbtn.c:(.devinit.text+0x2e88b): undefined reference to `input_free_device' intel_mid_powerbtn.c:(.devexit.text+0x42f0): undefined reference to `input_unregister_device' Signed-off-by: Randy Dunlap Cc: Hong Liu Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f0ef757d9fd..53f818d23ed 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -631,7 +631,7 @@ config GPIO_INTEL_PMIC config INTEL_MID_POWER_BUTTON tristate "power button driver for Intel MID platforms" - depends on INTEL_SCU_IPC + depends on INTEL_SCU_IPC && INPUT help This driver handles the power button on the Intel MID platforms. From 56e6e716b5211f4cda8db63b9a16d083ee193480 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:25 +0900 Subject: [PATCH 28/81] sony-laptop: add some debug printk useful for bug reports Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 13d8d63bcca..5cd39af376d 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -733,22 +733,30 @@ static int sony_find_snc_handle(int handle) for (i = 0x20; i < 0x30; i++) { acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); - if (result == handle) + if (result == handle) { + dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", + handle, i - 0x20); return i-0x20; + } } + dprintk("handle 0x%.4x not found\n", handle); return -1; } static int sony_call_snc_handle(int handle, int argument, int *result) { + int ret = 0; int offset = sony_find_snc_handle(handle); if (offset < 0) return -1; - return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, - result); + ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument, + result); + dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument, + *result); + return ret; } /* From 54286fd0da36a51f4cefe669b70a284a799f3e45 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:26 +0900 Subject: [PATCH 29/81] sony-laptop: documentation updates Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- Documentation/laptops/sony-laptop.txt | 37 ++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/Documentation/laptops/sony-laptop.txt b/Documentation/laptops/sony-laptop.txt index 23ce7d350d1..2bd4e82e5d9 100644 --- a/Documentation/laptops/sony-laptop.txt +++ b/Documentation/laptops/sony-laptop.txt @@ -14,7 +14,8 @@ Some models report hotkeys through the SNC or SPIC devices, such events are reported both through the ACPI subsystem as acpi events and through the INPUT subsystem. See the logs of acpid or /proc/acpi/event and /proc/bus/input/devices to find out what those events are and which input -devices are created by the driver. +devices are created by the driver. Additionally, loading the driver with the +debug option will report all events in the kernel log. Backlight control: ------------------ @@ -64,6 +65,16 @@ powers off the sound card, # echo "1" > /sys/devices/platform/sony-laptop/audiopower powers on the sound card. + +RFkill control: +--------------- +More recent Vaio models expose a consistent set of ACPI methods to +control radio frequency emitting devices. If you are a lucky owner of +such a laptop you will find the necessary rfkill devices under +/sys/class/rfkill. Check those starting with sony-* in + # grep . /sys/class/rfkill/*/{state,name} + + Development: ------------ @@ -75,8 +86,21 @@ pass the option 'debug=1'. REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS. In your kernel logs you will find the list of all ACPI methods -the SNC device has on your laptop. You can see the GCDP/GCDP methods -used to pwer on/off the CD drive, but there are others. +the SNC device has on your laptop. + +* For new models you will see a long list of meaningless method names, +reading the DSDT table source should reveal that: +(1) the SNC device uses an internal capability lookup table +(2) SN00 is used to find values in the lookup table +(3) SN06 and SN07 are used to call into the real methods based on + offsets you can obtain iterating the table using SN00 +(4) SN02 used to enable events. +Some values in the capability lookup table are more or less known, see +the code for all sony_call_snc_handle calls, others are more obscure. + +* For old models you can see the GCDP/GCDP methods used to pwer on/off +the CD drive, but there are others and they are usually different from +model to model. I HAVE NO IDEA WHAT THOSE METHODS DO. @@ -108,9 +132,8 @@ Bugs/Limitations: laptop, including permanent damage. * The sony-laptop and sonypi drivers do not interact at all. In the - future, sonypi could use sony-laptop to do (part of) its business. + future, sonypi will be removed and replaced by sony-laptop. * spicctrl, which is the userspace tool used to communicate with the - sonypi driver (through /dev/sonypi) does not try to use the - sony-laptop driver. In the future, spicctrl could try sonypi first, - and if it isn't present, try sony-laptop instead. + sonypi driver (through /dev/sonypi) is deprecated as well since all + its features are now available under the sysfs tree via sony-laptop. From 4eeb50220a4efd8c33598a228d03aff203a7ad07 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:27 +0900 Subject: [PATCH 30/81] sony-laptop: ignore hard switch rfkill events (SPIC) There is not much use for these events in userspace and handling the events themselves seems to get in the way of the actual activation of the rf devices. The SNC device doesn't expose them already. https://bugzilla.kernel.org/show_bug.cgi?id=15303 Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 8 +++++--- include/linux/sonypi.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5cd39af376d..5fa93cb6f33 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1592,8 +1592,8 @@ static struct sonypi_event sonypi_blueev[] = { /* The set of possible wireless events */ static struct sonypi_event sonypi_wlessev[] = { - { 0x59, SONYPI_EVENT_WIRELESS_ON }, - { 0x5a, SONYPI_EVENT_WIRELESS_OFF }, + { 0x59, SONYPI_EVENT_IGNORE }, + { 0x5a, SONYPI_EVENT_IGNORE }, { 0, 0 } }; @@ -2733,6 +2733,9 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) if (ev == dev->event_types[i].events[j].data) { device_event = dev->event_types[i].events[j].event; + /* some events may require ignoring */ + if (!device_event) + return IRQ_HANDLED; goto found; } } @@ -2752,7 +2755,6 @@ found: sony_laptop_report_input_event(device_event); acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event); sonypi_compat_report_event(device_event); - return IRQ_HANDLED; } diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h index 0e6dc389194..c0f87da78f8 100644 --- a/include/linux/sonypi.h +++ b/include/linux/sonypi.h @@ -40,6 +40,7 @@ /* events the user application reading /dev/sonypi can use */ +#define SONYPI_EVENT_IGNORE 0 #define SONYPI_EVENT_JOGDIAL_DOWN 1 #define SONYPI_EVENT_JOGDIAL_UP 2 #define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED 3 From d669793802be5ac105d8ecbab00404ae886d3ebe Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:28 +0900 Subject: [PATCH 31/81] sony-laptop: use pr_ for messages Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 100 +++++++++++++---------------- 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 5fa93cb6f33..89d66c70d82 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -71,8 +71,9 @@ #endif #define DRV_PFX "sony-laptop: " -#define dprintk(msg...) do { \ - if (debug) printk(KERN_WARNING DRV_PFX msg); \ +#define dprintk(msg...) do { \ + if (debug) \ + pr_warn(DRV_PFX msg); \ } while (0) #define SONY_LAPTOP_DRIVER_VERSION "0.6" @@ -402,7 +403,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) error = kfifo_alloc(&sony_laptop_input.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { - printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + pr_err(DRV_PFX "kfifo_alloc failed\n"); goto err_dec_users; } @@ -686,7 +687,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) return 0; } - printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n"); + pr_warn(DRV_PFX "acpi_callreadfunc failed\n"); return -1; } @@ -712,7 +713,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, if (status == AE_OK) { if (result != NULL) { if (out_obj.type != ACPI_TYPE_INTEGER) { - printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad " + pr_warn(DRV_PFX "acpi_evaluate_object bad " "return type\n"); return -1; } @@ -721,7 +722,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, return 0; } - printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n"); + pr_warn(DRV_PFX "acpi_evaluate_object failed\n"); return -1; } @@ -980,7 +981,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) } if (!key_event->data) - printk(KERN_INFO DRV_PFX + pr_info(DRV_PFX "Unknown event: 0x%x 0x%x\n", key_handle, ev); @@ -1004,7 +1005,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, struct acpi_device_info *info; if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { - printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", + pr_warn(DRV_PFX "method: name: %4.4s, args %X\n", (char *)&info->name, info->param_count); kfree(info); @@ -1045,7 +1046,7 @@ static int sony_nc_resume(struct acpi_device *device) ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, item->value, NULL); if (ret < 0) { - printk("%s: %d\n", __func__, ret); + pr_err(DRV_PFX "%s: %d\n", __func__, ret); break; } } @@ -1065,7 +1066,7 @@ static int sony_nc_resume(struct acpi_device *device) /* set the last requested brightness level */ if (sony_backlight_device && sony_backlight_update_status(sony_backlight_device) < 0) - printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n"); + pr_warn(DRV_PFX "unable to restore brightness level\n"); /* re-read rfkill state */ sony_nc_rfkill_update(); @@ -1213,13 +1214,9 @@ static void sony_nc_rfkill_setup(struct acpi_device *device) } device_enum = (union acpi_object *) buffer.pointer; - if (!device_enum) { - pr_err("Invalid SN06 return object\n"); - goto out_no_enum; - } - if (device_enum->type != ACPI_TYPE_BUFFER) { - pr_err("Invalid SN06 return object type 0x%.2x\n", - device_enum->type); + if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) { + pr_err(DRV_PFX "Invalid SN06 return object 0x%.2x\n", + device_enum->type); goto out_no_enum; } @@ -1260,8 +1257,8 @@ static int sony_nc_add(struct acpi_device *device) acpi_handle handle; struct sony_nc_value *item; - printk(KERN_INFO DRV_PFX "%s v%s.\n", - SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + pr_info(DRV_PFX "%s v%s.\n", SONY_NC_DRIVER_NAME, + SONY_LAPTOP_DRIVER_VERSION); sony_nc_acpi_device = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -1278,10 +1275,11 @@ static int sony_nc_add(struct acpi_device *device) } if (debug) { - status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, - 1, sony_walk_callback, NULL, NULL, NULL); + status = acpi_walk_namespace(ACPI_TYPE_METHOD, + sony_nc_acpi_handle, 1, sony_walk_callback, + NULL, NULL, NULL); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); + pr_warn(DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; goto outwalk; } @@ -1303,13 +1301,12 @@ static int sony_nc_add(struct acpi_device *device) /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to create input devices.\n"); + pr_err(DRV_PFX "Unable to create input devices.\n"); goto outwalk; } if (acpi_video_backlight_support()) { - printk(KERN_INFO DRV_PFX "brightness ignored, must be " + pr_info(DRV_PFX "brightness ignored, must be " "controlled by ACPI video driver\n"); } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { @@ -1323,7 +1320,7 @@ static int sony_nc_add(struct acpi_device *device) &props); if (IS_ERR(sony_backlight_device)) { - printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); + pr_warning(DRV_PFX "unable to register backlight device\n"); sony_backlight_device = NULL; } else { sony_backlight_device->props.brightness = @@ -1850,7 +1847,7 @@ out: if (pcidev) pci_dev_put(pcidev); - printk(KERN_INFO DRV_PFX "detected Type%d model\n", + pr_info(DRV_PFX "detected Type%d model\n", dev->model == SONYPI_DEVICE_TYPE1 ? 1 : dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); } @@ -1898,7 +1895,7 @@ static int __sony_pic_camera_ready(void) static int __sony_pic_camera_off(void) { if (!camera) { - printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + pr_warn(DRV_PFX "camera control not enabled\n"); return -ENODEV; } @@ -1918,7 +1915,7 @@ static int __sony_pic_camera_on(void) int i, j, x; if (!camera) { - printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + pr_warn(DRV_PFX "camera control not enabled\n"); return -ENODEV; } @@ -1941,7 +1938,7 @@ static int __sony_pic_camera_on(void) } if (j == 0) { - printk(KERN_WARNING DRV_PFX "failed to power on camera\n"); + pr_warn(DRV_PFX "failed to power on camera\n"); return -ENODEV; } @@ -1997,7 +1994,7 @@ int sony_pic_camera_command(int command, u8 value) ITERATIONS_SHORT); break; default: - printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n", + pr_err(DRV_PFX "sony_pic_camera_command invalid: %d\n", command); break; } @@ -2404,7 +2401,7 @@ static int sonypi_compat_init(void) error = kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL); if (error) { - printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + pr_err(DRV_PFX "kfifo_alloc failed\n"); return error; } @@ -2414,11 +2411,11 @@ static int sonypi_compat_init(void) sonypi_misc_device.minor = minor; error = misc_register(&sonypi_misc_device); if (error) { - printk(KERN_ERR DRV_PFX "misc_register failed\n"); + pr_err(DRV_PFX "misc_register failed\n"); goto err_free_kfifo; } if (minor == -1) - printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", + pr_info(DRV_PFX "device allocated minor is %d\n", sonypi_misc_device.minor); return 0; @@ -2478,8 +2475,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) } for (i = 0; i < p->interrupt_count; i++) { if (!p->interrupts[i]) { - printk(KERN_WARNING DRV_PFX - "Invalid IRQ %d\n", + pr_warn(DRV_PFX "Invalid IRQ %d\n", p->interrupts[i]); continue; } @@ -2518,7 +2514,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) ioport->io2.address_length); } else { - printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); + pr_err(DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n"); return AE_ERROR; } return AE_OK; @@ -2546,7 +2542,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) dprintk("Evaluating _STA\n"); result = acpi_bus_get_status(device); if (result) { - printk(KERN_WARNING DRV_PFX "Unable to read status\n"); + pr_warn(DRV_PFX "Unable to read status\n"); goto end; } @@ -2562,8 +2558,7 @@ static int sony_pic_possible_resources(struct acpi_device *device) status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, sony_pic_read_possible_resource, &spic_dev); if (ACPI_FAILURE(status)) { - printk(KERN_WARNING DRV_PFX - "Failure evaluating %s\n", + pr_warn(DRV_PFX "Failure evaluating %s\n", METHOD_NAME__PRS); result = -ENODEV; } @@ -2677,7 +2672,7 @@ static int sony_pic_enable(struct acpi_device *device, /* check for total failure */ if (ACPI_FAILURE(status)) { - printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n"); + pr_err(DRV_PFX "Error evaluating _SRS\n"); result = -ENODEV; goto end; } @@ -2769,7 +2764,7 @@ static int sony_pic_remove(struct acpi_device *device, int type) struct sony_pic_irq *irq, *tmp_irq; if (sony_pic_disable(device)) { - printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); + pr_err(DRV_PFX "Couldn't disable device.\n"); return -ENXIO; } @@ -2809,8 +2804,8 @@ static int sony_pic_add(struct acpi_device *device) struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - printk(KERN_INFO DRV_PFX "%s v%s.\n", - SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + pr_info(DRV_PFX "%s v%s.\n", SONY_PIC_DRIVER_NAME, + SONY_LAPTOP_DRIVER_VERSION); spic_dev.acpi_dev = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -2820,16 +2815,14 @@ static int sony_pic_add(struct acpi_device *device) /* read _PRS resources */ result = sony_pic_possible_resources(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to read possible resources.\n"); + pr_err(DRV_PFX "Unable to read possible resources.\n"); goto err_free_resources; } /* setup input devices and helper fifo */ result = sony_laptop_setup_input(device); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to create input devices.\n"); + pr_err(DRV_PFX "Unable to create input devices.\n"); goto err_free_resources; } @@ -2870,7 +2863,7 @@ static int sony_pic_add(struct acpi_device *device) } } if (!spic_dev.cur_ioport) { - printk(KERN_ERR DRV_PFX "Failed to request_region.\n"); + pr_err(DRV_PFX "Failed to request_region.\n"); result = -ENODEV; goto err_remove_compat; } @@ -2890,7 +2883,7 @@ static int sony_pic_add(struct acpi_device *device) } } if (!spic_dev.cur_irq) { - printk(KERN_ERR DRV_PFX "Failed to request_irq.\n"); + pr_err(DRV_PFX "Failed to request_irq.\n"); result = -ENODEV; goto err_release_region; } @@ -2898,7 +2891,7 @@ static int sony_pic_add(struct acpi_device *device) /* set resource status _SRS */ result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); if (result) { - printk(KERN_ERR DRV_PFX "Couldn't enable device.\n"); + pr_err(DRV_PFX "Couldn't enable device.\n"); goto err_free_irq; } @@ -3007,8 +3000,7 @@ static int __init sony_laptop_init(void) if (!no_spic && dmi_check_system(sonypi_dmi_table)) { result = acpi_bus_register_driver(&sony_pic_driver); if (result) { - printk(KERN_ERR DRV_PFX - "Unable to register SPIC driver."); + pr_err(DRV_PFX "Unable to register SPIC driver."); goto out; } spic_drv_registered = 1; @@ -3016,7 +3008,7 @@ static int __init sony_laptop_init(void) result = acpi_bus_register_driver(&sony_nc_driver); if (result) { - printk(KERN_ERR DRV_PFX "Unable to register SNC driver."); + pr_err(DRV_PFX "Unable to register SNC driver."); goto out_unregister_pic; } From 3672329c3642033286984313d05f4a5b57bd2da7 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:29 +0900 Subject: [PATCH 32/81] sony-laptop: remove unused Type4 define Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 89d66c70d82..6db1661a562 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1443,7 +1443,6 @@ static struct acpi_driver sony_nc_driver = { #define SONYPI_DEVICE_TYPE1 0x00000001 #define SONYPI_DEVICE_TYPE2 0x00000002 #define SONYPI_DEVICE_TYPE3 0x00000004 -#define SONYPI_DEVICE_TYPE4 0x00000008 #define SONYPI_TYPE1_OFFSET 0x04 #define SONYPI_TYPE2_OFFSET 0x12 From 2a4f0c81adcd1f812a63bc9106be2fd26f437730 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:30 +0900 Subject: [PATCH 33/81] sony-laptop: cache handles and report them via sysfs Avoid calling into acpi each time we need to lookup a method handle and report the available handles to ease collection of information when debugging issues. Also move initialization of the platform driver earlier to allow adding files from other setup functions. Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 97 +++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 6db1661a562..516dd22bbb2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -727,20 +727,79 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value, return -1; } -static int sony_find_snc_handle(int handle) +struct sony_nc_handles { + u16 cap[0x10]; + struct device_attribute devattr; +}; + +struct sony_nc_handles *handles; + +static ssize_t sony_nc_handles_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ", + handles->cap[i]); + } + len += snprintf(buffer + len, PAGE_SIZE - len, "\n"); + + return len; +} + +static int sony_nc_handles_setup(struct platform_device *pd) { int i; int result; - for (i = 0x20; i < 0x30; i++) { - acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result); - if (result == handle) { - dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", - handle, i - 0x20); - return i-0x20; + handles = kzalloc(sizeof(*handles), GFP_KERNEL); + + sysfs_attr_init(&handles->devattr.attr); + handles->devattr.attr.name = "handles"; + handles->devattr.attr.mode = S_IRUGO; + handles->devattr.show = sony_nc_handles_show; + + for (i = 0; i < ARRAY_SIZE(handles->cap); i++) { + if (!acpi_callsetfunc(sony_nc_acpi_handle, + "SN00", i + 0x20, &result)) { + dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n", + result, i); + handles->cap[i] = result; } } + /* allow reading capabilities via sysfs */ + if (device_create_file(&pd->dev, &handles->devattr)) { + kfree(handles); + handles = NULL; + return -1; + } + + return 0; +} + +static int sony_nc_handles_cleanup(struct platform_device *pd) +{ + if (handles) { + device_remove_file(&pd->dev, &handles->devattr); + kfree(handles); + handles = NULL; + } + return 0; +} + +static int sony_find_snc_handle(int handle) +{ + int i; + for (i = 0; i < 0x10; i++) { + if (handles->cap[i] == handle) { + dprintk("found handle 0x%.4x (offset: 0x%.2x)\n", + handle, i); + return i; + } + } dprintk("handle 0x%.4x not found\n", handle); return -1; } @@ -1274,6 +1333,10 @@ static int sony_nc_add(struct acpi_device *device) goto outwalk; } + result = sony_pf_add(); + if (result) + goto outpresent; + if (debug) { status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, 1, sony_walk_callback, @@ -1281,7 +1344,7 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_FAILURE(status)) { pr_warn(DRV_PFX "unable to walk acpi resources\n"); result = -ENODEV; - goto outwalk; + goto outpresent; } } @@ -1294,6 +1357,8 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); + if (sony_nc_handles_setup(sony_pf_device)) + goto outpresent; sony_nc_function_setup(device); sony_nc_rfkill_setup(device); } @@ -1302,7 +1367,7 @@ static int sony_nc_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { pr_err(DRV_PFX "Unable to create input devices.\n"); - goto outwalk; + goto outsnc; } if (acpi_video_backlight_support()) { @@ -1330,10 +1395,6 @@ static int sony_nc_add(struct acpi_device *device) } - result = sony_pf_add(); - if (result) - goto outbacklight; - /* create sony_pf sysfs attributes related to the SNC device */ for (item = sony_nc_values; item->name; ++item) { @@ -1379,14 +1440,17 @@ static int sony_nc_add(struct acpi_device *device) for (item = sony_nc_values; item->name; ++item) { device_remove_file(&sony_pf_device->dev, &item->devattr); } - sony_pf_remove(); - - outbacklight: if (sony_backlight_device) backlight_device_unregister(sony_backlight_device); sony_laptop_remove_input(); + outsnc: + sony_nc_handles_cleanup(sony_pf_device); + + outpresent: + sony_pf_remove(); + outwalk: sony_nc_rfkill_cleanup(); return result; @@ -1405,6 +1469,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } + sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); sony_nc_rfkill_cleanup(); From bf155714013e59c299e378055d60a4adf92e79db Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:31 +0900 Subject: [PATCH 34/81] sony-laptop: implement keyboard backlight support Recent Vaios have the opportunity to control the keyboard backlight via ACPI calls to the SNC device. Introduce two module parameters to control how keyboard backlight should be set at module loading (default to on and with 10 seconds timeout). Tested-by: Marco Chiappero Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 176 ++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 516dd22bbb2..326a4600fad 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -125,6 +125,19 @@ MODULE_PARM_DESC(minor, "default is -1 (automatic)"); #endif +static int kbd_backlight; /* = 1 */ +module_param(kbd_backlight, int, 0444); +MODULE_PARM_DESC(kbd_backlight, + "set this to 0 to disable keyboard backlight, " + "1 to enable it (default: 0)"); + +static int kbd_backlight_timeout; /* = 0 */ +module_param(kbd_backlight_timeout, int, 0444); +MODULE_PARM_DESC(kbd_backlight_timeout, + "set this to 0 to set the default 10 seconds timeout, " + "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " + "(default: 0)"); + enum sony_nc_rfkill { SONY_WIFI, SONY_BLUETOOTH, @@ -1309,6 +1322,161 @@ out_no_enum: return; } +/* Keyboard backlight feature */ +#define KBDBL_HANDLER 0x137 +#define KBDBL_PRESENT 0xB00 +#define SET_MODE 0xC00 +#define SET_TIMEOUT 0xE00 + +struct kbd_backlight { + int mode; + int timeout; + struct device_attribute mode_attr; + struct device_attribute timeout_attr; +}; + +struct kbd_backlight *kbdbl_handle; + +static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) +{ + int result; + + if (value > 1) + return -EINVAL; + + if (sony_call_snc_handle(KBDBL_HANDLER, + (value << 0x10) | SET_MODE, &result)) + return -EIO; + + kbdbl_handle->mode = value; + + return 0; +} + +static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + int ret = 0; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (strict_strtoul(buffer, 10, &value)) + return -EINVAL; + + ret = __sony_nc_kbd_backlight_mode_set(value); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode); + return count; +} + +static int __sony_nc_kbd_backlight_timeout_set(u8 value) +{ + int result; + + if (value > 3) + return -EINVAL; + + if (sony_call_snc_handle(KBDBL_HANDLER, + (value << 0x10) | SET_TIMEOUT, &result)) + return -EIO; + + kbdbl_handle->timeout = value; + + return 0; +} + +static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + int ret = 0; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (strict_strtoul(buffer, 10, &value)) + return -EINVAL; + + ret = __sony_nc_kbd_backlight_timeout_set(value); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout); + return count; +} + +static int sony_nc_kbd_backlight_setup(struct platform_device *pd) +{ + int result; + + if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result)) + return 0; + if (!(result & 0x02)) + return 0; + + kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); + + sysfs_attr_init(&kbdbl_handle->mode_attr.attr); + kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; + kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show; + kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store; + + sysfs_attr_init(&kbdbl_handle->timeout_attr.attr); + kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout"; + kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR; + kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show; + kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store; + + if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr)) + goto outkzalloc; + + if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr)) + goto outmode; + + __sony_nc_kbd_backlight_mode_set(kbd_backlight); + __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout); + + return 0; + +outmode: + device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); +outkzalloc: + kfree(kbdbl_handle); + kbdbl_handle = NULL; + return -1; +} + +static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) +{ + if (kbdbl_handle) { + device_remove_file(&pd->dev, &kbdbl_handle->mode_attr); + device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr); + kfree(kbdbl_handle); + } + return 0; +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1359,6 +1527,8 @@ static int sony_nc_add(struct acpi_device *device) dprintk("Doing SNC setup\n"); if (sony_nc_handles_setup(sony_pf_device)) goto outpresent; + if (sony_nc_kbd_backlight_setup(sony_pf_device)) + goto outsnc; sony_nc_function_setup(device); sony_nc_rfkill_setup(device); } @@ -1367,7 +1537,7 @@ static int sony_nc_add(struct acpi_device *device) result = sony_laptop_setup_input(device); if (result) { pr_err(DRV_PFX "Unable to create input devices.\n"); - goto outsnc; + goto outkbdbacklight; } if (acpi_video_backlight_support()) { @@ -1445,6 +1615,9 @@ static int sony_nc_add(struct acpi_device *device) sony_laptop_remove_input(); + outkbdbacklight: + sony_nc_kbd_backlight_cleanup(sony_pf_device); + outsnc: sony_nc_handles_cleanup(sony_pf_device); @@ -1469,6 +1642,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) device_remove_file(&sony_pf_device->dev, &item->devattr); } + sony_nc_kbd_backlight_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); sony_pf_remove(); sony_laptop_remove_input(); From 7751ab8e600f26e10c2ba12a92d48a4852a51da8 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 19 Feb 2011 11:52:32 +0900 Subject: [PATCH 35/81] sony-laptop: implement new backlight control method Reasonably recent Vaios have a 0x12f or 0x137 handler that exposes a fine lid backlight regulation with values ranging from 0 to 255. The patch is based on findings and code from Javier Achirica and Marco Chiappero Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 109 +++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 326a4600fad..d28a4a58a49 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -938,11 +938,39 @@ static int sony_backlight_get_brightness(struct backlight_device *bd) return value - 1; } -static struct backlight_device *sony_backlight_device; +static int sony_nc_get_brightness_ng(struct backlight_device *bd) +{ + int result; + int *handle = (int *)bl_get_data(bd); + + sony_call_snc_handle(*handle, 0x0200, &result); + + return result & 0xff; +} + +static int sony_nc_update_status_ng(struct backlight_device *bd) +{ + int value, result; + int *handle = (int *)bl_get_data(bd); + + value = bd->props.brightness; + sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result); + + return sony_nc_get_brightness_ng(bd); +} + static const struct backlight_ops sony_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, .update_status = sony_backlight_update_status, .get_brightness = sony_backlight_get_brightness, }; +static const struct backlight_ops sony_backlight_ng_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = sony_nc_update_status_ng, + .get_brightness = sony_nc_get_brightness_ng, +}; +static int backlight_ng_handle; +static struct backlight_device *sony_backlight_device; /* * New SNC-only Vaios event mapping to driver known keys @@ -1135,11 +1163,6 @@ static int sony_nc_resume(struct acpi_device *device) sony_nc_function_setup(device); } - /* set the last requested brightness level */ - if (sony_backlight_device && - sony_backlight_update_status(sony_backlight_device) < 0) - pr_warn(DRV_PFX "unable to restore brightness level\n"); - /* re-read rfkill state */ sony_nc_rfkill_update(); @@ -1477,6 +1500,52 @@ static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd) return 0; } +static void sony_nc_backlight_setup(void) +{ + acpi_handle unused; + int max_brightness = 0; + const struct backlight_ops *ops = NULL; + struct backlight_properties props; + + if (sony_find_snc_handle(0x12f) != -1) { + backlight_ng_handle = 0x12f; + ops = &sony_backlight_ng_ops; + max_brightness = 0xff; + + } else if (sony_find_snc_handle(0x137) != -1) { + backlight_ng_handle = 0x137; + ops = &sony_backlight_ng_ops; + max_brightness = 0xff; + + } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", + &unused))) { + ops = &sony_backlight_ops; + max_brightness = SONY_MAX_BRIGHTNESS - 1; + + } else + return; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = max_brightness; + sony_backlight_device = backlight_device_register("sony", NULL, + &backlight_ng_handle, + ops, &props); + + if (IS_ERR(sony_backlight_device)) { + pr_warning(DRV_PFX "unable to register backlight device\n"); + sony_backlight_device = NULL; + } else + sony_backlight_device->props.brightness = + ops->get_brightness(sony_backlight_device); +} + +static void sony_nc_backlight_cleanup(void) +{ + if (sony_backlight_device) + backlight_device_unregister(sony_backlight_device); +} + static int sony_nc_add(struct acpi_device *device) { acpi_status status; @@ -1543,26 +1612,8 @@ static int sony_nc_add(struct acpi_device *device) if (acpi_video_backlight_support()) { pr_info(DRV_PFX "brightness ignored, must be " "controlled by ACPI video driver\n"); - } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", - &handle))) { - struct backlight_properties props; - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = SONY_MAX_BRIGHTNESS - 1; - sony_backlight_device = backlight_device_register("sony", NULL, - NULL, - &sony_backlight_ops, - &props); - - if (IS_ERR(sony_backlight_device)) { - pr_warning(DRV_PFX "unable to register backlight device\n"); - sony_backlight_device = NULL; - } else { - sony_backlight_device->props.brightness = - sony_backlight_get_brightness - (sony_backlight_device); - } - + } else { + sony_nc_backlight_setup(); } /* create sony_pf sysfs attributes related to the SNC device */ @@ -1610,8 +1661,7 @@ static int sony_nc_add(struct acpi_device *device) for (item = sony_nc_values; item->name; ++item) { device_remove_file(&sony_pf_device->dev, &item->devattr); } - if (sony_backlight_device) - backlight_device_unregister(sony_backlight_device); + sony_nc_backlight_cleanup(); sony_laptop_remove_input(); @@ -1633,8 +1683,7 @@ static int sony_nc_remove(struct acpi_device *device, int type) { struct sony_nc_value *item; - if (sony_backlight_device) - backlight_device_unregister(sony_backlight_device); + sony_nc_backlight_cleanup(); sony_nc_acpi_device = NULL; From 9af0e0fb70ed8e2387323c19496a7e174363f7b6 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:20 +0200 Subject: [PATCH 36/81] hp-wmi: check query return value in hp_wmi_perform_query Check BIOS provided return value code in hp_wmi_perform_query and print a warning on error. Printing is suppressed for HPWMI_RET_UNKNOWN_CMDTYPE which is returned when the command type is unsupported. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 9e05af9c41c..87dfb248976 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -89,6 +89,13 @@ struct bios_return { u32 value; }; +enum hp_return_value { + HPWMI_RET_WRONG_SIGNATURE = 0x02, + HPWMI_RET_UNKNOWN_COMMAND = 0x03, + HPWMI_RET_UNKNOWN_CMDTYPE = 0x04, + HPWMI_RET_INVALID_PARAMETERS = 0x05, +}; + static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, @@ -171,6 +178,15 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, bios_return = *((struct bios_return *)obj->buffer.pointer); + if (bios_return.return_code) { + if (bios_return.return_code != HPWMI_RET_UNKNOWN_CMDTYPE) + printk(KERN_WARNING PREFIX "query 0x%x returned " + "error 0x%x\n", + query, bios_return.return_code); + kfree(obj); + return bios_return.return_code; + } + memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); kfree(obj); From 25bb067a08c5db70cd8bcf9e160ac81718ea075c Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:21 +0200 Subject: [PATCH 37/81] hp-wmi: remove a variable that is never read Remove the status variable from hp_wmi_perform_query which holds the return value from wmi_evaluate_method(). It is never checked as the function bails out if the output buffer hasn't been allocated which indicates the call failed. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 87dfb248976..8e27c27900a 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -153,7 +153,6 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, int buffersize) { struct bios_return bios_return; - acpi_status status; union acpi_object *obj; struct bios_args args = { .signature = 0x55434553, @@ -165,7 +164,7 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, struct acpi_buffer input = { sizeof(struct bios_args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); + wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); obj = output.pointer; From c3021ea1beeeb1aa8a92fa6946a6e25fc55f171d Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:22 +0200 Subject: [PATCH 38/81] hp-wmi: allow setting input and output buffer sizes separately Split buffersize parameter of hp_wmi_perform_query to insize and outsize. Existing callers are changed to use the same value for insize and outsize to avoid any regressions, with the exception of hp_wmi_set_block where the output buffer is unused and therefore outsize is set to 0 (this change is not seen by BIOS code). The maximum input buffer size is kept at 4 bytes as per struct bios_args. Some commands exist that take longer buffers, but they haven't been implemented. The data portion of bios_args can be trivially made dynamically allocated later when such larger buffers become needed. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 63 ++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 8e27c27900a..5c8ae65950d 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -86,7 +86,6 @@ struct bios_args { struct bios_return { u32 sigpass; u32 return_code; - u32 value; }; enum hp_return_value { @@ -136,7 +135,8 @@ static struct platform_driver hp_wmi_driver = { * query: The commandtype -> What should be queried * write: The command -> 0 read, 1 write, 3 ODM specific * buffer: Buffer used as input and/or output - * buffersize: Size of buffer + * insize: Size of input buffer + * outsize: Size of output buffer * * returns zero on success * an HP WMI query specific error code (which is positive) @@ -147,23 +147,28 @@ static struct platform_driver hp_wmi_driver = { * size. E.g. Battery info query (0x7) is defined to have 1 byte input * and 128 byte output. The caller would do: * buffer = kzalloc(128, GFP_KERNEL); - * ret = hp_wmi_perform_query(0x7, 0, buffer, 128) + * ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128) */ -static int hp_wmi_perform_query(int query, int write, u32 *buffer, - int buffersize) +static int hp_wmi_perform_query(int query, int write, void *buffer, + int insize, int outsize) { - struct bios_return bios_return; + struct bios_return *bios_return; + int actual_outsize; union acpi_object *obj; struct bios_args args = { .signature = 0x55434553, .command = write ? 0x2 : 0x1, .commandtype = query, - .datasize = buffersize, - .data = *buffer, + .datasize = insize, + .data = 0, }; struct acpi_buffer input = { sizeof(struct bios_args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + if (WARN_ON(insize > sizeof(args.data))) + return -EINVAL; + memcpy(&args.data, buffer, insize); + wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output); obj = output.pointer; @@ -175,19 +180,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer, return -EINVAL; } - bios_return = *((struct bios_return *)obj->buffer.pointer); + bios_return = (struct bios_return *)obj->buffer.pointer; - if (bios_return.return_code) { - if (bios_return.return_code != HPWMI_RET_UNKNOWN_CMDTYPE) + if (bios_return->return_code) { + if (bios_return->return_code != HPWMI_RET_UNKNOWN_CMDTYPE) printk(KERN_WARNING PREFIX "query 0x%x returned " "error 0x%x\n", - query, bios_return.return_code); + query, bios_return->return_code); kfree(obj); - return bios_return.return_code; + return bios_return->return_code; } - memcpy(buffer, &bios_return.value, sizeof(bios_return.value)); + if (!outsize) { + /* ignore output data */ + kfree(obj); + return 0; + } + actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return))); + memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize); + memset(buffer + actual_outsize, 0, outsize - actual_outsize); kfree(obj); return 0; } @@ -196,7 +208,7 @@ static int hp_wmi_display_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -206,7 +218,7 @@ static int hp_wmi_hddtemp_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -216,7 +228,7 @@ static int hp_wmi_als_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; return state; @@ -226,7 +238,7 @@ static int hp_wmi_dock_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return -EINVAL; @@ -238,7 +250,7 @@ static int hp_wmi_tablet_state(void) { int state = 0; int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state, - sizeof(state)); + sizeof(state), sizeof(state)); if (ret) return ret; @@ -252,7 +264,7 @@ static int hp_wmi_set_block(void *data, bool blocked) int ret; ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, - &query, sizeof(query)); + &query, sizeof(query), 0); if (ret) return -EINVAL; return 0; @@ -267,7 +279,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r) int wireless = 0; int mask; hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, - &wireless, sizeof(wireless)); + &wireless, sizeof(wireless), + sizeof(wireless)); /* TBD: Pass error */ mask = 0x200 << (r * 8); @@ -283,7 +296,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) int wireless = 0; int mask; hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, - &wireless, sizeof(wireless)); + &wireless, sizeof(wireless), + sizeof(wireless)); /* TBD: Pass error */ mask = 0x800 << (r * 8); @@ -344,7 +358,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr, { u32 tmp = simple_strtoul(buf, NULL, 10); int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp, - sizeof(tmp)); + sizeof(tmp), sizeof(tmp)); if (ret) return -EINVAL; @@ -417,6 +431,7 @@ static void hp_wmi_notify(u32 value, void *context) case HPWMI_BEZEL_BUTTON: ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0, &key_code, + sizeof(key_code), sizeof(key_code)); if (ret) break; @@ -523,7 +538,7 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) int wireless = 0; err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless, - sizeof(wireless)); + sizeof(wireless), sizeof(wireless)); if (err) return err; From eceb7bdf644b418175aab1c998137e692ac98a4c Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:23 +0200 Subject: [PATCH 39/81] hp-wmi: split rfkill initialization out of hp_wmi_bios_setup Split initialization of rfkill devices from hp_wmi_bios_setup() to hp_wmi_rfkill_setup(). This makes the code somewhat cleaner, especially with the future command 0x1b rfkill support. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 46 ++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 5c8ae65950d..f6a1c37af9c 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -532,7 +532,7 @@ static void cleanup_sysfs(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_tablet); } -static int __devinit hp_wmi_bios_setup(struct platform_device *device) +static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) { int err; int wireless = 0; @@ -542,22 +542,6 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) if (err) return err; - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_hddtemp); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_als); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_dock); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_tablet); - if (err) - goto add_sysfs_error; - if (wireless & 0x1) { wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, RFKILL_TYPE_WLAN, @@ -611,6 +595,34 @@ register_bluetooth_error: rfkill_unregister(wifi_rfkill); register_wifi_error: rfkill_destroy(wifi_rfkill); + return err; +} + +static int __devinit hp_wmi_bios_setup(struct platform_device *device) +{ + int err; + + err = hp_wmi_rfkill_setup(device); + if (err) + return err; + + err = device_create_file(&device->dev, &dev_attr_display); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_hddtemp); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_als); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_dock); + if (err) + goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_tablet); + if (err) + goto add_sysfs_error; + return 0; + add_sysfs_error: cleanup_sysfs(device); return err; From 6d97db590ca2787c202cf35e76f5d24b3007bc7c Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:24 +0200 Subject: [PATCH 40/81] hp-wmi: clear rfkill device pointers when appropriate NULLify rfkill pointers during initialization. This prevents dereference of invalid pointer in case the driver is rebound and some rfkill device isn't detected anymore. Clear them also in hp_wmi_rfkill_setup failure path so that an rfkill initialization failure doesn't need to be fatal for the whole driver. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index f6a1c37af9c..45b2bbe6d83 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -587,14 +587,17 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) return 0; register_wwan_err: rfkill_destroy(wwan_rfkill); + wwan_rfkill = NULL; if (bluetooth_rfkill) rfkill_unregister(bluetooth_rfkill); register_bluetooth_error: rfkill_destroy(bluetooth_rfkill); + bluetooth_rfkill = NULL; if (wifi_rfkill) rfkill_unregister(wifi_rfkill); register_wifi_error: rfkill_destroy(wifi_rfkill); + wifi_rfkill = NULL; return err; } @@ -602,6 +605,11 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) { int err; + /* clear detected rfkill devices */ + wifi_rfkill = NULL; + bluetooth_rfkill = NULL; + wwan_rfkill = NULL; + err = hp_wmi_rfkill_setup(device); if (err) return err; From 7cd635da42fda9272fd200121e565d116c0c65c9 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:25 +0200 Subject: [PATCH 41/81] hp-wmi: make rfkill initialization failure non-fatal hp_wmi_rfkill_setup cleans up after itself now, so failing completely is no longer necessary. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 45b2bbe6d83..524ffabc286 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -610,9 +610,7 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) bluetooth_rfkill = NULL; wwan_rfkill = NULL; - err = hp_wmi_rfkill_setup(device); - if (err) - return err; + hp_wmi_rfkill_setup(device); err = device_create_file(&device->dev, &dev_attr_display); if (err) From c0b9c6494498014f28dbec37fe327bf016a91356 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sun, 20 Feb 2011 20:07:26 +0200 Subject: [PATCH 42/81] hp-wmi: add rfkill support for wireless query 0x1b Some recent HP laptops use a new wireless query command type 0x1b. Add support for it. Tested on HP Mini 5102. Signed-off-by: Anssi Hannula Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 188 +++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 524ffabc286..1bc4a7539ba 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -2,6 +2,7 @@ * HP WMI hotkeys * * Copyright (C) 2008 Red Hat + * Copyright (C) 2010, 2011 Anssi Hannula * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac @@ -51,6 +52,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_HOTKEY_QUERY 0xc +#define HPWMI_WIRELESS2_QUERY 0x1b #define PREFIX "HP WMI: " #define UNIMP "Unimplemented " @@ -95,6 +97,39 @@ enum hp_return_value { HPWMI_RET_INVALID_PARAMETERS = 0x05, }; +enum hp_wireless2_bits { + HPWMI_POWER_STATE = 0x01, + HPWMI_POWER_SOFT = 0x02, + HPWMI_POWER_BIOS = 0x04, + HPWMI_POWER_HARD = 0x08, +}; + +#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \ + != (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) +#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT) + +struct bios_rfkill2_device_state { + u8 radio_type; + u8 bus_type; + u16 vendor_id; + u16 product_id; + u16 subsys_vendor_id; + u16 subsys_product_id; + u8 rfkill_id; + u8 power; + u8 unknown[4]; +}; + +/* 7 devices fit into the 128 byte buffer */ +#define HPWMI_MAX_RFKILL2_DEVICES 7 + +struct bios_rfkill2_state { + u8 unknown[7]; + u8 count; + u8 pad[8]; + struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES]; +}; + static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x02, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x03, { KEY_BRIGHTNESSDOWN } }, @@ -114,6 +149,15 @@ static struct rfkill *wifi_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *wwan_rfkill; +struct rfkill2_device { + u8 id; + int num; + struct rfkill *rfkill; +}; + +static int rfkill2_count; +static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; + static const struct dev_pm_ops hp_wmi_pm_ops = { .resume = hp_wmi_resume_handler, .restore = hp_wmi_resume_handler, @@ -308,6 +352,51 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r) return true; } +static int hp_wmi_rfkill2_set_block(void *data, bool blocked) +{ + int rfkill_id = (int)(long)data; + char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked }; + + if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1, + buffer, sizeof(buffer), 0)) + return -EINVAL; + return 0; +} + +static const struct rfkill_ops hp_wmi_rfkill2_ops = { + .set_block = hp_wmi_rfkill2_set_block, +}; + +static int hp_wmi_rfkill2_refresh(void) +{ + int err, i; + struct bios_rfkill2_state state; + + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, + 0, sizeof(state)); + if (err) + return err; + + for (i = 0; i < rfkill2_count; i++) { + int num = rfkill2[i].num; + struct bios_rfkill2_device_state *devstate; + devstate = &state.device[num]; + + if (num >= state.count || + devstate->rfkill_id != rfkill2[i].id) { + printk(KERN_WARNING PREFIX "power configuration of " + "the wireless devices unexpectedly changed\n"); + continue; + } + + rfkill_set_states(rfkill2[i].rfkill, + IS_SWBLOCKED(devstate->power), + IS_HWBLOCKED(devstate->power)); + } + + return 0; +} + static ssize_t show_display(struct device *dev, struct device_attribute *attr, char *buf) { @@ -442,6 +531,11 @@ static void hp_wmi_notify(u32 value, void *context) key_code); break; case HPWMI_WIRELESS: + if (rfkill2_count) { + hp_wmi_rfkill2_refresh(); + break; + } + if (wifi_rfkill) rfkill_set_states(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI), @@ -601,6 +695,87 @@ register_wifi_error: return err; } +static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device) +{ + int err, i; + struct bios_rfkill2_state state; + err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state, + 0, sizeof(state)); + if (err) + return err; + + if (state.count > HPWMI_MAX_RFKILL2_DEVICES) { + printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n"); + return -EINVAL; + } + + for (i = 0; i < state.count; i++) { + struct rfkill *rfkill; + enum rfkill_type type; + char *name; + switch (state.device[i].radio_type) { + case HPWMI_WIFI: + type = RFKILL_TYPE_WLAN; + name = "hp-wifi"; + break; + case HPWMI_BLUETOOTH: + type = RFKILL_TYPE_BLUETOOTH; + name = "hp-bluetooth"; + break; + case HPWMI_WWAN: + type = RFKILL_TYPE_WWAN; + name = "hp-wwan"; + break; + default: + printk(KERN_WARNING PREFIX "unknown device type 0x%x\n", + state.device[i].radio_type); + continue; + } + + if (!state.device[i].vendor_id) { + printk(KERN_WARNING PREFIX "zero device %d while %d " + "reported\n", i, state.count); + continue; + } + + rfkill = rfkill_alloc(name, &device->dev, type, + &hp_wmi_rfkill2_ops, (void *)(long)i); + if (!rfkill) { + err = -ENOMEM; + goto fail; + } + + rfkill2[rfkill2_count].id = state.device[i].rfkill_id; + rfkill2[rfkill2_count].num = i; + rfkill2[rfkill2_count].rfkill = rfkill; + + rfkill_init_sw_state(rfkill, + IS_SWBLOCKED(state.device[i].power)); + rfkill_set_hw_state(rfkill, + IS_HWBLOCKED(state.device[i].power)); + + if (!(state.device[i].power & HPWMI_POWER_BIOS)) + printk(KERN_INFO PREFIX "device %s blocked by BIOS\n", + name); + + err = rfkill_register(rfkill); + if (err) { + rfkill_destroy(rfkill); + goto fail; + } + + rfkill2_count++; + } + + return 0; +fail: + for (; rfkill2_count > 0; rfkill2_count--) { + rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill); + rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill); + } + return err; +} + static int __devinit hp_wmi_bios_setup(struct platform_device *device) { int err; @@ -609,8 +784,10 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device) wifi_rfkill = NULL; bluetooth_rfkill = NULL; wwan_rfkill = NULL; + rfkill2_count = 0; - hp_wmi_rfkill_setup(device); + if (hp_wmi_rfkill_setup(device)) + hp_wmi_rfkill2_setup(device); err = device_create_file(&device->dev, &dev_attr_display); if (err) @@ -636,8 +813,14 @@ add_sysfs_error: static int __exit hp_wmi_bios_remove(struct platform_device *device) { + int i; cleanup_sysfs(device); + for (i = 0; i < rfkill2_count; i++) { + rfkill_unregister(rfkill2[i].rfkill); + rfkill_destroy(rfkill2[i].rfkill); + } + if (wifi_rfkill) { rfkill_unregister(wifi_rfkill); rfkill_destroy(wifi_rfkill); @@ -670,6 +853,9 @@ static int hp_wmi_resume_handler(struct device *device) input_sync(hp_wmi_input_dev); } + if (rfkill2_count) + hp_wmi_rfkill2_refresh(); + if (wifi_rfkill) rfkill_set_states(wifi_rfkill, hp_wmi_get_sw_state(HPWMI_WIFI), From f017fbe7993d8416824aaf079fa1e6dc88d1ba55 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Sun, 20 Feb 2011 23:05:43 +0530 Subject: [PATCH 43/81] medfield: Add Thermal Driver This is the basic thermal sensor driver for Intel MID platform using the Medfield chipset. It plugs in via the thermal drivers and provides sensor readings for the device sensors. Signed-off-by: Durgadoss R Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 7 + drivers/platform/x86/Makefile | 3 +- drivers/platform/x86/intel_mid_thermal.c | 575 +++++++++++++++++++++++ 3 files changed, 584 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/intel_mid_thermal.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 53f818d23ed..b039d6a93f1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -637,6 +637,13 @@ config INTEL_MID_POWER_BUTTON If unsure, say N. +config INTEL_MFLD_THERMAL + tristate "Thermal driver for Intel Medfield platform" + depends on INTEL_SCU_IPC && THERMAL + help + Say Y here to enable thermal driver support for the Intel Medfield + platform. + config RAR_REGISTER bool "Restricted Access Region Register Driver" depends on PCI && X86_MRST diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 46f3fd2c885..a9877711b3f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -30,7 +30,8 @@ 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_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o +obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.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_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c new file mode 100644 index 00000000000..1215c309686 --- /dev/null +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -0,0 +1,575 @@ +/* + * intel_mid_thermal.c - Intel MID platform thermal driver + * + * Copyright (C) 2011 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Author: Durgadoss R + */ + +#define pr_fmt(fmt) "intel_mid_thermal: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Number of thermal sensors */ +#define MSIC_THERMAL_SENSORS 4 + +/* ADC1 - thermal registers */ +#define MSIC_THERM_ADC1CNTL1 0x1C0 +#define MSIC_ADC_ENBL 0x10 +#define MSIC_ADC_START 0x08 + +#define MSIC_THERM_ADC1CNTL3 0x1C2 +#define MSIC_ADCTHERM_ENBL 0x04 +#define MSIC_ADCRRDATA_ENBL 0x05 +#define MSIC_CHANL_MASK_VAL 0x0F + +#define MSIC_STOPBIT_MASK 16 +#define MSIC_ADCTHERM_MASK 4 +#define ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS) + +/* ADC channel code values */ +#define SKIN_SENSOR0_CODE 0x08 +#define SKIN_SENSOR1_CODE 0x09 +#define SYS_SENSOR_CODE 0x0A +#define MSIC_DIE_SENSOR_CODE 0x03 + +#define SKIN_THERM_SENSOR0 0 +#define SKIN_THERM_SENSOR1 1 +#define SYS_THERM_SENSOR2 2 +#define MSIC_DIE_THERM_SENSOR3 3 + +/* ADC code range */ +#define ADC_MAX 977 +#define ADC_MIN 162 +#define ADC_VAL0C 887 +#define ADC_VAL20C 720 +#define ADC_VAL40C 508 +#define ADC_VAL60C 315 + +/* ADC base addresses */ +#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ + +/* MSIC die attributes */ +#define MSIC_DIE_ADC_MIN 488 +#define MSIC_DIE_ADC_MAX 1004 + +/* This holds the address of the first free ADC channel, + * among the 15 channels + */ +static int channel_index; + +struct platform_info { + struct platform_device *pdev; + struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS]; +}; + +struct thermal_device_info { + unsigned int chnl_addr; + int direct; + /* This holds the current temperature in millidegree celsius */ + long curr_temp; +}; + +/** + * to_msic_die_temp - converts adc_val to msic_die temperature + * @adc_val: ADC value to be converted + * + * Can sleep + */ +static int to_msic_die_temp(uint16_t adc_val) +{ + return (368 * (adc_val) / 1000) - 220; +} + +/** + * is_valid_adc - checks whether the adc code is within the defined range + * @min: minimum value for the sensor + * @max: maximum value for the sensor + * + * Can sleep + */ +static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max) +{ + return (adc_val >= min) && (adc_val <= max); +} + +/** + * adc_to_temp - converts the ADC code to temperature in C + * @direct: true if ths channel is direct index + * @adc_val: the adc_val that needs to be converted + * @tp: temperature return value + * + * Linear approximation is used to covert the skin adc value into temperature. + * This technique is used to avoid very long look-up table to get + * the appropriate temp value from ADC value. + * The adc code vs sensor temp curve is split into five parts + * to achieve very close approximate temp value with less than + * 0.5C error + */ +static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp) +{ + int temp; + + /* Direct conversion for die temperature */ + if (direct) { + if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) { + *tp = to_msic_die_temp(adc_val) * 1000; + return 0; + } + return -ERANGE; + } + + if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX)) + return -ERANGE; + + /* Linear approximation for skin temperature */ + if (adc_val > ADC_VAL0C) + temp = 177 - (adc_val/5); + else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C)) + temp = 111 - (adc_val/8); + else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C)) + temp = 92 - (adc_val/10); + else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C)) + temp = 91 - (adc_val/10); + else + temp = 112 - (adc_val/6); + + /* Convert temperature in celsius to milli degree celsius */ + *tp = temp * 1000; + return 0; +} + +/** + * mid_read_temp - read sensors for temperature + * @temp: holds the current temperature for the sensor after reading + * + * reads the adc_code from the channel and converts it to real + * temperature. The converted value is stored in temp. + * + * Can sleep + */ +static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) +{ + struct thermal_device_info *td_info = tzd->devdata; + uint16_t adc_val, addr; + uint8_t data = 0; + int ret; + unsigned long curr_temp; + + + addr = td_info->chnl_addr; + + /* Enable the msic for conversion before reading */ + ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); + if (ret) + return ret; + + /* Re-toggle the RRDATARD bit (temporary workaround) */ + ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL); + if (ret) + return ret; + + /* Read the higher bits of data */ + ret = intel_scu_ipc_ioread8(addr, &data); + if (ret) + return ret; + + /* Shift bits to accomodate the lower two data bits */ + adc_val = (data << 2); + addr++; + + ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */ + if (ret) + return ret; + + /* Adding lower two bits to the higher bits */ + data &= 03; + adc_val += data; + + /* Convert ADC value to temperature */ + ret = adc_to_temp(td_info->direct, adc_val, &curr_temp); + if (ret == 0) + *temp = td_info->curr_temp = curr_temp; + return ret; +} + +/** + * configure_adc - enables/disables the ADC for conversion + * @val: zero: disables the ADC non-zero:enables the ADC + * + * Enable/Disable the ADC depending on the argument + * + * Can sleep + */ +static int configure_adc(int val) +{ + int ret; + uint8_t data; + + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + if (ret) + return ret; + + if (val) { + /* Enable and start the ADC */ + data |= (MSIC_ADC_ENBL | MSIC_ADC_START); + } else { + /* Just stop the ADC */ + data &= (~MSIC_ADC_START); + } + + return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data); +} + +/** + * set_up_therm_channel - enable thermal channel for conversion + * @base_addr: index of free msic ADC channel + * + * Enable all the three channels for conversion + * + * Can sleep + */ +static int set_up_therm_channel(u16 base_addr) +{ + int ret; + + /* Enable all the sensor channels */ + ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE); + if (ret) + return ret; + + ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE); + if (ret) + return ret; + + ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE); + if (ret) + return ret; + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + ret = intel_scu_ipc_iowrite8(base_addr + 3, + (MSIC_DIE_SENSOR_CODE | 0x10)); + if (ret) + return ret; + + /* Enable ADC and start it */ + return configure_adc(1); +} + +/** + * reset_stopbit - sets the stop bit to 0 on the given channel + * @addr: address of the channel + * + * Can sleep + */ +static int reset_stopbit(uint16_t addr) +{ + int ret; + uint8_t data; + ret = intel_scu_ipc_ioread8(addr, &data); + if (ret) + return ret; + /* Set the stop bit to zero */ + return intel_scu_ipc_iowrite8(addr, (data & 0xEF)); +} + +/** + * find_free_channel - finds an empty channel for conversion + * + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * + * FIXME: Ultimately the channel allocator will move into the intel_scu_ipc + * code. + */ +static int find_free_channel(void) +{ + int ret; + int i; + uint8_t data; + + /* check whether ADC is enabled */ + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + if (ret) + return ret; + + if ((data & MSIC_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < ADC_CHANLS_MAX; i++) { + ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data); + if (ret) + return ret; + + if (data & MSIC_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/** + * mid_initialize_adc - initializing the ADC + * @dev: our device structure + * + * Initialize the ADC for reading thermistor values. Can sleep. + */ +static int mid_initialize_adc(struct device *dev) +{ + u8 data; + u16 base_addr; + int ret; + + /* + * Ensure that adctherm is disabled before we + * initialize the ADC + */ + ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data); + if (ret) + return ret; + + if (data & MSIC_ADCTHERM_MASK) + dev_warn(dev, "ADCTHERM already set"); + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(); + if (channel_index < 0) { + dev_err(dev, "No free ADC channels"); + return channel_index; + } + + base_addr = ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + ret = reset_stopbit(base_addr); + if (ret) + return ret; + + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + ret = set_up_therm_channel(base_addr); + if (ret) { + dev_err(dev, "unable to enable ADC"); + return ret; + } + dev_dbg(dev, "ADC initialization successful"); + return ret; +} + +/** + * initialize_sensor - sets default temp and timer ranges + * @index: index of the sensor + * + * Context: can sleep + */ +static struct thermal_device_info *initialize_sensor(int index) +{ + struct thermal_device_info *td_info = + kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL); + + if (!td_info) + return NULL; + + /* Set the base addr of the channel for this sensor */ + td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index); + /* Sensor 3 is direct conversion */ + if (index == 3) + td_info->direct = 1; + return td_info; +} + +/** + * mid_thermal_resume - resume routine + * @pdev: platform device structure + * + * mid thermal resume: re-initializes the adc. Can sleep. + */ +static int mid_thermal_resume(struct platform_device *pdev) +{ + return mid_initialize_adc(&pdev->dev); +} + +/** + * mid_thermal_suspend - suspend routine + * @pdev: platform device structure + * + * mid thermal suspend implements the suspend functionality + * by stopping the ADC. Can sleep. + */ +static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + /* + * This just stops the ADC and does not disable it. + * temporary workaround until we have a generic ADC driver. + * If 0 is passed, it disables the ADC. + */ + return configure_adc(0); +} + +/** + * read_curr_temp - reads the current temperature and stores in temp + * @temp: holds the current temperature value after reading + * + * Can sleep + */ +static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp) +{ + WARN_ON(tzd == NULL); + return mid_read_temp(tzd, temp); +} + +/* Can't be const */ +static struct thermal_zone_device_ops tzd_ops = { + .get_temp = read_curr_temp, +}; + + +/** + * mid_thermal_probe - mfld thermal initialize + * @pdev: platform device structure + * + * mid thermal probe initializes the hardware and registers + * all the sensors with the generic thermal framework. Can sleep. + */ +static int mid_thermal_probe(struct platform_device *pdev) +{ + static char *name[MSIC_THERMAL_SENSORS] = { + "skin0", "skin1", "sys", "msicdie" + }; + + int ret; + int i; + struct platform_info *pinfo; + + pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + /* Initializing the hardware */ + ret = mid_initialize_adc(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "ADC init failed"); + kfree(pinfo); + return ret; + } + + /* Register each sensor with the generic thermal framework*/ + for (i = 0; i < MSIC_THERMAL_SENSORS; i++) { + pinfo->tzd[i] = thermal_zone_device_register(name[i], + 0, initialize_sensor(i), + &tzd_ops, 0, 0, 0, 0); + if (IS_ERR(pinfo->tzd[i])) + goto reg_fail; + } + + pinfo->pdev = pdev; + platform_set_drvdata(pdev, pinfo); + return 0; + +reg_fail: + ret = PTR_ERR(pinfo->tzd[i]); + while (--i >= 0) + thermal_zone_device_unregister(pinfo->tzd[i]); + configure_adc(0); + kfree(pinfo); + return ret; +} + +/** + * mid_thermal_remove - mfld thermal finalize + * @dev: platform device structure + * + * MLFD thermal remove unregisters all the sensors from the generic + * thermal framework. Can sleep. + */ +static int mid_thermal_remove(struct platform_device *pdev) +{ + int i; + struct platform_info *pinfo = platform_get_drvdata(pdev); + + for (i = 0; i < MSIC_THERMAL_SENSORS; i++) + thermal_zone_device_unregister(pinfo->tzd[i]); + + platform_set_drvdata(pdev, NULL); + + /* Stop the ADC */ + return configure_adc(0); +} + +/********************************************************************* + * Driver initialisation and finalization + *********************************************************************/ + +#define DRIVER_NAME "msic_sensor" + +static const struct platform_device_id therm_id_table[] = { + { DRIVER_NAME, 1 }, +}; + +static struct platform_driver mid_thermal_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = mid_thermal_probe, + .suspend = mid_thermal_suspend, + .resume = mid_thermal_resume, + .remove = __devexit_p(mid_thermal_remove), + .id_table = therm_id_table, +}; + +static int __init mid_thermal_module_init(void) +{ + return platform_driver_register(&mid_thermal_driver); +} + +static void __exit mid_thermal_module_exit(void) +{ + platform_driver_unregister(&mid_thermal_driver); +} + +module_init(mid_thermal_module_init); +module_exit(mid_thermal_module_exit); + +MODULE_AUTHOR("Durgadoss R "); +MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); +MODULE_LICENSE("GPL"); From 5b799d4fb787bb94f1068352220ab033ac7969f8 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:30 +0100 Subject: [PATCH 44/81] asus-wmi: move generic code to asus-wmi New Asus notebooks are using a WMI device similar to the one used in Eee PCs. Since we don't want to load a module named eeepc-laptop on Asus Notebooks, start by copying all the code to asus-wmi.c. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Makefile | 2 +- drivers/platform/x86/{eeepc-wmi.c => asus-wmi.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/platform/x86/{eeepc-wmi.c => asus-wmi.c} (100%) diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index a9877711b3f..443257617b2 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o -obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o +obj-$(CONFIG_EEEPC_WMI) += asus-wmi.o eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/asus-wmi.c similarity index 100% rename from drivers/platform/x86/eeepc-wmi.c rename to drivers/platform/x86/asus-wmi.c From e12e6d94db24e9050821965aa75c95e8d2c65f10 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:31 +0100 Subject: [PATCH 45/81] eeepc-wmi: asus generic asus-wmi.ko module New Asus notebooks are using a WMI device similar to the one used in Eee PCs. Since we don't want to load eeepc-wmi module on Asus notebooks, and we want to keep the eeepc-wmi module for backward compatibility, this patch introduce a new module, named asus-wmi, that will be used by eeepc-wmi and the new Asus Notebook WMI Driver. eeepc-wmi's input device strings (device name and phys) are kept, but rfkill and led names are changed (s/eeepc/asus/). This should not break anything since rfkill are used by type or index, not by name, and the eeepc::touchpad led wasn't working correctly before 2.6.39 anyway. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- ...form-eeepc-wmi => sysfs-platform-asus-wmi} | 8 +- drivers/platform/x86/Kconfig | 24 +- drivers/platform/x86/Makefile | 3 +- drivers/platform/x86/asus-wmi.c | 918 +++++++++--------- drivers/platform/x86/asus-wmi.h | 58 ++ drivers/platform/x86/eeepc-wmi.c | 162 ++++ 6 files changed, 678 insertions(+), 495 deletions(-) rename Documentation/ABI/testing/{sysfs-platform-eeepc-wmi => sysfs-platform-asus-wmi} (80%) create mode 100644 drivers/platform/x86/asus-wmi.h create mode 100644 drivers/platform/x86/eeepc-wmi.c diff --git a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi similarity index 80% rename from Documentation/ABI/testing/sysfs-platform-eeepc-wmi rename to Documentation/ABI/testing/sysfs-platform-asus-wmi index 26acb6842c4..2e7df91620d 100644 --- a/Documentation/ABI/testing/sysfs-platform-eeepc-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -1,4 +1,4 @@ -What: /sys/devices/platform/eeepc-wmi/cpufv +What: /sys/devices/platform//cpufv Date: Oct 2010 KernelVersion: 2.6.37 Contact: "Corentin Chary" @@ -9,21 +9,21 @@ Description: * 1 -> High Performance Mode * 2 -> Power Saving Mode -What: /sys/devices/platform/eeepc-wmi/camera +What: /sys/devices/platform//camera Date: Jan 2010 KernelVersion: 2.6.39 Contact: "Corentin Chary" Description: Control the camera. 1 means on, 0 means off. -What: /sys/devices/platform/eeepc-wmi/cardr +What: /sys/devices/platform//cardr Date: Jan 2010 KernelVersion: 2.6.39 Contact: "Corentin Chary" Description: Control the card reader. 1 means on, 0 means off. -What: /sys/devices/platform/eeepc-wmi/touchpad +What: /sys/devices/platform//touchpad Date: Jan 2010 KernelVersion: 2.6.39 Contact: "Corentin Chary" diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b039d6a93f1..7fde7ba01ab 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -451,10 +451,11 @@ config EEEPC_LAPTOP Bluetooth, backlight and allows powering on/off some other devices. - If you have an Eee PC laptop, say Y or M here. + If you have an Eee PC laptop, say Y or M here. If this driver + doesn't work on your Eee PC, try eeepc-wmi instead. -config EEEPC_WMI - tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)" +config ASUS_WMI + tristate "ASUS WMI Driver (EXPERIMENTAL)" depends on ACPI_WMI depends on INPUT depends on EXPERIMENTAL @@ -464,10 +465,23 @@ config EEEPC_WMI select LEDS_CLASS select NEW_LEDS ---help--- - Say Y here if you want to support WMI-based hotkeys on Eee PC laptops. + Say Y here if you have a WMI aware Asus laptop (like Eee PCs). To compile this driver as a module, choose M here: the module will - be called eeepc-wmi. + be called asus-wmi. + +config EEEPC_WMI + tristate "Eee PC WMI Driver (EXPERIMENTAL)" + depends on ASUS_WMI + ---help--- + This is a driver for newer Eee PC laptops. It adds extra features + like wireless radio and bluetooth control, leds, hotkeys, backlight... + + For more informations, see + + + If you have an ACPI-WMI compatible Eee PC laptop (>= 1000), say Y or M + here. config ACPI_WMI tristate "WMI" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 443257617b2..f9c83f48c92 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -3,8 +3,9 @@ # x86 Platform-Specific Drivers # obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o +obj-$(CONFIG_ASUS_WMI) += asus-wmi.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o -obj-$(CONFIG_EEEPC_WMI) += asus-wmi.o eeepc-wmi.o +obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index d3997757e79..35e6bb6c266 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1,5 +1,5 @@ /* - * Eee PC WMI hotkey driver + * Asus PC WMI hotkey driver * * Copyright(C) 2010 Intel Corporation. * Copyright(C) 2010 Corentin Chary @@ -42,23 +42,23 @@ #include #include #include -#include #include #include -#define EEEPC_WMI_FILE "eeepc-wmi" +#include "asus-wmi.h" -MODULE_AUTHOR("Yong Wang "); -MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); +MODULE_AUTHOR("Corentin Chary , " + "Yong Wang "); +MODULE_DESCRIPTION("Asus Generic WMI Driver"); MODULE_LICENSE("GPL"); -#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ +#define to_platform_driver(drv) \ + (container_of((drv), struct platform_driver, driver)) -#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" -#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define to_asus_wmi_driver(pdrv) \ + (container_of((pdrv), struct asus_wmi_driver, platform_driver)) -MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); -MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); +#define ASUS_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" #define NOTIFY_BRNUP_MIN 0x11 #define NOTIFY_BRNUP_MAX 0x1f @@ -66,90 +66,55 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); #define NOTIFY_BRNDOWN_MAX 0x2e /* WMI Methods */ -#define EEEPC_WMI_METHODID_DSTS 0x53544344 -#define EEEPC_WMI_METHODID_DEVS 0x53564544 -#define EEEPC_WMI_METHODID_CFVS 0x53564643 +#define ASUS_WMI_METHODID_DSTS 0x53544344 +#define ASUS_WMI_METHODID_DEVS 0x53564544 +#define ASUS_WMI_METHODID_CFVS 0x53564643 /* Wireless */ -#define EEEPC_WMI_DEVID_WLAN 0x00010011 -#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 -#define EEEPC_WMI_DEVID_WIMAX 0x00010017 -#define EEEPC_WMI_DEVID_WWAN3G 0x00010019 +#define ASUS_WMI_DEVID_WLAN 0x00010011 +#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 +#define ASUS_WMI_DEVID_WIMAX 0x00010017 +#define ASUS_WMI_DEVID_WWAN3G 0x00010019 /* Backlight and Brightness */ -#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 -#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 +#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 +#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 /* Misc */ -#define EEEPC_WMI_DEVID_CAMERA 0x00060013 +#define ASUS_WMI_DEVID_CAMERA 0x00060013 /* Storage */ -#define EEEPC_WMI_DEVID_CARDREADER 0x00080013 +#define ASUS_WMI_DEVID_CARDREADER 0x00080013 /* Input */ -#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 -#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 +#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 +#define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 /* DSTS masks */ -#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 -#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 -#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF -#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 - -static bool hotplug_wireless; - -module_param(hotplug_wireless, bool, 0444); -MODULE_PARM_DESC(hotplug_wireless, - "Enable hotplug for wireless device. " - "If your laptop needs that, please report to " - "acpi4asus-user@lists.sourceforge.net."); - -static const struct key_entry eeepc_wmi_keymap[] = { - /* Sleep already handled via generic ACPI code */ - { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, - { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, - { KE_KEY, 0x30, { KEY_VOLUMEUP } }, - { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, - { KE_KEY, 0x32, { KEY_MUTE } }, - { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ - { KE_KEY, 0x5d, { KEY_WLAN } }, - { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ - { KE_KEY, 0x82, { KEY_CAMERA } }, - { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, - { KE_KEY, 0x88, { KEY_WLAN } }, - { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, - { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ - { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ - { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, - { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, - { KE_KEY, 0xec, { KEY_CAMERA_UP } }, - { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, - { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, - { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, - { KE_END, 0}, -}; +#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 +#define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 +#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF +#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 struct bios_args { - u32 dev_id; - u32 ctrl_param; + u32 dev_id; + u32 ctrl_param; }; /* - * eeepc-wmi/ - debugfs root directory + * / - 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 asus_wmi_debug { struct dentry *root; u32 dev_id; u32 ctrl_param; }; -struct eeepc_wmi { - bool hotplug_wireless; - +struct asus_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; struct platform_device *platform_device; @@ -170,59 +135,61 @@ struct eeepc_wmi { struct workqueue_struct *hotplug_workqueue; struct work_struct hotplug_work; - struct eeepc_wmi_debug debug; + struct asus_wmi_debug debug; + + struct asus_wmi_driver *driver; }; -static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) +static int asus_wmi_input_init(struct asus_wmi *asus) { int err; - eeepc->inputdev = input_allocate_device(); - if (!eeepc->inputdev) + asus->inputdev = input_allocate_device(); + if (!asus->inputdev) return -ENOMEM; - eeepc->inputdev->name = "Eee PC WMI hotkeys"; - eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; - eeepc->inputdev->id.bustype = BUS_HOST; - eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; + asus->inputdev->name = asus->driver->input_phys; + asus->inputdev->phys = asus->driver->input_name; + asus->inputdev->id.bustype = BUS_HOST; + asus->inputdev->dev.parent = &asus->platform_device->dev; - err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); + err = sparse_keymap_setup(asus->inputdev, asus->driver->keymap, NULL); if (err) goto err_free_dev; - err = input_register_device(eeepc->inputdev); + err = input_register_device(asus->inputdev); if (err) goto err_free_keymap; return 0; err_free_keymap: - sparse_keymap_free(eeepc->inputdev); + sparse_keymap_free(asus->inputdev); err_free_dev: - input_free_device(eeepc->inputdev); + input_free_device(asus->inputdev); return err; } -static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_input_exit(struct asus_wmi *asus) { - if (eeepc->inputdev) { - sparse_keymap_free(eeepc->inputdev); - input_unregister_device(eeepc->inputdev); + if (asus->inputdev) { + sparse_keymap_free(asus->inputdev); + input_unregister_device(asus->inputdev); } - eeepc->inputdev = NULL; + asus->inputdev = NULL; } -static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) +static acpi_status asus_wmi_get_devstate(u32 dev_id, u32 *retval) { - struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; + struct acpi_buffer input = { (acpi_size) sizeof(u32), &dev_id }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; u32 tmp; - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_DSTS, + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, + 1, ASUS_WMI_METHODID_DSTS, &input, &output); if (ACPI_FAILURE(status)) @@ -230,7 +197,7 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) - tmp = (u32)obj->integer.value; + tmp = (u32) obj->integer.value; else tmp = 0; @@ -243,27 +210,27 @@ static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) } -static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, - u32 *retval) +static acpi_status asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, + u32 *retval) { struct bios_args args = { .dev_id = dev_id, .ctrl_param = ctrl_param, }; - struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; acpi_status status; if (!retval) { - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, - EEEPC_WMI_METHODID_DEVS, + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, + ASUS_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, + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, + ASUS_WMI_METHODID_DEVS, &input, &output); if (ACPI_FAILURE(status)) @@ -271,7 +238,7 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) - tmp = (u32)obj->integer.value; + tmp = (u32) obj->integer.value; else tmp = 0; @@ -284,25 +251,25 @@ static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, } /* Helper for special devices with magic return codes */ -static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask) +static int asus_wmi_get_devstate_bits(u32 dev_id, u32 mask) { u32 retval = 0; acpi_status status; - status = eeepc_wmi_get_devstate(dev_id, &retval); + status = asus_wmi_get_devstate(dev_id, &retval); if (ACPI_FAILURE(status)) return -EINVAL; - if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) + if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; return retval & mask; } -static int eeepc_wmi_get_devstate_simple(u32 dev_id) +static int asus_wmi_get_devstate_simple(u32 dev_id) { - return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT); + return asus_wmi_get_devstate_bits(dev_id, ASUS_WMI_DSTS_STATUS_BIT); } /* @@ -311,93 +278,92 @@ static int eeepc_wmi_get_devstate_simple(u32 dev_id) /* * 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 + * subsystem asks, we avoid messing with the Asus ACPI stuff during a * potentially bad time, such as a timer interrupt. */ static void tpd_led_update(struct work_struct *work) { int ctrl_param; - struct eeepc_wmi *eeepc; + struct asus_wmi *asus; - eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); + asus = container_of(work, struct asus_wmi, tpd_led_work); - ctrl_param = eeepc->tpd_led_wk; - eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); + ctrl_param = asus->tpd_led_wk; + asus_wmi_set_devstate(ASUS_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); } static void tpd_led_set(struct led_classdev *led_cdev, enum led_brightness value) { - struct eeepc_wmi *eeepc; + struct asus_wmi *asus; - eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + asus = container_of(led_cdev, struct asus_wmi, tpd_led); - eeepc->tpd_led_wk = !!value; - queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); + asus->tpd_led_wk = !!value; + queue_work(asus->led_workqueue, &asus->tpd_led_work); } -static int read_tpd_led_state(struct eeepc_wmi *eeepc) +static int read_tpd_led_state(struct asus_wmi *asus) { - return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED); + return asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_TOUCHPAD_LED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) { - struct eeepc_wmi *eeepc; + struct asus_wmi *asus; - eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); + asus = container_of(led_cdev, struct asus_wmi, tpd_led); - return read_tpd_led_state(eeepc); + return read_tpd_led_state(asus); } -static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) +static int asus_wmi_led_init(struct asus_wmi *asus) { int rv; - if (read_tpd_led_state(eeepc) < 0) + if (read_tpd_led_state(asus) < 0) return 0; - eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); - if (!eeepc->led_workqueue) + asus->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!asus->led_workqueue) return -ENOMEM; - INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); + INIT_WORK(&asus->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; + asus->tpd_led.name = "asus::touchpad"; + asus->tpd_led.brightness_set = tpd_led_set; + asus->tpd_led.brightness_get = tpd_led_get; + asus->tpd_led.max_brightness = 1; - rv = led_classdev_register(&eeepc->platform_device->dev, - &eeepc->tpd_led); + rv = led_classdev_register(&asus->platform_device->dev, &asus->tpd_led); if (rv) { - destroy_workqueue(eeepc->led_workqueue); + destroy_workqueue(asus->led_workqueue); return rv; } return 0; } -static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_led_exit(struct asus_wmi *asus) { - if (eeepc->tpd_led.dev) - led_classdev_unregister(&eeepc->tpd_led); - if (eeepc->led_workqueue) - destroy_workqueue(eeepc->led_workqueue); + if (asus->tpd_led.dev) + led_classdev_unregister(&asus->tpd_led); + if (asus->led_workqueue) + destroy_workqueue(asus->led_workqueue); } /* * PCI hotplug (for wlan rfkill) */ -static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) +static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus) { - int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); + int result = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); if (result < 0) return false; return !result; } -static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) +static void asus_rfkill_hotplug(struct asus_wmi *asus) { struct pci_dev *dev; struct pci_bus *bus; @@ -405,16 +371,16 @@ static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) bool absent; u32 l; - mutex_lock(&eeepc->wmi_lock); - blocked = eeepc_wlan_rfkill_blocked(eeepc); - mutex_unlock(&eeepc->wmi_lock); + mutex_lock(&asus->wmi_lock); + blocked = asus_wlan_rfkill_blocked(asus); + mutex_unlock(&asus->wmi_lock); - mutex_lock(&eeepc->hotplug_lock); + mutex_lock(&asus->hotplug_lock); - if (eeepc->wlan_rfkill) - rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); + if (asus->wlan_rfkill) + rfkill_set_sw_state(asus->wlan_rfkill, blocked); - if (eeepc->hotplug_slot) { + if (asus->hotplug_slot) { bus = pci_find_bus(0, 1); if (!bus) { pr_warning("Unable to find PCI bus 1?\n"); @@ -429,11 +395,11 @@ static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) if (blocked != absent) { pr_warning("BIOS says wireless lan is %s, " - "but the pci device is %s\n", - blocked ? "blocked" : "unblocked", - absent ? "absent" : "present"); + "but the pci device is %s\n", + blocked ? "blocked" : "unblocked", + absent ? "absent" : "present"); pr_warning("skipped wireless hotplug as probably " - "inappropriate for this model\n"); + "inappropriate for this model\n"); goto out_unlock; } @@ -460,28 +426,27 @@ static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) } out_unlock: - mutex_unlock(&eeepc->hotplug_lock); + mutex_unlock(&asus->hotplug_lock); } -static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +static void asus_rfkill_notify(acpi_handle handle, u32 event, void *data) { - struct eeepc_wmi *eeepc = data; + struct asus_wmi *asus = data; if (event != ACPI_NOTIFY_BUS_CHECK) return; /* - * We can't call directly eeepc_rfkill_hotplug because most + * We can't call directly asus_rfkill_hotplug because most * of the time WMBC is still being executed and not reetrant. * There is currently no way to tell ACPICA that we want this - * method to be serialized, we schedule a eeepc_rfkill_hotplug + * method to be serialized, we schedule a asus_rfkill_hotplug * call later, in a safer context. */ - queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work); + queue_work(asus->hotplug_workqueue, &asus->hotplug_work); } -static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, - char *node) +static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node) { acpi_status status; acpi_handle handle; @@ -491,8 +456,7 @@ static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, if (ACPI_SUCCESS(status)) { status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, - eeepc_rfkill_notify, - eeepc); + asus_rfkill_notify, asus); if (ACPI_FAILURE(status)) pr_warning("Failed to register notify on %s\n", node); } else @@ -501,8 +465,7 @@ static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, return 0; } -static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, - char *node) +static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) { acpi_status status = AE_OK; acpi_handle handle; @@ -511,18 +474,18 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, if (ACPI_SUCCESS(status)) { status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - eeepc_rfkill_notify); + ACPI_SYSTEM_NOTIFY, + asus_rfkill_notify); if (ACPI_FAILURE(status)) pr_err("Error removing rfkill notify handler %s\n", - node); + node); } } -static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, - u8 *value) +static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, + u8 *value) { - int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); + int result = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); if (result < 0) return result; @@ -531,27 +494,27 @@ static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, return 0; } -static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) +static void asus_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) { kfree(hotplug_slot->info); kfree(hotplug_slot); } -static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { +static struct hotplug_slot_ops asus_hotplug_slot_ops = { .owner = THIS_MODULE, - .get_adapter_status = eeepc_get_adapter_status, - .get_power_status = eeepc_get_adapter_status, + .get_adapter_status = asus_get_adapter_status, + .get_power_status = asus_get_adapter_status, }; -static void eeepc_hotplug_work(struct work_struct *work) +static void asus_hotplug_work(struct work_struct *work) { - struct eeepc_wmi *eeepc; + struct asus_wmi *asus; - eeepc = container_of(work, struct eeepc_wmi, hotplug_work); - eeepc_rfkill_hotplug(eeepc); + asus = container_of(work, struct asus_wmi, hotplug_work); + asus_rfkill_hotplug(asus); } -static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) +static int asus_setup_pci_hotplug(struct asus_wmi *asus) { int ret = -ENOMEM; struct pci_bus *bus = pci_find_bus(0, 1); @@ -561,29 +524,29 @@ static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) return -ENODEV; } - eeepc->hotplug_workqueue = - create_singlethread_workqueue("hotplug_workqueue"); - if (!eeepc->hotplug_workqueue) + asus->hotplug_workqueue = + create_singlethread_workqueue("hotplug_workqueue"); + if (!asus->hotplug_workqueue) goto error_workqueue; - INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work); + INIT_WORK(&asus->hotplug_work, asus_hotplug_work); - eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); - if (!eeepc->hotplug_slot) + asus->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); + if (!asus->hotplug_slot) goto error_slot; - eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), - GFP_KERNEL); - if (!eeepc->hotplug_slot->info) + asus->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), + GFP_KERNEL); + if (!asus->hotplug_slot->info) goto error_info; - eeepc->hotplug_slot->private = eeepc; - eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; - eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; - eeepc_get_adapter_status(eeepc->hotplug_slot, - &eeepc->hotplug_slot->info->adapter_status); + asus->hotplug_slot->private = asus; + asus->hotplug_slot->release = &asus_cleanup_pci_hotplug; + asus->hotplug_slot->ops = &asus_hotplug_slot_ops; + asus_get_adapter_status(asus->hotplug_slot, + &asus->hotplug_slot->info->adapter_status); - ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); + ret = pci_hp_register(asus->hotplug_slot, bus, 0, "asus-wifi"); if (ret) { pr_err("Unable to register hotplug slot - %d\n", ret); goto error_register; @@ -592,12 +555,12 @@ static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) return 0; error_register: - kfree(eeepc->hotplug_slot->info); + kfree(asus->hotplug_slot->info); error_info: - kfree(eeepc->hotplug_slot); - eeepc->hotplug_slot = NULL; + kfree(asus->hotplug_slot); + asus->hotplug_slot = NULL; error_slot: - destroy_workqueue(eeepc->hotplug_workqueue); + destroy_workqueue(asus->hotplug_workqueue); error_workqueue: return ret; } @@ -605,13 +568,13 @@ error_workqueue: /* * Rfkill devices */ -static int eeepc_rfkill_set(void *data, bool blocked) +static int asus_rfkill_set(void *data, bool blocked) { int dev_id = (unsigned long)data; u32 ctrl_param = !blocked; acpi_status status; - status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); + status = asus_wmi_set_devstate(dev_id, ctrl_param, NULL); if (ACPI_FAILURE(status)) return -EIO; @@ -619,68 +582,67 @@ static int eeepc_rfkill_set(void *data, bool blocked) return 0; } -static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) +static void asus_rfkill_query(struct rfkill *rfkill, void *data) { int dev_id = (unsigned long)data; int result; - result = eeepc_wmi_get_devstate_simple(dev_id); + result = asus_wmi_get_devstate_simple(dev_id); if (result < 0) - return ; + return; rfkill_set_sw_state(rfkill, !result); } -static int eeepc_rfkill_wlan_set(void *data, bool blocked) +static int asus_rfkill_wlan_set(void *data, bool blocked) { - struct eeepc_wmi *eeepc = data; + struct asus_wmi *asus = data; int ret; /* * This handler is enabled only if hotplug is enabled. - * In this case, the eeepc_wmi_set_devstate() will + * In this case, the asus_wmi_set_devstate() will * trigger a wmi notification and we need to wait * this call to finish before being able to call * any wmi method */ - mutex_lock(&eeepc->wmi_lock); - ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked); - mutex_unlock(&eeepc->wmi_lock); + mutex_lock(&asus->wmi_lock); + ret = asus_rfkill_set((void *)(long)ASUS_WMI_DEVID_WLAN, blocked); + mutex_unlock(&asus->wmi_lock); return ret; } -static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data) +static void asus_rfkill_wlan_query(struct rfkill *rfkill, void *data) { - eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN); + asus_rfkill_query(rfkill, (void *)(long)ASUS_WMI_DEVID_WLAN); } -static const struct rfkill_ops eeepc_rfkill_wlan_ops = { - .set_block = eeepc_rfkill_wlan_set, - .query = eeepc_rfkill_wlan_query, +static const struct rfkill_ops asus_rfkill_wlan_ops = { + .set_block = asus_rfkill_wlan_set, + .query = asus_rfkill_wlan_query, }; -static const struct rfkill_ops eeepc_rfkill_ops = { - .set_block = eeepc_rfkill_set, - .query = eeepc_rfkill_query, +static const struct rfkill_ops asus_rfkill_ops = { + .set_block = asus_rfkill_set, + .query = asus_rfkill_query, }; -static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, - struct rfkill **rfkill, - const char *name, - enum rfkill_type type, int dev_id) +static int asus_new_rfkill(struct asus_wmi *asus, + struct rfkill **rfkill, + const char *name, enum rfkill_type type, int dev_id) { - int result = eeepc_wmi_get_devstate_simple(dev_id); + int result = asus_wmi_get_devstate_simple(dev_id); if (result < 0) return result; - if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, - &eeepc_rfkill_wlan_ops, eeepc); + if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless) + *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, + &asus_rfkill_wlan_ops, asus); else - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, - &eeepc_rfkill_ops, (void *)(long)dev_id); + *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, + &asus_rfkill_ops, (void *)(long)dev_id); if (!*rfkill) return -EINVAL; @@ -695,82 +657,82 @@ static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, return 0; } -static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_rfkill_exit(struct asus_wmi *asus) { - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); - if (eeepc->wlan_rfkill) { - rfkill_unregister(eeepc->wlan_rfkill); - rfkill_destroy(eeepc->wlan_rfkill); - eeepc->wlan_rfkill = NULL; + asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5"); + asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6"); + asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); + if (asus->wlan_rfkill) { + rfkill_unregister(asus->wlan_rfkill); + rfkill_destroy(asus->wlan_rfkill); + asus->wlan_rfkill = NULL; } /* * Refresh pci hotplug in case the rfkill state was changed after - * eeepc_unregister_rfkill_notifier() + * asus_unregister_rfkill_notifier() */ - eeepc_rfkill_hotplug(eeepc); - if (eeepc->hotplug_slot) - pci_hp_deregister(eeepc->hotplug_slot); - if (eeepc->hotplug_workqueue) - destroy_workqueue(eeepc->hotplug_workqueue); + asus_rfkill_hotplug(asus); + if (asus->hotplug_slot) + pci_hp_deregister(asus->hotplug_slot); + if (asus->hotplug_workqueue) + destroy_workqueue(asus->hotplug_workqueue); - if (eeepc->bluetooth_rfkill) { - rfkill_unregister(eeepc->bluetooth_rfkill); - rfkill_destroy(eeepc->bluetooth_rfkill); - eeepc->bluetooth_rfkill = NULL; + if (asus->bluetooth_rfkill) { + rfkill_unregister(asus->bluetooth_rfkill); + rfkill_destroy(asus->bluetooth_rfkill); + asus->bluetooth_rfkill = NULL; } - if (eeepc->wimax_rfkill) { - rfkill_unregister(eeepc->wimax_rfkill); - rfkill_destroy(eeepc->wimax_rfkill); - eeepc->wimax_rfkill = NULL; + if (asus->wimax_rfkill) { + rfkill_unregister(asus->wimax_rfkill); + rfkill_destroy(asus->wimax_rfkill); + asus->wimax_rfkill = NULL; } - if (eeepc->wwan3g_rfkill) { - rfkill_unregister(eeepc->wwan3g_rfkill); - rfkill_destroy(eeepc->wwan3g_rfkill); - eeepc->wwan3g_rfkill = NULL; + if (asus->wwan3g_rfkill) { + rfkill_unregister(asus->wwan3g_rfkill); + rfkill_destroy(asus->wwan3g_rfkill); + asus->wwan3g_rfkill = NULL; } } -static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) +static int asus_wmi_rfkill_init(struct asus_wmi *asus) { int result = 0; - mutex_init(&eeepc->hotplug_lock); - mutex_init(&eeepc->wmi_lock); + mutex_init(&asus->hotplug_lock); + mutex_init(&asus->wmi_lock); - result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, - "eeepc-wlan", RFKILL_TYPE_WLAN, - EEEPC_WMI_DEVID_WLAN); + result = asus_new_rfkill(asus, &asus->wlan_rfkill, + "asus-wlan", RFKILL_TYPE_WLAN, + ASUS_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); + result = asus_new_rfkill(asus, &asus->bluetooth_rfkill, + "asus-bluetooth", RFKILL_TYPE_BLUETOOTH, + ASUS_WMI_DEVID_BLUETOOTH); if (result && result != -ENODEV) goto exit; - result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, - "eeepc-wimax", RFKILL_TYPE_WIMAX, - EEEPC_WMI_DEVID_WIMAX); + result = asus_new_rfkill(asus, &asus->wimax_rfkill, + "asus-wimax", RFKILL_TYPE_WIMAX, + ASUS_WMI_DEVID_WIMAX); if (result && result != -ENODEV) goto exit; - result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, - "eeepc-wwan3g", RFKILL_TYPE_WWAN, - EEEPC_WMI_DEVID_WWAN3G); + result = asus_new_rfkill(asus, &asus->wwan3g_rfkill, + "asus-wwan3g", RFKILL_TYPE_WWAN, + ASUS_WMI_DEVID_WWAN3G); if (result && result != -ENODEV) goto exit; - if (!eeepc->hotplug_wireless) + if (!asus->driver->hotplug_wireless) goto exit; - result = eeepc_setup_pci_hotplug(eeepc); + result = asus_setup_pci_hotplug(asus); /* * If we get -EBUSY then something else is handling the PCI hotplug - * don't fail in this case @@ -778,18 +740,18 @@ static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) if (result == -EBUSY) result = 0; - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); + asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P5"); + asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P6"); + asus_register_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); /* * Refresh pci hotplug in case the rfkill state was changed during * setup. */ - eeepc_rfkill_hotplug(eeepc); + asus_rfkill_hotplug(asus); exit: if (result && result != -ENODEV) - eeepc_wmi_rfkill_exit(eeepc); + asus_wmi_rfkill_exit(asus); if (result == -ENODEV) result = 0; @@ -802,7 +764,7 @@ exit: */ static int read_backlight_power(void) { - int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT); + int ret = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_BACKLIGHT); if (ret < 0) return ret; @@ -815,12 +777,12 @@ static int read_brightness(struct backlight_device *bd) u32 retval; acpi_status status; - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval); + status = asus_wmi_get_devstate(ASUS_WMI_DEVID_BRIGHTNESS, &retval); if (ACPI_FAILURE(status)) return -EIO; else - return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK; + return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } static int update_bl_status(struct backlight_device *bd) @@ -831,8 +793,8 @@ static int update_bl_status(struct backlight_device *bd) ctrl_param = bd->props.brightness; - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, - ctrl_param, NULL); + status = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, + ctrl_param, NULL); if (ACPI_FAILURE(status)) return -EIO; @@ -840,8 +802,8 @@ static int update_bl_status(struct backlight_device *bd) power = read_backlight_power(); if (power != -ENODEV && bd->props.power != power) { ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, - ctrl_param, NULL); + status = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); if (ACPI_FAILURE(status)) return -EIO; @@ -849,14 +811,14 @@ static int update_bl_status(struct backlight_device *bd) return 0; } -static const struct backlight_ops eeepc_wmi_bl_ops = { +static const struct backlight_ops asus_wmi_bl_ops = { .get_brightness = read_brightness, .update_status = update_bl_status, }; -static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) +static int asus_wmi_backlight_notify(struct asus_wmi *asus, int code) { - struct backlight_device *bd = eeepc->backlight_device; + struct backlight_device *bd = asus->backlight_device; int old = bd->props.brightness; int new = old; @@ -872,15 +834,15 @@ static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) return old; } -static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) +static int asus_wmi_backlight_init(struct asus_wmi *asus) { struct backlight_device *bd; struct backlight_properties props; int max; int power; - max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS, - EEEPC_WMI_DSTS_MAX_BRIGTH_MASK); + max = asus_wmi_get_devstate_bits(ASUS_WMI_DEVID_BRIGHTNESS, + ASUS_WMI_DSTS_MAX_BRIGTH_MASK); power = read_backlight_power(); if (max < 0 && power < 0) { @@ -899,15 +861,15 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = max; - bd = backlight_device_register(EEEPC_WMI_FILE, - &eeepc->platform_device->dev, eeepc, - &eeepc_wmi_bl_ops, &props); + bd = backlight_device_register(asus->driver->name, + &asus->platform_device->dev, asus, + &asus_wmi_bl_ops, &props); if (IS_ERR(bd)) { pr_err("Could not register backlight device\n"); return PTR_ERR(bd); } - eeepc->backlight_device = bd; + asus->backlight_device = bd; bd->props.brightness = read_brightness(bd); bd->props.power = power; @@ -916,17 +878,17 @@ static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) return 0; } -static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_backlight_exit(struct asus_wmi *asus) { - if (eeepc->backlight_device) - backlight_device_unregister(eeepc->backlight_device); + if (asus->backlight_device) + backlight_device_unregister(asus->backlight_device); - eeepc->backlight_device = NULL; + asus->backlight_device = NULL; } -static void eeepc_wmi_notify(u32 value, void *context) +static void asus_wmi_notify(u32 value, void *context) { - struct eeepc_wmi *eeepc = context; + struct asus_wmi *asus = context; struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; acpi_status status; @@ -953,11 +915,10 @@ static void eeepc_wmi_notify(u32 value, void *context) if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { if (!acpi_video_backlight_support()) - eeepc_wmi_backlight_notify(eeepc, orig_code); + asus_wmi_backlight_notify(asus, orig_code); } - if (!sparse_keymap_report_event(eeepc->inputdev, - code, 1, true)) + if (!sparse_keymap_report_event(asus->inputdev, code, 1, true)) pr_info("Unknown key %x pressed\n", code); } @@ -982,12 +943,12 @@ static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) u32 retval; int rv, value; - value = eeepc_wmi_get_devstate_simple(devid); - if (value == -ENODEV) /* Check device presence */ + value = asus_wmi_get_devstate_simple(devid); + if (value == -ENODEV) /* Check device presence */ return value; rv = parse_arg(buf, count, &value); - status = eeepc_wmi_set_devstate(devid, value, &retval); + status = asus_wmi_set_devstate(devid, value, &retval); if (ACPI_FAILURE(status)) return -EIO; @@ -996,7 +957,7 @@ static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) static ssize_t show_sys_wmi(int devid, char *buf) { - int value = eeepc_wmi_get_devstate_simple(devid); + int value = asus_wmi_get_devstate_simple(devid); if (value < 0) return value; @@ -1004,7 +965,7 @@ static ssize_t show_sys_wmi(int devid, char *buf) return sprintf(buf, "%d\n", value); } -#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ +#define ASUS_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ static ssize_t show_##_name(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ @@ -1025,15 +986,15 @@ static ssize_t show_sys_wmi(int devid, char *buf) .store = store_##_name, \ } -EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD); -EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); -EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); +ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD); +ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA); +ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; - struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; + struct acpi_buffer input = { (acpi_size) sizeof(value), &value }; acpi_status status; if (!count || sscanf(buf, "%i", &value) != 1) @@ -1041,8 +1002,8 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, if (value < 0 || value > 2) return -EINVAL; - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, - 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, + 1, ASUS_WMI_METHODID_CFVS, &input, NULL); if (ACPI_FAILURE(status)) return -EIO; @@ -1060,37 +1021,36 @@ static struct attribute *platform_attributes[] = { NULL }; -static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, - struct attribute *attr, - int idx) +static mode_t asus_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) { bool supported = true; int devid = -1; if (attr == &dev_attr_camera.attr) - devid = EEEPC_WMI_DEVID_CAMERA; + devid = ASUS_WMI_DEVID_CAMERA; else if (attr == &dev_attr_cardr.attr) - devid = EEEPC_WMI_DEVID_CARDREADER; + devid = ASUS_WMI_DEVID_CARDREADER; else if (attr == &dev_attr_touchpad.attr) - devid = EEEPC_WMI_DEVID_TOUCHPAD; + devid = ASUS_WMI_DEVID_TOUCHPAD; if (devid != -1) - supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; + supported = asus_wmi_get_devstate_simple(devid) != -ENODEV; return supported ? attr->mode : 0; } static struct attribute_group platform_attribute_group = { - .is_visible = eeepc_sysfs_is_visible, - .attrs = platform_attributes + .is_visible = asus_sysfs_is_visible, + .attrs = platform_attributes }; -static void eeepc_wmi_sysfs_exit(struct platform_device *device) +static void asus_wmi_sysfs_exit(struct platform_device *device) { sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); } -static int eeepc_wmi_sysfs_init(struct platform_device *device) +static int asus_wmi_sysfs_init(struct platform_device *device) { return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); } @@ -1098,111 +1058,111 @@ static int eeepc_wmi_sysfs_init(struct platform_device *device) /* * Platform device */ -static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) +static int __init asus_wmi_platform_init(struct asus_wmi *asus) { - return eeepc_wmi_sysfs_init(eeepc->platform_device); + return asus_wmi_sysfs_init(asus->platform_device); } -static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_platform_exit(struct asus_wmi *asus) { - eeepc_wmi_sysfs_exit(eeepc->platform_device); + asus_wmi_sysfs_exit(asus->platform_device); } /* * debugfs */ -struct eeepc_wmi_debugfs_node { - struct eeepc_wmi *eeepc; +struct asus_wmi_debugfs_node { + struct asus_wmi *asus; char *name; - int (*show)(struct seq_file *m, void *data); + int (*show) (struct seq_file *m, void *data); }; static int show_dsts(struct seq_file *m, void *data) { - struct eeepc_wmi *eeepc = m->private; + struct asus_wmi *asus = m->private; acpi_status status; u32 retval = -1; - status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); + status = asus_wmi_get_devstate(asus->debug.dev_id, &retval); if (ACPI_FAILURE(status)) return -EIO; - seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); + seq_printf(m, "DSTS(%x) = %x\n", asus->debug.dev_id, retval); return 0; } static int show_devs(struct seq_file *m, void *data) { - struct eeepc_wmi *eeepc = m->private; + struct asus_wmi *asus = m->private; acpi_status status; u32 retval = -1; - status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, - eeepc->debug.ctrl_param, &retval); + status = asus_wmi_set_devstate(asus->debug.dev_id, + asus->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); + seq_printf(m, "DEVS(%x, %x) = %x\n", asus->debug.dev_id, + asus->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 struct asus_wmi_debugfs_node asus_wmi_debug_files[] = { + {NULL, "devs", show_devs}, + {NULL, "dsts", show_dsts}, }; -static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) +static int asus_wmi_debugfs_open(struct inode *inode, struct file *file) { - struct eeepc_wmi_debugfs_node *node = inode->i_private; + struct asus_wmi_debugfs_node *node = inode->i_private; - return single_open(file, node->show, node->eeepc); + return single_open(file, node->show, node->asus); } -static const struct file_operations eeepc_wmi_debugfs_io_ops = { +static const struct file_operations asus_wmi_debugfs_io_ops = { .owner = THIS_MODULE, - .open = eeepc_wmi_debugfs_open, + .open = asus_wmi_debugfs_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) +static void asus_wmi_debugfs_exit(struct asus_wmi *asus) { - debugfs_remove_recursive(eeepc->debug.root); + debugfs_remove_recursive(asus->debug.root); } -static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) +static int asus_wmi_debugfs_init(struct asus_wmi *asus) { struct dentry *dent; int i; - eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); - if (!eeepc->debug.root) { + asus->debug.root = debugfs_create_dir(asus->driver->name, NULL); + if (!asus->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); + dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, + asus->debug.root, &asus->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); + dent = debugfs_create_x32("ctrl_param", S_IRUGO | S_IWUSR, + asus->debug.root, &asus->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]; + for (i = 0; i < ARRAY_SIZE(asus_wmi_debug_files); i++) { + struct asus_wmi_debugfs_node *node = &asus_wmi_debug_files[i]; - node->eeepc = eeepc; + node->asus = asus; dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, - eeepc->debug.root, node, - &eeepc_wmi_debugfs_io_ops); + asus->debug.root, node, + &asus_wmi_debugfs_io_ops); if (!dent) { pr_err("failed to create debug file: %s\n", node->name); goto error_debugfs; @@ -1212,131 +1172,112 @@ static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) return 0; error_debugfs: - eeepc_wmi_debugfs_exit(eeepc); + asus_wmi_debugfs_exit(asus); return -ENOMEM; } /* * WMI Driver */ -static void eeepc_dmi_check(struct eeepc_wmi *eeepc) +static int asus_wmi_add(struct platform_device *pdev) { - const char *model; - - model = dmi_get_system_info(DMI_PRODUCT_NAME); - if (!model) - return; - - /* - * Whitelist for wlan hotplug - * - * Eeepc 1000H needs the current hotplug code to handle - * Fn+F2 correctly. We may add other Eeepc here later, but - * it seems that most of the laptops supported by eeepc-wmi - * don't need to be on this list - */ - if (strcmp(model, "1000H") == 0) { - eeepc->hotplug_wireless = true; - pr_info("wlan hotplug enabled\n"); - } -} - -static int __init eeepc_wmi_add(struct platform_device *pdev) -{ - struct eeepc_wmi *eeepc; + struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); + struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv); + struct asus_wmi *asus; acpi_status status; int err; - eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); - if (!eeepc) + asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL); + if (!asus) return -ENOMEM; - eeepc->platform_device = pdev; - platform_set_drvdata(eeepc->platform_device, eeepc); + asus->driver = wdrv; + asus->platform_device = pdev; + wdrv->platform_device = pdev; + platform_set_drvdata(asus->platform_device, asus); - eeepc->hotplug_wireless = hotplug_wireless; - eeepc_dmi_check(eeepc); + if (wdrv->quirks) + wdrv->quirks(asus->driver); - err = eeepc_wmi_platform_init(eeepc); + err = asus_wmi_platform_init(asus); if (err) goto fail_platform; - err = eeepc_wmi_input_init(eeepc); + err = asus_wmi_input_init(asus); if (err) goto fail_input; - err = eeepc_wmi_led_init(eeepc); + err = asus_wmi_led_init(asus); if (err) goto fail_leds; - err = eeepc_wmi_rfkill_init(eeepc); + err = asus_wmi_rfkill_init(asus); if (err) goto fail_rfkill; if (!acpi_video_backlight_support()) { - err = eeepc_wmi_backlight_init(eeepc); + err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) 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); + status = wmi_install_notify_handler(asus->driver->event_guid, + asus_wmi_notify, asus); if (ACPI_FAILURE(status)) { - pr_err("Unable to register notify handler - %d\n", - status); + pr_err("Unable to register notify handler - %d\n", status); err = -ENODEV; goto fail_wmi_handler; } - err = eeepc_wmi_debugfs_init(eeepc); + err = asus_wmi_debugfs_init(asus); if (err) goto fail_debugfs; return 0; fail_debugfs: - wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); + wmi_remove_notify_handler(asus->driver->event_guid); fail_wmi_handler: - eeepc_wmi_backlight_exit(eeepc); + asus_wmi_backlight_exit(asus); fail_backlight: - eeepc_wmi_rfkill_exit(eeepc); + asus_wmi_rfkill_exit(asus); fail_rfkill: - eeepc_wmi_led_exit(eeepc); + asus_wmi_led_exit(asus); fail_leds: - eeepc_wmi_input_exit(eeepc); + asus_wmi_input_exit(asus); fail_input: - eeepc_wmi_platform_exit(eeepc); + asus_wmi_platform_exit(asus); fail_platform: - kfree(eeepc); + kfree(asus); return err; } -static int __exit eeepc_wmi_remove(struct platform_device *device) +static int asus_wmi_remove(struct platform_device *device) { - struct eeepc_wmi *eeepc; + struct asus_wmi *asus; - eeepc = platform_get_drvdata(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_rfkill_exit(eeepc); - eeepc_wmi_debugfs_exit(eeepc); - eeepc_wmi_platform_exit(eeepc); + asus = platform_get_drvdata(device); + wmi_remove_notify_handler(asus->driver->event_guid); + asus_wmi_backlight_exit(asus); + asus_wmi_input_exit(asus); + asus_wmi_led_exit(asus); + asus_wmi_rfkill_exit(asus); + asus_wmi_debugfs_exit(asus); + asus_wmi_platform_exit(asus); - kfree(eeepc); + kfree(asus); return 0; } /* * Platform driver - hibernate/resume callbacks */ -static int eeepc_hotk_thaw(struct device *device) +static int asus_hotk_thaw(struct device *device) { - struct eeepc_wmi *eeepc = dev_get_drvdata(device); + struct asus_wmi *asus = dev_get_drvdata(device); - if (eeepc->wlan_rfkill) { + if (asus->wlan_rfkill) { bool wlan; /* @@ -1344,111 +1285,118 @@ static int eeepc_hotk_thaw(struct device *device) * during suspend. Normally it restores it on resume, but * we should kick it ourselves in case hibernation is aborted. */ - wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); - eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL); + wlan = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); + asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL); } return 0; } -static int eeepc_hotk_restore(struct device *device) +static int asus_hotk_restore(struct device *device) { - struct eeepc_wmi *eeepc = dev_get_drvdata(device); + struct asus_wmi *asus = dev_get_drvdata(device); int bl; /* Refresh both wlan rfkill state and pci hotplug */ - if (eeepc->wlan_rfkill) - eeepc_rfkill_hotplug(eeepc); + if (asus->wlan_rfkill) + asus_rfkill_hotplug(asus); - if (eeepc->bluetooth_rfkill) { - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); - rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); + if (asus->bluetooth_rfkill) { + bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_BLUETOOTH); + rfkill_set_sw_state(asus->bluetooth_rfkill, bl); } - if (eeepc->wimax_rfkill) { - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX); - rfkill_set_sw_state(eeepc->wimax_rfkill, bl); + if (asus->wimax_rfkill) { + bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WIMAX); + rfkill_set_sw_state(asus->wimax_rfkill, bl); } - if (eeepc->wwan3g_rfkill) { - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); - rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); + if (asus->wwan3g_rfkill) { + bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WWAN3G); + rfkill_set_sw_state(asus->wwan3g_rfkill, bl); } return 0; } -static const struct dev_pm_ops eeepc_pm_ops = { - .thaw = eeepc_hotk_thaw, - .restore = eeepc_hotk_restore, +static const struct dev_pm_ops asus_pm_ops = { + .thaw = asus_hotk_thaw, + .restore = asus_hotk_restore, }; -static struct platform_driver platform_driver = { - .remove = __exit_p(eeepc_wmi_remove), - .driver = { - .name = EEEPC_WMI_FILE, - .owner = THIS_MODULE, - .pm = &eeepc_pm_ops, - }, -}; - -static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, - void *context, void **retval) +static int asus_wmi_probe(struct platform_device *pdev) { - pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); - *(bool *)context = true; - return AE_CTRL_TERMINATE; -} + struct platform_driver *pdrv = to_platform_driver(pdev->dev.driver); + struct asus_wmi_driver *wdrv = to_asus_wmi_driver(pdrv); + int ret; -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_probe(struct platform_device *pdev) -{ - if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || - !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { - pr_warning("No known WMI GUID found\n"); + if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) { + pr_warning("Management GUID not found\n"); 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"); + if (wdrv->event_guid && !wmi_has_guid(wdrv->event_guid)) { + pr_warning("Event GUID not found\n"); return -ENODEV; } - return eeepc_wmi_add(pdev); + if (wdrv->probe) { + ret = wdrv->probe(pdev); + if (ret) + return ret; + } + + return asus_wmi_add(pdev); } -static struct platform_device *platform_device; +static bool used; -static int __init eeepc_wmi_init(void) +int asus_wmi_register_driver(struct asus_wmi_driver *driver) { - platform_device = platform_create_bundle(&platform_driver, - eeepc_wmi_probe, + struct platform_driver *platform_driver; + struct platform_device *platform_device; + + if (used) + return -EBUSY; + + platform_driver = &driver->platform_driver; + platform_driver->remove = asus_wmi_remove; + platform_driver->driver.owner = driver->owner; + platform_driver->driver.name = driver->name; + platform_driver->driver.pm = &asus_pm_ops; + + platform_device = platform_create_bundle(platform_driver, + asus_wmi_probe, NULL, 0, NULL, 0); if (IS_ERR(platform_device)) return PTR_ERR(platform_device); + + used = true; + return 0; +} +EXPORT_SYMBOL_GPL(asus_wmi_register_driver); + +void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) +{ + platform_device_unregister(driver->platform_device); + platform_driver_unregister(&driver->platform_driver); + used = false; +} +EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver); + +static int __init asus_wmi_init(void) +{ + if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) { + pr_info("Asus Management GUID not found"); + return -ENODEV; + } + + pr_info("ASUS WMI generic driver loaded"); return 0; } -static void __exit eeepc_wmi_exit(void) +static void __exit asus_wmi_exit(void) { - platform_device_unregister(platform_device); - platform_driver_unregister(&platform_driver); + pr_info("ASUS WMI generic driver unloaded"); } -module_init(eeepc_wmi_init); -module_exit(eeepc_wmi_exit); +module_init(asus_wmi_init); +module_exit(asus_wmi_exit); diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h new file mode 100644 index 00000000000..c044522c876 --- /dev/null +++ b/drivers/platform/x86/asus-wmi.h @@ -0,0 +1,58 @@ +/* + * Asus PC WMI hotkey driver + * + * Copyright(C) 2010 Intel Corporation. + * Copyright(C) 2010-2011 Corentin Chary + * + * Portions based on wistron_btns.c: + * Copyright (C) 2005 Miloslav Trmac + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ASUS_WMI_H_ +#define _ASUS_WMI_H_ + +#include + +struct module; +struct key_entry; +struct asus_wmi; + +struct asus_wmi_driver { + bool hotplug_wireless; + + const char *name; + struct module *owner; + + const char *event_guid; + + const struct key_entry *keymap; + const char *input_name; + const char *input_phys; + + int (*probe) (struct platform_device *device); + void (*quirks) (struct asus_wmi_driver *driver); + + struct platform_driver platform_driver; + struct platform_device *platform_device; +}; + +int asus_wmi_register_driver(struct asus_wmi_driver *driver); +void asus_wmi_unregister_driver(struct asus_wmi_driver *driver); + +#endif /* !_ASUS_WMI_H_ */ diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c new file mode 100644 index 00000000000..3a060aefc5f --- /dev/null +++ b/drivers/platform/x86/eeepc-wmi.c @@ -0,0 +1,162 @@ +/* + * 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 + * Copyright (C) 2005 Bernhard Rosenkraenzer + * Copyright (C) 2005 Dmitry Torokhov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "asus-wmi.h" + +#define EEEPC_WMI_FILE "eeepc-wmi" + +MODULE_AUTHOR("Corentin Chary "); +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" + +MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); + +static bool hotplug_wireless; + +module_param(hotplug_wireless, bool, 0444); +MODULE_PARM_DESC(hotplug_wireless, + "Enable hotplug for wireless device. " + "If your laptop needs that, please report to " + "acpi4asus-user@lists.sourceforge.net."); + +static const struct key_entry eeepc_wmi_keymap[] = { + /* Sleep already handled via generic ACPI code */ + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ + { KE_KEY, 0x5d, { KEY_WLAN } }, + { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x88, { KEY_WLAN } }, + { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ + { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ + { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, + { KE_END, 0}, +}; + +static acpi_status 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 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 eeepc_wmi_probe(struct platform_device *pdev) +{ + 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 -EBUSY; + } + return 0; +} + +static void eeepc_dmi_check(struct asus_wmi_driver *driver) +{ + const char *model; + + model = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!model) + return; + + /* + * Whitelist for wlan hotplug + * + * Asus 1000H needs the current hotplug code to handle + * Fn+F2 correctly. We may add other Asus here later, but + * it seems that most of the laptops supported by asus-wmi + * don't need to be on this list + */ + if (strcmp(model, "1000H") == 0) { + driver->hotplug_wireless = true; + pr_info("wlan hotplug enabled\n"); + } +} + +static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) +{ + driver->hotplug_wireless = hotplug_wireless; + eeepc_dmi_check(driver); +} + +static struct asus_wmi_driver asus_wmi_driver = { + .name = EEEPC_WMI_FILE, + .owner = THIS_MODULE, + .event_guid = EEEPC_WMI_EVENT_GUID, + .keymap = eeepc_wmi_keymap, + .input_name = "Eee PC WMI hotkeys", + .input_phys = EEEPC_WMI_FILE "/input0", + .probe = eeepc_wmi_probe, + .quirks = eeepc_wmi_quirks, +}; + + +static int __init eeepc_wmi_init(void) +{ + return asus_wmi_register_driver(&asus_wmi_driver); +} + +static void __exit eeepc_wmi_exit(void) +{ + asus_wmi_unregister_driver(&asus_wmi_driver); +} + +module_init(eeepc_wmi_init); +module_exit(eeepc_wmi_exit); From 57ab7dae27fae6a492ec968dc543106685adcad5 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:32 +0100 Subject: [PATCH 46/81] asus-wmi: minor cleanups Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 34 ++++++++++++++++---------------- drivers/platform/x86/eeepc-wmi.c | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 35e6bb6c266..4c3f68a495d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2,7 +2,7 @@ * Asus PC WMI hotkey driver * * Copyright(C) 2010 Intel Corporation. - * Copyright(C) 2010 Corentin Chary + * Copyright(C) 2010-2011 Corentin Chary * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac @@ -87,7 +87,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DEVID_CARDREADER 0x00080013 /* Input */ -#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 +#define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 #define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 /* DSTS masks */ @@ -903,25 +903,25 @@ static void asus_wmi_notify(u32 value, void *context) obj = (union acpi_object *)response.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) { - code = obj->integer.value; - orig_code = code; + if (!obj || obj->type != ACPI_TYPE_INTEGER) + goto exit; - if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) - code = NOTIFY_BRNUP_MIN; - else if (code >= NOTIFY_BRNDOWN_MIN && - code <= NOTIFY_BRNDOWN_MAX) - code = NOTIFY_BRNDOWN_MIN; + code = obj->integer.value; + orig_code = code; - if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { - if (!acpi_video_backlight_support()) - asus_wmi_backlight_notify(asus, orig_code); - } + if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) + code = NOTIFY_BRNUP_MIN; + else if (code >= NOTIFY_BRNDOWN_MIN && + code <= NOTIFY_BRNDOWN_MAX) + code = NOTIFY_BRNDOWN_MIN; - if (!sparse_keymap_report_event(asus->inputdev, code, 1, true)) - pr_info("Unknown key %x pressed\n", code); - } + if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { + if (!acpi_video_backlight_support()) + asus_wmi_backlight_notify(asus, orig_code); + } else if (!sparse_keymap_report_event(asus->inputdev, code, 1, true)) + pr_info("Unknown key %x pressed\n", code); +exit: kfree(obj); } diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 3a060aefc5f..e69701fc422 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -2,7 +2,7 @@ * Eee PC WMI hotkey driver * * Copyright(C) 2010 Intel Corporation. - * Copyright(C) 2010 Corentin Chary + * Copyright(C) 2010-2011 Corentin Chary * * Portions based on wistron_btns.c: * Copyright (C) 2005 Miloslav Trmac From a7ce3f041e640daf96e227d8f7ffa6b988f33025 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:33 +0100 Subject: [PATCH 47/81] asus-wmi: introduce struct asus_rfkill First, this allow use to remove the custom asusrfkill_wlan_query, but this will also allow us to give struct asus_wmi * to get_devstate/set_devstate later. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 115 ++++++++++++++++---------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 4c3f68a495d..d0f0931ea5e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -114,6 +114,12 @@ struct asus_wmi_debug { u32 ctrl_param; }; +struct asus_rfkill { + struct asus_wmi *asus; + struct rfkill *rfkill; + u32 dev_id; +}; + struct asus_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -124,10 +130,10 @@ struct asus_wmi { struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; - struct rfkill *wlan_rfkill; - struct rfkill *bluetooth_rfkill; - struct rfkill *wimax_rfkill; - struct rfkill *wwan3g_rfkill; + struct asus_rfkill wlan; + struct asus_rfkill bluetooth; + struct asus_rfkill wimax; + struct asus_rfkill wwan3g; struct hotplug_slot *hotplug_slot; struct mutex hotplug_lock; @@ -377,8 +383,8 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus) mutex_lock(&asus->hotplug_lock); - if (asus->wlan_rfkill) - rfkill_set_sw_state(asus->wlan_rfkill, blocked); + if (asus->wlan.rfkill) + rfkill_set_sw_state(asus->wlan.rfkill, blocked); if (asus->hotplug_slot) { bus = pci_find_bus(0, 1); @@ -570,11 +576,11 @@ error_workqueue: */ static int asus_rfkill_set(void *data, bool blocked) { - int dev_id = (unsigned long)data; + struct asus_rfkill *priv = data; u32 ctrl_param = !blocked; acpi_status status; - status = asus_wmi_set_devstate(dev_id, ctrl_param, NULL); + status = asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL); if (ACPI_FAILURE(status)) return -EIO; @@ -584,20 +590,21 @@ static int asus_rfkill_set(void *data, bool blocked) static void asus_rfkill_query(struct rfkill *rfkill, void *data) { - int dev_id = (unsigned long)data; + struct asus_rfkill *priv = data; int result; - result = asus_wmi_get_devstate_simple(dev_id); + result = asus_wmi_get_devstate_simple(priv->dev_id); if (result < 0) return; - rfkill_set_sw_state(rfkill, !result); + rfkill_set_sw_state(priv->rfkill, !result); } static int asus_rfkill_wlan_set(void *data, bool blocked) { - struct asus_wmi *asus = data; + struct asus_rfkill *priv = data; + struct asus_wmi *asus = priv->asus; int ret; /* @@ -608,19 +615,14 @@ static int asus_rfkill_wlan_set(void *data, bool blocked) * any wmi method */ mutex_lock(&asus->wmi_lock); - ret = asus_rfkill_set((void *)(long)ASUS_WMI_DEVID_WLAN, blocked); + ret = asus_rfkill_set(data, blocked); mutex_unlock(&asus->wmi_lock); return ret; } -static void asus_rfkill_wlan_query(struct rfkill *rfkill, void *data) -{ - asus_rfkill_query(rfkill, (void *)(long)ASUS_WMI_DEVID_WLAN); -} - static const struct rfkill_ops asus_rfkill_wlan_ops = { .set_block = asus_rfkill_wlan_set, - .query = asus_rfkill_wlan_query, + .query = asus_rfkill_query, }; static const struct rfkill_ops asus_rfkill_ops = { @@ -629,20 +631,24 @@ static const struct rfkill_ops asus_rfkill_ops = { }; static int asus_new_rfkill(struct asus_wmi *asus, - struct rfkill **rfkill, + struct asus_rfkill *arfkill, const char *name, enum rfkill_type type, int dev_id) { int result = asus_wmi_get_devstate_simple(dev_id); + struct rfkill **rfkill = &arfkill->rfkill; if (result < 0) return result; + arfkill->dev_id = dev_id; + arfkill->asus = asus; + if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless) *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, - &asus_rfkill_wlan_ops, asus); + &asus_rfkill_wlan_ops, arfkill); else *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, - &asus_rfkill_ops, (void *)(long)dev_id); + &asus_rfkill_ops, arfkill); if (!*rfkill) return -EINVAL; @@ -662,10 +668,10 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P5"); asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P6"); asus_unregister_rfkill_notifier(asus, "\\_SB.PCI0.P0P7"); - if (asus->wlan_rfkill) { - rfkill_unregister(asus->wlan_rfkill); - rfkill_destroy(asus->wlan_rfkill); - asus->wlan_rfkill = NULL; + if (asus->wlan.rfkill) { + rfkill_unregister(asus->wlan.rfkill); + rfkill_destroy(asus->wlan.rfkill); + asus->wlan.rfkill = NULL; } /* * Refresh pci hotplug in case the rfkill state was changed after @@ -677,20 +683,20 @@ static void asus_wmi_rfkill_exit(struct asus_wmi *asus) if (asus->hotplug_workqueue) destroy_workqueue(asus->hotplug_workqueue); - if (asus->bluetooth_rfkill) { - rfkill_unregister(asus->bluetooth_rfkill); - rfkill_destroy(asus->bluetooth_rfkill); - asus->bluetooth_rfkill = NULL; + if (asus->bluetooth.rfkill) { + rfkill_unregister(asus->bluetooth.rfkill); + rfkill_destroy(asus->bluetooth.rfkill); + asus->bluetooth.rfkill = NULL; } - if (asus->wimax_rfkill) { - rfkill_unregister(asus->wimax_rfkill); - rfkill_destroy(asus->wimax_rfkill); - asus->wimax_rfkill = NULL; + if (asus->wimax.rfkill) { + rfkill_unregister(asus->wimax.rfkill); + rfkill_destroy(asus->wimax.rfkill); + asus->wimax.rfkill = NULL; } - if (asus->wwan3g_rfkill) { - rfkill_unregister(asus->wwan3g_rfkill); - rfkill_destroy(asus->wwan3g_rfkill); - asus->wwan3g_rfkill = NULL; + if (asus->wwan3g.rfkill) { + rfkill_unregister(asus->wwan3g.rfkill); + rfkill_destroy(asus->wwan3g.rfkill); + asus->wwan3g.rfkill = NULL; } } @@ -701,30 +707,27 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) mutex_init(&asus->hotplug_lock); mutex_init(&asus->wmi_lock); - result = asus_new_rfkill(asus, &asus->wlan_rfkill, - "asus-wlan", RFKILL_TYPE_WLAN, - ASUS_WMI_DEVID_WLAN); + result = asus_new_rfkill(asus, &asus->wlan, "asus-wlan", + RFKILL_TYPE_WLAN, ASUS_WMI_DEVID_WLAN); if (result && result != -ENODEV) goto exit; - result = asus_new_rfkill(asus, &asus->bluetooth_rfkill, + result = asus_new_rfkill(asus, &asus->bluetooth, "asus-bluetooth", RFKILL_TYPE_BLUETOOTH, ASUS_WMI_DEVID_BLUETOOTH); if (result && result != -ENODEV) goto exit; - result = asus_new_rfkill(asus, &asus->wimax_rfkill, - "asus-wimax", RFKILL_TYPE_WIMAX, - ASUS_WMI_DEVID_WIMAX); + result = asus_new_rfkill(asus, &asus->wimax, "asus-wimax", + RFKILL_TYPE_WIMAX, ASUS_WMI_DEVID_WIMAX); if (result && result != -ENODEV) goto exit; - result = asus_new_rfkill(asus, &asus->wwan3g_rfkill, - "asus-wwan3g", RFKILL_TYPE_WWAN, - ASUS_WMI_DEVID_WWAN3G); + result = asus_new_rfkill(asus, &asus->wwan3g, "asus-wwan3g", + RFKILL_TYPE_WWAN, ASUS_WMI_DEVID_WWAN3G); if (result && result != -ENODEV) goto exit; @@ -1277,7 +1280,7 @@ static int asus_hotk_thaw(struct device *device) { struct asus_wmi *asus = dev_get_drvdata(device); - if (asus->wlan_rfkill) { + if (asus->wlan.rfkill) { bool wlan; /* @@ -1298,20 +1301,20 @@ static int asus_hotk_restore(struct device *device) int bl; /* Refresh both wlan rfkill state and pci hotplug */ - if (asus->wlan_rfkill) + if (asus->wlan.rfkill) asus_rfkill_hotplug(asus); - if (asus->bluetooth_rfkill) { + if (asus->bluetooth.rfkill) { bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_BLUETOOTH); - rfkill_set_sw_state(asus->bluetooth_rfkill, bl); + rfkill_set_sw_state(asus->bluetooth.rfkill, bl); } - if (asus->wimax_rfkill) { + if (asus->wimax.rfkill) { bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WIMAX); - rfkill_set_sw_state(asus->wimax_rfkill, bl); + rfkill_set_sw_state(asus->wimax.rfkill, bl); } - if (asus->wwan3g_rfkill) { + if (asus->wwan3g.rfkill) { bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WWAN3G); - rfkill_set_sw_state(asus->wwan3g_rfkill, bl); + rfkill_set_sw_state(asus->wwan3g.rfkill, bl); } return 0; From a75fe0d78ec00d3d5b2c42b1ee76b22e99f213d1 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:34 +0100 Subject: [PATCH 48/81] asus-wmi: handle "unknown status" bit Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index d0f0931ea5e..39ce3c1a771 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -92,6 +92,7 @@ MODULE_LICENSE("GPL"); /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 +#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 @@ -270,6 +271,11 @@ static int asus_wmi_get_devstate_bits(u32 dev_id, u32 mask) if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; + if (mask == ASUS_WMI_DSTS_STATUS_BIT) { + if (retval & ASUS_WMI_DSTS_UNKNOWN_BIT) + return -ENODEV; + } + return retval & mask; } From d33da3b6866975b17fbec67540f6153f5dcdcec7 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:35 +0100 Subject: [PATCH 49/81] asus-wmi: factorise wmi_evaluate_method call This patch create a single function to call the WMI methods. This function handle inexistent methods (when implemented by the WMI devices, and this is not the case on Eee PCs), ACPI errors, etc.. Also pack struct bios_arg, and make sure that we always send a 64bit buffer when calling a WMI method, because this is needed on Asus notebooks. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 162 +++++++++++++------------------- 1 file changed, 65 insertions(+), 97 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 39ce3c1a771..34e6b4d83a9 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,8 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_METHODID_DEVS 0x53564544 #define ASUS_WMI_METHODID_CFVS 0x53564643 +#define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE + /* Wireless */ #define ASUS_WMI_DEVID_WLAN 0x00010011 #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 @@ -98,9 +100,9 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 struct bios_args { - u32 dev_id; - u32 ctrl_param; -}; + u32 arg0; + u32 arg1; +} __packed; /* * / - debugfs root directory @@ -187,20 +189,24 @@ static void asus_wmi_input_exit(struct asus_wmi *asus) asus->inputdev = NULL; } -static acpi_status asus_wmi_get_devstate(u32 dev_id, u32 *retval) +static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, + u32 *retval) { - struct acpi_buffer input = { (acpi_size) sizeof(u32), &dev_id }; + struct bios_args args = { + .arg0 = arg0, + .arg1 = arg1, + }; + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; acpi_status status; + union acpi_object *obj; u32 tmp; - status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, - 1, ASUS_WMI_METHODID_DSTS, + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id, &input, &output); if (ACPI_FAILURE(status)) - return status; + goto exit; obj = (union acpi_object *)output.pointer; if (obj && obj->type == ACPI_TYPE_INTEGER) @@ -213,60 +219,39 @@ static acpi_status asus_wmi_get_devstate(u32 dev_id, u32 *retval) kfree(obj); - return status; +exit: + if (ACPI_FAILURE(status)) + return -EIO; + if (tmp == ASUS_WMI_UNSUPPORTED_METHOD) + return -ENODEV; + + return 0; } -static acpi_status asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, +static int asus_wmi_get_devstate(u32 dev_id, u32 *retval) +{ + return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, + 0, retval); +} + +static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval) { - struct bios_args args = { - .dev_id = dev_id, - .ctrl_param = ctrl_param, - }; - struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; - acpi_status status; - - if (!retval) { - status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, - ASUS_WMI_METHODID_DEVS, - &input, NULL); - } else { - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - u32 tmp; - - status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, - ASUS_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; + return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, + ctrl_param, retval); } /* Helper for special devices with magic return codes */ static int asus_wmi_get_devstate_bits(u32 dev_id, u32 mask) { u32 retval = 0; - acpi_status status; + int err; - status = asus_wmi_get_devstate(dev_id, &retval); + err = asus_wmi_get_devstate(dev_id, &retval); - if (ACPI_FAILURE(status)) - return -EINVAL; + if (err < 0) + return err; if (!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)) return -ENODEV; @@ -584,14 +569,8 @@ static int asus_rfkill_set(void *data, bool blocked) { struct asus_rfkill *priv = data; u32 ctrl_param = !blocked; - acpi_status status; - status = asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL); - - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; + return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL); } static void asus_rfkill_query(struct rfkill *rfkill, void *data) @@ -784,38 +763,34 @@ static int read_backlight_power(void) static int read_brightness(struct backlight_device *bd) { u32 retval; - acpi_status status; + int err; - status = asus_wmi_get_devstate(ASUS_WMI_DEVID_BRIGHTNESS, &retval); + err = asus_wmi_get_devstate(ASUS_WMI_DEVID_BRIGHTNESS, &retval); - if (ACPI_FAILURE(status)) - return -EIO; - else - return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; + if (err < 0) + return err; + + return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } static int update_bl_status(struct backlight_device *bd) { u32 ctrl_param; - acpi_status status; - int power; + int power, err; ctrl_param = bd->props.brightness; - status = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, - ctrl_param, NULL); + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, + ctrl_param, NULL); - if (ACPI_FAILURE(status)) - return -EIO; + if (err < 0) + return err; power = read_backlight_power(); if (power != -ENODEV && bd->props.power != power) { ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); - status = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, - ctrl_param, NULL); - - if (ACPI_FAILURE(status)) - return -EIO; + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, + ctrl_param, NULL); } return 0; } @@ -948,19 +923,19 @@ static int parse_arg(const char *buf, unsigned long count, int *val) static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) { - acpi_status status; u32 retval; - int rv, value; + int rv, err, value; value = asus_wmi_get_devstate_simple(devid); if (value == -ENODEV) /* Check device presence */ return value; rv = parse_arg(buf, count, &value); - status = asus_wmi_set_devstate(devid, value, &retval); + err = asus_wmi_set_devstate(devid, value, &retval); + + if (err < 0) + return err; - if (ACPI_FAILURE(status)) - return -EIO; return rv; } @@ -1003,21 +978,13 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; - struct acpi_buffer input = { (acpi_size) sizeof(value), &value }; - acpi_status status; if (!count || sscanf(buf, "%i", &value) != 1) return -EINVAL; if (value < 0 || value > 2) return -EINVAL; - status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, - 1, ASUS_WMI_METHODID_CFVS, &input, NULL); - - if (ACPI_FAILURE(status)) - return -EIO; - else - return count; + return asus_wmi_evaluate_method(ASUS_WMI_METHODID_CFVS, value, 0, NULL); } static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); @@ -1089,13 +1056,13 @@ struct asus_wmi_debugfs_node { static int show_dsts(struct seq_file *m, void *data) { struct asus_wmi *asus = m->private; - acpi_status status; + int err; u32 retval = -1; - status = asus_wmi_get_devstate(asus->debug.dev_id, &retval); + err = asus_wmi_get_devstate(asus->debug.dev_id, &retval); - if (ACPI_FAILURE(status)) - return -EIO; + if (err < 0) + return err; seq_printf(m, "DSTS(%x) = %x\n", asus->debug.dev_id, retval); @@ -1105,13 +1072,14 @@ static int show_dsts(struct seq_file *m, void *data) static int show_devs(struct seq_file *m, void *data) { struct asus_wmi *asus = m->private; - acpi_status status; + int err; u32 retval = -1; - status = asus_wmi_set_devstate(asus->debug.dev_id, - asus->debug.ctrl_param, &retval); - if (ACPI_FAILURE(status)) - return -EIO; + err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, + &retval); + + if (err < 0) + return err; seq_printf(m, "DEVS(%x, %x) = %x\n", asus->debug.dev_id, asus->debug.ctrl_param, retval); From 1d070f89a723bd296865dd7eb61c8050763e6e3b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:36 +0100 Subject: [PATCH 50/81] asus-wmi: try to guess the right DSTS methods This is tricky, new WMI aware notebooks seems to use 0x53545344 while Eee PCs are using 0x53544344. But there is no way to know if there is an Eee PC in that wild that is using 0x53545344 or a notebook using 0x53544344. So the driver try to guess the available DSTS method ... But most Eee PCs never return 0xFFFFFFFE when a method is not available, they return 0 instead (and that's useless). So, first, try 0x53544344 then 0x53545344. We will find a better way when we got more data. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 99 ++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 34e6b4d83a9..fd5b08eecf1 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -67,6 +67,7 @@ MODULE_LICENSE("GPL"); /* WMI Methods */ #define ASUS_WMI_METHODID_DSTS 0x53544344 +#define ASUS_WMI_METHODID_DSTS2 0x53545344 #define ASUS_WMI_METHODID_DEVS 0x53564544 #define ASUS_WMI_METHODID_CFVS 0x53564643 @@ -124,6 +125,8 @@ struct asus_rfkill { }; struct asus_wmi { + int dsts_id; + struct input_dev *inputdev; struct backlight_device *backlight_device; struct platform_device *platform_device; @@ -229,26 +232,26 @@ exit: return 0; } -static int asus_wmi_get_devstate(u32 dev_id, u32 *retval) +static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval) { - return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, dev_id, - 0, retval); + return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval); } static int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, - u32 *retval) + u32 *retval) { return asus_wmi_evaluate_method(ASUS_WMI_METHODID_DEVS, dev_id, ctrl_param, retval); } /* Helper for special devices with magic return codes */ -static int asus_wmi_get_devstate_bits(u32 dev_id, u32 mask) +static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, + u32 dev_id, u32 mask) { u32 retval = 0; int err; - err = asus_wmi_get_devstate(dev_id, &retval); + err = asus_wmi_get_devstate(asus, dev_id, &retval); if (err < 0) return err; @@ -264,9 +267,10 @@ static int asus_wmi_get_devstate_bits(u32 dev_id, u32 mask) return retval & mask; } -static int asus_wmi_get_devstate_simple(u32 dev_id) +static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) { - return asus_wmi_get_devstate_bits(dev_id, ASUS_WMI_DSTS_STATUS_BIT); + return asus_wmi_get_devstate_bits(asus, dev_id, + ASUS_WMI_DSTS_STATUS_BIT); } /* @@ -302,7 +306,7 @@ static void tpd_led_set(struct led_classdev *led_cdev, static int read_tpd_led_state(struct asus_wmi *asus) { - return asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_TOUCHPAD_LED); + return asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_TOUCHPAD_LED); } static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) @@ -353,7 +357,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) */ static bool asus_wlan_rfkill_blocked(struct asus_wmi *asus) { - int result = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); + int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); if (result < 0) return false; @@ -482,7 +486,8 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) { - int result = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); + struct asus_wmi *asus = hotplug_slot->private; + int result = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); if (result < 0) return result; @@ -578,7 +583,7 @@ static void asus_rfkill_query(struct rfkill *rfkill, void *data) struct asus_rfkill *priv = data; int result; - result = asus_wmi_get_devstate_simple(priv->dev_id); + result = asus_wmi_get_devstate_simple(priv->asus, priv->dev_id); if (result < 0) return; @@ -619,7 +624,7 @@ static int asus_new_rfkill(struct asus_wmi *asus, struct asus_rfkill *arfkill, const char *name, enum rfkill_type type, int dev_id) { - int result = asus_wmi_get_devstate_simple(dev_id); + int result = asus_wmi_get_devstate_simple(asus, dev_id); struct rfkill **rfkill = &arfkill->rfkill; if (result < 0) @@ -750,9 +755,9 @@ exit: /* * Backlight */ -static int read_backlight_power(void) +static int read_backlight_power(struct asus_wmi *asus) { - int ret = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_BACKLIGHT); + int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT); if (ret < 0) return ret; @@ -762,10 +767,11 @@ static int read_backlight_power(void) static int read_brightness(struct backlight_device *bd) { + struct asus_wmi *asus = bl_get_data(bd); u32 retval; int err; - err = asus_wmi_get_devstate(ASUS_WMI_DEVID_BRIGHTNESS, &retval); + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); if (err < 0) return err; @@ -775,6 +781,7 @@ static int read_brightness(struct backlight_device *bd) static int update_bl_status(struct backlight_device *bd) { + struct asus_wmi *asus = bl_get_data(bd); u32 ctrl_param; int power, err; @@ -786,7 +793,7 @@ static int update_bl_status(struct backlight_device *bd) if (err < 0) return err; - power = read_backlight_power(); + power = read_backlight_power(asus); if (power != -ENODEV && bd->props.power != power) { ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, @@ -825,9 +832,9 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) int max; int power; - max = asus_wmi_get_devstate_bits(ASUS_WMI_DEVID_BRIGHTNESS, + max = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_BRIGHTNESS, ASUS_WMI_DSTS_MAX_BRIGTH_MASK); - power = read_backlight_power(); + power = read_backlight_power(asus); if (max < 0 && power < 0) { /* Try to keep the original error */ @@ -921,12 +928,13 @@ static int parse_arg(const char *buf, unsigned long count, int *val) return count; } -static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) +static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid, + const char *buf, size_t count) { u32 retval; int rv, err, value; - value = asus_wmi_get_devstate_simple(devid); + value = asus_wmi_get_devstate_simple(asus, devid); if (value == -ENODEV) /* Check device presence */ return value; @@ -939,9 +947,9 @@ static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) return rv; } -static ssize_t show_sys_wmi(int devid, char *buf) +static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf) { - int value = asus_wmi_get_devstate_simple(devid); + int value = asus_wmi_get_devstate_simple(asus, devid); if (value < 0) return value; @@ -954,13 +962,17 @@ static ssize_t show_sys_wmi(int devid, char *buf) struct device_attribute *attr, \ char *buf) \ { \ - return show_sys_wmi(_cm, buf); \ + struct asus_wmi *asus = dev_get_drvdata(dev); \ + \ + return show_sys_wmi(asus, _cm, buf); \ } \ static ssize_t store_##_name(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ - return store_sys_wmi(_cm, buf, count); \ + struct asus_wmi *asus = dev_get_drvdata(dev); \ + \ + return store_sys_wmi(asus, _cm, buf, count); \ } \ static struct device_attribute dev_attr_##_name = { \ .attr = { \ @@ -1000,7 +1012,10 @@ static struct attribute *platform_attributes[] = { static mode_t asus_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { - bool supported = true; + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct asus_wmi *asus = platform_get_drvdata(pdev); + bool ok = true; int devid = -1; if (attr == &dev_attr_camera.attr) @@ -1011,9 +1026,9 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_TOUCHPAD; if (devid != -1) - supported = asus_wmi_get_devstate_simple(devid) != -ENODEV; + ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); - return supported ? attr->mode : 0; + return ok ? attr->mode : 0; } static struct attribute_group platform_attribute_group = { @@ -1036,6 +1051,23 @@ static int asus_wmi_sysfs_init(struct platform_device *device) */ static int __init asus_wmi_platform_init(struct asus_wmi *asus) { + /* + * Eee PC and Notebooks seems to have different method_id for DSTS, + * but it may also be related to the BIOS's SPEC. + * Note, on most Eeepc, there is no way to check if a method exist + * or note, while on notebooks, they returns 0xFFFFFFFE on failure, + * but once again, SPEC may probably be used for that kind of things. + */ + if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL)) + asus->dsts_id = ASUS_WMI_METHODID_DSTS; + else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL)) + asus->dsts_id = ASUS_WMI_METHODID_DSTS2; + + if (!asus->dsts_id) { + pr_err("Can't find DSTS"); + return -ENODEV; + } + return asus_wmi_sysfs_init(asus->platform_device); } @@ -1059,7 +1091,7 @@ static int show_dsts(struct seq_file *m, void *data) int err; u32 retval = -1; - err = asus_wmi_get_devstate(asus->debug.dev_id, &retval); + err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); if (err < 0) return err; @@ -1262,7 +1294,7 @@ static int asus_hotk_thaw(struct device *device) * during suspend. Normally it restores it on resume, but * we should kick it ourselves in case hibernation is aborted. */ - wlan = asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WLAN); + wlan = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WLAN); asus_wmi_set_devstate(ASUS_WMI_DEVID_WLAN, wlan, NULL); } @@ -1279,15 +1311,16 @@ static int asus_hotk_restore(struct device *device) asus_rfkill_hotplug(asus); if (asus->bluetooth.rfkill) { - bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_BLUETOOTH); + bl = !asus_wmi_get_devstate_simple(asus, + ASUS_WMI_DEVID_BLUETOOTH); rfkill_set_sw_state(asus->bluetooth.rfkill, bl); } if (asus->wimax.rfkill) { - bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WIMAX); + bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WIMAX); rfkill_set_sw_state(asus->wimax.rfkill, bl); } if (asus->wwan3g.rfkill) { - bl = !asus_wmi_get_devstate_simple(ASUS_WMI_DEVID_WWAN3G); + bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_WWAN3G); rfkill_set_sw_state(asus->wwan3g.rfkill, bl); } From 8fbea019a1a70e0fb01e0f98c963e6042fbe94ab Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:37 +0100 Subject: [PATCH 51/81] asus-wmi: fix and clean backlight code Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 44 ++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index fd5b08eecf1..adf7b05c9ae 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -765,11 +765,29 @@ static int read_backlight_power(struct asus_wmi *asus) return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; } +static int read_brightness_max(struct asus_wmi *asus) +{ + u32 retval; + int err; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); + + if (err < 0) + return err; + + retval = retval & ASUS_WMI_DSTS_MAX_BRIGTH_MASK; + retval >>= 8; + + if (!retval) + return -ENODEV; + + return retval; +} + static int read_brightness(struct backlight_device *bd) { struct asus_wmi *asus = bl_get_data(bd); - u32 retval; - int err; + u32 retval, err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); @@ -799,7 +817,7 @@ static int update_bl_status(struct backlight_device *bd) err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, ctrl_param, NULL); } - return 0; + return err; } static const struct backlight_ops asus_wmi_bl_ops = { @@ -832,23 +850,19 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) int max; int power; - max = asus_wmi_get_devstate_bits(asus, ASUS_WMI_DEVID_BRIGHTNESS, - ASUS_WMI_DSTS_MAX_BRIGTH_MASK); - power = read_backlight_power(asus); + max = read_brightness_max(asus); - if (max < 0 && power < 0) { - /* Try to keep the original error */ - if (max == -ENODEV && power == -ENODEV) - return -ENODEV; - if (max != -ENODEV) - return max; - else - return power; - } if (max == -ENODEV) max = 0; + else if (max < 0) + return max; + + power = read_backlight_power(asus); + if (power == -ENODEV) power = FB_BLANK_UNBLANK; + else if (power < 0) + return power; memset(&props, 0, sizeof(struct backlight_properties)); props.max_brightness = max; From 46dbca871df753ce92c321a41a8a38eba7487680 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:38 +0100 Subject: [PATCH 52/81] asus-wmi: add calls to INIT, SPEC and SFUN on init INIT() call is needed to enable hotkeys on G73 SPEC() and SFUN() allow us to know more about available features. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index adf7b05c9ae..88596ea0f74 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -66,10 +66,13 @@ MODULE_LICENSE("GPL"); #define NOTIFY_BRNDOWN_MAX 0x2e /* WMI Methods */ +#define ASUS_WMI_METHODID_SPEC 0x43455053 +#define ASUS_WMI_METHODID_SFUN 0x4E554653 #define ASUS_WMI_METHODID_DSTS 0x53544344 #define ASUS_WMI_METHODID_DSTS2 0x53545344 #define ASUS_WMI_METHODID_DEVS 0x53564544 #define ASUS_WMI_METHODID_CFVS 0x53564643 +#define ASUS_WMI_METHODID_INIT 0x54494E49 #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE @@ -126,6 +129,8 @@ struct asus_rfkill { struct asus_wmi { int dsts_id; + int spec; + int sfun; struct input_dev *inputdev; struct backlight_device *backlight_device; @@ -1065,6 +1070,29 @@ static int asus_wmi_sysfs_init(struct platform_device *device) */ static int __init asus_wmi_platform_init(struct asus_wmi *asus) { + int rv; + + /* INIT enable hotkeys on some models */ + if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv)) + pr_info("Initialization: %#x", rv); + + /* We don't know yet what to do with this version... */ + if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) { + pr_info("BIOS WMI version: %d.%d", rv >> 8, rv & 0xFF); + asus->spec = rv; + } + + /* + * The SFUN method probably allows the original driver to get the list + * of features supported by a given model. For now, 0x0100 or 0x0800 + * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. + * The significance of others is yet to be found. + */ + if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) { + pr_info("SFUN value: %#x", rv); + asus->sfun = rv; + } + /* * Eee PC and Notebooks seems to have different method_id for DSTS, * but it may also be related to the BIOS's SPEC. From ef343491db1770a3af5010ba007167c348cdbe1a Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:39 +0100 Subject: [PATCH 53/81] asus-wmi: allow debugfs interface to call arbitrary method Also add some # format flags to debugfs output. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 47 +++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 88596ea0f74..9095c28340c 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -112,11 +112,14 @@ struct bios_args { * / - debugfs root directory * dev_id - current dev_id * ctrl_param - current ctrl_param + * method_id - current method_id * devs - call DEVS(dev_id, ctrl_param) and print result * dsts - call DSTS(dev_id) and print result + * call - call method_id(dev_id, ctrl_param) and print result */ struct asus_wmi_debug { struct dentry *root; + u32 method_id; u32 dev_id; u32 ctrl_param; }; @@ -1138,7 +1141,7 @@ static int show_dsts(struct seq_file *m, void *data) if (err < 0) return err; - seq_printf(m, "DSTS(%x) = %x\n", asus->debug.dev_id, retval); + seq_printf(m, "DSTS(%#x) = %#x\n", asus->debug.dev_id, retval); return 0; } @@ -1155,15 +1158,50 @@ static int show_devs(struct seq_file *m, void *data) if (err < 0) return err; - seq_printf(m, "DEVS(%x, %x) = %x\n", asus->debug.dev_id, + seq_printf(m, "DEVS(%#x, %#x) = %#x\n", asus->debug.dev_id, asus->debug.ctrl_param, retval); return 0; } +static int show_call(struct seq_file *m, void *data) +{ + struct asus_wmi *asus = m->private; + struct bios_args args = { + .arg0 = asus->debug.dev_id, + .arg1 = asus->debug.ctrl_param, + }; + struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, + 1, asus->debug.method_id, + &input, &output); + + if (ACPI_FAILURE(status)) + return -EIO; + + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + seq_printf(m, "%#x(%#x, %#x) = %#x\n", asus->debug.method_id, + asus->debug.dev_id, asus->debug.ctrl_param, + (u32) obj->integer.value); + else + seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id, + asus->debug.dev_id, asus->debug.ctrl_param, + obj->type); + + kfree(obj); + + return 0; +} + static struct asus_wmi_debugfs_node asus_wmi_debug_files[] = { {NULL, "devs", show_devs}, {NULL, "dsts", show_dsts}, + {NULL, "call", show_call}, }; static int asus_wmi_debugfs_open(struct inode *inode, struct file *file) @@ -1197,6 +1235,11 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus) goto error_debugfs; } + dent = debugfs_create_x32("method_id", S_IRUGO | S_IWUSR, + asus->debug.root, &asus->debug.method_id); + if (!dent) + goto error_debugfs; + dent = debugfs_create_x32("dev_id", S_IRUGO | S_IWUSR, asus->debug.root, &asus->debug.dev_id); if (!dent) From b229ece9911cc0b2415d3ffd55d7b2a28d30b614 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:40 +0100 Subject: [PATCH 54/81] asus-nb-wmi: Asus Notebooks WMI Driver Introduce a new driver for Asus Notebooks shipped with a WMI device instead of the old ACPI device. The WMI device is almost the same as the one present in Eee PC, but the event guid and the keymap are different. The keymap comes from asus-laptop module. On Asus notebooks, when you call the WMI device, you always need a 64bit buffer, even if you only want to get the state of a device (tested on a G73). Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- MAINTAINERS | 30 +-------- drivers/platform/x86/Kconfig | 16 ++++- drivers/platform/x86/Makefile | 1 + drivers/platform/x86/asus-nb-wmi.c | 98 ++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 28 deletions(-) create mode 100644 drivers/platform/x86/asus-nb-wmi.c diff --git a/MAINTAINERS b/MAINTAINERS index 8aa1cacddbc..6b4b9cdec37 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1157,14 +1157,14 @@ S: Maintained F: Documentation/hwmon/asc7621 F: drivers/hwmon/asc7621.c -ASUS ACPI EXTRAS DRIVER +ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS M: Corentin Chary -M: Karol Kozimor L: acpi4asus-user@lists.sourceforge.net L: platform-driver-x86@vger.kernel.org W: http://acpi4asus.sf.net S: Maintained -F: drivers/platform/x86/asus_acpi.c +F: drivers/platform/x86/asus*.c +F: drivers/platform/x86/eeepc*.c ASUS ASB100 HARDWARE MONITOR DRIVER M: "Mark M. Hoffman" @@ -1172,14 +1172,6 @@ L: lm-sensors@lm-sensors.org S: Maintained F: drivers/hwmon/asb100.c -ASUS LAPTOP 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/asus-laptop.c - ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API M: Dan Williams W: http://sourceforge.net/projects/xscaleiop @@ -2414,22 +2406,6 @@ T: git git://git.alsa-project.org/alsa-kernel.git S: Maintained F: sound/usb/misc/ua101.c -EEEPC LAPTOP 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-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/Kconfig b/drivers/platform/x86/Kconfig index 7fde7ba01ab..45f4d63c793 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -465,11 +465,25 @@ config ASUS_WMI select LEDS_CLASS select NEW_LEDS ---help--- - Say Y here if you have a WMI aware Asus laptop (like Eee PCs). + Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new + Asus Notebooks). To compile this driver as a module, choose M here: the module will be called asus-wmi. +config ASUS_NB_WMI + tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" + depends on ASUS_WMI + ---help--- + This is a driver for newer Asus notebooks. It adds extra features + like wireless radio and bluetooth control, leds, hotkeys, backlight... + + For more informations, see + + + If you have an ACPI-WMI compatible Asus Notebook, say Y or M + here. + config EEEPC_WMI tristate "Eee PC WMI Driver (EXPERIMENTAL)" depends on ASUS_WMI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index f9c83f48c92..0f7b23c0714 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_ASUS_WMI) += asus-wmi.o +obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c new file mode 100644 index 00000000000..0580d99b079 --- /dev/null +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -0,0 +1,98 @@ +/* + * Asus Notebooks WMI hotkey driver + * + * Copyright(C) 2010 Corentin Chary + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "asus-wmi.h" + +#define ASUS_NB_WMI_FILE "asus-nb-wmi" + +MODULE_AUTHOR("Corentin Chary "); +MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver"); +MODULE_LICENSE("GPL"); + +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" + +MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID); + +static const struct key_entry asus_nb_wmi_keymap[] = { + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, + { KE_KEY, 0x32, { KEY_MUTE } }, + { KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ + { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */ + { KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, + { KE_KEY, 0x41, { KEY_NEXTSONG } }, + { KE_KEY, 0x43, { KEY_STOPCD } }, + { KE_KEY, 0x45, { KEY_PLAYPAUSE } }, + { KE_KEY, 0x4c, { KEY_MEDIA } }, + { KE_KEY, 0x50, { KEY_EMAIL } }, + { KE_KEY, 0x51, { KEY_WWW } }, + { KE_KEY, 0x55, { KEY_CALC } }, + { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ + { KE_KEY, 0x5D, { KEY_WLAN } }, + { KE_KEY, 0x5E, { KEY_WLAN } }, + { KE_KEY, 0x5F, { KEY_WLAN } }, + { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, + { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, + { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, + { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, + { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x88, { KEY_RFKILL } }, + { KE_KEY, 0x8A, { KEY_PROG1 } }, + { KE_KEY, 0x95, { KEY_MEDIA } }, + { KE_KEY, 0x99, { KEY_PHONE } }, + { KE_KEY, 0xb5, { KEY_CALC } }, + { KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, + { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, + { KE_END, 0}, +}; + +static struct asus_wmi_driver asus_nb_wmi_driver = { + .name = ASUS_NB_WMI_FILE, + .owner = THIS_MODULE, + .event_guid = ASUS_NB_WMI_EVENT_GUID, + .keymap = asus_nb_wmi_keymap, + .input_name = "Asus WMI hotkeys", + .input_phys = ASUS_NB_WMI_FILE "/input0", +}; + + +static int __init asus_nb_wmi_init(void) +{ + return asus_wmi_register_driver(&asus_nb_wmi_driver); +} + +static void __exit asus_nb_wmi_exit(void) +{ + asus_wmi_unregister_driver(&asus_nb_wmi_driver); +} + +module_init(asus_nb_wmi_init); +module_exit(asus_nb_wmi_exit); From 2f686b54fbfcd82ebfb650a5c628c1b9ba8b9863 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:41 +0100 Subject: [PATCH 55/81] asus-wmi: add some common device ids and method ids I also found some leds ids (0x00020011-0x00020016 and 0x00040015), but since they are not really present on the notebook, I can't guess their name . Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 44 +++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 9095c28340c..05db6b2067b 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -66,25 +66,46 @@ MODULE_LICENSE("GPL"); #define NOTIFY_BRNDOWN_MAX 0x2e /* WMI Methods */ -#define ASUS_WMI_METHODID_SPEC 0x43455053 -#define ASUS_WMI_METHODID_SFUN 0x4E554653 -#define ASUS_WMI_METHODID_DSTS 0x53544344 -#define ASUS_WMI_METHODID_DSTS2 0x53545344 -#define ASUS_WMI_METHODID_DEVS 0x53564544 -#define ASUS_WMI_METHODID_CFVS 0x53564643 -#define ASUS_WMI_METHODID_INIT 0x54494E49 +#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */ +#define ASUS_WMI_METHODID_SFBD 0x44424653 /* Set First Boot Device */ +#define ASUS_WMI_METHODID_GLCD 0x44434C47 /* Get LCD status */ +#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */ +#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */ +#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */ +#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */ +#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */ +#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */ +#define ASUS_WMI_METHODID_DEVP 0x50564544 /* DEVice Policy */ +#define ASUS_WMI_METHODID_OSVR 0x5256534F /* OS VeRsion */ +#define ASUS_WMI_METHODID_DSTS 0x53544344 /* Device STatuS */ +#define ASUS_WMI_METHODID_DSTS2 0x53545344 /* Device STatuS #2*/ +#define ASUS_WMI_METHODID_BSTS 0x53545342 /* Bios STatuS ? */ +#define ASUS_WMI_METHODID_DEVS 0x53564544 /* DEVice Set */ +#define ASUS_WMI_METHODID_CFVS 0x53564643 /* CPU Frequency Volt Set */ +#define ASUS_WMI_METHODID_KBFT 0x5446424B /* KeyBoard FilTer */ +#define ASUS_WMI_METHODID_INIT 0x54494E49 /* INITialize */ +#define ASUS_WMI_METHODID_HKEY 0x59454B48 /* Hot KEY ?? */ #define ASUS_WMI_UNSUPPORTED_METHOD 0xFFFFFFFE /* Wireless */ +#define ASUS_WMI_DEVID_HW_SWITCH 0x00010001 +#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002 #define ASUS_WMI_DEVID_WLAN 0x00010011 #define ASUS_WMI_DEVID_BLUETOOTH 0x00010013 +#define ASUS_WMI_DEVID_GPS 0x00010015 #define ASUS_WMI_DEVID_WIMAX 0x00010017 #define ASUS_WMI_DEVID_WWAN3G 0x00010019 +#define ASUS_WMI_DEVID_UWB 0x00010021 + +/* Leds */ +/* 0x000200XX and 0x000400XX */ /* Backlight and Brightness */ #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 +#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 +#define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 @@ -96,10 +117,19 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DEVID_TOUCHPAD 0x00100011 #define ASUS_WMI_DEVID_TOUCHPAD_LED 0x00100012 +/* Fan, Thermal */ +#define ASUS_WMI_DEVID_THERMAL_CTRL 0x00110011 +#define ASUS_WMI_DEVID_FAN_CTRL 0x00110012 + +/* Power */ +#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 #define ASUS_WMI_DSTS_PRESENCE_BIT 0x00010000 +#define ASUS_WMI_DSTS_USER_BIT 0x00020000 +#define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 From e07babde13460d7b03842a6de8f22fbef93709e1 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:42 +0100 Subject: [PATCH 56/81] asus-wmi: add hwmon interface and pwm1 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/asus-wmi.c | 128 ++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 45f4d63c793..0c7cf666a1b 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -458,6 +458,7 @@ config ASUS_WMI tristate "ASUS WMI Driver (EXPERIMENTAL)" depends on ACPI_WMI depends on INPUT + depends on HWMON depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL || RFKILL = n diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 05db6b2067b..5b779a9443d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -167,6 +169,7 @@ struct asus_wmi { struct input_dev *inputdev; struct backlight_device *backlight_device; + struct device *hwmon_device; struct platform_device *platform_device; struct led_classdev tpd_led; @@ -790,6 +793,124 @@ exit: return result; } +/* + * Hwmon device + */ +static ssize_t asus_hwmon_pwm1(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + u32 value; + int err; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); + + if (err < 0) + return err; + + value |= 0xFF; + + if (value == 1) /* Low Speed */ + value = 85; + else if (value == 2) + value = 170; + else if (value == 3) + value = 255; + else if (value != 0) { + pr_err("Unknown fan speed %#x", value); + value = -1; + } + + return sprintf(buf, "%d\n", value); +} + +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0); + +static ssize_t +show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "asus\n"); +} +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); + +static struct attribute *hwmon_attributes[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +static mode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev->parent); + struct asus_wmi *asus = platform_get_drvdata(pdev); + bool ok = true; + int dev_id = -1; + u32 value = ASUS_WMI_UNSUPPORTED_METHOD; + + if (attr == &sensor_dev_attr_pwm1.dev_attr.attr) + dev_id = ASUS_WMI_DEVID_FAN_CTRL; + + if (dev_id != -1) { + int err = asus_wmi_get_devstate(asus, dev_id, &value); + + if (err < 0) + return err; + } + + if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) { + /* + * We need to find a better way, probably using sfun, + * bits or spec ... + * Currently we disable it if: + * - ASUS_WMI_UNSUPPORTED_METHOD is returned + * - reverved bits are non-zero + * - sfun and presence bit are not set + */ + if (value != ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000 + || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))) + ok = false; + } + + return ok ? attr->mode : 0; +} + +static struct attribute_group hwmon_attribute_group = { + .is_visible = asus_hwmon_sysfs_is_visible, + .attrs = hwmon_attributes +}; + +static void asus_wmi_hwmon_exit(struct asus_wmi *asus) +{ + struct device *hwmon; + + hwmon = asus->hwmon_device; + if (!hwmon) + return; + sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group); + hwmon_device_unregister(hwmon); + asus->hwmon_device = NULL; +} + +static int asus_wmi_hwmon_init(struct asus_wmi *asus) +{ + struct device *hwmon; + int result; + + hwmon = hwmon_device_register(&asus->platform_device->dev); + if (IS_ERR(hwmon)) { + pr_err("Could not register asus hwmon device\n"); + return PTR_ERR(hwmon); + } + asus->hwmon_device = hwmon; + result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group); + if (result) + asus_wmi_hwmon_exit(asus); + return result; +} + /* * Backlight */ @@ -1331,6 +1452,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_input; + err = asus_wmi_hwmon_init(asus); + if (err) + goto fail_hwmon; + err = asus_wmi_led_init(asus); if (err) goto fail_leds; @@ -1369,6 +1494,8 @@ fail_backlight: fail_rfkill: asus_wmi_led_exit(asus); fail_leds: + asus_wmi_hwmon_exit(asus); +fail_hwmon: asus_wmi_input_exit(asus); fail_input: asus_wmi_platform_exit(asus); @@ -1385,6 +1512,7 @@ static int asus_wmi_remove(struct platform_device *device) wmi_remove_notify_handler(asus->driver->event_guid); asus_wmi_backlight_exit(asus); asus_wmi_input_exit(asus); + asus_wmi_hwmon_exit(asus); asus_wmi_led_exit(asus); asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); From 5f8540094d23b8886d671ff1faf875b698e2f299 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Feb 2011 10:20:43 +0100 Subject: [PATCH 57/81] eeepc-wmi: restore KEY_CAMERA_* keys lost in 190ca27 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index e69701fc422..64e36de2285 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -65,11 +65,17 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x5d, { KEY_WLAN } }, { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ { KE_KEY, 0x82, { KEY_CAMERA } }, + { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, { KE_KEY, 0x88, { KEY_WLAN } }, { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, + { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, + { KE_KEY, 0xec, { KEY_CAMERA_UP } }, + { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, + { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, + { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, { KE_END, 0}, }; From 90ba4b1bfcf71bfe5c3844ddeb474aae4453cabd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 22 Feb 2011 13:47:35 -0800 Subject: [PATCH 58/81] eeepc-wmi: kconfig changes to fix build errors Fix eeepc-wmi build when CONFIG_HOTPLUG_PCI is not enabled: eeepc-wmi.c:(.text+0x3bc5e9): undefined reference to `pci_hp_deregister' eeepc-wmi.c:(.text+0x3bcca4): undefined reference to `__pci_hp_register' Signed-off-by: Randy Dunlap Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0c7cf666a1b..0253157085d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -462,6 +462,7 @@ config ASUS_WMI depends on EXPERIMENTAL depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL || RFKILL = n + depends on HOTPLUG_PCI select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS From 2165136585b5c7d6f118f1d90fbde550bb7de212 Mon Sep 17 00:00:00 2001 From: Ike Panhc Date: Wed, 23 Feb 2011 21:39:59 +0800 Subject: [PATCH 59/81] ideapad: read brightness setting on brightness key notify BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=25922 On ideapad Y530, the brightness key notify will be blocked if the last notify is not responsed by getting the brightness value. Read value when we get the notify shall fix the problem and will not have any difference on other ideapads. Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 114d95247cd..21b101899ba 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -459,6 +459,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 if (vpc_bit == 4) + read_ec_data(handle, 0x12, &vpc2); else ideapad_input_report(priv, vpc_bit); } From 3b3e73f7d879f914f25605f74a71c2dad34bc329 Mon Sep 17 00:00:00 2001 From: Keng-Yu Lin Date: Tue, 1 Mar 2011 12:56:13 +0800 Subject: [PATCH 60/81] eeepc-wmi: set the touchpad toggle key code to KEY_TOUCHPAD_TOGGLE Signed-off-by: Keng-Yu Lin Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 64e36de2285..0ddc434fb93 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -63,7 +63,7 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0x32, { KEY_MUTE } }, { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5d, { KEY_WLAN } }, - { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ + { KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */ { KE_KEY, 0x82, { KEY_CAMERA } }, { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, { KE_KEY, 0x88, { KEY_WLAN } }, From f11113b22cd84b0adc355eaaa80be433005f5f45 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 26 Feb 2011 15:54:27 +0300 Subject: [PATCH 61/81] sony-laptop: make a couple variables static Sparse complains that these variables should be static. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d28a4a58a49..60d83343fb0 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -745,7 +745,7 @@ struct sony_nc_handles { struct device_attribute devattr; }; -struct sony_nc_handles *handles; +static struct sony_nc_handles *handles; static ssize_t sony_nc_handles_show(struct device *dev, struct device_attribute *attr, char *buffer) @@ -1358,7 +1358,7 @@ struct kbd_backlight { struct device_attribute timeout_attr; }; -struct kbd_backlight *kbdbl_handle; +static struct kbd_backlight *kbdbl_handle; static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) { From 7227dedbc22f66f1bd310c866053b08de0496e38 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 26 Feb 2011 15:54:57 +0300 Subject: [PATCH 62/81] sony-laptop: return negative on failure in sony_nc_add() There were two places in sony_nc_add() where we returned zero on failure instead of a negative error code. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 60d83343fb0..363b89ef61c 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1594,9 +1594,11 @@ static int sony_nc_add(struct acpi_device *device) if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", &handle))) { dprintk("Doing SNC setup\n"); - if (sony_nc_handles_setup(sony_pf_device)) + result = sony_nc_handles_setup(sony_pf_device); + if (result) goto outpresent; - if (sony_nc_kbd_backlight_setup(sony_pf_device)) + result = sony_nc_kbd_backlight_setup(sony_pf_device); + if (result) goto outsnc; sony_nc_function_setup(device); sony_nc_rfkill_setup(device); From 31f007598cc547ba3239524470386af8ae5f1c13 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 26 Feb 2011 15:55:24 +0300 Subject: [PATCH 63/81] sony-laptop: handle allocation failures Return -ENOMEM if kzalloc() fails. The callers already handle error returns. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 363b89ef61c..bd1b9adfbaf 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -768,6 +768,8 @@ static int sony_nc_handles_setup(struct platform_device *pd) int result; handles = kzalloc(sizeof(*handles), GFP_KERNEL); + if (!handles) + return -ENOMEM; sysfs_attr_init(&handles->devattr.attr); handles->devattr.attr.name = "handles"; @@ -1458,6 +1460,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd) return 0; kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL); + if (!kbdbl_handle) + return -ENOMEM; sysfs_attr_init(&kbdbl_handle->mode_attr.attr); kbdbl_handle->mode_attr.attr.name = "kbd_backlight"; From 200140bdb52b259380e9082e2a4f25a4ddbb5d68 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 27 Feb 2011 17:13:25 +0300 Subject: [PATCH 64/81] sony-laptop: potential null dereference In the original code, if "device_enum" was NULL then it would dereference it when it printed the error message. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index bd1b9adfbaf..4ab898a39fe 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1311,7 +1311,11 @@ static void sony_nc_rfkill_setup(struct acpi_device *device) } device_enum = (union acpi_object *) buffer.pointer; - if (!device_enum || device_enum->type != ACPI_TYPE_BUFFER) { + if (!device_enum) { + pr_err(DRV_PFX "No SN06 return object."); + goto out_no_enum; + } + if (device_enum->type != ACPI_TYPE_BUFFER) { pr_err(DRV_PFX "Invalid SN06 return object 0x%.2x\n", device_enum->type); goto out_no_enum; From 47ae4352bece7b617a084770495b993fcd1a2be5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 26 Feb 2011 23:03:34 +0800 Subject: [PATCH 65/81] platform-driver-x86: intel_mid_thermal: fix unterminated platform_device_id table The platform_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_mid_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 1215c309686..6c12db50316 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -543,6 +543,7 @@ static int mid_thermal_remove(struct platform_device *pdev) static const struct platform_device_id therm_id_table[] = { { DRIVER_NAME, 1 }, + { } }; static struct platform_driver mid_thermal_driver = { From 8941178efad900e48e44000208513a6426c74368 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 2 Mar 2011 01:07:11 +0800 Subject: [PATCH 66/81] acer-wmi: set the touchpad toggle key code to KEY_TOUCHPAD_TOGGLE Set the touchpad toggle key code from F22 to KEY_TOUCHPAD_TOGGLE, and userspace should use udev's key re-mapping facilities while X is unable to process keycodes above 255 to adjust to the keycode. Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c9784705f6a..a8acf35d747 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -108,7 +108,7 @@ static const struct key_entry acer_wmi_keymap[] = { {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_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */ {KE_END, 0} }; From 143a4c0284dc2378b3ce78866b3548d90121d843 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Mon, 7 Mar 2011 15:46:28 +0800 Subject: [PATCH 67/81] msi-laptop: send out touchpad on/off key MSI BIOS's raw behavior is send out KEY_TOUCHPAD_TOGGLE key when user pressed touchpad hotkey. Actually, we can capture the real touchpad status by read 0xE4 EC address on MSI netbook/notebook. So, add msi-laptop input device for send out KEY_TOUCHPAD_ON or KEY_TOUCHPAD_OFF key when user pressed Fn+F3 touchpad hotkey. It leave userland applications to know the real touchpad status. Tested on MSI netbook U-100, U-115, U160(N051), U160DX, N014, N034 Tested on MSI notebook CR620 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/msi-laptop.c | 81 ++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 142d3857931..fb4da28f590 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -60,6 +60,8 @@ #include #include #include +#include +#include #define MSI_DRIVER_VERSION "0.5" @@ -78,6 +80,9 @@ #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d #define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0) +#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 +#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) + static int msi_laptop_resume(struct platform_device *device); #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f @@ -90,6 +95,14 @@ static int auto_brightness; module_param(auto_brightness, int, 0); MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)"); +static const struct key_entry msi_laptop_keymap[] = { + {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */ + {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */ + {KE_END, 0} +}; + +static struct input_dev *msi_laptop_input_dev; + static bool old_ec_model; static int wlan_s, bluetooth_s, threeg_s; static int threeg_exists; @@ -605,6 +618,21 @@ static void msi_update_rfkill(struct work_struct *ignored) } static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); +static void msi_send_touchpad_key(struct work_struct *ignored) +{ + u8 rdata; + int result; + + result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); + if (result < 0) + return; + + sparse_keymap_report_event(msi_laptop_input_dev, + (rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ? + KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true); +} +static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); + static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { @@ -613,12 +641,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, if (str & 0x20) return false; - /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/ + /* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/ if (unlikely(data == 0xe0)) { extended = true; return false; } else if (unlikely(extended)) { + extended = false; switch (data) { + case 0xE4: + schedule_delayed_work(&msi_touchpad_work, + round_jiffies_relative(0.5 * HZ)); + break; case 0x54: case 0x62: case 0x76: @@ -626,7 +659,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str, round_jiffies_relative(0.5 * HZ)); break; } - extended = false; } return false; @@ -731,6 +763,42 @@ static int msi_laptop_resume(struct platform_device *device) return 0; } +static int __init msi_laptop_input_setup(void) +{ + int err; + + msi_laptop_input_dev = input_allocate_device(); + if (!msi_laptop_input_dev) + return -ENOMEM; + + msi_laptop_input_dev->name = "MSI Laptop hotkeys"; + msi_laptop_input_dev->phys = "msi-laptop/input0"; + msi_laptop_input_dev->id.bustype = BUS_HOST; + + err = sparse_keymap_setup(msi_laptop_input_dev, + msi_laptop_keymap, NULL); + if (err) + goto err_free_dev; + + err = input_register_device(msi_laptop_input_dev); + if (err) + goto err_free_keymap; + + return 0; + +err_free_keymap: + sparse_keymap_free(msi_laptop_input_dev); +err_free_dev: + input_free_device(msi_laptop_input_dev); + return err; +} + +static void msi_laptop_input_destroy(void) +{ + sparse_keymap_free(msi_laptop_input_dev); + input_unregister_device(msi_laptop_input_dev); +} + static int load_scm_model_init(struct platform_device *sdev) { u8 data; @@ -759,6 +827,11 @@ static int load_scm_model_init(struct platform_device *sdev) if (result < 0) goto fail_rfkill; + /* setup input device */ + result = msi_laptop_input_setup(); + if (result) + goto fail_input; + result = i8042_install_filter(msi_laptop_i8042_filter); if (result) { printk(KERN_ERR @@ -769,6 +842,9 @@ static int load_scm_model_init(struct platform_device *sdev) return 0; fail_filter: + msi_laptop_input_destroy(); + +fail_input: rfkill_cleanup(); fail_rfkill: @@ -886,6 +962,7 @@ static void __exit msi_cleanup(void) { if (load_scm_model) { i8042_remove_filter(msi_laptop_i8042_filter); + msi_laptop_input_destroy(); cancel_delayed_work_sync(&msi_rfkill_work); rfkill_cleanup(); } From 9a0b74fd873005122145364d3dfe4e1c9da1dad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sat, 26 Feb 2011 21:18:58 +0100 Subject: [PATCH 68/81] acer-wmi: deactive mail led when power off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch deactive mail led when laptop is going to hibernete/suspend or power off. After resume from hibernate/suspend correctly restore mail led state. Signed-off-by: Pali Rohár Tested-by: Pali Rohár Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index a8acf35d747..4c820937698 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -991,6 +991,7 @@ static int __devinit acer_led_init(struct device *dev) static void acer_led_exit(void) { + set_u32(LED_OFF, ACER_CAP_MAILLED); led_classdev_unregister(&mail_led); } @@ -1553,6 +1554,7 @@ pm_message_t state) if (has_cap(ACER_CAP_MAILLED)) { get_u32(&value, ACER_CAP_MAILLED); + set_u32(LED_OFF, ACER_CAP_MAILLED); data->mailled = value; } @@ -1580,6 +1582,17 @@ static int acer_platform_resume(struct platform_device *device) return 0; } +static void acer_platform_shutdown(struct platform_device *device) +{ + struct acer_data *data = &interface->data; + + if (!data) + return; + + if (has_cap(ACER_CAP_MAILLED)) + set_u32(LED_OFF, ACER_CAP_MAILLED); +} + static struct platform_driver acer_platform_driver = { .driver = { .name = "acer-wmi", @@ -1589,6 +1602,7 @@ static struct platform_driver acer_platform_driver = { .remove = acer_platform_remove, .suspend = acer_platform_suspend, .resume = acer_platform_resume, + .shutdown = acer_platform_shutdown, }; static struct platform_device *acer_platform_device; From 298f19b2547ba11a577a15ca329daa6f4bbf5ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 11 Mar 2011 12:36:43 -0500 Subject: [PATCH 69/81] acer-wmi: Fix WMI ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch change WMI ID to upper characters. With this patch module acer-wmi is automatically loaded when WMI ID is detected. Signed-off-by: Pali Rohár Tested-by: Pali Rohár Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 4c820937698..c1036d288f1 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -84,7 +84,7 @@ MODULE_LICENSE("GPL"); #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" #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_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A" #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" /* @@ -93,7 +93,7 @@ MODULE_LICENSE("GPL"); #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:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3"); MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026"); enum acer_wmi_event_ids { From 2d70b73ae5b85c9d13f5dfbb8fc4fd5edae633dc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Mar 2011 12:41:19 -0500 Subject: [PATCH 70/81] Platform: add Samsung Laptop platform driver This adds the samsung-laptop driver to the kernel. It now supports all known Samsung laptops that use the SABI interface. Signed-off-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-driver-samsung-laptop | 19 + drivers/platform/x86/Kconfig | 13 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/samsung-laptop.c | 832 ++++++++++++++++++ 4 files changed, 865 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-samsung-laptop create mode 100644 drivers/platform/x86/samsung-laptop.c diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop new file mode 100644 index 00000000000..0a810231aad --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -0,0 +1,19 @@ +What: /sys/devices/platform/samsung/performance_level +Date: January 1, 2010 +KernelVersion: 2.6.33 +Contact: Greg Kroah-Hartman +Description: Some Samsung laptops have different "performance levels" + that are can be modified by a function key, and by this + sysfs file. These values don't always make a whole lot + of sense, but some users like to modify them to keep + their fans quiet at all costs. Reading from this file + will show the current performance level. Writing to the + file can change this value. + Valid options: + "silent" + "normal" + "overclock" + Note that not all laptops support all of these options. + Specifically, not all support the "overclock" option, + and it's still unknown if this value even changes + anything, other than making the user feel a bit better. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0253157085d..2ee442c2a5d 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -739,4 +739,17 @@ config XO15_EBOOK This switch is triggered as the screen is rotated and folded down to convert the device into ebook form. +config SAMSUNG_LAPTOP + tristate "Samsung Laptop driver" + depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86 + ---help--- + This module implements a driver for a wide range of different + Samsung laptops. It offers control over the different + function keys, wireless LED, LCD backlight level, and + sometimes provides a "performance_control" sysfs file to allow + the performance level of the laptop to be changed. + + To compile this driver as a module, choose M here: the module + will be called samsung-laptop. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 0f7b23c0714..029e8861d08 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -40,4 +40,5 @@ obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o +obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c new file mode 100644 index 00000000000..e0b390d45d8 --- /dev/null +++ b/drivers/platform/x86/samsung-laptop.c @@ -0,0 +1,832 @@ +/* + * Samsung Laptop driver + * + * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de) + * Copyright (C) 2009,2011 Novell Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This driver is needed because a number of Samsung laptops do not hook + * their control settings through ACPI. So we have to poke around in the + * BIOS to do things like brightness values, and "special" key controls. + */ + +/* + * We have 0 - 8 as valid brightness levels. The specs say that level 0 should + * be reserved by the BIOS (which really doesn't make much sense), we tell + * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 + */ +#define MAX_BRIGHT 0x07 + + +#define SABI_IFACE_MAIN 0x00 +#define SABI_IFACE_SUB 0x02 +#define SABI_IFACE_COMPLETE 0x04 +#define SABI_IFACE_DATA 0x05 + +/* Structure to get data back to the calling function */ +struct sabi_retval { + u8 retval[20]; +}; + +struct sabi_header_offsets { + u8 port; + u8 re_mem; + u8 iface_func; + u8 en_mem; + u8 data_offset; + u8 data_segment; +}; + +struct sabi_commands { + /* + * Brightness is 0 - 8, as described above. + * Value 0 is for the BIOS to use + */ + u8 get_brightness; + u8 set_brightness; + + /* + * first byte: + * 0x00 - wireless is off + * 0x01 - wireless is on + * second byte: + * 0x02 - 3G is off + * 0x03 - 3G is on + * TODO, verify 3G is correct, that doesn't seem right... + */ + u8 get_wireless_button; + u8 set_wireless_button; + + /* 0 is off, 1 is on */ + u8 get_backlight; + u8 set_backlight; + + /* + * 0x80 or 0x00 - no action + * 0x81 - recovery key pressed + */ + u8 get_recovery_mode; + u8 set_recovery_mode; + + /* + * on seclinux: 0 is low, 1 is high, + * on swsmi: 0 is normal, 1 is silent, 2 is turbo + */ + u8 get_performance_level; + u8 set_performance_level; + + /* + * Tell the BIOS that Linux is running on this machine. + * 81 is on, 80 is off + */ + u8 set_linux; +}; + +struct sabi_performance_level { + const char *name; + u8 value; +}; + +struct sabi_config { + const char *test_string; + u16 main_function; + const struct sabi_header_offsets header_offsets; + const struct sabi_commands commands; + const struct sabi_performance_level performance_levels[4]; + u8 min_brightness; + u8 max_brightness; +}; + +static const struct sabi_config sabi_configs[] = { + { + .test_string = "SECLINUX", + + .main_function = 0x4c49, + + .header_offsets = { + .port = 0x00, + .re_mem = 0x02, + .iface_func = 0x03, + .en_mem = 0x04, + .data_offset = 0x05, + .data_segment = 0x07, + }, + + .commands = { + .get_brightness = 0x00, + .set_brightness = 0x01, + + .get_wireless_button = 0x02, + .set_wireless_button = 0x03, + + .get_backlight = 0x04, + .set_backlight = 0x05, + + .get_recovery_mode = 0x06, + .set_recovery_mode = 0x07, + + .get_performance_level = 0x08, + .set_performance_level = 0x09, + + .set_linux = 0x0a, + }, + + .performance_levels = { + { + .name = "silent", + .value = 0, + }, + { + .name = "normal", + .value = 1, + }, + { }, + }, + .min_brightness = 1, + .max_brightness = 8, + }, + { + .test_string = "SwSmi@", + + .main_function = 0x5843, + + .header_offsets = { + .port = 0x00, + .re_mem = 0x04, + .iface_func = 0x02, + .en_mem = 0x03, + .data_offset = 0x05, + .data_segment = 0x07, + }, + + .commands = { + .get_brightness = 0x10, + .set_brightness = 0x11, + + .get_wireless_button = 0x12, + .set_wireless_button = 0x13, + + .get_backlight = 0x2d, + .set_backlight = 0x2e, + + .get_recovery_mode = 0xff, + .set_recovery_mode = 0xff, + + .get_performance_level = 0x31, + .set_performance_level = 0x32, + + .set_linux = 0xff, + }, + + .performance_levels = { + { + .name = "normal", + .value = 0, + }, + { + .name = "silent", + .value = 1, + }, + { + .name = "overclock", + .value = 2, + }, + { }, + }, + .min_brightness = 0, + .max_brightness = 8, + }, + { }, +}; + +static const struct sabi_config *sabi_config; + +static void __iomem *sabi; +static void __iomem *sabi_iface; +static void __iomem *f0000_segment; +static struct backlight_device *backlight_device; +static struct mutex sabi_mutex; +static struct platform_device *sdev; +static struct rfkill *rfk; + +static int force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, + "Disable the DMI check and forces the driver to be loaded"); + +static int debug; +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +static int sabi_get_command(u8 command, struct sabi_retval *sretval) +{ + int retval = 0; + u16 port = readw(sabi + sabi_config->header_offsets.port); + u8 complete, iface_data; + + mutex_lock(&sabi_mutex); + + /* enable memory to be able to write to it */ + outb(readb(sabi + sabi_config->header_offsets.en_mem), port); + + /* write out the command */ + writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); + writew(command, sabi_iface + SABI_IFACE_SUB); + writeb(0, sabi_iface + SABI_IFACE_COMPLETE); + outb(readb(sabi + sabi_config->header_offsets.iface_func), port); + + /* write protect memory to make it safe */ + outb(readb(sabi + sabi_config->header_offsets.re_mem), port); + + /* see if the command actually succeeded */ + complete = readb(sabi_iface + SABI_IFACE_COMPLETE); + iface_data = readb(sabi_iface + SABI_IFACE_DATA); + if (complete != 0xaa || iface_data == 0xff) { + pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", + command, complete, iface_data); + retval = -EINVAL; + goto exit; + } + /* + * Save off the data into a structure so the caller use it. + * Right now we only want the first 4 bytes, + * There are commands that need more, but not for the ones we + * currently care about. + */ + sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA); + sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1); + sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2); + sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3); + +exit: + mutex_unlock(&sabi_mutex); + return retval; + +} + +static int sabi_set_command(u8 command, u8 data) +{ + int retval = 0; + u16 port = readw(sabi + sabi_config->header_offsets.port); + u8 complete, iface_data; + + mutex_lock(&sabi_mutex); + + /* enable memory to be able to write to it */ + outb(readb(sabi + sabi_config->header_offsets.en_mem), port); + + /* write out the command */ + writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN); + writew(command, sabi_iface + SABI_IFACE_SUB); + writeb(0, sabi_iface + SABI_IFACE_COMPLETE); + writeb(data, sabi_iface + SABI_IFACE_DATA); + outb(readb(sabi + sabi_config->header_offsets.iface_func), port); + + /* write protect memory to make it safe */ + outb(readb(sabi + sabi_config->header_offsets.re_mem), port); + + /* see if the command actually succeeded */ + complete = readb(sabi_iface + SABI_IFACE_COMPLETE); + iface_data = readb(sabi_iface + SABI_IFACE_DATA); + if (complete != 0xaa || iface_data == 0xff) { + pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n", + command, complete, iface_data); + retval = -EINVAL; + } + + mutex_unlock(&sabi_mutex); + return retval; +} + +static void test_backlight(void) +{ + struct sabi_retval sretval; + + sabi_get_command(sabi_config->commands.get_backlight, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); + + sabi_set_command(sabi_config->commands.set_backlight, 0); + printk(KERN_DEBUG "backlight should be off\n"); + + sabi_get_command(sabi_config->commands.get_backlight, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); + + msleep(1000); + + sabi_set_command(sabi_config->commands.set_backlight, 1); + printk(KERN_DEBUG "backlight should be on\n"); + + sabi_get_command(sabi_config->commands.get_backlight, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); +} + +static void test_wireless(void) +{ + struct sabi_retval sretval; + + sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); + + sabi_set_command(sabi_config->commands.set_wireless_button, 0); + printk(KERN_DEBUG "wireless led should be off\n"); + + sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); + + msleep(1000); + + sabi_set_command(sabi_config->commands.set_wireless_button, 1); + printk(KERN_DEBUG "wireless led should be on\n"); + + sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); +} + +static u8 read_brightness(void) +{ + struct sabi_retval sretval; + int user_brightness = 0; + int retval; + + retval = sabi_get_command(sabi_config->commands.get_brightness, + &sretval); + if (!retval) { + user_brightness = sretval.retval[0]; + if (user_brightness != 0) + user_brightness -= sabi_config->min_brightness; + } + return user_brightness; +} + +static void set_brightness(u8 user_brightness) +{ + u8 user_level = user_brightness - sabi_config->min_brightness; + + sabi_set_command(sabi_config->commands.set_brightness, user_level); +} + +static int get_brightness(struct backlight_device *bd) +{ + return (int)read_brightness(); +} + +static int update_status(struct backlight_device *bd) +{ + set_brightness(bd->props.brightness); + + if (bd->props.power == FB_BLANK_UNBLANK) + sabi_set_command(sabi_config->commands.set_backlight, 1); + else + sabi_set_command(sabi_config->commands.set_backlight, 0); + return 0; +} + +static const struct backlight_ops backlight_ops = { + .get_brightness = get_brightness, + .update_status = update_status, +}; + +static int rfkill_set(void *data, bool blocked) +{ + /* Do something with blocked...*/ + /* + * blocked == false is on + * blocked == true is off + */ + if (blocked) + sabi_set_command(sabi_config->commands.set_wireless_button, 0); + else + sabi_set_command(sabi_config->commands.set_wireless_button, 1); + + return 0; +} + +static struct rfkill_ops rfkill_ops = { + .set_block = rfkill_set, +}; + +static int init_wireless(struct platform_device *sdev) +{ + int retval; + + rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN, + &rfkill_ops, NULL); + if (!rfk) + return -ENOMEM; + + retval = rfkill_register(rfk); + if (retval) { + rfkill_destroy(rfk); + return -ENODEV; + } + + return 0; +} + +static void destroy_wireless(void) +{ + rfkill_unregister(rfk); + rfkill_destroy(rfk); +} + +static ssize_t get_performance_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sabi_retval sretval; + int retval; + int i; + + /* Read the state */ + retval = sabi_get_command(sabi_config->commands.get_performance_level, + &sretval); + if (retval) + return retval; + + /* The logic is backwards, yeah, lots of fun... */ + for (i = 0; sabi_config->performance_levels[i].name; ++i) { + if (sretval.retval[0] == sabi_config->performance_levels[i].value) + return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name); + } + return sprintf(buf, "%s\n", "unknown"); +} + +static ssize_t set_performance_level(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + if (count >= 1) { + int i; + for (i = 0; sabi_config->performance_levels[i].name; ++i) { + const struct sabi_performance_level *level = + &sabi_config->performance_levels[i]; + if (!strncasecmp(level->name, buf, strlen(level->name))) { + sabi_set_command(sabi_config->commands.set_performance_level, + level->value); + break; + } + } + if (!sabi_config->performance_levels[i].name) + return -EINVAL; + } + return count; +} +static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, + get_performance_level, set_performance_level); + + +static int __init dmi_check_cb(const struct dmi_system_id *id) +{ + pr_info("found laptop model '%s'\n", + id->ident); + return 0; +} + +static struct dmi_system_id __initdata samsung_dmi_table[] = { + { + .ident = "N128", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N128"), + DMI_MATCH(DMI_BOARD_NAME, "N128"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "N130", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N130"), + DMI_MATCH(DMI_BOARD_NAME, "N130"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "X125", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X125"), + DMI_MATCH(DMI_BOARD_NAME, "X125"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "X120/X170", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"), + DMI_MATCH(DMI_BOARD_NAME, "X120/X170"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "NC10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "NC10"), + DMI_MATCH(DMI_BOARD_NAME, "NC10"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "NP-Q45", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"), + DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "X360", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "X360"), + DMI_MATCH(DMI_BOARD_NAME, "X360"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "R518", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R518"), + DMI_MATCH(DMI_BOARD_NAME, "R518"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "R519/R719", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"), + DMI_MATCH(DMI_BOARD_NAME, "R519/R719"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "N150/N210/N220", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), + DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "N150P/N210P/N220P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"), + DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "R530/R730", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"), + DMI_MATCH(DMI_BOARD_NAME, "R530/R730"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "NF110/NF210/NF310", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), + DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "N145P/N250P/N260P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), + DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "R70/R71", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"), + DMI_MATCH(DMI_BOARD_NAME, "R70/R71"), + }, + .callback = dmi_check_cb, + }, + { + .ident = "P460", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "P460"), + DMI_MATCH(DMI_BOARD_NAME, "P460"), + }, + .callback = dmi_check_cb, + }, + { }, +}; +MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); + +static int find_signature(void __iomem *memcheck, const char *testStr) +{ + int i = 0; + int loca; + + for (loca = 0; loca < 0xffff; loca++) { + char temp = readb(memcheck + loca); + + if (temp == testStr[i]) { + if (i == strlen(testStr)-1) + break; + ++i; + } else { + i = 0; + } + } + return loca; +} + +static int __init samsung_init(void) +{ + struct backlight_properties props; + struct sabi_retval sretval; + unsigned int ifaceP; + int i; + int loca; + int retval; + + mutex_init(&sabi_mutex); + + if (!force && !dmi_check_system(samsung_dmi_table)) + return -ENODEV; + + f0000_segment = ioremap_nocache(0xf0000, 0xffff); + if (!f0000_segment) { + pr_err("Can't map the segment at 0xf0000\n"); + return -EINVAL; + } + + /* Try to find one of the signatures in memory to find the header */ + for (i = 0; sabi_configs[i].test_string != 0; ++i) { + sabi_config = &sabi_configs[i]; + loca = find_signature(f0000_segment, sabi_config->test_string); + if (loca != 0xffff) + break; + } + + if (loca == 0xffff) { + pr_err("This computer does not support SABI\n"); + goto error_no_signature; + } + + /* point to the SMI port Number */ + loca += 1; + sabi = (f0000_segment + loca); + + if (debug) { + printk(KERN_DEBUG "This computer supports SABI==%x\n", + loca + 0xf0000 - 6); + printk(KERN_DEBUG "SABI header:\n"); + printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", + readw(sabi + sabi_config->header_offsets.port)); + printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", + readb(sabi + sabi_config->header_offsets.iface_func)); + printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", + readb(sabi + sabi_config->header_offsets.en_mem)); + printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", + readb(sabi + sabi_config->header_offsets.re_mem)); + printk(KERN_DEBUG " SABI data offset = 0x%04x\n", + readw(sabi + sabi_config->header_offsets.data_offset)); + printk(KERN_DEBUG " SABI data segment = 0x%04x\n", + readw(sabi + sabi_config->header_offsets.data_segment)); + } + + /* Get a pointer to the SABI Interface */ + ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4; + ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff; + sabi_iface = ioremap_nocache(ifaceP, 16); + if (!sabi_iface) { + pr_err("Can't remap %x\n", ifaceP); + goto exit; + } + if (debug) { + printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); + printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface); + + test_backlight(); + test_wireless(); + + retval = sabi_get_command(sabi_config->commands.get_brightness, + &sretval); + printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); + } + + /* Turn on "Linux" mode in the BIOS */ + if (sabi_config->commands.set_linux != 0xff) { + retval = sabi_set_command(sabi_config->commands.set_linux, + 0x81); + if (retval) { + pr_warn("Linux mode was not set!\n"); + goto error_no_platform; + } + } + + /* knock up a platform device to hang stuff off of */ + sdev = platform_device_register_simple("samsung", -1, NULL, 0); + if (IS_ERR(sdev)) + goto error_no_platform; + + /* create a backlight device to talk to this one */ + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = sabi_config->max_brightness; + backlight_device = backlight_device_register("samsung", &sdev->dev, + NULL, &backlight_ops, + &props); + if (IS_ERR(backlight_device)) + goto error_no_backlight; + + backlight_device->props.brightness = read_brightness(); + backlight_device->props.power = FB_BLANK_UNBLANK; + backlight_update_status(backlight_device); + + retval = init_wireless(sdev); + if (retval) + goto error_no_rfk; + + retval = device_create_file(&sdev->dev, &dev_attr_performance_level); + if (retval) + goto error_file_create; + +exit: + return 0; + +error_file_create: + destroy_wireless(); + +error_no_rfk: + backlight_device_unregister(backlight_device); + +error_no_backlight: + platform_device_unregister(sdev); + +error_no_platform: + iounmap(sabi_iface); + +error_no_signature: + iounmap(f0000_segment); + return -EINVAL; +} + +static void __exit samsung_exit(void) +{ + /* Turn off "Linux" mode in the BIOS */ + if (sabi_config->commands.set_linux != 0xff) + sabi_set_command(sabi_config->commands.set_linux, 0x80); + + device_remove_file(&sdev->dev, &dev_attr_performance_level); + backlight_device_unregister(backlight_device); + destroy_wireless(); + iounmap(sabi_iface); + iounmap(f0000_segment); + platform_device_unregister(sdev); +} + +module_init(samsung_init); +module_exit(samsung_exit); + +MODULE_AUTHOR("Greg Kroah-Hartman "); +MODULE_DESCRIPTION("Samsung Backlight driver"); +MODULE_LICENSE("GPL"); From bbe24fee22fa045a07ff7e926589ac722c7ee78d Mon Sep 17 00:00:00 2001 From: Joey Lee Date: Wed, 16 Mar 2011 01:55:19 -0600 Subject: [PATCH 71/81] msi-laptop: use pr_ for messages msi-laptop: use pr_ for messages Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/msi-laptop.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index fb4da28f590..23fb2afda00 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -51,6 +51,8 @@ * laptop as MSI S270. YMMV. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -445,8 +447,7 @@ static struct platform_device *msipf_device; static int dmi_check_cb(const struct dmi_system_id *id) { - printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n", - id->ident); + pr_info("Identified laptop model '%s'.\n", id->ident); return 1; } @@ -834,8 +835,7 @@ static int load_scm_model_init(struct platform_device *sdev) result = i8042_install_filter(msi_laptop_i8042_filter); if (result) { - printk(KERN_ERR - "msi-laptop: Unable to install key filter\n"); + pr_err("Unable to install key filter\n"); goto fail_filter; } @@ -875,7 +875,7 @@ static int __init msi_init(void) /* Register backlight stuff */ if (acpi_video_backlight_support()) { - printk(KERN_INFO "MSI: Brightness ignored, must be controlled " + pr_info("Brightness ignored, must be controlled " "by ACPI video driver\n"); } else { struct backlight_properties props; @@ -930,7 +930,7 @@ static int __init msi_init(void) if (auto_brightness != 2) set_auto_brightness(auto_brightness); - printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n"); + pr_info("driver "MSI_DRIVER_VERSION" successfully loaded.\n"); return 0; @@ -978,7 +978,7 @@ static void __exit msi_cleanup(void) if (auto_brightness != 2) set_auto_brightness(1); - printk(KERN_INFO "msi-laptop: driver unloaded.\n"); + pr_info("driver unloaded.\n"); } module_init(msi_init); From 0e4510f7c9708304104c07e87b7e5e78fb1716d1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 14 Mar 2011 18:53:14 +0800 Subject: [PATCH 72/81] platform-driver-x86: fix wrong merge for compal-laptop.c I found the commit 80183a4b "compal-laptop/fujitsu-laptop/msi-laptop: make dmi_check_cb to return 1 instead of 0" has wrong patch merge. The original patch change the return value for dmi_check_cb(): https://lkml.org/lkml/2010/7/2/88 But commit 80183a4b changed the return value for set_backlight_level. Signed-off-by: Axel Lin Signed-off-by: Matthew Garrett --- drivers/platform/x86/compal-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index eb95878fa58..3994c80549f 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -275,7 +275,7 @@ static int set_backlight_level(int level) ec_write(BACKLIGHT_LEVEL_ADDR, level); - return 1; + return 0; } static int get_backlight_level(void) @@ -763,7 +763,7 @@ static int dmi_check_cb(const struct dmi_system_id *id) printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n", id->ident); extra_features = false; - return 0; + return 1; } static int dmi_check_cb_extra(const struct dmi_system_id *id) @@ -772,7 +772,7 @@ static int dmi_check_cb_extra(const struct dmi_system_id *id) "enabling extra features\n", id->ident); extra_features = true; - return 0; + return 1; } static struct dmi_system_id __initdata compal_dmi_table[] = { From 2783658477205b0cf31e5c02d69214e36fe59aae Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 14 Mar 2011 18:56:18 +0800 Subject: [PATCH 73/81] platform-driver-x86: samsung-laptop: make dmi_check_cb to return 1 instead of 0 dmi_check_system() walks the table running matching functions until someone returns non zero or we hit the end. This patch makes dmi_check_cb to return 1 so dmi_check_system() return immediately when a match is found. Signed-off-by: Axel Lin Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index e0b390d45d8..de434c6dc2d 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -496,7 +496,7 @@ static int __init dmi_check_cb(const struct dmi_system_id *id) { pr_info("found laptop model '%s'\n", id->ident); - return 0; + return 1; } static struct dmi_system_id __initdata samsung_dmi_table[] = { From 0986f25fbba8827762b35222bb45d2fbb2d72fa4 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2011 10:06:23 +0300 Subject: [PATCH 74/81] asus-wmi: signedness bug in read_brightness() "err" needs to be signed for the error handling to work. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 5b779a9443d..a038595200e 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -946,7 +946,8 @@ static int read_brightness_max(struct asus_wmi *asus) static int read_brightness(struct backlight_device *bd) { struct asus_wmi *asus = bl_get_data(bd); - u32 retval, err; + u32 retval; + int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); From a1d6086739c5b8f23a48e02e62b0e495321a2122 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 Mar 2011 10:07:37 +0300 Subject: [PATCH 75/81] asus-wmi: potential NULL dereference in show_call() In the earlier check we assumed that "obj" could be NULL. I looked at some of the other places that call evaluate_object() and they check for NULL as well. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a038595200e..efc776cb0c6 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1343,7 +1343,7 @@ static int show_call(struct seq_file *m, void *data) else seq_printf(m, "%#x(%#x, %#x) = t:%d\n", asus->debug.method_id, asus->debug.dev_id, asus->debug.ctrl_param, - obj->type); + obj ? obj->type : -1); kfree(obj); From cae157026491d2e9a789b876a367dee7b17235cb Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Wed, 16 Mar 2011 18:52:36 +0800 Subject: [PATCH 76/81] acer-wmi: use pr_ for messages acer-wmi: use pr_ for messages Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Corentin Chary Cc: Dmitry Torokhov Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 68 ++++++++++++++------------------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c1036d288f1..d798314b81f 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -22,6 +22,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -46,12 +48,6 @@ MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); MODULE_LICENSE("GPL"); -#define ACER_LOGPREFIX "acer-wmi: " -#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 * Meaning is unknown - this number is required for writing to ACPI for AMW0 @@ -845,7 +841,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) has_type_aa = true; type_aa = (struct hotkey_function_type_aa *) header; - printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n", + pr_info("Function bitmap for Communication Button: 0x%x\n", type_aa->commun_func_bitmap); if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) @@ -1037,7 +1033,7 @@ static int __devinit acer_backlight_init(struct device *dev) bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops, &props); if (IS_ERR(bd)) { - printk(ACER_ERR "Could not register Acer backlight device\n"); + pr_err("Could not register Acer backlight device\n"); acer_backlight_device = NULL; return PTR_ERR(bd); } @@ -1084,8 +1080,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device) return AE_ERROR; } if (obj->buffer.length != 8) { - printk(ACER_WARNING "Unknown buffer length %d\n", - obj->buffer.length); + pr_warning("Unknown buffer length %d\n", obj->buffer.length); kfree(obj); return AE_ERROR; } @@ -1094,7 +1089,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device) kfree(obj); if (return_value.error_code || return_value.ec_return_value) - printk(ACER_WARNING "Get Device Status failed: " + pr_warning("Get Device Status failed: " "0x%x - 0x%x\n", return_value.error_code, return_value.ec_return_value); else @@ -1310,7 +1305,7 @@ static void acer_wmi_notify(u32 value, void *context) status = wmi_get_event_data(value, &response); if (status != AE_OK) { - printk(ACER_WARNING "bad event status 0x%x\n", status); + pr_warning("bad event status 0x%x\n", status); return; } @@ -1319,14 +1314,12 @@ static void acer_wmi_notify(u32 value, void *context) if (!obj) return; if (obj->type != ACPI_TYPE_BUFFER) { - printk(ACER_WARNING "Unknown response received %d\n", - obj->type); + pr_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); + pr_warning("Unknown buffer length %d\n", obj->buffer.length); kfree(obj); return; } @@ -1338,11 +1331,11 @@ static void acer_wmi_notify(u32 value, void *context) 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", + pr_warning("Unknown key number - 0x%x\n", return_value.key_num); break; default: - printk(ACER_WARNING "Unknown function number - %d - %d\n", + pr_warning("Unknown function number - %d - %d\n", return_value.function, return_value.key_num); break; } @@ -1371,8 +1364,7 @@ wmid3_set_lm_mode(struct lm_input_params *params, return AE_ERROR; } if (obj->buffer.length != 4) { - printk(ACER_WARNING "Unknown buffer length %d\n", - obj->buffer.length); + pr_warning("Unknown buffer length %d\n", obj->buffer.length); kfree(obj); return AE_ERROR; } @@ -1397,11 +1389,11 @@ static int acer_wmi_enable_ec_raw(void) 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: " + pr_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"); + pr_info("Enabled EC raw mode"); return status; } @@ -1420,7 +1412,7 @@ static int acer_wmi_enable_lm(void) 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: " + pr_warning("Enabling Launch Manager failed: " "0x%x - 0x%x\n", return_value.error_code, return_value.ec_return_value); @@ -1650,7 +1642,7 @@ static int create_debugfs(void) { interface->debug.root = debugfs_create_dir("acer-wmi", NULL); if (!interface->debug.root) { - printk(ACER_ERR "Failed to create debugfs directory"); + pr_err("Failed to create debugfs directory"); return -ENOMEM; } @@ -1671,11 +1663,10 @@ static int __init acer_wmi_init(void) { int err; - printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n"); + pr_info("Acer Laptop ACPI-WMI Extras\n"); if (dmi_check_system(acer_blacklist)) { - printk(ACER_INFO "Blacklisted hardware detected - " - "not loading\n"); + pr_info("Blacklisted hardware detected - not loading\n"); return -ENODEV; } @@ -1692,12 +1683,11 @@ static int __init acer_wmi_init(void) if (wmi_has_guid(WMID_GUID2) && interface) { if (ACPI_FAILURE(WMID_set_capabilities())) { - printk(ACER_ERR "Unable to detect available WMID " - "devices\n"); + pr_err("Unable to detect available WMID devices\n"); return -ENODEV; } } else if (!wmi_has_guid(WMID_GUID2) && interface) { - printk(ACER_ERR "No WMID device detection method found\n"); + pr_err("No WMID device detection method found\n"); return -ENODEV; } @@ -1705,8 +1695,7 @@ static int __init acer_wmi_init(void) interface = &AMW0_interface; if (ACPI_FAILURE(AMW0_set_capabilities())) { - printk(ACER_ERR "Unable to detect available AMW0 " - "devices\n"); + pr_err("Unable to detect available AMW0 devices\n"); return -ENODEV; } } @@ -1715,8 +1704,7 @@ static int __init acer_wmi_init(void) AMW0_find_mailled(); if (!interface) { - printk(ACER_INFO "No or unsupported WMI interface, unable to " - "load\n"); + pr_err("No or unsupported WMI interface, unable to load\n"); return -ENODEV; } @@ -1724,22 +1712,22 @@ static int __init acer_wmi_init(void) if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { interface->capability &= ~ACER_CAP_BRIGHTNESS; - printk(ACER_INFO "Brightness must be controlled by " + pr_info("Brightness must be controlled by " "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"); + pr_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"); + pr_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"); + pr_info("No WMID EC raw mode enable method\n"); } if (wmi_has_guid(ACERWMID_EVENT_GUID)) { @@ -1750,7 +1738,7 @@ static int __init acer_wmi_init(void) err = platform_driver_register(&acer_platform_driver); if (err) { - printk(ACER_ERR "Unable to register platform driver.\n"); + pr_err("Unable to register platform driver.\n"); goto error_platform_register; } @@ -1805,7 +1793,7 @@ static void __exit acer_wmi_exit(void) platform_device_unregister(acer_platform_device); platform_driver_unregister(&acer_platform_driver); - printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n"); + pr_info("Acer Laptop WMI Extras unloaded\n"); return; } From c8440336fe376036e473554c30f7266987961734 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Thu, 17 Mar 2011 17:18:22 -0300 Subject: [PATCH 77/81] platform-drivers: x86: fix common misspellings Signed-off-by: Lucas De Marchi Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 2 +- drivers/platform/x86/compal-laptop.c | 2 +- drivers/platform/x86/eeepc-laptop.c | 2 +- drivers/platform/x86/intel_rar_register.c | 2 +- drivers/platform/x86/intel_scu_ipc.c | 2 +- drivers/platform/x86/sony-laptop.c | 6 +++--- drivers/platform/x86/thinkpad_acpi.c | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 62ef43ef06d..c53b3ff7978 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -29,7 +29,7 @@ * John Belmonte - ACPI code for Toshiba laptop was a good starting point. * Eric Burghard - LED display support for W1N * Josh Green - Light Sens support - * Thomas Tuttle - His first patch for led support was very helpfull + * Thomas Tuttle - His first patch for led support was very helpful * Sam Lin - GPS support */ diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 3994c80549f..c16a27641ce 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -201,7 +201,7 @@ static bool extra_features; * into 0x4F and read a few bytes from the output, like so: * u8 writeData = 0x33; * ec_transaction(0x4F, &writeData, 1, buffer, 32, 0); - * That address is labled "fan1 table information" in the service manual. + * That address is labelled "fan1 table information" in the service manual. * It should be clear which value in 'buffer' changes). This seems to be * related to fan speed. It isn't a proper 'realtime' fan speed value * though, because physically stopping or speeding up the fan doesn't diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 6605beac0d0..5f2dd386152 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1322,7 +1322,7 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name) { int dummy; - /* Some BIOSes do not report cm although it is avaliable. + /* Some BIOSes do not report cm although it is available. Check if cm_getv[cm] works and, if yes, assume cm should be set. */ if (!(eeepc->cm_supported & (1 << cm)) && !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) { diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c index 2b11a33325e..bde47e9080c 100644 --- a/drivers/platform/x86/intel_rar_register.c +++ b/drivers/platform/x86/intel_rar_register.c @@ -485,7 +485,7 @@ EXPORT_SYMBOL(rar_lock); * * The register_rar function is to used by other device drivers * to ensure that this driver is ready. As we cannot be sure of - * the compile/execute order of drivers in ther kernel, it is + * the compile/execute order of drivers in the kernel, it is * best to give this driver a callback function to call when * it is ready to give out addresses. The callback function * would have those steps that continue the initialization of diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index a91d510a798..940accbe28d 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; version 2 * of the License. * - * SCU runing in ARC processor communicates with other entity running in IA + * SCU running in ARC processor communicates with other entity running in IA * core through IPC mechanism which in turn messaging between IA core ad SCU. * SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and * SCU where IPC-2 is used between P-Unit and SCU. This driver delas with diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 4ab898a39fe..e642f5f2950 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -605,7 +605,7 @@ struct sony_nc_value { int value; /* current setting */ int valid; /* Has ever been set */ int debug; /* active only in debug mode ? */ - struct device_attribute devattr; /* sysfs atribute */ + struct device_attribute devattr; /* sysfs attribute */ }; #define SNC_HANDLE_NAMES(_name, _values...) \ @@ -3129,7 +3129,7 @@ static int sony_pic_add(struct acpi_device *device) /* request io port */ list_for_each_entry_reverse(io, &spic_dev.ioports, list) { if (request_region(io->io1.minimum, io->io1.address_length, - "Sony Programable I/O Device")) { + "Sony Programmable I/O Device")) { dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n", io->io1.minimum, io->io1.maximum, io->io1.address_length); @@ -3137,7 +3137,7 @@ static int sony_pic_add(struct acpi_device *device) if (io->io2.minimum) { if (request_region(io->io2.minimum, io->io2.address_length, - "Sony Programable I/O Device")) { + "Sony Programmable I/O Device")) { dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n", io->io2.minimum, io->io2.maximum, io->io2.address_length); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 947bdcaa0ce..a08561f5349 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2407,7 +2407,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, * This code is supposed to duplicate the IBM firmware behaviour: * - Pressing MUTE issues mute hotkey message, even when already mute * - Pressing Volume up/down issues volume up/down hotkey messages, - * even when already at maximum or minumum volume + * even when already at maximum or minimum volume * - The act of unmuting issues volume up/down notification, * depending which key was used to unmute * @@ -2990,7 +2990,7 @@ static void tpacpi_send_radiosw_update(void) * rfkill input events, or we will race the rfkill core input * handler. * - * tpacpi_inputdev_send_mutex works as a syncronization point + * tpacpi_inputdev_send_mutex works as a synchronization point * for the above. * * We optimize to avoid numerous calls to hotkey_get_wlsw. From 8215af019040ce9182728afee9642d8fdeb17f59 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Mon, 28 Mar 2011 16:52:02 +0800 Subject: [PATCH 78/81] acer-wmi: does not set persistence state by rfkill_init_sw_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acer BIOS keeps devices state when system reboot, but reset to default device states (Wlan on, Bluetooth off, wwan on) if system cold boot. That means BIOS's initial state is not always real persistence. So, removed rfkill_init_sw_state because it sets initial state to persistence then replicate to other new killswitch when rfkill-input enabled. After removed it, acer-wmi set initial soft-block state after rfkill register, and doesn't allow set_block until rfkill initial finished. Reference: bko#31002 https://bugzilla.kernel.org/show_bug.cgi?id=31002 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Oldřich Jedlička Cc: Johannes Berg Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index d798314b81f..652a84ec36c 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -217,6 +217,7 @@ struct acer_debug { static struct rfkill *wireless_rfkill; static struct rfkill *bluetooth_rfkill; static struct rfkill *threeg_rfkill; +static bool rfkill_inited; /* Each low-level interface must define at least some of the following */ struct wmi_interface { @@ -1157,9 +1158,13 @@ static int acer_rfkill_set(void *data, bool blocked) { acpi_status status; u32 cap = (unsigned long)data; - status = set_u32(!blocked, cap); - if (ACPI_FAILURE(status)) - return -ENODEV; + + if (rfkill_inited) { + status = set_u32(!blocked, cap); + if (ACPI_FAILURE(status)) + return -ENODEV; + } + return 0; } @@ -1183,14 +1188,16 @@ static struct rfkill *acer_rfkill_register(struct device *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); return ERR_PTR(err); } + + if (ACPI_SUCCESS(status)) + rfkill_set_sw_state(rfkill_dev, !state); + return rfkill_dev; } @@ -1225,6 +1232,8 @@ static int acer_rfkill_init(struct device *dev) } } + rfkill_inited = true; + schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); return 0; From 70a9b9047aebd53ac38837a1046da52a2f8d9636 Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Mon, 28 Mar 2011 06:34:13 -0400 Subject: [PATCH 79/81] acer-wmi: does not poll device status when WMI event is available Acer WMI hotkey event's result include current device status, just need sync the status to killswitch after acer-wmi driver receive hotkey event but not always poll device status. This is good for performance. But, if use EC raw mode, Acer BIOS will not emit wmi event and leave EC to control device status. So, still startup polling job when doesn't detect WMI event GUID or user choice to use ec_raw_mode. Tested on Acer TravelMate 8572 notebook. Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 652a84ec36c..5ea6c3477d1 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1234,14 +1234,17 @@ static int acer_rfkill_init(struct device *dev) rfkill_inited = true; - schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); + if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) + schedule_delayed_work(&acer_rfkill_work, + round_jiffies_relative(HZ)); return 0; } static void acer_rfkill_exit(void) { - cancel_delayed_work_sync(&acer_rfkill_work); + if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) + cancel_delayed_work_sync(&acer_rfkill_work); rfkill_unregister(wireless_rfkill); rfkill_destroy(wireless_rfkill); @@ -1338,6 +1341,19 @@ static void acer_wmi_notify(u32 value, void *context) switch (return_value.function) { case WMID_HOTKEY_EVENT: + if (return_value.device_state) { + u16 device_state = return_value.device_state; + pr_debug("deivces states: 0x%x\n", device_state); + if (has_cap(ACER_CAP_WIRELESS)) + rfkill_set_sw_state(wireless_rfkill, + !(device_state & ACER_WMID3_GDS_WIRELESS)); + if (has_cap(ACER_CAP_BLUETOOTH)) + rfkill_set_sw_state(bluetooth_rfkill, + !(device_state & ACER_WMID3_GDS_BLUETOOTH)); + if (has_cap(ACER_CAP_THREEG)) + rfkill_set_sw_state(threeg_rfkill, + !(device_state & ACER_WMID3_GDS_THREEG)); + } if (!sparse_keymap_report_event(acer_wmi_input_dev, return_value.key_num, 1, true)) pr_warning("Unknown key number - 0x%x\n", From a3424216e4935221fdaa5ca3c26e024f11297164 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 28 Mar 2011 06:36:30 -0400 Subject: [PATCH 80/81] ips: use interruptible waits in ips-monitor This is what I intended to do since: 1) the driver handles variable waits just fine, and 2) interruptible waits aren't reported as load in the load avg. Reported-and-tested-by: Andreas Hartmann Signed-off-by: Jesse Barnes 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 1294a39373b..85c8ad43c0c 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1111,7 +1111,7 @@ static int ips_monitor(void *data) last_msecs = jiffies_to_msecs(jiffies); expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); - __set_current_state(TASK_UNINTERRUPTIBLE); + __set_current_state(TASK_INTERRUPTIBLE); mod_timer(&timer, expire); schedule(); From bd1573a5546b4351b5d042f1e4cd631ea67cc6b0 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 28 Mar 2011 06:40:23 -0400 Subject: [PATCH 81/81] xo15-ebook: Remove device.wakeup_count This is handled automatically now. Signed-off-by: Matthew Garrett --- drivers/platform/x86/xo15-ebook.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 2343bb36e56..c1372ed9d2e 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -133,7 +133,6 @@ static int ebook_switch_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count++; device_set_wakeup_enable(&device->dev, true); }