Linux Audio

Check our new training course

Loading...
v4.6
 1/*
 2 * Asus Wireless Radio Control Driver
 3 *
 4 * Copyright (C) 2015-2016 Endless Mobile, Inc.
 5 *
 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.
 9 */
10
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
19struct asus_wireless_data {
20	struct input_dev *idev;
 
 
 
 
 
21};
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23static void asus_wireless_notify(struct acpi_device *adev, u32 event)
24{
25	struct asus_wireless_data *data = acpi_driver_data(adev);
26
27	dev_dbg(&adev->dev, "event=%#x\n", event);
28	if (event != 0x88) {
29		dev_notice(&adev->dev, "Unknown ASHS event: %#x\n", event);
30		return;
31	}
32	input_report_key(data->idev, KEY_RFKILL, 1);
33	input_report_key(data->idev, KEY_RFKILL, 0);
34	input_sync(data->idev);
35}
36
37static int asus_wireless_add(struct acpi_device *adev)
38{
39	struct asus_wireless_data *data;
 
40
41	data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
42	if (!data)
43		return -ENOMEM;
44	adev->driver_data = data;
45
46	data->idev = devm_input_allocate_device(&adev->dev);
47	if (!data->idev)
48		return -ENOMEM;
49	data->idev->name = "Asus Wireless Radio Control";
50	data->idev->phys = "asus-wireless/input0";
51	data->idev->id.bustype = BUS_HOST;
52	data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
53	set_bit(EV_KEY, data->idev->evbit);
54	set_bit(KEY_RFKILL, data->idev->keybit);
55	return input_register_device(data->idev);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56}
57
58static int asus_wireless_remove(struct acpi_device *adev)
59{
 
 
 
 
60	return 0;
61}
62
63static const struct acpi_device_id device_ids[] = {
64	{"ATK4001", 0},
65	{"ATK4002", 0},
66	{"", 0},
67};
68MODULE_DEVICE_TABLE(acpi, device_ids);
69
70static struct acpi_driver asus_wireless_driver = {
71	.name = "Asus Wireless Radio Control Driver",
72	.class = "hotkey",
73	.ids = device_ids,
74	.ops = {
75		.add = asus_wireless_add,
76		.remove = asus_wireless_remove,
77		.notify = asus_wireless_notify,
78	},
79};
80module_acpi_driver(asus_wireless_driver);
81
82MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
83MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
84MODULE_LICENSE("GPL");
v4.10.11
  1/*
  2 * Asus Wireless Radio Control Driver
  3 *
  4 * Copyright (C) 2015-2016 Endless Mobile, Inc.
  5 *
  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.
  9 */
 10
 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>
 19
 20#define ASUS_WIRELESS_LED_STATUS 0x2
 21#define ASUS_WIRELESS_LED_OFF 0x4
 22#define ASUS_WIRELESS_LED_ON 0x5
 23
 24struct asus_wireless_data {
 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;
 31};
 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
 90static void asus_wireless_notify(struct acpi_device *adev, u32 event)
 91{
 92	struct asus_wireless_data *data = acpi_driver_data(adev);
 93
 94	dev_dbg(&adev->dev, "event=%#x\n", event);
 95	if (event != 0x88) {
 96		dev_notice(&adev->dev, "Unknown ASHS event: %#x\n", event);
 97		return;
 98	}
 99	input_report_key(data->idev, KEY_RFKILL, 1);
100	input_report_key(data->idev, KEY_RFKILL, 0);
101	input_sync(data->idev);
102}
103
104static int asus_wireless_add(struct acpi_device *adev)
105{
106	struct asus_wireless_data *data;
107	int err;
108
109	data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL);
110	if (!data)
111		return -ENOMEM;
112	adev->driver_data = data;
113
114	data->idev = devm_input_allocate_device(&adev->dev);
115	if (!data->idev)
116		return -ENOMEM;
117	data->idev->name = "Asus Wireless Radio Control";
118	data->idev->phys = "asus-wireless/input0";
119	data->idev->id.bustype = BUS_HOST;
120	data->idev->id.vendor = PCI_VENDOR_ID_ASUSTEK;
121	set_bit(EV_KEY, data->idev->evbit);
122	set_bit(KEY_RFKILL, data->idev->keybit);
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;
141}
142
143static int asus_wireless_remove(struct acpi_device *adev)
144{
145	struct asus_wireless_data *data = acpi_driver_data(adev);
146
147	if (data->wq)
148		destroy_workqueue(data->wq);
149	return 0;
150}
151
152static const struct acpi_device_id device_ids[] = {
153	{"ATK4001", 0},
154	{"ATK4002", 0},
155	{"", 0},
156};
157MODULE_DEVICE_TABLE(acpi, device_ids);
158
159static struct acpi_driver asus_wireless_driver = {
160	.name = "Asus Wireless Radio Control Driver",
161	.class = "hotkey",
162	.ids = device_ids,
163	.ops = {
164		.add = asus_wireless_add,
165		.remove = asus_wireless_remove,
166		.notify = asus_wireless_notify,
167	},
168};
169module_acpi_driver(asus_wireless_driver);
170
171MODULE_DESCRIPTION("Asus Wireless Radio Control Driver");
172MODULE_AUTHOR("João Paulo Rechi Vita <jprvita@gmail.com>");
173MODULE_LICENSE("GPL");