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 acpi_status
asus_wireless_method(acpi_handle handle
, const char *method
,
58 struct acpi_object_list p
;
59 union acpi_object obj
;
62 acpi_handle_debug(handle
, "Evaluating method %s, parameter %#x\n",
64 obj
.type
= ACPI_TYPE_INTEGER
;
65 obj
.integer
.value
= param
;
69 s
= acpi_evaluate_integer(handle
, (acpi_string
) method
, &p
, ret
);
71 acpi_handle_err(handle
,
72 "Failed to eval method %s, param %#x (%d)\n",
75 acpi_handle_debug(handle
, "%s returned %#llx\n", method
, *ret
);
80 static enum led_brightness
led_state_get(struct led_classdev
*led
)
82 struct asus_wireless_data
*data
;
86 data
= container_of(led
, struct asus_wireless_data
, led
);
87 s
= asus_wireless_method(acpi_device_handle(data
->adev
), "HSWC",
88 data
->hswc_params
->status
, &ret
);
89 if (ACPI_SUCCESS(s
) && ret
== data
->hswc_params
->on
)
94 static void led_state_update(struct work_struct
*work
)
96 struct asus_wireless_data
*data
;
99 data
= container_of(work
, struct asus_wireless_data
, led_work
);
100 asus_wireless_method(acpi_device_handle(data
->adev
), "HSWC",
101 data
->led_state
, &ret
);
104 static void led_state_set(struct led_classdev
*led
, enum led_brightness value
)
106 struct asus_wireless_data
*data
;
108 data
= container_of(led
, struct asus_wireless_data
, led
);
109 data
->led_state
= value
== LED_OFF
? data
->hswc_params
->off
:
110 data
->hswc_params
->on
;
111 queue_work(data
->wq
, &data
->led_work
);
114 static void asus_wireless_notify(struct acpi_device
*adev
, u32 event
)
116 struct asus_wireless_data
*data
= acpi_driver_data(adev
);
118 dev_dbg(&adev
->dev
, "event=%#x\n", event
);
120 dev_notice(&adev
->dev
, "Unknown ASHS event: %#x\n", event
);
123 input_report_key(data
->idev
, KEY_RFKILL
, 1);
124 input_sync(data
->idev
);
125 input_report_key(data
->idev
, KEY_RFKILL
, 0);
126 input_sync(data
->idev
);
129 static int asus_wireless_add(struct acpi_device
*adev
)
131 struct asus_wireless_data
*data
;
132 const struct acpi_device_id
*id
;
135 data
= devm_kzalloc(&adev
->dev
, sizeof(*data
), GFP_KERNEL
);
138 adev
->driver_data
= data
;
141 data
->idev
= devm_input_allocate_device(&adev
->dev
);
144 data
->idev
->name
= "Asus Wireless Radio Control";
145 data
->idev
->phys
= "asus-wireless/input0";
146 data
->idev
->id
.bustype
= BUS_HOST
;
147 data
->idev
->id
.vendor
= PCI_VENDOR_ID_ASUSTEK
;
148 set_bit(EV_KEY
, data
->idev
->evbit
);
149 set_bit(KEY_RFKILL
, data
->idev
->keybit
);
150 err
= input_register_device(data
->idev
);
154 for (id
= device_ids
; id
->id
[0]; id
++) {
155 if (!strcmp((char *) id
->id
, acpi_device_hid(adev
))) {
157 (const struct hswc_params
*)id
->driver_data
;
161 if (!data
->hswc_params
)
164 data
->wq
= create_singlethread_workqueue("asus_wireless_workqueue");
167 INIT_WORK(&data
->led_work
, led_state_update
);
168 data
->led
.name
= "asus-wireless::airplane";
169 data
->led
.brightness_set
= led_state_set
;
170 data
->led
.brightness_get
= led_state_get
;
171 data
->led
.flags
= LED_CORE_SUSPENDRESUME
;
172 data
->led
.max_brightness
= 1;
173 data
->led
.default_trigger
= "rfkill-none";
174 err
= devm_led_classdev_register(&adev
->dev
, &data
->led
);
176 destroy_workqueue(data
->wq
);
181 static int asus_wireless_remove(struct acpi_device
*adev
)
183 struct asus_wireless_data
*data
= acpi_driver_data(adev
);
186 devm_led_classdev_unregister(&adev
->dev
, &data
->led
);
187 destroy_workqueue(data
->wq
);
192 static struct acpi_driver asus_wireless_driver
= {
193 .name
= "Asus Wireless Radio Control Driver",
197 .add
= asus_wireless_add
,
198 .remove
= asus_wireless_remove
,
199 .notify
= asus_wireless_notify
,
202 module_acpi_driver(asus_wireless_driver
);
204 MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
205 MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
206 MODULE_LICENSE("GPL");