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");
34 MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
35 MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
37 /* Temporary workaround until the WMI sysfs interface goes in
38 { "svn", DMI_SYS_VENDOR },
39 { "pn", DMI_PRODUCT_NAME },
40 { "pvr", DMI_PRODUCT_VERSION },
41 { "rvn", DMI_BOARD_VENDOR },
42 { "rn", DMI_BOARD_NAME },
45 MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-6638:*");
47 #define DRV_NAME "msi-wmi"
48 #define DRV_PFX DRV_NAME ": "
50 #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
51 #define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
53 #define dprintk(msg...) pr_debug(DRV_PFX msg)
56 char type
; /* See KE_* below */
63 * KE_KEY the only used key type, but keep this, others might also
64 * show up in the future. Compare with hp-wmi.c
66 enum { KE_KEY
, KE_END
};
68 static struct key_entry msi_wmi_keymap
[] = {
69 { KE_KEY
, 0xd0, KEY_BRIGHTNESSUP
, {0, } },
70 { KE_KEY
, 0xd1, KEY_BRIGHTNESSDOWN
, {0, } },
71 { KE_KEY
, 0xd2, KEY_VOLUMEUP
, {0, } },
72 { KE_KEY
, 0xd3, KEY_VOLUMEDOWN
, {0, } },
76 struct backlight_device
*backlight
;
78 static int backlight_map
[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF };
80 static struct input_dev
*msi_wmi_input_dev
;
82 static int msi_wmi_query_block(int instance
, int *ret
)
85 union acpi_object
*obj
;
87 struct acpi_buffer output
= { ACPI_ALLOCATE_BUFFER
, NULL
};
89 status
= wmi_query_block(MSIWMI_BIOS_GUID
, instance
, &output
);
93 if (!obj
|| obj
->type
!= ACPI_TYPE_INTEGER
) {
95 printk(KERN_ERR DRV_PFX
"query block returned object "
96 "type: %d - buffer length:%d\n", obj
->type
,
97 obj
->type
== ACPI_TYPE_BUFFER
?
98 obj
->buffer
.length
: 0);
103 *ret
= obj
->integer
.value
;
108 static int msi_wmi_set_block(int instance
, int value
)
112 struct acpi_buffer input
= { sizeof(int), &value
};
114 dprintk("Going to set block of instance: %d - value: %d\n",
117 status
= wmi_set_block(MSIWMI_BIOS_GUID
, instance
, &input
);
119 return ACPI_SUCCESS(status
) ? 0 : 1;
122 static int bl_get(struct backlight_device
*bd
)
124 int level
, err
, ret
= 0;
126 /* Instance 1 is "get backlight", cmp with DSDT */
127 err
= msi_wmi_query_block(1, &ret
);
129 printk(KERN_ERR DRV_PFX
"Could not query backlight: %d\n", err
);
130 dprintk("Get: Query block returned: %d\n", ret
);
131 for (level
= 0; level
< ARRAY_SIZE(backlight_map
); level
++) {
132 if (backlight_map
[level
] == ret
) {
133 dprintk("Current backlight level: 0x%X - index: %d\n",
134 backlight_map
[level
], level
);
138 if (level
== ARRAY_SIZE(backlight_map
)) {
139 printk(KERN_ERR DRV_PFX
"get: Invalid brightness value: 0x%X\n",
146 static int bl_set_status(struct backlight_device
*bd
)
148 int bright
= bd
->props
.brightness
;
149 if (bright
>= ARRAY_SIZE(backlight_map
) || bright
< 0)
152 /* Instance 0 is "set backlight" */
153 return msi_wmi_set_block(0, backlight_map
[bright
]);
156 static struct backlight_ops msi_backlight_ops
= {
157 .get_brightness
= bl_get
,
158 .update_status
= bl_set_status
,
161 static struct key_entry
*msi_wmi_get_entry_by_scancode(int code
)
163 struct key_entry
*key
;
165 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++)
166 if (code
== key
->code
)
172 static struct key_entry
*msi_wmi_get_entry_by_keycode(int keycode
)
174 struct key_entry
*key
;
176 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++)
177 if (key
->type
== KE_KEY
&& keycode
== key
->keycode
)
183 static int msi_wmi_getkeycode(struct input_dev
*dev
, int scancode
, int *keycode
)
185 struct key_entry
*key
= msi_wmi_get_entry_by_scancode(scancode
);
187 if (key
&& key
->type
== KE_KEY
) {
188 *keycode
= key
->keycode
;
195 static int msi_wmi_setkeycode(struct input_dev
*dev
, int scancode
, int keycode
)
197 struct key_entry
*key
;
200 if (keycode
< 0 || keycode
> KEY_MAX
)
203 key
= msi_wmi_get_entry_by_scancode(scancode
);
204 if (key
&& key
->type
== KE_KEY
) {
205 old_keycode
= key
->keycode
;
206 key
->keycode
= keycode
;
207 set_bit(keycode
, dev
->keybit
);
208 if (!msi_wmi_get_entry_by_keycode(old_keycode
))
209 clear_bit(old_keycode
, dev
->keybit
);
216 static void msi_wmi_notify(u32 value
, void *context
)
218 struct acpi_buffer response
= { ACPI_ALLOCATE_BUFFER
, NULL
};
219 static struct key_entry
*key
;
220 union acpi_object
*obj
;
223 wmi_get_event_data(value
, &response
);
225 obj
= (union acpi_object
*)response
.pointer
;
227 if (obj
&& obj
->type
== ACPI_TYPE_INTEGER
) {
228 int eventcode
= obj
->integer
.value
;
229 dprintk("Eventcode: 0x%x\n", eventcode
);
230 key
= msi_wmi_get_entry_by_scancode(eventcode
);
232 cur
= ktime_get_real();
233 /* Ignore event if the same event happened in a 50 ms
234 timeframe -> Key press may result in 10-20 GPEs */
235 if (ktime_to_us(ktime_sub(cur
, key
->last_pressed
))
237 dprintk("Suppressed key event 0x%X - "
238 "Last press was %lld us ago\n",
240 ktime_to_us(ktime_sub(cur
,
241 key
->last_pressed
)));
244 key
->last_pressed
= cur
;
248 /* Brightness is served via acpi video driver */
250 (key
->keycode
== KEY_BRIGHTNESSUP
||
251 key
->keycode
== KEY_BRIGHTNESSDOWN
))
254 dprintk("Send key: 0x%X - "
255 "Input layer keycode: %d\n", key
->code
,
257 input_report_key(msi_wmi_input_dev
,
259 input_sync(msi_wmi_input_dev
);
260 input_report_key(msi_wmi_input_dev
,
262 input_sync(msi_wmi_input_dev
);
266 printk(KERN_INFO
"Unknown key pressed - %x\n",
269 printk(KERN_INFO DRV_PFX
"Unknown event received\n");
270 kfree(response
.pointer
);
273 static int __init
msi_wmi_input_setup(void)
275 struct key_entry
*key
;
278 msi_wmi_input_dev
= input_allocate_device();
279 if (!msi_wmi_input_dev
)
282 msi_wmi_input_dev
->name
= "MSI WMI hotkeys";
283 msi_wmi_input_dev
->phys
= "wmi/input0";
284 msi_wmi_input_dev
->id
.bustype
= BUS_HOST
;
285 msi_wmi_input_dev
->getkeycode
= msi_wmi_getkeycode
;
286 msi_wmi_input_dev
->setkeycode
= msi_wmi_setkeycode
;
288 for (key
= msi_wmi_keymap
; key
->type
!= KE_END
; key
++) {
291 set_bit(EV_KEY
, msi_wmi_input_dev
->evbit
);
292 set_bit(key
->keycode
, msi_wmi_input_dev
->keybit
);
297 err
= input_register_device(msi_wmi_input_dev
);
300 input_free_device(msi_wmi_input_dev
);
307 static int __init
msi_wmi_init(void)
311 if (!wmi_has_guid(MSIWMI_EVENT_GUID
)) {
313 "This machine doesn't have MSI-hotkeys through WMI\n");
316 err
= wmi_install_notify_handler(MSIWMI_EVENT_GUID
,
317 msi_wmi_notify
, NULL
);
321 err
= msi_wmi_input_setup();
323 goto err_uninstall_notifier
;
325 if (!acpi_video_backlight_support()) {
326 backlight
= backlight_device_register(DRV_NAME
,
327 NULL
, NULL
, &msi_backlight_ops
);
328 if (IS_ERR(backlight
))
331 backlight
->props
.max_brightness
= ARRAY_SIZE(backlight_map
) - 1;
334 goto err_free_backlight
;
336 backlight
->props
.brightness
= err
;
338 dprintk("Event handler installed\n");
343 backlight_device_unregister(backlight
);
345 input_unregister_device(msi_wmi_input_dev
);
346 err_uninstall_notifier
:
347 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
351 static void __exit
msi_wmi_exit(void)
353 if (wmi_has_guid(MSIWMI_EVENT_GUID
)) {
354 wmi_remove_notify_handler(MSIWMI_EVENT_GUID
);
355 input_unregister_device(msi_wmi_input_dev
);
356 backlight_device_unregister(backlight
);
360 module_init(msi_wmi_init
);
361 module_exit(msi_wmi_exit
);