asus-laptop: Add rfkill support for Pegatron Lucid tablet
Anisse Astier [Fri, 14 Oct 2011 09:13:42 +0000 (11:13 +0200)]
Add three new rfkill switches in this driver that are specific to
Pegatron Lucid tablet.
Please note that you might not need all three switches. For example if
you don't have a 3G module inside your tablet.

Also, on my device, the gpio for the wifi/bt module is connected to the
bluetooth line. Therefore to activate your wireless lan interface, you
need to use the "pega-bt" rfkill switch.

Finally, the rfkill switch only works before the wireless module is
loaded the first time. Unloading ath9k doesn't help, a reboot is
necessary.

Signed-off-by: Anisse Astier <anisse@astier.eu>
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Matthew Garrett <mjg@redhat.com>

drivers/platform/x86/asus-laptop.c

index 05c0e59..edaccad 100644 (file)
@@ -186,6 +186,9 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
 
 #define METHOD_PEGA_ENABLE     "ENPR"
 #define METHOD_PEGA_DISABLE    "DAPR"
+#define PEGA_WLAN      0x00
+#define PEGA_BLUETOOTH 0x01
+#define PEGA_WWAN      0x02
 #define PEGA_ALS       0x04
 #define PEGA_ALS_POWER 0x05
 
@@ -213,6 +216,15 @@ struct asus_led {
 };
 
 /*
+ * Same thing for rfkill
+ */
+struct asus_pega_rfkill {
+       int control_id;         /* type of control. Maps to PEGA_* values */
+       struct rfkill *rfkill;
+       struct asus_laptop *asus;
+};
+
+/*
  * This is the main structure, we can use it to store anything interesting
  * about the hotk device
  */
@@ -246,6 +258,10 @@ struct asus_laptop {
 
        struct rfkill *gps_rfkill;
 
+       struct asus_pega_rfkill wlanrfk;
+       struct asus_pega_rfkill btrfk;
+       struct asus_pega_rfkill wwanrfk;
+
        acpi_handle handle;     /* the handle of the hotk device */
        u32 ledd_status;        /* status of the LED display */
        u8 light_level;         /* light sensor level */
@@ -1263,6 +1279,86 @@ static int asus_rfkill_init(struct asus_laptop *asus)
        return result;
 }
 
+static int pega_rfkill_set(void *data, bool blocked)
+{
+       struct asus_pega_rfkill *pega_rfk = data;
+
+       int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
+       pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
+
+       return ret;
+}
+
+static const struct rfkill_ops pega_rfkill_ops = {
+       .set_block = pega_rfkill_set,
+};
+
+static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
+{
+       pr_warn("Terminating %d\n", pega_rfk->control_id);
+       if (pega_rfk->rfkill) {
+               rfkill_unregister(pega_rfk->rfkill);
+               rfkill_destroy(pega_rfk->rfkill);
+               pega_rfk->rfkill = NULL;
+       }
+}
+
+static void pega_rfkill_exit(struct asus_laptop *asus)
+{
+       pega_rfkill_terminate(&asus->wwanrfk);
+       pega_rfkill_terminate(&asus->btrfk);
+       pega_rfkill_terminate(&asus->wlanrfk);
+}
+
+static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
+               const char *name, int controlid, int rfkill_type)
+{
+       int result;
+
+       pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
+       pega_rfk->control_id = controlid;
+       pega_rfk->asus = asus;
+       pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
+                                       rfkill_type, &pega_rfkill_ops, pega_rfk);
+       if (!pega_rfk->rfkill)
+               return -EINVAL;
+
+       result = rfkill_register(pega_rfk->rfkill);
+       if (result) {
+               rfkill_destroy(pega_rfk->rfkill);
+               pega_rfk->rfkill = NULL;
+       }
+
+       return result;
+}
+
+static int pega_rfkill_init(struct asus_laptop *asus)
+{
+       int ret = 0;
+
+       if(!asus->is_pega_lucid)
+               return -ENODEV;
+
+       ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
+       if(ret)
+               return ret;
+       ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
+       if(ret)
+               goto err_btrfk;
+       ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
+       if(ret)
+               goto err_wwanrfk;
+
+       pr_warn("Pega rfkill init succeeded\n");
+       return 0;
+err_wwanrfk:
+       pega_rfkill_terminate(&asus->btrfk);
+err_btrfk:
+       pega_rfkill_terminate(&asus->wlanrfk);
+
+       return ret;
+}
+
 /*
  * Input device (i.e. hotkeys)
  */
@@ -1697,9 +1793,15 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
        if (result && result != -ENODEV)
                goto fail_pega_accel;
 
+       result = pega_rfkill_init(asus);
+       if (result && result != -ENODEV)
+               goto fail_pega_rfkill;
+
        asus_device_present = true;
        return 0;
 
+fail_pega_rfkill:
+       pega_accel_exit(asus);
 fail_pega_accel:
        asus_rfkill_exit(asus);
 fail_rfkill:
@@ -1726,6 +1828,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
        asus_led_exit(asus);
        asus_input_exit(asus);
        pega_accel_exit(asus);
+       pega_rfkill_exit(asus);
        asus_platform_exit(asus);
 
        kfree(asus->name);