1 /* 2 * toshiba_wmi.c - Toshiba WMI Hotkey Driver 3 * 4 * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19 20 #include <linux/kernel.h> 21 #include <linux/module.h> 22 #include <linux/init.h> 23 #include <linux/types.h> 24 #include <linux/acpi.h> 25 #include <linux/input.h> 26 #include <linux/input/sparse-keymap.h> 27 #include <linux/dmi.h> 28 29 MODULE_AUTHOR("Azael Avalos"); 30 MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver"); 31 MODULE_LICENSE("GPL"); 32 33 #define WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" 34 35 MODULE_ALIAS("wmi:"WMI_EVENT_GUID); 36 37 static struct input_dev *toshiba_wmi_input_dev; 38 39 static const struct key_entry toshiba_wmi_keymap[] __initconst = { 40 /* TODO: Add keymap values once found... */ 41 /*{ KE_KEY, 0x00, { KEY_ } },*/ 42 { KE_END, 0 } 43 }; 44 45 static void toshiba_wmi_notify(u32 value, void *context) 46 { 47 struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; 48 union acpi_object *obj; 49 acpi_status status; 50 51 status = wmi_get_event_data(value, &response); 52 if (ACPI_FAILURE(status)) { 53 pr_err("Bad event status 0x%x\n", status); 54 return; 55 } 56 57 obj = (union acpi_object *)response.pointer; 58 if (!obj) 59 return; 60 61 /* TODO: Add proper checks once we have data */ 62 pr_debug("Unknown event received, obj type %x\n", obj->type); 63 64 kfree(response.pointer); 65 } 66 67 static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = { 68 { 69 .ident = "Toshiba laptop", 70 .matches = { 71 DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 72 }, 73 }, 74 {} 75 }; 76 77 static int __init toshiba_wmi_input_setup(void) 78 { 79 acpi_status status; 80 int err; 81 82 toshiba_wmi_input_dev = input_allocate_device(); 83 if (!toshiba_wmi_input_dev) 84 return -ENOMEM; 85 86 toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys"; 87 toshiba_wmi_input_dev->phys = "wmi/input0"; 88 toshiba_wmi_input_dev->id.bustype = BUS_HOST; 89 90 err = sparse_keymap_setup(toshiba_wmi_input_dev, 91 toshiba_wmi_keymap, NULL); 92 if (err) 93 goto err_free_dev; 94 95 status = wmi_install_notify_handler(WMI_EVENT_GUID, 96 toshiba_wmi_notify, NULL); 97 if (ACPI_FAILURE(status)) { 98 err = -EIO; 99 goto err_free_dev; 100 } 101 102 err = input_register_device(toshiba_wmi_input_dev); 103 if (err) 104 goto err_remove_notifier; 105 106 return 0; 107 108 err_remove_notifier: 109 wmi_remove_notify_handler(WMI_EVENT_GUID); 110 err_free_dev: 111 input_free_device(toshiba_wmi_input_dev); 112 return err; 113 } 114 115 static void toshiba_wmi_input_destroy(void) 116 { 117 wmi_remove_notify_handler(WMI_EVENT_GUID); 118 input_unregister_device(toshiba_wmi_input_dev); 119 } 120 121 static int __init toshiba_wmi_init(void) 122 { 123 int ret; 124 125 if (!wmi_has_guid(WMI_EVENT_GUID) || 126 !dmi_check_system(toshiba_wmi_dmi_table)) 127 return -ENODEV; 128 129 ret = toshiba_wmi_input_setup(); 130 if (ret) { 131 pr_err("Failed to setup input device\n"); 132 return ret; 133 } 134 135 pr_info("Toshiba WMI Hotkey Driver\n"); 136 137 return 0; 138 } 139 140 static void __exit toshiba_wmi_exit(void) 141 { 142 if (wmi_has_guid(WMI_EVENT_GUID)) 143 toshiba_wmi_input_destroy(); 144 } 145 146 module_init(toshiba_wmi_init); 147 module_exit(toshiba_wmi_exit); 148