summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-02-26 17:20:18 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2010-02-26 17:20:18 -0800
commit98723153dc32106e5be701da15551853c9f785a9 (patch)
treea04631cbeb50703474ac4f5f20310f7ef53b2b38 /drivers
parent4cbd55188fe01f22783815cbb6d4f55a0ebf5969 (diff)
parent6335e4d56681f6f08f24f4b812a72d402793d393 (diff)
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: toshiba_acpi: Add full hotkey support hp-wmi: Add support for tablet rotation key dell-laptop: Add another Dell laptop to the DMI whitelist classmate-laptop: use a single MODULE_DEVICE_TABLE to get correct aliases dell-laptop: Pay attention to which devices the hardware switch controls dell-laptop: Use buffer with 32-bit physical address dell-laptop: Blacklist machines not supporting dell-laptop dell-laptop: Block software state changes when rfkill hard blocked dell-laptop: Fix small memory leak dell-laptop: Fix platform device unregistration dell-laptop: Update rfkill state on kill switch compal-laptop: Replace sysfs support with rfkill support compal-laptop: Add support for known Compal made Dell laptops MAINTAINERS: update drivers/platform/x86 information
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/Kconfig1
-rw-r--r--drivers/platform/x86/classmate-laptop.c31
-rw-r--r--drivers/platform/x86/compal-laptop.c247
-rw-r--r--drivers/platform/x86/dell-laptop.c256
-rw-r--r--drivers/platform/x86/hp-wmi.c1
-rw-r--r--drivers/platform/x86/toshiba_acpi.c206
6 files changed, 550 insertions, 192 deletions
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/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);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index 1a387e79f719..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 <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
+#include <linux/rfkill.h>
#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)
@@ -310,6 +234,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
+ },
+
{ }
};
@@ -348,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);
@@ -382,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");
}
@@ -403,3 +369,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:*");
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 3780994dc8f2..b7f4d2705916 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -22,6 +22,8 @@
#include <linux/rfkill.h>
#include <linux/power_supply.h>
#include <linux/acpi.h>
+#include <linux/mm.h>
+#include <linux/i8042.h>
#include "../../firmware/dcdbas.h"
#define BRIGHTNESS_TOKEN 0x7d
@@ -79,9 +81,73 @@ 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"),
+ },
+ },
{ }
};
+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 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);
+ 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 */
@@ -160,6 +226,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
@@ -180,33 +248,62 @@ 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)
{
- struct calling_interface_buffer buffer;
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 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;
+ }
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- buffer.input[0] = (1 | (radio<<8) | (disable << 16));
- dell_send_request(&buffer, 17, 11);
+ buffer->input[0] = (1 | (radio<<8) | (disable << 16));
+ dell_send_request(buffer, 17, 11);
- return 0;
+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;
+ int hwswitch_bit = (unsigned long)data - 1;
- 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)));
+
+ if (hwswitch_state & (BIT(hwswitch_bit)))
+ rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static const struct rfkill_ops dell_rfkill_ops = {
@@ -214,15 +311,36 @@ 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;
int status;
int ret;
- memset(&buffer, 0, sizeof(struct calling_interface_buffer));
- dell_send_request(&buffer, 17, 11);
- status = buffer.output[1];
+ if (dmi_check_system(dell_blacklist)) {
+ printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - "
+ "not enabling rfkill\n");
+ return 0;
+ }
+
+ 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)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
@@ -298,39 +416,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 = {
@@ -338,9 +466,32 @@ 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;
int max_intensity = 0;
int ret;
@@ -366,6 +517,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) {
@@ -373,6 +535,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.
@@ -381,13 +550,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(
@@ -410,8 +579,12 @@ static int __init dell_init(void)
return 0;
fail_backlight:
+ i8042_remove_filter(dell_laptop_i8042_filter);
+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);
@@ -424,8 +597,16 @@ 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();
+ if (platform_device) {
+ platform_device_del(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+ kfree(da_tokens);
+ free_page((unsigned long)buffer);
}
module_init(dell_init);
@@ -435,3 +616,4 @@ MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
+MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
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}
};
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 <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
+#include <linux/input.h>
#include <asm/uaccess.h>
@@ -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",