Commit 5b799d4fb787bb94f1068352220ab033ac7969f8
Committed by
Matthew Garrett
1 parent
f017fbe799
Exists in
master
and in
4 other branches
asus-wmi: move generic code to asus-wmi
New Asus notebooks are using a WMI device similar to the one used in Eee PCs. Since we don't want to load a module named eeepc-laptop on Asus Notebooks, start by copying all the code to asus-wmi.c. Signed-off-by: Corentin Chary <corentincj@iksaif.net> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Showing 3 changed files with 1455 additions and 1455 deletions Side-by-side Diff
drivers/platform/x86/Makefile
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | # |
5 | 5 | obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o |
6 | 6 | obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o |
7 | -obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o | |
7 | +obj-$(CONFIG_EEEPC_WMI) += asus-wmi.o eeepc-wmi.o | |
8 | 8 | obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o |
9 | 9 | obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o |
10 | 10 | obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o |
drivers/platform/x86/asus-wmi.c
Changes suppressed. Click to show
1 | +/* | |
2 | + * Eee PC WMI hotkey driver | |
3 | + * | |
4 | + * Copyright(C) 2010 Intel Corporation. | |
5 | + * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> | |
6 | + * | |
7 | + * Portions based on wistron_btns.c: | |
8 | + * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | |
9 | + * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | |
10 | + * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | |
11 | + * | |
12 | + * This program is free software; you can redistribute it and/or modify | |
13 | + * it under the terms of the GNU General Public License as published by | |
14 | + * the Free Software Foundation; either version 2 of the License, or | |
15 | + * (at your option) any later version. | |
16 | + * | |
17 | + * This program is distributed in the hope that it will be useful, | |
18 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | + * GNU General Public License for more details. | |
21 | + * | |
22 | + * You should have received a copy of the GNU General Public License | |
23 | + * along with this program; if not, write to the Free Software | |
24 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
25 | + */ | |
26 | + | |
27 | +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
28 | + | |
29 | +#include <linux/kernel.h> | |
30 | +#include <linux/module.h> | |
31 | +#include <linux/init.h> | |
32 | +#include <linux/types.h> | |
33 | +#include <linux/slab.h> | |
34 | +#include <linux/input.h> | |
35 | +#include <linux/input/sparse-keymap.h> | |
36 | +#include <linux/fb.h> | |
37 | +#include <linux/backlight.h> | |
38 | +#include <linux/leds.h> | |
39 | +#include <linux/rfkill.h> | |
40 | +#include <linux/pci.h> | |
41 | +#include <linux/pci_hotplug.h> | |
42 | +#include <linux/debugfs.h> | |
43 | +#include <linux/seq_file.h> | |
44 | +#include <linux/platform_device.h> | |
45 | +#include <linux/dmi.h> | |
46 | +#include <acpi/acpi_bus.h> | |
47 | +#include <acpi/acpi_drivers.h> | |
48 | + | |
49 | +#define EEEPC_WMI_FILE "eeepc-wmi" | |
50 | + | |
51 | +MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | |
52 | +MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | |
53 | +MODULE_LICENSE("GPL"); | |
54 | + | |
55 | +#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ | |
56 | + | |
57 | +#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | |
58 | +#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | |
59 | + | |
60 | +MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); | |
61 | +MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | |
62 | + | |
63 | +#define NOTIFY_BRNUP_MIN 0x11 | |
64 | +#define NOTIFY_BRNUP_MAX 0x1f | |
65 | +#define NOTIFY_BRNDOWN_MIN 0x20 | |
66 | +#define NOTIFY_BRNDOWN_MAX 0x2e | |
67 | + | |
68 | +/* WMI Methods */ | |
69 | +#define EEEPC_WMI_METHODID_DSTS 0x53544344 | |
70 | +#define EEEPC_WMI_METHODID_DEVS 0x53564544 | |
71 | +#define EEEPC_WMI_METHODID_CFVS 0x53564643 | |
72 | + | |
73 | +/* Wireless */ | |
74 | +#define EEEPC_WMI_DEVID_WLAN 0x00010011 | |
75 | +#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 | |
76 | +#define EEEPC_WMI_DEVID_WIMAX 0x00010017 | |
77 | +#define EEEPC_WMI_DEVID_WWAN3G 0x00010019 | |
78 | + | |
79 | +/* Backlight and Brightness */ | |
80 | +#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 | |
81 | +#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 | |
82 | + | |
83 | +/* Misc */ | |
84 | +#define EEEPC_WMI_DEVID_CAMERA 0x00060013 | |
85 | + | |
86 | +/* Storage */ | |
87 | +#define EEEPC_WMI_DEVID_CARDREADER 0x00080013 | |
88 | + | |
89 | +/* Input */ | |
90 | +#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 | |
91 | +#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 | |
92 | + | |
93 | +/* DSTS masks */ | |
94 | +#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 | |
95 | +#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 | |
96 | +#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF | |
97 | +#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 | |
98 | + | |
99 | +static bool hotplug_wireless; | |
100 | + | |
101 | +module_param(hotplug_wireless, bool, 0444); | |
102 | +MODULE_PARM_DESC(hotplug_wireless, | |
103 | + "Enable hotplug for wireless device. " | |
104 | + "If your laptop needs that, please report to " | |
105 | + "acpi4asus-user@lists.sourceforge.net."); | |
106 | + | |
107 | +static const struct key_entry eeepc_wmi_keymap[] = { | |
108 | + /* Sleep already handled via generic ACPI code */ | |
109 | + { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, | |
110 | + { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, | |
111 | + { KE_KEY, 0x30, { KEY_VOLUMEUP } }, | |
112 | + { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, | |
113 | + { KE_KEY, 0x32, { KEY_MUTE } }, | |
114 | + { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ | |
115 | + { KE_KEY, 0x5d, { KEY_WLAN } }, | |
116 | + { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ | |
117 | + { KE_KEY, 0x82, { KEY_CAMERA } }, | |
118 | + { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, | |
119 | + { KE_KEY, 0x88, { KEY_WLAN } }, | |
120 | + { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, | |
121 | + { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ | |
122 | + { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ | |
123 | + { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, | |
124 | + { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, | |
125 | + { KE_KEY, 0xec, { KEY_CAMERA_UP } }, | |
126 | + { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, | |
127 | + { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, | |
128 | + { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, | |
129 | + { KE_END, 0}, | |
130 | +}; | |
131 | + | |
132 | +struct bios_args { | |
133 | + u32 dev_id; | |
134 | + u32 ctrl_param; | |
135 | +}; | |
136 | + | |
137 | +/* | |
138 | + * eeepc-wmi/ - debugfs root directory | |
139 | + * dev_id - current dev_id | |
140 | + * ctrl_param - current ctrl_param | |
141 | + * devs - call DEVS(dev_id, ctrl_param) and print result | |
142 | + * dsts - call DSTS(dev_id) and print result | |
143 | + */ | |
144 | +struct eeepc_wmi_debug { | |
145 | + struct dentry *root; | |
146 | + u32 dev_id; | |
147 | + u32 ctrl_param; | |
148 | +}; | |
149 | + | |
150 | +struct eeepc_wmi { | |
151 | + bool hotplug_wireless; | |
152 | + | |
153 | + struct input_dev *inputdev; | |
154 | + struct backlight_device *backlight_device; | |
155 | + struct platform_device *platform_device; | |
156 | + | |
157 | + struct led_classdev tpd_led; | |
158 | + int tpd_led_wk; | |
159 | + struct workqueue_struct *led_workqueue; | |
160 | + struct work_struct tpd_led_work; | |
161 | + | |
162 | + struct rfkill *wlan_rfkill; | |
163 | + struct rfkill *bluetooth_rfkill; | |
164 | + struct rfkill *wimax_rfkill; | |
165 | + struct rfkill *wwan3g_rfkill; | |
166 | + | |
167 | + struct hotplug_slot *hotplug_slot; | |
168 | + struct mutex hotplug_lock; | |
169 | + struct mutex wmi_lock; | |
170 | + struct workqueue_struct *hotplug_workqueue; | |
171 | + struct work_struct hotplug_work; | |
172 | + | |
173 | + struct eeepc_wmi_debug debug; | |
174 | +}; | |
175 | + | |
176 | +static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | |
177 | +{ | |
178 | + int err; | |
179 | + | |
180 | + eeepc->inputdev = input_allocate_device(); | |
181 | + if (!eeepc->inputdev) | |
182 | + return -ENOMEM; | |
183 | + | |
184 | + eeepc->inputdev->name = "Eee PC WMI hotkeys"; | |
185 | + eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | |
186 | + eeepc->inputdev->id.bustype = BUS_HOST; | |
187 | + eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; | |
188 | + | |
189 | + err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | |
190 | + if (err) | |
191 | + goto err_free_dev; | |
192 | + | |
193 | + err = input_register_device(eeepc->inputdev); | |
194 | + if (err) | |
195 | + goto err_free_keymap; | |
196 | + | |
197 | + return 0; | |
198 | + | |
199 | +err_free_keymap: | |
200 | + sparse_keymap_free(eeepc->inputdev); | |
201 | +err_free_dev: | |
202 | + input_free_device(eeepc->inputdev); | |
203 | + return err; | |
204 | +} | |
205 | + | |
206 | +static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | |
207 | +{ | |
208 | + if (eeepc->inputdev) { | |
209 | + sparse_keymap_free(eeepc->inputdev); | |
210 | + input_unregister_device(eeepc->inputdev); | |
211 | + } | |
212 | + | |
213 | + eeepc->inputdev = NULL; | |
214 | +} | |
215 | + | |
216 | +static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) | |
217 | +{ | |
218 | + struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | |
219 | + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | |
220 | + union acpi_object *obj; | |
221 | + acpi_status status; | |
222 | + u32 tmp; | |
223 | + | |
224 | + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | |
225 | + 1, EEEPC_WMI_METHODID_DSTS, | |
226 | + &input, &output); | |
227 | + | |
228 | + if (ACPI_FAILURE(status)) | |
229 | + return status; | |
230 | + | |
231 | + obj = (union acpi_object *)output.pointer; | |
232 | + if (obj && obj->type == ACPI_TYPE_INTEGER) | |
233 | + tmp = (u32)obj->integer.value; | |
234 | + else | |
235 | + tmp = 0; | |
236 | + | |
237 | + if (retval) | |
238 | + *retval = tmp; | |
239 | + | |
240 | + kfree(obj); | |
241 | + | |
242 | + return status; | |
243 | + | |
244 | +} | |
245 | + | |
246 | +static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, | |
247 | + u32 *retval) | |
248 | +{ | |
249 | + struct bios_args args = { | |
250 | + .dev_id = dev_id, | |
251 | + .ctrl_param = ctrl_param, | |
252 | + }; | |
253 | + struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | |
254 | + acpi_status status; | |
255 | + | |
256 | + if (!retval) { | |
257 | + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | |
258 | + EEEPC_WMI_METHODID_DEVS, | |
259 | + &input, NULL); | |
260 | + } else { | |
261 | + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | |
262 | + union acpi_object *obj; | |
263 | + u32 tmp; | |
264 | + | |
265 | + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | |
266 | + EEEPC_WMI_METHODID_DEVS, | |
267 | + &input, &output); | |
268 | + | |
269 | + if (ACPI_FAILURE(status)) | |
270 | + return status; | |
271 | + | |
272 | + obj = (union acpi_object *)output.pointer; | |
273 | + if (obj && obj->type == ACPI_TYPE_INTEGER) | |
274 | + tmp = (u32)obj->integer.value; | |
275 | + else | |
276 | + tmp = 0; | |
277 | + | |
278 | + *retval = tmp; | |
279 | + | |
280 | + kfree(obj); | |
281 | + } | |
282 | + | |
283 | + return status; | |
284 | +} | |
285 | + | |
286 | +/* Helper for special devices with magic return codes */ | |
287 | +static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask) | |
288 | +{ | |
289 | + u32 retval = 0; | |
290 | + acpi_status status; | |
291 | + | |
292 | + status = eeepc_wmi_get_devstate(dev_id, &retval); | |
293 | + | |
294 | + if (ACPI_FAILURE(status)) | |
295 | + return -EINVAL; | |
296 | + | |
297 | + if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) | |
298 | + return -ENODEV; | |
299 | + | |
300 | + return retval & mask; | |
301 | +} | |
302 | + | |
303 | +static int eeepc_wmi_get_devstate_simple(u32 dev_id) | |
304 | +{ | |
305 | + return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT); | |
306 | +} | |
307 | + | |
308 | +/* | |
309 | + * LEDs | |
310 | + */ | |
311 | +/* | |
312 | + * These functions actually update the LED's, and are called from a | |
313 | + * workqueue. By doing this as separate work rather than when the LED | |
314 | + * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a | |
315 | + * potentially bad time, such as a timer interrupt. | |
316 | + */ | |
317 | +static void tpd_led_update(struct work_struct *work) | |
318 | +{ | |
319 | + int ctrl_param; | |
320 | + struct eeepc_wmi *eeepc; | |
321 | + | |
322 | + eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); | |
323 | + | |
324 | + ctrl_param = eeepc->tpd_led_wk; | |
325 | + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); | |
326 | +} | |
327 | + | |
328 | +static void tpd_led_set(struct led_classdev *led_cdev, | |
329 | + enum led_brightness value) | |
330 | +{ | |
331 | + struct eeepc_wmi *eeepc; | |
332 | + | |
333 | + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | |
334 | + | |
335 | + eeepc->tpd_led_wk = !!value; | |
336 | + queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | |
337 | +} | |
338 | + | |
339 | +static int read_tpd_led_state(struct eeepc_wmi *eeepc) | |
340 | +{ | |
341 | + return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED); | |
342 | +} | |
343 | + | |
344 | +static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | |
345 | +{ | |
346 | + struct eeepc_wmi *eeepc; | |
347 | + | |
348 | + eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | |
349 | + | |
350 | + return read_tpd_led_state(eeepc); | |
351 | +} | |
352 | + | |
353 | +static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) | |
354 | +{ | |
355 | + int rv; | |
356 | + | |
357 | + if (read_tpd_led_state(eeepc) < 0) | |
358 | + return 0; | |
359 | + | |
360 | + eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); | |
361 | + if (!eeepc->led_workqueue) | |
362 | + return -ENOMEM; | |
363 | + INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); | |
364 | + | |
365 | + eeepc->tpd_led.name = "eeepc::touchpad"; | |
366 | + eeepc->tpd_led.brightness_set = tpd_led_set; | |
367 | + eeepc->tpd_led.brightness_get = tpd_led_get; | |
368 | + eeepc->tpd_led.max_brightness = 1; | |
369 | + | |
370 | + rv = led_classdev_register(&eeepc->platform_device->dev, | |
371 | + &eeepc->tpd_led); | |
372 | + if (rv) { | |
373 | + destroy_workqueue(eeepc->led_workqueue); | |
374 | + return rv; | |
375 | + } | |
376 | + | |
377 | + return 0; | |
378 | +} | |
379 | + | |
380 | +static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) | |
381 | +{ | |
382 | + if (eeepc->tpd_led.dev) | |
383 | + led_classdev_unregister(&eeepc->tpd_led); | |
384 | + if (eeepc->led_workqueue) | |
385 | + destroy_workqueue(eeepc->led_workqueue); | |
386 | +} | |
387 | + | |
388 | +/* | |
389 | + * PCI hotplug (for wlan rfkill) | |
390 | + */ | |
391 | +static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) | |
392 | +{ | |
393 | + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
394 | + | |
395 | + if (result < 0) | |
396 | + return false; | |
397 | + return !result; | |
398 | +} | |
399 | + | |
400 | +static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) | |
401 | +{ | |
402 | + struct pci_dev *dev; | |
403 | + struct pci_bus *bus; | |
404 | + bool blocked; | |
405 | + bool absent; | |
406 | + u32 l; | |
407 | + | |
408 | + mutex_lock(&eeepc->wmi_lock); | |
409 | + blocked = eeepc_wlan_rfkill_blocked(eeepc); | |
410 | + mutex_unlock(&eeepc->wmi_lock); | |
411 | + | |
412 | + mutex_lock(&eeepc->hotplug_lock); | |
413 | + | |
414 | + if (eeepc->wlan_rfkill) | |
415 | + rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); | |
416 | + | |
417 | + if (eeepc->hotplug_slot) { | |
418 | + bus = pci_find_bus(0, 1); | |
419 | + if (!bus) { | |
420 | + pr_warning("Unable to find PCI bus 1?\n"); | |
421 | + goto out_unlock; | |
422 | + } | |
423 | + | |
424 | + if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { | |
425 | + pr_err("Unable to read PCI config space?\n"); | |
426 | + goto out_unlock; | |
427 | + } | |
428 | + absent = (l == 0xffffffff); | |
429 | + | |
430 | + if (blocked != absent) { | |
431 | + pr_warning("BIOS says wireless lan is %s, " | |
432 | + "but the pci device is %s\n", | |
433 | + blocked ? "blocked" : "unblocked", | |
434 | + absent ? "absent" : "present"); | |
435 | + pr_warning("skipped wireless hotplug as probably " | |
436 | + "inappropriate for this model\n"); | |
437 | + goto out_unlock; | |
438 | + } | |
439 | + | |
440 | + if (!blocked) { | |
441 | + dev = pci_get_slot(bus, 0); | |
442 | + if (dev) { | |
443 | + /* Device already present */ | |
444 | + pci_dev_put(dev); | |
445 | + goto out_unlock; | |
446 | + } | |
447 | + dev = pci_scan_single_device(bus, 0); | |
448 | + if (dev) { | |
449 | + pci_bus_assign_resources(bus); | |
450 | + if (pci_bus_add_device(dev)) | |
451 | + pr_err("Unable to hotplug wifi\n"); | |
452 | + } | |
453 | + } else { | |
454 | + dev = pci_get_slot(bus, 0); | |
455 | + if (dev) { | |
456 | + pci_remove_bus_device(dev); | |
457 | + pci_dev_put(dev); | |
458 | + } | |
459 | + } | |
460 | + } | |
461 | + | |
462 | +out_unlock: | |
463 | + mutex_unlock(&eeepc->hotplug_lock); | |
464 | +} | |
465 | + | |
466 | +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |
467 | +{ | |
468 | + struct eeepc_wmi *eeepc = data; | |
469 | + | |
470 | + if (event != ACPI_NOTIFY_BUS_CHECK) | |
471 | + return; | |
472 | + | |
473 | + /* | |
474 | + * We can't call directly eeepc_rfkill_hotplug because most | |
475 | + * of the time WMBC is still being executed and not reetrant. | |
476 | + * There is currently no way to tell ACPICA that we want this | |
477 | + * method to be serialized, we schedule a eeepc_rfkill_hotplug | |
478 | + * call later, in a safer context. | |
479 | + */ | |
480 | + queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work); | |
481 | +} | |
482 | + | |
483 | +static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, | |
484 | + char *node) | |
485 | +{ | |
486 | + acpi_status status; | |
487 | + acpi_handle handle; | |
488 | + | |
489 | + status = acpi_get_handle(NULL, node, &handle); | |
490 | + | |
491 | + if (ACPI_SUCCESS(status)) { | |
492 | + status = acpi_install_notify_handler(handle, | |
493 | + ACPI_SYSTEM_NOTIFY, | |
494 | + eeepc_rfkill_notify, | |
495 | + eeepc); | |
496 | + if (ACPI_FAILURE(status)) | |
497 | + pr_warning("Failed to register notify on %s\n", node); | |
498 | + } else | |
499 | + return -ENODEV; | |
500 | + | |
501 | + return 0; | |
502 | +} | |
503 | + | |
504 | +static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, | |
505 | + char *node) | |
506 | +{ | |
507 | + acpi_status status = AE_OK; | |
508 | + acpi_handle handle; | |
509 | + | |
510 | + status = acpi_get_handle(NULL, node, &handle); | |
511 | + | |
512 | + if (ACPI_SUCCESS(status)) { | |
513 | + status = acpi_remove_notify_handler(handle, | |
514 | + ACPI_SYSTEM_NOTIFY, | |
515 | + eeepc_rfkill_notify); | |
516 | + if (ACPI_FAILURE(status)) | |
517 | + pr_err("Error removing rfkill notify handler %s\n", | |
518 | + node); | |
519 | + } | |
520 | +} | |
521 | + | |
522 | +static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | |
523 | + u8 *value) | |
524 | +{ | |
525 | + int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
526 | + | |
527 | + if (result < 0) | |
528 | + return result; | |
529 | + | |
530 | + *value = !!result; | |
531 | + return 0; | |
532 | +} | |
533 | + | |
534 | +static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) | |
535 | +{ | |
536 | + kfree(hotplug_slot->info); | |
537 | + kfree(hotplug_slot); | |
538 | +} | |
539 | + | |
540 | +static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { | |
541 | + .owner = THIS_MODULE, | |
542 | + .get_adapter_status = eeepc_get_adapter_status, | |
543 | + .get_power_status = eeepc_get_adapter_status, | |
544 | +}; | |
545 | + | |
546 | +static void eeepc_hotplug_work(struct work_struct *work) | |
547 | +{ | |
548 | + struct eeepc_wmi *eeepc; | |
549 | + | |
550 | + eeepc = container_of(work, struct eeepc_wmi, hotplug_work); | |
551 | + eeepc_rfkill_hotplug(eeepc); | |
552 | +} | |
553 | + | |
554 | +static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) | |
555 | +{ | |
556 | + int ret = -ENOMEM; | |
557 | + struct pci_bus *bus = pci_find_bus(0, 1); | |
558 | + | |
559 | + if (!bus) { | |
560 | + pr_err("Unable to find wifi PCI bus\n"); | |
561 | + return -ENODEV; | |
562 | + } | |
563 | + | |
564 | + eeepc->hotplug_workqueue = | |
565 | + create_singlethread_workqueue("hotplug_workqueue"); | |
566 | + if (!eeepc->hotplug_workqueue) | |
567 | + goto error_workqueue; | |
568 | + | |
569 | + INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work); | |
570 | + | |
571 | + eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | |
572 | + if (!eeepc->hotplug_slot) | |
573 | + goto error_slot; | |
574 | + | |
575 | + eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), | |
576 | + GFP_KERNEL); | |
577 | + if (!eeepc->hotplug_slot->info) | |
578 | + goto error_info; | |
579 | + | |
580 | + eeepc->hotplug_slot->private = eeepc; | |
581 | + eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; | |
582 | + eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; | |
583 | + eeepc_get_adapter_status(eeepc->hotplug_slot, | |
584 | + &eeepc->hotplug_slot->info->adapter_status); | |
585 | + | |
586 | + ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); | |
587 | + if (ret) { | |
588 | + pr_err("Unable to register hotplug slot - %d\n", ret); | |
589 | + goto error_register; | |
590 | + } | |
591 | + | |
592 | + return 0; | |
593 | + | |
594 | +error_register: | |
595 | + kfree(eeepc->hotplug_slot->info); | |
596 | +error_info: | |
597 | + kfree(eeepc->hotplug_slot); | |
598 | + eeepc->hotplug_slot = NULL; | |
599 | +error_slot: | |
600 | + destroy_workqueue(eeepc->hotplug_workqueue); | |
601 | +error_workqueue: | |
602 | + return ret; | |
603 | +} | |
604 | + | |
605 | +/* | |
606 | + * Rfkill devices | |
607 | + */ | |
608 | +static int eeepc_rfkill_set(void *data, bool blocked) | |
609 | +{ | |
610 | + int dev_id = (unsigned long)data; | |
611 | + u32 ctrl_param = !blocked; | |
612 | + acpi_status status; | |
613 | + | |
614 | + status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); | |
615 | + | |
616 | + if (ACPI_FAILURE(status)) | |
617 | + return -EIO; | |
618 | + | |
619 | + return 0; | |
620 | +} | |
621 | + | |
622 | +static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) | |
623 | +{ | |
624 | + int dev_id = (unsigned long)data; | |
625 | + int result; | |
626 | + | |
627 | + result = eeepc_wmi_get_devstate_simple(dev_id); | |
628 | + | |
629 | + if (result < 0) | |
630 | + return ; | |
631 | + | |
632 | + rfkill_set_sw_state(rfkill, !result); | |
633 | +} | |
634 | + | |
635 | +static int eeepc_rfkill_wlan_set(void *data, bool blocked) | |
636 | +{ | |
637 | + struct eeepc_wmi *eeepc = data; | |
638 | + int ret; | |
639 | + | |
640 | + /* | |
641 | + * This handler is enabled only if hotplug is enabled. | |
642 | + * In this case, the eeepc_wmi_set_devstate() will | |
643 | + * trigger a wmi notification and we need to wait | |
644 | + * this call to finish before being able to call | |
645 | + * any wmi method | |
646 | + */ | |
647 | + mutex_lock(&eeepc->wmi_lock); | |
648 | + ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked); | |
649 | + mutex_unlock(&eeepc->wmi_lock); | |
650 | + return ret; | |
651 | +} | |
652 | + | |
653 | +static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data) | |
654 | +{ | |
655 | + eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN); | |
656 | +} | |
657 | + | |
658 | +static const struct rfkill_ops eeepc_rfkill_wlan_ops = { | |
659 | + .set_block = eeepc_rfkill_wlan_set, | |
660 | + .query = eeepc_rfkill_wlan_query, | |
661 | +}; | |
662 | + | |
663 | +static const struct rfkill_ops eeepc_rfkill_ops = { | |
664 | + .set_block = eeepc_rfkill_set, | |
665 | + .query = eeepc_rfkill_query, | |
666 | +}; | |
667 | + | |
668 | +static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, | |
669 | + struct rfkill **rfkill, | |
670 | + const char *name, | |
671 | + enum rfkill_type type, int dev_id) | |
672 | +{ | |
673 | + int result = eeepc_wmi_get_devstate_simple(dev_id); | |
674 | + | |
675 | + if (result < 0) | |
676 | + return result; | |
677 | + | |
678 | + if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) | |
679 | + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | |
680 | + &eeepc_rfkill_wlan_ops, eeepc); | |
681 | + else | |
682 | + *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | |
683 | + &eeepc_rfkill_ops, (void *)(long)dev_id); | |
684 | + | |
685 | + if (!*rfkill) | |
686 | + return -EINVAL; | |
687 | + | |
688 | + rfkill_init_sw_state(*rfkill, !result); | |
689 | + result = rfkill_register(*rfkill); | |
690 | + if (result) { | |
691 | + rfkill_destroy(*rfkill); | |
692 | + *rfkill = NULL; | |
693 | + return result; | |
694 | + } | |
695 | + return 0; | |
696 | +} | |
697 | + | |
698 | +static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) | |
699 | +{ | |
700 | + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | |
701 | + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | |
702 | + eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | |
703 | + if (eeepc->wlan_rfkill) { | |
704 | + rfkill_unregister(eeepc->wlan_rfkill); | |
705 | + rfkill_destroy(eeepc->wlan_rfkill); | |
706 | + eeepc->wlan_rfkill = NULL; | |
707 | + } | |
708 | + /* | |
709 | + * Refresh pci hotplug in case the rfkill state was changed after | |
710 | + * eeepc_unregister_rfkill_notifier() | |
711 | + */ | |
712 | + eeepc_rfkill_hotplug(eeepc); | |
713 | + if (eeepc->hotplug_slot) | |
714 | + pci_hp_deregister(eeepc->hotplug_slot); | |
715 | + if (eeepc->hotplug_workqueue) | |
716 | + destroy_workqueue(eeepc->hotplug_workqueue); | |
717 | + | |
718 | + if (eeepc->bluetooth_rfkill) { | |
719 | + rfkill_unregister(eeepc->bluetooth_rfkill); | |
720 | + rfkill_destroy(eeepc->bluetooth_rfkill); | |
721 | + eeepc->bluetooth_rfkill = NULL; | |
722 | + } | |
723 | + if (eeepc->wimax_rfkill) { | |
724 | + rfkill_unregister(eeepc->wimax_rfkill); | |
725 | + rfkill_destroy(eeepc->wimax_rfkill); | |
726 | + eeepc->wimax_rfkill = NULL; | |
727 | + } | |
728 | + if (eeepc->wwan3g_rfkill) { | |
729 | + rfkill_unregister(eeepc->wwan3g_rfkill); | |
730 | + rfkill_destroy(eeepc->wwan3g_rfkill); | |
731 | + eeepc->wwan3g_rfkill = NULL; | |
732 | + } | |
733 | +} | |
734 | + | |
735 | +static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) | |
736 | +{ | |
737 | + int result = 0; | |
738 | + | |
739 | + mutex_init(&eeepc->hotplug_lock); | |
740 | + mutex_init(&eeepc->wmi_lock); | |
741 | + | |
742 | + result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, | |
743 | + "eeepc-wlan", RFKILL_TYPE_WLAN, | |
744 | + EEEPC_WMI_DEVID_WLAN); | |
745 | + | |
746 | + if (result && result != -ENODEV) | |
747 | + goto exit; | |
748 | + | |
749 | + result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, | |
750 | + "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, | |
751 | + EEEPC_WMI_DEVID_BLUETOOTH); | |
752 | + | |
753 | + if (result && result != -ENODEV) | |
754 | + goto exit; | |
755 | + | |
756 | + result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, | |
757 | + "eeepc-wimax", RFKILL_TYPE_WIMAX, | |
758 | + EEEPC_WMI_DEVID_WIMAX); | |
759 | + | |
760 | + if (result && result != -ENODEV) | |
761 | + goto exit; | |
762 | + | |
763 | + result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, | |
764 | + "eeepc-wwan3g", RFKILL_TYPE_WWAN, | |
765 | + EEEPC_WMI_DEVID_WWAN3G); | |
766 | + | |
767 | + if (result && result != -ENODEV) | |
768 | + goto exit; | |
769 | + | |
770 | + if (!eeepc->hotplug_wireless) | |
771 | + goto exit; | |
772 | + | |
773 | + result = eeepc_setup_pci_hotplug(eeepc); | |
774 | + /* | |
775 | + * If we get -EBUSY then something else is handling the PCI hotplug - | |
776 | + * don't fail in this case | |
777 | + */ | |
778 | + if (result == -EBUSY) | |
779 | + result = 0; | |
780 | + | |
781 | + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | |
782 | + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | |
783 | + eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | |
784 | + /* | |
785 | + * Refresh pci hotplug in case the rfkill state was changed during | |
786 | + * setup. | |
787 | + */ | |
788 | + eeepc_rfkill_hotplug(eeepc); | |
789 | + | |
790 | +exit: | |
791 | + if (result && result != -ENODEV) | |
792 | + eeepc_wmi_rfkill_exit(eeepc); | |
793 | + | |
794 | + if (result == -ENODEV) | |
795 | + result = 0; | |
796 | + | |
797 | + return result; | |
798 | +} | |
799 | + | |
800 | +/* | |
801 | + * Backlight | |
802 | + */ | |
803 | +static int read_backlight_power(void) | |
804 | +{ | |
805 | + int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT); | |
806 | + | |
807 | + if (ret < 0) | |
808 | + return ret; | |
809 | + | |
810 | + return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | |
811 | +} | |
812 | + | |
813 | +static int read_brightness(struct backlight_device *bd) | |
814 | +{ | |
815 | + u32 retval; | |
816 | + acpi_status status; | |
817 | + | |
818 | + status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval); | |
819 | + | |
820 | + if (ACPI_FAILURE(status)) | |
821 | + return -EIO; | |
822 | + else | |
823 | + return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK; | |
824 | +} | |
825 | + | |
826 | +static int update_bl_status(struct backlight_device *bd) | |
827 | +{ | |
828 | + u32 ctrl_param; | |
829 | + acpi_status status; | |
830 | + int power; | |
831 | + | |
832 | + ctrl_param = bd->props.brightness; | |
833 | + | |
834 | + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, | |
835 | + ctrl_param, NULL); | |
836 | + | |
837 | + if (ACPI_FAILURE(status)) | |
838 | + return -EIO; | |
839 | + | |
840 | + power = read_backlight_power(); | |
841 | + if (power != -ENODEV && bd->props.power != power) { | |
842 | + ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); | |
843 | + status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, | |
844 | + ctrl_param, NULL); | |
845 | + | |
846 | + if (ACPI_FAILURE(status)) | |
847 | + return -EIO; | |
848 | + } | |
849 | + return 0; | |
850 | +} | |
851 | + | |
852 | +static const struct backlight_ops eeepc_wmi_bl_ops = { | |
853 | + .get_brightness = read_brightness, | |
854 | + .update_status = update_bl_status, | |
855 | +}; | |
856 | + | |
857 | +static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) | |
858 | +{ | |
859 | + struct backlight_device *bd = eeepc->backlight_device; | |
860 | + int old = bd->props.brightness; | |
861 | + int new = old; | |
862 | + | |
863 | + if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | |
864 | + new = code - NOTIFY_BRNUP_MIN + 1; | |
865 | + else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) | |
866 | + new = code - NOTIFY_BRNDOWN_MIN; | |
867 | + | |
868 | + bd->props.brightness = new; | |
869 | + backlight_update_status(bd); | |
870 | + backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | |
871 | + | |
872 | + return old; | |
873 | +} | |
874 | + | |
875 | +static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | |
876 | +{ | |
877 | + struct backlight_device *bd; | |
878 | + struct backlight_properties props; | |
879 | + int max; | |
880 | + int power; | |
881 | + | |
882 | + max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS, | |
883 | + EEEPC_WMI_DSTS_MAX_BRIGTH_MASK); | |
884 | + power = read_backlight_power(); | |
885 | + | |
886 | + if (max < 0 && power < 0) { | |
887 | + /* Try to keep the original error */ | |
888 | + if (max == -ENODEV && power == -ENODEV) | |
889 | + return -ENODEV; | |
890 | + if (max != -ENODEV) | |
891 | + return max; | |
892 | + else | |
893 | + return power; | |
894 | + } | |
895 | + if (max == -ENODEV) | |
896 | + max = 0; | |
897 | + if (power == -ENODEV) | |
898 | + power = FB_BLANK_UNBLANK; | |
899 | + | |
900 | + memset(&props, 0, sizeof(struct backlight_properties)); | |
901 | + props.max_brightness = max; | |
902 | + bd = backlight_device_register(EEEPC_WMI_FILE, | |
903 | + &eeepc->platform_device->dev, eeepc, | |
904 | + &eeepc_wmi_bl_ops, &props); | |
905 | + if (IS_ERR(bd)) { | |
906 | + pr_err("Could not register backlight device\n"); | |
907 | + return PTR_ERR(bd); | |
908 | + } | |
909 | + | |
910 | + eeepc->backlight_device = bd; | |
911 | + | |
912 | + bd->props.brightness = read_brightness(bd); | |
913 | + bd->props.power = power; | |
914 | + backlight_update_status(bd); | |
915 | + | |
916 | + return 0; | |
917 | +} | |
918 | + | |
919 | +static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) | |
920 | +{ | |
921 | + if (eeepc->backlight_device) | |
922 | + backlight_device_unregister(eeepc->backlight_device); | |
923 | + | |
924 | + eeepc->backlight_device = NULL; | |
925 | +} | |
926 | + | |
927 | +static void eeepc_wmi_notify(u32 value, void *context) | |
928 | +{ | |
929 | + struct eeepc_wmi *eeepc = context; | |
930 | + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | |
931 | + union acpi_object *obj; | |
932 | + acpi_status status; | |
933 | + int code; | |
934 | + int orig_code; | |
935 | + | |
936 | + status = wmi_get_event_data(value, &response); | |
937 | + if (status != AE_OK) { | |
938 | + pr_err("bad event status 0x%x\n", status); | |
939 | + return; | |
940 | + } | |
941 | + | |
942 | + obj = (union acpi_object *)response.pointer; | |
943 | + | |
944 | + if (obj && obj->type == ACPI_TYPE_INTEGER) { | |
945 | + code = obj->integer.value; | |
946 | + orig_code = code; | |
947 | + | |
948 | + if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | |
949 | + code = NOTIFY_BRNUP_MIN; | |
950 | + else if (code >= NOTIFY_BRNDOWN_MIN && | |
951 | + code <= NOTIFY_BRNDOWN_MAX) | |
952 | + code = NOTIFY_BRNDOWN_MIN; | |
953 | + | |
954 | + if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { | |
955 | + if (!acpi_video_backlight_support()) | |
956 | + eeepc_wmi_backlight_notify(eeepc, orig_code); | |
957 | + } | |
958 | + | |
959 | + if (!sparse_keymap_report_event(eeepc->inputdev, | |
960 | + code, 1, true)) | |
961 | + pr_info("Unknown key %x pressed\n", code); | |
962 | + } | |
963 | + | |
964 | + kfree(obj); | |
965 | +} | |
966 | + | |
967 | +/* | |
968 | + * Sys helpers | |
969 | + */ | |
970 | +static int parse_arg(const char *buf, unsigned long count, int *val) | |
971 | +{ | |
972 | + if (!count) | |
973 | + return 0; | |
974 | + if (sscanf(buf, "%i", val) != 1) | |
975 | + return -EINVAL; | |
976 | + return count; | |
977 | +} | |
978 | + | |
979 | +static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) | |
980 | +{ | |
981 | + acpi_status status; | |
982 | + u32 retval; | |
983 | + int rv, value; | |
984 | + | |
985 | + value = eeepc_wmi_get_devstate_simple(devid); | |
986 | + if (value == -ENODEV) /* Check device presence */ | |
987 | + return value; | |
988 | + | |
989 | + rv = parse_arg(buf, count, &value); | |
990 | + status = eeepc_wmi_set_devstate(devid, value, &retval); | |
991 | + | |
992 | + if (ACPI_FAILURE(status)) | |
993 | + return -EIO; | |
994 | + return rv; | |
995 | +} | |
996 | + | |
997 | +static ssize_t show_sys_wmi(int devid, char *buf) | |
998 | +{ | |
999 | + int value = eeepc_wmi_get_devstate_simple(devid); | |
1000 | + | |
1001 | + if (value < 0) | |
1002 | + return value; | |
1003 | + | |
1004 | + return sprintf(buf, "%d\n", value); | |
1005 | +} | |
1006 | + | |
1007 | +#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ | |
1008 | + static ssize_t show_##_name(struct device *dev, \ | |
1009 | + struct device_attribute *attr, \ | |
1010 | + char *buf) \ | |
1011 | + { \ | |
1012 | + return show_sys_wmi(_cm, buf); \ | |
1013 | + } \ | |
1014 | + static ssize_t store_##_name(struct device *dev, \ | |
1015 | + struct device_attribute *attr, \ | |
1016 | + const char *buf, size_t count) \ | |
1017 | + { \ | |
1018 | + return store_sys_wmi(_cm, buf, count); \ | |
1019 | + } \ | |
1020 | + static struct device_attribute dev_attr_##_name = { \ | |
1021 | + .attr = { \ | |
1022 | + .name = __stringify(_name), \ | |
1023 | + .mode = _mode }, \ | |
1024 | + .show = show_##_name, \ | |
1025 | + .store = store_##_name, \ | |
1026 | + } | |
1027 | + | |
1028 | +EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD); | |
1029 | +EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); | |
1030 | +EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); | |
1031 | + | |
1032 | +static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, | |
1033 | + const char *buf, size_t count) | |
1034 | +{ | |
1035 | + int value; | |
1036 | + struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; | |
1037 | + acpi_status status; | |
1038 | + | |
1039 | + if (!count || sscanf(buf, "%i", &value) != 1) | |
1040 | + return -EINVAL; | |
1041 | + if (value < 0 || value > 2) | |
1042 | + return -EINVAL; | |
1043 | + | |
1044 | + status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | |
1045 | + 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); | |
1046 | + | |
1047 | + if (ACPI_FAILURE(status)) | |
1048 | + return -EIO; | |
1049 | + else | |
1050 | + return count; | |
1051 | +} | |
1052 | + | |
1053 | +static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); | |
1054 | + | |
1055 | +static struct attribute *platform_attributes[] = { | |
1056 | + &dev_attr_cpufv.attr, | |
1057 | + &dev_attr_camera.attr, | |
1058 | + &dev_attr_cardr.attr, | |
1059 | + &dev_attr_touchpad.attr, | |
1060 | + NULL | |
1061 | +}; | |
1062 | + | |
1063 | +static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, | |
1064 | + struct attribute *attr, | |
1065 | + int idx) | |
1066 | +{ | |
1067 | + bool supported = true; | |
1068 | + int devid = -1; | |
1069 | + | |
1070 | + if (attr == &dev_attr_camera.attr) | |
1071 | + devid = EEEPC_WMI_DEVID_CAMERA; | |
1072 | + else if (attr == &dev_attr_cardr.attr) | |
1073 | + devid = EEEPC_WMI_DEVID_CARDREADER; | |
1074 | + else if (attr == &dev_attr_touchpad.attr) | |
1075 | + devid = EEEPC_WMI_DEVID_TOUCHPAD; | |
1076 | + | |
1077 | + if (devid != -1) | |
1078 | + supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; | |
1079 | + | |
1080 | + return supported ? attr->mode : 0; | |
1081 | +} | |
1082 | + | |
1083 | +static struct attribute_group platform_attribute_group = { | |
1084 | + .is_visible = eeepc_sysfs_is_visible, | |
1085 | + .attrs = platform_attributes | |
1086 | +}; | |
1087 | + | |
1088 | +static void eeepc_wmi_sysfs_exit(struct platform_device *device) | |
1089 | +{ | |
1090 | + sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); | |
1091 | +} | |
1092 | + | |
1093 | +static int eeepc_wmi_sysfs_init(struct platform_device *device) | |
1094 | +{ | |
1095 | + return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); | |
1096 | +} | |
1097 | + | |
1098 | +/* | |
1099 | + * Platform device | |
1100 | + */ | |
1101 | +static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) | |
1102 | +{ | |
1103 | + return eeepc_wmi_sysfs_init(eeepc->platform_device); | |
1104 | +} | |
1105 | + | |
1106 | +static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) | |
1107 | +{ | |
1108 | + eeepc_wmi_sysfs_exit(eeepc->platform_device); | |
1109 | +} | |
1110 | + | |
1111 | +/* | |
1112 | + * debugfs | |
1113 | + */ | |
1114 | +struct eeepc_wmi_debugfs_node { | |
1115 | + struct eeepc_wmi *eeepc; | |
1116 | + char *name; | |
1117 | + int (*show)(struct seq_file *m, void *data); | |
1118 | +}; | |
1119 | + | |
1120 | +static int show_dsts(struct seq_file *m, void *data) | |
1121 | +{ | |
1122 | + struct eeepc_wmi *eeepc = m->private; | |
1123 | + acpi_status status; | |
1124 | + u32 retval = -1; | |
1125 | + | |
1126 | + status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); | |
1127 | + | |
1128 | + if (ACPI_FAILURE(status)) | |
1129 | + return -EIO; | |
1130 | + | |
1131 | + seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); | |
1132 | + | |
1133 | + return 0; | |
1134 | +} | |
1135 | + | |
1136 | +static int show_devs(struct seq_file *m, void *data) | |
1137 | +{ | |
1138 | + struct eeepc_wmi *eeepc = m->private; | |
1139 | + acpi_status status; | |
1140 | + u32 retval = -1; | |
1141 | + | |
1142 | + status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, | |
1143 | + eeepc->debug.ctrl_param, &retval); | |
1144 | + if (ACPI_FAILURE(status)) | |
1145 | + return -EIO; | |
1146 | + | |
1147 | + seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, | |
1148 | + eeepc->debug.ctrl_param, retval); | |
1149 | + | |
1150 | + return 0; | |
1151 | +} | |
1152 | + | |
1153 | +static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { | |
1154 | + { NULL, "devs", show_devs }, | |
1155 | + { NULL, "dsts", show_dsts }, | |
1156 | +}; | |
1157 | + | |
1158 | +static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) | |
1159 | +{ | |
1160 | + struct eeepc_wmi_debugfs_node *node = inode->i_private; | |
1161 | + | |
1162 | + return single_open(file, node->show, node->eeepc); | |
1163 | +} | |
1164 | + | |
1165 | +static const struct file_operations eeepc_wmi_debugfs_io_ops = { | |
1166 | + .owner = THIS_MODULE, | |
1167 | + .open = eeepc_wmi_debugfs_open, | |
1168 | + .read = seq_read, | |
1169 | + .llseek = seq_lseek, | |
1170 | + .release = single_release, | |
1171 | +}; | |
1172 | + | |
1173 | +static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) | |
1174 | +{ | |
1175 | + debugfs_remove_recursive(eeepc->debug.root); | |
1176 | +} | |
1177 | + | |
1178 | +static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) | |
1179 | +{ | |
1180 | + struct dentry *dent; | |
1181 | + int i; | |
1182 | + | |
1183 | + eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); | |
1184 | + if (!eeepc->debug.root) { | |
1185 | + pr_err("failed to create debugfs directory"); | |
1186 | + goto error_debugfs; | |
1187 | + } | |
1188 | + | |
1189 | + dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, | |
1190 | + eeepc->debug.root, &eeepc->debug.dev_id); | |
1191 | + if (!dent) | |
1192 | + goto error_debugfs; | |
1193 | + | |
1194 | + dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, | |
1195 | + eeepc->debug.root, &eeepc->debug.ctrl_param); | |
1196 | + if (!dent) | |
1197 | + goto error_debugfs; | |
1198 | + | |
1199 | + for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { | |
1200 | + struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; | |
1201 | + | |
1202 | + node->eeepc = eeepc; | |
1203 | + dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | |
1204 | + eeepc->debug.root, node, | |
1205 | + &eeepc_wmi_debugfs_io_ops); | |
1206 | + if (!dent) { | |
1207 | + pr_err("failed to create debug file: %s\n", node->name); | |
1208 | + goto error_debugfs; | |
1209 | + } | |
1210 | + } | |
1211 | + | |
1212 | + return 0; | |
1213 | + | |
1214 | +error_debugfs: | |
1215 | + eeepc_wmi_debugfs_exit(eeepc); | |
1216 | + return -ENOMEM; | |
1217 | +} | |
1218 | + | |
1219 | +/* | |
1220 | + * WMI Driver | |
1221 | + */ | |
1222 | +static void eeepc_dmi_check(struct eeepc_wmi *eeepc) | |
1223 | +{ | |
1224 | + const char *model; | |
1225 | + | |
1226 | + model = dmi_get_system_info(DMI_PRODUCT_NAME); | |
1227 | + if (!model) | |
1228 | + return; | |
1229 | + | |
1230 | + /* | |
1231 | + * Whitelist for wlan hotplug | |
1232 | + * | |
1233 | + * Eeepc 1000H needs the current hotplug code to handle | |
1234 | + * Fn+F2 correctly. We may add other Eeepc here later, but | |
1235 | + * it seems that most of the laptops supported by eeepc-wmi | |
1236 | + * don't need to be on this list | |
1237 | + */ | |
1238 | + if (strcmp(model, "1000H") == 0) { | |
1239 | + eeepc->hotplug_wireless = true; | |
1240 | + pr_info("wlan hotplug enabled\n"); | |
1241 | + } | |
1242 | +} | |
1243 | + | |
1244 | +static int __init eeepc_wmi_add(struct platform_device *pdev) | |
1245 | +{ | |
1246 | + struct eeepc_wmi *eeepc; | |
1247 | + acpi_status status; | |
1248 | + int err; | |
1249 | + | |
1250 | + eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | |
1251 | + if (!eeepc) | |
1252 | + return -ENOMEM; | |
1253 | + | |
1254 | + eeepc->platform_device = pdev; | |
1255 | + platform_set_drvdata(eeepc->platform_device, eeepc); | |
1256 | + | |
1257 | + eeepc->hotplug_wireless = hotplug_wireless; | |
1258 | + eeepc_dmi_check(eeepc); | |
1259 | + | |
1260 | + err = eeepc_wmi_platform_init(eeepc); | |
1261 | + if (err) | |
1262 | + goto fail_platform; | |
1263 | + | |
1264 | + err = eeepc_wmi_input_init(eeepc); | |
1265 | + if (err) | |
1266 | + goto fail_input; | |
1267 | + | |
1268 | + err = eeepc_wmi_led_init(eeepc); | |
1269 | + if (err) | |
1270 | + goto fail_leds; | |
1271 | + | |
1272 | + err = eeepc_wmi_rfkill_init(eeepc); | |
1273 | + if (err) | |
1274 | + goto fail_rfkill; | |
1275 | + | |
1276 | + if (!acpi_video_backlight_support()) { | |
1277 | + err = eeepc_wmi_backlight_init(eeepc); | |
1278 | + if (err && err != -ENODEV) | |
1279 | + goto fail_backlight; | |
1280 | + } else | |
1281 | + pr_info("Backlight controlled by ACPI video driver\n"); | |
1282 | + | |
1283 | + status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | |
1284 | + eeepc_wmi_notify, eeepc); | |
1285 | + if (ACPI_FAILURE(status)) { | |
1286 | + pr_err("Unable to register notify handler - %d\n", | |
1287 | + status); | |
1288 | + err = -ENODEV; | |
1289 | + goto fail_wmi_handler; | |
1290 | + } | |
1291 | + | |
1292 | + err = eeepc_wmi_debugfs_init(eeepc); | |
1293 | + if (err) | |
1294 | + goto fail_debugfs; | |
1295 | + | |
1296 | + return 0; | |
1297 | + | |
1298 | +fail_debugfs: | |
1299 | + wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | |
1300 | +fail_wmi_handler: | |
1301 | + eeepc_wmi_backlight_exit(eeepc); | |
1302 | +fail_backlight: | |
1303 | + eeepc_wmi_rfkill_exit(eeepc); | |
1304 | +fail_rfkill: | |
1305 | + eeepc_wmi_led_exit(eeepc); | |
1306 | +fail_leds: | |
1307 | + eeepc_wmi_input_exit(eeepc); | |
1308 | +fail_input: | |
1309 | + eeepc_wmi_platform_exit(eeepc); | |
1310 | +fail_platform: | |
1311 | + kfree(eeepc); | |
1312 | + return err; | |
1313 | +} | |
1314 | + | |
1315 | +static int __exit eeepc_wmi_remove(struct platform_device *device) | |
1316 | +{ | |
1317 | + struct eeepc_wmi *eeepc; | |
1318 | + | |
1319 | + eeepc = platform_get_drvdata(device); | |
1320 | + wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | |
1321 | + eeepc_wmi_backlight_exit(eeepc); | |
1322 | + eeepc_wmi_input_exit(eeepc); | |
1323 | + eeepc_wmi_led_exit(eeepc); | |
1324 | + eeepc_wmi_rfkill_exit(eeepc); | |
1325 | + eeepc_wmi_debugfs_exit(eeepc); | |
1326 | + eeepc_wmi_platform_exit(eeepc); | |
1327 | + | |
1328 | + kfree(eeepc); | |
1329 | + return 0; | |
1330 | +} | |
1331 | + | |
1332 | +/* | |
1333 | + * Platform driver - hibernate/resume callbacks | |
1334 | + */ | |
1335 | +static int eeepc_hotk_thaw(struct device *device) | |
1336 | +{ | |
1337 | + struct eeepc_wmi *eeepc = dev_get_drvdata(device); | |
1338 | + | |
1339 | + if (eeepc->wlan_rfkill) { | |
1340 | + bool wlan; | |
1341 | + | |
1342 | + /* | |
1343 | + * Work around bios bug - acpi _PTS turns off the wireless led | |
1344 | + * during suspend. Normally it restores it on resume, but | |
1345 | + * we should kick it ourselves in case hibernation is aborted. | |
1346 | + */ | |
1347 | + wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
1348 | + eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL); | |
1349 | + } | |
1350 | + | |
1351 | + return 0; | |
1352 | +} | |
1353 | + | |
1354 | +static int eeepc_hotk_restore(struct device *device) | |
1355 | +{ | |
1356 | + struct eeepc_wmi *eeepc = dev_get_drvdata(device); | |
1357 | + int bl; | |
1358 | + | |
1359 | + /* Refresh both wlan rfkill state and pci hotplug */ | |
1360 | + if (eeepc->wlan_rfkill) | |
1361 | + eeepc_rfkill_hotplug(eeepc); | |
1362 | + | |
1363 | + if (eeepc->bluetooth_rfkill) { | |
1364 | + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); | |
1365 | + rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); | |
1366 | + } | |
1367 | + if (eeepc->wimax_rfkill) { | |
1368 | + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX); | |
1369 | + rfkill_set_sw_state(eeepc->wimax_rfkill, bl); | |
1370 | + } | |
1371 | + if (eeepc->wwan3g_rfkill) { | |
1372 | + bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); | |
1373 | + rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); | |
1374 | + } | |
1375 | + | |
1376 | + return 0; | |
1377 | +} | |
1378 | + | |
1379 | +static const struct dev_pm_ops eeepc_pm_ops = { | |
1380 | + .thaw = eeepc_hotk_thaw, | |
1381 | + .restore = eeepc_hotk_restore, | |
1382 | +}; | |
1383 | + | |
1384 | +static struct platform_driver platform_driver = { | |
1385 | + .remove = __exit_p(eeepc_wmi_remove), | |
1386 | + .driver = { | |
1387 | + .name = EEEPC_WMI_FILE, | |
1388 | + .owner = THIS_MODULE, | |
1389 | + .pm = &eeepc_pm_ops, | |
1390 | + }, | |
1391 | +}; | |
1392 | + | |
1393 | +static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, | |
1394 | + void *context, void **retval) | |
1395 | +{ | |
1396 | + pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); | |
1397 | + *(bool *)context = true; | |
1398 | + return AE_CTRL_TERMINATE; | |
1399 | +} | |
1400 | + | |
1401 | +static int __init eeepc_wmi_check_atkd(void) | |
1402 | +{ | |
1403 | + acpi_status status; | |
1404 | + bool found = false; | |
1405 | + | |
1406 | + status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, | |
1407 | + &found, NULL); | |
1408 | + | |
1409 | + if (ACPI_FAILURE(status) || !found) | |
1410 | + return 0; | |
1411 | + return -1; | |
1412 | +} | |
1413 | + | |
1414 | +static int __init eeepc_wmi_probe(struct platform_device *pdev) | |
1415 | +{ | |
1416 | + if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | |
1417 | + !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { | |
1418 | + pr_warning("No known WMI GUID found\n"); | |
1419 | + return -ENODEV; | |
1420 | + } | |
1421 | + | |
1422 | + if (eeepc_wmi_check_atkd()) { | |
1423 | + pr_warning("WMI device present, but legacy ATKD device is also " | |
1424 | + "present and enabled."); | |
1425 | + pr_warning("You probably booted with acpi_osi=\"Linux\" or " | |
1426 | + "acpi_osi=\"!Windows 2009\""); | |
1427 | + pr_warning("Can't load eeepc-wmi, use default acpi_osi " | |
1428 | + "(preferred) or eeepc-laptop"); | |
1429 | + return -ENODEV; | |
1430 | + } | |
1431 | + | |
1432 | + return eeepc_wmi_add(pdev); | |
1433 | +} | |
1434 | + | |
1435 | +static struct platform_device *platform_device; | |
1436 | + | |
1437 | +static int __init eeepc_wmi_init(void) | |
1438 | +{ | |
1439 | + platform_device = platform_create_bundle(&platform_driver, | |
1440 | + eeepc_wmi_probe, | |
1441 | + NULL, 0, NULL, 0); | |
1442 | + if (IS_ERR(platform_device)) | |
1443 | + return PTR_ERR(platform_device); | |
1444 | + return 0; | |
1445 | +} | |
1446 | + | |
1447 | +static void __exit eeepc_wmi_exit(void) | |
1448 | +{ | |
1449 | + platform_device_unregister(platform_device); | |
1450 | + platform_driver_unregister(&platform_driver); | |
1451 | +} | |
1452 | + | |
1453 | +module_init(eeepc_wmi_init); | |
1454 | +module_exit(eeepc_wmi_exit); |
drivers/platform/x86/eeepc-wmi.c
Changes suppressed. Click to show
1 | -/* | |
2 | - * Eee PC WMI hotkey driver | |
3 | - * | |
4 | - * Copyright(C) 2010 Intel Corporation. | |
5 | - * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> | |
6 | - * | |
7 | - * Portions based on wistron_btns.c: | |
8 | - * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> | |
9 | - * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> | |
10 | - * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> | |
11 | - * | |
12 | - * This program is free software; you can redistribute it and/or modify | |
13 | - * it under the terms of the GNU General Public License as published by | |
14 | - * the Free Software Foundation; either version 2 of the License, or | |
15 | - * (at your option) any later version. | |
16 | - * | |
17 | - * This program is distributed in the hope that it will be useful, | |
18 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | - * GNU General Public License for more details. | |
21 | - * | |
22 | - * You should have received a copy of the GNU General Public License | |
23 | - * along with this program; if not, write to the Free Software | |
24 | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
25 | - */ | |
26 | - | |
27 | -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
28 | - | |
29 | -#include <linux/kernel.h> | |
30 | -#include <linux/module.h> | |
31 | -#include <linux/init.h> | |
32 | -#include <linux/types.h> | |
33 | -#include <linux/slab.h> | |
34 | -#include <linux/input.h> | |
35 | -#include <linux/input/sparse-keymap.h> | |
36 | -#include <linux/fb.h> | |
37 | -#include <linux/backlight.h> | |
38 | -#include <linux/leds.h> | |
39 | -#include <linux/rfkill.h> | |
40 | -#include <linux/pci.h> | |
41 | -#include <linux/pci_hotplug.h> | |
42 | -#include <linux/debugfs.h> | |
43 | -#include <linux/seq_file.h> | |
44 | -#include <linux/platform_device.h> | |
45 | -#include <linux/dmi.h> | |
46 | -#include <acpi/acpi_bus.h> | |
47 | -#include <acpi/acpi_drivers.h> | |
48 | - | |
49 | -#define EEEPC_WMI_FILE "eeepc-wmi" | |
50 | - | |
51 | -MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>"); | |
52 | -MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver"); | |
53 | -MODULE_LICENSE("GPL"); | |
54 | - | |
55 | -#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */ | |
56 | - | |
57 | -#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" | |
58 | -#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" | |
59 | - | |
60 | -MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); | |
61 | -MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID); | |
62 | - | |
63 | -#define NOTIFY_BRNUP_MIN 0x11 | |
64 | -#define NOTIFY_BRNUP_MAX 0x1f | |
65 | -#define NOTIFY_BRNDOWN_MIN 0x20 | |
66 | -#define NOTIFY_BRNDOWN_MAX 0x2e | |
67 | - | |
68 | -/* WMI Methods */ | |
69 | -#define EEEPC_WMI_METHODID_DSTS 0x53544344 | |
70 | -#define EEEPC_WMI_METHODID_DEVS 0x53564544 | |
71 | -#define EEEPC_WMI_METHODID_CFVS 0x53564643 | |
72 | - | |
73 | -/* Wireless */ | |
74 | -#define EEEPC_WMI_DEVID_WLAN 0x00010011 | |
75 | -#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013 | |
76 | -#define EEEPC_WMI_DEVID_WIMAX 0x00010017 | |
77 | -#define EEEPC_WMI_DEVID_WWAN3G 0x00010019 | |
78 | - | |
79 | -/* Backlight and Brightness */ | |
80 | -#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011 | |
81 | -#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012 | |
82 | - | |
83 | -/* Misc */ | |
84 | -#define EEEPC_WMI_DEVID_CAMERA 0x00060013 | |
85 | - | |
86 | -/* Storage */ | |
87 | -#define EEEPC_WMI_DEVID_CARDREADER 0x00080013 | |
88 | - | |
89 | -/* Input */ | |
90 | -#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011 | |
91 | -#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012 | |
92 | - | |
93 | -/* DSTS masks */ | |
94 | -#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001 | |
95 | -#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000 | |
96 | -#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF | |
97 | -#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 | |
98 | - | |
99 | -static bool hotplug_wireless; | |
100 | - | |
101 | -module_param(hotplug_wireless, bool, 0444); | |
102 | -MODULE_PARM_DESC(hotplug_wireless, | |
103 | - "Enable hotplug for wireless device. " | |
104 | - "If your laptop needs that, please report to " | |
105 | - "acpi4asus-user@lists.sourceforge.net."); | |
106 | - | |
107 | -static const struct key_entry eeepc_wmi_keymap[] = { | |
108 | - /* Sleep already handled via generic ACPI code */ | |
109 | - { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } }, | |
110 | - { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } }, | |
111 | - { KE_KEY, 0x30, { KEY_VOLUMEUP } }, | |
112 | - { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, | |
113 | - { KE_KEY, 0x32, { KEY_MUTE } }, | |
114 | - { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */ | |
115 | - { KE_KEY, 0x5d, { KEY_WLAN } }, | |
116 | - { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */ | |
117 | - { KE_KEY, 0x82, { KEY_CAMERA } }, | |
118 | - { KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } }, | |
119 | - { KE_KEY, 0x88, { KEY_WLAN } }, | |
120 | - { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } }, | |
121 | - { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */ | |
122 | - { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */ | |
123 | - { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } }, | |
124 | - { KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } }, | |
125 | - { KE_KEY, 0xec, { KEY_CAMERA_UP } }, | |
126 | - { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, | |
127 | - { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, | |
128 | - { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, | |
129 | - { KE_END, 0}, | |
130 | -}; | |
131 | - | |
132 | -struct bios_args { | |
133 | - u32 dev_id; | |
134 | - u32 ctrl_param; | |
135 | -}; | |
136 | - | |
137 | -/* | |
138 | - * eeepc-wmi/ - debugfs root directory | |
139 | - * dev_id - current dev_id | |
140 | - * ctrl_param - current ctrl_param | |
141 | - * devs - call DEVS(dev_id, ctrl_param) and print result | |
142 | - * dsts - call DSTS(dev_id) and print result | |
143 | - */ | |
144 | -struct eeepc_wmi_debug { | |
145 | - struct dentry *root; | |
146 | - u32 dev_id; | |
147 | - u32 ctrl_param; | |
148 | -}; | |
149 | - | |
150 | -struct eeepc_wmi { | |
151 | - bool hotplug_wireless; | |
152 | - | |
153 | - struct input_dev *inputdev; | |
154 | - struct backlight_device *backlight_device; | |
155 | - struct platform_device *platform_device; | |
156 | - | |
157 | - struct led_classdev tpd_led; | |
158 | - int tpd_led_wk; | |
159 | - struct workqueue_struct *led_workqueue; | |
160 | - struct work_struct tpd_led_work; | |
161 | - | |
162 | - struct rfkill *wlan_rfkill; | |
163 | - struct rfkill *bluetooth_rfkill; | |
164 | - struct rfkill *wimax_rfkill; | |
165 | - struct rfkill *wwan3g_rfkill; | |
166 | - | |
167 | - struct hotplug_slot *hotplug_slot; | |
168 | - struct mutex hotplug_lock; | |
169 | - struct mutex wmi_lock; | |
170 | - struct workqueue_struct *hotplug_workqueue; | |
171 | - struct work_struct hotplug_work; | |
172 | - | |
173 | - struct eeepc_wmi_debug debug; | |
174 | -}; | |
175 | - | |
176 | -static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc) | |
177 | -{ | |
178 | - int err; | |
179 | - | |
180 | - eeepc->inputdev = input_allocate_device(); | |
181 | - if (!eeepc->inputdev) | |
182 | - return -ENOMEM; | |
183 | - | |
184 | - eeepc->inputdev->name = "Eee PC WMI hotkeys"; | |
185 | - eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0"; | |
186 | - eeepc->inputdev->id.bustype = BUS_HOST; | |
187 | - eeepc->inputdev->dev.parent = &eeepc->platform_device->dev; | |
188 | - | |
189 | - err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL); | |
190 | - if (err) | |
191 | - goto err_free_dev; | |
192 | - | |
193 | - err = input_register_device(eeepc->inputdev); | |
194 | - if (err) | |
195 | - goto err_free_keymap; | |
196 | - | |
197 | - return 0; | |
198 | - | |
199 | -err_free_keymap: | |
200 | - sparse_keymap_free(eeepc->inputdev); | |
201 | -err_free_dev: | |
202 | - input_free_device(eeepc->inputdev); | |
203 | - return err; | |
204 | -} | |
205 | - | |
206 | -static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc) | |
207 | -{ | |
208 | - if (eeepc->inputdev) { | |
209 | - sparse_keymap_free(eeepc->inputdev); | |
210 | - input_unregister_device(eeepc->inputdev); | |
211 | - } | |
212 | - | |
213 | - eeepc->inputdev = NULL; | |
214 | -} | |
215 | - | |
216 | -static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval) | |
217 | -{ | |
218 | - struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id }; | |
219 | - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | |
220 | - union acpi_object *obj; | |
221 | - acpi_status status; | |
222 | - u32 tmp; | |
223 | - | |
224 | - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | |
225 | - 1, EEEPC_WMI_METHODID_DSTS, | |
226 | - &input, &output); | |
227 | - | |
228 | - if (ACPI_FAILURE(status)) | |
229 | - return status; | |
230 | - | |
231 | - obj = (union acpi_object *)output.pointer; | |
232 | - if (obj && obj->type == ACPI_TYPE_INTEGER) | |
233 | - tmp = (u32)obj->integer.value; | |
234 | - else | |
235 | - tmp = 0; | |
236 | - | |
237 | - if (retval) | |
238 | - *retval = tmp; | |
239 | - | |
240 | - kfree(obj); | |
241 | - | |
242 | - return status; | |
243 | - | |
244 | -} | |
245 | - | |
246 | -static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param, | |
247 | - u32 *retval) | |
248 | -{ | |
249 | - struct bios_args args = { | |
250 | - .dev_id = dev_id, | |
251 | - .ctrl_param = ctrl_param, | |
252 | - }; | |
253 | - struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | |
254 | - acpi_status status; | |
255 | - | |
256 | - if (!retval) { | |
257 | - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | |
258 | - EEEPC_WMI_METHODID_DEVS, | |
259 | - &input, NULL); | |
260 | - } else { | |
261 | - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | |
262 | - union acpi_object *obj; | |
263 | - u32 tmp; | |
264 | - | |
265 | - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1, | |
266 | - EEEPC_WMI_METHODID_DEVS, | |
267 | - &input, &output); | |
268 | - | |
269 | - if (ACPI_FAILURE(status)) | |
270 | - return status; | |
271 | - | |
272 | - obj = (union acpi_object *)output.pointer; | |
273 | - if (obj && obj->type == ACPI_TYPE_INTEGER) | |
274 | - tmp = (u32)obj->integer.value; | |
275 | - else | |
276 | - tmp = 0; | |
277 | - | |
278 | - *retval = tmp; | |
279 | - | |
280 | - kfree(obj); | |
281 | - } | |
282 | - | |
283 | - return status; | |
284 | -} | |
285 | - | |
286 | -/* Helper for special devices with magic return codes */ | |
287 | -static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask) | |
288 | -{ | |
289 | - u32 retval = 0; | |
290 | - acpi_status status; | |
291 | - | |
292 | - status = eeepc_wmi_get_devstate(dev_id, &retval); | |
293 | - | |
294 | - if (ACPI_FAILURE(status)) | |
295 | - return -EINVAL; | |
296 | - | |
297 | - if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT)) | |
298 | - return -ENODEV; | |
299 | - | |
300 | - return retval & mask; | |
301 | -} | |
302 | - | |
303 | -static int eeepc_wmi_get_devstate_simple(u32 dev_id) | |
304 | -{ | |
305 | - return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT); | |
306 | -} | |
307 | - | |
308 | -/* | |
309 | - * LEDs | |
310 | - */ | |
311 | -/* | |
312 | - * These functions actually update the LED's, and are called from a | |
313 | - * workqueue. By doing this as separate work rather than when the LED | |
314 | - * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a | |
315 | - * potentially bad time, such as a timer interrupt. | |
316 | - */ | |
317 | -static void tpd_led_update(struct work_struct *work) | |
318 | -{ | |
319 | - int ctrl_param; | |
320 | - struct eeepc_wmi *eeepc; | |
321 | - | |
322 | - eeepc = container_of(work, struct eeepc_wmi, tpd_led_work); | |
323 | - | |
324 | - ctrl_param = eeepc->tpd_led_wk; | |
325 | - eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL); | |
326 | -} | |
327 | - | |
328 | -static void tpd_led_set(struct led_classdev *led_cdev, | |
329 | - enum led_brightness value) | |
330 | -{ | |
331 | - struct eeepc_wmi *eeepc; | |
332 | - | |
333 | - eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | |
334 | - | |
335 | - eeepc->tpd_led_wk = !!value; | |
336 | - queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work); | |
337 | -} | |
338 | - | |
339 | -static int read_tpd_led_state(struct eeepc_wmi *eeepc) | |
340 | -{ | |
341 | - return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED); | |
342 | -} | |
343 | - | |
344 | -static enum led_brightness tpd_led_get(struct led_classdev *led_cdev) | |
345 | -{ | |
346 | - struct eeepc_wmi *eeepc; | |
347 | - | |
348 | - eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led); | |
349 | - | |
350 | - return read_tpd_led_state(eeepc); | |
351 | -} | |
352 | - | |
353 | -static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc) | |
354 | -{ | |
355 | - int rv; | |
356 | - | |
357 | - if (read_tpd_led_state(eeepc) < 0) | |
358 | - return 0; | |
359 | - | |
360 | - eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue"); | |
361 | - if (!eeepc->led_workqueue) | |
362 | - return -ENOMEM; | |
363 | - INIT_WORK(&eeepc->tpd_led_work, tpd_led_update); | |
364 | - | |
365 | - eeepc->tpd_led.name = "eeepc::touchpad"; | |
366 | - eeepc->tpd_led.brightness_set = tpd_led_set; | |
367 | - eeepc->tpd_led.brightness_get = tpd_led_get; | |
368 | - eeepc->tpd_led.max_brightness = 1; | |
369 | - | |
370 | - rv = led_classdev_register(&eeepc->platform_device->dev, | |
371 | - &eeepc->tpd_led); | |
372 | - if (rv) { | |
373 | - destroy_workqueue(eeepc->led_workqueue); | |
374 | - return rv; | |
375 | - } | |
376 | - | |
377 | - return 0; | |
378 | -} | |
379 | - | |
380 | -static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc) | |
381 | -{ | |
382 | - if (eeepc->tpd_led.dev) | |
383 | - led_classdev_unregister(&eeepc->tpd_led); | |
384 | - if (eeepc->led_workqueue) | |
385 | - destroy_workqueue(eeepc->led_workqueue); | |
386 | -} | |
387 | - | |
388 | -/* | |
389 | - * PCI hotplug (for wlan rfkill) | |
390 | - */ | |
391 | -static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc) | |
392 | -{ | |
393 | - int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
394 | - | |
395 | - if (result < 0) | |
396 | - return false; | |
397 | - return !result; | |
398 | -} | |
399 | - | |
400 | -static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc) | |
401 | -{ | |
402 | - struct pci_dev *dev; | |
403 | - struct pci_bus *bus; | |
404 | - bool blocked; | |
405 | - bool absent; | |
406 | - u32 l; | |
407 | - | |
408 | - mutex_lock(&eeepc->wmi_lock); | |
409 | - blocked = eeepc_wlan_rfkill_blocked(eeepc); | |
410 | - mutex_unlock(&eeepc->wmi_lock); | |
411 | - | |
412 | - mutex_lock(&eeepc->hotplug_lock); | |
413 | - | |
414 | - if (eeepc->wlan_rfkill) | |
415 | - rfkill_set_sw_state(eeepc->wlan_rfkill, blocked); | |
416 | - | |
417 | - if (eeepc->hotplug_slot) { | |
418 | - bus = pci_find_bus(0, 1); | |
419 | - if (!bus) { | |
420 | - pr_warning("Unable to find PCI bus 1?\n"); | |
421 | - goto out_unlock; | |
422 | - } | |
423 | - | |
424 | - if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) { | |
425 | - pr_err("Unable to read PCI config space?\n"); | |
426 | - goto out_unlock; | |
427 | - } | |
428 | - absent = (l == 0xffffffff); | |
429 | - | |
430 | - if (blocked != absent) { | |
431 | - pr_warning("BIOS says wireless lan is %s, " | |
432 | - "but the pci device is %s\n", | |
433 | - blocked ? "blocked" : "unblocked", | |
434 | - absent ? "absent" : "present"); | |
435 | - pr_warning("skipped wireless hotplug as probably " | |
436 | - "inappropriate for this model\n"); | |
437 | - goto out_unlock; | |
438 | - } | |
439 | - | |
440 | - if (!blocked) { | |
441 | - dev = pci_get_slot(bus, 0); | |
442 | - if (dev) { | |
443 | - /* Device already present */ | |
444 | - pci_dev_put(dev); | |
445 | - goto out_unlock; | |
446 | - } | |
447 | - dev = pci_scan_single_device(bus, 0); | |
448 | - if (dev) { | |
449 | - pci_bus_assign_resources(bus); | |
450 | - if (pci_bus_add_device(dev)) | |
451 | - pr_err("Unable to hotplug wifi\n"); | |
452 | - } | |
453 | - } else { | |
454 | - dev = pci_get_slot(bus, 0); | |
455 | - if (dev) { | |
456 | - pci_remove_bus_device(dev); | |
457 | - pci_dev_put(dev); | |
458 | - } | |
459 | - } | |
460 | - } | |
461 | - | |
462 | -out_unlock: | |
463 | - mutex_unlock(&eeepc->hotplug_lock); | |
464 | -} | |
465 | - | |
466 | -static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | |
467 | -{ | |
468 | - struct eeepc_wmi *eeepc = data; | |
469 | - | |
470 | - if (event != ACPI_NOTIFY_BUS_CHECK) | |
471 | - return; | |
472 | - | |
473 | - /* | |
474 | - * We can't call directly eeepc_rfkill_hotplug because most | |
475 | - * of the time WMBC is still being executed and not reetrant. | |
476 | - * There is currently no way to tell ACPICA that we want this | |
477 | - * method to be serialized, we schedule a eeepc_rfkill_hotplug | |
478 | - * call later, in a safer context. | |
479 | - */ | |
480 | - queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work); | |
481 | -} | |
482 | - | |
483 | -static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc, | |
484 | - char *node) | |
485 | -{ | |
486 | - acpi_status status; | |
487 | - acpi_handle handle; | |
488 | - | |
489 | - status = acpi_get_handle(NULL, node, &handle); | |
490 | - | |
491 | - if (ACPI_SUCCESS(status)) { | |
492 | - status = acpi_install_notify_handler(handle, | |
493 | - ACPI_SYSTEM_NOTIFY, | |
494 | - eeepc_rfkill_notify, | |
495 | - eeepc); | |
496 | - if (ACPI_FAILURE(status)) | |
497 | - pr_warning("Failed to register notify on %s\n", node); | |
498 | - } else | |
499 | - return -ENODEV; | |
500 | - | |
501 | - return 0; | |
502 | -} | |
503 | - | |
504 | -static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc, | |
505 | - char *node) | |
506 | -{ | |
507 | - acpi_status status = AE_OK; | |
508 | - acpi_handle handle; | |
509 | - | |
510 | - status = acpi_get_handle(NULL, node, &handle); | |
511 | - | |
512 | - if (ACPI_SUCCESS(status)) { | |
513 | - status = acpi_remove_notify_handler(handle, | |
514 | - ACPI_SYSTEM_NOTIFY, | |
515 | - eeepc_rfkill_notify); | |
516 | - if (ACPI_FAILURE(status)) | |
517 | - pr_err("Error removing rfkill notify handler %s\n", | |
518 | - node); | |
519 | - } | |
520 | -} | |
521 | - | |
522 | -static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, | |
523 | - u8 *value) | |
524 | -{ | |
525 | - int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
526 | - | |
527 | - if (result < 0) | |
528 | - return result; | |
529 | - | |
530 | - *value = !!result; | |
531 | - return 0; | |
532 | -} | |
533 | - | |
534 | -static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot) | |
535 | -{ | |
536 | - kfree(hotplug_slot->info); | |
537 | - kfree(hotplug_slot); | |
538 | -} | |
539 | - | |
540 | -static struct hotplug_slot_ops eeepc_hotplug_slot_ops = { | |
541 | - .owner = THIS_MODULE, | |
542 | - .get_adapter_status = eeepc_get_adapter_status, | |
543 | - .get_power_status = eeepc_get_adapter_status, | |
544 | -}; | |
545 | - | |
546 | -static void eeepc_hotplug_work(struct work_struct *work) | |
547 | -{ | |
548 | - struct eeepc_wmi *eeepc; | |
549 | - | |
550 | - eeepc = container_of(work, struct eeepc_wmi, hotplug_work); | |
551 | - eeepc_rfkill_hotplug(eeepc); | |
552 | -} | |
553 | - | |
554 | -static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc) | |
555 | -{ | |
556 | - int ret = -ENOMEM; | |
557 | - struct pci_bus *bus = pci_find_bus(0, 1); | |
558 | - | |
559 | - if (!bus) { | |
560 | - pr_err("Unable to find wifi PCI bus\n"); | |
561 | - return -ENODEV; | |
562 | - } | |
563 | - | |
564 | - eeepc->hotplug_workqueue = | |
565 | - create_singlethread_workqueue("hotplug_workqueue"); | |
566 | - if (!eeepc->hotplug_workqueue) | |
567 | - goto error_workqueue; | |
568 | - | |
569 | - INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work); | |
570 | - | |
571 | - eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); | |
572 | - if (!eeepc->hotplug_slot) | |
573 | - goto error_slot; | |
574 | - | |
575 | - eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info), | |
576 | - GFP_KERNEL); | |
577 | - if (!eeepc->hotplug_slot->info) | |
578 | - goto error_info; | |
579 | - | |
580 | - eeepc->hotplug_slot->private = eeepc; | |
581 | - eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug; | |
582 | - eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops; | |
583 | - eeepc_get_adapter_status(eeepc->hotplug_slot, | |
584 | - &eeepc->hotplug_slot->info->adapter_status); | |
585 | - | |
586 | - ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi"); | |
587 | - if (ret) { | |
588 | - pr_err("Unable to register hotplug slot - %d\n", ret); | |
589 | - goto error_register; | |
590 | - } | |
591 | - | |
592 | - return 0; | |
593 | - | |
594 | -error_register: | |
595 | - kfree(eeepc->hotplug_slot->info); | |
596 | -error_info: | |
597 | - kfree(eeepc->hotplug_slot); | |
598 | - eeepc->hotplug_slot = NULL; | |
599 | -error_slot: | |
600 | - destroy_workqueue(eeepc->hotplug_workqueue); | |
601 | -error_workqueue: | |
602 | - return ret; | |
603 | -} | |
604 | - | |
605 | -/* | |
606 | - * Rfkill devices | |
607 | - */ | |
608 | -static int eeepc_rfkill_set(void *data, bool blocked) | |
609 | -{ | |
610 | - int dev_id = (unsigned long)data; | |
611 | - u32 ctrl_param = !blocked; | |
612 | - acpi_status status; | |
613 | - | |
614 | - status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL); | |
615 | - | |
616 | - if (ACPI_FAILURE(status)) | |
617 | - return -EIO; | |
618 | - | |
619 | - return 0; | |
620 | -} | |
621 | - | |
622 | -static void eeepc_rfkill_query(struct rfkill *rfkill, void *data) | |
623 | -{ | |
624 | - int dev_id = (unsigned long)data; | |
625 | - int result; | |
626 | - | |
627 | - result = eeepc_wmi_get_devstate_simple(dev_id); | |
628 | - | |
629 | - if (result < 0) | |
630 | - return ; | |
631 | - | |
632 | - rfkill_set_sw_state(rfkill, !result); | |
633 | -} | |
634 | - | |
635 | -static int eeepc_rfkill_wlan_set(void *data, bool blocked) | |
636 | -{ | |
637 | - struct eeepc_wmi *eeepc = data; | |
638 | - int ret; | |
639 | - | |
640 | - /* | |
641 | - * This handler is enabled only if hotplug is enabled. | |
642 | - * In this case, the eeepc_wmi_set_devstate() will | |
643 | - * trigger a wmi notification and we need to wait | |
644 | - * this call to finish before being able to call | |
645 | - * any wmi method | |
646 | - */ | |
647 | - mutex_lock(&eeepc->wmi_lock); | |
648 | - ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked); | |
649 | - mutex_unlock(&eeepc->wmi_lock); | |
650 | - return ret; | |
651 | -} | |
652 | - | |
653 | -static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data) | |
654 | -{ | |
655 | - eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN); | |
656 | -} | |
657 | - | |
658 | -static const struct rfkill_ops eeepc_rfkill_wlan_ops = { | |
659 | - .set_block = eeepc_rfkill_wlan_set, | |
660 | - .query = eeepc_rfkill_wlan_query, | |
661 | -}; | |
662 | - | |
663 | -static const struct rfkill_ops eeepc_rfkill_ops = { | |
664 | - .set_block = eeepc_rfkill_set, | |
665 | - .query = eeepc_rfkill_query, | |
666 | -}; | |
667 | - | |
668 | -static int eeepc_new_rfkill(struct eeepc_wmi *eeepc, | |
669 | - struct rfkill **rfkill, | |
670 | - const char *name, | |
671 | - enum rfkill_type type, int dev_id) | |
672 | -{ | |
673 | - int result = eeepc_wmi_get_devstate_simple(dev_id); | |
674 | - | |
675 | - if (result < 0) | |
676 | - return result; | |
677 | - | |
678 | - if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless) | |
679 | - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | |
680 | - &eeepc_rfkill_wlan_ops, eeepc); | |
681 | - else | |
682 | - *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type, | |
683 | - &eeepc_rfkill_ops, (void *)(long)dev_id); | |
684 | - | |
685 | - if (!*rfkill) | |
686 | - return -EINVAL; | |
687 | - | |
688 | - rfkill_init_sw_state(*rfkill, !result); | |
689 | - result = rfkill_register(*rfkill); | |
690 | - if (result) { | |
691 | - rfkill_destroy(*rfkill); | |
692 | - *rfkill = NULL; | |
693 | - return result; | |
694 | - } | |
695 | - return 0; | |
696 | -} | |
697 | - | |
698 | -static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc) | |
699 | -{ | |
700 | - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | |
701 | - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | |
702 | - eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | |
703 | - if (eeepc->wlan_rfkill) { | |
704 | - rfkill_unregister(eeepc->wlan_rfkill); | |
705 | - rfkill_destroy(eeepc->wlan_rfkill); | |
706 | - eeepc->wlan_rfkill = NULL; | |
707 | - } | |
708 | - /* | |
709 | - * Refresh pci hotplug in case the rfkill state was changed after | |
710 | - * eeepc_unregister_rfkill_notifier() | |
711 | - */ | |
712 | - eeepc_rfkill_hotplug(eeepc); | |
713 | - if (eeepc->hotplug_slot) | |
714 | - pci_hp_deregister(eeepc->hotplug_slot); | |
715 | - if (eeepc->hotplug_workqueue) | |
716 | - destroy_workqueue(eeepc->hotplug_workqueue); | |
717 | - | |
718 | - if (eeepc->bluetooth_rfkill) { | |
719 | - rfkill_unregister(eeepc->bluetooth_rfkill); | |
720 | - rfkill_destroy(eeepc->bluetooth_rfkill); | |
721 | - eeepc->bluetooth_rfkill = NULL; | |
722 | - } | |
723 | - if (eeepc->wimax_rfkill) { | |
724 | - rfkill_unregister(eeepc->wimax_rfkill); | |
725 | - rfkill_destroy(eeepc->wimax_rfkill); | |
726 | - eeepc->wimax_rfkill = NULL; | |
727 | - } | |
728 | - if (eeepc->wwan3g_rfkill) { | |
729 | - rfkill_unregister(eeepc->wwan3g_rfkill); | |
730 | - rfkill_destroy(eeepc->wwan3g_rfkill); | |
731 | - eeepc->wwan3g_rfkill = NULL; | |
732 | - } | |
733 | -} | |
734 | - | |
735 | -static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc) | |
736 | -{ | |
737 | - int result = 0; | |
738 | - | |
739 | - mutex_init(&eeepc->hotplug_lock); | |
740 | - mutex_init(&eeepc->wmi_lock); | |
741 | - | |
742 | - result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill, | |
743 | - "eeepc-wlan", RFKILL_TYPE_WLAN, | |
744 | - EEEPC_WMI_DEVID_WLAN); | |
745 | - | |
746 | - if (result && result != -ENODEV) | |
747 | - goto exit; | |
748 | - | |
749 | - result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill, | |
750 | - "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH, | |
751 | - EEEPC_WMI_DEVID_BLUETOOTH); | |
752 | - | |
753 | - if (result && result != -ENODEV) | |
754 | - goto exit; | |
755 | - | |
756 | - result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill, | |
757 | - "eeepc-wimax", RFKILL_TYPE_WIMAX, | |
758 | - EEEPC_WMI_DEVID_WIMAX); | |
759 | - | |
760 | - if (result && result != -ENODEV) | |
761 | - goto exit; | |
762 | - | |
763 | - result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill, | |
764 | - "eeepc-wwan3g", RFKILL_TYPE_WWAN, | |
765 | - EEEPC_WMI_DEVID_WWAN3G); | |
766 | - | |
767 | - if (result && result != -ENODEV) | |
768 | - goto exit; | |
769 | - | |
770 | - if (!eeepc->hotplug_wireless) | |
771 | - goto exit; | |
772 | - | |
773 | - result = eeepc_setup_pci_hotplug(eeepc); | |
774 | - /* | |
775 | - * If we get -EBUSY then something else is handling the PCI hotplug - | |
776 | - * don't fail in this case | |
777 | - */ | |
778 | - if (result == -EBUSY) | |
779 | - result = 0; | |
780 | - | |
781 | - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5"); | |
782 | - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6"); | |
783 | - eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7"); | |
784 | - /* | |
785 | - * Refresh pci hotplug in case the rfkill state was changed during | |
786 | - * setup. | |
787 | - */ | |
788 | - eeepc_rfkill_hotplug(eeepc); | |
789 | - | |
790 | -exit: | |
791 | - if (result && result != -ENODEV) | |
792 | - eeepc_wmi_rfkill_exit(eeepc); | |
793 | - | |
794 | - if (result == -ENODEV) | |
795 | - result = 0; | |
796 | - | |
797 | - return result; | |
798 | -} | |
799 | - | |
800 | -/* | |
801 | - * Backlight | |
802 | - */ | |
803 | -static int read_backlight_power(void) | |
804 | -{ | |
805 | - int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT); | |
806 | - | |
807 | - if (ret < 0) | |
808 | - return ret; | |
809 | - | |
810 | - return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | |
811 | -} | |
812 | - | |
813 | -static int read_brightness(struct backlight_device *bd) | |
814 | -{ | |
815 | - u32 retval; | |
816 | - acpi_status status; | |
817 | - | |
818 | - status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval); | |
819 | - | |
820 | - if (ACPI_FAILURE(status)) | |
821 | - return -EIO; | |
822 | - else | |
823 | - return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK; | |
824 | -} | |
825 | - | |
826 | -static int update_bl_status(struct backlight_device *bd) | |
827 | -{ | |
828 | - u32 ctrl_param; | |
829 | - acpi_status status; | |
830 | - int power; | |
831 | - | |
832 | - ctrl_param = bd->props.brightness; | |
833 | - | |
834 | - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, | |
835 | - ctrl_param, NULL); | |
836 | - | |
837 | - if (ACPI_FAILURE(status)) | |
838 | - return -EIO; | |
839 | - | |
840 | - power = read_backlight_power(); | |
841 | - if (power != -ENODEV && bd->props.power != power) { | |
842 | - ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); | |
843 | - status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT, | |
844 | - ctrl_param, NULL); | |
845 | - | |
846 | - if (ACPI_FAILURE(status)) | |
847 | - return -EIO; | |
848 | - } | |
849 | - return 0; | |
850 | -} | |
851 | - | |
852 | -static const struct backlight_ops eeepc_wmi_bl_ops = { | |
853 | - .get_brightness = read_brightness, | |
854 | - .update_status = update_bl_status, | |
855 | -}; | |
856 | - | |
857 | -static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code) | |
858 | -{ | |
859 | - struct backlight_device *bd = eeepc->backlight_device; | |
860 | - int old = bd->props.brightness; | |
861 | - int new = old; | |
862 | - | |
863 | - if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | |
864 | - new = code - NOTIFY_BRNUP_MIN + 1; | |
865 | - else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX) | |
866 | - new = code - NOTIFY_BRNDOWN_MIN; | |
867 | - | |
868 | - bd->props.brightness = new; | |
869 | - backlight_update_status(bd); | |
870 | - backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY); | |
871 | - | |
872 | - return old; | |
873 | -} | |
874 | - | |
875 | -static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc) | |
876 | -{ | |
877 | - struct backlight_device *bd; | |
878 | - struct backlight_properties props; | |
879 | - int max; | |
880 | - int power; | |
881 | - | |
882 | - max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS, | |
883 | - EEEPC_WMI_DSTS_MAX_BRIGTH_MASK); | |
884 | - power = read_backlight_power(); | |
885 | - | |
886 | - if (max < 0 && power < 0) { | |
887 | - /* Try to keep the original error */ | |
888 | - if (max == -ENODEV && power == -ENODEV) | |
889 | - return -ENODEV; | |
890 | - if (max != -ENODEV) | |
891 | - return max; | |
892 | - else | |
893 | - return power; | |
894 | - } | |
895 | - if (max == -ENODEV) | |
896 | - max = 0; | |
897 | - if (power == -ENODEV) | |
898 | - power = FB_BLANK_UNBLANK; | |
899 | - | |
900 | - memset(&props, 0, sizeof(struct backlight_properties)); | |
901 | - props.max_brightness = max; | |
902 | - bd = backlight_device_register(EEEPC_WMI_FILE, | |
903 | - &eeepc->platform_device->dev, eeepc, | |
904 | - &eeepc_wmi_bl_ops, &props); | |
905 | - if (IS_ERR(bd)) { | |
906 | - pr_err("Could not register backlight device\n"); | |
907 | - return PTR_ERR(bd); | |
908 | - } | |
909 | - | |
910 | - eeepc->backlight_device = bd; | |
911 | - | |
912 | - bd->props.brightness = read_brightness(bd); | |
913 | - bd->props.power = power; | |
914 | - backlight_update_status(bd); | |
915 | - | |
916 | - return 0; | |
917 | -} | |
918 | - | |
919 | -static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc) | |
920 | -{ | |
921 | - if (eeepc->backlight_device) | |
922 | - backlight_device_unregister(eeepc->backlight_device); | |
923 | - | |
924 | - eeepc->backlight_device = NULL; | |
925 | -} | |
926 | - | |
927 | -static void eeepc_wmi_notify(u32 value, void *context) | |
928 | -{ | |
929 | - struct eeepc_wmi *eeepc = context; | |
930 | - struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; | |
931 | - union acpi_object *obj; | |
932 | - acpi_status status; | |
933 | - int code; | |
934 | - int orig_code; | |
935 | - | |
936 | - status = wmi_get_event_data(value, &response); | |
937 | - if (status != AE_OK) { | |
938 | - pr_err("bad event status 0x%x\n", status); | |
939 | - return; | |
940 | - } | |
941 | - | |
942 | - obj = (union acpi_object *)response.pointer; | |
943 | - | |
944 | - if (obj && obj->type == ACPI_TYPE_INTEGER) { | |
945 | - code = obj->integer.value; | |
946 | - orig_code = code; | |
947 | - | |
948 | - if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) | |
949 | - code = NOTIFY_BRNUP_MIN; | |
950 | - else if (code >= NOTIFY_BRNDOWN_MIN && | |
951 | - code <= NOTIFY_BRNDOWN_MAX) | |
952 | - code = NOTIFY_BRNDOWN_MIN; | |
953 | - | |
954 | - if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { | |
955 | - if (!acpi_video_backlight_support()) | |
956 | - eeepc_wmi_backlight_notify(eeepc, orig_code); | |
957 | - } | |
958 | - | |
959 | - if (!sparse_keymap_report_event(eeepc->inputdev, | |
960 | - code, 1, true)) | |
961 | - pr_info("Unknown key %x pressed\n", code); | |
962 | - } | |
963 | - | |
964 | - kfree(obj); | |
965 | -} | |
966 | - | |
967 | -/* | |
968 | - * Sys helpers | |
969 | - */ | |
970 | -static int parse_arg(const char *buf, unsigned long count, int *val) | |
971 | -{ | |
972 | - if (!count) | |
973 | - return 0; | |
974 | - if (sscanf(buf, "%i", val) != 1) | |
975 | - return -EINVAL; | |
976 | - return count; | |
977 | -} | |
978 | - | |
979 | -static ssize_t store_sys_wmi(int devid, const char *buf, size_t count) | |
980 | -{ | |
981 | - acpi_status status; | |
982 | - u32 retval; | |
983 | - int rv, value; | |
984 | - | |
985 | - value = eeepc_wmi_get_devstate_simple(devid); | |
986 | - if (value == -ENODEV) /* Check device presence */ | |
987 | - return value; | |
988 | - | |
989 | - rv = parse_arg(buf, count, &value); | |
990 | - status = eeepc_wmi_set_devstate(devid, value, &retval); | |
991 | - | |
992 | - if (ACPI_FAILURE(status)) | |
993 | - return -EIO; | |
994 | - return rv; | |
995 | -} | |
996 | - | |
997 | -static ssize_t show_sys_wmi(int devid, char *buf) | |
998 | -{ | |
999 | - int value = eeepc_wmi_get_devstate_simple(devid); | |
1000 | - | |
1001 | - if (value < 0) | |
1002 | - return value; | |
1003 | - | |
1004 | - return sprintf(buf, "%d\n", value); | |
1005 | -} | |
1006 | - | |
1007 | -#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ | |
1008 | - static ssize_t show_##_name(struct device *dev, \ | |
1009 | - struct device_attribute *attr, \ | |
1010 | - char *buf) \ | |
1011 | - { \ | |
1012 | - return show_sys_wmi(_cm, buf); \ | |
1013 | - } \ | |
1014 | - static ssize_t store_##_name(struct device *dev, \ | |
1015 | - struct device_attribute *attr, \ | |
1016 | - const char *buf, size_t count) \ | |
1017 | - { \ | |
1018 | - return store_sys_wmi(_cm, buf, count); \ | |
1019 | - } \ | |
1020 | - static struct device_attribute dev_attr_##_name = { \ | |
1021 | - .attr = { \ | |
1022 | - .name = __stringify(_name), \ | |
1023 | - .mode = _mode }, \ | |
1024 | - .show = show_##_name, \ | |
1025 | - .store = store_##_name, \ | |
1026 | - } | |
1027 | - | |
1028 | -EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD); | |
1029 | -EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA); | |
1030 | -EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER); | |
1031 | - | |
1032 | -static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, | |
1033 | - const char *buf, size_t count) | |
1034 | -{ | |
1035 | - int value; | |
1036 | - struct acpi_buffer input = { (acpi_size)sizeof(value), &value }; | |
1037 | - acpi_status status; | |
1038 | - | |
1039 | - if (!count || sscanf(buf, "%i", &value) != 1) | |
1040 | - return -EINVAL; | |
1041 | - if (value < 0 || value > 2) | |
1042 | - return -EINVAL; | |
1043 | - | |
1044 | - status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, | |
1045 | - 1, EEEPC_WMI_METHODID_CFVS, &input, NULL); | |
1046 | - | |
1047 | - if (ACPI_FAILURE(status)) | |
1048 | - return -EIO; | |
1049 | - else | |
1050 | - return count; | |
1051 | -} | |
1052 | - | |
1053 | -static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); | |
1054 | - | |
1055 | -static struct attribute *platform_attributes[] = { | |
1056 | - &dev_attr_cpufv.attr, | |
1057 | - &dev_attr_camera.attr, | |
1058 | - &dev_attr_cardr.attr, | |
1059 | - &dev_attr_touchpad.attr, | |
1060 | - NULL | |
1061 | -}; | |
1062 | - | |
1063 | -static mode_t eeepc_sysfs_is_visible(struct kobject *kobj, | |
1064 | - struct attribute *attr, | |
1065 | - int idx) | |
1066 | -{ | |
1067 | - bool supported = true; | |
1068 | - int devid = -1; | |
1069 | - | |
1070 | - if (attr == &dev_attr_camera.attr) | |
1071 | - devid = EEEPC_WMI_DEVID_CAMERA; | |
1072 | - else if (attr == &dev_attr_cardr.attr) | |
1073 | - devid = EEEPC_WMI_DEVID_CARDREADER; | |
1074 | - else if (attr == &dev_attr_touchpad.attr) | |
1075 | - devid = EEEPC_WMI_DEVID_TOUCHPAD; | |
1076 | - | |
1077 | - if (devid != -1) | |
1078 | - supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV; | |
1079 | - | |
1080 | - return supported ? attr->mode : 0; | |
1081 | -} | |
1082 | - | |
1083 | -static struct attribute_group platform_attribute_group = { | |
1084 | - .is_visible = eeepc_sysfs_is_visible, | |
1085 | - .attrs = platform_attributes | |
1086 | -}; | |
1087 | - | |
1088 | -static void eeepc_wmi_sysfs_exit(struct platform_device *device) | |
1089 | -{ | |
1090 | - sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); | |
1091 | -} | |
1092 | - | |
1093 | -static int eeepc_wmi_sysfs_init(struct platform_device *device) | |
1094 | -{ | |
1095 | - return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); | |
1096 | -} | |
1097 | - | |
1098 | -/* | |
1099 | - * Platform device | |
1100 | - */ | |
1101 | -static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc) | |
1102 | -{ | |
1103 | - return eeepc_wmi_sysfs_init(eeepc->platform_device); | |
1104 | -} | |
1105 | - | |
1106 | -static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc) | |
1107 | -{ | |
1108 | - eeepc_wmi_sysfs_exit(eeepc->platform_device); | |
1109 | -} | |
1110 | - | |
1111 | -/* | |
1112 | - * debugfs | |
1113 | - */ | |
1114 | -struct eeepc_wmi_debugfs_node { | |
1115 | - struct eeepc_wmi *eeepc; | |
1116 | - char *name; | |
1117 | - int (*show)(struct seq_file *m, void *data); | |
1118 | -}; | |
1119 | - | |
1120 | -static int show_dsts(struct seq_file *m, void *data) | |
1121 | -{ | |
1122 | - struct eeepc_wmi *eeepc = m->private; | |
1123 | - acpi_status status; | |
1124 | - u32 retval = -1; | |
1125 | - | |
1126 | - status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval); | |
1127 | - | |
1128 | - if (ACPI_FAILURE(status)) | |
1129 | - return -EIO; | |
1130 | - | |
1131 | - seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval); | |
1132 | - | |
1133 | - return 0; | |
1134 | -} | |
1135 | - | |
1136 | -static int show_devs(struct seq_file *m, void *data) | |
1137 | -{ | |
1138 | - struct eeepc_wmi *eeepc = m->private; | |
1139 | - acpi_status status; | |
1140 | - u32 retval = -1; | |
1141 | - | |
1142 | - status = eeepc_wmi_set_devstate(eeepc->debug.dev_id, | |
1143 | - eeepc->debug.ctrl_param, &retval); | |
1144 | - if (ACPI_FAILURE(status)) | |
1145 | - return -EIO; | |
1146 | - | |
1147 | - seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id, | |
1148 | - eeepc->debug.ctrl_param, retval); | |
1149 | - | |
1150 | - return 0; | |
1151 | -} | |
1152 | - | |
1153 | -static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = { | |
1154 | - { NULL, "devs", show_devs }, | |
1155 | - { NULL, "dsts", show_dsts }, | |
1156 | -}; | |
1157 | - | |
1158 | -static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file) | |
1159 | -{ | |
1160 | - struct eeepc_wmi_debugfs_node *node = inode->i_private; | |
1161 | - | |
1162 | - return single_open(file, node->show, node->eeepc); | |
1163 | -} | |
1164 | - | |
1165 | -static const struct file_operations eeepc_wmi_debugfs_io_ops = { | |
1166 | - .owner = THIS_MODULE, | |
1167 | - .open = eeepc_wmi_debugfs_open, | |
1168 | - .read = seq_read, | |
1169 | - .llseek = seq_lseek, | |
1170 | - .release = single_release, | |
1171 | -}; | |
1172 | - | |
1173 | -static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc) | |
1174 | -{ | |
1175 | - debugfs_remove_recursive(eeepc->debug.root); | |
1176 | -} | |
1177 | - | |
1178 | -static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc) | |
1179 | -{ | |
1180 | - struct dentry *dent; | |
1181 | - int i; | |
1182 | - | |
1183 | - eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL); | |
1184 | - if (!eeepc->debug.root) { | |
1185 | - pr_err("failed to create debugfs directory"); | |
1186 | - goto error_debugfs; | |
1187 | - } | |
1188 | - | |
1189 | - dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR, | |
1190 | - eeepc->debug.root, &eeepc->debug.dev_id); | |
1191 | - if (!dent) | |
1192 | - goto error_debugfs; | |
1193 | - | |
1194 | - dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR, | |
1195 | - eeepc->debug.root, &eeepc->debug.ctrl_param); | |
1196 | - if (!dent) | |
1197 | - goto error_debugfs; | |
1198 | - | |
1199 | - for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) { | |
1200 | - struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i]; | |
1201 | - | |
1202 | - node->eeepc = eeepc; | |
1203 | - dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO, | |
1204 | - eeepc->debug.root, node, | |
1205 | - &eeepc_wmi_debugfs_io_ops); | |
1206 | - if (!dent) { | |
1207 | - pr_err("failed to create debug file: %s\n", node->name); | |
1208 | - goto error_debugfs; | |
1209 | - } | |
1210 | - } | |
1211 | - | |
1212 | - return 0; | |
1213 | - | |
1214 | -error_debugfs: | |
1215 | - eeepc_wmi_debugfs_exit(eeepc); | |
1216 | - return -ENOMEM; | |
1217 | -} | |
1218 | - | |
1219 | -/* | |
1220 | - * WMI Driver | |
1221 | - */ | |
1222 | -static void eeepc_dmi_check(struct eeepc_wmi *eeepc) | |
1223 | -{ | |
1224 | - const char *model; | |
1225 | - | |
1226 | - model = dmi_get_system_info(DMI_PRODUCT_NAME); | |
1227 | - if (!model) | |
1228 | - return; | |
1229 | - | |
1230 | - /* | |
1231 | - * Whitelist for wlan hotplug | |
1232 | - * | |
1233 | - * Eeepc 1000H needs the current hotplug code to handle | |
1234 | - * Fn+F2 correctly. We may add other Eeepc here later, but | |
1235 | - * it seems that most of the laptops supported by eeepc-wmi | |
1236 | - * don't need to be on this list | |
1237 | - */ | |
1238 | - if (strcmp(model, "1000H") == 0) { | |
1239 | - eeepc->hotplug_wireless = true; | |
1240 | - pr_info("wlan hotplug enabled\n"); | |
1241 | - } | |
1242 | -} | |
1243 | - | |
1244 | -static int __init eeepc_wmi_add(struct platform_device *pdev) | |
1245 | -{ | |
1246 | - struct eeepc_wmi *eeepc; | |
1247 | - acpi_status status; | |
1248 | - int err; | |
1249 | - | |
1250 | - eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL); | |
1251 | - if (!eeepc) | |
1252 | - return -ENOMEM; | |
1253 | - | |
1254 | - eeepc->platform_device = pdev; | |
1255 | - platform_set_drvdata(eeepc->platform_device, eeepc); | |
1256 | - | |
1257 | - eeepc->hotplug_wireless = hotplug_wireless; | |
1258 | - eeepc_dmi_check(eeepc); | |
1259 | - | |
1260 | - err = eeepc_wmi_platform_init(eeepc); | |
1261 | - if (err) | |
1262 | - goto fail_platform; | |
1263 | - | |
1264 | - err = eeepc_wmi_input_init(eeepc); | |
1265 | - if (err) | |
1266 | - goto fail_input; | |
1267 | - | |
1268 | - err = eeepc_wmi_led_init(eeepc); | |
1269 | - if (err) | |
1270 | - goto fail_leds; | |
1271 | - | |
1272 | - err = eeepc_wmi_rfkill_init(eeepc); | |
1273 | - if (err) | |
1274 | - goto fail_rfkill; | |
1275 | - | |
1276 | - if (!acpi_video_backlight_support()) { | |
1277 | - err = eeepc_wmi_backlight_init(eeepc); | |
1278 | - if (err && err != -ENODEV) | |
1279 | - goto fail_backlight; | |
1280 | - } else | |
1281 | - pr_info("Backlight controlled by ACPI video driver\n"); | |
1282 | - | |
1283 | - status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID, | |
1284 | - eeepc_wmi_notify, eeepc); | |
1285 | - if (ACPI_FAILURE(status)) { | |
1286 | - pr_err("Unable to register notify handler - %d\n", | |
1287 | - status); | |
1288 | - err = -ENODEV; | |
1289 | - goto fail_wmi_handler; | |
1290 | - } | |
1291 | - | |
1292 | - err = eeepc_wmi_debugfs_init(eeepc); | |
1293 | - if (err) | |
1294 | - goto fail_debugfs; | |
1295 | - | |
1296 | - return 0; | |
1297 | - | |
1298 | -fail_debugfs: | |
1299 | - wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | |
1300 | -fail_wmi_handler: | |
1301 | - eeepc_wmi_backlight_exit(eeepc); | |
1302 | -fail_backlight: | |
1303 | - eeepc_wmi_rfkill_exit(eeepc); | |
1304 | -fail_rfkill: | |
1305 | - eeepc_wmi_led_exit(eeepc); | |
1306 | -fail_leds: | |
1307 | - eeepc_wmi_input_exit(eeepc); | |
1308 | -fail_input: | |
1309 | - eeepc_wmi_platform_exit(eeepc); | |
1310 | -fail_platform: | |
1311 | - kfree(eeepc); | |
1312 | - return err; | |
1313 | -} | |
1314 | - | |
1315 | -static int __exit eeepc_wmi_remove(struct platform_device *device) | |
1316 | -{ | |
1317 | - struct eeepc_wmi *eeepc; | |
1318 | - | |
1319 | - eeepc = platform_get_drvdata(device); | |
1320 | - wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID); | |
1321 | - eeepc_wmi_backlight_exit(eeepc); | |
1322 | - eeepc_wmi_input_exit(eeepc); | |
1323 | - eeepc_wmi_led_exit(eeepc); | |
1324 | - eeepc_wmi_rfkill_exit(eeepc); | |
1325 | - eeepc_wmi_debugfs_exit(eeepc); | |
1326 | - eeepc_wmi_platform_exit(eeepc); | |
1327 | - | |
1328 | - kfree(eeepc); | |
1329 | - return 0; | |
1330 | -} | |
1331 | - | |
1332 | -/* | |
1333 | - * Platform driver - hibernate/resume callbacks | |
1334 | - */ | |
1335 | -static int eeepc_hotk_thaw(struct device *device) | |
1336 | -{ | |
1337 | - struct eeepc_wmi *eeepc = dev_get_drvdata(device); | |
1338 | - | |
1339 | - if (eeepc->wlan_rfkill) { | |
1340 | - bool wlan; | |
1341 | - | |
1342 | - /* | |
1343 | - * Work around bios bug - acpi _PTS turns off the wireless led | |
1344 | - * during suspend. Normally it restores it on resume, but | |
1345 | - * we should kick it ourselves in case hibernation is aborted. | |
1346 | - */ | |
1347 | - wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN); | |
1348 | - eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL); | |
1349 | - } | |
1350 | - | |
1351 | - return 0; | |
1352 | -} | |
1353 | - | |
1354 | -static int eeepc_hotk_restore(struct device *device) | |
1355 | -{ | |
1356 | - struct eeepc_wmi *eeepc = dev_get_drvdata(device); | |
1357 | - int bl; | |
1358 | - | |
1359 | - /* Refresh both wlan rfkill state and pci hotplug */ | |
1360 | - if (eeepc->wlan_rfkill) | |
1361 | - eeepc_rfkill_hotplug(eeepc); | |
1362 | - | |
1363 | - if (eeepc->bluetooth_rfkill) { | |
1364 | - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH); | |
1365 | - rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl); | |
1366 | - } | |
1367 | - if (eeepc->wimax_rfkill) { | |
1368 | - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX); | |
1369 | - rfkill_set_sw_state(eeepc->wimax_rfkill, bl); | |
1370 | - } | |
1371 | - if (eeepc->wwan3g_rfkill) { | |
1372 | - bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G); | |
1373 | - rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl); | |
1374 | - } | |
1375 | - | |
1376 | - return 0; | |
1377 | -} | |
1378 | - | |
1379 | -static const struct dev_pm_ops eeepc_pm_ops = { | |
1380 | - .thaw = eeepc_hotk_thaw, | |
1381 | - .restore = eeepc_hotk_restore, | |
1382 | -}; | |
1383 | - | |
1384 | -static struct platform_driver platform_driver = { | |
1385 | - .remove = __exit_p(eeepc_wmi_remove), | |
1386 | - .driver = { | |
1387 | - .name = EEEPC_WMI_FILE, | |
1388 | - .owner = THIS_MODULE, | |
1389 | - .pm = &eeepc_pm_ops, | |
1390 | - }, | |
1391 | -}; | |
1392 | - | |
1393 | -static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level, | |
1394 | - void *context, void **retval) | |
1395 | -{ | |
1396 | - pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID); | |
1397 | - *(bool *)context = true; | |
1398 | - return AE_CTRL_TERMINATE; | |
1399 | -} | |
1400 | - | |
1401 | -static int __init eeepc_wmi_check_atkd(void) | |
1402 | -{ | |
1403 | - acpi_status status; | |
1404 | - bool found = false; | |
1405 | - | |
1406 | - status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, | |
1407 | - &found, NULL); | |
1408 | - | |
1409 | - if (ACPI_FAILURE(status) || !found) | |
1410 | - return 0; | |
1411 | - return -1; | |
1412 | -} | |
1413 | - | |
1414 | -static int __init eeepc_wmi_probe(struct platform_device *pdev) | |
1415 | -{ | |
1416 | - if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) || | |
1417 | - !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) { | |
1418 | - pr_warning("No known WMI GUID found\n"); | |
1419 | - return -ENODEV; | |
1420 | - } | |
1421 | - | |
1422 | - if (eeepc_wmi_check_atkd()) { | |
1423 | - pr_warning("WMI device present, but legacy ATKD device is also " | |
1424 | - "present and enabled."); | |
1425 | - pr_warning("You probably booted with acpi_osi=\"Linux\" or " | |
1426 | - "acpi_osi=\"!Windows 2009\""); | |
1427 | - pr_warning("Can't load eeepc-wmi, use default acpi_osi " | |
1428 | - "(preferred) or eeepc-laptop"); | |
1429 | - return -ENODEV; | |
1430 | - } | |
1431 | - | |
1432 | - return eeepc_wmi_add(pdev); | |
1433 | -} | |
1434 | - | |
1435 | -static struct platform_device *platform_device; | |
1436 | - | |
1437 | -static int __init eeepc_wmi_init(void) | |
1438 | -{ | |
1439 | - platform_device = platform_create_bundle(&platform_driver, | |
1440 | - eeepc_wmi_probe, | |
1441 | - NULL, 0, NULL, 0); | |
1442 | - if (IS_ERR(platform_device)) | |
1443 | - return PTR_ERR(platform_device); | |
1444 | - return 0; | |
1445 | -} | |
1446 | - | |
1447 | -static void __exit eeepc_wmi_exit(void) | |
1448 | -{ | |
1449 | - platform_device_unregister(platform_device); | |
1450 | - platform_driver_unregister(&platform_driver); | |
1451 | -} | |
1452 | - | |
1453 | -module_init(eeepc_wmi_init); | |
1454 | -module_exit(eeepc_wmi_exit); |