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