blob: eb4c0ce88ac1c3fffdf1dedc2af0e532c6f62d81 [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;
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
Corentin Chary27c136c2010-11-29 08:14:05 +0100147/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
Yong Wang45f2c692010-04-11 09:27:19 +0800148static struct platform_device *platform_device;
Yong Wangee027e42010-03-21 10:26:34 +0800149
Yong Wang81248882010-04-11 09:26:33 +0800150static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
Yong Wangee027e42010-03-21 10:26:34 +0800151{
152 int err;
153
Yong Wang81248882010-04-11 09:26:33 +0800154 eeepc->inputdev = input_allocate_device();
155 if (!eeepc->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800156 return -ENOMEM;
157
Yong Wang81248882010-04-11 09:26:33 +0800158 eeepc->inputdev->name = "Eee PC WMI hotkeys";
Yong Wang45f2c692010-04-11 09:27:19 +0800159 eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
Yong Wang81248882010-04-11 09:26:33 +0800160 eeepc->inputdev->id.bustype = BUS_HOST;
Corentin Chary27c136c2010-11-29 08:14:05 +0100161 eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
Yong Wangee027e42010-03-21 10:26:34 +0800162
Yong Wang81248882010-04-11 09:26:33 +0800163 err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800164 if (err)
165 goto err_free_dev;
166
Yong Wang81248882010-04-11 09:26:33 +0800167 err = input_register_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800168 if (err)
169 goto err_free_keymap;
170
171 return 0;
172
173err_free_keymap:
Yong Wang81248882010-04-11 09:26:33 +0800174 sparse_keymap_free(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800175err_free_dev:
Yong Wang81248882010-04-11 09:26:33 +0800176 input_free_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800177 return err;
178}
179
Yong Wang81248882010-04-11 09:26:33 +0800180static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
181{
182 if (eeepc->inputdev) {
183 sparse_keymap_free(eeepc->inputdev);
184 input_unregister_device(eeepc->inputdev);
185 }
186
187 eeepc->inputdev = NULL;
188}
189
Corentin Chary2a3f0062010-11-29 08:14:10 +0100190static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800191{
192 struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
193 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
194 union acpi_object *obj;
195 acpi_status status;
196 u32 tmp;
197
198 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
Corentin Charyafa7c882011-02-06 13:28:28 +0100199 1, EEEPC_WMI_METHODID_DSTS,
200 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800201
202 if (ACPI_FAILURE(status))
203 return status;
204
205 obj = (union acpi_object *)output.pointer;
206 if (obj && obj->type == ACPI_TYPE_INTEGER)
207 tmp = (u32)obj->integer.value;
208 else
209 tmp = 0;
210
Corentin Chary2a3f0062010-11-29 08:14:10 +0100211 if (retval)
212 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800213
214 kfree(obj);
215
216 return status;
217
218}
219
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100220static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
221 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800222{
223 struct bios_args args = {
224 .dev_id = dev_id,
225 .ctrl_param = ctrl_param,
226 };
227 struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
228 acpi_status status;
229
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100230 if (!retval) {
231 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
232 EEEPC_WMI_METHODID_DEVS,
233 &input, NULL);
234 } else {
235 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
236 union acpi_object *obj;
237 u32 tmp;
238
239 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
240 EEEPC_WMI_METHODID_DEVS,
241 &input, &output);
242
243 if (ACPI_FAILURE(status))
244 return status;
245
246 obj = (union acpi_object *)output.pointer;
247 if (obj && obj->type == ACPI_TYPE_INTEGER)
248 tmp = (u32)obj->integer.value;
249 else
250 tmp = 0;
251
252 *retval = tmp;
253
254 kfree(obj);
255 }
Yong Wang3d7b1652010-04-11 09:27:54 +0800256
257 return status;
258}
259
Corentin Chary084fca62010-11-29 08:14:06 +0100260/*
261 * LEDs
262 */
263/*
264 * These functions actually update the LED's, and are called from a
265 * workqueue. By doing this as separate work rather than when the LED
266 * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
267 * potentially bad time, such as a timer interrupt.
268 */
269static void tpd_led_update(struct work_struct *work)
270{
271 int ctrl_param;
272 struct eeepc_wmi *eeepc;
273
274 eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
275
276 ctrl_param = eeepc->tpd_led_wk;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100277 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100278}
279
280static void tpd_led_set(struct led_classdev *led_cdev,
281 enum led_brightness value)
282{
283 struct eeepc_wmi *eeepc;
284
285 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
286
287 eeepc->tpd_led_wk = !!value;
288 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
289}
290
291static int read_tpd_state(struct eeepc_wmi *eeepc)
292{
Corentin Charydfed65d2010-11-29 08:14:12 +0100293 u32 retval;
Corentin Chary084fca62010-11-29 08:14:06 +0100294 acpi_status status;
295
Corentin Chary2a3f0062010-11-29 08:14:10 +0100296 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
Corentin Chary084fca62010-11-29 08:14:06 +0100297
298 if (ACPI_FAILURE(status))
299 return -1;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100300 else if (!retval || retval == 0x00060000)
Corentin Chary084fca62010-11-29 08:14:06 +0100301 /*
302 * if touchpad led is present, DSTS will set some bits,
303 * usually 0x00020000.
304 * 0x00060000 means that the device is not supported
305 */
306 return -ENODEV;
307 else
308 /* Status is stored in the first bit */
Corentin Chary2a3f0062010-11-29 08:14:10 +0100309 return retval & 0x1;
Corentin Chary084fca62010-11-29 08:14:06 +0100310}
311
312static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
313{
314 struct eeepc_wmi *eeepc;
315
316 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
317
318 return read_tpd_state(eeepc);
319}
320
321static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
322{
323 int rv;
324
325 if (read_tpd_state(eeepc) < 0)
326 return 0;
327
328 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
329 if (!eeepc->led_workqueue)
330 return -ENOMEM;
331 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
332
333 eeepc->tpd_led.name = "eeepc::touchpad";
334 eeepc->tpd_led.brightness_set = tpd_led_set;
335 eeepc->tpd_led.brightness_get = tpd_led_get;
336 eeepc->tpd_led.max_brightness = 1;
337
338 rv = led_classdev_register(&eeepc->platform_device->dev,
339 &eeepc->tpd_led);
340 if (rv) {
341 destroy_workqueue(eeepc->led_workqueue);
342 return rv;
343 }
344
345 return 0;
346}
347
348static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
349{
350 if (eeepc->tpd_led.dev)
351 led_classdev_unregister(&eeepc->tpd_led);
352 if (eeepc->led_workqueue)
353 destroy_workqueue(eeepc->led_workqueue);
354}
355
356/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100357 * PCI hotplug (for wlan rfkill)
358 */
359static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc)
360{
361 u32 retval;
362 acpi_status status;
363
364 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
365
366 if (ACPI_FAILURE(status))
367 return false;
368
369 return !(retval & 0x1);
370}
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{
497 u32 retval;
498 acpi_status status;
499
500 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_WLAN, &retval);
501
502 if (ACPI_FAILURE(status))
503 return -EIO;
504
505 if (!retval || retval == 0x00060000)
506 return -ENODEV;
507 else
508 *value = (retval & 0x1);
509
510 return 0;
511}
512
513static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
514{
515 kfree(hotplug_slot->info);
516 kfree(hotplug_slot);
517}
518
519static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
520 .owner = THIS_MODULE,
521 .get_adapter_status = eeepc_get_adapter_status,
522 .get_power_status = eeepc_get_adapter_status,
523};
524
Corentin Chary279f8f92011-02-06 13:28:29 +0100525static void eeepc_hotplug_work(struct work_struct *work)
526{
527 struct eeepc_wmi *eeepc;
528
529 eeepc = container_of(work, struct eeepc_wmi, hotplug_work);
530 eeepc_rfkill_hotplug(eeepc);
531}
532
Corentin Charyafa7c882011-02-06 13:28:28 +0100533static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc)
534{
535 int ret = -ENOMEM;
536 struct pci_bus *bus = pci_find_bus(0, 1);
537
538 if (!bus) {
539 pr_err("Unable to find wifi PCI bus\n");
540 return -ENODEV;
541 }
542
Corentin Chary279f8f92011-02-06 13:28:29 +0100543 eeepc->hotplug_workqueue =
544 create_singlethread_workqueue("hotplug_workqueue");
545 if (!eeepc->hotplug_workqueue)
546 goto error_workqueue;
547
548 INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work);
549
Corentin Charyafa7c882011-02-06 13:28:28 +0100550 eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
551 if (!eeepc->hotplug_slot)
552 goto error_slot;
553
554 eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
555 GFP_KERNEL);
556 if (!eeepc->hotplug_slot->info)
557 goto error_info;
558
559 eeepc->hotplug_slot->private = eeepc;
560 eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
561 eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
562 eeepc_get_adapter_status(eeepc->hotplug_slot,
563 &eeepc->hotplug_slot->info->adapter_status);
564
565 ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
566 if (ret) {
567 pr_err("Unable to register hotplug slot - %d\n", ret);
568 goto error_register;
569 }
570
571 return 0;
572
573error_register:
574 kfree(eeepc->hotplug_slot->info);
575error_info:
576 kfree(eeepc->hotplug_slot);
577 eeepc->hotplug_slot = NULL;
578error_slot:
Corentin Chary279f8f92011-02-06 13:28:29 +0100579 destroy_workqueue(eeepc->hotplug_workqueue);
580error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100581 return ret;
582}
583
584/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100585 * Rfkill devices
586 */
587static int eeepc_rfkill_set(void *data, bool blocked)
588{
589 int dev_id = (unsigned long)data;
590 u32 ctrl_param = !blocked;
591
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100592 return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100593}
594
595static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
596{
597 int dev_id = (unsigned long)data;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100598 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100599 acpi_status status;
600
Corentin Chary2a3f0062010-11-29 08:14:10 +0100601 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100602
603 if (ACPI_FAILURE(status))
604 return ;
605
Corentin Chary2a3f0062010-11-29 08:14:10 +0100606 rfkill_set_sw_state(rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100607}
608
Corentin Chary279f8f92011-02-06 13:28:29 +0100609static int eeepc_rfkill_wlan_set(void *data, bool blocked)
610{
611 struct eeepc_wmi *eeepc = data;
612 int ret;
613
614 /*
615 * This handler is enabled only if hotplug is enabled.
616 * In this case, the eeepc_wmi_set_devstate() will
617 * trigger a wmi notification and we need to wait
618 * this call to finish before being able to call
619 * any wmi method
620 */
621 mutex_lock(&eeepc->wmi_lock);
622 ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked);
623 mutex_unlock(&eeepc->wmi_lock);
624 return ret;
625}
626
627static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data)
628{
629 eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN);
630}
631
632static const struct rfkill_ops eeepc_rfkill_wlan_ops = {
633 .set_block = eeepc_rfkill_wlan_set,
634 .query = eeepc_rfkill_wlan_query,
635};
636
Corentin Charyba48fdb2010-11-29 08:14:07 +0100637static const struct rfkill_ops eeepc_rfkill_ops = {
638 .set_block = eeepc_rfkill_set,
639 .query = eeepc_rfkill_query,
640};
641
642static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
643 struct rfkill **rfkill,
644 const char *name,
645 enum rfkill_type type, int dev_id)
646{
647 int result;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100648 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100649 acpi_status status;
650
Corentin Chary2a3f0062010-11-29 08:14:10 +0100651 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100652
653 if (ACPI_FAILURE(status))
654 return -1;
655
656 /* If the device is present, DSTS will always set some bits
657 * 0x00070000 - 1110000000000000000 - device supported
658 * 0x00060000 - 1100000000000000000 - not supported
659 * 0x00020000 - 0100000000000000000 - device supported
660 * 0x00010000 - 0010000000000000000 - not supported / special mode ?
661 */
Corentin Chary2a3f0062010-11-29 08:14:10 +0100662 if (!retval || retval == 0x00060000)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100663 return -ENODEV;
664
Corentin Chary279f8f92011-02-06 13:28:29 +0100665 if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless)
666 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
667 &eeepc_rfkill_wlan_ops, eeepc);
668 else
669 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
670 &eeepc_rfkill_ops, (void *)(long)dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100671
672 if (!*rfkill)
673 return -EINVAL;
674
Corentin Chary2a3f0062010-11-29 08:14:10 +0100675 rfkill_init_sw_state(*rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100676 result = rfkill_register(*rfkill);
677 if (result) {
678 rfkill_destroy(*rfkill);
679 *rfkill = NULL;
680 return result;
681 }
682 return 0;
683}
684
685static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
686{
Corentin Charyafa7c882011-02-06 13:28:28 +0100687 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
688 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
689 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Corentin Charyba48fdb2010-11-29 08:14:07 +0100690 if (eeepc->wlan_rfkill) {
691 rfkill_unregister(eeepc->wlan_rfkill);
692 rfkill_destroy(eeepc->wlan_rfkill);
693 eeepc->wlan_rfkill = NULL;
694 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100695 /*
696 * Refresh pci hotplug in case the rfkill state was changed after
697 * eeepc_unregister_rfkill_notifier()
698 */
699 eeepc_rfkill_hotplug(eeepc);
700 if (eeepc->hotplug_slot)
701 pci_hp_deregister(eeepc->hotplug_slot);
Corentin Chary279f8f92011-02-06 13:28:29 +0100702 if (eeepc->hotplug_workqueue)
703 destroy_workqueue(eeepc->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +0100704
Corentin Charyba48fdb2010-11-29 08:14:07 +0100705 if (eeepc->bluetooth_rfkill) {
706 rfkill_unregister(eeepc->bluetooth_rfkill);
707 rfkill_destroy(eeepc->bluetooth_rfkill);
708 eeepc->bluetooth_rfkill = NULL;
709 }
710 if (eeepc->wwan3g_rfkill) {
711 rfkill_unregister(eeepc->wwan3g_rfkill);
712 rfkill_destroy(eeepc->wwan3g_rfkill);
713 eeepc->wwan3g_rfkill = NULL;
714 }
715}
716
717static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
718{
719 int result = 0;
720
Corentin Charyafa7c882011-02-06 13:28:28 +0100721 mutex_init(&eeepc->hotplug_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100722 mutex_init(&eeepc->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100723
Corentin Charyba48fdb2010-11-29 08:14:07 +0100724 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
725 "eeepc-wlan", RFKILL_TYPE_WLAN,
726 EEEPC_WMI_DEVID_WLAN);
727
728 if (result && result != -ENODEV)
729 goto exit;
730
731 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
732 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
733 EEEPC_WMI_DEVID_BLUETOOTH);
734
735 if (result && result != -ENODEV)
736 goto exit;
737
738 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
739 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
740 EEEPC_WMI_DEVID_WWAN3G);
741
742 if (result && result != -ENODEV)
743 goto exit;
744
Corentin Charyafa7c882011-02-06 13:28:28 +0100745 result = eeepc_setup_pci_hotplug(eeepc);
746 /*
747 * If we get -EBUSY then something else is handling the PCI hotplug -
748 * don't fail in this case
749 */
750 if (result == -EBUSY)
751 result = 0;
752
753 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
754 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
755 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
756 /*
757 * Refresh pci hotplug in case the rfkill state was changed during
758 * setup.
759 */
760 eeepc_rfkill_hotplug(eeepc);
761
Corentin Charyba48fdb2010-11-29 08:14:07 +0100762exit:
763 if (result && result != -ENODEV)
764 eeepc_wmi_rfkill_exit(eeepc);
765
766 if (result == -ENODEV)
767 result = 0;
768
769 return result;
770}
771
772/*
Corentin Chary084fca62010-11-29 08:14:06 +0100773 * Backlight
774 */
Yong Wang3d7b1652010-04-11 09:27:54 +0800775static int read_brightness(struct backlight_device *bd)
776{
Corentin Charydfed65d2010-11-29 08:14:12 +0100777 u32 retval;
Yong Wang3d7b1652010-04-11 09:27:54 +0800778 acpi_status status;
779
Corentin Chary2a3f0062010-11-29 08:14:10 +0100780 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800781
782 if (ACPI_FAILURE(status))
783 return -1;
784 else
Corentin Chary2a3f0062010-11-29 08:14:10 +0100785 return retval & 0xFF;
Yong Wang3d7b1652010-04-11 09:27:54 +0800786}
787
788static int update_bl_status(struct backlight_device *bd)
789{
790
Corentin Charydfed65d2010-11-29 08:14:12 +0100791 u32 ctrl_param;
Yong Wang3d7b1652010-04-11 09:27:54 +0800792 acpi_status status;
793
794 ctrl_param = bd->props.brightness;
795
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100796 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
797 ctrl_param, NULL);
Yong Wang3d7b1652010-04-11 09:27:54 +0800798
799 if (ACPI_FAILURE(status))
800 return -1;
801 else
802 return 0;
803}
804
805static const struct backlight_ops eeepc_wmi_bl_ops = {
806 .get_brightness = read_brightness,
807 .update_status = update_bl_status,
808};
809
810static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
811{
812 struct backlight_device *bd = eeepc->backlight_device;
813 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +0200814 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +0800815
816 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
817 new = code - NOTIFY_BRNUP_MIN + 1;
818 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
819 new = code - NOTIFY_BRNDOWN_MIN;
820
821 bd->props.brightness = new;
822 backlight_update_status(bd);
823 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
824
825 return old;
826}
827
828static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
829{
830 struct backlight_device *bd;
831 struct backlight_properties props;
832
833 memset(&props, 0, sizeof(struct backlight_properties));
834 props.max_brightness = 15;
835 bd = backlight_device_register(EEEPC_WMI_FILE,
Corentin Chary27c136c2010-11-29 08:14:05 +0100836 &eeepc->platform_device->dev, eeepc,
Yong Wang3d7b1652010-04-11 09:27:54 +0800837 &eeepc_wmi_bl_ops, &props);
838 if (IS_ERR(bd)) {
839 pr_err("Could not register backlight device\n");
840 return PTR_ERR(bd);
841 }
842
843 eeepc->backlight_device = bd;
844
845 bd->props.brightness = read_brightness(bd);
846 bd->props.power = FB_BLANK_UNBLANK;
847 backlight_update_status(bd);
848
849 return 0;
850}
851
852static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
853{
854 if (eeepc->backlight_device)
855 backlight_device_unregister(eeepc->backlight_device);
856
857 eeepc->backlight_device = NULL;
858}
859
860static void eeepc_wmi_notify(u32 value, void *context)
861{
862 struct eeepc_wmi *eeepc = context;
863 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
864 union acpi_object *obj;
865 acpi_status status;
866 int code;
867 int orig_code;
868
869 status = wmi_get_event_data(value, &response);
870 if (status != AE_OK) {
871 pr_err("bad event status 0x%x\n", status);
872 return;
873 }
874
875 obj = (union acpi_object *)response.pointer;
876
877 if (obj && obj->type == ACPI_TYPE_INTEGER) {
878 code = obj->integer.value;
879 orig_code = code;
880
881 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
882 code = NOTIFY_BRNUP_MIN;
883 else if (code >= NOTIFY_BRNDOWN_MIN &&
884 code <= NOTIFY_BRNDOWN_MAX)
885 code = NOTIFY_BRNDOWN_MIN;
886
887 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
888 if (!acpi_video_backlight_support())
889 eeepc_wmi_backlight_notify(eeepc, orig_code);
890 }
891
892 if (!sparse_keymap_report_event(eeepc->inputdev,
893 code, 1, true))
894 pr_info("Unknown key %x pressed\n", code);
895 }
896
897 kfree(obj);
898}
899
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -0700900static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
901 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -0500902{
903 int value;
904 struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
905 acpi_status status;
906
907 if (!count || sscanf(buf, "%i", &value) != 1)
908 return -EINVAL;
909 if (value < 0 || value > 2)
910 return -EINVAL;
911
912 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
913 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
914
915 if (ACPI_FAILURE(status))
916 return -EIO;
917 else
918 return count;
919}
920
921static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
922
Corentin Chary4e37b422010-11-29 08:14:08 +0100923static struct attribute *platform_attributes[] = {
924 &dev_attr_cpufv.attr,
925 NULL
926};
927
928static struct attribute_group platform_attribute_group = {
929 .attrs = platform_attributes
930};
931
Chris Bagwell7f80d732010-10-11 18:47:18 -0500932static void eeepc_wmi_sysfs_exit(struct platform_device *device)
933{
Corentin Chary4e37b422010-11-29 08:14:08 +0100934 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500935}
936
937static int eeepc_wmi_sysfs_init(struct platform_device *device)
938{
Corentin Chary4e37b422010-11-29 08:14:08 +0100939 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500940}
941
Corentin Chary27c136c2010-11-29 08:14:05 +0100942/*
943 * Platform device
944 */
945static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
946{
947 int err;
948
949 eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
950 if (!eeepc->platform_device)
951 return -ENOMEM;
952 platform_set_drvdata(eeepc->platform_device, eeepc);
953
954 err = platform_device_add(eeepc->platform_device);
955 if (err)
956 goto fail_platform_device;
957
958 err = eeepc_wmi_sysfs_init(eeepc->platform_device);
959 if (err)
960 goto fail_sysfs;
961 return 0;
962
963fail_sysfs:
964 platform_device_del(eeepc->platform_device);
965fail_platform_device:
966 platform_device_put(eeepc->platform_device);
967 return err;
968}
969
970static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
971{
972 eeepc_wmi_sysfs_exit(eeepc->platform_device);
973 platform_device_unregister(eeepc->platform_device);
974}
975
976/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100977 * debugfs
978 */
979struct eeepc_wmi_debugfs_node {
980 struct eeepc_wmi *eeepc;
981 char *name;
982 int (*show)(struct seq_file *m, void *data);
983};
984
985static int show_dsts(struct seq_file *m, void *data)
986{
987 struct eeepc_wmi *eeepc = m->private;
988 acpi_status status;
989 u32 retval = -1;
990
991 status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
992
993 if (ACPI_FAILURE(status))
994 return -EIO;
995
996 seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
997
998 return 0;
999}
1000
1001static int show_devs(struct seq_file *m, void *data)
1002{
1003 struct eeepc_wmi *eeepc = m->private;
1004 acpi_status status;
1005 u32 retval = -1;
1006
1007 status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
1008 eeepc->debug.ctrl_param, &retval);
1009 if (ACPI_FAILURE(status))
1010 return -EIO;
1011
1012 seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
1013 eeepc->debug.ctrl_param, retval);
1014
1015 return 0;
1016}
1017
1018static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
1019 { NULL, "devs", show_devs },
1020 { NULL, "dsts", show_dsts },
1021};
1022
1023static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
1024{
1025 struct eeepc_wmi_debugfs_node *node = inode->i_private;
1026
1027 return single_open(file, node->show, node->eeepc);
1028}
1029
1030static const struct file_operations eeepc_wmi_debugfs_io_ops = {
1031 .owner = THIS_MODULE,
1032 .open = eeepc_wmi_debugfs_open,
1033 .read = seq_read,
1034 .llseek = seq_lseek,
1035 .release = single_release,
1036};
1037
1038static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
1039{
1040 debugfs_remove_recursive(eeepc->debug.root);
1041}
1042
1043static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
1044{
1045 struct dentry *dent;
1046 int i;
1047
1048 eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
1049 if (!eeepc->debug.root) {
1050 pr_err("failed to create debugfs directory");
1051 goto error_debugfs;
1052 }
1053
1054 dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
1055 eeepc->debug.root, &eeepc->debug.dev_id);
1056 if (!dent)
1057 goto error_debugfs;
1058
1059 dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
1060 eeepc->debug.root, &eeepc->debug.ctrl_param);
1061 if (!dent)
1062 goto error_debugfs;
1063
1064 for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
1065 struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
1066
1067 node->eeepc = eeepc;
1068 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1069 eeepc->debug.root, node,
1070 &eeepc_wmi_debugfs_io_ops);
1071 if (!dent) {
1072 pr_err("failed to create debug file: %s\n", node->name);
1073 goto error_debugfs;
1074 }
1075 }
1076
1077 return 0;
1078
1079error_debugfs:
1080 eeepc_wmi_debugfs_exit(eeepc);
1081 return -ENOMEM;
1082}
1083
1084/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001085 * WMI Driver
1086 */
Corentin Charyafa7c882011-02-06 13:28:28 +01001087static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
1088{
1089 const char *model;
1090
1091 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1092 if (!model)
1093 return;
1094
1095 /*
1096 * Whitelist for wlan hotplug
1097 *
1098 * Eeepc 1000H needs the current hotplug code to handle
1099 * Fn+F2 correctly. We may add other Eeepc here later, but
1100 * it seems that most of the laptops supported by eeepc-wmi
1101 * don't need to be on this list
1102 */
1103 if (strcmp(model, "1000H") == 0) {
1104 eeepc->hotplug_wireless = true;
1105 pr_info("wlan hotplug enabled\n");
1106 }
1107}
1108
Corentin Chary27c136c2010-11-29 08:14:05 +01001109static struct platform_device * __init eeepc_wmi_add(void)
Yong Wangee027e42010-03-21 10:26:34 +08001110{
Yong Wang45f2c692010-04-11 09:27:19 +08001111 struct eeepc_wmi *eeepc;
Yong Wangee027e42010-03-21 10:26:34 +08001112 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001113 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001114
Corentin Chary27c136c2010-11-29 08:14:05 +01001115 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
1116 if (!eeepc)
1117 return ERR_PTR(-ENOMEM);
1118
Corentin Charyafa7c882011-02-06 13:28:28 +01001119 eeepc->hotplug_wireless = hotplug_wireless;
1120 eeepc_dmi_check(eeepc);
1121
Corentin Chary27c136c2010-11-29 08:14:05 +01001122 /*
1123 * Register the platform device first. It is used as a parent for the
1124 * sub-devices below.
1125 */
1126 err = eeepc_wmi_platform_init(eeepc);
1127 if (err)
1128 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001129
1130 err = eeepc_wmi_input_init(eeepc);
1131 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001132 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001133
Corentin Chary084fca62010-11-29 08:14:06 +01001134 err = eeepc_wmi_led_init(eeepc);
1135 if (err)
1136 goto fail_leds;
1137
Corentin Charyba48fdb2010-11-29 08:14:07 +01001138 err = eeepc_wmi_rfkill_init(eeepc);
1139 if (err)
1140 goto fail_rfkill;
1141
Yong Wang3d7b1652010-04-11 09:27:54 +08001142 if (!acpi_video_backlight_support()) {
1143 err = eeepc_wmi_backlight_init(eeepc);
1144 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001145 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001146 } else
1147 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001148
1149 status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
Corentin Chary27c136c2010-11-29 08:14:05 +01001150 eeepc_wmi_notify, eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001151 if (ACPI_FAILURE(status)) {
1152 pr_err("Unable to register notify handler - %d\n",
1153 status);
1154 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001155 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001156 }
1157
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001158 err = eeepc_wmi_debugfs_init(eeepc);
1159 if (err)
1160 goto fail_debugfs;
1161
Corentin Chary27c136c2010-11-29 08:14:05 +01001162 return eeepc->platform_device;
Yong Wang45f2c692010-04-11 09:27:19 +08001163
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001164fail_debugfs:
1165 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Corentin Chary27c136c2010-11-29 08:14:05 +01001166fail_wmi_handler:
Yong Wang3d7b1652010-04-11 09:27:54 +08001167 eeepc_wmi_backlight_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001168fail_backlight:
Corentin Charyba48fdb2010-11-29 08:14:07 +01001169 eeepc_wmi_rfkill_exit(eeepc);
1170fail_rfkill:
Corentin Chary084fca62010-11-29 08:14:06 +01001171 eeepc_wmi_led_exit(eeepc);
1172fail_leds:
Yong Wang45f2c692010-04-11 09:27:19 +08001173 eeepc_wmi_input_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001174fail_input:
1175 eeepc_wmi_platform_exit(eeepc);
1176fail_platform:
1177 kfree(eeepc);
1178 return ERR_PTR(err);
Yong Wang45f2c692010-04-11 09:27:19 +08001179}
1180
Corentin Chary27c136c2010-11-29 08:14:05 +01001181static int eeepc_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001182{
1183 struct eeepc_wmi *eeepc;
1184
1185 eeepc = platform_get_drvdata(device);
1186 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +08001187 eeepc_wmi_backlight_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001188 eeepc_wmi_input_exit(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +01001189 eeepc_wmi_led_exit(eeepc);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001190 eeepc_wmi_rfkill_exit(eeepc);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001191 eeepc_wmi_debugfs_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001192 eeepc_wmi_platform_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001193
Corentin Chary27c136c2010-11-29 08:14:05 +01001194 kfree(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001195 return 0;
1196}
1197
1198static struct platform_driver platform_driver = {
1199 .driver = {
1200 .name = EEEPC_WMI_FILE,
1201 .owner = THIS_MODULE,
1202 },
Yong Wang45f2c692010-04-11 09:27:19 +08001203};
1204
Corentin Charyd358cb52010-11-29 08:14:14 +01001205static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
1206 void *context, void **retval)
1207{
1208 pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
1209 *(bool *)context = true;
1210 return AE_CTRL_TERMINATE;
1211}
1212
1213static int __init eeepc_wmi_check_atkd(void)
1214{
1215 acpi_status status;
1216 bool found = false;
1217
1218 status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
1219 &found, NULL);
1220
1221 if (ACPI_FAILURE(status) || !found)
1222 return 0;
1223 return -1;
1224}
1225
Yong Wang45f2c692010-04-11 09:27:19 +08001226static int __init eeepc_wmi_init(void)
1227{
Yong Wang45f2c692010-04-11 09:27:19 +08001228 int err;
1229
Yong Wang3d7b1652010-04-11 09:27:54 +08001230 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
1231 !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
Yong Wang81248882010-04-11 09:26:33 +08001232 pr_warning("No known WMI GUID found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001233 return -ENODEV;
1234 }
1235
Corentin Charyd358cb52010-11-29 08:14:14 +01001236 if (eeepc_wmi_check_atkd()) {
1237 pr_warning("WMI device present, but legacy ATKD device is also "
1238 "present and enabled.");
1239 pr_warning("You probably booted with acpi_osi=\"Linux\" or "
1240 "acpi_osi=\"!Windows 2009\"");
1241 pr_warning("Can't load eeepc-wmi, use default acpi_osi "
1242 "(preferred) or eeepc-laptop");
1243 return -ENODEV;
1244 }
1245
Corentin Chary27c136c2010-11-29 08:14:05 +01001246 platform_device = eeepc_wmi_add();
1247 if (IS_ERR(platform_device)) {
1248 err = PTR_ERR(platform_device);
1249 goto fail_eeepc_wmi;
Yong Wang81248882010-04-11 09:26:33 +08001250 }
Yong Wangee027e42010-03-21 10:26:34 +08001251
Yong Wang45f2c692010-04-11 09:27:19 +08001252 err = platform_driver_register(&platform_driver);
1253 if (err) {
1254 pr_warning("Unable to register platform driver\n");
Corentin Chary27c136c2010-11-29 08:14:05 +01001255 goto fail_platform_driver;
Yong Wangee027e42010-03-21 10:26:34 +08001256 }
1257
1258 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001259
Corentin Chary27c136c2010-11-29 08:14:05 +01001260fail_platform_driver:
1261 eeepc_wmi_remove(platform_device);
1262fail_eeepc_wmi:
Yong Wang45f2c692010-04-11 09:27:19 +08001263 return err;
Yong Wangee027e42010-03-21 10:26:34 +08001264}
1265
1266static void __exit eeepc_wmi_exit(void)
1267{
Corentin Chary27c136c2010-11-29 08:14:05 +01001268 eeepc_wmi_remove(platform_device);
Yong Wang45f2c692010-04-11 09:27:19 +08001269 platform_driver_unregister(&platform_driver);
Yong Wangee027e42010-03-21 10:26:34 +08001270}
1271
1272module_init(eeepc_wmi_init);
1273module_exit(eeepc_wmi_exit);