blob: d8234582b5419cb48a605c60e9abd666f9e6523b [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;
Corentin Chary7898cf12011-02-06 13:28:30 +0100591 acpi_status status;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100592
Corentin Chary7898cf12011-02-06 13:28:30 +0100593 status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
594
595 if (ACPI_FAILURE(status))
596 return -EIO;
597
598 return 0;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100599}
600
601static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
602{
603 int dev_id = (unsigned long)data;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100604 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100605 acpi_status status;
606
Corentin Chary2a3f0062010-11-29 08:14:10 +0100607 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100608
609 if (ACPI_FAILURE(status))
610 return ;
611
Corentin Chary2a3f0062010-11-29 08:14:10 +0100612 rfkill_set_sw_state(rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100613}
614
Corentin Chary279f8f92011-02-06 13:28:29 +0100615static int eeepc_rfkill_wlan_set(void *data, bool blocked)
616{
617 struct eeepc_wmi *eeepc = data;
618 int ret;
619
620 /*
621 * This handler is enabled only if hotplug is enabled.
622 * In this case, the eeepc_wmi_set_devstate() will
623 * trigger a wmi notification and we need to wait
624 * this call to finish before being able to call
625 * any wmi method
626 */
627 mutex_lock(&eeepc->wmi_lock);
628 ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked);
629 mutex_unlock(&eeepc->wmi_lock);
630 return ret;
631}
632
633static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data)
634{
635 eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN);
636}
637
638static const struct rfkill_ops eeepc_rfkill_wlan_ops = {
639 .set_block = eeepc_rfkill_wlan_set,
640 .query = eeepc_rfkill_wlan_query,
641};
642
Corentin Charyba48fdb2010-11-29 08:14:07 +0100643static const struct rfkill_ops eeepc_rfkill_ops = {
644 .set_block = eeepc_rfkill_set,
645 .query = eeepc_rfkill_query,
646};
647
648static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
649 struct rfkill **rfkill,
650 const char *name,
651 enum rfkill_type type, int dev_id)
652{
653 int result;
Corentin Chary2a3f0062010-11-29 08:14:10 +0100654 u32 retval;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100655 acpi_status status;
656
Corentin Chary2a3f0062010-11-29 08:14:10 +0100657 status = eeepc_wmi_get_devstate(dev_id, &retval);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100658
659 if (ACPI_FAILURE(status))
660 return -1;
661
662 /* If the device is present, DSTS will always set some bits
663 * 0x00070000 - 1110000000000000000 - device supported
664 * 0x00060000 - 1100000000000000000 - not supported
665 * 0x00020000 - 0100000000000000000 - device supported
666 * 0x00010000 - 0010000000000000000 - not supported / special mode ?
667 */
Corentin Chary2a3f0062010-11-29 08:14:10 +0100668 if (!retval || retval == 0x00060000)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100669 return -ENODEV;
670
Corentin Chary279f8f92011-02-06 13:28:29 +0100671 if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless)
672 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
673 &eeepc_rfkill_wlan_ops, eeepc);
674 else
675 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
676 &eeepc_rfkill_ops, (void *)(long)dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100677
678 if (!*rfkill)
679 return -EINVAL;
680
Corentin Chary2a3f0062010-11-29 08:14:10 +0100681 rfkill_init_sw_state(*rfkill, !(retval & 0x1));
Corentin Charyba48fdb2010-11-29 08:14:07 +0100682 result = rfkill_register(*rfkill);
683 if (result) {
684 rfkill_destroy(*rfkill);
685 *rfkill = NULL;
686 return result;
687 }
688 return 0;
689}
690
691static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
692{
Corentin Charyafa7c882011-02-06 13:28:28 +0100693 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
694 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
695 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Corentin Charyba48fdb2010-11-29 08:14:07 +0100696 if (eeepc->wlan_rfkill) {
697 rfkill_unregister(eeepc->wlan_rfkill);
698 rfkill_destroy(eeepc->wlan_rfkill);
699 eeepc->wlan_rfkill = NULL;
700 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100701 /*
702 * Refresh pci hotplug in case the rfkill state was changed after
703 * eeepc_unregister_rfkill_notifier()
704 */
705 eeepc_rfkill_hotplug(eeepc);
706 if (eeepc->hotplug_slot)
707 pci_hp_deregister(eeepc->hotplug_slot);
Corentin Chary279f8f92011-02-06 13:28:29 +0100708 if (eeepc->hotplug_workqueue)
709 destroy_workqueue(eeepc->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +0100710
Corentin Charyba48fdb2010-11-29 08:14:07 +0100711 if (eeepc->bluetooth_rfkill) {
712 rfkill_unregister(eeepc->bluetooth_rfkill);
713 rfkill_destroy(eeepc->bluetooth_rfkill);
714 eeepc->bluetooth_rfkill = NULL;
715 }
716 if (eeepc->wwan3g_rfkill) {
717 rfkill_unregister(eeepc->wwan3g_rfkill);
718 rfkill_destroy(eeepc->wwan3g_rfkill);
719 eeepc->wwan3g_rfkill = NULL;
720 }
721}
722
723static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
724{
725 int result = 0;
726
Corentin Charyafa7c882011-02-06 13:28:28 +0100727 mutex_init(&eeepc->hotplug_lock);
Corentin Chary279f8f92011-02-06 13:28:29 +0100728 mutex_init(&eeepc->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100729
Corentin Charyba48fdb2010-11-29 08:14:07 +0100730 result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
731 "eeepc-wlan", RFKILL_TYPE_WLAN,
732 EEEPC_WMI_DEVID_WLAN);
733
734 if (result && result != -ENODEV)
735 goto exit;
736
737 result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
738 "eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
739 EEEPC_WMI_DEVID_BLUETOOTH);
740
741 if (result && result != -ENODEV)
742 goto exit;
743
744 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
745 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
746 EEEPC_WMI_DEVID_WWAN3G);
747
748 if (result && result != -ENODEV)
749 goto exit;
750
Corentin Charyafa7c882011-02-06 13:28:28 +0100751 result = eeepc_setup_pci_hotplug(eeepc);
752 /*
753 * If we get -EBUSY then something else is handling the PCI hotplug -
754 * don't fail in this case
755 */
756 if (result == -EBUSY)
757 result = 0;
758
759 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
760 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
761 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
762 /*
763 * Refresh pci hotplug in case the rfkill state was changed during
764 * setup.
765 */
766 eeepc_rfkill_hotplug(eeepc);
767
Corentin Charyba48fdb2010-11-29 08:14:07 +0100768exit:
769 if (result && result != -ENODEV)
770 eeepc_wmi_rfkill_exit(eeepc);
771
772 if (result == -ENODEV)
773 result = 0;
774
775 return result;
776}
777
778/*
Corentin Chary084fca62010-11-29 08:14:06 +0100779 * Backlight
780 */
Yong Wang3d7b1652010-04-11 09:27:54 +0800781static int read_brightness(struct backlight_device *bd)
782{
Corentin Charydfed65d2010-11-29 08:14:12 +0100783 u32 retval;
Yong Wang3d7b1652010-04-11 09:27:54 +0800784 acpi_status status;
785
Corentin Chary2a3f0062010-11-29 08:14:10 +0100786 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800787
788 if (ACPI_FAILURE(status))
789 return -1;
790 else
Corentin Chary2a3f0062010-11-29 08:14:10 +0100791 return retval & 0xFF;
Yong Wang3d7b1652010-04-11 09:27:54 +0800792}
793
794static int update_bl_status(struct backlight_device *bd)
795{
796
Corentin Charydfed65d2010-11-29 08:14:12 +0100797 u32 ctrl_param;
Yong Wang3d7b1652010-04-11 09:27:54 +0800798 acpi_status status;
799
800 ctrl_param = bd->props.brightness;
801
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100802 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
803 ctrl_param, NULL);
Yong Wang3d7b1652010-04-11 09:27:54 +0800804
805 if (ACPI_FAILURE(status))
806 return -1;
807 else
808 return 0;
809}
810
811static const struct backlight_ops eeepc_wmi_bl_ops = {
812 .get_brightness = read_brightness,
813 .update_status = update_bl_status,
814};
815
816static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
817{
818 struct backlight_device *bd = eeepc->backlight_device;
819 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +0200820 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +0800821
822 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
823 new = code - NOTIFY_BRNUP_MIN + 1;
824 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
825 new = code - NOTIFY_BRNDOWN_MIN;
826
827 bd->props.brightness = new;
828 backlight_update_status(bd);
829 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
830
831 return old;
832}
833
834static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
835{
836 struct backlight_device *bd;
837 struct backlight_properties props;
838
839 memset(&props, 0, sizeof(struct backlight_properties));
840 props.max_brightness = 15;
841 bd = backlight_device_register(EEEPC_WMI_FILE,
Corentin Chary27c136c2010-11-29 08:14:05 +0100842 &eeepc->platform_device->dev, eeepc,
Yong Wang3d7b1652010-04-11 09:27:54 +0800843 &eeepc_wmi_bl_ops, &props);
844 if (IS_ERR(bd)) {
845 pr_err("Could not register backlight device\n");
846 return PTR_ERR(bd);
847 }
848
849 eeepc->backlight_device = bd;
850
851 bd->props.brightness = read_brightness(bd);
852 bd->props.power = FB_BLANK_UNBLANK;
853 backlight_update_status(bd);
854
855 return 0;
856}
857
858static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
859{
860 if (eeepc->backlight_device)
861 backlight_device_unregister(eeepc->backlight_device);
862
863 eeepc->backlight_device = NULL;
864}
865
866static void eeepc_wmi_notify(u32 value, void *context)
867{
868 struct eeepc_wmi *eeepc = context;
869 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
870 union acpi_object *obj;
871 acpi_status status;
872 int code;
873 int orig_code;
874
875 status = wmi_get_event_data(value, &response);
876 if (status != AE_OK) {
877 pr_err("bad event status 0x%x\n", status);
878 return;
879 }
880
881 obj = (union acpi_object *)response.pointer;
882
883 if (obj && obj->type == ACPI_TYPE_INTEGER) {
884 code = obj->integer.value;
885 orig_code = code;
886
887 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
888 code = NOTIFY_BRNUP_MIN;
889 else if (code >= NOTIFY_BRNDOWN_MIN &&
890 code <= NOTIFY_BRNDOWN_MAX)
891 code = NOTIFY_BRNDOWN_MIN;
892
893 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
894 if (!acpi_video_backlight_support())
895 eeepc_wmi_backlight_notify(eeepc, orig_code);
896 }
897
898 if (!sparse_keymap_report_event(eeepc->inputdev,
899 code, 1, true))
900 pr_info("Unknown key %x pressed\n", code);
901 }
902
903 kfree(obj);
904}
905
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -0700906static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
907 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -0500908{
909 int value;
910 struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
911 acpi_status status;
912
913 if (!count || sscanf(buf, "%i", &value) != 1)
914 return -EINVAL;
915 if (value < 0 || value > 2)
916 return -EINVAL;
917
918 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
919 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
920
921 if (ACPI_FAILURE(status))
922 return -EIO;
923 else
924 return count;
925}
926
927static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
928
Corentin Chary4e37b422010-11-29 08:14:08 +0100929static struct attribute *platform_attributes[] = {
930 &dev_attr_cpufv.attr,
931 NULL
932};
933
934static struct attribute_group platform_attribute_group = {
935 .attrs = platform_attributes
936};
937
Chris Bagwell7f80d732010-10-11 18:47:18 -0500938static void eeepc_wmi_sysfs_exit(struct platform_device *device)
939{
Corentin Chary4e37b422010-11-29 08:14:08 +0100940 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500941}
942
943static int eeepc_wmi_sysfs_init(struct platform_device *device)
944{
Corentin Chary4e37b422010-11-29 08:14:08 +0100945 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -0500946}
947
Corentin Chary27c136c2010-11-29 08:14:05 +0100948/*
949 * Platform device
950 */
951static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
952{
953 int err;
954
955 eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
956 if (!eeepc->platform_device)
957 return -ENOMEM;
958 platform_set_drvdata(eeepc->platform_device, eeepc);
959
960 err = platform_device_add(eeepc->platform_device);
961 if (err)
962 goto fail_platform_device;
963
964 err = eeepc_wmi_sysfs_init(eeepc->platform_device);
965 if (err)
966 goto fail_sysfs;
967 return 0;
968
969fail_sysfs:
970 platform_device_del(eeepc->platform_device);
971fail_platform_device:
972 platform_device_put(eeepc->platform_device);
973 return err;
974}
975
976static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
977{
978 eeepc_wmi_sysfs_exit(eeepc->platform_device);
979 platform_device_unregister(eeepc->platform_device);
980}
981
982/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100983 * debugfs
984 */
985struct eeepc_wmi_debugfs_node {
986 struct eeepc_wmi *eeepc;
987 char *name;
988 int (*show)(struct seq_file *m, void *data);
989};
990
991static int show_dsts(struct seq_file *m, void *data)
992{
993 struct eeepc_wmi *eeepc = m->private;
994 acpi_status status;
995 u32 retval = -1;
996
997 status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
998
999 if (ACPI_FAILURE(status))
1000 return -EIO;
1001
1002 seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
1003
1004 return 0;
1005}
1006
1007static int show_devs(struct seq_file *m, void *data)
1008{
1009 struct eeepc_wmi *eeepc = m->private;
1010 acpi_status status;
1011 u32 retval = -1;
1012
1013 status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
1014 eeepc->debug.ctrl_param, &retval);
1015 if (ACPI_FAILURE(status))
1016 return -EIO;
1017
1018 seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
1019 eeepc->debug.ctrl_param, retval);
1020
1021 return 0;
1022}
1023
1024static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
1025 { NULL, "devs", show_devs },
1026 { NULL, "dsts", show_dsts },
1027};
1028
1029static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
1030{
1031 struct eeepc_wmi_debugfs_node *node = inode->i_private;
1032
1033 return single_open(file, node->show, node->eeepc);
1034}
1035
1036static const struct file_operations eeepc_wmi_debugfs_io_ops = {
1037 .owner = THIS_MODULE,
1038 .open = eeepc_wmi_debugfs_open,
1039 .read = seq_read,
1040 .llseek = seq_lseek,
1041 .release = single_release,
1042};
1043
1044static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
1045{
1046 debugfs_remove_recursive(eeepc->debug.root);
1047}
1048
1049static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
1050{
1051 struct dentry *dent;
1052 int i;
1053
1054 eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
1055 if (!eeepc->debug.root) {
1056 pr_err("failed to create debugfs directory");
1057 goto error_debugfs;
1058 }
1059
1060 dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
1061 eeepc->debug.root, &eeepc->debug.dev_id);
1062 if (!dent)
1063 goto error_debugfs;
1064
1065 dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
1066 eeepc->debug.root, &eeepc->debug.ctrl_param);
1067 if (!dent)
1068 goto error_debugfs;
1069
1070 for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
1071 struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
1072
1073 node->eeepc = eeepc;
1074 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1075 eeepc->debug.root, node,
1076 &eeepc_wmi_debugfs_io_ops);
1077 if (!dent) {
1078 pr_err("failed to create debug file: %s\n", node->name);
1079 goto error_debugfs;
1080 }
1081 }
1082
1083 return 0;
1084
1085error_debugfs:
1086 eeepc_wmi_debugfs_exit(eeepc);
1087 return -ENOMEM;
1088}
1089
1090/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001091 * WMI Driver
1092 */
Corentin Charyafa7c882011-02-06 13:28:28 +01001093static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
1094{
1095 const char *model;
1096
1097 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1098 if (!model)
1099 return;
1100
1101 /*
1102 * Whitelist for wlan hotplug
1103 *
1104 * Eeepc 1000H needs the current hotplug code to handle
1105 * Fn+F2 correctly. We may add other Eeepc here later, but
1106 * it seems that most of the laptops supported by eeepc-wmi
1107 * don't need to be on this list
1108 */
1109 if (strcmp(model, "1000H") == 0) {
1110 eeepc->hotplug_wireless = true;
1111 pr_info("wlan hotplug enabled\n");
1112 }
1113}
1114
Corentin Chary27c136c2010-11-29 08:14:05 +01001115static struct platform_device * __init eeepc_wmi_add(void)
Yong Wangee027e42010-03-21 10:26:34 +08001116{
Yong Wang45f2c692010-04-11 09:27:19 +08001117 struct eeepc_wmi *eeepc;
Yong Wangee027e42010-03-21 10:26:34 +08001118 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001119 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001120
Corentin Chary27c136c2010-11-29 08:14:05 +01001121 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
1122 if (!eeepc)
1123 return ERR_PTR(-ENOMEM);
1124
Corentin Charyafa7c882011-02-06 13:28:28 +01001125 eeepc->hotplug_wireless = hotplug_wireless;
1126 eeepc_dmi_check(eeepc);
1127
Corentin Chary27c136c2010-11-29 08:14:05 +01001128 /*
1129 * Register the platform device first. It is used as a parent for the
1130 * sub-devices below.
1131 */
1132 err = eeepc_wmi_platform_init(eeepc);
1133 if (err)
1134 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001135
1136 err = eeepc_wmi_input_init(eeepc);
1137 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001138 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001139
Corentin Chary084fca62010-11-29 08:14:06 +01001140 err = eeepc_wmi_led_init(eeepc);
1141 if (err)
1142 goto fail_leds;
1143
Corentin Charyba48fdb2010-11-29 08:14:07 +01001144 err = eeepc_wmi_rfkill_init(eeepc);
1145 if (err)
1146 goto fail_rfkill;
1147
Yong Wang3d7b1652010-04-11 09:27:54 +08001148 if (!acpi_video_backlight_support()) {
1149 err = eeepc_wmi_backlight_init(eeepc);
1150 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001151 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001152 } else
1153 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001154
1155 status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
Corentin Chary27c136c2010-11-29 08:14:05 +01001156 eeepc_wmi_notify, eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001157 if (ACPI_FAILURE(status)) {
1158 pr_err("Unable to register notify handler - %d\n",
1159 status);
1160 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001161 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001162 }
1163
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001164 err = eeepc_wmi_debugfs_init(eeepc);
1165 if (err)
1166 goto fail_debugfs;
1167
Corentin Chary27c136c2010-11-29 08:14:05 +01001168 return eeepc->platform_device;
Yong Wang45f2c692010-04-11 09:27:19 +08001169
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001170fail_debugfs:
1171 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Corentin Chary27c136c2010-11-29 08:14:05 +01001172fail_wmi_handler:
Yong Wang3d7b1652010-04-11 09:27:54 +08001173 eeepc_wmi_backlight_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001174fail_backlight:
Corentin Charyba48fdb2010-11-29 08:14:07 +01001175 eeepc_wmi_rfkill_exit(eeepc);
1176fail_rfkill:
Corentin Chary084fca62010-11-29 08:14:06 +01001177 eeepc_wmi_led_exit(eeepc);
1178fail_leds:
Yong Wang45f2c692010-04-11 09:27:19 +08001179 eeepc_wmi_input_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001180fail_input:
1181 eeepc_wmi_platform_exit(eeepc);
1182fail_platform:
1183 kfree(eeepc);
1184 return ERR_PTR(err);
Yong Wang45f2c692010-04-11 09:27:19 +08001185}
1186
Corentin Chary27c136c2010-11-29 08:14:05 +01001187static int eeepc_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001188{
1189 struct eeepc_wmi *eeepc;
1190
1191 eeepc = platform_get_drvdata(device);
1192 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +08001193 eeepc_wmi_backlight_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001194 eeepc_wmi_input_exit(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +01001195 eeepc_wmi_led_exit(eeepc);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001196 eeepc_wmi_rfkill_exit(eeepc);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001197 eeepc_wmi_debugfs_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001198 eeepc_wmi_platform_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001199
Corentin Chary27c136c2010-11-29 08:14:05 +01001200 kfree(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001201 return 0;
1202}
1203
1204static struct platform_driver platform_driver = {
1205 .driver = {
1206 .name = EEEPC_WMI_FILE,
1207 .owner = THIS_MODULE,
1208 },
Yong Wang45f2c692010-04-11 09:27:19 +08001209};
1210
Corentin Charyd358cb52010-11-29 08:14:14 +01001211static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
1212 void *context, void **retval)
1213{
1214 pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
1215 *(bool *)context = true;
1216 return AE_CTRL_TERMINATE;
1217}
1218
1219static int __init eeepc_wmi_check_atkd(void)
1220{
1221 acpi_status status;
1222 bool found = false;
1223
1224 status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
1225 &found, NULL);
1226
1227 if (ACPI_FAILURE(status) || !found)
1228 return 0;
1229 return -1;
1230}
1231
Yong Wang45f2c692010-04-11 09:27:19 +08001232static int __init eeepc_wmi_init(void)
1233{
Yong Wang45f2c692010-04-11 09:27:19 +08001234 int err;
1235
Yong Wang3d7b1652010-04-11 09:27:54 +08001236 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
1237 !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
Yong Wang81248882010-04-11 09:26:33 +08001238 pr_warning("No known WMI GUID found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001239 return -ENODEV;
1240 }
1241
Corentin Charyd358cb52010-11-29 08:14:14 +01001242 if (eeepc_wmi_check_atkd()) {
1243 pr_warning("WMI device present, but legacy ATKD device is also "
1244 "present and enabled.");
1245 pr_warning("You probably booted with acpi_osi=\"Linux\" or "
1246 "acpi_osi=\"!Windows 2009\"");
1247 pr_warning("Can't load eeepc-wmi, use default acpi_osi "
1248 "(preferred) or eeepc-laptop");
1249 return -ENODEV;
1250 }
1251
Corentin Chary27c136c2010-11-29 08:14:05 +01001252 platform_device = eeepc_wmi_add();
1253 if (IS_ERR(platform_device)) {
1254 err = PTR_ERR(platform_device);
1255 goto fail_eeepc_wmi;
Yong Wang81248882010-04-11 09:26:33 +08001256 }
Yong Wangee027e42010-03-21 10:26:34 +08001257
Yong Wang45f2c692010-04-11 09:27:19 +08001258 err = platform_driver_register(&platform_driver);
1259 if (err) {
1260 pr_warning("Unable to register platform driver\n");
Corentin Chary27c136c2010-11-29 08:14:05 +01001261 goto fail_platform_driver;
Yong Wangee027e42010-03-21 10:26:34 +08001262 }
1263
1264 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001265
Corentin Chary27c136c2010-11-29 08:14:05 +01001266fail_platform_driver:
1267 eeepc_wmi_remove(platform_device);
1268fail_eeepc_wmi:
Yong Wang45f2c692010-04-11 09:27:19 +08001269 return err;
Yong Wangee027e42010-03-21 10:26:34 +08001270}
1271
1272static void __exit eeepc_wmi_exit(void)
1273{
Corentin Chary27c136c2010-11-29 08:14:05 +01001274 eeepc_wmi_remove(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);