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(event: value, out: &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(objp: 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(dev: toshiba_wmi_input_dev, |
81 | keymap: toshiba_wmi_keymap, NULL); |
82 | if (err) |
83 | goto err_free_dev; |
84 | |
85 | status = wmi_install_notify_handler(WMI_EVENT_GUID, |
86 | handler: 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(dev: 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(list: 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 | |