diff options
author | Seth Forshee <seth.forshee@canonical.com> | 2012-01-18 14:44:09 -0500 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2012-03-22 09:31:51 -0400 |
commit | 29cd293f9f8cd76444657622980010b9364b1de6 (patch) | |
tree | f39a927d66951f13d031dfa45f3558a689ac0575 /drivers | |
parent | 3e2abc5a35d25442821e1733687b7abbc83b5072 (diff) |
toshiba_acpi: Support alternate hotkey interfaces
There are two types of problems that prevent hotkeys from working
on many of the machines supported by toshiba_acpi. The first of
these is the lack of a functioning SCI for hotkey events. For these
machines it is possible to filter the Fn keypresses from the
keyboard and generate a notification by executing the ACPI NTFY
method.
The second problem is a lack of support for HCI_SYSTEM_EVENT, which
is used for reading the hotkey scancodes. On these machines the
scancodes can be read by executing the ACPI NTFY method.
This patch fixes both problems by installing an i8042 filter when
the NTFY method is present to generate notifications and by
detecting which of INFO or HCI_SYSTEM_EVENT is supported for
reading scancodes. If neither method of reading scancodes is
supported, the hotkey input device is not registered.
Signed-off-by: Azael Avalos <coproscefalo@gmail.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 231 |
1 files changed, 195 insertions, 36 deletions
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index eab5e11fb41f..42d73b0f530a 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -52,6 +52,8 @@ | |||
52 | #include <linux/input/sparse-keymap.h> | 52 | #include <linux/input/sparse-keymap.h> |
53 | #include <linux/leds.h> | 53 | #include <linux/leds.h> |
54 | #include <linux/slab.h> | 54 | #include <linux/slab.h> |
55 | #include <linux/workqueue.h> | ||
56 | #include <linux/i8042.h> | ||
55 | 57 | ||
56 | #include <asm/uaccess.h> | 58 | #include <asm/uaccess.h> |
57 | 59 | ||
@@ -61,6 +63,9 @@ MODULE_AUTHOR("John Belmonte"); | |||
61 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); | 63 | MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); |
62 | MODULE_LICENSE("GPL"); | 64 | MODULE_LICENSE("GPL"); |
63 | 65 | ||
66 | /* Scan code for Fn key on TOS1900 models */ | ||
67 | #define TOS1900_FN_SCAN 0x6e | ||
68 | |||
64 | /* Toshiba ACPI method paths */ | 69 | /* Toshiba ACPI method paths */ |
65 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 70 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
66 | 71 | ||
@@ -95,6 +100,8 @@ MODULE_LICENSE("GPL"); | |||
95 | #define HCI_WIRELESS 0x0056 | 100 | #define HCI_WIRELESS 0x0056 |
96 | 101 | ||
97 | /* field definitions */ | 102 | /* field definitions */ |
103 | #define HCI_HOTKEY_DISABLE 0x0b | ||
104 | #define HCI_HOTKEY_ENABLE 0x09 | ||
98 | #define HCI_LCD_BRIGHTNESS_BITS 3 | 105 | #define HCI_LCD_BRIGHTNESS_BITS 3 |
99 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) | 106 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) |
100 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) | 107 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) |
@@ -111,6 +118,7 @@ struct toshiba_acpi_dev { | |||
111 | const char *method_hci; | 118 | const char *method_hci; |
112 | struct rfkill *bt_rfk; | 119 | struct rfkill *bt_rfk; |
113 | struct input_dev *hotkey_dev; | 120 | struct input_dev *hotkey_dev; |
121 | struct work_struct hotkey_work; | ||
114 | struct backlight_device *backlight_dev; | 122 | struct backlight_device *backlight_dev; |
115 | struct led_classdev led_dev; | 123 | struct led_classdev led_dev; |
116 | 124 | ||
@@ -122,10 +130,14 @@ struct toshiba_acpi_dev { | |||
122 | unsigned int video_supported:1; | 130 | unsigned int video_supported:1; |
123 | unsigned int fan_supported:1; | 131 | unsigned int fan_supported:1; |
124 | unsigned int system_event_supported:1; | 132 | unsigned int system_event_supported:1; |
133 | unsigned int ntfy_supported:1; | ||
134 | unsigned int info_supported:1; | ||
125 | 135 | ||
126 | struct mutex mutex; | 136 | struct mutex mutex; |
127 | }; | 137 | }; |
128 | 138 | ||
139 | static struct toshiba_acpi_dev *toshiba_acpi; | ||
140 | |||
129 | static const struct acpi_device_id toshiba_device_ids[] = { | 141 | static const struct acpi_device_id toshiba_device_ids[] = { |
130 | {"TOS6200", 0}, | 142 | {"TOS6200", 0}, |
131 | {"TOS6208", 0}, | 143 | {"TOS6208", 0}, |
@@ -847,10 +859,78 @@ static const struct backlight_ops toshiba_backlight_data = { | |||
847 | .update_status = set_lcd_status, | 859 | .update_status = set_lcd_status, |
848 | }; | 860 | }; |
849 | 861 | ||
862 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, | ||
863 | struct serio *port) | ||
864 | { | ||
865 | if (str & 0x20) | ||
866 | return false; | ||
867 | |||
868 | if (unlikely(data == 0xe0)) | ||
869 | return false; | ||
870 | |||
871 | if ((data & 0x7f) == TOS1900_FN_SCAN) { | ||
872 | schedule_work(&toshiba_acpi->hotkey_work); | ||
873 | return true; | ||
874 | } | ||
875 | |||
876 | return false; | ||
877 | } | ||
878 | |||
879 | static void toshiba_acpi_hotkey_work(struct work_struct *work) | ||
880 | { | ||
881 | acpi_handle ec_handle = ec_get_handle(); | ||
882 | acpi_status status; | ||
883 | |||
884 | if (!ec_handle) | ||
885 | return; | ||
886 | |||
887 | status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); | ||
888 | if (ACPI_FAILURE(status)) | ||
889 | pr_err("ACPI NTFY method execution failed\n"); | ||
890 | } | ||
891 | |||
892 | /* | ||
893 | * Returns hotkey scancode, or < 0 on failure. | ||
894 | */ | ||
895 | static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) | ||
896 | { | ||
897 | struct acpi_buffer buf; | ||
898 | union acpi_object out_obj; | ||
899 | acpi_status status; | ||
900 | |||
901 | buf.pointer = &out_obj; | ||
902 | buf.length = sizeof(out_obj); | ||
903 | |||
904 | status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO", | ||
905 | NULL, &buf); | ||
906 | if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) { | ||
907 | pr_err("ACPI INFO method execution failed\n"); | ||
908 | return -EIO; | ||
909 | } | ||
910 | |||
911 | return out_obj.integer.value; | ||
912 | } | ||
913 | |||
914 | static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, | ||
915 | int scancode) | ||
916 | { | ||
917 | if (scancode == 0x100) | ||
918 | return; | ||
919 | |||
920 | /* act on key press; ignore key release */ | ||
921 | if (scancode & 0x80) | ||
922 | return; | ||
923 | |||
924 | if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) | ||
925 | pr_info("Unknown key %x\n", scancode); | ||
926 | } | ||
927 | |||
850 | static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) | 928 | static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) |
851 | { | 929 | { |
852 | acpi_status status; | 930 | acpi_status status; |
931 | acpi_handle ec_handle, handle; | ||
853 | int error; | 932 | int error; |
933 | u32 hci_result; | ||
854 | 934 | ||
855 | dev->hotkey_dev = input_allocate_device(); | 935 | dev->hotkey_dev = input_allocate_device(); |
856 | if (!dev->hotkey_dev) { | 936 | if (!dev->hotkey_dev) { |
@@ -866,21 +946,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) | |||
866 | if (error) | 946 | if (error) |
867 | goto err_free_dev; | 947 | goto err_free_dev; |
868 | 948 | ||
949 | /* | ||
950 | * For some machines the SCI responsible for providing hotkey | ||
951 | * notification doesn't fire. We can trigger the notification | ||
952 | * whenever the Fn key is pressed using the NTFY method, if | ||
953 | * supported, so if it's present set up an i8042 key filter | ||
954 | * for this purpose. | ||
955 | */ | ||
956 | status = AE_ERROR; | ||
957 | ec_handle = ec_get_handle(); | ||
958 | if (ec_handle) | ||
959 | status = acpi_get_handle(ec_handle, "NTFY", &handle); | ||
960 | |||
961 | if (ACPI_SUCCESS(status)) { | ||
962 | INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); | ||
963 | |||
964 | error = i8042_install_filter(toshiba_acpi_i8042_filter); | ||
965 | if (error) { | ||
966 | pr_err("Error installing key filter\n"); | ||
967 | goto err_free_keymap; | ||
968 | } | ||
969 | |||
970 | dev->ntfy_supported = 1; | ||
971 | } | ||
972 | |||
973 | /* | ||
974 | * Determine hotkey query interface. Prefer using the INFO | ||
975 | * method when it is available. | ||
976 | */ | ||
977 | status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle); | ||
978 | if (ACPI_SUCCESS(status)) { | ||
979 | dev->info_supported = 1; | ||
980 | } else { | ||
981 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
982 | if (hci_result == HCI_SUCCESS) | ||
983 | dev->system_event_supported = 1; | ||
984 | } | ||
985 | |||
986 | if (!dev->info_supported && !dev->system_event_supported) { | ||
987 | pr_warn("No hotkey query interface found\n"); | ||
988 | goto err_remove_filter; | ||
989 | } | ||
990 | |||
869 | status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); | 991 | status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); |
870 | if (ACPI_FAILURE(status)) { | 992 | if (ACPI_FAILURE(status)) { |
871 | pr_info("Unable to enable hotkeys\n"); | 993 | pr_info("Unable to enable hotkeys\n"); |
872 | error = -ENODEV; | 994 | error = -ENODEV; |
873 | goto err_free_keymap; | 995 | goto err_remove_filter; |
874 | } | 996 | } |
875 | 997 | ||
876 | error = input_register_device(dev->hotkey_dev); | 998 | error = input_register_device(dev->hotkey_dev); |
877 | if (error) { | 999 | if (error) { |
878 | pr_info("Unable to register input device\n"); | 1000 | pr_info("Unable to register input device\n"); |
879 | goto err_free_keymap; | 1001 | goto err_remove_filter; |
880 | } | 1002 | } |
881 | 1003 | ||
1004 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); | ||
882 | return 0; | 1005 | return 0; |
883 | 1006 | ||
1007 | err_remove_filter: | ||
1008 | if (dev->ntfy_supported) | ||
1009 | i8042_remove_filter(toshiba_acpi_i8042_filter); | ||
884 | err_free_keymap: | 1010 | err_free_keymap: |
885 | sparse_keymap_free(dev->hotkey_dev); | 1011 | sparse_keymap_free(dev->hotkey_dev); |
886 | err_free_dev: | 1012 | err_free_dev: |
@@ -895,6 +1021,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) | |||
895 | 1021 | ||
896 | remove_toshiba_proc_entries(dev); | 1022 | remove_toshiba_proc_entries(dev); |
897 | 1023 | ||
1024 | if (dev->ntfy_supported) { | ||
1025 | i8042_remove_filter(toshiba_acpi_i8042_filter); | ||
1026 | cancel_work_sync(&dev->hotkey_work); | ||
1027 | } | ||
1028 | |||
898 | if (dev->hotkey_dev) { | 1029 | if (dev->hotkey_dev) { |
899 | input_unregister_device(dev->hotkey_dev); | 1030 | input_unregister_device(dev->hotkey_dev); |
900 | sparse_keymap_free(dev->hotkey_dev); | 1031 | sparse_keymap_free(dev->hotkey_dev); |
@@ -911,6 +1042,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) | |||
911 | if (dev->illumination_supported) | 1042 | if (dev->illumination_supported) |
912 | led_classdev_unregister(&dev->led_dev); | 1043 | led_classdev_unregister(&dev->led_dev); |
913 | 1044 | ||
1045 | if (toshiba_acpi) | ||
1046 | toshiba_acpi = NULL; | ||
1047 | |||
914 | kfree(dev); | 1048 | kfree(dev); |
915 | 1049 | ||
916 | return 0; | 1050 | return 0; |
@@ -936,12 +1070,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
936 | { | 1070 | { |
937 | struct toshiba_acpi_dev *dev; | 1071 | struct toshiba_acpi_dev *dev; |
938 | const char *hci_method; | 1072 | const char *hci_method; |
939 | u32 hci_result; | ||
940 | u32 dummy; | 1073 | u32 dummy; |
941 | bool bt_present; | 1074 | bool bt_present; |
942 | int ret = 0; | 1075 | int ret = 0; |
943 | struct backlight_properties props; | 1076 | struct backlight_properties props; |
944 | 1077 | ||
1078 | if (toshiba_acpi) | ||
1079 | return -EBUSY; | ||
1080 | |||
945 | pr_info("Toshiba Laptop ACPI Extras version %s\n", | 1081 | pr_info("Toshiba Laptop ACPI Extras version %s\n", |
946 | TOSHIBA_ACPI_VERSION); | 1082 | TOSHIBA_ACPI_VERSION); |
947 | 1083 | ||
@@ -963,11 +1099,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
963 | 1099 | ||
964 | mutex_init(&dev->mutex); | 1100 | mutex_init(&dev->mutex); |
965 | 1101 | ||
966 | /* enable event fifo */ | ||
967 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
968 | if (hci_result == HCI_SUCCESS) | ||
969 | dev->system_event_supported = 1; | ||
970 | |||
971 | props.type = BACKLIGHT_PLATFORM; | 1102 | props.type = BACKLIGHT_PLATFORM; |
972 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | 1103 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; |
973 | dev->backlight_dev = backlight_device_register("toshiba", | 1104 | dev->backlight_dev = backlight_device_register("toshiba", |
@@ -1024,6 +1155,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
1024 | 1155 | ||
1025 | create_toshiba_proc_entries(dev); | 1156 | create_toshiba_proc_entries(dev); |
1026 | 1157 | ||
1158 | toshiba_acpi = dev; | ||
1159 | |||
1027 | return 0; | 1160 | return 0; |
1028 | 1161 | ||
1029 | error: | 1162 | error: |
@@ -1036,40 +1169,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) | |||
1036 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | 1169 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
1037 | u32 hci_result, value; | 1170 | u32 hci_result, value; |
1038 | int retries = 3; | 1171 | int retries = 3; |
1172 | int scancode; | ||
1039 | 1173 | ||
1040 | if (!dev->system_event_supported || event != 0x80) | 1174 | if (event != 0x80) |
1041 | return; | 1175 | return; |
1042 | 1176 | ||
1043 | do { | 1177 | if (dev->info_supported) { |
1044 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); | 1178 | scancode = toshiba_acpi_query_hotkey(dev); |
1045 | switch (hci_result) { | 1179 | if (scancode < 0) |
1046 | case HCI_SUCCESS: | 1180 | pr_err("Failed to query hotkey event\n"); |
1047 | if (value == 0x100) | 1181 | else if (scancode != 0) |
1048 | continue; | 1182 | toshiba_acpi_report_hotkey(dev, scancode); |
1049 | /* act on key press; ignore key release */ | 1183 | } else if (dev->system_event_supported) { |
1050 | if (value & 0x80) | 1184 | do { |
1051 | continue; | 1185 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); |
1052 | 1186 | switch (hci_result) { | |
1053 | if (!sparse_keymap_report_event(dev->hotkey_dev, | 1187 | case HCI_SUCCESS: |
1054 | value, 1, true)) { | 1188 | toshiba_acpi_report_hotkey(dev, (int)value); |
1055 | pr_info("Unknown key %x\n", | 1189 | break; |
1056 | value); | 1190 | case HCI_NOT_SUPPORTED: |
1191 | /* | ||
1192 | * This is a workaround for an unresolved | ||
1193 | * issue on some machines where system events | ||
1194 | * sporadically become disabled. | ||
1195 | */ | ||
1196 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, | ||
1197 | &hci_result); | ||
1198 | pr_notice("Re-enabled hotkeys\n"); | ||
1199 | /* fall through */ | ||
1200 | default: | ||
1201 | retries--; | ||
1202 | break; | ||
1057 | } | 1203 | } |
1058 | break; | 1204 | } while (retries && hci_result != HCI_EMPTY); |
1059 | case HCI_NOT_SUPPORTED: | 1205 | } |
1060 | /* This is a workaround for an unresolved issue on | ||
1061 | * some machines where system events sporadically | ||
1062 | * become disabled. */ | ||
1063 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
1064 | pr_notice("Re-enabled hotkeys\n"); | ||
1065 | /* fall through */ | ||
1066 | default: | ||
1067 | retries--; | ||
1068 | break; | ||
1069 | } | ||
1070 | } while (retries && hci_result != HCI_EMPTY); | ||
1071 | } | 1206 | } |
1072 | 1207 | ||
1208 | static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, | ||
1209 | pm_message_t state) | ||
1210 | { | ||
1211 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | ||
1212 | u32 result; | ||
1213 | |||
1214 | if (dev->hotkey_dev) | ||
1215 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | |||
1220 | static int toshiba_acpi_resume(struct acpi_device *acpi_dev) | ||
1221 | { | ||
1222 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | ||
1223 | u32 result; | ||
1224 | |||
1225 | if (dev->hotkey_dev) | ||
1226 | hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); | ||
1227 | |||
1228 | return 0; | ||
1229 | } | ||
1073 | 1230 | ||
1074 | static struct acpi_driver toshiba_acpi_driver = { | 1231 | static struct acpi_driver toshiba_acpi_driver = { |
1075 | .name = "Toshiba ACPI driver", | 1232 | .name = "Toshiba ACPI driver", |
@@ -1080,6 +1237,8 @@ static struct acpi_driver toshiba_acpi_driver = { | |||
1080 | .add = toshiba_acpi_add, | 1237 | .add = toshiba_acpi_add, |
1081 | .remove = toshiba_acpi_remove, | 1238 | .remove = toshiba_acpi_remove, |
1082 | .notify = toshiba_acpi_notify, | 1239 | .notify = toshiba_acpi_notify, |
1240 | .suspend = toshiba_acpi_suspend, | ||
1241 | .resume = toshiba_acpi_resume, | ||
1083 | }, | 1242 | }, |
1084 | }; | 1243 | }; |
1085 | 1244 | ||