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 platform-driver-x86" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html