aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorJoão Paulo Rechi Vita <jprvita@gmail.com>2016-06-13 16:57:30 -0400
committerDarren Hart <dvhart@linux.intel.com>2016-07-01 19:01:26 -0400
commit2c1a49c961237ed8758af3188673b0e9a8826ac6 (patch)
tree00da181b507b503382ca8d0531eaa87cbad1f89e /drivers/platform
parent08a245160bce8b3d044fa0fdac9ca099bd720706 (diff)
asus-wireless: Toggle airplane mode LED
In the ASHS device we have the HSWC method, which 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 laptops that do not have an airplane mode LED, OWGD has an empty implementation and OWGS simply returns 0. On the ones that have an airplane mode LED 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 exposes the airplane mode indicator LED to userspace under the name asus-wireless::airplane, so it can be driven according to userspace's policy. Signed-off-by: João Paulo Rechi Vita <jprvita@endlessm.com> Reviewed-by: Corentin Chary <corentin.chary@gmail.com> Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig2
-rw-r--r--drivers/platform/x86/asus-wireless.c91
2 files changed, 92 insertions, 1 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 633fc5c191fc..b0b98e413343 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -604,6 +604,8 @@ config ASUS_WIRELESS
604 tristate "Asus Wireless Radio Control Driver" 604 tristate "Asus Wireless Radio Control Driver"
605 depends on ACPI 605 depends on ACPI
606 depends on INPUT 606 depends on INPUT
607 select NEW_LEDS
608 select LEDS_CLASS
607 ---help--- 609 ---help---
608 The Asus Wireless Radio Control handles the airplane mode hotkey 610 The Asus Wireless Radio Control handles the airplane mode hotkey
609 present on some Asus laptops. 611 present on some Asus laptops.
diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c
index 9ec721e26532..9f31bc1a47d0 100644
--- a/drivers/platform/x86/asus-wireless.c
+++ b/drivers/platform/x86/asus-wireless.c
@@ -15,11 +15,78 @@
15#include <linux/acpi.h> 15#include <linux/acpi.h>
16#include <linux/input.h> 16#include <linux/input.h>
17#include <linux/pci_ids.h> 17#include <linux/pci_ids.h>
18#include <linux/leds.h>
19
20#define ASUS_WIRELESS_LED_STATUS 0x2
21#define ASUS_WIRELESS_LED_OFF 0x4
22#define ASUS_WIRELESS_LED_ON 0x5
18 23
19struct asus_wireless_data { 24struct asus_wireless_data {
20 struct input_dev *idev; 25 struct input_dev *idev;
26 struct acpi_device *adev;
27 struct workqueue_struct *wq;
28 struct work_struct led_work;
29 struct led_classdev led;
30 int led_state;
21}; 31};
22 32
33static u64 asus_wireless_method(acpi_handle handle, const char *method,
34 int param)
35{
36 struct acpi_object_list p;
37 union acpi_object obj;
38 acpi_status s;
39 u64 ret;
40
41 acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
42 method, param);
43 obj.type = ACPI_TYPE_INTEGER;
44 obj.integer.value = param;
45 p.count = 1;
46 p.pointer = &obj;
47
48 s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
49 if (ACPI_FAILURE(s))
50 acpi_handle_err(handle,
51 "Failed to eval method %s, param %#x (%d)\n",
52 method, param, s);
53 acpi_handle_debug(handle, "%s returned %#x\n", method, (uint) ret);
54 return ret;
55}
56
57static enum led_brightness led_state_get(struct led_classdev *led)
58{
59 struct asus_wireless_data *data;
60 int s;
61
62 data = container_of(led, struct asus_wireless_data, led);
63 s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
64 ASUS_WIRELESS_LED_STATUS);
65 if (s == ASUS_WIRELESS_LED_ON)
66 return LED_FULL;
67 return LED_OFF;
68}
69
70static void led_state_update(struct work_struct *work)
71{
72 struct asus_wireless_data *data;
73
74 data = container_of(work, struct asus_wireless_data, led_work);
75 asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
76 data->led_state);
77}
78
79static void led_state_set(struct led_classdev *led,
80 enum led_brightness value)
81{
82 struct asus_wireless_data *data;
83
84 data = container_of(led, struct asus_wireless_data, led);
85 data->led_state = value == LED_OFF ? ASUS_WIRELESS_LED_OFF :
86 ASUS_WIRELESS_LED_ON;
87 queue_work(data->wq, &data->led_work);
88}
89
23static void asus_wireless_notify(struct acpi_device *adev, u32 event) 90static void asus_wireless_notify(struct acpi_device *adev, u32 event)
24{ 91{
25 struct asus_wireless_data *data = acpi_driver_data(adev); 92 struct asus_wireless_data *data = acpi_driver_data(adev);
@@ -37,6 +104,7 @@ static void asus_wireless_notify(struct acpi_device *adev, u32 event)
37static int asus_wireless_add(struct acpi_device *adev) 104static int asus_wireless_add(struct acpi_device *adev)
38{ 105{
39 struct asus_wireless_data *data; 106 struct asus_wireless_data *data;
107 int err;
40 108
41 data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); 109 data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
42 if (!data) 110 if (!data)
@@ -52,11 +120,32 @@ static int asus_wireless_add(struct acpi_device *adev)
52 data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK; 120 data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
53 set_bit(EV_KEY, data->idev->evbit); 121 set_bit(EV_KEY, data->idev->evbit);
54 set_bit(KEY_RFKILL, data->idev->keybit); 122 set_bit(KEY_RFKILL, data->idev->keybit);
55 return input_register_device(data->idev); 123 err = input_register_device(data->idev);
124 if (err)
125 return err;
126
127 data->adev = adev;
128 data->wq = create_singlethread_workqueue("asus_wireless_workqueue");
129 if (!data->wq)
130 return -ENOMEM;
131 INIT_WORK(&data->led_work, led_state_update);
132 data->led.name = "asus-wireless::airplane";
133 data->led.brightness_set = led_state_set;
134 data->led.brightness_get = led_state_get;
135 data->led.flags = LED_CORE_SUSPENDRESUME;
136 data->led.max_brightness = 1;
137 err = devm_led_classdev_register(&adev->dev, &data->led);
138 if (err)
139 destroy_workqueue(data->wq);
140 return err;
56} 141}
57 142
58static int asus_wireless_remove(struct acpi_device *adev) 143static int asus_wireless_remove(struct acpi_device *adev)
59{ 144{
145 struct asus_wireless_data *data = acpi_driver_data(adev);
146
147 if (data->wq)
148 destroy_workqueue(data->wq);
60 return 0; 149 return 0;
61} 150}
62 151