msi-wmi.c revision fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9d
1d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger/* 2d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * MSI WMI hotkeys 3d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * 4d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * Copyright (C) 2009 Novell <trenn@suse.de> 5d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * 6d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * Most stuff taken over from hp-wmi 7d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * 8d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * This program is free software; you can redistribute it and/or modify 9d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * it under the terms of the GNU General Public License as published by 10d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * the Free Software Foundation; either version 2 of the License, or 11d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * (at your option) any later version. 12d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * 13d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * This program is distributed in the hope that it will be useful, 14d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * but WITHOUT ANY WARRANTY; without even the implied warranty of 15d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * GNU General Public License for more details. 17d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * 18d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * You should have received a copy of the GNU General Public License 19d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * along with this program; if not, write to the Free Software 20d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger */ 22d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 23d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 24d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#include <linux/kernel.h> 25d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#include <linux/input.h> 26c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier#include <linux/input/sparse-keymap.h> 27d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#include <linux/acpi.h> 28d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#include <linux/backlight.h> 29d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 30d12d8baff927a31b7e13b72ed9549be6f296a6efThomas RenningerMODULE_AUTHOR("Thomas Renninger <trenn@suse.de>"); 31d12d8baff927a31b7e13b72ed9549be6f296a6efThomas RenningerMODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); 32d12d8baff927a31b7e13b72ed9549be6f296a6efThomas RenningerMODULE_LICENSE("GPL"); 33d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 34d12d8baff927a31b7e13b72ed9549be6f296a6efThomas RenningerMODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); 35d12d8baff927a31b7e13b72ed9549be6f296a6efThomas RenningerMODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); 36d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 37d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#define DRV_NAME "msi-wmi" 38d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#define DRV_PFX DRV_NAME ": " 39d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 40d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" 41d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" 42d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 43822ddc042a12aa2a8c2030ad4ebc660bc0e66c3fAnisse Astier#define dprintk(msg...) pr_debug(DRV_PFX msg) 44d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 45c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier#define KEYCODE_BASE 0xD0 46de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier#define MSI_WMI_BRIGHTNESSUP KEYCODE_BASE 47de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier#define MSI_WMI_BRIGHTNESSDOWN (KEYCODE_BASE + 1) 48de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier#define MSI_WMI_VOLUMEUP (KEYCODE_BASE + 2) 49de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier#define MSI_WMI_VOLUMEDOWN (KEYCODE_BASE + 3) 50d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic struct key_entry msi_wmi_keymap[] = { 51de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} }, 52de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, 53de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} }, 54de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} }, 55d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger { KE_END, 0} 56d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger}; 57c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astierstatic ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1]; 58d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 59d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstruct backlight_device *backlight; 60d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 61d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int backlight_map[] = { 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF }; 62d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 63d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic struct input_dev *msi_wmi_input_dev; 64d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 65d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int msi_wmi_query_block(int instance, int *ret) 66d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 67d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger acpi_status status; 68d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger union acpi_object *obj; 69d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 70d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 71d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 72d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger status = wmi_query_block(MSIWMI_BIOS_GUID, instance, &output); 73d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 74d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger obj = output.pointer; 75d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 76d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (!obj || obj->type != ACPI_TYPE_INTEGER) { 77d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (obj) { 78d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger printk(KERN_ERR DRV_PFX "query block returned object " 79d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger "type: %d - buffer length:%d\n", obj->type, 80d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger obj->type == ACPI_TYPE_BUFFER ? 81d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger obj->buffer.length : 0); 82d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 83d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger kfree(obj); 84d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return -EINVAL; 85d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 86d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger *ret = obj->integer.value; 87d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger kfree(obj); 88d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return 0; 89d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 90d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 91d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int msi_wmi_set_block(int instance, int value) 92d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 93d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger acpi_status status; 94d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 95d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger struct acpi_buffer input = { sizeof(int), &value }; 96d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 97d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Going to set block of instance: %d - value: %d\n", 98d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger instance, value); 99d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 100d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger status = wmi_set_block(MSIWMI_BIOS_GUID, instance, &input); 101d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 102d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return ACPI_SUCCESS(status) ? 0 : 1; 103d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 104d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 105d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int bl_get(struct backlight_device *bd) 106d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 107de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier int level, err, ret; 108d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 109d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger /* Instance 1 is "get backlight", cmp with DSDT */ 110d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger err = msi_wmi_query_block(1, &ret); 111de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier if (err) { 112d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger printk(KERN_ERR DRV_PFX "Could not query backlight: %d\n", err); 113de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier return -EINVAL; 114de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier } 115d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Get: Query block returned: %d\n", ret); 116d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger for (level = 0; level < ARRAY_SIZE(backlight_map); level++) { 117d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (backlight_map[level] == ret) { 118d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Current backlight level: 0x%X - index: %d\n", 119d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger backlight_map[level], level); 120d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger break; 121d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 122d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 123d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (level == ARRAY_SIZE(backlight_map)) { 124d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger printk(KERN_ERR DRV_PFX "get: Invalid brightness value: 0x%X\n", 125d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger ret); 126d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return -EINVAL; 127d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 128d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return level; 129d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 130d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 131d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int bl_set_status(struct backlight_device *bd) 132d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 133d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger int bright = bd->props.brightness; 134d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (bright >= ARRAY_SIZE(backlight_map) || bright < 0) 135d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return -EINVAL; 136d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 137d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger /* Instance 0 is "set backlight" */ 138d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return msi_wmi_set_block(0, backlight_map[bright]); 139d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 140d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 141d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic struct backlight_ops msi_backlight_ops = { 142d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger .get_brightness = bl_get, 143d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger .update_status = bl_set_status, 144d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger}; 145d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 146d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic void msi_wmi_notify(u32 value, void *context) 147d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 148d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 149d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger static struct key_entry *key; 150d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger union acpi_object *obj; 151d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger ktime_t cur; 152fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown acpi_status status; 153d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 154fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown status = wmi_get_event_data(value, &response); 155fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown if (status != AE_OK) { 156fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown printk(KERN_INFO DRV_PFX "bad event status 0x%x\n", status); 157fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown return; 158fda11e61ff8a4e3a8ebbd434e46560b67cc0ca9dLen Brown } 159d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 160d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger obj = (union acpi_object *)response.pointer; 161d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 162d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (obj && obj->type == ACPI_TYPE_INTEGER) { 163d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger int eventcode = obj->integer.value; 164d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Eventcode: 0x%x\n", eventcode); 165c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev, 166c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier eventcode); 167d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (key) { 168c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier ktime_t diff; 169d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger cur = ktime_get_real(); 170c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier diff = ktime_sub(cur, last_pressed[key->code - 171c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier KEYCODE_BASE]); 172d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger /* Ignore event if the same event happened in a 50 ms 173d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger timeframe -> Key press may result in 10-20 GPEs */ 174c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier if (ktime_to_us(diff) < 1000 * 50) { 175d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Suppressed key event 0x%X - " 176d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger "Last press was %lld us ago\n", 177c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier key->code, ktime_to_us(diff)); 178d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return; 179d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 180c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier last_pressed[key->code - KEYCODE_BASE] = cur; 181d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 182d607af93006594f7da1d4b7d44724c5308f4e892Anisse Astier if (key->type == KE_KEY && 183d607af93006594f7da1d4b7d44724c5308f4e892Anisse Astier /* Brightness is served via acpi video driver */ 184de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier (!acpi_video_backlight_support() || 185de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier (key->code != MSI_WMI_BRIGHTNESSUP && 186de078e5747fa3a95efac04fd6725dcceb4520416Anisse Astier key->code != MSI_WMI_BRIGHTNESSDOWN))) { 187d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger dprintk("Send key: 0x%X - " 188d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger "Input layer keycode: %d\n", key->code, 189d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger key->keycode); 190c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier sparse_keymap_report_entry(msi_wmi_input_dev, 191c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier key, 1, true); 192d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 193d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } else 194d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger printk(KERN_INFO "Unknown key pressed - %x\n", 195d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger eventcode); 196d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } else 197d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger printk(KERN_INFO DRV_PFX "Unknown event received\n"); 198d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger kfree(response.pointer); 199d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 200d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 201d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int __init msi_wmi_input_setup(void) 202d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 203d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger int err; 204d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 205d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger msi_wmi_input_dev = input_allocate_device(); 20646b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (!msi_wmi_input_dev) 20746b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier return -ENOMEM; 208d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 209d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger msi_wmi_input_dev->name = "MSI WMI hotkeys"; 210d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger msi_wmi_input_dev->phys = "wmi/input0"; 211d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger msi_wmi_input_dev->id.bustype = BUS_HOST; 212c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier 213c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier err = sparse_keymap_setup(msi_wmi_input_dev, msi_wmi_keymap, NULL); 214c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier if (err) 215c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier goto err_free_dev; 216d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 217d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger err = input_register_device(msi_wmi_input_dev); 218d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 219c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier if (err) 220c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier goto err_free_keymap; 221c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier 222c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier memset(last_pressed, 0, sizeof(last_pressed)); 223d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 224d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return 0; 225c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier 226c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astiererr_free_keymap: 227c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier sparse_keymap_free(msi_wmi_input_dev); 228c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astiererr_free_dev: 229c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier input_free_device(msi_wmi_input_dev); 230c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier return err; 231d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 232d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 233d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic int __init msi_wmi_init(void) 234d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 235d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger int err; 236d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 23746b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { 23846b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier printk(KERN_ERR 23946b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier "This machine doesn't have MSI-hotkeys through WMI\n"); 24046b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier return -ENODEV; 24146b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier } 24246b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, 24346b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier msi_wmi_notify, NULL); 244f27725756be8a2c2dc65eaf70d0b52807aa2f113Len Brown if (ACPI_FAILURE(err)) 24546b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier return -EINVAL; 246d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 24746b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier err = msi_wmi_input_setup(); 24846b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (err) 24946b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier goto err_uninstall_notifier; 250d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 25146b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (!acpi_video_backlight_support()) { 25246b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier backlight = backlight_device_register(DRV_NAME, 25346b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier NULL, NULL, &msi_backlight_ops); 25446b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (IS_ERR(backlight)) 25546b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier goto err_free_input; 25646b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier 25746b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier backlight->props.max_brightness = ARRAY_SIZE(backlight_map) - 1; 25846b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier err = bl_get(NULL); 25946b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier if (err < 0) 26046b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier goto err_free_backlight; 26146b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier 26246b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier backlight->props.brightness = err; 263d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 264822ddc042a12aa2a8c2030ad4ebc660bc0e66c3fAnisse Astier dprintk("Event handler installed\n"); 26546b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier 266d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger return 0; 26746b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier 26846b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astiererr_free_backlight: 26946b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier backlight_device_unregister(backlight); 27046b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astiererr_free_input: 27146b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier input_unregister_device(msi_wmi_input_dev); 27246b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astiererr_uninstall_notifier: 27346b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier wmi_remove_notify_handler(MSIWMI_EVENT_GUID); 27446b51eb9e14afb3bde4bc2fe3bbc22ce012647d4Anisse Astier return err; 275d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 276d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 277d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningerstatic void __exit msi_wmi_exit(void) 278d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger{ 279d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger if (wmi_has_guid(MSIWMI_EVENT_GUID)) { 280d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger wmi_remove_notify_handler(MSIWMI_EVENT_GUID); 281c30116c6f0d26cd6e46dfa578163d573ef4730b2Anisse Astier sparse_keymap_free(msi_wmi_input_dev); 282d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger input_unregister_device(msi_wmi_input_dev); 283d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger backlight_device_unregister(backlight); 284d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger } 285d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger} 286d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renninger 287d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningermodule_init(msi_wmi_init); 288d12d8baff927a31b7e13b72ed9549be6f296a6efThomas Renningermodule_exit(msi_wmi_exit); 289