18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * toshiba_wmi.c - Toshiba WMI Hotkey Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <linux/input.h>
168c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h>
178c2ecf20Sopenharmony_ci#include <linux/dmi.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Azael Avalos");
208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define WMI_EVENT_GUID	"59142400-C6A3-40FA-BADB-8A2652834100"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:"WMI_EVENT_GUID);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct input_dev *toshiba_wmi_input_dev;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const struct key_entry toshiba_wmi_keymap[] __initconst = {
308c2ecf20Sopenharmony_ci	/* TODO: Add keymap values once found... */
318c2ecf20Sopenharmony_ci	/*{ KE_KEY, 0x00, { KEY_ } },*/
328c2ecf20Sopenharmony_ci	{ KE_END, 0 }
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void toshiba_wmi_notify(u32 value, void *context)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
388c2ecf20Sopenharmony_ci	union acpi_object *obj;
398c2ecf20Sopenharmony_ci	acpi_status status;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	status = wmi_get_event_data(value, &response);
428c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
438c2ecf20Sopenharmony_ci		pr_err("Bad event status 0x%x\n", status);
448c2ecf20Sopenharmony_ci		return;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	obj = (union acpi_object *)response.pointer;
488c2ecf20Sopenharmony_ci	if (!obj)
498c2ecf20Sopenharmony_ci		return;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* TODO: Add proper checks once we have data */
528c2ecf20Sopenharmony_ci	pr_debug("Unknown event received, obj type %x\n", obj->type);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	kfree(response.pointer);
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
588c2ecf20Sopenharmony_ci	{
598c2ecf20Sopenharmony_ci		.ident = "Toshiba laptop",
608c2ecf20Sopenharmony_ci		.matches = {
618c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
628c2ecf20Sopenharmony_ci		},
638c2ecf20Sopenharmony_ci	},
648c2ecf20Sopenharmony_ci	{}
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int __init toshiba_wmi_input_setup(void)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	acpi_status status;
708c2ecf20Sopenharmony_ci	int err;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	toshiba_wmi_input_dev = input_allocate_device();
738c2ecf20Sopenharmony_ci	if (!toshiba_wmi_input_dev)
748c2ecf20Sopenharmony_ci		return -ENOMEM;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys";
778c2ecf20Sopenharmony_ci	toshiba_wmi_input_dev->phys = "wmi/input0";
788c2ecf20Sopenharmony_ci	toshiba_wmi_input_dev->id.bustype = BUS_HOST;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	err = sparse_keymap_setup(toshiba_wmi_input_dev,
818c2ecf20Sopenharmony_ci				  toshiba_wmi_keymap, NULL);
828c2ecf20Sopenharmony_ci	if (err)
838c2ecf20Sopenharmony_ci		goto err_free_dev;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	status = wmi_install_notify_handler(WMI_EVENT_GUID,
868c2ecf20Sopenharmony_ci					    toshiba_wmi_notify, NULL);
878c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
888c2ecf20Sopenharmony_ci		err = -EIO;
898c2ecf20Sopenharmony_ci		goto err_free_dev;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	err = input_register_device(toshiba_wmi_input_dev);
938c2ecf20Sopenharmony_ci	if (err)
948c2ecf20Sopenharmony_ci		goto err_remove_notifier;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	return 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci err_remove_notifier:
998c2ecf20Sopenharmony_ci	wmi_remove_notify_handler(WMI_EVENT_GUID);
1008c2ecf20Sopenharmony_ci err_free_dev:
1018c2ecf20Sopenharmony_ci	input_free_device(toshiba_wmi_input_dev);
1028c2ecf20Sopenharmony_ci	return err;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void toshiba_wmi_input_destroy(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	wmi_remove_notify_handler(WMI_EVENT_GUID);
1088c2ecf20Sopenharmony_ci	input_unregister_device(toshiba_wmi_input_dev);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int __init toshiba_wmi_init(void)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	int ret;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (!wmi_has_guid(WMI_EVENT_GUID) ||
1168c2ecf20Sopenharmony_ci	    !dmi_check_system(toshiba_wmi_dmi_table))
1178c2ecf20Sopenharmony_ci		return -ENODEV;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ret = toshiba_wmi_input_setup();
1208c2ecf20Sopenharmony_ci	if (ret) {
1218c2ecf20Sopenharmony_ci		pr_err("Failed to setup input device\n");
1228c2ecf20Sopenharmony_ci		return ret;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	pr_info("Toshiba WMI Hotkey Driver\n");
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void __exit toshiba_wmi_exit(void)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	if (wmi_has_guid(WMI_EVENT_GUID))
1338c2ecf20Sopenharmony_ci		toshiba_wmi_input_destroy();
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cimodule_init(toshiba_wmi_init);
1378c2ecf20Sopenharmony_cimodule_exit(toshiba_wmi_exit);
138