Skip to content

Commit f835857

Browse files
palidvhart
authored andcommitted
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 <[email protected]> Tested-by: Gabriele Mazzotta <[email protected]> Signed-off-by: Darren Hart <[email protected]>
1 parent b05ffc9 commit f835857

File tree

1 file changed

+86
-8
lines changed

1 file changed

+86
-8
lines changed

drivers/platform/x86/dell-laptop.c

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <linux/debugfs.h>
3333
#include <linux/seq_file.h>
3434
#include "../../firmware/dcdbas.h"
35+
#include "dell-rbtn.h"
3536

3637
#define BRIGHTNESS_TOKEN 0x7d
3738
#define KBD_LED_OFF_TOKEN 0x01E1
@@ -642,6 +643,20 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
642643
return false;
643644
}
644645

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+
645660
static int __init dell_setup_rfkill(void)
646661
{
647662
int status, ret, whitelisted;
@@ -718,10 +733,62 @@ static int __init dell_setup_rfkill(void)
718733
goto err_wwan;
719734
}
720735

721-
ret = i8042_install_filter(dell_laptop_i8042_filter);
722-
if (ret) {
723-
pr_warn("Unable to install key filter\n");
736+
/*
737+
* Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices
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");
724784
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");
725792
}
726793

727794
return 0;
@@ -744,6 +811,14 @@ static int __init dell_setup_rfkill(void)
744811

745812
static void dell_cleanup_rfkill(void)
746813
{
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);
747822
if (wifi_rfkill) {
748823
rfkill_unregister(wifi_rfkill);
749824
rfkill_destroy(wifi_rfkill);
@@ -1961,8 +2036,6 @@ static int __init dell_init(void)
19612036
return 0;
19622037

19632038
fail_backlight:
1964-
i8042_remove_filter(dell_laptop_i8042_filter);
1965-
cancel_delayed_work_sync(&dell_rfkill_work);
19662039
dell_cleanup_rfkill();
19672040
fail_rfkill:
19682041
free_page((unsigned long)bufferpage);
@@ -1983,8 +2056,6 @@ static void __exit dell_exit(void)
19832056
if (quirks && quirks->touchpad_led)
19842057
touchpad_led_exit();
19852058
kbd_led_exit();
1986-
i8042_remove_filter(dell_laptop_i8042_filter);
1987-
cancel_delayed_work_sync(&dell_rfkill_work);
19882059
backlight_device_unregister(dell_backlight_device);
19892060
dell_cleanup_rfkill();
19902061
if (platform_device) {
@@ -1995,7 +2066,14 @@ static void __exit dell_exit(void)
19952066
free_page((unsigned long)buffer);
19962067
}
19972068

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);
19992077
module_exit(dell_exit);
20002078

20012079
MODULE_AUTHOR("Matthew Garrett <[email protected]>");

0 commit comments

Comments
 (0)