Jesse Keating 2f82dd
commit 61a2aa30877a6e2be1d3fb3a71385e1f741819d7
Jesse Keating 2f82dd
Author: Matthew Garrett <mjg@redhat.com>
Jesse Keating 2f82dd
Date:   Fri Mar 6 00:25:45 2009 +0000
Jesse Keating 2f82dd
Jesse Keating 2f82dd
    toshiba-acpi: Add support for hotkey notifications
Jesse Keating 2f82dd
    
Jesse Keating 2f82dd
    Calling the ENAB method on Toshiba laptops results in notifications being
Jesse Keating 2f82dd
    sent when laptop hotkeys are pressed. This patch simply calls that method
Jesse Keating 2f82dd
    and sets up an input device if it's successful.
Jesse Keating 2f82dd
    
Jesse Keating 2f82dd
    Signed-off-by: Matthew Garrett <mjg@redhat.com>
Jesse Keating 2f82dd
Jesse Keating 2f82dd
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
Jesse Keating 2f82dd
index 40e60fc..604f9fa 100644
Jesse Keating 2f82dd
--- a/drivers/platform/x86/toshiba_acpi.c
Jesse Keating 2f82dd
+++ b/drivers/platform/x86/toshiba_acpi.c
Jesse Keating 2f82dd
@@ -46,6 +46,7 @@
Jesse Keating 2f82dd
 #include <linux platform_device.h="">
Jesse Keating 2f82dd
 #include <linux rfkill.h="">
Jesse Keating 2f82dd
 #include <linux input-polldev.h="">
Jesse Keating 2f82dd
+#include <linux input.h="">
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 #include <asm uaccess.h="">
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
@@ -62,9 +63,10 @@ MODULE_LICENSE("GPL");
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 /* Toshiba ACPI method paths */
Jesse Keating 2f82dd
 #define METHOD_LCD_BRIGHTNESS	"\\_SB_.PCI0.VGA_.LCD_._BCM"
Jesse Keating 2f82dd
-#define METHOD_HCI_1		"\\_SB_.VALD.GHCI"
Jesse Keating 2f82dd
-#define METHOD_HCI_2		"\\_SB_.VALZ.GHCI"
Jesse Keating 2f82dd
+#define TOSH_INTERFACE_1	"\\_SB_.VALD"
Jesse Keating 2f82dd
+#define TOSH_INTERFACE_2	"\\_SB_.VALZ"
Jesse Keating 2f82dd
 #define METHOD_VIDEO_OUT	"\\_SB_.VALX.DSSX"
