2 * ideapad_acpi.c - Lenovo IdeaPad ACPI Extras
4 * Copyright © 2010 Intel Corporation
5 * Copyright © 2010 David Woodhouse <dwmw2@infradead.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 #include <linux/kernel.h>
24 #include <linux/module.h>
25 #include <linux/init.h>
26 #include <linux/types.h>
27 #include <acpi/acpi_bus.h>
28 #include <acpi/acpi_drivers.h>
29 #include <linux/rfkill.h>
31 #define IDEAPAD_DEV_CAMERA 0
32 #define IDEAPAD_DEV_WLAN 1
33 #define IDEAPAD_DEV_BLUETOOTH 2
34 #define IDEAPAD_DEV_3G 3
35 #define IDEAPAD_DEV_KILLSW 4
37 static struct rfkill *ideapad_rfkill[5];
39 static const char *ideapad_rfk_names[] = {
40 "ideapad_camera", "ideapad_wlan", "ideapad_bluetooth", "ideapad_3g", "ideapad_rfkill"
42 static const int ideapad_rfk_types[] = {
43 0, RFKILL_TYPE_WLAN, RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_WWAN, RFKILL_TYPE_WLAN
46 static int ideapad_dev_exists(int device)
49 union acpi_object in_param;
50 struct acpi_object_list input = { 1, &in_param };
51 struct acpi_buffer output;
52 union acpi_object out_obj;
54 output.length = sizeof(out_obj);
55 output.pointer = &out_obj;
57 in_param.type = ACPI_TYPE_INTEGER;
58 in_param.integer.value = device + 1;
60 status = acpi_evaluate_object(NULL, "\\_SB_.DECN", &input, &output);
61 if (ACPI_FAILURE(status)) {
62 printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method failed %d. Is this an IdeaPAD?\n", status);
65 if (out_obj.type != ACPI_TYPE_INTEGER) {
66 printk(KERN_WARNING "IdeaPAD \\_SB_.DECN method returned unexpected type\n");
69 return out_obj.integer.value;
72 static int ideapad_dev_get_state(int device)
75 union acpi_object in_param;
76 struct acpi_object_list input = { 1, &in_param };
77 struct acpi_buffer output;
78 union acpi_object out_obj;
80 output.length = sizeof(out_obj);
81 output.pointer = &out_obj;
83 in_param.type = ACPI_TYPE_INTEGER;
84 in_param.integer.value = device + 1;
86 status = acpi_evaluate_object(NULL, "\\_SB_.GECN", &input, &output);
87 if (ACPI_FAILURE(status)) {
88 printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method failed %d\n", status);
91 if (out_obj.type != ACPI_TYPE_INTEGER) {
92 printk(KERN_WARNING "IdeaPAD \\_SB_.GECN method returned unexpected type\n");
95 return out_obj.integer.value;
98 static int ideapad_dev_set_state(int device, int state)
101 union acpi_object in_params[2];
102 struct acpi_object_list input = { 2, in_params };
104 in_params[0].type = ACPI_TYPE_INTEGER;
105 in_params[0].integer.value = device + 1;
106 in_params[1].type = ACPI_TYPE_INTEGER;
107 in_params[1].integer.value = state;
109 status = acpi_evaluate_object(NULL, "\\_SB_.SECN", &input, NULL);
110 if (ACPI_FAILURE(status)) {
111 printk(KERN_WARNING "IdeaPAD \\_SB_.SECN method failed %d\n", status);
116 static ssize_t show_ideapad_cam(struct device *dev,
117 struct device_attribute *attr,
120 int state = ideapad_dev_get_state(IDEAPAD_DEV_CAMERA);
124 return sprintf(buf, "%d\n", state);
127 static ssize_t store_ideapad_cam(struct device *dev,
128 struct device_attribute *attr,
129 const char *buf, size_t count)
135 if (sscanf(buf, "%i", &state) != 1)
137 ret = ideapad_dev_set_state(IDEAPAD_DEV_CAMERA, state);
143 static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
145 static int ideapad_rfk_set(void *data, bool blocked)
147 int device = (unsigned long)data;
149 if (device == IDEAPAD_DEV_KILLSW)
151 return ideapad_dev_set_state(device, !blocked);
154 static struct rfkill_ops ideapad_rfk_ops = {
155 .set_block = ideapad_rfk_set,
158 static void ideapad_sync_rfk_state(void)
160 int hw_blocked = !ideapad_dev_get_state(IDEAPAD_DEV_KILLSW);
163 rfkill_set_hw_state(ideapad_rfkill[IDEAPAD_DEV_KILLSW], hw_blocked);
164 for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
165 if (ideapad_rfkill[i])
166 rfkill_set_hw_state(ideapad_rfkill[i], hw_blocked);
170 for (i = IDEAPAD_DEV_WLAN; i < IDEAPAD_DEV_KILLSW; i++)
171 if (ideapad_rfkill[i])
172 rfkill_set_sw_state(ideapad_rfkill[i], !ideapad_dev_get_state(i));
175 static int ideapad_register_rfkill(struct acpi_device *device, int dev)
179 ideapad_rfkill[dev] = rfkill_alloc(ideapad_rfk_names[dev], &device->dev,
180 ideapad_rfk_types[dev], &ideapad_rfk_ops,
182 if (!ideapad_rfkill[dev])
185 ret = rfkill_register(ideapad_rfkill[dev]);
187 rfkill_destroy(ideapad_rfkill[dev]);
193 static void ideapad_unregister_rfkill(int dev)
195 if (!ideapad_rfkill[dev])
198 rfkill_unregister(ideapad_rfkill[dev]);
199 rfkill_destroy(ideapad_rfkill[dev]);
202 static const struct acpi_device_id ideapad_device_ids[] = {
206 MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
208 static int ideapad_acpi_add(struct acpi_device *device)
213 for (i = IDEAPAD_DEV_CAMERA; i < IDEAPAD_DEV_KILLSW; i++) {
214 devs_present[i] = ideapad_dev_exists(i);
215 if (devs_present[i] < 0)
216 return devs_present[i];
219 /* The hardware switch is always present */
220 devs_present[IDEAPAD_DEV_KILLSW] = 1;
222 if (devs_present[IDEAPAD_DEV_CAMERA]) {
223 int ret = device_create_file(&device->dev, &dev_attr_camera_power);
228 for (i = IDEAPAD_DEV_WLAN; i <= IDEAPAD_DEV_KILLSW; i++) {
229 if (!devs_present[i])
232 ideapad_register_rfkill(device, i);
234 ideapad_sync_rfk_state();
238 static int ideapad_acpi_remove(struct acpi_device *device, int type)
241 device_remove_file(&device->dev, &dev_attr_camera_power);
242 for (i = 0; i < 5; i++)
243 ideapad_unregister_rfkill(i);
247 static void ideapad_acpi_notify(struct acpi_device *device, u32 event)
249 ideapad_sync_rfk_state();
252 static struct acpi_driver ideapad_acpi_driver = {
253 .name = "ideapad_acpi",
255 .ids = ideapad_device_ids,
256 .ops.add = ideapad_acpi_add,
257 .ops.remove = ideapad_acpi_remove,
258 .ops.notify = ideapad_acpi_notify,
259 .owner = THIS_MODULE,
263 static int __init ideapad_acpi_module_init(void)
265 acpi_bus_register_driver(&ideapad_acpi_driver);
271 static void __exit ideapad_acpi_module_exit(void)
273 acpi_bus_unregister_driver(&ideapad_acpi_driver);
277 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
278 MODULE_DESCRIPTION("IdeaPad ACPI Extras");
279 MODULE_LICENSE("GPL");
281 module_init(ideapad_acpi_module_init);
282 module_exit(ideapad_acpi_module_exit);