4 * Copyright (C) 2009 Novell <trenn@suse.de>
6 * Most stuff taken over from hp-wmi
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <linux/kernel.h>
26 #include <linux/input.h>
27 #include <linux/acpi.h>
28 #include <linux/backlight.h>
30 MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
31 MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
32 MODULE_LICENSE("GPL");
35 module_param(debug
, int, 0);
36 MODULE_PARM_DESC(debug
, "Set this to 1 to let the driver be more verbose");
38 MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
39 MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
41 /* Temporary workaround until the WMI sysfs interface goes in
42 { "svn", DMI_SYS_VENDOR },
43 { "pn", DMI_PRODUCT_NAME },
44 { "pvr", DMI_PRODUCT_VERSION },
45 { "rvn", DMI_BOARD_VENDOR },
46 { "rn", DMI_BOARD_NAME },
49 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*");
51 #define DRV_NAME "msi-wmi"
52 #define DRV_PFX DRV_NAME ": "
54 #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
55 #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
57 #define dprintk(msg...) do { \
59 printk(KERN_INFO DRV_PFX msg); \
63 char type
; /* See KE_* below */
71 * KE_KEY the only used key type, but keep this, others might also
72 * show up in the future. Compare with hp-wmi.c
74 enum { KE_KEY
, KE_END
};
76 static struct key_entry msi_wmi_keymap
[] = {
77 { KE_KEY
, 0xd0, KEY_BRIGHTNESSUP
, 0, {0, } },
78 { KE_KEY
, 0xd1, KEY_BRIGHTNESSDOWN
, 1, {0, } },
79 { KE_KEY
, 0xd2, KEY_VOLUMEUP
, 2, {0, } },
80 { KE_KEY
, 0xd3, KEY_VOLUMEDOWN
, 3, {0, } },
84 struct backlight_device
*backlight
;
86 static int backlight_map
[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF };
88 static struct input_dev
*msi_wmi_input_dev
;
90 static int msi_wmi_query_block(int instance
, int *ret
)
93 union acpi_object
*obj
;
95 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
97 status
= wmi_query_block(MSIWMI_BIOS_GUID
, instance
, &output
);
101 if (!obj
|| obj
->type
!= ACPI_TYPE_INTEGER
) {
103 printk(KERN_ERR DRV_PFX
"query block returned object "
104 "type: %d - buffer length:%d\n", obj
->type
,
105 obj
->type
== ACPI_TYPE_BUFFER
?
106 obj
->buffer
.length
: 0);
111 *ret
= obj
->integer
.value
;
116 static int msi_wmi_set_block(int instance
, int value
)
120 struct acpi_buffer input
= { sizeof(int), &value
};
122 dprintk("Going to set block of instance: %d - value: %d\n",
125 status
= wmi_set_block(MSIWMI_BIOS_GUID
, instance
, &input
);
127 return ACPI_SUCCESS(status
) ? 0 : 1;
130 static int bl_get(struct backlight_device
*bd
)
132 int level
, err
, ret
= 0;
134 /* Instance 1 is "get backlight", cmp with DSDT */
135 err
= msi_wmi_query_block(1, &ret
);
137 printk(KERN_ERR DRV_PFX
"Could not query backlight: %d\n", err
);
138 dprintk("Get: Query block returned: %d\n", ret
);
139 for (level
= 0; level
< ARRAY_SIZE(backlight_map
); level
++) {
140 if (backlight_map
[level
] == ret
) {
141 dprintk("Current backlight level: 0x%X - index: %d\n",
142 backlight_map
[level
], level
);
146 if (level
== ARRAY_SIZE(backlight_map
)) {
147 printk(KERN_ERR DRV_PFX
"get: Invalid brightness value: 0x%X\n",
154 static int bl_set_status(struct backlight_device
*bd
)
156 int bright
= bd
->props
.brightness
;
157 if (bright
>= ARRAY_SIZE(backlight_map
) || bright
< 0)
160 /* Instance 0 is "set backlight" */
161 return msi_wmi_set_block(0, backlight_map
[bright
]);
164 static struct backlight_ops msi_backlight_ops
= {
165 .get_brightness
= bl_get
,
166 .update_status
= bl_set_status
,
169 static struct key_entry
*msi_wmi_get_entry_by_scancode(int code
)
171 struct key_entry
*key
;
173 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++)
174 if (code
== key
->code
)
180 static struct key_entry
*msi_wmi_get_entry_by_keycode(int keycode
)
182 struct key_entry
*key
;
184 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++)
185 if (key
->type
== KE_KEY
&& keycode
== key
->keycode
)
191 static int msi_wmi_getkeycode(struct input_dev
*dev
, int scancode
, int *keycode
)
193 struct key_entry
*key
= msi_wmi_get_entry_by_scancode(scancode
);
195 if (key
&& key
->type
== KE_KEY
) {
196 *keycode
= key
->keycode
;
203 static int msi_wmi_setkeycode(struct input_dev
*dev
, int scancode
, int keycode
)
205 struct key_entry
*key
;
208 if (keycode
< 0 || keycode
> KEY_MAX
)
211 key
= msi_wmi_get_entry_by_scancode(scancode
);
212 if (key
&& key
->type
== KE_KEY
) {
213 old_keycode
= key
->keycode
;
214 key
->keycode
= keycode
;
215 set_bit(keycode
, dev
->keybit
);
216 if (!msi_wmi_get_entry_by_keycode(old_keycode
))
217 clear_bit(old_keycode
, dev
->keybit
);
224 static void msi_wmi_notify(u32 value
, void *context
)
226 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
227 static struct key_entry
*key
;
228 union acpi_object
*obj
;
231 wmi_get_event_data(value
, &response
);
233 obj
= (union acpi_object
*)response
.pointer
;
235 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
) {
236 int eventcode
= obj
->integer
.value
;
237 dprintk("Eventcode: 0x%x\n", eventcode
);
238 key
= msi_wmi_get_entry_by_scancode(eventcode
);
240 cur
= ktime_get_real();
241 /* Ignore event if the same event happened in a 50 ms
242 timeframe -> Key press may result in 10-20 GPEs */
243 if (ktime_to_us(ktime_sub(cur
, key
->last_pressed
))
245 dprintk("Suppressed key event 0x%X - "
246 "Last press was %lld us ago\n",
248 ktime_to_us(ktime_sub(cur
,
249 key
->last_pressed
)));
252 key
->last_pressed
= cur
;
256 /* Brightness is served via acpi video driver */
258 (key
->keycode
== KEY_BRIGHTNESSUP
||
259 key
->keycode
== KEY_BRIGHTNESSDOWN
))
262 dprintk("Send key: 0x%X - "
263 "Input layer keycode: %d\n", key
->code
,
265 input_report_key(msi_wmi_input_dev
,
267 input_sync(msi_wmi_input_dev
);
268 input_report_key(msi_wmi_input_dev
,
270 input_sync(msi_wmi_input_dev
);
274 printk(KERN_INFO
"Unknown key pressed - %x\n",
277 printk(KERN_INFO DRV_PFX
"Unknown event received\n");
278 kfree(response
.pointer
);
281 static int __init
msi_wmi_input_setup(void)
283 struct key_entry
*key
;
286 msi_wmi_input_dev
= input_allocate_device();
288 msi_wmi_input_dev
->name
= "MSI WMI hotkeys";
289 msi_wmi_input_dev
->phys
= "wmi/input0";
290 msi_wmi_input_dev
->id
.bustype
= BUS_HOST
;
291 msi_wmi_input_dev
->getkeycode
= msi_wmi_getkeycode
;
292 msi_wmi_input_dev
->setkeycode
= msi_wmi_setkeycode
;
294 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++) {
297 set_bit(EV_KEY
, msi_wmi_input_dev
->evbit
);
298 set_bit(key
->keycode
, msi_wmi_input_dev
->keybit
);
303 err
= input_register_device(msi_wmi_input_dev
);
306 input_free_device(msi_wmi_input_dev
);
313 static int __init
msi_wmi_init(void)
317 if (wmi_has_guid(MSIWMI_EVENT_GUID
)) {
318 err
= wmi_install_notify_handler(MSIWMI_EVENT_GUID
,
319 msi_wmi_notify
, NULL
);
323 err
= msi_wmi_input_setup();
325 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
329 if (!acpi_video_backlight_support()) {
330 backlight
= backlight_device_register(DRV_NAME
,
331 NULL
, NULL
, &msi_backlight_ops
);
332 if (IS_ERR(backlight
)) {
333 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
334 input_unregister_device(msi_wmi_input_dev
);
338 backlight
->props
.max_brightness
= ARRAY_SIZE(backlight_map
) - 1;
341 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
342 input_unregister_device(msi_wmi_input_dev
);
343 backlight_device_unregister(backlight
);
346 backlight
->props
.brightness
= err
;
349 printk(KERN_INFO DRV_PFX
"Event handler installed\n");
353 static void __exit
msi_wmi_exit(void)
355 if (wmi_has_guid(MSIWMI_EVENT_GUID
)) {
356 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
357 input_unregister_device(msi_wmi_input_dev
);
358 backlight_device_unregister(backlight
);
362 module_init(msi_wmi_init
);
363 module_exit(msi_wmi_exit
);