eeepc-wmi: add rfkill support for wlan, bluetooth and 3g
authorCorentin Chary <corentincj@iksaif.net>
Mon, 29 Nov 2010 07:14:07 +0000 (08:14 +0100)
committerMatthew Garrett <mjg@redhat.com>
Fri, 7 Jan 2011 22:03:42 +0000 (17:03 -0500)
wimax support is missing because I don't have any DSDT
with WMI and wimax support.

Most of the code comes from eeepc-laptop.

Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
drivers/platform/x86/Kconfig
drivers/platform/x86/eeepc-wmi.c

index adcdbbd7d3fdc462c71795c26c9287017e8d31b1..4c7f8b92610365ab0d025d682b50d8d8839bc8b1 100644 (file)
@@ -425,6 +425,7 @@ config EEEPC_WMI
        depends on INPUT
        depends on EXPERIMENTAL
        depends on BACKLIGHT_CLASS_DEVICE
+       depends on RFKILL || RFKILL = n
        select INPUT_SPARSEKMAP
        select LEDS_CLASS
        select NEW_LEDS
index f12a25da40551a510217f81f8592abbcf22070ef..fb548f4b03e0aba8d6f2f4b1079e8538646b5728 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/fb.h>
 #include <linux/backlight.h>
 #include <linux/leds.h>
+#include <linux/rfkill.h>
 #include <linux/platform_device.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
@@ -62,6 +63,9 @@ MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
 
 #define EEEPC_WMI_DEVID_BACKLIGHT      0x00050012
 #define EEEPC_WMI_DEVID_TPDLED         0x00100011
+#define EEEPC_WMI_DEVID_WLAN           0x00010011
+#define EEEPC_WMI_DEVID_BLUETOOTH      0x00010013
+#define EEEPC_WMI_DEVID_WWAN3G         0x00010019
 
 static const struct key_entry eeepc_wmi_keymap[] = {
        /* Sleep already handled via generic ACPI code */
@@ -94,6 +98,10 @@ struct eeepc_wmi {
        int tpd_led_wk;
        struct workqueue_struct *led_workqueue;
        struct work_struct tpd_led_work;
+
+       struct rfkill *wlan_rfkill;
+       struct rfkill *bluetooth_rfkill;
+       struct rfkill *wwan3g_rfkill;
 };
 
 /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
@@ -279,6 +287,129 @@ static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
                destroy_workqueue(eeepc->led_workqueue);
 }
 
+/*
+ * Rfkill devices
+ */
+static int eeepc_rfkill_set(void *data, bool blocked)
+{
+       int dev_id = (unsigned long)data;
+       u32 ctrl_param = !blocked;
+
+       return eeepc_wmi_set_devstate(dev_id, ctrl_param);
+}
+
+static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
+{
+       int dev_id = (unsigned long)data;
+       u32 ctrl_param;
+       acpi_status status;
+
+       status = eeepc_wmi_get_devstate(dev_id, &ctrl_param);
+
+       if (ACPI_FAILURE(status))
+               return ;
+
+       rfkill_set_sw_state(rfkill, !(ctrl_param & 0x1));
+}
+
+static const struct rfkill_ops eeepc_rfkill_ops = {
+       .set_block = eeepc_rfkill_set,
+       .query = eeepc_rfkill_query,
+};
+
+static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
+                           struct rfkill **rfkill,
+                           const char *name,
+                           enum rfkill_type type, int dev_id)
+{
+       int result;
+       u32 ctrl_param;
+       acpi_status status;
+
+       status = eeepc_wmi_get_devstate(dev_id, &ctrl_param);
+
+       if (ACPI_FAILURE(status))
+               return -1;
+
+       /* If the device is present, DSTS will always set some bits
+        * 0x00070000 - 1110000000000000000 - device supported
+        * 0x00060000 - 1100000000000000000 - not supported
+        * 0x00020000 - 0100000000000000000 - device supported
+        * 0x00010000 - 0010000000000000000 - not supported / special mode ?
+        */
+       if (!ctrl_param || ctrl_param == 0x00060000)
+               return -ENODEV;
+
+       *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
+                              &eeepc_rfkill_ops, (void *)(long)dev_id);
+
+       if (!*rfkill)
+               return -EINVAL;
+
+       rfkill_init_sw_state(*rfkill, !(ctrl_param & 0x1));
+       result = rfkill_register(*rfkill);
+       if (result) {
+               rfkill_destroy(*rfkill);
+               *rfkill = NULL;
+               return result;
+       }
+       return 0;
+}
+
+static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
+{
+       if (eeepc->wlan_rfkill) {
+               rfkill_unregister(eeepc->wlan_rfkill);
+               rfkill_destroy(eeepc->wlan_rfkill);
+               eeepc->wlan_rfkill = NULL;
+       }
+       if (eeepc->bluetooth_rfkill) {
+               rfkill_unregister(eeepc->bluetooth_rfkill);
+               rfkill_destroy(eeepc->bluetooth_rfkill);
+               eeepc->bluetooth_rfkill = NULL;
+       }
+       if (eeepc->wwan3g_rfkill) {
+               rfkill_unregister(eeepc->wwan3g_rfkill);
+               rfkill_destroy(eeepc->wwan3g_rfkill);
+               eeepc->wwan3g_rfkill = NULL;
+       }
+}
+
+static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
+{
+       int result = 0;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
+                                 "eeepc-wlan", RFKILL_TYPE_WLAN,
+                                 EEEPC_WMI_DEVID_WLAN);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
+                                 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
+                                 EEEPC_WMI_DEVID_BLUETOOTH);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+       result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
+                                 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
+                                 EEEPC_WMI_DEVID_WWAN3G);
+
+       if (result && result != -ENODEV)
+               goto exit;
+
+exit:
+       if (result && result != -ENODEV)
+               eeepc_wmi_rfkill_exit(eeepc);
+
+       if (result == -ENODEV)
+               result = 0;
+
+       return result;
+}
+
 /*
  * Backlight
  */
@@ -512,6 +643,10 @@ static struct platform_device * __init eeepc_wmi_add(void)
        if (err)
                goto fail_leds;
 
+       err = eeepc_wmi_rfkill_init(eeepc);
+       if (err)
+               goto fail_rfkill;
+
        if (!acpi_video_backlight_support()) {
                err = eeepc_wmi_backlight_init(eeepc);
                if (err)
@@ -533,6 +668,8 @@ static struct platform_device * __init eeepc_wmi_add(void)
 fail_wmi_handler:
        eeepc_wmi_backlight_exit(eeepc);
 fail_backlight:
+       eeepc_wmi_rfkill_exit(eeepc);
+fail_rfkill:
        eeepc_wmi_led_exit(eeepc);
 fail_leds:
        eeepc_wmi_input_exit(eeepc);
@@ -552,6 +689,7 @@ static int eeepc_wmi_remove(struct platform_device *device)
        eeepc_wmi_backlight_exit(eeepc);
        eeepc_wmi_input_exit(eeepc);
        eeepc_wmi_led_exit(eeepc);
+       eeepc_wmi_rfkill_exit(eeepc);
        eeepc_wmi_platform_exit(eeepc);
 
        kfree(eeepc);