32
32
#include <linux/debugfs.h>
33
33
#include <linux/seq_file.h>
34
34
#include "../../firmware/dcdbas.h"
35
+ #include "dell-rbtn.h"
35
36
36
37
#define BRIGHTNESS_TOKEN 0x7d
37
38
#define KBD_LED_OFF_TOKEN 0x01E1
@@ -642,6 +643,20 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
642
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
660
static int __init dell_setup_rfkill (void )
646
661
{
647
662
int status , ret , whitelisted ;
@@ -718,10 +733,62 @@ static int __init dell_setup_rfkill(void)
718
733
goto err_wwan ;
719
734
}
720
735
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" );
724
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
794
return 0 ;
@@ -744,6 +811,14 @@ static int __init dell_setup_rfkill(void)
744
811
745
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
822
if (wifi_rfkill ) {
748
823
rfkill_unregister (wifi_rfkill );
749
824
rfkill_destroy (wifi_rfkill );
@@ -1961,8 +2036,6 @@ static int __init dell_init(void)
1961
2036
return 0 ;
1962
2037
1963
2038
fail_backlight :
1964
- i8042_remove_filter (dell_laptop_i8042_filter );
1965
- cancel_delayed_work_sync (& dell_rfkill_work );
1966
2039
dell_cleanup_rfkill ();
1967
2040
fail_rfkill :
1968
2041
free_page ((unsigned long )bufferpage );
@@ -1983,8 +2056,6 @@ static void __exit dell_exit(void)
1983
2056
if (quirks && quirks -> touchpad_led )
1984
2057
touchpad_led_exit ();
1985
2058
kbd_led_exit ();
1986
- i8042_remove_filter (dell_laptop_i8042_filter );
1987
- cancel_delayed_work_sync (& dell_rfkill_work );
1988
2059
backlight_device_unregister (dell_backlight_device );
1989
2060
dell_cleanup_rfkill ();
1990
2061
if (platform_device ) {
@@ -1995,7 +2066,14 @@ static void __exit dell_exit(void)
1995
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
2077
module_exit (dell_exit );
2000
2078
2001
2079
MODULE_AUTHOR (
"Matthew Garrett <[email protected] >" );
0 commit comments