On Sat, Dec 26, 2015 at 4:56 PM, João Paulo Rechi Vita
<jprv...@gmail.com> wrote:
> In the ASHS device we have the HSWC method, which basically calls either
> OWGD or OWGS, depending on its parameter:
>
>         Device (ASHS)
>         {
>                 Name (_HID, "ATK4002")  // _HID: Hardware ID
>                 Method (HSWC, 1, Serialized)
>                 {
>                         If ((Arg0 < 0x02))
>                         {
>                                 OWGD (Arg0)
>                                 Return (One)
>                         }
>                         If ((Arg0 == 0x02))
>                         {
>                                 Local0 = OWGS ()
>                                 If (Local0)
>                                 {
>                                         Return (0x05)
>                                 }
>                                 Else
>                                 {
>                                         Return (0x04)
>                                 }
>                         }
>                         If ((Arg0 == 0x03))
>                         {
>                                 Return (0xFF)
>                         }
>                         If ((Arg0 == 0x04))
>                         {
>                                 OWGD (Zero)
>                                 Return (One)
>                         }
>                         If ((Arg0 == 0x05))
>                         {
>                                 OWGD (One)
>                                 Return (One)
>                         }
>                         If ((Arg0 == 0x80))
>                         {
>                                 Return (One)
>                         }
>                 }
>                 Method (_STA, 0, NotSerialized)  // _STA: Status
>                 {
>                         If ((MSOS () >= OSW8))
>                         {
>                                 Return (0x0F)
>                         }
>                         Else
>                         {
>                                 Return (Zero)
>                         }
>                 }
>         }
>
> On the Asus E202SA laptop, which does not have an airplane mode LED,
> OWGD has an empty implementation and OWGS simply returns 0. On the Asus
> X555UB these methods have the following implementation:
>
>         Method (OWGD, 1, Serialized)
>         {
>                 SGPL (0x0203000F, Arg0)
>                 SGPL (0x0203000F, Arg0)
>         }
>
>         Method (OWGS, 0, Serialized)
>         {
>                 Store (RGPL (0x0203000F), Local0)
>                 Return (Local0)
>         }
>
> Where OWGD(1) sets the airplane mode LED ON, OWGD(0) set it off, and
> OWGS() returns its state.
>
> This commit makes use of a newly implemented RFKill LED trigger to
> trigger the LED when the system enters or exits "Airplane Mode", there
> is, when all radios are blocked.
>
> Signed-off-by: João Paulo Rechi Vita <jprv...@endlessm.com>
> ---
>  drivers/platform/x86/Kconfig         |  2 +
>  drivers/platform/x86/asus-wireless.c | 81 
> ++++++++++++++++++++++++++++++++++++
>  2 files changed, 83 insertions(+)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index d3a088b..3d8dc0b 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -592,6 +592,8 @@ config ASUS_WIRELESS
>         depends on ACPI
>         depends on INPUT
>         default m
> +       select NEW_LEDS
> +       select LEDS_CLASS
>         ---help---
>           The Asus Wireless Radio Control handles the airplane mode hotkey
>           present on some Asus laptops.
> diff --git a/drivers/platform/x86/asus-wireless.c 
> b/drivers/platform/x86/asus-wireless.c
> index 7928efd..489ef83 100644
> --- a/drivers/platform/x86/asus-wireless.c
> +++ b/drivers/platform/x86/asus-wireless.c
> @@ -17,13 +17,76 @@
>  #include <linux/acpi.h>
>  #include <linux/input.h>
>  #include <linux/pci_ids.h>
> +#include <linux/leds.h>
>
>  #define ASUS_WIRELESS_MODULE_NAME "Asus Wireless Radio Control Driver"
> +#define ASUS_WIRELESS_LED_STATUS 0x2
> +#define ASUS_WIRELESS_LED_OFF 0x4
> +#define ASUS_WIRELESS_LED_ON 0x5
>
>  struct asus_wireless_data {
>         struct input_dev *inputdev;
> +       struct acpi_device *acpidev;

You can get this easily from struct device.

> +       struct workqueue_struct *wq;
> +       struct work_struct led_work;
> +       struct led_classdev led;
> +       int led_state;
>  };
>
> +static u64 asus_wireless_method(acpi_handle handle, const char *method,
> +                               int param)
> +{
> +       union acpi_object obj;
> +       struct acpi_object_list p;
> +       acpi_status s;
> +       u64 ret;
> +
> +       pr_debug("Evaluating method %s, parameter 0x%X\n", method, param);

acpi_handle_* in such cases.

> +       obj.type = ACPI_TYPE_INTEGER;
> +       obj.integer.value = param;
> +       p.count = 1;
> +       p.pointer = &obj;
> +
> +       s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
> +       if (!ACPI_SUCCESS(s))

ACPI_FAILURE()

> +               pr_err("Failed to evaluate method %s, parameter 0x%X (%d)\n",
> +                      method, param, s);
> +       pr_debug("%s returned 0x%X\n", method, (uint) ret);
> +       return ret;
> +}
> +
> +static enum led_brightness asus_wireless_led_get(struct led_classdev *led)
> +{
> +       struct asus_wireless_data *data;
> +       int s;
> +
> +       data = container_of(led, struct asus_wireless_data, led);
> +       s = asus_wireless_method(data->acpidev->handle, "HSWC",

Usually we get a handle through specific macro ACPI_HANDLE from a
struct device (see above).

> +                                ASUS_WIRELESS_LED_STATUS);
> +       if (s == ASUS_WIRELESS_LED_ON)
> +               return LED_FULL;
> +       return LED_OFF;
> +}
> +
> +static void asus_wireless_led_update(struct work_struct *work)
> +{
> +       struct asus_wireless_data *data;
> +
> +       data = container_of(work, struct asus_wireless_data, led_work);
> +       asus_wireless_method(data->acpidev->handle, "HSWC", data->led_state);

Ditto.

> +}
> +
> +static void asus_wireless_led_set(struct led_classdev *led,
> +                                 enum led_brightness value)
> +{
> +       struct asus_wireless_data *data;
> +
> +       data = container_of(led, struct asus_wireless_data, led);
> +       data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
> +                                            ASUS_WIRELESS_LED_ON;
> +       queue_work(data->wq, &data->led_work);
> +}
> +
>  static void asus_wireless_notify(struct acpi_device *device, u32 event)
>  {
>         struct asus_wireless_data *data = acpi_driver_data(device);
> @@ -49,6 +112,7 @@ static int asus_wireless_add(struct acpi_device *device)
>                 return -ENOMEM;
>         device->driver_data = data;
>
> +       data->acpidev = device;
>         data->inputdev = input_allocate_device();
>         if (!data->inputdev)
>                 goto fail;
> @@ -64,6 +128,21 @@ static int asus_wireless_add(struct acpi_device *device)
>         err = input_register_device(data->inputdev);
>         if (err)
>                 goto fail;
> +
> +       data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
> +       if (!data->wq)
> +               goto fail;
> +
> +       INIT_WORK(&data->led_work, asus_wireless_led_update);
> +       data->led.name = "asus-wireless::airplane_mode";
> +       data->led.brightness_set = asus_wireless_led_set;
> +       data->led.brightness_get = asus_wireless_led_get;
> +       data->led.flags = LED_CORE_SUSPENDRESUME;
> +       data->led.max_brightness = 1;
> +       data->led.default_trigger = "rfkill-airplane-mode";
> +       err = led_classdev_register(&device->dev, &data->led);
> +       if (err)
> +               goto fail;
>         return 0;
>
>  fail:
> @@ -78,6 +157,8 @@ static int asus_wireless_remove(struct acpi_device *device)
>         pr_info("Removing "ASUS_WIRELESS_MODULE_NAME"\n");
>         if (data->inputdev)
>                 input_unregister_device(data->inputdev);
> +       if (data->wq)
> +               destroy_workqueue(data->wq);
>         kfree(data);
>         return 0;
>  }
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



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

Reply via email to