114991fc7SAzael Avalos /*
214991fc7SAzael Avalos  * toshiba_wmi.c - Toshiba WMI Hotkey Driver
314991fc7SAzael Avalos  *
414991fc7SAzael Avalos  * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
514991fc7SAzael Avalos  *
614991fc7SAzael Avalos  * This program is free software; you can redistribute it and/or modify
714991fc7SAzael Avalos  * it under the terms of the GNU General Public License as published by
814991fc7SAzael Avalos  * the Free Software Foundation; either version 2 of the License, or
914991fc7SAzael Avalos  * (at your option) any later version.
1014991fc7SAzael Avalos  *
1114991fc7SAzael Avalos  * This program is distributed in the hope that it will be useful,
1214991fc7SAzael Avalos  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1314991fc7SAzael Avalos  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1414991fc7SAzael Avalos  * GNU General Public License for more details.
1514991fc7SAzael Avalos  *
1614991fc7SAzael Avalos  */
1714991fc7SAzael Avalos 
1814991fc7SAzael Avalos #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1914991fc7SAzael Avalos 
2014991fc7SAzael Avalos #include <linux/kernel.h>
2114991fc7SAzael Avalos #include <linux/module.h>
2214991fc7SAzael Avalos #include <linux/init.h>
2314991fc7SAzael Avalos #include <linux/types.h>
2414991fc7SAzael Avalos #include <linux/acpi.h>
2514991fc7SAzael Avalos #include <linux/input.h>
2614991fc7SAzael Avalos #include <linux/input/sparse-keymap.h>
2714991fc7SAzael Avalos 
2814991fc7SAzael Avalos MODULE_AUTHOR("Azael Avalos");
2914991fc7SAzael Avalos MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
3014991fc7SAzael Avalos MODULE_LICENSE("GPL");
3114991fc7SAzael Avalos 
3214991fc7SAzael Avalos #define TOSHIBA_WMI_EVENT_GUID	"59142400-C6A3-40FA-BADB-8A2652834100"
3314991fc7SAzael Avalos 
3414991fc7SAzael Avalos MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID);
3514991fc7SAzael Avalos 
3614991fc7SAzael Avalos static struct input_dev *toshiba_wmi_input_dev;
3714991fc7SAzael Avalos 
3814991fc7SAzael Avalos static const struct key_entry toshiba_wmi_keymap[] __initconst = {
3914991fc7SAzael Avalos 	/* TODO: Add keymap values once found... */
4014991fc7SAzael Avalos 	/*{ KE_KEY, 0x00, { KEY_ } },*/
4114991fc7SAzael Avalos 	{ KE_END, 0 }
4214991fc7SAzael Avalos };
4314991fc7SAzael Avalos 
4414991fc7SAzael Avalos static void toshiba_wmi_notify(u32 value, void *context)
4514991fc7SAzael Avalos {
4614991fc7SAzael Avalos 	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
4714991fc7SAzael Avalos 	union acpi_object *obj;
4814991fc7SAzael Avalos 	acpi_status status;
4914991fc7SAzael Avalos 
5014991fc7SAzael Avalos 	status = wmi_get_event_data(value, &response);
5114991fc7SAzael Avalos 	if (ACPI_FAILURE(status)) {
5214991fc7SAzael Avalos 		pr_err("Bad event status 0x%x\n", status);
5314991fc7SAzael Avalos 		return;
5414991fc7SAzael Avalos 	}
5514991fc7SAzael Avalos 
5614991fc7SAzael Avalos 	obj = (union acpi_object *)response.pointer;
5714991fc7SAzael Avalos 	if (!obj)
5814991fc7SAzael Avalos 		return;
5914991fc7SAzael Avalos 
6014991fc7SAzael Avalos 	/* TODO: Add proper checks once we have data */
6114991fc7SAzael Avalos 	pr_debug("Unknown event received, obj type %x\n", obj->type);
6214991fc7SAzael Avalos 
6314991fc7SAzael Avalos 	kfree(response.pointer);
6414991fc7SAzael Avalos }
6514991fc7SAzael Avalos 
6614991fc7SAzael Avalos static int __init toshiba_wmi_input_setup(void)
6714991fc7SAzael Avalos {
6814991fc7SAzael Avalos 	acpi_status status;
6914991fc7SAzael Avalos 	int err;
7014991fc7SAzael Avalos 
7114991fc7SAzael Avalos 	toshiba_wmi_input_dev = input_allocate_device();
7214991fc7SAzael Avalos 	if (!toshiba_wmi_input_dev)
7314991fc7SAzael Avalos 		return -ENOMEM;
7414991fc7SAzael Avalos 
7514991fc7SAzael Avalos 	toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys";
7614991fc7SAzael Avalos 	toshiba_wmi_input_dev->phys = "wmi/input0";
7714991fc7SAzael Avalos 	toshiba_wmi_input_dev->id.bustype = BUS_HOST;
7814991fc7SAzael Avalos 
7914991fc7SAzael Avalos 	err = sparse_keymap_setup(toshiba_wmi_input_dev,
8014991fc7SAzael Avalos 				  toshiba_wmi_keymap, NULL);
8114991fc7SAzael Avalos 	if (err)
8214991fc7SAzael Avalos 		goto err_free_dev;
8314991fc7SAzael Avalos 
8414991fc7SAzael Avalos 	status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID,
8514991fc7SAzael Avalos 					    toshiba_wmi_notify, NULL);
8614991fc7SAzael Avalos 	if (ACPI_FAILURE(status)) {
8714991fc7SAzael Avalos 		err = -EIO;
8814991fc7SAzael Avalos 		goto err_free_keymap;
8914991fc7SAzael Avalos 	}
9014991fc7SAzael Avalos 
9114991fc7SAzael Avalos 	err = input_register_device(toshiba_wmi_input_dev);
9214991fc7SAzael Avalos 	if (err)
9314991fc7SAzael Avalos 		goto err_remove_notifier;
9414991fc7SAzael Avalos 
9514991fc7SAzael Avalos 	return 0;
9614991fc7SAzael Avalos 
9714991fc7SAzael Avalos  err_remove_notifier:
9814991fc7SAzael Avalos 	wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
9914991fc7SAzael Avalos  err_free_keymap:
10014991fc7SAzael Avalos 	sparse_keymap_free(toshiba_wmi_input_dev);
10114991fc7SAzael Avalos  err_free_dev:
10214991fc7SAzael Avalos 	input_free_device(toshiba_wmi_input_dev);
10314991fc7SAzael Avalos 	return err;
10414991fc7SAzael Avalos }
10514991fc7SAzael Avalos 
10614991fc7SAzael Avalos static void toshiba_wmi_input_destroy(void)
10714991fc7SAzael Avalos {
10814991fc7SAzael Avalos 	wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
10914991fc7SAzael Avalos 	sparse_keymap_free(toshiba_wmi_input_dev);
11014991fc7SAzael Avalos 	input_unregister_device(toshiba_wmi_input_dev);
11114991fc7SAzael Avalos }
11214991fc7SAzael Avalos 
11314991fc7SAzael Avalos static int __init toshiba_wmi_init(void)
11414991fc7SAzael Avalos {
11514991fc7SAzael Avalos 	int ret;
11614991fc7SAzael Avalos 
11714991fc7SAzael Avalos 	if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
11814991fc7SAzael Avalos 		return -ENODEV;
11914991fc7SAzael Avalos 
12014991fc7SAzael Avalos 	ret = toshiba_wmi_input_setup();
12114991fc7SAzael Avalos 	if (ret) {
12214991fc7SAzael Avalos 		pr_err("Failed to setup input device\n");
12314991fc7SAzael Avalos 		return ret;
12414991fc7SAzael Avalos 	}
12514991fc7SAzael Avalos 
12614991fc7SAzael Avalos 	pr_info("Toshiba WMI Hotkey Driver\n");
12714991fc7SAzael Avalos 
12814991fc7SAzael Avalos 	return 0;
12914991fc7SAzael Avalos }
13014991fc7SAzael Avalos 
13114991fc7SAzael Avalos static void __exit toshiba_wmi_exit(void)
13214991fc7SAzael Avalos {
13314991fc7SAzael Avalos 	if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
13414991fc7SAzael Avalos 		toshiba_wmi_input_destroy();
13514991fc7SAzael Avalos }
13614991fc7SAzael Avalos 
13714991fc7SAzael Avalos module_init(toshiba_wmi_init);
13814991fc7SAzael Avalos module_exit(toshiba_wmi_exit);
139