2 * Asus Wireless Radio Control Driver
4 * Copyright (C) 2015-2016 Endless Mobile, Inc.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/types.h>
15 #include <linux/acpi.h>
16 #include <linux/input.h>
17 #include <linux/pci_ids.h>
18 #include <linux/leds.h>
26 struct asus_wireless_data
{
27 struct input_dev
*idev
;
28 struct acpi_device
*adev
;
29 const struct hswc_params
*hswc_params
;
30 struct workqueue_struct
*wq
;
31 struct work_struct led_work
;
32 struct led_classdev led
;
36 static const struct hswc_params atk4001_id_params
= {
42 static const struct hswc_params atk4002_id_params
= {
48 static const struct acpi_device_id device_ids
[] = {
49 {"ATK4001", (kernel_ulong_t
)&atk4001_id_params
},
50 {"ATK4002", (kernel_ulong_t
)&atk4002_id_params
},
53 MODULE_DEVICE_TABLE(acpi
, device_ids
);
55 static u64
asus_wireless_method(acpi_handle handle
, const char *method
,
58 struct acpi_object_list p
;
59 union acpi_object obj
;
63 acpi_handle_debug(handle
, "Evaluating method %s, parameter %#x\n",
65 obj
.type
= ACPI_TYPE_INTEGER
;
66 obj
.integer
.value
= param
;
70 s
= acpi_evaluate_integer(handle
, (acpi_string
) method
, &p
, &ret
);
72 acpi_handle_err(handle
,
73 "Failed to eval method %s, param %#x (%d)\n",
75 acpi_handle_debug(handle
, "%s returned %#x\n", method
, (uint
) ret
);
79 static enum led_brightness
led_state_get(struct led_classdev
*led
)
81 struct asus_wireless_data
*data
;
84 data
= container_of(led
, struct asus_wireless_data
, led
);
85 s
= asus_wireless_method(acpi_device_handle(data
->adev
), "HSWC",
86 data
->hswc_params
->status
);
87 if (s
== data
->hswc_params
->on
)
92 static void led_state_update(struct work_struct
*work
)
94 struct asus_wireless_data
*data
;
96 data
= container_of(work
, struct asus_wireless_data
, led_work
);
97 asus_wireless_method(acpi_device_handle(data
->adev
), "HSWC",
101 static void led_state_set(struct led_classdev
*led
, enum led_brightness value
)
103 struct asus_wireless_data
*data
;
105 data
= container_of(led
, struct asus_wireless_data
, led
);
106 data
->led_state
= value
== LED_OFF
? data
->hswc_params
->off
:
107 data
->hswc_params
->on
;
108 queue_work(data
->wq
, &data
->led_work
);
111 static void asus_wireless_notify(struct acpi_device
*adev
, u32 event
)
113 struct asus_wireless_data
*data
= acpi_driver_data(adev
);
115 dev_dbg(&adev
->dev
, "event=%#x\n", event
);
117 dev_notice(&adev
->dev
, "Unknown ASHS event: %#x\n", event
);
120 input_report_key(data
->idev
, KEY_RFKILL
, 1);
121 input_sync(data
->idev
);
122 input_report_key(data
->idev
, KEY_RFKILL
, 0);
123 input_sync(data
->idev
);
126 static int asus_wireless_add(struct acpi_device
*adev
)
128 struct asus_wireless_data
*data
;
129 const struct acpi_device_id
*id
;
132 data
= devm_kzalloc(&adev
->dev
, sizeof(*data
), GFP_KERNEL
);
135 adev
->driver_data
= data
;
138 data
->idev
= devm_input_allocate_device(&adev
->dev
);
141 data
->idev
->name
= "Asus Wireless Radio Control";
142 data
->idev
->phys
= "asus-wireless/input0";
143 data
->idev
->id
.bustype
= BUS_HOST
;
144 data
->idev
->id
.vendor
= PCI_VENDOR_ID_ASUSTEK
;
145 set_bit(EV_KEY
, data
->idev
->evbit
);
146 set_bit(KEY_RFKILL
, data
->idev
->keybit
);
147 err
= input_register_device(data
->idev
);
151 for (id
= device_ids
; id
->id
[0]; id
++) {
152 if (!strcmp((char *) id
->id
, acpi_device_hid(adev
))) {
154 (const struct hswc_params
*)id
->driver_data
;
158 if (!data
->hswc_params
)
161 data
->wq
= create_singlethread_workqueue("asus_wireless_workqueue");
164 INIT_WORK(&data
->led_work
, led_state_update
);
165 data
->led
.name
= "asus-wireless::airplane";
166 data
->led
.brightness_set
= led_state_set
;
167 data
->led
.brightness_get
= led_state_get
;
168 data
->led
.flags
= LED_CORE_SUSPENDRESUME
;
169 data
->led
.max_brightness
= 1;
170 err
= devm_led_classdev_register(&adev
->dev
, &data
->led
);
172 destroy_workqueue(data
->wq
);
177 static int asus_wireless_remove(struct acpi_device
*adev
)
179 struct asus_wireless_data
*data
= acpi_driver_data(adev
);
182 devm_led_classdev_unregister(&adev
->dev
, &data
->led
);
183 destroy_workqueue(data
->wq
);
188 static struct acpi_driver asus_wireless_driver
= {
189 .name
= "Asus Wireless Radio Control Driver",
193 .add
= asus_wireless_add
,
194 .remove
= asus_wireless_remove
,
195 .notify
= asus_wireless_notify
,
198 module_acpi_driver(asus_wireless_driver
);
200 MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
201 MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
202 MODULE_LICENSE("GPL");