Re: [PATCH 4/4] asus-wireless: Toggle airplane mode LED

From: Andy Shevchenko
Date: Sun Dec 27 2015 - 08:32:34 EST


On Sat, Dec 26, 2015 at 4:56 PM, JoÃo Paulo Rechi Vita
<jprvita@xxxxxxxxx> 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 <jprvita@xxxxxxxxxxxx>
> ---
> 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 majordomo@xxxxxxxxxxxxxxx
> 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/