blob: aa9e1d1719da3de088bf8a095e9483d2a5e33493 [file] [log] [blame]
Yong Wangee027e42010-03-21 10:26:34 +08001/*
2 * Eee PC WMI hotkey driver
3 *
4 * Copyright(C) 2010 Intel Corporation.
Corentin Chary4c4edfa2010-11-29 08:14:11 +01005 * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
Yong Wangee027e42010-03-21 10:26:34 +08006 *
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
Yong Wang81248882010-04-11 09:26:33 +080027#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
Yong Wangee027e42010-03-21 10:26:34 +080029#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/types.h>
Tejun Heoa32f3922010-04-05 11:37:59 +090033#include <linux/slab.h>
Yong Wangee027e42010-03-21 10:26:34 +080034#include <linux/input.h>
35#include <linux/input/sparse-keymap.h>
Yong Wang3d7b1652010-04-11 09:27:54 +080036#include <linux/fb.h>
37#include <linux/backlight.h>
Corentin Chary084fca62010-11-29 08:14:06 +010038#include <linux/leds.h>
Corentin Charyba48fdb2010-11-29 08:14:07 +010039#include <linux/rfkill.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010040#include <linux/pci.h>
41#include <linux/pci_hotplug.h>
Corentin Chary8c1b2d82010-11-29 08:14:09 +010042#include <linux/debugfs.h>
43#include <linux/seq_file.h>
Yong Wang45f2c692010-04-11 09:27:19 +080044#include <linux/platform_device.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010045#include <linux/dmi.h>
Yong Wangee027e42010-03-21 10:26:34 +080046#include <acpi/acpi_bus.h>
47#include <acpi/acpi_drivers.h>
48
Yong Wang45f2c692010-04-11 09:27:19 +080049#define EEEPC_WMI_FILE "eeepc-wmi"
50
Yong Wangee027e42010-03-21 10:26:34 +080051MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
52MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
53MODULE_LICENSE("GPL");
54
Corentin Charyd358cb52010-11-29 08:14:14 +010055#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
56
Yong Wangee027e42010-03-21 10:26:34 +080057#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
Yong Wang3d7b1652010-04-11 09:27:54 +080058#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
Yong Wangee027e42010-03-21 10:26:34 +080059
60MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +080061MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
Yong Wangee027e42010-03-21 10:26:34 +080062
Corentin Chary33e0e6f2011-02-06 13:28:34 +010063#define NOTIFY_BRNUP_MIN 0x11
64#define NOTIFY_BRNUP_MAX 0x1f
65#define NOTIFY_BRNDOWN_MIN 0x20
66#define NOTIFY_BRNDOWN_MAX 0x2e
Yong Wangee027e42010-03-21 10:26:34 +080067
Corentin Chary33e0e6f2011-02-06 13:28:34 +010068#define EEEPC_WMI_METHODID_DSTS 0x53544344
69#define EEEPC_WMI_METHODID_DEVS 0x53564544
70#define EEEPC_WMI_METHODID_CFVS 0x53564643
Yong Wang3d7b1652010-04-11 09:27:54 +080071
Corentin Charyba48fdb2010-11-29 08:14:07 +010072#define EEEPC_WMI_DEVID_WLAN 0x00010011
73#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
74#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
Corentin Chary33e0e6f2011-02-06 13:28:34 +010075#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
76#define EEEPC_WMI_DEVID_TPDLED 0x00100011
Yong Wang3d7b1652010-04-11 09:27:54 +080077
Corentin Charyafa7c882011-02-06 13:28:28 +010078static bool hotplug_wireless;
79
80module_param(hotplug_wireless, bool, 0444);
81MODULE_PARM_DESC(hotplug_wireless,
82 "Enable hotplug for wireless device. "
83 "If your laptop needs that, please report to "
84 "acpi4asus-user@lists.sourceforge.net.");
85
Yong Wangee027e42010-03-21 10:26:34 +080086static const struct key_entry eeepc_wmi_keymap[] = {
87 /* Sleep already handled via generic ACPI code */
Yong Wangee027e42010-03-21 10:26:34 +080088 { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
89 { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
Corentin Chary5628e5a2011-02-06 13:28:26 +010090 { KE_KEY, 0x30, { KEY_VOLUMEUP } },
91 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
92 { KE_KEY, 0x32, { KEY_MUTE } },
93 { KE_KEY, 0x5c, { KEY_F15 } },
94 { KE_KEY, 0x5d, { KEY_WLAN } },
Chris Bagwelleda17482010-10-11 18:47:17 -050095 { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
Corentin Charybc40cce2011-02-06 13:28:27 +010096 { KE_KEY, 0x88, { KEY_WLAN } },
Corentin Chary5628e5a2011-02-06 13:28:26 +010097 { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
98 { KE_KEY, 0xe0, { KEY_PROG1 } },
Chris Bagwelleda17482010-10-11 18:47:17 -050099 { KE_KEY, 0xe1, { KEY_F14 } },
100 { KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
Yong Wangee027e42010-03-21 10:26:34 +0800101 { KE_END, 0},
102};
103
Yong Wang3d7b1652010-04-11 09:27:54 +0800104struct bios_args {
105 u32 dev_id;
106 u32 ctrl_param;
107};
108
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100109/*
110 * eeepc-wmi/ - debugfs root directory
111 * dev_id - current dev_id
112 * ctrl_param - current ctrl_param
113 * devs - call DEVS(dev_id, ctrl_param) and print result
114 * dsts - call DSTS(dev_id) and print result
115 */
116struct eeepc_wmi_debug {
117 struct dentry *root;
118 u32 dev_id;
119 u32 ctrl_param;
120};
121
Yong Wang81248882010-04-11 09:26:33 +0800122struct eeepc_wmi {
Corentin Charyafa7c882011-02-06 13:28:28 +0100123 bool hotplug_wireless;
124
Yong Wang81248882010-04-11 09:26:33 +0800125 struct input_dev *inputdev;
Yong Wang3d7b1652010-04-11 09:27:54 +0800126 struct backlight_device *backlight_device;
Corentin Chary27c136c2010-11-29 08:14:05 +0100127 struct platform_device *platform_device;
Corentin Chary084fca62010-11-29 08:14:06 +0100128
129 struct led_classdev tpd_led;
130 int tpd_led_wk;
131 struct workqueue_struct *led_workqueue;
132 struct work_struct tpd_led_work;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100133
134 struct rfkill *wlan_rfkill;
135 struct rfkill *bluetooth_rfkill;
136 struct rfkill *wwan3g_rfkill;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100137
Corentin Charyafa7c882011-02-06 13:28:28 +0100138 struct hotplug_slot *hotplug_slot;
139 struct mutex hotplug_lock;
Corentin Chary279f8f92011-02-06 13:28:29 +0100140 struct mutex wmi_lock;
141 struct workqueue_struct *hotplug_workqueue;
142 struct work_struct hotplug_work;
Corentin Charyafa7c882011-02-06 13:28:28 +0100143
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100144 struct eeepc_wmi_debug debug;
Yong Wang81248882010-04-11 09:26:33 +0800145};
146
Yong Wang81248882010-04-11 09:26:33 +0800147static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
Yong Wangee027e42010-03-21 10:26:34 +0800148{
149 int err;
150
Yong Wang81248882010-04-11 09:26:33 +0800151 eeepc->inputdev = input_allocate_device();
152 if (!eeepc->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800153 return -ENOMEM;
154
Yong Wang81248882010-04-11 09:26:33 +0800155 eeepc->inputdev->name = "Eee PC WMI hotkeys";
Yong Wang45f2c692010-04-11 09:27:19 +0800156 eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
Yong Wang81248882010-04-11 09:26:33 +0800157 eeepc->inputdev->id.bustype = BUS_HOST;
Corentin Chary27c136c2010-11-29 08:14:05 +0100158 eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
Yong Wangee027e42010-03-21 10:26:34 +0800159
Yong Wang81248882010-04-11 09:26:33 +0800160 err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800161 if (err)
162 goto err_free_dev;
163
Yong Wang81248882010-04-11 09:26:33 +0800164 err = input_register_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800165 if (err)
166 goto err_free_keymap;
167
168 return 0;
169
170err_free_keymap:
Yong Wang81248882010-04-11 09:26:33 +0800171 sparse_keymap_free(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800172err_free_dev:
Yong Wang81248882010-04-11 09:26:33 +0800173 input_free_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800174 return err;
175}
176
Yong Wang81248882010-04-11 09:26:33 +0800177static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
178{
179 if (eeepc->inputdev) {
180 sparse_keymap_free(eeepc->inputdev);
181 input_unregister_device(eeepc->inputdev);
182 }
183
184 eeepc->inputdev = NULL;
185}
186
Corentin Chary2a3f0062010-11-29 08:14:10 +0100187static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800188{
189 struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
190 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
191 union acpi_object *obj;
192 acpi_status status;
193 u32 tmp;
194
195 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
Corentin Charyafa7c882011-02-06 13:28:28 +0100196 1, EEEPC_WMI_METHODID_DSTS,
197 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800198
199 if (ACPI_FAILURE(status))
200 return status;
201
202 obj = (union acpi_object *)output.pointer;
203 if (obj && obj->type == ACPI_TYPE_INTEGER)
204 tmp = (u32)obj->integer.value;
205 else
206 tmp = 0;
207
Corentin Chary2a3f0062010-11-29 08:14:10 +0100208 if (retval)
209 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800210
211 kfree(obj);
212
213 return status;
214
215}
216
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100217static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
218 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800219{
220 struct bios_args args = {
221 .dev_id = dev_id,
222 .ctrl_param = ctrl_param,
223 };
224 struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
225 acpi_status status;
226
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100227 if (!retval) {
228 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
229 EEEPC_WMI_METHODID_DEVS,
230 &input, NULL);
231 } else {
232 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
233 union acpi_object *obj;
234 u32 tmp;
235
236 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
237 EEEPC_WMI_METHODID_DEVS,
238 &input, &output);
239
240 if (ACPI_FAILURE(status))
241 return status;
242
243 obj = (union acpi_object *)output.pointer;
244 if (obj && obj->type == ACPI_TYPE_INTEGER)
245 tmp = (u32)obj->integer.value;
246 else
247 tmp = 0;
248
249 *retval = tmp;
250
251 kfree(obj);
252 }
Yong Wang3d7b1652010-04-11 09:27:54 +0800253
254 return status;
255}
256
Corentin Chary5c956382011-02-06 13:28:31 +0100257/* Helper for special devices with magic return codes */
258static int eeepc_wmi_get_devstate_simple(u32 dev_id)
259{
260 u32 retval = 0;
261 acpi_status status;
262
263 status = eeepc_wmi_get_devstate(dev_id, &retval);
264
265 if (ACPI_FAILURE(status))
266 return -EINVAL;
267
268 /* If the device is present, DSTS will always set some bits
269 * 0x00070000 - 1110000000000000000 - device supported
270 * 0x00060000 - 1100000000000000000 - not supported
271 * 0x00020000 - 0100000000000000000 - device supported
272 * 0x00010000 - 0010000000000000000 - not supported / special mode ?
273 */
274 if (!retval || retval == 0x00060000)
275 return -ENODEV;
276
277 return retval & 0x1;
278}
279
Corentin Chary084fca62010-11-29 08:14:06 +0100280/*
281 * LEDs
282 */
283/*
284 * These functions actually update the LED's, and are called from a
285 * workqueue. By doing this as separate work rather than when the LED
286 * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
287 * potentially bad time, such as a timer interrupt.
288 */
289static void tpd_led_update(struct work_struct *work)
290{
291 int ctrl_param;
292 struct eeepc_wmi *eeepc;
293
294 eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
295
296 ctrl_param = eeepc->tpd_led_wk;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100297 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100298}
299
300static void tpd_led_set(struct led_classdev *led_cdev,
301 enum led_brightness value)
302{
303 struct eeepc_wmi *eeepc;
304
305 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
306
307 eeepc->tpd_led_wk = !!value;
308 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
309}
310
311static int read_tpd_state(struct eeepc_wmi *eeepc)
312{
Corentin Chary5c956382011-02-06 13:28:31 +0100313 return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TPDLED);
Corentin Chary084fca62010-11-29 08:14:06 +0100314}
315
316static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
317{
318 struct eeepc_wmi *eeepc;
319
320 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
321
322 return read_tpd_state(eeepc);
323}
324
325static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
326{
327 int rv;
328
329 if (read_tpd_state(eeepc) < 0)
330 return 0;
331
332 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
333 if (!eeepc->led_workqueue)
334 return -ENOMEM;
335 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
336
337 eeepc->tpd_led.name = "eeepc::touchpad";
338 eeepc->tpd_led.brightness_set = tpd_led_set;
339 eeepc->tpd_led.brightness_get = tpd_led_get;
340 eeepc->tpd_led.max_brightness = 1;
341
342 rv = led_classdev_register(&eeepc->platform_device->dev,
343 &eeepc->tpd_led);
344 if (rv) {
345 destroy_workqueue(eeepc->led_workqueue);
346 return rv;
347 }
348
349 return 0;
350}
351
352static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
353{
354 if (eeepc->tpd_led.dev)
355 led_classdev_unregister(&eeepc->tpd_led);
356 if (eeepc->led_workqueue)
357 destroy_workqueue(eeepc->led_workqueue);
358}
359
360/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100361 * PCI hotplug (for wlan rfkill)
362 */
363static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc)
364{
Corentin Chary5c956382011-02-06 13:28:31 +0100365 int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100366
Corentin Chary5c956382011-02-06 13:28:31 +0100367 if (result < 0)
Corentin Charyafa7c882011-02-06 13:28:28 +0100368 return false;
Corentin Chary5c956382011-02-06 13:28:31 +0100369 return !result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100370}
371
372static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc)
373{
374 struct pci_dev *dev;
375 struct pci_bus *bus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100376 bool blocked;
Corentin Charyafa7c882011-02-06 13:28:28 +0100377 bool absent;
378 u32 l;
379
Corentin Chary279f8f92011-02-06 13:28:29 +0100380 mutex_lock(&eeepc->wmi_lock);
381 blocked = eeepc_wlan_rfkill_blocked(eeepc);
382 mutex_unlock(&eeepc->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100383
384 mutex_lock(&eeepc->hotplug_lock);
385
Corentin Chary279f8f92011-02-06 13:28:29 +0100386 if (eeepc->wlan_rfkill)
387 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
388
Corentin Charyafa7c882011-02-06 13:28:28 +0100389 if (eeepc->hotplug_slot) {
390 bus = pci_find_bus(0, 1);
391 if (!bus) {
392 pr_warning("Unable to find PCI bus 1?\n");
393 goto out_unlock;
394 }
395
396 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
397 pr_err("Unable to read PCI config space?\n");
398 goto out_unlock;
399 }
400 absent = (l == 0xffffffff);
401
402 if (blocked != absent) {
403 pr_warning("BIOS says wireless lan is %s, "
404 "but the pci device is %s\n",
405 blocked ? "blocked" : "unblocked",
406 absent ? "absent" : "present");
407 pr_warning("skipped wireless hotplug as probably "
408 "inappropriate for this model\n");
409 goto out_unlock;
410 }
411
412 if (!blocked) {
413 dev = pci_get_slot(bus, 0);
414 if (dev) {
415 /* Device already present */
416 pci_dev_put(dev);
417 goto out_unlock;
418 }
419 dev = pci_scan_single_device(bus, 0);
420 if (dev) {
421 pci_bus_assign_resources(bus);
422 if (pci_bus_add_device(dev))
423 pr_err("Unable to hotplug wifi\n");
424 }
425 } else {
426 dev = pci_get_slot(bus, 0);
427 if (dev) {
428 pci_remove_bus_device(dev);
429 pci_dev_put(dev);
430 }
431 }
432 }
433
434out_unlock:
435 mutex_unlock(&eeepc->hotplug_lock);
436}
437
438static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
439{
440 struct eeepc_wmi *eeepc = data;
441
442 if (event != ACPI_NOTIFY_BUS_CHECK)
443 return;
444
Corentin Chary279f8f92011-02-06 13:28:29 +0100445 /*
446 * We can't call directly eeepc_rfkill_hotplug because most
447 * of the time WMBC is still being executed and not reetrant.
448 * There is currently no way to tell ACPICA that we want this
449 * method to be serialized, we schedule a eeepc_rfkill_hotplug
450 * call later, in a safer context.
451 */
452 queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work);
Corentin Charyafa7c882011-02-06 13:28:28 +0100453}
454
455static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc,
456 char *node)
457{
458 acpi_status status;
459 acpi_handle handle;
460
461 status = acpi_get_handle(NULL, node, &handle);
462
463 if (ACPI_SUCCESS(status)) {
464 status = acpi_install_notify_handler(handle,
465 ACPI_SYSTEM_NOTIFY,
466 eeepc_rfkill_notify,
467 eeepc);
468 if (ACPI_FAILURE(status))
469 pr_warning("Failed to register notify on %s\n", node);
470 } else
471 return -ENODEV;
472
473 return 0;
474}
475
476static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc,
477 char *node)
478{
479 acpi_status status = AE_OK;
480 acpi_handle handle;
481
482 status = acpi_get_handle(NULL, node, &handle);
483
484 if (ACPI_SUCCESS(status)) {
485 status = acpi_remove_notify_handler(handle,
486 ACPI_SYSTEM_NOTIFY,
487 eeepc_rfkill_notify);
488 if (ACPI_FAILURE(status))
489 pr_err("Error removing rfkill notify handler %s\n",
490 node);
491 }
492}
493
494static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
495 u8 *value)
496{
Corentin Chary5c956382011-02-06 13:28:31 +0100497 int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100498
Corentin Chary5c956382011-02-06 13:28:31 +0100499 if (result < 0)
500 return result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100501
Corentin Chary5c956382011-02-06 13:28:31 +0100502 *value = !!result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100503 return 0;
504}
505
506static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
507{
508 kfree(hotplug_slot->info);
509 kfree(hotplug_slot);
510}
511
512static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
513 .owner = THIS_MODULE,
514 .get_adapter_status = eeepc_get_adapter_status,
515 .get_power_status = eeepc_get_adapter_status,
516};
517
Corentin Chary279f8f92011-02-06 13:28:29 +0100518static void eeepc_hotplug_work(struct work_struct *work)
519{
520 struct eeepc_wmi *eeepc;
521
522 eeepc = container_of(work, struct eeepc_wmi, hotplug_work);
523 eeepc_rfkill_hotplug(eeepc);
524}
525
Corentin Charyafa7c882011-02-06 13:28:28 +0100526static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc)
527{
528 int ret = -ENOMEM;
529 struct pci_bus *bus = pci_find_bus(0, 1);
530
531 if (!bus) {
532 pr_err("Unable to find wifi PCI bus\n");
533 return -ENODEV;
534 }
535
Corentin Chary279f8f92011-02-06 13:28:29 +0100536 eeepc->hotplug_workqueue =
537 create_singlethread_workqueue("hotplug_workqueue");
538 if (!eeepc->hotplug_workqueue)
539 goto error_workqueue;
540
541 INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work);
542
Corentin Charyafa7c882011-02-06 13:28:28 +0100543 eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
544 if (!eeepc->hotplug_slot)
545 goto error_slot;
546
547 eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
548 GFP_KERNEL);
549 if (!eeepc->hotplug_slot->info)
550 goto error_info;
551
552 eeepc->hotplug_slot->private = eeepc;
553 eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
554 eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
555 eeepc_get_adapter_status(eeepc->hotplug_slot,
556 &eeepc->hotplug_slot->info->adapter_status);
557
558 ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
559 if (ret) {
560 pr_err("Unable to register hotplug slot - %d\n", ret);
561 goto error_register;
562 }
563
564 return 0;
565
566error_register:
567 kfree(eeepc->hotplug_slot->info);
568error_info:
569 kfree(eeepc->hotplug_slot);
570 eeepc->hotplug_slot = NULL;
571error_slot:
Corentin Chary279f8f92011-02-06 13:28:29 +0100572 destroy_workqueue(eeepc->hotplug_workqueue);
573error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100574 return ret;
575}
576
577/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100578 * Rfkill devices
579 */
580static int eeepc_rfkill_set(void *data, bool blocked)
581{
582 int dev_id = (unsigned long)data;
583 u32 ctrl_param = !blocked;
Corentin Chary7898cf12011-02-06 13:28:30 +0100584 acpi_status status;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100585
Corentin Chary7898cf12011-02-06 13:28:30 +0100586 status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
587
588 if (ACPI_FAILURE(status))
589 return -EIO;
590
591 return 0;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100592}
593
594static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
595{
596 int dev_id = (unsigned long)data;
Corentin Chary5c956382011-02-06 13:28:31 +0100597 int result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100598
Corentin Chary5c956382011-02-06 13:28:31 +0100599 result = eeepc_wmi_get_devstate_simple(dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100600
Corentin Chary5c956382011-02-06 13:28:31 +0100601 if (result < 0)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100602 return ;
603
Corentin Chary5c956382011-02-06 13:28:31 +0100604 rfkill_set_sw_state(rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100605}
606
Corentin Chary279f8f92011-02-06 13:28:29 +0100607static int eeepc_rfkill_wlan_set(void *data, bool blocked)
608{
609 struct eeepc_wmi *eeepc = data;
610 int ret;
611
612 /*
613 * This handler is enabled only if hotplug is enabled.
614 * In this case, the eeepc_wmi_set_devstate() will
615 * trigger a wmi notification and we need to wait
616 * this call to finish before being able to call
617 * any wmi method
618 */
619 mutex_lock(&eeepc->wmi_lock);
620 ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked);
621 mutex_unlock(&eeepc->wmi_lock);
622 return ret;
623}
624
625static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data)
626{
627 eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN);
628}
629
630static const struct rfkill_ops eeepc_rfkill_wlan_ops = {
631 .set_block = eeepc_rfkill_wlan_set,
632 .query = eeepc_rfkill_wlan_query,
633};
634
Corentin Charyba48fdb2010-11-29 08:14:07 +0100635static const struct rfkill_ops eeepc_rfkill_ops = {
636 .set_block = eeepc_rfkill_set,
637 .query = eeepc_rfkill_query,
638};
639
640static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
641 struct rfkill **rfkill,
642 const char *name,
643 enum rfkill_type type, int dev_id)
644{
Corentin Chary5c956382011-02-06 13:28:31 +0100645 int result = eeepc_wmi_get_devstate_simple(dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100646
Corentin Chary5c956382011-02-06 13:28:31 +0100647 if (result < 0)
648 return result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100649
Corentin Chary279f8f92011-02-06 13:28:29 +0100650 if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless)
651 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
652 &eeepc_rfkill_wlan_ops, eeepc);
653 else
654 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
655 &eeepc_rfkill_ops, (void *)(long)dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100656
657 if (!*rfkill)
658 return -EINVAL;
659
Corentin Chary5c956382011-02-06 13:28:31 +0100660 rfkill_init_sw_state(*rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100661 result = rfkill_register(*rfkill);
662 if (result) {
663 rfkill_destroy(*rfkill);
664 *rfkill = NULL;
665 return result;
666 }
667 return 0;
668}
669
670static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
671{
Corentin Charyafa7c882011-02-06 13:28:28 +0100672 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
673 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
674 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Corentin Charyba48fdb2010-11-29 08:14:07 +0100675 if (eeepc->wlan_rfkill) {
676 rfkill_unregister(eeepc->wlan_rfkill);
677 rfkill_destroy(eeepc->wlan_rfkill);
678 eeepc->wlan_rfkill = NULL;
679 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100680 /*
681 * Refresh pci hotplug in case the rfkill state was changed after
682 * eeepc_unregister_rfkill_notifier()
683 */
684 eeepc_rfkill_hotplug(eeepc);
685 if (eeepc->hotplug_slot)
686 pci_hp_deregister(eeepc->hotplug_slot);
Corentin Chary279f8f92011-02-06 13:28:29 +0100687 if (eeepc->hotplug_workqueue)
688 destroy_workqueue(eeepc->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +0100689
Corentin Charyba48fdb2010-11-29 08:14:07 +0100690 if (eeepc->bluetooth_rfkill) {
691 rfkill_unregister(eeepc->bluetooth_rfkill);
692 rfkill_destroy(eeepc->bluetooth_rfkill);
693 eeepc->bluetooth_rfkill = NULL;
694 }
695 if (eeepc->wwan3g_rfkill) {
696 rfkill_unregister(eeepc->wwan3g_rfkill);
697 rfkill_destroy(eeepc->wwan3g_rfkill);
698 eeepc->wwan3g_rfkill = NULL;
699 }
700}
701
702static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
703{
704 int result = 0;
705
Corentin Charyafa7c882011-02-06 13:28:28 +0100706 mutex_init(&eeepc->hotplug_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100707 mutex_init(&eeepc->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100708
Corentin Charyba48fdb2010-11-29 08:14:07 +0100709 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
710 "eeepc-wlan", RFKILL_TYPE_WLAN,
711 EEEPC_WMI_DEVID_WLAN);
712
713 if (result && result != -ENODEV)
714 goto exit;
715
716 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
717 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
718 EEEPC_WMI_DEVID_BLUETOOTH);
719
720 if (result && result != -ENODEV)
721 goto exit;
722
723 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
724 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
725 EEEPC_WMI_DEVID_WWAN3G);
726
727 if (result && result != -ENODEV)
728 goto exit;
729
Corentin Charyafa7c882011-02-06 13:28:28 +0100730 result = eeepc_setup_pci_hotplug(eeepc);
731 /*
732 * If we get -EBUSY then something else is handling the PCI hotplug -
733 * don't fail in this case
734 */
735 if (result == -EBUSY)
736 result = 0;
737
738 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
739 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
740 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
741 /*
742 * Refresh pci hotplug in case the rfkill state was changed during
743 * setup.
744 */
745 eeepc_rfkill_hotplug(eeepc);
746
Corentin Charyba48fdb2010-11-29 08:14:07 +0100747exit:
748 if (result && result != -ENODEV)
749 eeepc_wmi_rfkill_exit(eeepc);
750
751 if (result == -ENODEV)
752 result = 0;
753
754 return result;
755}
756
757/*
Corentin Chary084fca62010-11-29 08:14:06 +0100758 * Backlight
759 */
Yong Wang3d7b1652010-04-11 09:27:54 +0800760static int read_brightness(struct backlight_device *bd)
761{
Corentin Charydfed65d2010-11-29 08:14:12 +0100762 u32 retval;
Yong Wang3d7b1652010-04-11 09:27:54 +0800763 acpi_status status;
764
Corentin Chary2a3f0062010-11-29 08:14:10 +0100765 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800766
767 if (ACPI_FAILURE(status))
768 return -1;
769 else
Corentin Chary2a3f0062010-11-29 08:14:10 +0100770 return retval & 0xFF;
Yong Wang3d7b1652010-04-11 09:27:54 +0800771}
772
773static int update_bl_status(struct backlight_device *bd)
774{
775
Corentin Charydfed65d2010-11-29 08:14:12 +0100776 u32 ctrl_param;
Yong Wang3d7b1652010-04-11 09:27:54 +0800777 acpi_status status;
778
779 ctrl_param = bd->props.brightness;
780
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100781 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
782 ctrl_param, NULL);
Yong Wang3d7b1652010-04-11 09:27:54 +0800783
784 if (ACPI_FAILURE(status))
785 return -1;
786 else
787 return 0;
788}
789
790static const struct backlight_ops eeepc_wmi_bl_ops = {
791 .get_brightness = read_brightness,
792 .update_status = update_bl_status,
793};
794
795static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
796{
797 struct backlight_device *bd = eeepc->backlight_device;
798 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +0200799 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +0800800
801 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
802 new = code - NOTIFY_BRNUP_MIN + 1;
803 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
804 new = code - NOTIFY_BRNDOWN_MIN;
805
806 bd->props.brightness = new;
807 backlight_update_status(bd);
808 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
809
810 return old;
811}
812
813static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
814{
815 struct backlight_device *bd;
816 struct backlight_properties props;
817
818 memset(&props, 0, sizeof(struct backlight_properties));
819 props.max_brightness = 15;
820 bd = backlight_device_register(EEEPC_WMI_FILE,
Corentin Chary27c136c2010-11-29 08:14:05 +0100821 &eeepc->platform_device->dev, eeepc,
Yong Wang3d7b1652010-04-11 09:27:54 +0800822 &eeepc_wmi_bl_ops, &props);
823 if (IS_ERR(bd)) {
824 pr_err("Could not register backlight device\n");
825 return PTR_ERR(bd);
826 }
827
828 eeepc->backlight_device = bd;
829
830 bd->props.brightness = read_brightness(bd);
831 bd->props.power = FB_BLANK_UNBLANK;
832 backlight_update_status(bd);
833
834 return 0;
835}
836
837static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
838{
839 if (eeepc->backlight_device)
840 backlight_device_unregister(eeepc->backlight_device);
841
842 eeepc->backlight_device = NULL;
843}
844
845static void eeepc_wmi_notify(u32 value, void *context)
846{
847 struct eeepc_wmi *eeepc = context;
848 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
849 union acpi_object *obj;
850 acpi_status status;
851 int code;
852 int orig_code;
853
854 status = wmi_get_event_data(value, &response);
855 if (status != AE_OK) {
856 pr_err("bad event status 0x%x\n", status);
857 return;
858 }
859
860 obj = (union acpi_object *)response.pointer;
861
862 if (obj && obj->type == ACPI_TYPE_INTEGER) {
863 code = obj->integer.value;
864 orig_code = code;
865
866 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
867 code = NOTIFY_BRNUP_MIN;
868 else if (code >= NOTIFY_BRNDOWN_MIN &&
869 code <= NOTIFY_BRNDOWN_MAX)
870 code = NOTIFY_BRNDOWN_MIN;
871
872 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
873 if (!acpi_video_backlight_support())
874 eeepc_wmi_backlight_notify(eeepc, orig_code);
875 }
876
877 if (!sparse_keymap_report_event(eeepc->inputdev,
878 code, 1, true))
879 pr_info("Unknown key %x pressed\n", code);
880 }
881
882 kfree(obj);
883}
884
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -0700885static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
886 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -0500887{
888 int value;
889 struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
890 acpi_status status;
891
892 if (!count || sscanf(buf, "%i", &value) != 1)
893 return -EINVAL;
894 if (value < 0 || value > 2)
895 return -EINVAL;
896
897 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
898 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
899
900 if (ACPI_FAILURE(status))
901 return -EIO;
902 else
903 return count;
904}
905
906static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
907
Corentin Chary4e37b422010-11-29 08:14:08 +0100908static struct attribute *platform_attributes[] = {
909 &dev_attr_cpufv.attr,
910 NULL
911};
912
913static struct attribute_group platform_attribute_group = {
914 .attrs = platform_attributes
915};
916
Chris Bagwell7f80d732010-10-11 18:47:18 -0500917static void eeepc_wmi_sysfs_exit(struct platform_device *device)
918{
Corentin Chary4e37b422010-11-29 08:14:08 +0100919 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500920}
921
922static int eeepc_wmi_sysfs_init(struct platform_device *device)
923{
Corentin Chary4e37b422010-11-29 08:14:08 +0100924 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500925}
926
Corentin Chary27c136c2010-11-29 08:14:05 +0100927/*
928 * Platform device
929 */
930static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
931{
Corentin Charya04ce292011-02-06 13:28:33 +0100932 return eeepc_wmi_sysfs_init(eeepc->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +0100933}
934
935static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
936{
937 eeepc_wmi_sysfs_exit(eeepc->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +0100938}
939
940/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100941 * debugfs
942 */
943struct eeepc_wmi_debugfs_node {
944 struct eeepc_wmi *eeepc;
945 char *name;
946 int (*show)(struct seq_file *m, void *data);
947};
948
949static int show_dsts(struct seq_file *m, void *data)
950{
951 struct eeepc_wmi *eeepc = m->private;
952 acpi_status status;
953 u32 retval = -1;
954
955 status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
956
957 if (ACPI_FAILURE(status))
958 return -EIO;
959
960 seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
961
962 return 0;
963}
964
965static int show_devs(struct seq_file *m, void *data)
966{
967 struct eeepc_wmi *eeepc = m->private;
968 acpi_status status;
969 u32 retval = -1;
970
971 status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
972 eeepc->debug.ctrl_param, &retval);
973 if (ACPI_FAILURE(status))
974 return -EIO;
975
976 seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
977 eeepc->debug.ctrl_param, retval);
978
979 return 0;
980}
981
982static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
983 { NULL, "devs", show_devs },
984 { NULL, "dsts", show_dsts },
985};
986
987static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
988{
989 struct eeepc_wmi_debugfs_node *node = inode->i_private;
990
991 return single_open(file, node->show, node->eeepc);
992}
993
994static const struct file_operations eeepc_wmi_debugfs_io_ops = {
995 .owner = THIS_MODULE,
996 .open = eeepc_wmi_debugfs_open,
997 .read = seq_read,
998 .llseek = seq_lseek,
999 .release = single_release,
1000};
1001
1002static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
1003{
1004 debugfs_remove_recursive(eeepc->debug.root);
1005}
1006
1007static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
1008{
1009 struct dentry *dent;
1010 int i;
1011
1012 eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
1013 if (!eeepc->debug.root) {
1014 pr_err("failed to create debugfs directory");
1015 goto error_debugfs;
1016 }
1017
1018 dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
1019 eeepc->debug.root, &eeepc->debug.dev_id);
1020 if (!dent)
1021 goto error_debugfs;
1022
1023 dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
1024 eeepc->debug.root, &eeepc->debug.ctrl_param);
1025 if (!dent)
1026 goto error_debugfs;
1027
1028 for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
1029 struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
1030
1031 node->eeepc = eeepc;
1032 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1033 eeepc->debug.root, node,
1034 &eeepc_wmi_debugfs_io_ops);
1035 if (!dent) {
1036 pr_err("failed to create debug file: %s\n", node->name);
1037 goto error_debugfs;
1038 }
1039 }
1040
1041 return 0;
1042
1043error_debugfs:
1044 eeepc_wmi_debugfs_exit(eeepc);
1045 return -ENOMEM;
1046}
1047
1048/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001049 * WMI Driver
1050 */
Corentin Charyafa7c882011-02-06 13:28:28 +01001051static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
1052{
1053 const char *model;
1054
1055 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1056 if (!model)
1057 return;
1058
1059 /*
1060 * Whitelist for wlan hotplug
1061 *
1062 * Eeepc 1000H needs the current hotplug code to handle
1063 * Fn+F2 correctly. We may add other Eeepc here later, but
1064 * it seems that most of the laptops supported by eeepc-wmi
1065 * don't need to be on this list
1066 */
1067 if (strcmp(model, "1000H") == 0) {
1068 eeepc->hotplug_wireless = true;
1069 pr_info("wlan hotplug enabled\n");
1070 }
1071}
1072
Corentin Charya04ce292011-02-06 13:28:33 +01001073static int __init eeepc_wmi_add(struct platform_device *pdev)
Yong Wangee027e42010-03-21 10:26:34 +08001074{
Yong Wang45f2c692010-04-11 09:27:19 +08001075 struct eeepc_wmi *eeepc;
Yong Wangee027e42010-03-21 10:26:34 +08001076 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001077 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001078
Corentin Chary27c136c2010-11-29 08:14:05 +01001079 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
1080 if (!eeepc)
Corentin Charya04ce292011-02-06 13:28:33 +01001081 return -ENOMEM;
1082
1083 eeepc->platform_device = pdev;
1084 platform_set_drvdata(eeepc->platform_device, eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001085
Corentin Charyafa7c882011-02-06 13:28:28 +01001086 eeepc->hotplug_wireless = hotplug_wireless;
1087 eeepc_dmi_check(eeepc);
1088
Corentin Chary27c136c2010-11-29 08:14:05 +01001089 err = eeepc_wmi_platform_init(eeepc);
1090 if (err)
1091 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001092
1093 err = eeepc_wmi_input_init(eeepc);
1094 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001095 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001096
Corentin Chary084fca62010-11-29 08:14:06 +01001097 err = eeepc_wmi_led_init(eeepc);
1098 if (err)
1099 goto fail_leds;
1100
Corentin Charyba48fdb2010-11-29 08:14:07 +01001101 err = eeepc_wmi_rfkill_init(eeepc);
1102 if (err)
1103 goto fail_rfkill;
1104
Yong Wang3d7b1652010-04-11 09:27:54 +08001105 if (!acpi_video_backlight_support()) {
1106 err = eeepc_wmi_backlight_init(eeepc);
1107 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001108 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001109 } else
1110 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001111
1112 status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
Corentin Chary27c136c2010-11-29 08:14:05 +01001113 eeepc_wmi_notify, eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001114 if (ACPI_FAILURE(status)) {
1115 pr_err("Unable to register notify handler - %d\n",
1116 status);
1117 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001118 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001119 }
1120
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001121 err = eeepc_wmi_debugfs_init(eeepc);
1122 if (err)
1123 goto fail_debugfs;
1124
Corentin Charya04ce292011-02-06 13:28:33 +01001125 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001126
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001127fail_debugfs:
1128 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Corentin Chary27c136c2010-11-29 08:14:05 +01001129fail_wmi_handler:
Yong Wang3d7b1652010-04-11 09:27:54 +08001130 eeepc_wmi_backlight_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001131fail_backlight:
Corentin Charyba48fdb2010-11-29 08:14:07 +01001132 eeepc_wmi_rfkill_exit(eeepc);
1133fail_rfkill:
Corentin Chary084fca62010-11-29 08:14:06 +01001134 eeepc_wmi_led_exit(eeepc);
1135fail_leds:
Yong Wang45f2c692010-04-11 09:27:19 +08001136 eeepc_wmi_input_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001137fail_input:
1138 eeepc_wmi_platform_exit(eeepc);
1139fail_platform:
1140 kfree(eeepc);
Corentin Charya04ce292011-02-06 13:28:33 +01001141 return err;
Yong Wang45f2c692010-04-11 09:27:19 +08001142}
1143
Corentin Charya04ce292011-02-06 13:28:33 +01001144static int __exit eeepc_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001145{
1146 struct eeepc_wmi *eeepc;
1147
1148 eeepc = platform_get_drvdata(device);
1149 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +08001150 eeepc_wmi_backlight_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001151 eeepc_wmi_input_exit(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +01001152 eeepc_wmi_led_exit(eeepc);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001153 eeepc_wmi_rfkill_exit(eeepc);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001154 eeepc_wmi_debugfs_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001155 eeepc_wmi_platform_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001156
Corentin Chary27c136c2010-11-29 08:14:05 +01001157 kfree(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001158 return 0;
1159}
1160
Corentin Chary0773d7f2011-02-06 13:28:32 +01001161/*
1162 * Platform driver - hibernate/resume callbacks
1163 */
1164static int eeepc_hotk_thaw(struct device *device)
1165{
1166 struct eeepc_wmi *eeepc = dev_get_drvdata(device);
1167
1168 if (eeepc->wlan_rfkill) {
1169 bool wlan;
1170
1171 /*
1172 * Work around bios bug - acpi _PTS turns off the wireless led
1173 * during suspend. Normally it restores it on resume, but
1174 * we should kick it ourselves in case hibernation is aborted.
1175 */
1176 wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
1177 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL);
1178 }
1179
1180 return 0;
1181}
1182
1183static int eeepc_hotk_restore(struct device *device)
1184{
1185 struct eeepc_wmi *eeepc = dev_get_drvdata(device);
1186 int bl;
1187
1188 /* Refresh both wlan rfkill state and pci hotplug */
1189 if (eeepc->wlan_rfkill)
1190 eeepc_rfkill_hotplug(eeepc);
1191
1192 if (eeepc->bluetooth_rfkill) {
1193 bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH);
1194 rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl);
1195}
1196 if (eeepc->wwan3g_rfkill) {
1197 bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G);
1198 rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl);
1199 }
1200
1201 return 0;
1202}
1203
1204static const struct dev_pm_ops eeepc_pm_ops = {
1205 .thaw = eeepc_hotk_thaw,
1206 .restore = eeepc_hotk_restore,
1207};
1208
Yong Wang45f2c692010-04-11 09:27:19 +08001209static struct platform_driver platform_driver = {
Corentin Charya04ce292011-02-06 13:28:33 +01001210 .remove = __exit_p(eeepc_wmi_remove),
Yong Wang45f2c692010-04-11 09:27:19 +08001211 .driver = {
1212 .name = EEEPC_WMI_FILE,
1213 .owner = THIS_MODULE,
Corentin Chary0773d7f2011-02-06 13:28:32 +01001214 .pm = &eeepc_pm_ops,
Yong Wang45f2c692010-04-11 09:27:19 +08001215 },
Yong Wang45f2c692010-04-11 09:27:19 +08001216};
1217
Corentin Charyd358cb52010-11-29 08:14:14 +01001218static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
1219 void *context, void **retval)
1220{
1221 pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
1222 *(bool *)context = true;
1223 return AE_CTRL_TERMINATE;
1224}
1225
1226static int __init eeepc_wmi_check_atkd(void)
1227{
1228 acpi_status status;
1229 bool found = false;
1230
1231 status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
1232 &found, NULL);
1233
1234 if (ACPI_FAILURE(status) || !found)
1235 return 0;
1236 return -1;
1237}
1238
Corentin Charya04ce292011-02-06 13:28:33 +01001239static int __init eeepc_wmi_probe(struct platform_device *pdev)
Yong Wang45f2c692010-04-11 09:27:19 +08001240{
Yong Wang3d7b1652010-04-11 09:27:54 +08001241 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
1242 !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
Yong Wang81248882010-04-11 09:26:33 +08001243 pr_warning("No known WMI GUID found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001244 return -ENODEV;
1245 }
1246
Corentin Charyd358cb52010-11-29 08:14:14 +01001247 if (eeepc_wmi_check_atkd()) {
1248 pr_warning("WMI device present, but legacy ATKD device is also "
1249 "present and enabled.");
1250 pr_warning("You probably booted with acpi_osi=\"Linux\" or "
1251 "acpi_osi=\"!Windows 2009\"");
1252 pr_warning("Can't load eeepc-wmi, use default acpi_osi "
1253 "(preferred) or eeepc-laptop");
1254 return -ENODEV;
1255 }
1256
Corentin Charya04ce292011-02-06 13:28:33 +01001257 return eeepc_wmi_add(pdev);
1258}
Yong Wangee027e42010-03-21 10:26:34 +08001259
Corentin Charya04ce292011-02-06 13:28:33 +01001260static struct platform_device *platform_device;
Yong Wangee027e42010-03-21 10:26:34 +08001261
Corentin Charya04ce292011-02-06 13:28:33 +01001262static int __init eeepc_wmi_init(void)
1263{
1264 platform_device = platform_create_bundle(&platform_driver,
1265 eeepc_wmi_probe,
1266 NULL, 0, NULL, 0);
1267 if (IS_ERR(platform_device))
1268 return PTR_ERR(platform_device);
Yong Wangee027e42010-03-21 10:26:34 +08001269 return 0;
1270}
1271
1272static void __exit eeepc_wmi_exit(void)
1273{
Corentin Charya04ce292011-02-06 13:28:33 +01001274 platform_device_unregister(platform_device);
Yong Wang45f2c692010-04-11 09:27:19 +08001275 platform_driver_unregister(&platform_driver);
Yong Wangee027e42010-03-21 10:26:34 +08001276}
1277
1278module_init(eeepc_wmi_init);
1279module_exit(eeepc_wmi_exit);