blob: 16c7f2d62515d850dd99a21b2310de14914bf3bd [file] [log] [blame]
Yong Wangee027e42010-03-21 10:26:34 +08001/*
2 * Eee PC WMI hotkey driver
3 *
4 * Copyright(C) 2010 Intel Corporation.
Corentin Chary4c4edfa2010-11-29 08:14:11 +01005 * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
Yong Wangee027e42010-03-21 10:26:34 +08006 *
7 * Portions based on wistron_btns.c:
8 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
9 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
10 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
Yong Wang81248882010-04-11 09:26:33 +080027#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28
Yong Wangee027e42010-03-21 10:26:34 +080029#include <linux/kernel.h>
30#include <linux/module.h>
31#include <linux/init.h>
32#include <linux/types.h>
Tejun Heoa32f3922010-04-05 11:37:59 +090033#include <linux/slab.h>
Yong Wangee027e42010-03-21 10:26:34 +080034#include <linux/input.h>
35#include <linux/input/sparse-keymap.h>
Yong Wang3d7b1652010-04-11 09:27:54 +080036#include <linux/fb.h>
37#include <linux/backlight.h>
Corentin Chary084fca62010-11-29 08:14:06 +010038#include <linux/leds.h>
Corentin Charyba48fdb2010-11-29 08:14:07 +010039#include <linux/rfkill.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010040#include <linux/pci.h>
41#include <linux/pci_hotplug.h>
Corentin Chary8c1b2d82010-11-29 08:14:09 +010042#include <linux/debugfs.h>
43#include <linux/seq_file.h>
Yong Wang45f2c692010-04-11 09:27:19 +080044#include <linux/platform_device.h>
Corentin Charyafa7c882011-02-06 13:28:28 +010045#include <linux/dmi.h>
Yong Wangee027e42010-03-21 10:26:34 +080046#include <acpi/acpi_bus.h>
47#include <acpi/acpi_drivers.h>
48
Yong Wang45f2c692010-04-11 09:27:19 +080049#define EEEPC_WMI_FILE "eeepc-wmi"
50
Yong Wangee027e42010-03-21 10:26:34 +080051MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
52MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
53MODULE_LICENSE("GPL");
54
Corentin Charyd358cb52010-11-29 08:14:14 +010055#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
56
Yong Wangee027e42010-03-21 10:26:34 +080057#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
Yong Wang3d7b1652010-04-11 09:27:54 +080058#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
Yong Wangee027e42010-03-21 10:26:34 +080059
60MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +080061MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
Yong Wangee027e42010-03-21 10:26:34 +080062
Corentin Chary33e0e6f2011-02-06 13:28:34 +010063#define NOTIFY_BRNUP_MIN 0x11
64#define NOTIFY_BRNUP_MAX 0x1f
65#define NOTIFY_BRNDOWN_MIN 0x20
66#define NOTIFY_BRNDOWN_MAX 0x2e
Yong Wangee027e42010-03-21 10:26:34 +080067
Corentin Chary33e0e6f2011-02-06 13:28:34 +010068#define EEEPC_WMI_METHODID_DSTS 0x53544344
69#define EEEPC_WMI_METHODID_DEVS 0x53564544
70#define EEEPC_WMI_METHODID_CFVS 0x53564643
Yong Wang3d7b1652010-04-11 09:27:54 +080071
Corentin Charyba48fdb2010-11-29 08:14:07 +010072#define EEEPC_WMI_DEVID_WLAN 0x00010011
73#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
Corentin Chary2e9e1592011-02-06 13:28:37 +010074#define EEEPC_WMI_DEVID_WIMAX 0x00010017
Corentin Charyba48fdb2010-11-29 08:14:07 +010075#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
Corentin Charyb7187262011-02-06 13:28:39 +010076#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050011
77#define EEEPC_WMI_DEVID_BRIGHTNESS 0x00050012
Corentin Chary9e1565b2011-02-06 13:28:36 +010078#define EEEPC_WMI_DEVID_CAMERA 0x00060013
79#define EEEPC_WMI_DEVID_CARDREADER 0x00080013
Corentin Chary4615bb62011-02-06 13:28:42 +010080#define EEEPC_WMI_DEVID_TOUCHPAD 0x00100011
Corentin Chary8571d752011-02-06 13:28:41 +010081#define EEEPC_WMI_DEVID_TOUCHPAD_LED 0x00100012
Yong Wang3d7b1652010-04-11 09:27:54 +080082
Corentin Charyaafa7192011-02-06 13:28:35 +010083#define EEEPC_WMI_DSTS_STATUS_BIT 0x00000001
84#define EEEPC_WMI_DSTS_PRESENCE_BIT 0x00010000
Corentin Charyb7187262011-02-06 13:28:39 +010085#define EEEPC_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF
86#define EEEPC_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00
Corentin Charyaafa7192011-02-06 13:28:35 +010087
Corentin Charyafa7c882011-02-06 13:28:28 +010088static bool hotplug_wireless;
89
90module_param(hotplug_wireless, bool, 0444);
91MODULE_PARM_DESC(hotplug_wireless,
92 "Enable hotplug for wireless device. "
93 "If your laptop needs that, please report to "
94 "acpi4asus-user@lists.sourceforge.net.");
95
Yong Wangee027e42010-03-21 10:26:34 +080096static const struct key_entry eeepc_wmi_keymap[] = {
97 /* Sleep already handled via generic ACPI code */
Yong Wangee027e42010-03-21 10:26:34 +080098 { KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
99 { KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
Corentin Chary5628e5a2011-02-06 13:28:26 +0100100 { KE_KEY, 0x30, { KEY_VOLUMEUP } },
101 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
102 { KE_KEY, 0x32, { KEY_MUTE } },
Corentin Chary77ca5b02011-02-06 13:30:48 +0100103 { KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */
Corentin Chary5628e5a2011-02-06 13:28:26 +0100104 { KE_KEY, 0x5d, { KEY_WLAN } },
Chris Bagwelleda17482010-10-11 18:47:17 -0500105 { KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
Corentin Charybc40cce2011-02-06 13:28:27 +0100106 { KE_KEY, 0x88, { KEY_WLAN } },
Corentin Chary5628e5a2011-02-06 13:28:26 +0100107 { KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
Corentin Chary77ca5b02011-02-06 13:30:48 +0100108 { KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
109 { KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
Corentin Chary54c799a2011-02-06 13:28:38 +0100110 { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
Yong Wangee027e42010-03-21 10:26:34 +0800111 { KE_END, 0},
112};
113
Yong Wang3d7b1652010-04-11 09:27:54 +0800114struct bios_args {
115 u32 dev_id;
116 u32 ctrl_param;
117};
118
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100119/*
120 * eeepc-wmi/ - debugfs root directory
121 * dev_id - current dev_id
122 * ctrl_param - current ctrl_param
123 * devs - call DEVS(dev_id, ctrl_param) and print result
124 * dsts - call DSTS(dev_id) and print result
125 */
126struct eeepc_wmi_debug {
127 struct dentry *root;
128 u32 dev_id;
129 u32 ctrl_param;
130};
131
Yong Wang81248882010-04-11 09:26:33 +0800132struct eeepc_wmi {
Corentin Charyafa7c882011-02-06 13:28:28 +0100133 bool hotplug_wireless;
134
Yong Wang81248882010-04-11 09:26:33 +0800135 struct input_dev *inputdev;
Yong Wang3d7b1652010-04-11 09:27:54 +0800136 struct backlight_device *backlight_device;
Corentin Chary27c136c2010-11-29 08:14:05 +0100137 struct platform_device *platform_device;
Corentin Chary084fca62010-11-29 08:14:06 +0100138
139 struct led_classdev tpd_led;
140 int tpd_led_wk;
141 struct workqueue_struct *led_workqueue;
142 struct work_struct tpd_led_work;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100143
144 struct rfkill *wlan_rfkill;
145 struct rfkill *bluetooth_rfkill;
Corentin Chary2e9e1592011-02-06 13:28:37 +0100146 struct rfkill *wimax_rfkill;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100147 struct rfkill *wwan3g_rfkill;
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100148
Corentin Charyafa7c882011-02-06 13:28:28 +0100149 struct hotplug_slot *hotplug_slot;
150 struct mutex hotplug_lock;
Corentin Chary279f8f92011-02-06 13:28:29 +0100151 struct mutex wmi_lock;
152 struct workqueue_struct *hotplug_workqueue;
153 struct work_struct hotplug_work;
Corentin Charyafa7c882011-02-06 13:28:28 +0100154
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100155 struct eeepc_wmi_debug debug;
Yong Wang81248882010-04-11 09:26:33 +0800156};
157
Yong Wang81248882010-04-11 09:26:33 +0800158static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
Yong Wangee027e42010-03-21 10:26:34 +0800159{
160 int err;
161
Yong Wang81248882010-04-11 09:26:33 +0800162 eeepc->inputdev = input_allocate_device();
163 if (!eeepc->inputdev)
Yong Wangee027e42010-03-21 10:26:34 +0800164 return -ENOMEM;
165
Yong Wang81248882010-04-11 09:26:33 +0800166 eeepc->inputdev->name = "Eee PC WMI hotkeys";
Yong Wang45f2c692010-04-11 09:27:19 +0800167 eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
Yong Wang81248882010-04-11 09:26:33 +0800168 eeepc->inputdev->id.bustype = BUS_HOST;
Corentin Chary27c136c2010-11-29 08:14:05 +0100169 eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
Yong Wangee027e42010-03-21 10:26:34 +0800170
Yong Wang81248882010-04-11 09:26:33 +0800171 err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
Yong Wangee027e42010-03-21 10:26:34 +0800172 if (err)
173 goto err_free_dev;
174
Yong Wang81248882010-04-11 09:26:33 +0800175 err = input_register_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800176 if (err)
177 goto err_free_keymap;
178
179 return 0;
180
181err_free_keymap:
Yong Wang81248882010-04-11 09:26:33 +0800182 sparse_keymap_free(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800183err_free_dev:
Yong Wang81248882010-04-11 09:26:33 +0800184 input_free_device(eeepc->inputdev);
Yong Wangee027e42010-03-21 10:26:34 +0800185 return err;
186}
187
Yong Wang81248882010-04-11 09:26:33 +0800188static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
189{
190 if (eeepc->inputdev) {
191 sparse_keymap_free(eeepc->inputdev);
192 input_unregister_device(eeepc->inputdev);
193 }
194
195 eeepc->inputdev = NULL;
196}
197
Corentin Chary2a3f0062010-11-29 08:14:10 +0100198static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800199{
200 struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
201 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
202 union acpi_object *obj;
203 acpi_status status;
204 u32 tmp;
205
206 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
Corentin Charyafa7c882011-02-06 13:28:28 +0100207 1, EEEPC_WMI_METHODID_DSTS,
208 &input, &output);
Yong Wang3d7b1652010-04-11 09:27:54 +0800209
210 if (ACPI_FAILURE(status))
211 return status;
212
213 obj = (union acpi_object *)output.pointer;
214 if (obj && obj->type == ACPI_TYPE_INTEGER)
215 tmp = (u32)obj->integer.value;
216 else
217 tmp = 0;
218
Corentin Chary2a3f0062010-11-29 08:14:10 +0100219 if (retval)
220 *retval = tmp;
Yong Wang3d7b1652010-04-11 09:27:54 +0800221
222 kfree(obj);
223
224 return status;
225
226}
227
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100228static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
229 u32 *retval)
Yong Wang3d7b1652010-04-11 09:27:54 +0800230{
231 struct bios_args args = {
232 .dev_id = dev_id,
233 .ctrl_param = ctrl_param,
234 };
235 struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
236 acpi_status status;
237
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100238 if (!retval) {
239 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
240 EEEPC_WMI_METHODID_DEVS,
241 &input, NULL);
242 } else {
243 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
244 union acpi_object *obj;
245 u32 tmp;
246
247 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
248 EEEPC_WMI_METHODID_DEVS,
249 &input, &output);
250
251 if (ACPI_FAILURE(status))
252 return status;
253
254 obj = (union acpi_object *)output.pointer;
255 if (obj && obj->type == ACPI_TYPE_INTEGER)
256 tmp = (u32)obj->integer.value;
257 else
258 tmp = 0;
259
260 *retval = tmp;
261
262 kfree(obj);
263 }
Yong Wang3d7b1652010-04-11 09:27:54 +0800264
265 return status;
266}
267
Corentin Chary5c956382011-02-06 13:28:31 +0100268/* Helper for special devices with magic return codes */
Corentin Charyb7187262011-02-06 13:28:39 +0100269static int eeepc_wmi_get_devstate_bits(u32 dev_id, u32 mask)
Corentin Chary5c956382011-02-06 13:28:31 +0100270{
271 u32 retval = 0;
272 acpi_status status;
273
274 status = eeepc_wmi_get_devstate(dev_id, &retval);
275
276 if (ACPI_FAILURE(status))
277 return -EINVAL;
278
Corentin Charyaafa7192011-02-06 13:28:35 +0100279 if (!(retval & EEEPC_WMI_DSTS_PRESENCE_BIT))
Corentin Chary5c956382011-02-06 13:28:31 +0100280 return -ENODEV;
281
Corentin Charyb7187262011-02-06 13:28:39 +0100282 return retval & mask;
283}
284
285static int eeepc_wmi_get_devstate_simple(u32 dev_id)
286{
287 return eeepc_wmi_get_devstate_bits(dev_id, EEEPC_WMI_DSTS_STATUS_BIT);
Corentin Chary5c956382011-02-06 13:28:31 +0100288}
289
Corentin Chary084fca62010-11-29 08:14:06 +0100290/*
291 * LEDs
292 */
293/*
294 * These functions actually update the LED's, and are called from a
295 * workqueue. By doing this as separate work rather than when the LED
296 * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
297 * potentially bad time, such as a timer interrupt.
298 */
299static void tpd_led_update(struct work_struct *work)
300{
301 int ctrl_param;
302 struct eeepc_wmi *eeepc;
303
304 eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
305
306 ctrl_param = eeepc->tpd_led_wk;
Corentin Chary8571d752011-02-06 13:28:41 +0100307 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TOUCHPAD_LED, ctrl_param, NULL);
Corentin Chary084fca62010-11-29 08:14:06 +0100308}
309
310static void tpd_led_set(struct led_classdev *led_cdev,
311 enum led_brightness value)
312{
313 struct eeepc_wmi *eeepc;
314
315 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
316
317 eeepc->tpd_led_wk = !!value;
318 queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
319}
320
Corentin Chary8571d752011-02-06 13:28:41 +0100321static int read_tpd_led_state(struct eeepc_wmi *eeepc)
Corentin Chary084fca62010-11-29 08:14:06 +0100322{
Corentin Chary8571d752011-02-06 13:28:41 +0100323 return eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_TOUCHPAD_LED);
Corentin Chary084fca62010-11-29 08:14:06 +0100324}
325
326static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
327{
328 struct eeepc_wmi *eeepc;
329
330 eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
331
Corentin Chary8571d752011-02-06 13:28:41 +0100332 return read_tpd_led_state(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +0100333}
334
335static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
336{
337 int rv;
338
Corentin Chary8571d752011-02-06 13:28:41 +0100339 if (read_tpd_led_state(eeepc) < 0)
Corentin Chary084fca62010-11-29 08:14:06 +0100340 return 0;
341
342 eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
343 if (!eeepc->led_workqueue)
344 return -ENOMEM;
345 INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
346
347 eeepc->tpd_led.name = "eeepc::touchpad";
348 eeepc->tpd_led.brightness_set = tpd_led_set;
349 eeepc->tpd_led.brightness_get = tpd_led_get;
350 eeepc->tpd_led.max_brightness = 1;
351
352 rv = led_classdev_register(&eeepc->platform_device->dev,
353 &eeepc->tpd_led);
354 if (rv) {
355 destroy_workqueue(eeepc->led_workqueue);
356 return rv;
357 }
358
359 return 0;
360}
361
362static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
363{
364 if (eeepc->tpd_led.dev)
365 led_classdev_unregister(&eeepc->tpd_led);
366 if (eeepc->led_workqueue)
367 destroy_workqueue(eeepc->led_workqueue);
368}
369
370/*
Corentin Charyafa7c882011-02-06 13:28:28 +0100371 * PCI hotplug (for wlan rfkill)
372 */
373static bool eeepc_wlan_rfkill_blocked(struct eeepc_wmi *eeepc)
374{
Corentin Chary5c956382011-02-06 13:28:31 +0100375 int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100376
Corentin Chary5c956382011-02-06 13:28:31 +0100377 if (result < 0)
Corentin Charyafa7c882011-02-06 13:28:28 +0100378 return false;
Corentin Chary5c956382011-02-06 13:28:31 +0100379 return !result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100380}
381
382static void eeepc_rfkill_hotplug(struct eeepc_wmi *eeepc)
383{
384 struct pci_dev *dev;
385 struct pci_bus *bus;
Corentin Chary279f8f92011-02-06 13:28:29 +0100386 bool blocked;
Corentin Charyafa7c882011-02-06 13:28:28 +0100387 bool absent;
388 u32 l;
389
Corentin Chary279f8f92011-02-06 13:28:29 +0100390 mutex_lock(&eeepc->wmi_lock);
391 blocked = eeepc_wlan_rfkill_blocked(eeepc);
392 mutex_unlock(&eeepc->wmi_lock);
Corentin Charyafa7c882011-02-06 13:28:28 +0100393
394 mutex_lock(&eeepc->hotplug_lock);
395
Corentin Chary279f8f92011-02-06 13:28:29 +0100396 if (eeepc->wlan_rfkill)
397 rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
398
Corentin Charyafa7c882011-02-06 13:28:28 +0100399 if (eeepc->hotplug_slot) {
400 bus = pci_find_bus(0, 1);
401 if (!bus) {
402 pr_warning("Unable to find PCI bus 1?\n");
403 goto out_unlock;
404 }
405
406 if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
407 pr_err("Unable to read PCI config space?\n");
408 goto out_unlock;
409 }
410 absent = (l == 0xffffffff);
411
412 if (blocked != absent) {
413 pr_warning("BIOS says wireless lan is %s, "
414 "but the pci device is %s\n",
415 blocked ? "blocked" : "unblocked",
416 absent ? "absent" : "present");
417 pr_warning("skipped wireless hotplug as probably "
418 "inappropriate for this model\n");
419 goto out_unlock;
420 }
421
422 if (!blocked) {
423 dev = pci_get_slot(bus, 0);
424 if (dev) {
425 /* Device already present */
426 pci_dev_put(dev);
427 goto out_unlock;
428 }
429 dev = pci_scan_single_device(bus, 0);
430 if (dev) {
431 pci_bus_assign_resources(bus);
432 if (pci_bus_add_device(dev))
433 pr_err("Unable to hotplug wifi\n");
434 }
435 } else {
436 dev = pci_get_slot(bus, 0);
437 if (dev) {
438 pci_remove_bus_device(dev);
439 pci_dev_put(dev);
440 }
441 }
442 }
443
444out_unlock:
445 mutex_unlock(&eeepc->hotplug_lock);
446}
447
448static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
449{
450 struct eeepc_wmi *eeepc = data;
451
452 if (event != ACPI_NOTIFY_BUS_CHECK)
453 return;
454
Corentin Chary279f8f92011-02-06 13:28:29 +0100455 /*
456 * We can't call directly eeepc_rfkill_hotplug because most
457 * of the time WMBC is still being executed and not reetrant.
458 * There is currently no way to tell ACPICA that we want this
459 * method to be serialized, we schedule a eeepc_rfkill_hotplug
460 * call later, in a safer context.
461 */
462 queue_work(eeepc->hotplug_workqueue, &eeepc->hotplug_work);
Corentin Charyafa7c882011-02-06 13:28:28 +0100463}
464
465static int eeepc_register_rfkill_notifier(struct eeepc_wmi *eeepc,
466 char *node)
467{
468 acpi_status status;
469 acpi_handle handle;
470
471 status = acpi_get_handle(NULL, node, &handle);
472
473 if (ACPI_SUCCESS(status)) {
474 status = acpi_install_notify_handler(handle,
475 ACPI_SYSTEM_NOTIFY,
476 eeepc_rfkill_notify,
477 eeepc);
478 if (ACPI_FAILURE(status))
479 pr_warning("Failed to register notify on %s\n", node);
480 } else
481 return -ENODEV;
482
483 return 0;
484}
485
486static void eeepc_unregister_rfkill_notifier(struct eeepc_wmi *eeepc,
487 char *node)
488{
489 acpi_status status = AE_OK;
490 acpi_handle handle;
491
492 status = acpi_get_handle(NULL, node, &handle);
493
494 if (ACPI_SUCCESS(status)) {
495 status = acpi_remove_notify_handler(handle,
496 ACPI_SYSTEM_NOTIFY,
497 eeepc_rfkill_notify);
498 if (ACPI_FAILURE(status))
499 pr_err("Error removing rfkill notify handler %s\n",
500 node);
501 }
502}
503
504static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
505 u8 *value)
506{
Corentin Chary5c956382011-02-06 13:28:31 +0100507 int result = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
Corentin Charyafa7c882011-02-06 13:28:28 +0100508
Corentin Chary5c956382011-02-06 13:28:31 +0100509 if (result < 0)
510 return result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100511
Corentin Chary5c956382011-02-06 13:28:31 +0100512 *value = !!result;
Corentin Charyafa7c882011-02-06 13:28:28 +0100513 return 0;
514}
515
516static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
517{
518 kfree(hotplug_slot->info);
519 kfree(hotplug_slot);
520}
521
522static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
523 .owner = THIS_MODULE,
524 .get_adapter_status = eeepc_get_adapter_status,
525 .get_power_status = eeepc_get_adapter_status,
526};
527
Corentin Chary279f8f92011-02-06 13:28:29 +0100528static void eeepc_hotplug_work(struct work_struct *work)
529{
530 struct eeepc_wmi *eeepc;
531
532 eeepc = container_of(work, struct eeepc_wmi, hotplug_work);
533 eeepc_rfkill_hotplug(eeepc);
534}
535
Corentin Charyafa7c882011-02-06 13:28:28 +0100536static int eeepc_setup_pci_hotplug(struct eeepc_wmi *eeepc)
537{
538 int ret = -ENOMEM;
539 struct pci_bus *bus = pci_find_bus(0, 1);
540
541 if (!bus) {
542 pr_err("Unable to find wifi PCI bus\n");
543 return -ENODEV;
544 }
545
Corentin Chary279f8f92011-02-06 13:28:29 +0100546 eeepc->hotplug_workqueue =
547 create_singlethread_workqueue("hotplug_workqueue");
548 if (!eeepc->hotplug_workqueue)
549 goto error_workqueue;
550
551 INIT_WORK(&eeepc->hotplug_work, eeepc_hotplug_work);
552
Corentin Charyafa7c882011-02-06 13:28:28 +0100553 eeepc->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
554 if (!eeepc->hotplug_slot)
555 goto error_slot;
556
557 eeepc->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
558 GFP_KERNEL);
559 if (!eeepc->hotplug_slot->info)
560 goto error_info;
561
562 eeepc->hotplug_slot->private = eeepc;
563 eeepc->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
564 eeepc->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
565 eeepc_get_adapter_status(eeepc->hotplug_slot,
566 &eeepc->hotplug_slot->info->adapter_status);
567
568 ret = pci_hp_register(eeepc->hotplug_slot, bus, 0, "eeepc-wifi");
569 if (ret) {
570 pr_err("Unable to register hotplug slot - %d\n", ret);
571 goto error_register;
572 }
573
574 return 0;
575
576error_register:
577 kfree(eeepc->hotplug_slot->info);
578error_info:
579 kfree(eeepc->hotplug_slot);
580 eeepc->hotplug_slot = NULL;
581error_slot:
Corentin Chary279f8f92011-02-06 13:28:29 +0100582 destroy_workqueue(eeepc->hotplug_workqueue);
583error_workqueue:
Corentin Charyafa7c882011-02-06 13:28:28 +0100584 return ret;
585}
586
587/*
Corentin Charyba48fdb2010-11-29 08:14:07 +0100588 * Rfkill devices
589 */
590static int eeepc_rfkill_set(void *data, bool blocked)
591{
592 int dev_id = (unsigned long)data;
593 u32 ctrl_param = !blocked;
Corentin Chary7898cf12011-02-06 13:28:30 +0100594 acpi_status status;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100595
Corentin Chary7898cf12011-02-06 13:28:30 +0100596 status = eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
597
598 if (ACPI_FAILURE(status))
599 return -EIO;
600
601 return 0;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100602}
603
604static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
605{
606 int dev_id = (unsigned long)data;
Corentin Chary5c956382011-02-06 13:28:31 +0100607 int result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100608
Corentin Chary5c956382011-02-06 13:28:31 +0100609 result = eeepc_wmi_get_devstate_simple(dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100610
Corentin Chary5c956382011-02-06 13:28:31 +0100611 if (result < 0)
Corentin Charyba48fdb2010-11-29 08:14:07 +0100612 return ;
613
Corentin Chary5c956382011-02-06 13:28:31 +0100614 rfkill_set_sw_state(rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100615}
616
Corentin Chary279f8f92011-02-06 13:28:29 +0100617static int eeepc_rfkill_wlan_set(void *data, bool blocked)
618{
619 struct eeepc_wmi *eeepc = data;
620 int ret;
621
622 /*
623 * This handler is enabled only if hotplug is enabled.
624 * In this case, the eeepc_wmi_set_devstate() will
625 * trigger a wmi notification and we need to wait
626 * this call to finish before being able to call
627 * any wmi method
628 */
629 mutex_lock(&eeepc->wmi_lock);
630 ret = eeepc_rfkill_set((void *)(long)EEEPC_WMI_DEVID_WLAN, blocked);
631 mutex_unlock(&eeepc->wmi_lock);
632 return ret;
633}
634
635static void eeepc_rfkill_wlan_query(struct rfkill *rfkill, void *data)
636{
637 eeepc_rfkill_query(rfkill, (void *)(long)EEEPC_WMI_DEVID_WLAN);
638}
639
640static const struct rfkill_ops eeepc_rfkill_wlan_ops = {
641 .set_block = eeepc_rfkill_wlan_set,
642 .query = eeepc_rfkill_wlan_query,
643};
644
Corentin Charyba48fdb2010-11-29 08:14:07 +0100645static const struct rfkill_ops eeepc_rfkill_ops = {
646 .set_block = eeepc_rfkill_set,
647 .query = eeepc_rfkill_query,
648};
649
650static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
651 struct rfkill **rfkill,
652 const char *name,
653 enum rfkill_type type, int dev_id)
654{
Corentin Chary5c956382011-02-06 13:28:31 +0100655 int result = eeepc_wmi_get_devstate_simple(dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100656
Corentin Chary5c956382011-02-06 13:28:31 +0100657 if (result < 0)
658 return result;
Corentin Charyba48fdb2010-11-29 08:14:07 +0100659
Corentin Chary279f8f92011-02-06 13:28:29 +0100660 if (dev_id == EEEPC_WMI_DEVID_WLAN && eeepc->hotplug_wireless)
661 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
662 &eeepc_rfkill_wlan_ops, eeepc);
663 else
664 *rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
665 &eeepc_rfkill_ops, (void *)(long)dev_id);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100666
667 if (!*rfkill)
668 return -EINVAL;
669
Corentin Chary5c956382011-02-06 13:28:31 +0100670 rfkill_init_sw_state(*rfkill, !result);
Corentin Charyba48fdb2010-11-29 08:14:07 +0100671 result = rfkill_register(*rfkill);
672 if (result) {
673 rfkill_destroy(*rfkill);
674 *rfkill = NULL;
675 return result;
676 }
677 return 0;
678}
679
680static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
681{
Corentin Charyafa7c882011-02-06 13:28:28 +0100682 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
683 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
684 eeepc_unregister_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
Corentin Charyba48fdb2010-11-29 08:14:07 +0100685 if (eeepc->wlan_rfkill) {
686 rfkill_unregister(eeepc->wlan_rfkill);
687 rfkill_destroy(eeepc->wlan_rfkill);
688 eeepc->wlan_rfkill = NULL;
689 }
Corentin Charyafa7c882011-02-06 13:28:28 +0100690 /*
691 * Refresh pci hotplug in case the rfkill state was changed after
692 * eeepc_unregister_rfkill_notifier()
693 */
694 eeepc_rfkill_hotplug(eeepc);
695 if (eeepc->hotplug_slot)
696 pci_hp_deregister(eeepc->hotplug_slot);
Corentin Chary279f8f92011-02-06 13:28:29 +0100697 if (eeepc->hotplug_workqueue)
698 destroy_workqueue(eeepc->hotplug_workqueue);
Corentin Charyafa7c882011-02-06 13:28:28 +0100699
Corentin Charyba48fdb2010-11-29 08:14:07 +0100700 if (eeepc->bluetooth_rfkill) {
701 rfkill_unregister(eeepc->bluetooth_rfkill);
702 rfkill_destroy(eeepc->bluetooth_rfkill);
703 eeepc->bluetooth_rfkill = NULL;
704 }
Corentin Chary2e9e1592011-02-06 13:28:37 +0100705 if (eeepc->wimax_rfkill) {
706 rfkill_unregister(eeepc->wimax_rfkill);
707 rfkill_destroy(eeepc->wimax_rfkill);
708 eeepc->wimax_rfkill = NULL;
709 }
Corentin Charyba48fdb2010-11-29 08:14:07 +0100710 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
Corentin Chary2e9e1592011-02-06 13:28:37 +0100738 result = eeepc_new_rfkill(eeepc, &eeepc->wimax_rfkill,
739 "eeepc-wimax", RFKILL_TYPE_WIMAX,
740 EEEPC_WMI_DEVID_WIMAX);
741
742 if (result && result != -ENODEV)
743 goto exit;
744
Corentin Charyba48fdb2010-11-29 08:14:07 +0100745 result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
746 "eeepc-wwan3g", RFKILL_TYPE_WWAN,
747 EEEPC_WMI_DEVID_WWAN3G);
748
749 if (result && result != -ENODEV)
750 goto exit;
751
Corentin Charyc14d4b82011-02-06 13:28:40 +0100752 if (!eeepc->hotplug_wireless)
753 goto exit;
754
Corentin Charyafa7c882011-02-06 13:28:28 +0100755 result = eeepc_setup_pci_hotplug(eeepc);
756 /*
757 * If we get -EBUSY then something else is handling the PCI hotplug -
758 * don't fail in this case
759 */
760 if (result == -EBUSY)
761 result = 0;
762
763 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P5");
764 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P6");
765 eeepc_register_rfkill_notifier(eeepc, "\\_SB.PCI0.P0P7");
766 /*
767 * Refresh pci hotplug in case the rfkill state was changed during
768 * setup.
769 */
770 eeepc_rfkill_hotplug(eeepc);
771
Corentin Charyba48fdb2010-11-29 08:14:07 +0100772exit:
773 if (result && result != -ENODEV)
774 eeepc_wmi_rfkill_exit(eeepc);
775
776 if (result == -ENODEV)
777 result = 0;
778
779 return result;
780}
781
782/*
Corentin Chary084fca62010-11-29 08:14:06 +0100783 * Backlight
784 */
Corentin Charyb7187262011-02-06 13:28:39 +0100785static int read_backlight_power(void)
786{
787 int ret = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BACKLIGHT);
788
789 if (ret < 0)
790 return ret;
791
792 return ret ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
793}
794
Yong Wang3d7b1652010-04-11 09:27:54 +0800795static int read_brightness(struct backlight_device *bd)
796{
Corentin Charydfed65d2010-11-29 08:14:12 +0100797 u32 retval;
Yong Wang3d7b1652010-04-11 09:27:54 +0800798 acpi_status status;
799
Corentin Charyb7187262011-02-06 13:28:39 +0100800 status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BRIGHTNESS, &retval);
Yong Wang3d7b1652010-04-11 09:27:54 +0800801
802 if (ACPI_FAILURE(status))
Corentin Charyb7187262011-02-06 13:28:39 +0100803 return -EIO;
Yong Wang3d7b1652010-04-11 09:27:54 +0800804 else
Corentin Charyb7187262011-02-06 13:28:39 +0100805 return retval & EEEPC_WMI_DSTS_BRIGHTNESS_MASK;
Yong Wang3d7b1652010-04-11 09:27:54 +0800806}
807
808static int update_bl_status(struct backlight_device *bd)
809{
Corentin Charydfed65d2010-11-29 08:14:12 +0100810 u32 ctrl_param;
Yong Wang3d7b1652010-04-11 09:27:54 +0800811 acpi_status status;
Corentin Charyb7187262011-02-06 13:28:39 +0100812 int power;
Yong Wang3d7b1652010-04-11 09:27:54 +0800813
814 ctrl_param = bd->props.brightness;
815
Corentin Charyb7187262011-02-06 13:28:39 +0100816 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BRIGHTNESS,
Corentin Chary8c1b2d82010-11-29 08:14:09 +0100817 ctrl_param, NULL);
Yong Wang3d7b1652010-04-11 09:27:54 +0800818
819 if (ACPI_FAILURE(status))
Corentin Charyb7187262011-02-06 13:28:39 +0100820 return -EIO;
821
822 power = read_backlight_power();
823 if (power != -ENODEV && bd->props.power != power) {
824 ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
825 status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
826 ctrl_param, NULL);
827
828 if (ACPI_FAILURE(status))
829 return -EIO;
830 }
831 return 0;
Yong Wang3d7b1652010-04-11 09:27:54 +0800832}
833
834static const struct backlight_ops eeepc_wmi_bl_ops = {
835 .get_brightness = read_brightness,
836 .update_status = update_bl_status,
837};
838
839static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
840{
841 struct backlight_device *bd = eeepc->backlight_device;
842 int old = bd->props.brightness;
Daniel Mackb7670ed2010-05-19 12:37:01 +0200843 int new = old;
Yong Wang3d7b1652010-04-11 09:27:54 +0800844
845 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
846 new = code - NOTIFY_BRNUP_MIN + 1;
847 else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
848 new = code - NOTIFY_BRNDOWN_MIN;
849
850 bd->props.brightness = new;
851 backlight_update_status(bd);
852 backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
853
854 return old;
855}
856
857static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
858{
859 struct backlight_device *bd;
860 struct backlight_properties props;
Corentin Charyb7187262011-02-06 13:28:39 +0100861 int max;
862 int power;
863
864 max = eeepc_wmi_get_devstate_bits(EEEPC_WMI_DEVID_BRIGHTNESS,
865 EEEPC_WMI_DSTS_MAX_BRIGTH_MASK);
866 power = read_backlight_power();
867
868 if (max < 0 && power < 0) {
869 /* Try to keep the original error */
870 if (max == -ENODEV && power == -ENODEV)
871 return -ENODEV;
872 if (max != -ENODEV)
873 return max;
874 else
875 return power;
876 }
877 if (max == -ENODEV)
878 max = 0;
879 if (power == -ENODEV)
880 power = FB_BLANK_UNBLANK;
Yong Wang3d7b1652010-04-11 09:27:54 +0800881
882 memset(&props, 0, sizeof(struct backlight_properties));
Corentin Charyb7187262011-02-06 13:28:39 +0100883 props.max_brightness = max;
Yong Wang3d7b1652010-04-11 09:27:54 +0800884 bd = backlight_device_register(EEEPC_WMI_FILE,
Corentin Chary27c136c2010-11-29 08:14:05 +0100885 &eeepc->platform_device->dev, eeepc,
Yong Wang3d7b1652010-04-11 09:27:54 +0800886 &eeepc_wmi_bl_ops, &props);
887 if (IS_ERR(bd)) {
888 pr_err("Could not register backlight device\n");
889 return PTR_ERR(bd);
890 }
891
892 eeepc->backlight_device = bd;
893
894 bd->props.brightness = read_brightness(bd);
Corentin Charyb7187262011-02-06 13:28:39 +0100895 bd->props.power = power;
Yong Wang3d7b1652010-04-11 09:27:54 +0800896 backlight_update_status(bd);
897
898 return 0;
899}
900
901static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
902{
903 if (eeepc->backlight_device)
904 backlight_device_unregister(eeepc->backlight_device);
905
906 eeepc->backlight_device = NULL;
907}
908
909static void eeepc_wmi_notify(u32 value, void *context)
910{
911 struct eeepc_wmi *eeepc = context;
912 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
913 union acpi_object *obj;
914 acpi_status status;
915 int code;
916 int orig_code;
917
918 status = wmi_get_event_data(value, &response);
919 if (status != AE_OK) {
920 pr_err("bad event status 0x%x\n", status);
921 return;
922 }
923
924 obj = (union acpi_object *)response.pointer;
925
926 if (obj && obj->type == ACPI_TYPE_INTEGER) {
927 code = obj->integer.value;
928 orig_code = code;
929
930 if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
931 code = NOTIFY_BRNUP_MIN;
932 else if (code >= NOTIFY_BRNDOWN_MIN &&
933 code <= NOTIFY_BRNDOWN_MAX)
934 code = NOTIFY_BRNDOWN_MIN;
935
936 if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
937 if (!acpi_video_backlight_support())
938 eeepc_wmi_backlight_notify(eeepc, orig_code);
939 }
940
941 if (!sparse_keymap_report_event(eeepc->inputdev,
942 code, 1, true))
943 pr_info("Unknown key %x pressed\n", code);
944 }
945
946 kfree(obj);
947}
948
Corentin Chary9e1565b2011-02-06 13:28:36 +0100949/*
950 * Sys helpers
951 */
952static int parse_arg(const char *buf, unsigned long count, int *val)
953{
954 if (!count)
955 return 0;
956 if (sscanf(buf, "%i", val) != 1)
957 return -EINVAL;
958 return count;
959}
960
961static ssize_t store_sys_wmi(int devid, const char *buf, size_t count)
962{
963 acpi_status status;
964 u32 retval;
965 int rv, value;
966
967 value = eeepc_wmi_get_devstate_simple(devid);
968 if (value == -ENODEV) /* Check device presence */
969 return value;
970
971 rv = parse_arg(buf, count, &value);
972 status = eeepc_wmi_set_devstate(devid, value, &retval);
973
974 if (ACPI_FAILURE(status))
975 return -EIO;
976 return rv;
977}
978
979static ssize_t show_sys_wmi(int devid, char *buf)
980{
981 int value = eeepc_wmi_get_devstate_simple(devid);
982
983 if (value < 0)
984 return value;
985
986 return sprintf(buf, "%d\n", value);
987}
988
989#define EEEPC_WMI_CREATE_DEVICE_ATTR(_name, _mode, _cm) \
990 static ssize_t show_##_name(struct device *dev, \
991 struct device_attribute *attr, \
992 char *buf) \
993 { \
994 return show_sys_wmi(_cm, buf); \
995 } \
996 static ssize_t store_##_name(struct device *dev, \
997 struct device_attribute *attr, \
998 const char *buf, size_t count) \
999 { \
1000 return store_sys_wmi(_cm, buf, count); \
1001 } \
1002 static struct device_attribute dev_attr_##_name = { \
1003 .attr = { \
1004 .name = __stringify(_name), \
1005 .mode = _mode }, \
1006 .show = show_##_name, \
1007 .store = store_##_name, \
1008 }
1009
Corentin Chary4615bb62011-02-06 13:28:42 +01001010EEEPC_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, EEEPC_WMI_DEVID_TOUCHPAD);
Corentin Chary9e1565b2011-02-06 13:28:36 +01001011EEEPC_WMI_CREATE_DEVICE_ATTR(camera, 0644, EEEPC_WMI_DEVID_CAMERA);
1012EEEPC_WMI_CREATE_DEVICE_ATTR(cardr, 0644, EEEPC_WMI_DEVID_CARDREADER);
1013
Dmitry Torokhov67fa38e2010-11-03 11:14:01 -07001014static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
1015 const char *buf, size_t count)
Chris Bagwell7f80d732010-10-11 18:47:18 -05001016{
1017 int value;
1018 struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
1019 acpi_status status;
1020
1021 if (!count || sscanf(buf, "%i", &value) != 1)
1022 return -EINVAL;
1023 if (value < 0 || value > 2)
1024 return -EINVAL;
1025
1026 status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
1027 1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
1028
1029 if (ACPI_FAILURE(status))
1030 return -EIO;
1031 else
1032 return count;
1033}
1034
1035static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
1036
Corentin Chary4e37b422010-11-29 08:14:08 +01001037static struct attribute *platform_attributes[] = {
1038 &dev_attr_cpufv.attr,
Corentin Chary9e1565b2011-02-06 13:28:36 +01001039 &dev_attr_camera.attr,
1040 &dev_attr_cardr.attr,
Corentin Chary4615bb62011-02-06 13:28:42 +01001041 &dev_attr_touchpad.attr,
Corentin Chary4e37b422010-11-29 08:14:08 +01001042 NULL
1043};
1044
Corentin Chary9e1565b2011-02-06 13:28:36 +01001045static mode_t eeepc_sysfs_is_visible(struct kobject *kobj,
1046 struct attribute *attr,
1047 int idx)
1048{
1049 bool supported = true;
1050 int devid = -1;
1051
1052 if (attr == &dev_attr_camera.attr)
1053 devid = EEEPC_WMI_DEVID_CAMERA;
1054 else if (attr == &dev_attr_cardr.attr)
1055 devid = EEEPC_WMI_DEVID_CARDREADER;
Corentin Chary4615bb62011-02-06 13:28:42 +01001056 else if (attr == &dev_attr_touchpad.attr)
1057 devid = EEEPC_WMI_DEVID_TOUCHPAD;
Corentin Chary9e1565b2011-02-06 13:28:36 +01001058
1059 if (devid != -1)
1060 supported = eeepc_wmi_get_devstate_simple(devid) != -ENODEV;
1061
1062 return supported ? attr->mode : 0;
1063}
1064
Corentin Chary4e37b422010-11-29 08:14:08 +01001065static struct attribute_group platform_attribute_group = {
Corentin Chary9e1565b2011-02-06 13:28:36 +01001066 .is_visible = eeepc_sysfs_is_visible,
1067 .attrs = platform_attributes
Corentin Chary4e37b422010-11-29 08:14:08 +01001068};
1069
Chris Bagwell7f80d732010-10-11 18:47:18 -05001070static void eeepc_wmi_sysfs_exit(struct platform_device *device)
1071{
Corentin Chary4e37b422010-11-29 08:14:08 +01001072 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001073}
1074
1075static int eeepc_wmi_sysfs_init(struct platform_device *device)
1076{
Corentin Chary4e37b422010-11-29 08:14:08 +01001077 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
Chris Bagwell7f80d732010-10-11 18:47:18 -05001078}
1079
Corentin Chary27c136c2010-11-29 08:14:05 +01001080/*
1081 * Platform device
1082 */
1083static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
1084{
Corentin Charya04ce292011-02-06 13:28:33 +01001085 return eeepc_wmi_sysfs_init(eeepc->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001086}
1087
1088static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
1089{
1090 eeepc_wmi_sysfs_exit(eeepc->platform_device);
Corentin Chary27c136c2010-11-29 08:14:05 +01001091}
1092
1093/*
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001094 * debugfs
1095 */
1096struct eeepc_wmi_debugfs_node {
1097 struct eeepc_wmi *eeepc;
1098 char *name;
1099 int (*show)(struct seq_file *m, void *data);
1100};
1101
1102static int show_dsts(struct seq_file *m, void *data)
1103{
1104 struct eeepc_wmi *eeepc = m->private;
1105 acpi_status status;
1106 u32 retval = -1;
1107
1108 status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
1109
1110 if (ACPI_FAILURE(status))
1111 return -EIO;
1112
1113 seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
1114
1115 return 0;
1116}
1117
1118static int show_devs(struct seq_file *m, void *data)
1119{
1120 struct eeepc_wmi *eeepc = m->private;
1121 acpi_status status;
1122 u32 retval = -1;
1123
1124 status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
1125 eeepc->debug.ctrl_param, &retval);
1126 if (ACPI_FAILURE(status))
1127 return -EIO;
1128
1129 seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
1130 eeepc->debug.ctrl_param, retval);
1131
1132 return 0;
1133}
1134
1135static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
1136 { NULL, "devs", show_devs },
1137 { NULL, "dsts", show_dsts },
1138};
1139
1140static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
1141{
1142 struct eeepc_wmi_debugfs_node *node = inode->i_private;
1143
1144 return single_open(file, node->show, node->eeepc);
1145}
1146
1147static const struct file_operations eeepc_wmi_debugfs_io_ops = {
1148 .owner = THIS_MODULE,
1149 .open = eeepc_wmi_debugfs_open,
1150 .read = seq_read,
1151 .llseek = seq_lseek,
1152 .release = single_release,
1153};
1154
1155static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
1156{
1157 debugfs_remove_recursive(eeepc->debug.root);
1158}
1159
1160static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
1161{
1162 struct dentry *dent;
1163 int i;
1164
1165 eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
1166 if (!eeepc->debug.root) {
1167 pr_err("failed to create debugfs directory");
1168 goto error_debugfs;
1169 }
1170
1171 dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
1172 eeepc->debug.root, &eeepc->debug.dev_id);
1173 if (!dent)
1174 goto error_debugfs;
1175
1176 dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
1177 eeepc->debug.root, &eeepc->debug.ctrl_param);
1178 if (!dent)
1179 goto error_debugfs;
1180
1181 for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
1182 struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
1183
1184 node->eeepc = eeepc;
1185 dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
1186 eeepc->debug.root, node,
1187 &eeepc_wmi_debugfs_io_ops);
1188 if (!dent) {
1189 pr_err("failed to create debug file: %s\n", node->name);
1190 goto error_debugfs;
1191 }
1192 }
1193
1194 return 0;
1195
1196error_debugfs:
1197 eeepc_wmi_debugfs_exit(eeepc);
1198 return -ENOMEM;
1199}
1200
1201/*
Corentin Chary27c136c2010-11-29 08:14:05 +01001202 * WMI Driver
1203 */
Corentin Charyafa7c882011-02-06 13:28:28 +01001204static void eeepc_dmi_check(struct eeepc_wmi *eeepc)
1205{
1206 const char *model;
1207
1208 model = dmi_get_system_info(DMI_PRODUCT_NAME);
1209 if (!model)
1210 return;
1211
1212 /*
1213 * Whitelist for wlan hotplug
1214 *
1215 * Eeepc 1000H needs the current hotplug code to handle
1216 * Fn+F2 correctly. We may add other Eeepc here later, but
1217 * it seems that most of the laptops supported by eeepc-wmi
1218 * don't need to be on this list
1219 */
1220 if (strcmp(model, "1000H") == 0) {
1221 eeepc->hotplug_wireless = true;
1222 pr_info("wlan hotplug enabled\n");
1223 }
1224}
1225
Corentin Charya04ce292011-02-06 13:28:33 +01001226static int __init eeepc_wmi_add(struct platform_device *pdev)
Yong Wangee027e42010-03-21 10:26:34 +08001227{
Yong Wang45f2c692010-04-11 09:27:19 +08001228 struct eeepc_wmi *eeepc;
Yong Wangee027e42010-03-21 10:26:34 +08001229 acpi_status status;
Corentin Chary27c136c2010-11-29 08:14:05 +01001230 int err;
Yong Wangee027e42010-03-21 10:26:34 +08001231
Corentin Chary27c136c2010-11-29 08:14:05 +01001232 eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
1233 if (!eeepc)
Corentin Charya04ce292011-02-06 13:28:33 +01001234 return -ENOMEM;
1235
1236 eeepc->platform_device = pdev;
1237 platform_set_drvdata(eeepc->platform_device, eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001238
Corentin Charyafa7c882011-02-06 13:28:28 +01001239 eeepc->hotplug_wireless = hotplug_wireless;
1240 eeepc_dmi_check(eeepc);
1241
Corentin Chary27c136c2010-11-29 08:14:05 +01001242 err = eeepc_wmi_platform_init(eeepc);
1243 if (err)
1244 goto fail_platform;
Yong Wang45f2c692010-04-11 09:27:19 +08001245
1246 err = eeepc_wmi_input_init(eeepc);
1247 if (err)
Corentin Chary27c136c2010-11-29 08:14:05 +01001248 goto fail_input;
Yong Wang3d7b1652010-04-11 09:27:54 +08001249
Corentin Chary084fca62010-11-29 08:14:06 +01001250 err = eeepc_wmi_led_init(eeepc);
1251 if (err)
1252 goto fail_leds;
1253
Corentin Charyba48fdb2010-11-29 08:14:07 +01001254 err = eeepc_wmi_rfkill_init(eeepc);
1255 if (err)
1256 goto fail_rfkill;
1257
Yong Wang3d7b1652010-04-11 09:27:54 +08001258 if (!acpi_video_backlight_support()) {
1259 err = eeepc_wmi_backlight_init(eeepc);
Corentin Charyb7187262011-02-06 13:28:39 +01001260 if (err && err != -ENODEV)
Corentin Chary27c136c2010-11-29 08:14:05 +01001261 goto fail_backlight;
Yong Wang3d7b1652010-04-11 09:27:54 +08001262 } else
1263 pr_info("Backlight controlled by ACPI video driver\n");
Yong Wang45f2c692010-04-11 09:27:19 +08001264
1265 status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
Corentin Chary27c136c2010-11-29 08:14:05 +01001266 eeepc_wmi_notify, eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001267 if (ACPI_FAILURE(status)) {
1268 pr_err("Unable to register notify handler - %d\n",
1269 status);
1270 err = -ENODEV;
Corentin Chary27c136c2010-11-29 08:14:05 +01001271 goto fail_wmi_handler;
Yong Wang45f2c692010-04-11 09:27:19 +08001272 }
1273
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001274 err = eeepc_wmi_debugfs_init(eeepc);
1275 if (err)
1276 goto fail_debugfs;
1277
Corentin Charya04ce292011-02-06 13:28:33 +01001278 return 0;
Yong Wang45f2c692010-04-11 09:27:19 +08001279
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001280fail_debugfs:
1281 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Corentin Chary27c136c2010-11-29 08:14:05 +01001282fail_wmi_handler:
Yong Wang3d7b1652010-04-11 09:27:54 +08001283 eeepc_wmi_backlight_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001284fail_backlight:
Corentin Charyba48fdb2010-11-29 08:14:07 +01001285 eeepc_wmi_rfkill_exit(eeepc);
1286fail_rfkill:
Corentin Chary084fca62010-11-29 08:14:06 +01001287 eeepc_wmi_led_exit(eeepc);
1288fail_leds:
Yong Wang45f2c692010-04-11 09:27:19 +08001289 eeepc_wmi_input_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001290fail_input:
1291 eeepc_wmi_platform_exit(eeepc);
1292fail_platform:
1293 kfree(eeepc);
Corentin Charya04ce292011-02-06 13:28:33 +01001294 return err;
Yong Wang45f2c692010-04-11 09:27:19 +08001295}
1296
Corentin Charya04ce292011-02-06 13:28:33 +01001297static int __exit eeepc_wmi_remove(struct platform_device *device)
Yong Wang45f2c692010-04-11 09:27:19 +08001298{
1299 struct eeepc_wmi *eeepc;
1300
1301 eeepc = platform_get_drvdata(device);
1302 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
Yong Wang3d7b1652010-04-11 09:27:54 +08001303 eeepc_wmi_backlight_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001304 eeepc_wmi_input_exit(eeepc);
Corentin Chary084fca62010-11-29 08:14:06 +01001305 eeepc_wmi_led_exit(eeepc);
Corentin Charyba48fdb2010-11-29 08:14:07 +01001306 eeepc_wmi_rfkill_exit(eeepc);
Corentin Chary8c1b2d82010-11-29 08:14:09 +01001307 eeepc_wmi_debugfs_exit(eeepc);
Corentin Chary27c136c2010-11-29 08:14:05 +01001308 eeepc_wmi_platform_exit(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001309
Corentin Chary27c136c2010-11-29 08:14:05 +01001310 kfree(eeepc);
Yong Wang45f2c692010-04-11 09:27:19 +08001311 return 0;
1312}
1313
Corentin Chary0773d7f2011-02-06 13:28:32 +01001314/*
1315 * Platform driver - hibernate/resume callbacks
1316 */
1317static int eeepc_hotk_thaw(struct device *device)
1318{
1319 struct eeepc_wmi *eeepc = dev_get_drvdata(device);
1320
1321 if (eeepc->wlan_rfkill) {
1322 bool wlan;
1323
1324 /*
1325 * Work around bios bug - acpi _PTS turns off the wireless led
1326 * during suspend. Normally it restores it on resume, but
1327 * we should kick it ourselves in case hibernation is aborted.
1328 */
1329 wlan = eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WLAN);
1330 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_WLAN, wlan, NULL);
1331 }
1332
1333 return 0;
1334}
1335
1336static int eeepc_hotk_restore(struct device *device)
1337{
1338 struct eeepc_wmi *eeepc = dev_get_drvdata(device);
1339 int bl;
1340
1341 /* Refresh both wlan rfkill state and pci hotplug */
1342 if (eeepc->wlan_rfkill)
1343 eeepc_rfkill_hotplug(eeepc);
1344
1345 if (eeepc->bluetooth_rfkill) {
1346 bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_BLUETOOTH);
1347 rfkill_set_sw_state(eeepc->bluetooth_rfkill, bl);
Corentin Chary2e9e1592011-02-06 13:28:37 +01001348 }
1349 if (eeepc->wimax_rfkill) {
1350 bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WIMAX);
1351 rfkill_set_sw_state(eeepc->wimax_rfkill, bl);
1352 }
Corentin Chary0773d7f2011-02-06 13:28:32 +01001353 if (eeepc->wwan3g_rfkill) {
1354 bl = !eeepc_wmi_get_devstate_simple(EEEPC_WMI_DEVID_WWAN3G);
1355 rfkill_set_sw_state(eeepc->wwan3g_rfkill, bl);
1356 }
1357
1358 return 0;
1359}
1360
1361static const struct dev_pm_ops eeepc_pm_ops = {
1362 .thaw = eeepc_hotk_thaw,
1363 .restore = eeepc_hotk_restore,
1364};
1365
Yong Wang45f2c692010-04-11 09:27:19 +08001366static struct platform_driver platform_driver = {
Corentin Charya04ce292011-02-06 13:28:33 +01001367 .remove = __exit_p(eeepc_wmi_remove),
Yong Wang45f2c692010-04-11 09:27:19 +08001368 .driver = {
1369 .name = EEEPC_WMI_FILE,
1370 .owner = THIS_MODULE,
Corentin Chary0773d7f2011-02-06 13:28:32 +01001371 .pm = &eeepc_pm_ops,
Yong Wang45f2c692010-04-11 09:27:19 +08001372 },
Yong Wang45f2c692010-04-11 09:27:19 +08001373};
1374
Corentin Charyd358cb52010-11-29 08:14:14 +01001375static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
1376 void *context, void **retval)
1377{
1378 pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
1379 *(bool *)context = true;
1380 return AE_CTRL_TERMINATE;
1381}
1382
1383static int __init eeepc_wmi_check_atkd(void)
1384{
1385 acpi_status status;
1386 bool found = false;
1387
1388 status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device,
1389 &found, NULL);
1390
1391 if (ACPI_FAILURE(status) || !found)
1392 return 0;
1393 return -1;
1394}
1395
Corentin Charya04ce292011-02-06 13:28:33 +01001396static int __init eeepc_wmi_probe(struct platform_device *pdev)
Yong Wang45f2c692010-04-11 09:27:19 +08001397{
Yong Wang3d7b1652010-04-11 09:27:54 +08001398 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
1399 !wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
Yong Wang81248882010-04-11 09:26:33 +08001400 pr_warning("No known WMI GUID found\n");
Yong Wangee027e42010-03-21 10:26:34 +08001401 return -ENODEV;
1402 }
1403
Corentin Charyd358cb52010-11-29 08:14:14 +01001404 if (eeepc_wmi_check_atkd()) {
1405 pr_warning("WMI device present, but legacy ATKD device is also "
1406 "present and enabled.");
1407 pr_warning("You probably booted with acpi_osi=\"Linux\" or "
1408 "acpi_osi=\"!Windows 2009\"");
1409 pr_warning("Can't load eeepc-wmi, use default acpi_osi "
1410 "(preferred) or eeepc-laptop");
1411 return -ENODEV;
1412 }
1413
Corentin Charya04ce292011-02-06 13:28:33 +01001414 return eeepc_wmi_add(pdev);
1415}
Yong Wangee027e42010-03-21 10:26:34 +08001416
Corentin Charya04ce292011-02-06 13:28:33 +01001417static struct platform_device *platform_device;
Yong Wangee027e42010-03-21 10:26:34 +08001418
Corentin Charya04ce292011-02-06 13:28:33 +01001419static int __init eeepc_wmi_init(void)
1420{
1421 platform_device = platform_create_bundle(&platform_driver,
1422 eeepc_wmi_probe,
1423 NULL, 0, NULL, 0);
1424 if (IS_ERR(platform_device))
1425 return PTR_ERR(platform_device);
Yong Wangee027e42010-03-21 10:26:34 +08001426 return 0;
1427}
1428
1429static void __exit eeepc_wmi_exit(void)
1430{
Corentin Charya04ce292011-02-06 13:28:33 +01001431 platform_device_unregister(platform_device);
Yong Wang45f2c692010-04-11 09:27:19 +08001432 platform_driver_unregister(&platform_driver);
Yong Wangee027e42010-03-21 10:26:34 +08001433}
1434
1435module_init(eeepc_wmi_init);
1436module_exit(eeepc_wmi_exit);