[PATCH v2 3/3] platform: x86: dell-laptop: Use dell-rbtn instead i8042 filter when possible

From: Pali RohÃr
Date: Wed Apr 29 2015 - 05:51:34 EST


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 hacks). On some machines it can also
control rfkill devices, but can turn on/off all radio devices.

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.

Signed-off-by: Pali RohÃr <pali.rohar@xxxxxxxxx>
---
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/dell-laptop.c | 67 +++++++++++++++++++++++++++++++++---
2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 9d065c2..357e17f 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -97,6 +97,7 @@ config DELL_LAPTOP
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on SERIO_I8042
+ select DELL_RBTN
select POWER_SUPPLY
select LEDS_CLASS
select NEW_LEDS
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index d688d80..c9ea0f8 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -32,6 +32,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include "../../firmware/dcdbas.h"
+#include "dell-rbtn.h"

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

+static bool dell_laptop_use_rbtn;
+
+static int dell_laptop_rbtn_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ schedule_delayed_work(&dell_rfkill_work,
+ round_jiffies_relative(HZ / 4));
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dell_laptop_rbtn_notifier = {
+ .notifier_call = dell_laptop_rbtn_notifier_call,
+};
+
static int __init dell_setup_rfkill(void)
{
int status, ret, whitelisted;
@@ -718,10 +733,46 @@ static int __init dell_setup_rfkill(void)
goto err_wwan;
}

- ret = i8042_install_filter(dell_laptop_i8042_filter);
- if (ret) {
- pr_warn("Unable to install key filter\n");
+ /*
+ * Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices
+ * which can receive HW button switch events and also can control radio
+ * devices. Somtimes ACPI device supports only reciving events (without
+ * enable/disable software control).
+ *
+ * Dell SMBIOS on whitelisted models supports controlling radio devices
+ * but does not support receiving HW button switch events. We can use
+ * i8042 filter hook function to receive keyboard data and handle
+ * keycode for HW button.
+ *
+ * Dell Airplane Mode Switch driver supports only one rfkill switch
+ * which enable/disable all radio devices. But Dell SMBIOS supports more
+ * granularity and can enable/disable also one type of radio device
+ * (e.g disable only bluetooth device without touching wifi device).
+ *
+ * So if it is possible we will use Dell Airplane Mode Switch ACPI
+ * driver for receiving HW events and Dell SMBIOS for setting rfkill
+ * states. If ACPI driver or device is not available we will fallback to
+ * i8042 filter hook function.
+ *
+ * To prevent duplicate rfkill devices which control and do same thing,
+ * dell-rbtn driver will automatically remove its own rfkill devices
+ * once function dell_rbtn_notifier_register() is called.
+ */
+
+ ret = dell_rbtn_notifier_register(&dell_laptop_rbtn_notifier);
+ if (ret == 0) {
+ pr_info("Using dell-rbtn acpi driver for receiving events\n");
+ dell_laptop_use_rbtn = true;
+ } else if (ret != -ENODEV) {
+ pr_warn("Unable to register dell rbtn notifier\n");
goto err_filter;
+ } else {
+ ret = i8042_install_filter(dell_laptop_i8042_filter);
+ if (ret) {
+ pr_warn("Unable to install key filter\n");
+ goto err_filter;
+ }
+ pr_info("Using i8042 filter function for receiving events\n");
}

return 0;
@@ -1961,7 +2012,10 @@ static int __init dell_init(void)
return 0;

fail_backlight:
- i8042_remove_filter(dell_laptop_i8042_filter);
+ if (dell_laptop_use_rbtn)
+ dell_rbtn_notifier_unregister(&dell_laptop_rbtn_notifier);
+ else
+ i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
dell_cleanup_rfkill();
fail_rfkill:
@@ -1983,7 +2037,10 @@ static void __exit dell_exit(void)
if (quirks && quirks->touchpad_led)
touchpad_led_exit();
kbd_led_exit();
- i8042_remove_filter(dell_laptop_i8042_filter);
+ if (dell_laptop_use_rbtn)
+ dell_rbtn_notifier_unregister(&dell_laptop_rbtn_notifier);
+ else
+ i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);
dell_cleanup_rfkill();
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/