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