blob: 01bc2b3da98a95ee123423de905144a99bd03e09 [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
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
Yong Wang3d7b1652010-04-11 09:27:54 +080068#define EEEPC_WMI_METHODID_DEVS 0x53564544
69#define EEEPC_WMI_METHODID_DSTS 0x53544344
Chris Bagwell7f80d732010-10-11 18:47:18 -050070#define EEEPC_WMI_METHODID_CFVS 0x53564643
Yong Wang3d7b1652010-04-11 09:27:54 +080071
72#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
Corentin Chary084fca62010-11-29 08:14:06 +010073#define EEEPC_WMI_DEVID_TPDLED 0x00100011
Corentin Charyba48fdb2010-11-29 08:14:07 +010074#define EEEPC_WMI_DEVID_WLAN 0x00010011
75#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
76#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
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;
140
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100141 struct eeepc_wmi_debug debug;
Yong Wang81248882010-04-11 09:26:33 +0800142};
143
Corentin Chary27c136c2010-11-29 08:14:05 +0100144/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
Yong Wang45f2c692010-04-11 09:27:19 +0800145static struct platform_device *platform_device;
Yong Wangee027e42010-03-21 10:26:34 +0800146
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 Chary084fca62010-11-29 08:14:06 +0100257/*
258 * LEDs
259 */
260/*
261 * These functions actually update the LED's, and are called from a
262 * workqueue. By doing this as separate work rather than when the LED
263 * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
264 * potentially bad time, such as a timer interrupt.
265 */
266static void tpd_led_update(struct work_struct *work)
267{
268 int ctrl_param;
269 struct eeepc_wmi *eeepc;
270
271 eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
272
273 ctrl_param = eeepc->tpd_led_wk;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100274 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100275}
276
277static void tpd_led_set(struct led_classdev *led_cdev,
278 enum led_brightness value)
279{
280 struct eeepc_wmi *eeepc;
281
282 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
283
284 eeepc->tpd_led_wk = !!value;
285 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
286}
287
288static int read_tpd_state(struct eeepc_wmi *eeepc)
289{
Corentin Charydfed65d2010-11-29 08:14:12 +0100290 u32 retval;
Corentin Chary084fca62010-11-29 08:14:06 +0100291 acpi_status status;
292
Corentin Chary2a3f0062010-11-29 08:14:10 +0100293 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
Corentin Chary084fca62010-11-29 08:14:06 +0100294
295 if (ACPI_FAILURE(status))
296 return -1;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100297 else if (!retval || retval == 0x00060000)
Corentin Chary084fca62010-11-29 08:14:06 +0100298 /*
299 * if touchpad led is present, DSTS will set some bits,
300 * usually 0x00020000.
301 * 0x00060000 means that the device is not supported
302 */
303 return -ENODEV;
304 else
305 /* Status is stored in the first bit */
Corentin Chary2a3f0062010-11-29 08:14:10 +0100306 return retval & 0x1;
Corentin Chary084fca62010-11-29 08:14:06 +0100307}
308
309static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
310{
311 struct eeepc_wmi *eeepc;
312
313 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
314
315 return read_tpd_state(eeepc);
316}
317
318static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
319{
320 int rv;
321
322 if (read_tpd_state(eeepc) < 0)
323 return 0;
324
325 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
326 if (!eeepc->led_workqueue)
327 return -ENOMEM;
328 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
329
330 eeepc->tpd_led.name = "eeepc::touchpad";
331 eeepc->tpd_led.brightness_set = tpd_led_set;
332 eeepc->tpd_led.brightness_get = tpd_led_get;
333 eeepc->tpd_led.max_brightness = 1;
334
335 rv = led_classdev_register(&eeepc->platform_device->dev,
336 &eeepc->tpd_led);
337 if (rv) {
338 destroy_workqueue(eeepc->led_workqueue);
339 return rv;
340 }
341
342 return 0;
343}
344
345static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
346{
347 if (eeepc->tpd_led.dev)
348 led_classdev_unregister(&eeepc->tpd_led);
349 if (eeepc->led_workqueue)
350 destroy_workqueue(eeepc->led_workqueue);
351}
352
353/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100354 * PCI hotplug (for wlan rfkill)
355 */
356static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc)
357{
358 u32 retval;
359 acpi_status status;
360
361 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
362
363 if (ACPI_FAILURE(status))
364 return false;
365
366 return !(retval & 0x1);
367}
368
369static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc)
370{
371 struct pci_dev *dev;
372 struct pci_bus *bus;
373 bool blocked = eeepc_wlan_rfkill_blocked(eeepc);
374 bool absent;
375 u32 l;
376
377 if (eeepc->wlan_rfkill)
378 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
379
380 mutex_lock(&eeepc->hotplug_lock);
381
382 if (eeepc->hotplug_slot) {
383 bus = pci_find_bus(0, 1);
384 if (!bus) {
385 pr_warning("Unable to find PCI bus 1?\n");
386 goto out_unlock;
387 }
388
389 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
390 pr_err("Unable to read PCI config space?\n");
391 goto out_unlock;
392 }
393 absent = (l == 0xffffffff);
394
395 if (blocked != absent) {
396 pr_warning("BIOS says wireless lan is %s, "
397 "but the pci device is %s\n",
398 blocked ? "blocked" : "unblocked",
399 absent ? "absent" : "present");
400 pr_warning("skipped wireless hotplug as probably "
401 "inappropriate for this model\n");
402 goto out_unlock;
403 }
404
405 if (!blocked) {
406 dev = pci_get_slot(bus, 0);
407 if (dev) {
408 /* Device already present */
409 pci_dev_put(dev);
410 goto out_unlock;
411 }
412 dev = pci_scan_single_device(bus, 0);
413 if (dev) {
414 pci_bus_assign_resources(bus);
415 if (pci_bus_add_device(dev))
416 pr_err("Unable to hotplug wifi\n");
417 }
418 } else {
419 dev = pci_get_slot(bus, 0);
420 if (dev) {
421 pci_remove_bus_device(dev);
422 pci_dev_put(dev);
423 }
424 }
425 }
426
427out_unlock:
428 mutex_unlock(&eeepc->hotplug_lock);
429}
430
431static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
432{
433 struct eeepc_wmi *eeepc = data;
434
435 if (event != ACPI_NOTIFY_BUS_CHECK)
436 return;
437
438 eeepc_rfkill_hotplug(eeepc);
439}
440
441static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc,
442 char *node)
443{
444 acpi_status status;
445 acpi_handle handle;
446
447 status = acpi_get_handle(NULL, node, &handle);
448
449 if (ACPI_SUCCESS(status)) {
450 status = acpi_install_notify_handler(handle,
451 ACPI_SYSTEM_NOTIFY,
452 eeepc_rfkill_notify,
453 eeepc);
454 if (ACPI_FAILURE(status))
455 pr_warning("Failed to register notify on %s\n", node);
456 } else
457 return -ENODEV;
458
459 return 0;
460}
461
462static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc,
463 char *node)
464{
465 acpi_status status = AE_OK;
466 acpi_handle handle;
467
468 status = acpi_get_handle(NULL, node, &handle);
469
470 if (ACPI_SUCCESS(status)) {
471 status = acpi_remove_notify_handler(handle,
472 ACPI_SYSTEM_NOTIFY,
473 eeepc_rfkill_notify);
474 if (ACPI_FAILURE(status))
475 pr_err("Error removing rfkill notify handler %s\n",
476 node);
477 }
478}
479
480static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
481 u8 *value)
482{
483 u32 retval;
484 acpi_status status;
485
486 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
487
488 if (ACPI_FAILURE(status))
489 return -EIO;
490
491 if (!retval || retval == 0x00060000)
492 return -ENODEV;
493 else
494 *value = (retval & 0x1);
495
496 return 0;
497}
498
499static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
500{
501 kfree(hotplug_slot->info);
502 kfree(hotplug_slot);
503}
504
505static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
506 .owner = THIS_MODULE,
507 .get_adapter_status = eeepc_get_adapter_status,
508 .get_power_status = eeepc_get_adapter_status,
509};
510
511static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc)
512{
513 int ret = -ENOMEM;
514 struct pci_bus *bus = pci_find_bus(0, 1);
515
516 if (!bus) {
517 pr_err("Unable to find wifi PCI bus\n");
518 return -ENODEV;
519 }
520
521 eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
522 if (!eeepc->hotplug_slot)
523 goto error_slot;
524
525 eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
526 GFP_KERNEL);
527 if (!eeepc->hotplug_slot->info)
528 goto error_info;
529
530 eeepc->hotplug_slot->private = eeepc;
531 eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
532 eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
533 eeepc_get_adapter_status(eeepc->hotplug_slot,
534 &eeepc->hotplug_slot->info->adapter_status);
535
536 ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
537 if (ret) {
538 pr_err("Unable to register hotplug slot - %d\n", ret);
539 goto error_register;
540 }
541
542 return 0;
543
544error_register:
545 kfree(eeepc->hotplug_slot->info);
546error_info:
547 kfree(eeepc->hotplug_slot);
548 eeepc->hotplug_slot = NULL;
549error_slot:
550 return ret;
551}
552
553/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100554 * Rfkill devices
555 */
556static int eeepc_rfkill_set(void *data, bool blocked)
557{
558 int dev_id = (unsigned long)data;
559 u32 ctrl_param = !blocked;
560
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100561 return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100562}
563
564static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
565{
566 int dev_id = (unsigned long)data;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100567 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100568 acpi_status status;
569
Corentin Chary2a3f0062010-11-29 08:14:10 +0100570 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100571
572 if (ACPI_FAILURE(status))
573 return ;
574
Corentin Chary2a3f0062010-11-29 08:14:10 +0100575 rfkill_set_sw_state(rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100576}
577
578static const struct rfkill_ops eeepc_rfkill_ops = {
579 .set_block = eeepc_rfkill_set,
580 .query = eeepc_rfkill_query,
581};
582
583static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
584 struct rfkill **rfkill,
585 const char *name,
586 enum rfkill_type type, int dev_id)
587{
588 int result;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100589 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100590 acpi_status status;
591
Corentin Chary2a3f0062010-11-29 08:14:10 +0100592 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100593
594 if (ACPI_FAILURE(status))
595 return -1;
596
597 /* If the device is present, DSTS will always set some bits
598 * 0x00070000 - 1110000000000000000 - device supported
599 * 0x00060000 - 1100000000000000000 - not supported
600 * 0x00020000 - 0100000000000000000 - device supported
601 * 0x00010000 - 0010000000000000000 - not supported / special mode ?
602 */
Corentin Chary2a3f0062010-11-29 08:14:10 +0100603 if (!retval || retval == 0x00060000)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100604 return -ENODEV;
605
606 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
607 &eeepc_rfkill_ops, (void *)(long)dev_id);
608
609 if (!*rfkill)
610 return -EINVAL;
611
Corentin Chary2a3f0062010-11-29 08:14:10 +0100612 rfkill_init_sw_state(*rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100613 result = rfkill_register(*rfkill);
614 if (result) {
615 rfkill_destroy(*rfkill);
616 *rfkill = NULL;
617 return result;
618 }
619 return 0;
620}
621
622static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
623{
Corentin Charyafa7c882011-02-06 13:28:28 +0100624 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
625 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
626 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Corentin Charyba48fdb2010-11-29 08:14:07 +0100627 if (eeepc->wlan_rfkill) {
628 rfkill_unregister(eeepc->wlan_rfkill);
629 rfkill_destroy(eeepc->wlan_rfkill);
630 eeepc->wlan_rfkill = NULL;
631 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100632 /*
633 * Refresh pci hotplug in case the rfkill state was changed after
634 * eeepc_unregister_rfkill_notifier()
635 */
636 eeepc_rfkill_hotplug(eeepc);
637 if (eeepc->hotplug_slot)
638 pci_hp_deregister(eeepc->hotplug_slot);
639
Corentin Charyba48fdb2010-11-29 08:14:07 +0100640 if (eeepc->bluetooth_rfkill) {
641 rfkill_unregister(eeepc->bluetooth_rfkill);
642 rfkill_destroy(eeepc->bluetooth_rfkill);
643 eeepc->bluetooth_rfkill = NULL;
644 }
645 if (eeepc->wwan3g_rfkill) {
646 rfkill_unregister(eeepc->wwan3g_rfkill);
647 rfkill_destroy(eeepc->wwan3g_rfkill);
648 eeepc->wwan3g_rfkill = NULL;
649 }
650}
651
652static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
653{
654 int result = 0;
655
Corentin Charyafa7c882011-02-06 13:28:28 +0100656 mutex_init(&eeepc->hotplug_lock);
657
Corentin Charyba48fdb2010-11-29 08:14:07 +0100658 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
659 "eeepc-wlan", RFKILL_TYPE_WLAN,
660 EEEPC_WMI_DEVID_WLAN);
661
662 if (result && result != -ENODEV)
663 goto exit;
664
665 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
666 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
667 EEEPC_WMI_DEVID_BLUETOOTH);
668
669 if (result && result != -ENODEV)
670 goto exit;
671
672 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
673 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
674 EEEPC_WMI_DEVID_WWAN3G);
675
676 if (result && result != -ENODEV)
677 goto exit;
678
Corentin Charyafa7c882011-02-06 13:28:28 +0100679 result = eeepc_setup_pci_hotplug(eeepc);
680 /*
681 * If we get -EBUSY then something else is handling the PCI hotplug -
682 * don't fail in this case
683 */
684 if (result == -EBUSY)
685 result = 0;
686
687 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
688 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
689 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
690 /*
691 * Refresh pci hotplug in case the rfkill state was changed during
692 * setup.
693 */
694 eeepc_rfkill_hotplug(eeepc);
695
Corentin Charyba48fdb2010-11-29 08:14:07 +0100696exit:
697 if (result && result != -ENODEV)
698 eeepc_wmi_rfkill_exit(eeepc);
699
700 if (result == -ENODEV)
701 result = 0;
702
703 return result;
704}
705
706/*
Corentin Chary084fca62010-11-29 08:14:06 +0100707 * Backlight
708 */
Yong Wang3d7b1652010-04-11 09:27:54 +0800709static int read_brightness(struct backlight_device *bd)
710{
Corentin Charydfed65d2010-11-29 08:14:12 +0100711 u32 retval;
Yong Wang3d7b1652010-04-11 09:27:54 +0800712 acpi_status status;
713
Corentin Chary2a3f0062010-11-29 08:14:10 +0100714 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800715
716 if (ACPI_FAILURE(status))
717 return -1;
718 else
Corentin Chary2a3f0062010-11-29 08:14:10 +0100719 return retval & 0xFF;
Yong Wang3d7b1652010-04-11 09:27:54 +0800720}
721
722static int update_bl_status(struct backlight_device *bd)
723{
724
Corentin Charydfed65d2010-11-29 08:14:12 +0100725 u32 ctrl_param;
Yong Wang3d7b1652010-04-11 09:27:54 +0800726 acpi_status status;
727
728 ctrl_param = bd->props.brightness;
729
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100730 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
731 ctrl_param, NULL);
Yong Wang3d7b1652010-04-11 09:27:54 +0800732
733 if (ACPI_FAILURE(status))
734 return -1;
735 else
736 return 0;
737}
738
739static const struct backlight_ops eeepc_wmi_bl_ops = {
740 .get_brightness = read_brightness,
741 .update_status = update_bl_status,
742};
743
744static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
745{
746 struct backlight_device *bd = eeepc->backlight_device;
747 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +0200748 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +0800749
750 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
751 new = code - NOTIFY_BRNUP_MIN + 1;
752 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
753 new = code - NOTIFY_BRNDOWN_MIN;
754
755 bd->props.brightness = new;
756 backlight_update_status(bd);
757 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
758
759 return old;
760}
761
762static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
763{
764 struct backlight_device *bd;
765 struct backlight_properties props;
766
767 memset(&props, 0, sizeof(struct backlight_properties));
768 props.max_brightness = 15;
769 bd = backlight_device_register(EEEPC_WMI_FILE,
Corentin Chary27c136c2010-11-29 08:14:05 +0100770 &eeepc->platform_device->dev, eeepc,
Yong Wang3d7b1652010-04-11 09:27:54 +0800771 &eeepc_wmi_bl_ops, &props);
772 if (IS_ERR(bd)) {
773 pr_err("Could not register backlight device\n");
774 return PTR_ERR(bd);
775 }
776
777 eeepc->backlight_device = bd;
778
779 bd->props.brightness = read_brightness(bd);
780 bd->props.power = FB_BLANK_UNBLANK;
781 backlight_update_status(bd);
782
783 return 0;
784}
785
786static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
787{
788 if (eeepc->backlight_device)
789 backlight_device_unregister(eeepc->backlight_device);
790
791 eeepc->backlight_device = NULL;
792}
793
794static void eeepc_wmi_notify(u32 value, void *context)
795{
796 struct eeepc_wmi *eeepc = context;
797 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
798 union acpi_object *obj;
799 acpi_status status;
800 int code;
801 int orig_code;
802
803 status = wmi_get_event_data(value, &response);
804 if (status != AE_OK) {
805 pr_err("bad event status 0x%x\n", status);
806 return;
807 }
808
809 obj = (union acpi_object *)response.pointer;
810
811 if (obj && obj->type == ACPI_TYPE_INTEGER) {
812 code = obj->integer.value;
813 orig_code = code;
814
815 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
816 code = NOTIFY_BRNUP_MIN;
817 else if (code >= NOTIFY_BRNDOWN_MIN &&
818 code <= NOTIFY_BRNDOWN_MAX)
819 code = NOTIFY_BRNDOWN_MIN;
820
821 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
822 if (!acpi_video_backlight_support())
823 eeepc_wmi_backlight_notify(eeepc, orig_code);
824 }
825
826 if (!sparse_keymap_report_event(eeepc->inputdev,
827 code, 1, true))
828 pr_info("Unknown key %x pressed\n", code);
829 }
830
831 kfree(obj);
832}
833
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -0700834static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
835 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -0500836{
837 int value;
838 struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
839 acpi_status status;
840
841 if (!count || sscanf(buf, "%i", &value) != 1)
842 return -EINVAL;
843 if (value < 0 || value > 2)
844 return -EINVAL;
845
846 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
847 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
848
849 if (ACPI_FAILURE(status))
850 return -EIO;
851 else
852 return count;
853}
854
855static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
856
Corentin Chary4e37b422010-11-29 08:14:08 +0100857static struct attribute *platform_attributes[] = {
858 &dev_attr_cpufv.attr,
859 NULL
860};
861
862static struct attribute_group platform_attribute_group = {
863 .attrs = platform_attributes
864};
865
Chris Bagwell7f80d732010-10-11 18:47:18 -0500866static void eeepc_wmi_sysfs_exit(struct platform_device *device)
867{
Corentin Chary4e37b422010-11-29 08:14:08 +0100868 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500869}
870
871static int eeepc_wmi_sysfs_init(struct platform_device *device)
872{
Corentin Chary4e37b422010-11-29 08:14:08 +0100873 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500874}
875
Corentin Chary27c136c2010-11-29 08:14:05 +0100876/*
877 * Platform device
878 */
879static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
880{
881 int err;
882
883 eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
884 if (!eeepc->platform_device)
885 return -ENOMEM;
886 platform_set_drvdata(eeepc->platform_device, eeepc);
887
888 err = platform_device_add(eeepc->platform_device);
889 if (err)
890 goto fail_platform_device;
891
892 err = eeepc_wmi_sysfs_init(eeepc->platform_device);
893 if (err)
894 goto fail_sysfs;
895 return 0;
896
897fail_sysfs:
898 platform_device_del(eeepc->platform_device);
899fail_platform_device:
900 platform_device_put(eeepc->platform_device);
901 return err;
902}
903
904static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
905{
906 eeepc_wmi_sysfs_exit(eeepc->platform_device);
907 platform_device_unregister(eeepc->platform_device);
908}
909
910/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100911 * debugfs
912 */
913struct eeepc_wmi_debugfs_node {
914 struct eeepc_wmi *eeepc;
915 char *name;
916 int (*show)(struct seq_file *m, void *data);
917};
918
919static int show_dsts(struct seq_file *m, void *data)
920{
921 struct eeepc_wmi *eeepc = m->private;
922 acpi_status status;
923 u32 retval = -1;
924
925 status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
926
927 if (ACPI_FAILURE(status))
928 return -EIO;
929
930 seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
931
932 return 0;
933}
934
935static int show_devs(struct seq_file *m, void *data)
936{
937 struct eeepc_wmi *eeepc = m->private;
938 acpi_status status;
939 u32 retval = -1;
940
941 status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
942 eeepc->debug.ctrl_param, &retval);
943 if (ACPI_FAILURE(status))
944 return -EIO;
945
946 seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
947 eeepc->debug.ctrl_param, retval);
948
949 return 0;
950}
951
952static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
953 { NULL, "devs", show_devs },
954 { NULL, "dsts", show_dsts },
955};
956
957static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
958{
959 struct eeepc_wmi_debugfs_node *node = inode->i_private;
960
961 return single_open(file, node->show, node->eeepc);
962}
963
964static const struct file_operations eeepc_wmi_debugfs_io_ops = {
965 .owner = THIS_MODULE,
966 .open = eeepc_wmi_debugfs_open,
967 .read = seq_read,
968 .llseek = seq_lseek,
969 .release = single_release,
970};
971
972static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
973{
974 debugfs_remove_recursive(eeepc->debug.root);
975}
976
977static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
978{
979 struct dentry *dent;
980 int i;
981
982 eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
983 if (!eeepc->debug.root) {
984 pr_err("failed to create debugfs directory");
985 goto error_debugfs;
986 }
987
988 dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
989 eeepc->debug.root, &eeepc->debug.dev_id);
990 if (!dent)
991 goto error_debugfs;
992
993 dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
994 eeepc->debug.root, &eeepc->debug.ctrl_param);
995 if (!dent)
996 goto error_debugfs;
997
998 for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
999 struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
1000
1001 node->eeepc = eeepc;
1002 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1003 eeepc->debug.root, node,
1004 &eeepc_wmi_debugfs_io_ops);
1005 if (!dent) {
1006 pr_err("failed to create debug file: %s\n", node->name);
1007 goto error_debugfs;
1008 }
1009 }
1010
1011 return 0;
1012
1013error_debugfs:
1014 eeepc_wmi_debugfs_exit(eeepc);
1015 return -ENOMEM;
1016}
1017
1018/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001019 * WMI Driver
1020 */
Corentin Charyafa7c882011-02-06 13:28:28 +01001021static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
1022{
1023 const char *model;
1024
1025 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1026 if (!model)
1027 return;
1028
1029 /*
1030 * Whitelist for wlan hotplug
1031 *
1032 * Eeepc 1000H needs the current hotplug code to handle
1033 * Fn+F2 correctly. We may add other Eeepc here later, but
1034 * it seems that most of the laptops supported by eeepc-wmi
1035 * don't need to be on this list
1036 */
1037 if (strcmp(model, "1000H") == 0) {
1038 eeepc->hotplug_wireless = true;
1039 pr_info("wlan hotplug enabled\n");
1040 }
1041}
1042
Corentin Chary27c136c2010-11-29 08:14:05 +01001043static struct platform_device * __init eeepc_wmi_add(void)
Yong Wangee027e42010-03-21 10:26:34 +08001044{
Yong Wang45f2c692010-04-11 09:27:19 +08001045 struct eeepc_wmi *eeepc;
Yong Wangee027e42010-03-21 10:26:34 +08001046 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001047 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001048
Corentin Chary27c136c2010-11-29 08:14:05 +01001049 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
1050 if (!eeepc)
1051 return ERR_PTR(-ENOMEM);
1052
Corentin Charyafa7c882011-02-06 13:28:28 +01001053 eeepc->hotplug_wireless = hotplug_wireless;
1054 eeepc_dmi_check(eeepc);
1055
Corentin Chary27c136c2010-11-29 08:14:05 +01001056 /*
1057 * Register the platform device first. It is used as a parent for the
1058 * sub-devices below.
1059 */
1060 err = eeepc_wmi_platform_init(eeepc);
1061 if (err)
1062 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001063
1064 err = eeepc_wmi_input_init(eeepc);
1065 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001066 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001067
Corentin Chary084fca62010-11-29 08:14:06 +01001068 err = eeepc_wmi_led_init(eeepc);
1069 if (err)
1070 goto fail_leds;
1071
Corentin Charyba48fdb2010-11-29 08:14:07 +01001072 err = eeepc_wmi_rfkill_init(eeepc);
1073 if (err)
1074 goto fail_rfkill;
1075
Yong Wang3d7b1652010-04-11 09:27:54 +08001076 if (!acpi_video_backlight_support()) {
1077 err = eeepc_wmi_backlight_init(eeepc);
1078 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001079 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001080 } else
1081 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001082
1083 status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
Corentin Chary27c136c2010-11-29 08:14:05 +01001084 eeepc_wmi_notify, eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001085 if (ACPI_FAILURE(status)) {
1086 pr_err("Unable to register notify handler - %d\n",
1087 status);
1088 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001089 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001090 }
1091
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001092 err = eeepc_wmi_debugfs_init(eeepc);
1093 if (err)
1094 goto fail_debugfs;
1095
Corentin Chary27c136c2010-11-29 08:14:05 +01001096 return eeepc->platform_device;
Yong Wang45f2c692010-04-11 09:27:19 +08001097
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001098fail_debugfs:
1099 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Corentin Chary27c136c2010-11-29 08:14:05 +01001100fail_wmi_handler:
Yong Wang3d7b1652010-04-11 09:27:54 +08001101 eeepc_wmi_backlight_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001102fail_backlight:
Corentin Charyba48fdb2010-11-29 08:14:07 +01001103 eeepc_wmi_rfkill_exit(eeepc);
1104fail_rfkill:
Corentin Chary084fca62010-11-29 08:14:06 +01001105 eeepc_wmi_led_exit(eeepc);
1106fail_leds:
Yong Wang45f2c692010-04-11 09:27:19 +08001107 eeepc_wmi_input_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001108fail_input:
1109 eeepc_wmi_platform_exit(eeepc);
1110fail_platform:
1111 kfree(eeepc);
1112 return ERR_PTR(err);
Yong Wang45f2c692010-04-11 09:27:19 +08001113}
1114
Corentin Chary27c136c2010-11-29 08:14:05 +01001115static int eeepc_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001116{
1117 struct eeepc_wmi *eeepc;
1118
1119 eeepc = platform_get_drvdata(device);
1120 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +08001121 eeepc_wmi_backlight_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001122 eeepc_wmi_input_exit(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +01001123 eeepc_wmi_led_exit(eeepc);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001124 eeepc_wmi_rfkill_exit(eeepc);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001125 eeepc_wmi_debugfs_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001126 eeepc_wmi_platform_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001127
Corentin Chary27c136c2010-11-29 08:14:05 +01001128 kfree(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001129 return 0;
1130}
1131
1132static struct platform_driver platform_driver = {
1133 .driver = {
1134 .name = EEEPC_WMI_FILE,
1135 .owner = THIS_MODULE,
1136 },
Yong Wang45f2c692010-04-11 09:27:19 +08001137};
1138
Corentin Charyd358cb52010-11-29 08:14:14 +01001139static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
1140 void *context, void **retval)
1141{
1142 pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
1143 *(bool *)context = true;
1144 return AE_CTRL_TERMINATE;
1145}
1146
1147static int __init eeepc_wmi_check_atkd(void)
1148{
1149 acpi_status status;
1150 bool found = false;
1151
1152 status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
1153 &found, NULL);
1154
1155 if (ACPI_FAILURE(status) || !found)
1156 return 0;
1157 return -1;
1158}
1159
Yong Wang45f2c692010-04-11 09:27:19 +08001160static int __init eeepc_wmi_init(void)
1161{
Yong Wang45f2c692010-04-11 09:27:19 +08001162 int err;
1163
Yong Wang3d7b1652010-04-11 09:27:54 +08001164 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
1165 !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
Yong Wang81248882010-04-11 09:26:33 +08001166 pr_warning("No known WMI GUID found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001167 return -ENODEV;
1168 }
1169
Corentin Charyd358cb52010-11-29 08:14:14 +01001170 if (eeepc_wmi_check_atkd()) {
1171 pr_warning("WMI device present, but legacy ATKD device is also "
1172 "present and enabled.");
1173 pr_warning("You probably booted with acpi_osi=\"Linux\" or "
1174 "acpi_osi=\"!Windows 2009\"");
1175 pr_warning("Can't load eeepc-wmi, use default acpi_osi "
1176 "(preferred) or eeepc-laptop");
1177 return -ENODEV;
1178 }
1179
Corentin Chary27c136c2010-11-29 08:14:05 +01001180 platform_device = eeepc_wmi_add();
1181 if (IS_ERR(platform_device)) {
1182 err = PTR_ERR(platform_device);
1183 goto fail_eeepc_wmi;
Yong Wang81248882010-04-11 09:26:33 +08001184 }
Yong Wangee027e42010-03-21 10:26:34 +08001185
Yong Wang45f2c692010-04-11 09:27:19 +08001186 err = platform_driver_register(&platform_driver);
1187 if (err) {
1188 pr_warning("Unable to register platform driver\n");
Corentin Chary27c136c2010-11-29 08:14:05 +01001189 goto fail_platform_driver;
Yong Wangee027e42010-03-21 10:26:34 +08001190 }
1191
1192 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001193
Corentin Chary27c136c2010-11-29 08:14:05 +01001194fail_platform_driver:
1195 eeepc_wmi_remove(platform_device);
1196fail_eeepc_wmi:
Yong Wang45f2c692010-04-11 09:27:19 +08001197 return err;
Yong Wangee027e42010-03-21 10:26:34 +08001198}
1199
1200static void __exit eeepc_wmi_exit(void)
1201{
Corentin Chary27c136c2010-11-29 08:14:05 +01001202 eeepc_wmi_remove(platform_device);
Yong Wang45f2c692010-04-11 09:27:19 +08001203 platform_driver_unregister(&platform_driver);
Yong Wangee027e42010-03-21 10:26:34 +08001204}
1205
1206module_init(eeepc_wmi_init);
1207module_exit(eeepc_wmi_exit);