2 * Eee PC WMI hotkey driver
4 * Copyright(C) 2010 Intel Corporation.
6 * Portions based on wistron_btns.c:
7 * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
8 * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
9 * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
28 #include <linux/kernel.h>
29 #include <linux/module.h>
30 #include <linux/init.h>
31 #include <linux/types.h>
32 #include <linux/slab.h>
33 #include <linux/input.h>
34 #include <linux/input/sparse-keymap.h>
36 #include <linux/backlight.h>
37 #include <linux/leds.h>
38 #include <linux/platform_device.h>
39 #include <acpi/acpi_bus.h>
40 #include <acpi/acpi_drivers.h>
42 #define EEEPC_WMI_FILE "eeepc-wmi"
44 MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
45 MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
46 MODULE_LICENSE("GPL");
48 #define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
49 #define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
51 MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID
);
52 MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID
);
54 #define NOTIFY_BRNUP_MIN 0x11
55 #define NOTIFY_BRNUP_MAX 0x1f
56 #define NOTIFY_BRNDOWN_MIN 0x20
57 #define NOTIFY_BRNDOWN_MAX 0x2e
59 #define EEEPC_WMI_METHODID_DEVS 0x53564544
60 #define EEEPC_WMI_METHODID_DSTS 0x53544344
61 #define EEEPC_WMI_METHODID_CFVS 0x53564643
63 #define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
64 #define EEEPC_WMI_DEVID_TPDLED 0x00100011
66 static const struct key_entry eeepc_wmi_keymap
[] = {
67 /* Sleep already handled via generic ACPI code */
68 { KE_KEY
, 0x5d, { KEY_WLAN
} },
69 { KE_KEY
, 0x32, { KEY_MUTE
} },
70 { KE_KEY
, 0x31, { KEY_VOLUMEDOWN
} },
71 { KE_KEY
, 0x30, { KEY_VOLUMEUP
} },
72 { KE_IGNORE
, NOTIFY_BRNDOWN_MIN
, { KEY_BRIGHTNESSDOWN
} },
73 { KE_IGNORE
, NOTIFY_BRNUP_MIN
, { KEY_BRIGHTNESSUP
} },
74 { KE_KEY
, 0xcc, { KEY_SWITCHVIDEOMODE
} },
75 { KE_KEY
, 0x6b, { KEY_F13
} }, /* Disable Touchpad */
76 { KE_KEY
, 0xe1, { KEY_F14
} },
77 { KE_KEY
, 0xe9, { KEY_DISPLAY_OFF
} },
78 { KE_KEY
, 0xe0, { KEY_PROG1
} },
79 { KE_KEY
, 0x5c, { KEY_F15
} },
89 struct input_dev
*inputdev
;
90 struct backlight_device
*backlight_device
;
91 struct platform_device
*platform_device
;
93 struct led_classdev tpd_led
;
95 struct workqueue_struct
*led_workqueue
;
96 struct work_struct tpd_led_work
;
99 /* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
100 static struct platform_device
*platform_device
;
102 static int eeepc_wmi_input_init(struct eeepc_wmi
*eeepc
)
106 eeepc
->inputdev
= input_allocate_device();
107 if (!eeepc
->inputdev
)
110 eeepc
->inputdev
->name
= "Eee PC WMI hotkeys";
111 eeepc
->inputdev
->phys
= EEEPC_WMI_FILE
"/input0";
112 eeepc
->inputdev
->id
.bustype
= BUS_HOST
;
113 eeepc
->inputdev
->dev
.parent
= &eeepc
->platform_device
->dev
;
115 err
= sparse_keymap_setup(eeepc
->inputdev
, eeepc_wmi_keymap
, NULL
);
119 err
= input_register_device(eeepc
->inputdev
);
121 goto err_free_keymap
;
126 sparse_keymap_free(eeepc
->inputdev
);
128 input_free_device(eeepc
->inputdev
);
132 static void eeepc_wmi_input_exit(struct eeepc_wmi
*eeepc
)
134 if (eeepc
->inputdev
) {
135 sparse_keymap_free(eeepc
->inputdev
);
136 input_unregister_device(eeepc
->inputdev
);
139 eeepc
->inputdev
= NULL
;
142 static acpi_status
eeepc_wmi_get_devstate(u32 dev_id
, u32
*ctrl_param
)
144 struct acpi_buffer input
= { (acpi_size
)sizeof(u32
), &dev_id
};
145 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
146 union acpi_object
*obj
;
150 status
= wmi_evaluate_method(EEEPC_WMI_MGMT_GUID
,
151 1, EEEPC_WMI_METHODID_DSTS
, &input
, &output
);
153 if (ACPI_FAILURE(status
))
156 obj
= (union acpi_object
*)output
.pointer
;
157 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
)
158 tmp
= (u32
)obj
->integer
.value
;
171 static acpi_status
eeepc_wmi_set_devstate(u32 dev_id
, u32 ctrl_param
)
173 struct bios_args args
= {
175 .ctrl_param
= ctrl_param
,
177 struct acpi_buffer input
= { (acpi_size
)sizeof(args
), &args
};
180 status
= wmi_evaluate_method(EEEPC_WMI_MGMT_GUID
,
181 1, EEEPC_WMI_METHODID_DEVS
, &input
, NULL
);
190 * These functions actually update the LED's, and are called from a
191 * workqueue. By doing this as separate work rather than when the LED
192 * subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
193 * potentially bad time, such as a timer interrupt.
195 static void tpd_led_update(struct work_struct
*work
)
198 struct eeepc_wmi
*eeepc
;
200 eeepc
= container_of(work
, struct eeepc_wmi
, tpd_led_work
);
202 ctrl_param
= eeepc
->tpd_led_wk
;
203 eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED
, ctrl_param
);
206 static void tpd_led_set(struct led_classdev
*led_cdev
,
207 enum led_brightness value
)
209 struct eeepc_wmi
*eeepc
;
211 eeepc
= container_of(led_cdev
, struct eeepc_wmi
, tpd_led
);
213 eeepc
->tpd_led_wk
= !!value
;
214 queue_work(eeepc
->led_workqueue
, &eeepc
->tpd_led_work
);
217 static int read_tpd_state(struct eeepc_wmi
*eeepc
)
219 static u32 ctrl_param
;
222 status
= eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED
, &ctrl_param
);
224 if (ACPI_FAILURE(status
))
226 else if (!ctrl_param
|| ctrl_param
== 0x00060000)
228 * if touchpad led is present, DSTS will set some bits,
229 * usually 0x00020000.
230 * 0x00060000 means that the device is not supported
234 /* Status is stored in the first bit */
235 return ctrl_param
& 0x1;
238 static enum led_brightness
tpd_led_get(struct led_classdev
*led_cdev
)
240 struct eeepc_wmi
*eeepc
;
242 eeepc
= container_of(led_cdev
, struct eeepc_wmi
, tpd_led
);
244 return read_tpd_state(eeepc
);
247 static int eeepc_wmi_led_init(struct eeepc_wmi
*eeepc
)
251 if (read_tpd_state(eeepc
) < 0)
254 eeepc
->led_workqueue
= create_singlethread_workqueue("led_workqueue");
255 if (!eeepc
->led_workqueue
)
257 INIT_WORK(&eeepc
->tpd_led_work
, tpd_led_update
);
259 eeepc
->tpd_led
.name
= "eeepc::touchpad";
260 eeepc
->tpd_led
.brightness_set
= tpd_led_set
;
261 eeepc
->tpd_led
.brightness_get
= tpd_led_get
;
262 eeepc
->tpd_led
.max_brightness
= 1;
264 rv
= led_classdev_register(&eeepc
->platform_device
->dev
,
267 destroy_workqueue(eeepc
->led_workqueue
);
274 static void eeepc_wmi_led_exit(struct eeepc_wmi
*eeepc
)
276 if (eeepc
->tpd_led
.dev
)
277 led_classdev_unregister(&eeepc
->tpd_led
);
278 if (eeepc
->led_workqueue
)
279 destroy_workqueue(eeepc
->led_workqueue
);
285 static int read_brightness(struct backlight_device
*bd
)
287 static u32 ctrl_param
;
290 status
= eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT
, &ctrl_param
);
292 if (ACPI_FAILURE(status
))
295 return ctrl_param
& 0xFF;
298 static int update_bl_status(struct backlight_device
*bd
)
301 static u32 ctrl_param
;
304 ctrl_param
= bd
->props
.brightness
;
306 status
= eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT
, ctrl_param
);
308 if (ACPI_FAILURE(status
))
314 static const struct backlight_ops eeepc_wmi_bl_ops
= {
315 .get_brightness
= read_brightness
,
316 .update_status
= update_bl_status
,
319 static int eeepc_wmi_backlight_notify(struct eeepc_wmi
*eeepc
, int code
)
321 struct backlight_device
*bd
= eeepc
->backlight_device
;
322 int old
= bd
->props
.brightness
;
325 if (code
>= NOTIFY_BRNUP_MIN
&& code
<= NOTIFY_BRNUP_MAX
)
326 new = code
- NOTIFY_BRNUP_MIN
+ 1;
327 else if (code
>= NOTIFY_BRNDOWN_MIN
&& code
<= NOTIFY_BRNDOWN_MAX
)
328 new = code
- NOTIFY_BRNDOWN_MIN
;
330 bd
->props
.brightness
= new;
331 backlight_update_status(bd
);
332 backlight_force_update(bd
, BACKLIGHT_UPDATE_HOTKEY
);
337 static int eeepc_wmi_backlight_init(struct eeepc_wmi
*eeepc
)
339 struct backlight_device
*bd
;
340 struct backlight_properties props
;
342 memset(&props
, 0, sizeof(struct backlight_properties
));
343 props
.max_brightness
= 15;
344 bd
= backlight_device_register(EEEPC_WMI_FILE
,
345 &eeepc
->platform_device
->dev
, eeepc
,
346 &eeepc_wmi_bl_ops
, &props
);
348 pr_err("Could not register backlight device\n");
352 eeepc
->backlight_device
= bd
;
354 bd
->props
.brightness
= read_brightness(bd
);
355 bd
->props
.power
= FB_BLANK_UNBLANK
;
356 backlight_update_status(bd
);
361 static void eeepc_wmi_backlight_exit(struct eeepc_wmi
*eeepc
)
363 if (eeepc
->backlight_device
)
364 backlight_device_unregister(eeepc
->backlight_device
);
366 eeepc
->backlight_device
= NULL
;
369 static void eeepc_wmi_notify(u32 value
, void *context
)
371 struct eeepc_wmi
*eeepc
= context
;
372 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
373 union acpi_object
*obj
;
378 status
= wmi_get_event_data(value
, &response
);
379 if (status
!= AE_OK
) {
380 pr_err("bad event status 0x%x\n", status
);
384 obj
= (union acpi_object
*)response
.pointer
;
386 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
) {
387 code
= obj
->integer
.value
;
390 if (code
>= NOTIFY_BRNUP_MIN
&& code
<= NOTIFY_BRNUP_MAX
)
391 code
= NOTIFY_BRNUP_MIN
;
392 else if (code
>= NOTIFY_BRNDOWN_MIN
&&
393 code
<= NOTIFY_BRNDOWN_MAX
)
394 code
= NOTIFY_BRNDOWN_MIN
;
396 if (code
== NOTIFY_BRNUP_MIN
|| code
== NOTIFY_BRNDOWN_MIN
) {
397 if (!acpi_video_backlight_support())
398 eeepc_wmi_backlight_notify(eeepc
, orig_code
);
401 if (!sparse_keymap_report_event(eeepc
->inputdev
,
403 pr_info("Unknown key %x pressed\n", code
);
409 static ssize_t
store_cpufv(struct device
*dev
, struct device_attribute
*attr
,
410 const char *buf
, size_t count
)
413 struct acpi_buffer input
= { (acpi_size
)sizeof(value
), &value
};
416 if (!count
|| sscanf(buf
, "%i", &value
) != 1)
418 if (value
< 0 || value
> 2)
421 status
= wmi_evaluate_method(EEEPC_WMI_MGMT_GUID
,
422 1, EEEPC_WMI_METHODID_CFVS
, &input
, NULL
);
424 if (ACPI_FAILURE(status
))
430 static DEVICE_ATTR(cpufv
, S_IRUGO
| S_IWUSR
, NULL
, store_cpufv
);
432 static void eeepc_wmi_sysfs_exit(struct platform_device
*device
)
434 device_remove_file(&device
->dev
, &dev_attr_cpufv
);
437 static int eeepc_wmi_sysfs_init(struct platform_device
*device
)
439 int retval
= -ENOMEM
;
441 retval
= device_create_file(&device
->dev
, &dev_attr_cpufv
);
448 eeepc_wmi_sysfs_exit(device
);
455 static int __init
eeepc_wmi_platform_init(struct eeepc_wmi
*eeepc
)
459 eeepc
->platform_device
= platform_device_alloc(EEEPC_WMI_FILE
, -1);
460 if (!eeepc
->platform_device
)
462 platform_set_drvdata(eeepc
->platform_device
, eeepc
);
464 err
= platform_device_add(eeepc
->platform_device
);
466 goto fail_platform_device
;
468 err
= eeepc_wmi_sysfs_init(eeepc
->platform_device
);
474 platform_device_del(eeepc
->platform_device
);
475 fail_platform_device
:
476 platform_device_put(eeepc
->platform_device
);
480 static void eeepc_wmi_platform_exit(struct eeepc_wmi
*eeepc
)
482 eeepc_wmi_sysfs_exit(eeepc
->platform_device
);
483 platform_device_unregister(eeepc
->platform_device
);
489 static struct platform_device
* __init
eeepc_wmi_add(void)
491 struct eeepc_wmi
*eeepc
;
495 eeepc
= kzalloc(sizeof(struct eeepc_wmi
), GFP_KERNEL
);
497 return ERR_PTR(-ENOMEM
);
500 * Register the platform device first. It is used as a parent for the
503 err
= eeepc_wmi_platform_init(eeepc
);
507 err
= eeepc_wmi_input_init(eeepc
);
511 err
= eeepc_wmi_led_init(eeepc
);
515 if (!acpi_video_backlight_support()) {
516 err
= eeepc_wmi_backlight_init(eeepc
);
520 pr_info("Backlight controlled by ACPI video driver\n");
522 status
= wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID
,
523 eeepc_wmi_notify
, eeepc
);
524 if (ACPI_FAILURE(status
)) {
525 pr_err("Unable to register notify handler - %d\n",
528 goto fail_wmi_handler
;
531 return eeepc
->platform_device
;
534 eeepc_wmi_backlight_exit(eeepc
);
536 eeepc_wmi_led_exit(eeepc
);
538 eeepc_wmi_input_exit(eeepc
);
540 eeepc_wmi_platform_exit(eeepc
);
546 static int eeepc_wmi_remove(struct platform_device
*device
)
548 struct eeepc_wmi
*eeepc
;
550 eeepc
= platform_get_drvdata(device
);
551 wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID
);
552 eeepc_wmi_backlight_exit(eeepc
);
553 eeepc_wmi_input_exit(eeepc
);
554 eeepc_wmi_led_exit(eeepc
);
555 eeepc_wmi_platform_exit(eeepc
);
561 static struct platform_driver platform_driver
= {
563 .name
= EEEPC_WMI_FILE
,
564 .owner
= THIS_MODULE
,
568 static int __init
eeepc_wmi_init(void)
572 if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID
) ||
573 !wmi_has_guid(EEEPC_WMI_MGMT_GUID
)) {
574 pr_warning("No known WMI GUID found\n");
578 platform_device
= eeepc_wmi_add();
579 if (IS_ERR(platform_device
)) {
580 err
= PTR_ERR(platform_device
);
584 err
= platform_driver_register(&platform_driver
);
586 pr_warning("Unable to register platform driver\n");
587 goto fail_platform_driver
;
592 fail_platform_driver
:
593 eeepc_wmi_remove(platform_device
);
598 static void __exit
eeepc_wmi_exit(void)
600 eeepc_wmi_remove(platform_device
);
601 platform_driver_unregister(&platform_driver
);
604 module_init(eeepc_wmi_init
);
605 module_exit(eeepc_wmi_exit
);