diff options
author | Pali Rohár <pali.rohar@gmail.com> | 2015-06-06 04:23:30 -0400 |
---|---|---|
committer | Darren Hart <dvhart@linux.intel.com> | 2015-06-11 01:04:28 -0400 |
commit | f8358578e2f23bd82d0454c17676bdb28a40664a (patch) | |
tree | e1b1039d0710ffb59ea79cd688b54dbd7e69f78a /drivers/platform | |
parent | b05ffc95f9ed986534b67538e239e9c4ba254b55 (diff) |
dell-laptop: Use dell-rbtn instead i8042 filter when possible
Until now module dell-laptop registered rfkill device which used i8042
filter function for receiving HW switch rfkill events (handling special
keycode).
But for some dell laptops there is native ACPI driver dell-rbtn which can
receive rfkill events (without i8042 hooks).
So this patch will combine best from both sides. It will use native ACPI
driver dell-rbtn for receiving events and dell-laptop SMBIOS interface for
enabling or disabling radio devices. If ACPI driver or device will not be
available fallback to i8042 filter function will be used.
Patch also changes module_init() to late_initcall() to ensure that init
function will be called after initializing dell-rbtn.c driver.
Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Tested-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 94 |
1 files changed, 86 insertions, 8 deletions
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d688d806a8a5..83e3d7f9d923 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/debugfs.h> | 32 | #include <linux/debugfs.h> |
33 | #include <linux/seq_file.h> | 33 | #include <linux/seq_file.h> |
34 | #include "../../firmware/dcdbas.h" | 34 | #include "../../firmware/dcdbas.h" |
35 | #include "dell-rbtn.h" | ||
35 | 36 | ||
36 | #define BRIGHTNESS_TOKEN 0x7d | 37 | #define BRIGHTNESS_TOKEN 0x7d |
37 | #define KBD_LED_OFF_TOKEN 0x01E1 | 38 | #define KBD_LED_OFF_TOKEN 0x01E1 |
@@ -642,6 +643,20 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | |||
642 | return false; | 643 | return false; |
643 | } | 644 | } |
644 | 645 | ||
646 | static int (*dell_rbtn_notifier_register_func)(struct notifier_block *); | ||
647 | static int (*dell_rbtn_notifier_unregister_func)(struct notifier_block *); | ||
648 | |||
649 | static int dell_laptop_rbtn_notifier_call(struct notifier_block *nb, | ||
650 | unsigned long action, void *data) | ||
651 | { | ||
652 | schedule_delayed_work(&dell_rfkill_work, 0); | ||
653 | return NOTIFY_OK; | ||
654 | } | ||
655 | |||
656 | static struct notifier_block dell_laptop_rbtn_notifier = { | ||
657 | .notifier_call = dell_laptop_rbtn_notifier_call, | ||
658 | }; | ||
659 | |||
645 | static int __init dell_setup_rfkill(void) | 660 | static int __init dell_setup_rfkill(void) |
646 | { | 661 | { |
647 | int status, ret, whitelisted; | 662 | int status, ret, whitelisted; |
@@ -718,10 +733,62 @@ static int __init dell_setup_rfkill(void) | |||
718 | goto err_wwan; | 733 | goto err_wwan; |
719 | } | 734 | } |
720 | 735 | ||
721 | ret = i8042_install_filter(dell_laptop_i8042_filter); | 736 | /* |
722 | if (ret) { | 737 | * Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices |
723 | pr_warn("Unable to install key filter\n"); | 738 | * which can receive events from HW slider switch. |
739 | * | ||
740 | * Dell SMBIOS on whitelisted models supports controlling radio devices | ||
741 | * but does not support receiving HW button switch events. We can use | ||
742 | * i8042 filter hook function to receive keyboard data and handle | ||
743 | * keycode for HW button. | ||
744 | * | ||
745 | * So if it is possible we will use Dell Airplane Mode Switch ACPI | ||
746 | * driver for receiving HW events and Dell SMBIOS for setting rfkill | ||
747 | * states. If ACPI driver or device is not available we will fallback to | ||
748 | * i8042 filter hook function. | ||
749 | * | ||
750 | * To prevent duplicate rfkill devices which control and do same thing, | ||
751 | * dell-rbtn driver will automatically remove its own rfkill devices | ||
752 | * once function dell_rbtn_notifier_register() is called. | ||
753 | */ | ||
754 | |||
755 | dell_rbtn_notifier_register_func = | ||
756 | symbol_request(dell_rbtn_notifier_register); | ||
757 | if (dell_rbtn_notifier_register_func) { | ||
758 | dell_rbtn_notifier_unregister_func = | ||
759 | symbol_request(dell_rbtn_notifier_unregister); | ||
760 | if (!dell_rbtn_notifier_unregister_func) { | ||
761 | symbol_put(dell_rbtn_notifier_register); | ||
762 | dell_rbtn_notifier_register_func = NULL; | ||
763 | } | ||
764 | } | ||
765 | |||
766 | if (dell_rbtn_notifier_register_func) { | ||
767 | ret = dell_rbtn_notifier_register_func( | ||
768 | &dell_laptop_rbtn_notifier); | ||
769 | symbol_put(dell_rbtn_notifier_register); | ||
770 | dell_rbtn_notifier_register_func = NULL; | ||
771 | if (ret != 0) { | ||
772 | symbol_put(dell_rbtn_notifier_unregister); | ||
773 | dell_rbtn_notifier_unregister_func = NULL; | ||
774 | } | ||
775 | } else { | ||
776 | pr_info("Symbols from dell-rbtn acpi driver are not available\n"); | ||
777 | ret = -ENODEV; | ||
778 | } | ||
779 | |||
780 | if (ret == 0) { | ||
781 | pr_info("Using dell-rbtn acpi driver for receiving events\n"); | ||
782 | } else if (ret != -ENODEV) { | ||
783 | pr_warn("Unable to register dell rbtn notifier\n"); | ||
724 | goto err_filter; | 784 | goto err_filter; |
785 | } else { | ||
786 | ret = i8042_install_filter(dell_laptop_i8042_filter); | ||
787 | if (ret) { | ||
788 | pr_warn("Unable to install key filter\n"); | ||
789 | goto err_filter; | ||
790 | } | ||
791 | pr_info("Using i8042 filter function for receiving events\n"); | ||
725 | } | 792 | } |
726 | 793 | ||
727 | return 0; | 794 | return 0; |
@@ -744,6 +811,14 @@ err_wifi: | |||
744 | 811 | ||
745 | static void dell_cleanup_rfkill(void) | 812 | static void dell_cleanup_rfkill(void) |
746 | { | 813 | { |
814 | if (dell_rbtn_notifier_unregister_func) { | ||
815 | dell_rbtn_notifier_unregister_func(&dell_laptop_rbtn_notifier); | ||
816 | symbol_put(dell_rbtn_notifier_unregister); | ||
817 | dell_rbtn_notifier_unregister_func = NULL; | ||
818 | } else { | ||
819 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
820 | } | ||
821 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
747 | if (wifi_rfkill) { | 822 | if (wifi_rfkill) { |
748 | rfkill_unregister(wifi_rfkill); | 823 | rfkill_unregister(wifi_rfkill); |
749 | rfkill_destroy(wifi_rfkill); | 824 | rfkill_destroy(wifi_rfkill); |
@@ -1961,8 +2036,6 @@ static int __init dell_init(void) | |||
1961 | return 0; | 2036 | return 0; |
1962 | 2037 | ||
1963 | fail_backlight: | 2038 | fail_backlight: |
1964 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
1965 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
1966 | dell_cleanup_rfkill(); | 2039 | dell_cleanup_rfkill(); |
1967 | fail_rfkill: | 2040 | fail_rfkill: |
1968 | free_page((unsigned long)bufferpage); | 2041 | free_page((unsigned long)bufferpage); |
@@ -1983,8 +2056,6 @@ static void __exit dell_exit(void) | |||
1983 | if (quirks && quirks->touchpad_led) | 2056 | if (quirks && quirks->touchpad_led) |
1984 | touchpad_led_exit(); | 2057 | touchpad_led_exit(); |
1985 | kbd_led_exit(); | 2058 | kbd_led_exit(); |
1986 | i8042_remove_filter(dell_laptop_i8042_filter); | ||
1987 | cancel_delayed_work_sync(&dell_rfkill_work); | ||
1988 | backlight_device_unregister(dell_backlight_device); | 2059 | backlight_device_unregister(dell_backlight_device); |
1989 | dell_cleanup_rfkill(); | 2060 | dell_cleanup_rfkill(); |
1990 | if (platform_device) { | 2061 | if (platform_device) { |
@@ -1995,7 +2066,14 @@ static void __exit dell_exit(void) | |||
1995 | free_page((unsigned long)buffer); | 2066 | free_page((unsigned long)buffer); |
1996 | } | 2067 | } |
1997 | 2068 | ||
1998 | module_init(dell_init); | 2069 | /* dell-rbtn.c driver export functions which will not work correctly (and could |
2070 | * cause kernel crash) if they are called before dell-rbtn.c init code. This is | ||
2071 | * not problem when dell-rbtn.c is compiled as external module. When both files | ||
2072 | * (dell-rbtn.c and dell-laptop.c) are compiled statically into kernel, then we | ||
2073 | * need to ensure that dell_init() will be called after initializing dell-rbtn. | ||
2074 | * This can be achieved by late_initcall() instead module_init(). | ||
2075 | */ | ||
2076 | late_initcall(dell_init); | ||
1999 | module_exit(dell_exit); | 2077 | module_exit(dell_exit); |
2000 | 2078 | ||
2001 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); | 2079 | MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); |