Jesse Keating 2f82dd
+#define GHCI_METHOD		".GHCI"
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 /* Toshiba HCI interface definitions
Jesse Keating 2f82dd
  *
Jesse Keating 2f82dd
@@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = {
Jesse Keating 2f82dd
 };
Jesse Keating 2f82dd
 MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
+struct key_entry {
Jesse Keating 2f82dd
+	char type;
Jesse Keating 2f82dd
+	u16 code;
Jesse Keating 2f82dd
+	u16 keycode;
Jesse Keating 2f82dd
+};
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+enum {KE_KEY, KE_END};
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static struct key_entry toshiba_acpi_keymap[]  = {
Jesse Keating 2f82dd
+	{KE_KEY, 0x101, KEY_MUTE},
Jesse Keating 2f82dd
+	{KE_KEY, 0x13b, KEY_COFFEE},
Jesse Keating 2f82dd
+	{KE_KEY, 0x13c, KEY_BATTERY},
Jesse Keating 2f82dd
+	{KE_KEY, 0x13d, KEY_SLEEP},
Jesse Keating 2f82dd
+	{KE_KEY, 0x13e, KEY_SUSPEND},
Jesse Keating 2f82dd
+	{KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
Jesse Keating 2f82dd
+	{KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
Jesse Keating 2f82dd
+	{KE_KEY, 0x141, KEY_BRIGHTNESSUP},
Jesse Keating 2f82dd
+	{KE_KEY, 0x142, KEY_WLAN},
Jesse Keating 2f82dd
+	{KE_KEY, 0x143, KEY_PROG1},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb05, KEY_PROG2},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb06, KEY_WWW},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb07, KEY_MAIL},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb30, KEY_STOP},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb31, KEY_PREVIOUSSONG},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb32, KEY_NEXTSONG},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb33, KEY_PLAYPAUSE},
Jesse Keating 2f82dd
+	{KE_KEY, 0xb5a, KEY_MEDIA},
Jesse Keating 2f82dd
+	{KE_END, 0, 0},
Jesse Keating 2f82dd
+};
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
 /* utility
Jesse Keating 2f82dd
  */
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
@@ -252,6 +284,8 @@ struct toshiba_acpi_dev {
Jesse Keating 2f82dd
 	struct platform_device *p_dev;
Jesse Keating 2f82dd
 	struct rfkill *rfk_dev;
Jesse Keating 2f82dd
 	struct input_polled_dev *poll_dev;
Jesse Keating 2f82dd
+	struct input_dev *hotkey_dev;
Jesse Keating 2f82dd
+	acpi_handle handle;
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 	const char *bt_name;
Jesse Keating 2f82dd
 	const char *rfk_name;
Jesse Keating 2f82dd
@@ -702,6 +736,154 @@ static struct backlight_ops toshiba_backlight_data = {
Jesse Keating 2f82dd
         .update_status  = set_lcd_status,
Jesse Keating 2f82dd
 };
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
+static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	struct key_entry *key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
Jesse Keating 2f82dd
+		if (code == key->code)
Jesse Keating 2f82dd
+			return key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	return NULL;
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	struct key_entry *key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
Jesse Keating 2f82dd
+		if (code == key->keycode && key->type == KE_KEY)
Jesse Keating 2f82dd
+			return key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	return NULL;
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode,
Jesse Keating 2f82dd
+				   int *keycode)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	if (key && key->type == KE_KEY) {
Jesse Keating 2f82dd
+		*keycode = key->keycode;
Jesse Keating 2f82dd
+		return 0;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	return -EINVAL;
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static int toshiba_acpi_setkeycode(struct input_dev *dev, int scancode,
Jesse Keating 2f82dd
+				   int keycode)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	struct key_entry *key;
Jesse Keating 2f82dd
+	int old_keycode;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	if (keycode < 0 || keycode > KEY_MAX)
Jesse Keating 2f82dd
+		return -EINVAL;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	key = toshiba_acpi_get_entry_by_scancode(scancode);
Jesse Keating 2f82dd
+	if (key && key->type == KE_KEY) {
Jesse Keating 2f82dd
+		old_keycode = key->keycode;
Jesse Keating 2f82dd
+		key->keycode = keycode;
Jesse Keating 2f82dd
+		set_bit(keycode, dev->keybit);
Jesse Keating 2f82dd
+		if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
Jesse Keating 2f82dd
+			clear_bit(old_keycode, dev->keybit);
Jesse Keating 2f82dd
+		return 0;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	return -EINVAL;
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	u32 hci_result, value;
Jesse Keating 2f82dd
+	struct key_entry *key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	if (event != 0x80)
Jesse Keating 2f82dd
+		return;
Jesse Keating 2f82dd
+	do {
Jesse Keating 2f82dd
+		hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
Jesse Keating 2f82dd
+		if (hci_result == HCI_SUCCESS) {
Jesse Keating 2f82dd
+			if (value == 0x100)
Jesse Keating 2f82dd
+				continue;
Jesse Keating 2f82dd
+			else if (value & 0x80) {
Jesse Keating 2f82dd
+				key = toshiba_acpi_get_entry_by_scancode
Jesse Keating 2f82dd
+					(value & ~0x80);
Jesse Keating 2f82dd
+				if (!key) {
Jesse Keating 2f82dd
+					printk(MY_INFO "Unknown key %x\n",
Jesse Keating 2f82dd
+					       value & ~0x80);
Jesse Keating 2f82dd
+					continue;
Jesse Keating 2f82dd
+				}
Jesse Keating 2f82dd
+				input_report_key(toshiba_acpi.hotkey_dev,
Jesse Keating 2f82dd
+						 key->keycode, 1);
Jesse Keating 2f82dd
+				input_sync(toshiba_acpi.hotkey_dev);
Jesse Keating 2f82dd
+				input_report_key(toshiba_acpi.hotkey_dev,
Jesse Keating 2f82dd
+						 key->keycode, 0);
Jesse Keating 2f82dd
+				input_sync(toshiba_acpi.hotkey_dev);
Jesse Keating 2f82dd
+			}
Jesse Keating 2f82dd
+		} else if (hci_result == HCI_NOT_SUPPORTED) {
Jesse Keating 2f82dd
+			/* This is a workaround for an unresolved issue on
Jesse Keating 2f82dd
+			 * some machines where system events sporadically
Jesse Keating 2f82dd
+			 * become disabled. */
Jesse Keating 2f82dd
+			hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
Jesse Keating 2f82dd
+			printk(MY_NOTICE "Re-enabled hotkeys\n");
Jesse Keating 2f82dd
+		}
Jesse Keating 2f82dd
+	} while (hci_result != HCI_EMPTY);
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+static int toshiba_acpi_setup_keyboard(char *device)
Jesse Keating 2f82dd
+{
Jesse Keating 2f82dd
+	acpi_status status;
Jesse Keating 2f82dd
+	acpi_handle handle;
Jesse Keating 2f82dd
+	int result;
Jesse Keating 2f82dd
+	const struct key_entry *key;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	status = acpi_get_handle(NULL, device, &handle);
Jesse Keating 2f82dd
+	if (ACPI_FAILURE(status)) {
Jesse Keating 2f82dd
+		printk(MY_INFO "Unable to get notification device\n");
Jesse Keating 2f82dd
+		return -ENODEV;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	toshiba_acpi.handle = handle;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
Jesse Keating 2f82dd
+	if (ACPI_FAILURE(status)) {
Jesse Keating 2f82dd
+		printk(MY_INFO "Unable to enable hotkeys\n");
Jesse Keating 2f82dd
+		return -ENODEV;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	status = acpi_install_notify_handler (handle, ACPI_DEVICE_NOTIFY,
Jesse Keating 2f82dd
+					      toshiba_acpi_notify, NULL);
Jesse Keating 2f82dd
+	if (ACPI_FAILURE(status)) {
Jesse Keating 2f82dd
+		printk(MY_INFO "Unable to install hotkey notification\n");
Jesse Keating 2f82dd
+		return -ENODEV;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev = input_allocate_device();
Jesse Keating 2f82dd
+	if (!toshiba_acpi.hotkey_dev) {
Jesse Keating 2f82dd
+		printk(MY_INFO "Unable to register input device\n");
Jesse Keating 2f82dd
+		return -ENOMEM;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev->name = "Toshiba input device";
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev->phys = device;
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
Jesse Keating 2f82dd
+	toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
Jesse Keating 2f82dd
+		set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
Jesse Keating 2f82dd
+		set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	result = input_register_device(toshiba_acpi.hotkey_dev);
Jesse Keating 2f82dd
+	if (result) {
Jesse Keating 2f82dd
+		printk(MY_INFO "Unable to register input device\n");
Jesse Keating 2f82dd
+		return result;
Jesse Keating 2f82dd
+	}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
+	return 0;
Jesse Keating 2f82dd
+}
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
 static void toshiba_acpi_exit(void)
Jesse Keating 2f82dd
 {
Jesse Keating 2f82dd
 	if (toshiba_acpi.poll_dev) {
Jesse Keating 2f82dd
@@ -709,12 +891,18 @@ static void toshiba_acpi_exit(void)
Jesse Keating 2f82dd
 		input_free_polled_device(toshiba_acpi.poll_dev);
Jesse Keating 2f82dd
 	}
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
+	if (toshiba_acpi.hotkey_dev)
Jesse Keating 2f82dd
+		input_unregister_device(toshiba_acpi.hotkey_dev);
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
 	if (toshiba_acpi.rfk_dev)
Jesse Keating 2f82dd
 		rfkill_unregister(toshiba_acpi.rfk_dev);
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 	if (toshiba_backlight_device)
Jesse Keating 2f82dd
 		backlight_device_unregister(toshiba_backlight_device);
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
+	acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
Jesse Keating 2f82dd
+				   toshiba_acpi_notify);
Jesse Keating 2f82dd
+
Jesse Keating 2f82dd
 	remove_device();
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 	if (toshiba_proc_dir)
Jesse Keating 2f82dd
@@ -738,11 +926,15 @@ static int __init toshiba_acpi_init(void)
Jesse Keating 2f82dd
 		return -ENODEV;
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 	/* simple device detection: look for HCI method */
Jesse Keating 2f82dd
-	if (is_valid_acpi_path(METHOD_HCI_1))
Jesse Keating 2f82dd
-		method_hci = METHOD_HCI_1;
Jesse Keating 2f82dd
-	else if (is_valid_acpi_path(METHOD_HCI_2))
Jesse Keating 2f82dd
-		method_hci = METHOD_HCI_2;
Jesse Keating 2f82dd
-	else
Jesse Keating 2f82dd
+	if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
Jesse Keating 2f82dd
+		method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
Jesse Keating 2f82dd
+		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
Jesse Keating 2f82dd
+			printk(MY_INFO "Unable to activate hotkeys\n");
Jesse Keating 2f82dd
+	} else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
Jesse Keating 2f82dd
+		method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
Jesse Keating 2f82dd
+		if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
Jesse Keating 2f82dd
+			printk(MY_INFO "Unable to activate hotkeys\n");
Jesse Keating 2f82dd
+	} else
Jesse Keating 2f82dd
 		return -ENODEV;
Jesse Keating 2f82dd
 
Jesse Keating 2f82dd
 	printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",