aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2015-06-06 04:23:30 -0400
committerDarren Hart <dvhart@linux.intel.com>2015-06-11 01:04:28 -0400
commitf8358578e2f23bd82d0454c17676bdb28a40664a (patch)
treee1b1039d0710ffb59ea79cd688b54dbd7e69f78a /drivers/platform
parentb05ffc95f9ed986534b67538e239e9c4ba254b55 (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.c94
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
646static int (*dell_rbtn_notifier_register_func)(struct notifier_block *);
647static int (*dell_rbtn_notifier_unregister_func)(struct notifier_block *);
648
649static 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
656static struct notifier_block dell_laptop_rbtn_notifier = {
657 .notifier_call = dell_laptop_rbtn_notifier_call,
658};
659
645static int __init dell_setup_rfkill(void) 660static 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
745static void dell_cleanup_rfkill(void) 812static 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
1963fail_backlight: 2038fail_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();
1967fail_rfkill: 2040fail_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
1998module_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 */
2076late_initcall(dell_init);
1999module_exit(dell_exit); 2077module_exit(dell_exit);
2000 2078
2001MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 2079MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");