From d094485323a1f2abc7e4665700d6036de36fdaef Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 11 Feb 2010 10:40:13 -0500 Subject: MAINTAINERS: update drivers/platform/x86 information Many of the drivers/platform/x86 drivers have nothing to do with ACPI, so it's kind of inappropriate for them to be stuck under the ACPI mailing list. Add a new mailing list (platform-driver-x86@vger.kernel.org) and, with Len's blessing, add myself as subsystem maintainer. Signed-off-by: Matthew Garrett Cc: Anisse Astier Cc: Carlos Corbacho Cc: Cezary Jackiewicz Cc: Corentin Chary Cc: Daniel Oliveira Nascimento Cc: Harald Welte Cc: Henrique de Moraes Holschuh Cc: Herton Ronaldo Krzesinski Cc: Jonathan Woithe Cc: Karol Kozimor Cc: Len Brown Cc: Lennart Poettering Cc: Mattia Dongili Cc: Peter Feuerer Cc: Sujith Thomas Cc: Thadeu Lima de Souza Cascardo --- MAINTAINERS | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 2533fc45a44a..f355d11e453c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -221,6 +221,7 @@ F: drivers/net/acenic* ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER M: Peter Feuerer +L: platform-driver-x86@vger.kernel.org W: http://piie.net/?section=acerhdf S: Maintained F: drivers/platform/x86/acerhdf.c @@ -228,6 +229,7 @@ F: drivers/platform/x86/acerhdf.c ACER WMI LAPTOP EXTRAS M: Carlos Corbacho L: aceracpi@googlegroups.com (subscribers-only) +L: platform-driver-x86@vger.kernel.org W: http://code.google.com/p/aceracpi S: Maintained F: drivers/platform/x86/acer-wmi.c @@ -288,7 +290,7 @@ F: drivers/acpi/video.c ACPI WMI DRIVER M: Carlos Corbacho -L: linux-acpi@vger.kernel.org +L: platform-driver-x86@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Maintained F: drivers/platform/x86/wmi.c @@ -968,6 +970,7 @@ ASUS ACPI EXTRAS DRIVER 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 @@ -981,6 +984,7 @@ 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 @@ -1473,6 +1477,7 @@ F: drivers/scsi/fnic/ CMPC ACPI DRIVER M: Thadeu Lima de Souza Cascardo M: Daniel Oliveira Nascimento +L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/classmate-laptop.c @@ -1516,6 +1521,7 @@ F: drivers/pci/hotplug/cpcihp_generic.c COMPAL LAPTOP SUPPORT M: Cezary Jackiewicz +L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/compal-laptop.c @@ -1746,6 +1752,7 @@ F: drivers/net/defxx.* DELL LAPTOP DRIVER M: Matthew Garrett +L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/dell-laptop.c @@ -2028,6 +2035,7 @@ F: drivers/edac/r82600_edac.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 @@ -2295,7 +2303,7 @@ F: arch/frv/ FUJITSU LAPTOP EXTRAS M: Jonathan Woithe -L: linux-acpi@vger.kernel.org +L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/fujitsu-laptop.c @@ -2561,6 +2569,7 @@ F: drivers/net/wireless/hostap/ HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER M: Carlos Corbacho +L: platform-driver-x86@vger.kernel.org S: Odd Fixes F: drivers/platform/x86/tc1100-wmi.c @@ -2771,7 +2780,7 @@ F: drivers/video/i810/ INTEL MENLOW THERMAL DRIVER M: Sujith Thomas -L: linux-acpi@vger.kernel.org +L: platform-driver-x86@vger.kernel.org W: http://www.lesswatts.org/projects/acpi/ S: Supported F: drivers/platform/x86/intel_menlow.c @@ -3637,6 +3646,7 @@ F: drivers/char/mxser.* MSI LAPTOP SUPPORT M: Lennart Poettering +L: platform-driver-x86@vger.kernel.org W: https://tango.0pointer.de/mailman/listinfo/s270-linux W: http://0pointer.de/lennart/tchibo.html S: Maintained @@ -3644,6 +3654,7 @@ F: drivers/platform/x86/msi-laptop.c MSI WMI SUPPORT M: Anisse Astier +L: platform-driver-x86@vger.kernel.org S: Supported F: drivers/platform/x86/msi-wmi.c @@ -4096,6 +4107,7 @@ F: drivers/i2c/busses/i2c-pasemi.c PANASONIC LAPTOP ACPI EXTRAS DRIVER M: Harald Welte +L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/panasonic-laptop.c @@ -5034,7 +5046,7 @@ F: include/linux/ssb/ SONY VAIO CONTROL DEVICE DRIVER M: Mattia Dongili -L: linux-acpi@vger.kernel.org +L: platform-driver-x86@vger.kernel.org W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers S: Maintained F: Documentation/laptops/sony-laptop.txt @@ -5240,6 +5252,7 @@ F: arch/xtensa/ THINKPAD ACPI EXTRAS DRIVER M: Henrique de Moraes Holschuh L: ibm-acpi-devel@lists.sourceforge.net +L: platform-driver-x86@vger.kernel.org W: http://ibm-acpi.sourceforge.net W: http://thinkwiki.org/wiki/Ibm-acpi T: git git://repo.or.cz/linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git @@ -5293,10 +5306,12 @@ F: security/tomoyo/ TOPSTAR LAPTOP EXTRAS DRIVER M: Herton Ronaldo Krzesinski +L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/topstar-laptop.c TOSHIBA ACPI EXTRAS DRIVER +L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/toshiba_acpi.c @@ -6024,6 +6039,12 @@ S: Maintained F: Documentation/x86/ F: arch/x86/ +X86 PLATFORM DRIVERS +M: Matthew Garrett +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86 + XEN HYPERVISOR INTERFACE M: Jeremy Fitzhardinge M: Chris Wright -- cgit v1.2.3 From 34325b9d2a7d36e2ba74a6652f2e3e8d57dfb145 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 24 Aug 2009 16:00:47 -0500 Subject: compal-laptop: Add support for known Compal made Dell laptops The following Dell laptops are known to have been manufacturer by Compal and are supported by the compal-laptop platform driver - Mini 9 - Mini 10 - Mini 12 - Mini 10v - Inspiron 11z Signed-off-by: Mario Limonciello Signed-off-by: Tim Gardner Signed-off-by: Matthew Garrett Cc: Cezary Jackiewicz --- drivers/platform/x86/compal-laptop.c | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 1a387e79f719..c78d254303bc 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -310,6 +310,47 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }, .callback = dmi_check_cb }, + { + .ident = "Dell Mini 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 10v", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Inspiron 11z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + }, + .callback = dmi_check_cb + }, + { + .ident = "Dell Mini 12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + }, + .callback = dmi_check_cb + }, + { } }; @@ -403,3 +444,8 @@ MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); +MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); -- cgit v1.2.3 From 493e91433e966a50964e221db92756eeb90dd54e Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 25 Aug 2009 10:30:13 -0500 Subject: compal-laptop: Replace sysfs support with rfkill support This drops the support for manually groking the files in sysfs to turn on and off the WLAN and BT for Compal laptops in favor of platform rfkill support. It has been combined into a single patch to not introduce regressions in the process of simply adding rfkill support Signed-off-by: Mario Limonciello Signed-off-by: Tim Gardner Signed-off-by: Matthew Garrett Cc: Cezary Jackiewicz --- drivers/platform/x86/compal-laptop.c | 201 +++++++++++------------------------ 1 file changed, 63 insertions(+), 138 deletions(-) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index c78d254303bc..2740b40aad9b 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -26,17 +26,8 @@ /* * comapl-laptop.c - Compal laptop support. * - * This driver exports a few files in /sys/devices/platform/compal-laptop/: - * - * wlan - wlan subsystem state: contains 0 or 1 (rw) - * - * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw) - * - * raw - raw value taken from embedded controller register (ro) - * - * In addition to these platform device attributes the driver - * registers itself in the Linux backlight control subsystem and is - * available to userspace under /sys/class/backlight/compal-laptop/. + * The driver registers itself with the rfkill subsystem and + * the Linux backlight control subsystem. * * This driver might work on other laptops produced by Compal. If you * want to try it you can pass force=1 as argument to the module which @@ -51,6 +42,7 @@ #include #include #include +#include #define COMPAL_DRIVER_VERSION "0.2.6" @@ -63,6 +55,10 @@ #define WLAN_MASK 0x01 #define BT_MASK 0x02 +static struct rfkill *wifi_rfkill; +static struct rfkill *bt_rfkill; +static struct platform_device *compal_device; + static int force; module_param(force, bool, 0); MODULE_PARM_DESC(force, "Force driver load, ignore DMI data"); @@ -88,65 +84,75 @@ static int get_lcd_level(void) return (int) result; } -static int set_wlan_state(int state) +static int compal_rfkill_set(void *data, bool blocked) { + unsigned long radio = (unsigned long) data; u8 result, value; ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - if ((result & KILLSWITCH_MASK) == 0) - return -EINVAL; - else { - if (state) - value = (u8) (result | WLAN_MASK); - else - value = (u8) (result & ~WLAN_MASK); - ec_write(COMPAL_EC_COMMAND_WIRELESS, value); - } + if (!blocked) + value = (u8) (result | radio); + else + value = (u8) (result & ~radio); + ec_write(COMPAL_EC_COMMAND_WIRELESS, value); return 0; } -static int set_bluetooth_state(int state) +static void compal_rfkill_poll(struct rfkill *rfkill, void *data) { - u8 result, value; + u8 result; + bool hw_blocked; ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - if ((result & KILLSWITCH_MASK) == 0) - return -EINVAL; - else { - if (state) - value = (u8) (result | BT_MASK); - else - value = (u8) (result & ~BT_MASK); - ec_write(COMPAL_EC_COMMAND_WIRELESS, value); - } - - return 0; + hw_blocked = !(result & KILLSWITCH_MASK); + rfkill_set_hw_state(rfkill, hw_blocked); } -static int get_wireless_state(int *wlan, int *bluetooth) +static const struct rfkill_ops compal_rfkill_ops = { + .poll = compal_rfkill_poll, + .set_block = compal_rfkill_set, +}; + +static int setup_rfkill(void) { - u8 result; + int ret; - ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); + wifi_rfkill = rfkill_alloc("compal-wifi", &compal_device->dev, + RFKILL_TYPE_WLAN, &compal_rfkill_ops, + (void *) WLAN_MASK); + if (!wifi_rfkill) + return -ENOMEM; - if (wlan) { - if ((result & KILLSWITCH_MASK) == 0) - *wlan = 0; - else - *wlan = result & WLAN_MASK; - } + ret = rfkill_register(wifi_rfkill); + if (ret) + goto err_wifi; - if (bluetooth) { - if ((result & KILLSWITCH_MASK) == 0) - *bluetooth = 0; - else - *bluetooth = (result & BT_MASK) >> 1; + bt_rfkill = rfkill_alloc("compal-bluetooth", &compal_device->dev, + RFKILL_TYPE_BLUETOOTH, &compal_rfkill_ops, + (void *) BT_MASK); + if (!bt_rfkill) { + ret = -ENOMEM; + goto err_allocate_bt; } + ret = rfkill_register(bt_rfkill); + if (ret) + goto err_register_bt; return 0; + +err_register_bt: + rfkill_destroy(bt_rfkill); + +err_allocate_bt: + rfkill_unregister(wifi_rfkill); + +err_wifi: + rfkill_destroy(wifi_rfkill); + + return ret; } /* Backlight device stuff */ @@ -169,86 +175,6 @@ static struct backlight_ops compalbl_ops = { static struct backlight_device *compalbl_device; -/* Platform device */ - -static ssize_t show_wlan(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, enabled; - - ret = get_wireless_state(&enabled, NULL); - if (ret < 0) - return ret; - - return sprintf(buf, "%i\n", enabled); -} - -static ssize_t show_raw(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 result; - - ec_read(COMPAL_EC_COMMAND_WIRELESS, &result); - - return sprintf(buf, "%i\n", result); -} - -static ssize_t show_bluetooth(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int ret, enabled; - - ret = get_wireless_state(NULL, &enabled); - if (ret < 0) - return ret; - - return sprintf(buf, "%i\n", enabled); -} - -static ssize_t store_wlan_state(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int state, ret; - - if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) - return -EINVAL; - - ret = set_wlan_state(state); - if (ret < 0) - return ret; - - return count; -} - -static ssize_t store_bluetooth_state(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int state, ret; - - if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1)) - return -EINVAL; - - ret = set_bluetooth_state(state); - if (ret < 0) - return ret; - - return count; -} - -static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state); -static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state); -static DEVICE_ATTR(raw, 0444, show_raw, NULL); - -static struct attribute *compal_attributes[] = { - &dev_attr_bluetooth.attr, - &dev_attr_wlan.attr, - &dev_attr_raw.attr, - NULL -}; - -static struct attribute_group compal_attribute_group = { - .attrs = compal_attributes -}; static struct platform_driver compal_driver = { .driver = { @@ -257,8 +183,6 @@ static struct platform_driver compal_driver = { } }; -static struct platform_device *compal_device; - /* Initialization */ static int dmi_check_cb(const struct dmi_system_id *id) @@ -389,23 +313,21 @@ static int __init compal_init(void) ret = platform_device_add(compal_device); if (ret) - goto fail_platform_device1; + goto fail_platform_device; - ret = sysfs_create_group(&compal_device->dev.kobj, - &compal_attribute_group); + ret = setup_rfkill(); if (ret) - goto fail_platform_device2; + goto fail_rfkill; printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION " successfully loaded.\n"); return 0; -fail_platform_device2: - +fail_rfkill: platform_device_del(compal_device); -fail_platform_device1: +fail_platform_device: platform_device_put(compal_device); @@ -423,10 +345,13 @@ fail_backlight: static void __exit compal_cleanup(void) { - sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group); platform_device_unregister(compal_device); platform_driver_unregister(&compal_driver); backlight_device_unregister(compalbl_device); + rfkill_unregister(wifi_rfkill); + rfkill_destroy(wifi_rfkill); + rfkill_unregister(bt_rfkill); + rfkill_destroy(bt_rfkill); printk(KERN_INFO "compal-laptop: driver unloaded.\n"); } -- cgit v1.2.3 From 814cb8adbe2fb49302ac65bc31fa749143823860 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 9 Dec 2009 18:23:36 +0000 Subject: dell-laptop: Update rfkill state on kill switch The rfkill interface on Dells only sends a notification that the switch has been changed via the keyboard controller. Add a filter so we can pick these notifications up and update the rfkill state appropriately. Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/dell-laptop.c | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f526e735c5ab..6848f213eb53 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -79,6 +79,7 @@ config DELL_LAPTOP depends on BACKLIGHT_CLASS_DEVICE depends on RFKILL || RFKILL = n depends on POWER_SUPPLY + depends on SERIO_I8042 default n ---help--- This driver adds support for rfkill and backlight control to Dell diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 3780994dc8f2..9d7e0be6bb0f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -214,6 +215,18 @@ static const struct rfkill_ops dell_rfkill_ops = { .query = dell_rfkill_query, }; +static void dell_update_rfkill(struct work_struct *ignored) +{ + if (wifi_rfkill) + dell_rfkill_query(wifi_rfkill, (void *)1); + if (bluetooth_rfkill) + dell_rfkill_query(bluetooth_rfkill, (void *)2); + if (wwan_rfkill) + dell_rfkill_query(wwan_rfkill, (void *)3); +} +static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); + + static int __init dell_setup_rfkill(void) { struct calling_interface_buffer buffer; @@ -338,6 +351,30 @@ static struct backlight_ops dell_ops = { .update_status = dell_send_intensity, }; +bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + + if (str & 0x20) + return false; + + if (unlikely(data == 0xe0)) { + extended = true; + return false; + } else if (unlikely(extended)) { + switch (data) { + case 0x8: + schedule_delayed_work(&dell_rfkill_work, + round_jiffies_relative(HZ)); + break; + } + extended = false; + } + + return false; +} + static int __init dell_init(void) { struct calling_interface_buffer buffer; @@ -373,6 +410,13 @@ static int __init dell_init(void) goto fail_rfkill; } + ret = i8042_install_filter(dell_laptop_i8042_filter); + if (ret) { + printk(KERN_WARNING + "dell-laptop: Unable to install key filter\n"); + goto fail_filter; + } + #ifdef CONFIG_ACPI /* In the event of an ACPI backlight being available, don't * register the platform controller. @@ -410,6 +454,8 @@ static int __init dell_init(void) return 0; fail_backlight: + i8042_remove_filter(dell_laptop_i8042_filter); +fail_filter: dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); @@ -424,6 +470,8 @@ fail_platform_driver: static void __exit dell_exit(void) { + cancel_delayed_work_sync(&dell_rfkill_work); + i8042_remove_filter(dell_laptop_i8042_filter); backlight_device_unregister(dell_backlight_device); dell_cleanup_rfkill(); } -- cgit v1.2.3 From facd61d7b70d9c061f08e0b76976f7f833888a70 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 9 Feb 2010 14:03:04 -0500 Subject: dell-laptop: Fix platform device unregistration dell-laptop currently fails to clean up its platform device correctly. Make sure that it's unregistered. Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 9d7e0be6bb0f..b4c2190e89f9 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -474,6 +474,10 @@ static void __exit dell_exit(void) i8042_remove_filter(dell_laptop_i8042_filter); backlight_device_unregister(dell_backlight_device); dell_cleanup_rfkill(); + if (platform_device) { + platform_device_del(platform_device); + platform_driver_unregister(&platform_driver); + } } module_init(dell_init); -- cgit v1.2.3 From e551260b233e8dcbdc9d5f238ad4aa1200dc2ea7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 9 Feb 2010 14:05:01 -0500 Subject: dell-laptop: Fix small memory leak da_tokens was not being freed by dell-laptop on unload. Fix that. Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index b4c2190e89f9..b13eb6a2cb51 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -478,6 +478,7 @@ static void __exit dell_exit(void) platform_device_del(platform_device); platform_driver_unregister(&platform_driver); } + kfree(da_tokens); } module_init(dell_init); -- cgit v1.2.3 From ec1722a21491e4c354bc3bbc704fef9a8fa811fb Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 9 Feb 2010 14:11:05 -0500 Subject: dell-laptop: Block software state changes when rfkill hard blocked The "hardware" switch is tied directly to a BIOS interface that will connect and disconnect the hardware from the bus. If you use the software interface to request the BIOS to make these changes, the HW switch will be in an inconsistent state and LEDs may not reflect the state of the HW. Signed-off-by: Mario Limonciello --- drivers/platform/x86/dell-laptop.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index b13eb6a2cb51..81213e815400 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -190,6 +190,10 @@ static int dell_rfkill_set(void *data, bool blocked) unsigned long radio = (unsigned long)data; memset(&buffer, 0, sizeof(struct calling_interface_buffer)); + dell_send_request(&buffer, 17, 11); + if (!(buffer.output[1] & BIT(16))) + return -EINVAL; + buffer.input[0] = (1 | (radio<<8) | (disable << 16)); dell_send_request(&buffer, 17, 11); -- cgit v1.2.3 From e5fefd0c8c4e6bd11742625230d1c5026e2afb35 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 9 Feb 2010 17:41:03 -0500 Subject: dell-laptop: Blacklist machines not supporting dell-laptop The Mini family doesn't support smbios 17,11 although it reports it does. Signed-off-by: Mario Limonciello Signed-off-by: Tim Gardner --- drivers/platform/x86/dell-laptop.c | 46 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 81213e815400..d8b1b39aa9db 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -83,6 +83,46 @@ static const struct dmi_system_id __initdata dell_device_table[] = { { } }; +static struct dmi_system_id __devinitdata dell_blacklist[] = { + /* Supported by compal-laptop */ + { + .ident = "Dell Mini 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"), + }, + }, + { + .ident = "Dell Mini 10", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"), + }, + }, + { + .ident = "Dell Mini 10v", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"), + }, + }, + { + .ident = "Dell Inspiron 11z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"), + }, + }, + { + .ident = "Dell Mini 12", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"), + }, + }, + {} +}; + static void __init parse_da_table(const struct dmi_header *dm) { /* Final token is a terminator, so we don't want to copy it */ @@ -237,6 +277,12 @@ static int __init dell_setup_rfkill(void) int status; int ret; + if (dmi_check_system(dell_blacklist)) { + printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - " + "not enabling rfkill\n"); + return 0; + } + memset(&buffer, 0, sizeof(struct calling_interface_buffer)); dell_send_request(&buffer, 17, 11); status = buffer.output[1]; -- cgit v1.2.3 From 116ee77b2858d9c89c0327f3a47c8ba864bf4a96 Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 10 Feb 2010 14:12:13 -0500 Subject: dell-laptop: Use buffer with 32-bit physical address Calls to communicate with system firmware via a SMI (using dcdbas) need to use a buffer that has a physical address of 4GB or less. Currently the dell-laptop driver does not guarantee this, and when the buffer address is higher than 4GB, the address is truncated to 32 bits and the SMI handler writes to the wrong memory address. Signed-off-by: Stuart Hayes Acked-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 122 +++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 39 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d8b1b39aa9db..1a0bfd43f8f0 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "../../firmware/dcdbas.h" @@ -123,6 +124,21 @@ static struct dmi_system_id __devinitdata dell_blacklist[] = { {} }; +static struct calling_interface_buffer *buffer; +struct page *bufferpage; +DEFINE_MUTEX(buffer_mutex); + +static void get_buffer(void) +{ + mutex_lock(&buffer_mutex); + memset(buffer, 0, sizeof(struct calling_interface_buffer)); +} + +static void release_buffer(void) +{ + mutex_unlock(&buffer_mutex); +} + static void __init parse_da_table(const struct dmi_header *dm) { /* Final token is a terminator, so we don't want to copy it */ @@ -225,30 +241,35 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, static int dell_rfkill_set(void *data, bool blocked) { - struct calling_interface_buffer buffer; int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; + int ret = 0; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - dell_send_request(&buffer, 17, 11); - if (!(buffer.output[1] & BIT(16))) - return -EINVAL; + get_buffer(); + dell_send_request(buffer, 17, 11); - buffer.input[0] = (1 | (radio<<8) | (disable << 16)); - dell_send_request(&buffer, 17, 11); + if (!(buffer->output[1] & BIT(16))) { + ret = -EINVAL; + goto out; + } - return 0; + buffer->input[0] = (1 | (radio<<8) | (disable << 16)); + dell_send_request(buffer, 17, 11); + +out: + release_buffer(); + return ret; } static void dell_rfkill_query(struct rfkill *rfkill, void *data) { - struct calling_interface_buffer buffer; int status; int bit = (unsigned long)data + 16; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - dell_send_request(&buffer, 17, 11); - status = buffer.output[1]; + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); rfkill_set_hw_state(rfkill, !(status & BIT(16))); @@ -273,7 +294,6 @@ static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); static int __init dell_setup_rfkill(void) { - struct calling_interface_buffer buffer; int status; int ret; @@ -283,9 +303,10 @@ static int __init dell_setup_rfkill(void) return 0; } - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - dell_send_request(&buffer, 17, 11); - status = buffer.output[1]; + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, @@ -361,39 +382,49 @@ static void dell_cleanup_rfkill(void) static int dell_send_intensity(struct backlight_device *bd) { - struct calling_interface_buffer buffer; + int ret = 0; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); - buffer.input[1] = bd->props.brightness; + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); + buffer->input[1] = bd->props.brightness; - if (buffer.input[0] == -1) - return -ENODEV; + if (buffer->input[0] == -1) { + ret = -ENODEV; + goto out; + } if (power_supply_is_system_supplied() > 0) - dell_send_request(&buffer, 1, 2); + dell_send_request(buffer, 1, 2); else - dell_send_request(&buffer, 1, 1); + dell_send_request(buffer, 1, 1); +out: + release_buffer(); return 0; } static int dell_get_intensity(struct backlight_device *bd) { - struct calling_interface_buffer buffer; + int ret = 0; - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); - if (buffer.input[0] == -1) - return -ENODEV; + if (buffer->input[0] == -1) { + ret = -ENODEV; + goto out; + } if (power_supply_is_system_supplied() > 0) - dell_send_request(&buffer, 0, 2); + dell_send_request(buffer, 0, 2); else - dell_send_request(&buffer, 0, 1); + dell_send_request(buffer, 0, 1); - return buffer.output[1]; +out: + release_buffer(); + if (ret) + return ret; + return buffer->output[1]; } static struct backlight_ops dell_ops = { @@ -427,7 +458,6 @@ bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, static int __init dell_init(void) { - struct calling_interface_buffer buffer; int max_intensity = 0; int ret; @@ -453,6 +483,17 @@ static int __init dell_init(void) if (ret) goto fail_platform_device2; + /* + * Allocate buffer below 4GB for SMI data--only 32-bit physical addr + * is passed to SMI handler. + */ + bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); + + if (!bufferpage) + goto fail_buffer; + buffer = page_address(bufferpage); + mutex_init(&buffer_mutex); + ret = dell_setup_rfkill(); if (ret) { @@ -475,13 +516,13 @@ static int __init dell_init(void) return 0; #endif - memset(&buffer, 0, sizeof(struct calling_interface_buffer)); - buffer.input[0] = find_token_location(BRIGHTNESS_TOKEN); - - if (buffer.input[0] != -1) { - dell_send_request(&buffer, 0, 2); - max_intensity = buffer.output[3]; + get_buffer(); + buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); + if (buffer->input[0] != -1) { + dell_send_request(buffer, 0, 2); + max_intensity = buffer->output[3]; } + release_buffer(); if (max_intensity) { dell_backlight_device = backlight_device_register( @@ -508,6 +549,8 @@ fail_backlight: fail_filter: dell_cleanup_rfkill(); fail_rfkill: + free_page((unsigned long)bufferpage); +fail_buffer: platform_device_del(platform_device); fail_platform_device2: platform_device_put(platform_device); @@ -529,6 +572,7 @@ static void __exit dell_exit(void) platform_driver_unregister(&platform_driver); } kfree(da_tokens); + free_page((unsigned long)buffer); } module_init(dell_init); -- cgit v1.2.3 From c6760ac4268b6dbe03d74e5faef35bc862a82542 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 10 Feb 2010 14:44:03 -0500 Subject: dell-laptop: Pay attention to which devices the hardware switch controls Right now, we assume that the hardware rfkill switch on Dells toggles all radio devices. In fact, this can be configured in the BIOS and so right now we may mark a device as hardware killed even when it isn't. Add code to query the devices controlled by the switch, and use this when determining the hardware kill state of a radio. Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 1a0bfd43f8f0..04b2ddbe63d2 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -128,6 +128,8 @@ static struct calling_interface_buffer *buffer; struct page *bufferpage; DEFINE_MUTEX(buffer_mutex); +static int hwswitch_state; + static void get_buffer(void) { mutex_lock(&buffer_mutex); @@ -217,6 +219,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, /* Derived from information in DellWirelessCtl.cpp: Class 17, select 11 is radio control. It returns an array of 32-bit values. + Input byte 0 = 0: Wireless information + result[0]: return code result[1]: Bit 0: Hardware switch supported @@ -237,18 +241,35 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, Bits 20-31: Reserved result[2]: NVRAM size in bytes result[3]: NVRAM format version number + + Input byte 0 = 2: Wireless switch configuration + result[0]: return code + result[1]: + Bit 0: Wifi controlled by switch + Bit 1: Bluetooth controlled by switch + Bit 2: WWAN controlled by switch + Bits 3-6: Reserved + Bit 7: Wireless switch config locked + Bit 8: Wifi locator enabled + Bits 9-14: Reserved + Bit 15: Wifi locator setting locked + Bits 16-31: Reserved */ static int dell_rfkill_set(void *data, bool blocked) { int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; + int hwswitch_bit = (unsigned long)data - 1; int ret = 0; get_buffer(); dell_send_request(buffer, 17, 11); - if (!(buffer->output[1] & BIT(16))) { + /* If the hardware switch controls this radio, and the hardware + switch is disabled, don't allow changing the software state */ + if ((hwswitch_state & BIT(hwswitch_bit)) && + !(buffer->output[1] & BIT(16))) { ret = -EINVAL; goto out; } @@ -265,6 +286,7 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data) { int status; int bit = (unsigned long)data + 16; + int hwswitch_bit = (unsigned long)data - 1; get_buffer(); dell_send_request(buffer, 17, 11); @@ -272,7 +294,9 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data) release_buffer(); rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); - rfkill_set_hw_state(rfkill, !(status & BIT(16))); + + if (hwswitch_state & (BIT(hwswitch_bit))) + rfkill_set_hw_state(rfkill, !(status & BIT(16))); } static const struct rfkill_ops dell_rfkill_ops = { @@ -306,6 +330,9 @@ static int __init dell_setup_rfkill(void) get_buffer(); dell_send_request(buffer, 17, 11); status = buffer->output[1]; + buffer->input[0] = 0x2; + dell_send_request(buffer, 17, 11); + hwswitch_state = buffer->output[1]; release_buffer(); if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { -- cgit v1.2.3 From 02e77a55f7b7e36888e39c62439fedb90ae4e808 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 9 Feb 2010 17:37:27 -0500 Subject: classmate-laptop: use a single MODULE_DEVICE_TABLE to get correct aliases Instead of a MODULE_DEVICE_TABLE for every acpi_driver ids table, we create a table containing all ids to export to get a module alias for each one. This will fix automatic loading of the driver when one of the ACPI devices is not present (like the accelerometer, which is not present in some models). Signed-off-by: Thadeu Lima de Souza Cascardo --- drivers/platform/x86/classmate-laptop.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index ed90082cdf1d..8cb20e45bad6 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -34,6 +34,11 @@ struct cmpc_accel { #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 +#define CMPC_ACCEL_HID "ACCE0000" +#define CMPC_TABLET_HID "TBLT0000" +#define CMPC_BL_HID "IPML200" +#define CMPC_KEYS_HID "FnBT0000" + /* * Generic input device code. */ @@ -282,10 +287,9 @@ static int cmpc_accel_remove(struct acpi_device *acpi, int type) } static const struct acpi_device_id cmpc_accel_device_ids[] = { - {"ACCE0000", 0}, + {CMPC_ACCEL_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids); static struct acpi_driver cmpc_accel_acpi_driver = { .owner = THIS_MODULE, @@ -366,10 +370,9 @@ static int cmpc_tablet_resume(struct acpi_device *acpi) } static const struct acpi_device_id cmpc_tablet_device_ids[] = { - {"TBLT0000", 0}, + {CMPC_TABLET_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids); static struct acpi_driver cmpc_tablet_acpi_driver = { .owner = THIS_MODULE, @@ -477,17 +480,16 @@ static int cmpc_bl_remove(struct acpi_device *acpi, int type) return 0; } -static const struct acpi_device_id cmpc_device_ids[] = { - {"IPML200", 0}, +static const struct acpi_device_id cmpc_bl_device_ids[] = { + {CMPC_BL_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); static struct acpi_driver cmpc_bl_acpi_driver = { .owner = THIS_MODULE, .name = "cmpc", .class = "cmpc", - .ids = cmpc_device_ids, + .ids = cmpc_bl_device_ids, .ops = { .add = cmpc_bl_add, .remove = cmpc_bl_remove @@ -540,10 +542,9 @@ static int cmpc_keys_remove(struct acpi_device *acpi, int type) } static const struct acpi_device_id cmpc_keys_device_ids[] = { - {"FnBT0000", 0}, + {CMPC_KEYS_HID, 0}, {"", 0} }; -MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids); static struct acpi_driver cmpc_keys_acpi_driver = { .owner = THIS_MODULE, @@ -607,3 +608,13 @@ static void cmpc_exit(void) module_init(cmpc_init); module_exit(cmpc_exit); + +static const struct acpi_device_id cmpc_device_ids[] = { + {CMPC_ACCEL_HID, 0}, + {CMPC_TABLET_HID, 0}, + {CMPC_BL_HID, 0}, + {CMPC_KEYS_HID, 0}, + {"", 0} +}; + +MODULE_DEVICE_TABLE(acpi, cmpc_device_ids); -- cgit v1.2.3 From cb6a7937f4328a267e0806680ade500ed2c3c01a Mon Sep 17 00:00:00 2001 From: Erik Andren Date: Sun, 14 Feb 2010 11:53:23 -0500 Subject: dell-laptop: Add another Dell laptop to the DMI whitelist The Latitude C640 has another variation of dell in its DMI vendor entry. Add it to the whitelist in order to enjoy the sweet fruits of software backlight toggling. Signed-off-by: Erik Andren --- drivers/platform/x86/dell-laptop.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 04b2ddbe63d2..b7f4d2705916 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -81,6 +81,13 @@ static const struct dmi_system_id __initdata dell_device_table[] = { DMI_MATCH(DMI_CHASSIS_TYPE, "8"), }, }, + { + .ident = "Dell Computer Corporation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, { } }; @@ -609,3 +616,4 @@ MODULE_AUTHOR("Matthew Garrett "); MODULE_DESCRIPTION("Dell laptop driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); +MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); -- cgit v1.2.3 From caeacf59af3d114a3121bb04f8057109bbf289ea Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Wed, 17 Feb 2010 10:29:39 -0500 Subject: hp-wmi: Add support for tablet rotation key The HP touchsmart tablet has a key for rotating the UI from landscape to portrait. Add support for it. Signed-off-by: Matthew Garrett --- drivers/platform/x86/hp-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index ad4c414dbfbc..3aa57da8b43b 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -89,6 +89,7 @@ static struct key_entry hp_wmi_keymap[] = { {KE_KEY, 0x20e6, KEY_PROG1}, {KE_KEY, 0x2142, KEY_MEDIA}, {KE_KEY, 0x213b, KEY_INFO}, + {KE_KEY, 0x2169, KEY_DIRECTION}, {KE_KEY, 0x231b, KEY_HELP}, {KE_END, 0} }; -- cgit v1.2.3 From 6335e4d56681f6f08f24f4b812a72d402793d393 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 25 Feb 2010 15:20:54 -0500 Subject: toshiba_acpi: Add full hotkey support Calling the ENAB method on Toshiba laptops results in notifications being sent when laptop hotkeys are pressed. This patch simply calls that method and sets up an input device if it's successful. Signed-off-by: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/platform/x86/toshiba_acpi.c | 206 ++++++++++++++++++++++++++++++++++-- 1 file changed, 199 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 77bf5d8f893a..26c211724acf 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -46,6 +46,7 @@ #include #include #include +#include #include @@ -62,9 +63,10 @@ MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" -#define METHOD_HCI_1 "\\_SB_.VALD.GHCI" -#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" +#define TOSH_INTERFACE_1 "\\_SB_.VALD" +#define TOSH_INTERFACE_2 "\\_SB_.VALZ" #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" +#define GHCI_METHOD ".GHCI" /* Toshiba HCI interface definitions * @@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); +struct key_entry { + char type; + u16 code; + u16 keycode; +}; + +enum {KE_KEY, KE_END}; + +static struct key_entry toshiba_acpi_keymap[] = { + {KE_KEY, 0x101, KEY_MUTE}, + {KE_KEY, 0x13b, KEY_COFFEE}, + {KE_KEY, 0x13c, KEY_BATTERY}, + {KE_KEY, 0x13d, KEY_SLEEP}, + {KE_KEY, 0x13e, KEY_SUSPEND}, + {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE}, + {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN}, + {KE_KEY, 0x141, KEY_BRIGHTNESSUP}, + {KE_KEY, 0x142, KEY_WLAN}, + {KE_KEY, 0x143, KEY_PROG1}, + {KE_KEY, 0xb05, KEY_PROG2}, + {KE_KEY, 0xb06, KEY_WWW}, + {KE_KEY, 0xb07, KEY_MAIL}, + {KE_KEY, 0xb30, KEY_STOP}, + {KE_KEY, 0xb31, KEY_PREVIOUSSONG}, + {KE_KEY, 0xb32, KEY_NEXTSONG}, + {KE_KEY, 0xb33, KEY_PLAYPAUSE}, + {KE_KEY, 0xb5a, KEY_MEDIA}, + {KE_END, 0, 0}, +}; + /* utility */ @@ -251,6 +283,8 @@ static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) struct toshiba_acpi_dev { struct platform_device *p_dev; struct rfkill *bt_rfk; + struct input_dev *hotkey_dev; + acpi_handle handle; const char *bt_name; @@ -711,8 +745,159 @@ static struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->code) + return key; + + return NULL; +} + +static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code) +{ + struct key_entry *key; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) + if (code == key->keycode && key->type == KE_KEY) + return key; + + return NULL; +} + +static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode, + int *keycode) +{ + struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode); + + if (key && key->type == KE_KEY) { + *keycode = key->keycode; + return 0; + } + + return -EINVAL; +} + +static int toshiba_acpi_setkeycode(struct input_dev *dev, int scancode, + int keycode) +{ + struct key_entry *key; + int old_keycode; + + if (keycode < 0 || keycode > KEY_MAX) + return -EINVAL; + + key = toshiba_acpi_get_entry_by_scancode(scancode); + if (key && key->type == KE_KEY) { + old_keycode = key->keycode; + key->keycode = keycode; + set_bit(keycode, dev->keybit); + if (!toshiba_acpi_get_entry_by_keycode(old_keycode)) + clear_bit(old_keycode, dev->keybit); + return 0; + } + + return -EINVAL; +} + +static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) +{ + u32 hci_result, value; + struct key_entry *key; + + if (event != 0x80) + return; + do { + hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + if (value == 0x100) + continue; + else if (value & 0x80) { + key = toshiba_acpi_get_entry_by_scancode + (value & ~0x80); + if (!key) { + printk(MY_INFO "Unknown key %x\n", + value & ~0x80); + continue; + } + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 1); + input_sync(toshiba_acpi.hotkey_dev); + input_report_key(toshiba_acpi.hotkey_dev, + key->keycode, 0); + input_sync(toshiba_acpi.hotkey_dev); + } + } else if (hci_result == HCI_NOT_SUPPORTED) { + /* This is a workaround for an unresolved issue on + * some machines where system events sporadically + * become disabled. */ + hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + printk(MY_NOTICE "Re-enabled hotkeys\n"); + } + } while (hci_result != HCI_EMPTY); +} + +static int toshiba_acpi_setup_keyboard(char *device) +{ + acpi_status status; + acpi_handle handle; + int result; + const struct key_entry *key; + + status = acpi_get_handle(NULL, device, &handle); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to get notification device\n"); + return -ENODEV; + } + + toshiba_acpi.handle = handle; + + status = acpi_evaluate_object(handle, "ENAB", NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to enable hotkeys\n"); + return -ENODEV; + } + + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(MY_INFO "Unable to install hotkey notification\n"); + return -ENODEV; + } + + toshiba_acpi.hotkey_dev = input_allocate_device(); + if (!toshiba_acpi.hotkey_dev) { + printk(MY_INFO "Unable to register input device\n"); + return -ENOMEM; + } + + toshiba_acpi.hotkey_dev->name = "Toshiba input device"; + toshiba_acpi.hotkey_dev->phys = device; + toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; + toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode; + toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode; + + for (key = toshiba_acpi_keymap; key->type != KE_END; key++) { + set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit); + set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit); + } + + result = input_register_device(toshiba_acpi.hotkey_dev); + if (result) { + printk(MY_INFO "Unable to register input device\n"); + return result; + } + + return 0; +} + static void toshiba_acpi_exit(void) { + if (toshiba_acpi.hotkey_dev) + input_unregister_device(toshiba_acpi.hotkey_dev); + if (toshiba_acpi.bt_rfk) { rfkill_unregister(toshiba_acpi.bt_rfk); rfkill_destroy(toshiba_acpi.bt_rfk); @@ -726,6 +911,9 @@ static void toshiba_acpi_exit(void) if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + platform_device_unregister(toshiba_acpi.p_dev); return; @@ -742,11 +930,15 @@ static int __init toshiba_acpi_init(void) return -ENODEV; /* simple device detection: look for HCI method */ - if (is_valid_acpi_path(METHOD_HCI_1)) - method_hci = METHOD_HCI_1; - else if (is_valid_acpi_path(METHOD_HCI_2)) - method_hci = METHOD_HCI_2; - else + if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_1 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { + method_hci = TOSH_INTERFACE_2 GHCI_METHOD; + if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) + printk(MY_INFO "Unable to activate hotkeys\n"); + } else return -ENODEV; printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", -- cgit v1.2.3