1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
214991fc7SAzael Avalos /*
314991fc7SAzael Avalos  * toshiba_wmi.c - Toshiba WMI Hotkey Driver
414991fc7SAzael Avalos  *
514991fc7SAzael Avalos  * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
614991fc7SAzael Avalos  */
714991fc7SAzael Avalos 
814991fc7SAzael Avalos #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
914991fc7SAzael Avalos 
1014991fc7SAzael Avalos #include <linux/kernel.h>
1114991fc7SAzael Avalos #include <linux/module.h>
1214991fc7SAzael Avalos #include <linux/init.h>
1314991fc7SAzael Avalos #include <linux/types.h>
1414991fc7SAzael Avalos #include <linux/acpi.h>
1514991fc7SAzael Avalos #include <linux/input.h>
1614991fc7SAzael Avalos #include <linux/input/sparse-keymap.h>
171c80e960SAzael Avalos #include <linux/dmi.h>
1814991fc7SAzael Avalos 
1914991fc7SAzael Avalos MODULE_AUTHOR("Azael Avalos");
2014991fc7SAzael Avalos MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
2114991fc7SAzael Avalos MODULE_LICENSE("GPL");
2214991fc7SAzael Avalos 
231c80e960SAzael Avalos #define WMI_EVENT_GUID	"59142400-C6A3-40FA-BADB-8A2652834100"
2414991fc7SAzael Avalos 
251c80e960SAzael Avalos MODULE_ALIAS("wmi:"WMI_EVENT_GUID);
2614991fc7SAzael Avalos 
2714991fc7SAzael Avalos static struct input_dev *toshiba_wmi_input_dev;
2814991fc7SAzael Avalos 
2914991fc7SAzael Avalos static const struct key_entry toshiba_wmi_keymap[] __initconst = {
3014991fc7SAzael Avalos 	/* TODO: Add keymap values once found... */
3114991fc7SAzael Avalos 	/*{ KE_KEY, 0x00, { KEY_ } },*/
3214991fc7SAzael Avalos 	{ KE_END, 0 }
3314991fc7SAzael Avalos };
3414991fc7SAzael Avalos 
toshiba_wmi_notify(u32 value,void * context)3514991fc7SAzael Avalos static void toshiba_wmi_notify(u32 value, void *context)
3614991fc7SAzael Avalos {
3714991fc7SAzael Avalos 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
3814991fc7SAzael Avalos 	union acpi_object *obj;
3914991fc7SAzael Avalos 	acpi_status status;
4014991fc7SAzael Avalos 
4114991fc7SAzael Avalos 	status = wmi_get_event_data(value, &response);
4214991fc7SAzael Avalos 	if (ACPI_FAILURE(status)) {
4314991fc7SAzael Avalos 		pr_err("Bad event status 0x%x\n", status);
4414991fc7SAzael Avalos 		return;
4514991fc7SAzael Avalos 	}
4614991fc7SAzael Avalos 
4714991fc7SAzael Avalos 	obj = (union acpi_object *)response.pointer;
4814991fc7SAzael Avalos 	if (!obj)
4914991fc7SAzael Avalos 		return;
5014991fc7SAzael Avalos 
5114991fc7SAzael Avalos 	/* TODO: Add proper checks once we have data */
5214991fc7SAzael Avalos 	pr_debug("Unknown event received, obj type %x\n", obj->type);
5314991fc7SAzael Avalos 
5414991fc7SAzael Avalos 	kfree(response.pointer);
5514991fc7SAzael Avalos }
5614991fc7SAzael Avalos 
576faadbbbSChristoph Hellwig static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
581c80e960SAzael Avalos 	{
591c80e960SAzael Avalos 		.ident = "Toshiba laptop",
601c80e960SAzael Avalos 		.matches = {
611c80e960SAzael Avalos 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
621c80e960SAzael Avalos 		},
631c80e960SAzael Avalos 	},
641c80e960SAzael Avalos 	{}
651c80e960SAzael Avalos };
661c80e960SAzael Avalos 
toshiba_wmi_input_setup(void)6714991fc7SAzael Avalos static int __init toshiba_wmi_input_setup(void)
6814991fc7SAzael Avalos {
6914991fc7SAzael Avalos 	acpi_status status;
7014991fc7SAzael Avalos 	int err;
7114991fc7SAzael Avalos 
7214991fc7SAzael Avalos 	toshiba_wmi_input_dev = input_allocate_device();
7314991fc7SAzael Avalos 	if (!toshiba_wmi_input_dev)
7414991fc7SAzael Avalos 		return -ENOMEM;
7514991fc7SAzael Avalos 
7614991fc7SAzael Avalos 	toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys";
7714991fc7SAzael Avalos 	toshiba_wmi_input_dev->phys = "wmi/input0";
7814991fc7SAzael Avalos 	toshiba_wmi_input_dev->id.bustype = BUS_HOST;
7914991fc7SAzael Avalos 
8014991fc7SAzael Avalos 	err = sparse_keymap_setup(toshiba_wmi_input_dev,
8114991fc7SAzael Avalos 				  toshiba_wmi_keymap, NULL);
8214991fc7SAzael Avalos 	if (err)
8314991fc7SAzael Avalos 		goto err_free_dev;
8414991fc7SAzael Avalos 
851c80e960SAzael Avalos 	status = wmi_install_notify_handler(WMI_EVENT_GUID,
8614991fc7SAzael Avalos 					    toshiba_wmi_notify, NULL);
8714991fc7SAzael Avalos 	if (ACPI_FAILURE(status)) {
8814991fc7SAzael Avalos 		err = -EIO;
89c6d973f4SMichał Kępień 		goto err_free_dev;
9014991fc7SAzael Avalos 	}
9114991fc7SAzael Avalos 
9214991fc7SAzael Avalos 	err = input_register_device(toshiba_wmi_input_dev);
9314991fc7SAzael Avalos 	if (err)
9414991fc7SAzael Avalos 		goto err_remove_notifier;
9514991fc7SAzael Avalos 
9614991fc7SAzael Avalos 	return 0;
9714991fc7SAzael Avalos 
9814991fc7SAzael Avalos  err_remove_notifier:
991c80e960SAzael Avalos 	wmi_remove_notify_handler(WMI_EVENT_GUID);
10014991fc7SAzael Avalos  err_free_dev:
10114991fc7SAzael Avalos 	input_free_device(toshiba_wmi_input_dev);
10214991fc7SAzael Avalos 	return err;
10314991fc7SAzael Avalos }
10414991fc7SAzael Avalos 
toshiba_wmi_input_destroy(void)10514991fc7SAzael Avalos static void toshiba_wmi_input_destroy(void)
10614991fc7SAzael Avalos {
1071c80e960SAzael Avalos 	wmi_remove_notify_handler(WMI_EVENT_GUID);
10814991fc7SAzael Avalos 	input_unregister_device(toshiba_wmi_input_dev);
10914991fc7SAzael Avalos }
11014991fc7SAzael Avalos 
toshiba_wmi_init(void)11114991fc7SAzael Avalos static int __init toshiba_wmi_init(void)
11214991fc7SAzael Avalos {
11314991fc7SAzael Avalos 	int ret;
11414991fc7SAzael Avalos 
1151c80e960SAzael Avalos 	if (!wmi_has_guid(WMI_EVENT_GUID) ||
1161c80e960SAzael Avalos 	    !dmi_check_system(toshiba_wmi_dmi_table))
11714991fc7SAzael Avalos 		return -ENODEV;
11814991fc7SAzael Avalos 
11914991fc7SAzael Avalos 	ret = toshiba_wmi_input_setup();
12014991fc7SAzael Avalos 	if (ret) {
12114991fc7SAzael Avalos 		pr_err("Failed to setup input device\n");
12214991fc7SAzael Avalos 		return ret;
12314991fc7SAzael Avalos 	}
12414991fc7SAzael Avalos 
12514991fc7SAzael Avalos 	pr_info("Toshiba WMI Hotkey Driver\n");
12614991fc7SAzael Avalos 
12714991fc7SAzael Avalos 	return 0;
12814991fc7SAzael Avalos }
12914991fc7SAzael Avalos 
toshiba_wmi_exit(void)13014991fc7SAzael Avalos static void __exit toshiba_wmi_exit(void)
13114991fc7SAzael Avalos {
1321c80e960SAzael Avalos 	if (wmi_has_guid(WMI_EVENT_GUID))
13314991fc7SAzael Avalos 		toshiba_wmi_input_destroy();
13414991fc7SAzael Avalos }
13514991fc7SAzael Avalos 
13614991fc7SAzael Avalos module_init(toshiba_wmi_init);
13714991fc7SAzael Avalos module_exit(toshiba_wmi_exit);
138