18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * drivers/macintosh/mac_hid.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * HID support stuff for Macintosh computers.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2000 Franz Sirl.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file will soon be removed in favor of an uinput userspace tool.
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
148c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
158c2ecf20Sopenharmony_ci#include <linux/input.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int mouse_emulate_buttons;
228c2ecf20Sopenharmony_cistatic int mouse_button2_keycode = KEY_RIGHTCTRL;	/* right control key */
238c2ecf20Sopenharmony_cistatic int mouse_button3_keycode = KEY_RIGHTALT;	/* right option key */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct input_dev *mac_hid_emumouse_dev;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mac_hid_emumouse_mutex);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int mac_hid_create_emumouse(void)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	static struct lock_class_key mac_hid_emumouse_dev_event_class;
328c2ecf20Sopenharmony_ci	static struct lock_class_key mac_hid_emumouse_dev_mutex_class;
338c2ecf20Sopenharmony_ci	int err;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev = input_allocate_device();
368c2ecf20Sopenharmony_ci	if (!mac_hid_emumouse_dev)
378c2ecf20Sopenharmony_ci		return -ENOMEM;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	lockdep_set_class(&mac_hid_emumouse_dev->event_lock,
408c2ecf20Sopenharmony_ci			  &mac_hid_emumouse_dev_event_class);
418c2ecf20Sopenharmony_ci	lockdep_set_class(&mac_hid_emumouse_dev->mutex,
428c2ecf20Sopenharmony_ci			  &mac_hid_emumouse_dev_mutex_class);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->name = "Macintosh mouse button emulation";
458c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->id.bustype = BUS_ADB;
468c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->id.vendor = 0x0001;
478c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->id.product = 0x0001;
488c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->id.version = 0x0100;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
518c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->keybit[BIT_WORD(BTN_MOUSE)] =
528c2ecf20Sopenharmony_ci		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT);
538c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	err = input_register_device(mac_hid_emumouse_dev);
568c2ecf20Sopenharmony_ci	if (err) {
578c2ecf20Sopenharmony_ci		input_free_device(mac_hid_emumouse_dev);
588c2ecf20Sopenharmony_ci		mac_hid_emumouse_dev = NULL;
598c2ecf20Sopenharmony_ci		return err;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void mac_hid_destroy_emumouse(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	input_unregister_device(mac_hid_emumouse_dev);
688c2ecf20Sopenharmony_ci	mac_hid_emumouse_dev = NULL;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic bool mac_hid_emumouse_filter(struct input_handle *handle,
728c2ecf20Sopenharmony_ci				    unsigned int type, unsigned int code,
738c2ecf20Sopenharmony_ci				    int value)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	unsigned int btn;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (type != EV_KEY)
788c2ecf20Sopenharmony_ci		return false;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (code == mouse_button2_keycode)
818c2ecf20Sopenharmony_ci		btn = BTN_MIDDLE;
828c2ecf20Sopenharmony_ci	else if (code == mouse_button3_keycode)
838c2ecf20Sopenharmony_ci		btn = BTN_RIGHT;
848c2ecf20Sopenharmony_ci	else
858c2ecf20Sopenharmony_ci		return false;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	input_report_key(mac_hid_emumouse_dev, btn, value);
888c2ecf20Sopenharmony_ci	input_sync(mac_hid_emumouse_dev);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return true;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int mac_hid_emumouse_connect(struct input_handler *handler,
948c2ecf20Sopenharmony_ci				    struct input_dev *dev,
958c2ecf20Sopenharmony_ci				    const struct input_device_id *id)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct input_handle *handle;
988c2ecf20Sopenharmony_ci	int error;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Don't bind to ourselves */
1018c2ecf20Sopenharmony_ci	if (dev == mac_hid_emumouse_dev)
1028c2ecf20Sopenharmony_ci		return -ENODEV;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
1058c2ecf20Sopenharmony_ci	if (!handle)
1068c2ecf20Sopenharmony_ci		return -ENOMEM;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	handle->dev = dev;
1098c2ecf20Sopenharmony_ci	handle->handler = handler;
1108c2ecf20Sopenharmony_ci	handle->name = "mac-button-emul";
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	error = input_register_handle(handle);
1138c2ecf20Sopenharmony_ci	if (error) {
1148c2ecf20Sopenharmony_ci		printk(KERN_ERR
1158c2ecf20Sopenharmony_ci			"mac_hid: Failed to register button emulation handle, "
1168c2ecf20Sopenharmony_ci			"error %d\n", error);
1178c2ecf20Sopenharmony_ci		goto err_free;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	error = input_open_device(handle);
1218c2ecf20Sopenharmony_ci	if (error) {
1228c2ecf20Sopenharmony_ci		printk(KERN_ERR
1238c2ecf20Sopenharmony_ci			"mac_hid: Failed to open input device, error %d\n",
1248c2ecf20Sopenharmony_ci			error);
1258c2ecf20Sopenharmony_ci		goto err_unregister;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	return 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci err_unregister:
1318c2ecf20Sopenharmony_ci	input_unregister_handle(handle);
1328c2ecf20Sopenharmony_ci err_free:
1338c2ecf20Sopenharmony_ci	kfree(handle);
1348c2ecf20Sopenharmony_ci	return error;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void mac_hid_emumouse_disconnect(struct input_handle *handle)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	input_close_device(handle);
1408c2ecf20Sopenharmony_ci	input_unregister_handle(handle);
1418c2ecf20Sopenharmony_ci	kfree(handle);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic const struct input_device_id mac_hid_emumouse_ids[] = {
1458c2ecf20Sopenharmony_ci	{
1468c2ecf20Sopenharmony_ci		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
1478c2ecf20Sopenharmony_ci		.evbit = { BIT_MASK(EV_KEY) },
1488c2ecf20Sopenharmony_ci	},
1498c2ecf20Sopenharmony_ci	{ },
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic struct input_handler mac_hid_emumouse_handler = {
1558c2ecf20Sopenharmony_ci	.filter		= mac_hid_emumouse_filter,
1568c2ecf20Sopenharmony_ci	.connect	= mac_hid_emumouse_connect,
1578c2ecf20Sopenharmony_ci	.disconnect	= mac_hid_emumouse_disconnect,
1588c2ecf20Sopenharmony_ci	.name		= "mac-button-emul",
1598c2ecf20Sopenharmony_ci	.id_table	= mac_hid_emumouse_ids,
1608c2ecf20Sopenharmony_ci};
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int mac_hid_start_emulation(void)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int err;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	err = mac_hid_create_emumouse();
1678c2ecf20Sopenharmony_ci	if (err)
1688c2ecf20Sopenharmony_ci		return err;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	err = input_register_handler(&mac_hid_emumouse_handler);
1718c2ecf20Sopenharmony_ci	if (err) {
1728c2ecf20Sopenharmony_ci		mac_hid_destroy_emumouse();
1738c2ecf20Sopenharmony_ci		return err;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void mac_hid_stop_emulation(void)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	input_unregister_handler(&mac_hid_emumouse_handler);
1828c2ecf20Sopenharmony_ci	mac_hid_destroy_emumouse();
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int mac_hid_toggle_emumouse(struct ctl_table *table, int write,
1868c2ecf20Sopenharmony_ci				   void *buffer, size_t *lenp, loff_t *ppos)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int *valp = table->data;
1898c2ecf20Sopenharmony_ci	int old_val = *valp;
1908c2ecf20Sopenharmony_ci	int rc;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	rc = mutex_lock_killable(&mac_hid_emumouse_mutex);
1938c2ecf20Sopenharmony_ci	if (rc)
1948c2ecf20Sopenharmony_ci		return rc;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	rc = proc_dointvec(table, write, buffer, lenp, ppos);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (rc == 0 && write && *valp != old_val) {
1998c2ecf20Sopenharmony_ci		if (*valp == 1)
2008c2ecf20Sopenharmony_ci			rc = mac_hid_start_emulation();
2018c2ecf20Sopenharmony_ci		else if (*valp == 0)
2028c2ecf20Sopenharmony_ci			mac_hid_stop_emulation();
2038c2ecf20Sopenharmony_ci		else
2048c2ecf20Sopenharmony_ci			rc = -EINVAL;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* Restore the old value in case of error */
2088c2ecf20Sopenharmony_ci	if (rc)
2098c2ecf20Sopenharmony_ci		*valp = old_val;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	mutex_unlock(&mac_hid_emumouse_mutex);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return rc;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* file(s) in /proc/sys/dev/mac_hid */
2178c2ecf20Sopenharmony_cistatic struct ctl_table mac_hid_files[] = {
2188c2ecf20Sopenharmony_ci	{
2198c2ecf20Sopenharmony_ci		.procname	= "mouse_button_emulation",
2208c2ecf20Sopenharmony_ci		.data		= &mouse_emulate_buttons,
2218c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2228c2ecf20Sopenharmony_ci		.mode		= 0644,
2238c2ecf20Sopenharmony_ci		.proc_handler	= mac_hid_toggle_emumouse,
2248c2ecf20Sopenharmony_ci	},
2258c2ecf20Sopenharmony_ci	{
2268c2ecf20Sopenharmony_ci		.procname	= "mouse_button2_keycode",
2278c2ecf20Sopenharmony_ci		.data		= &mouse_button2_keycode,
2288c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2298c2ecf20Sopenharmony_ci		.mode		= 0644,
2308c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec,
2318c2ecf20Sopenharmony_ci	},
2328c2ecf20Sopenharmony_ci	{
2338c2ecf20Sopenharmony_ci		.procname	= "mouse_button3_keycode",
2348c2ecf20Sopenharmony_ci		.data		= &mouse_button3_keycode,
2358c2ecf20Sopenharmony_ci		.maxlen		= sizeof(int),
2368c2ecf20Sopenharmony_ci		.mode		= 0644,
2378c2ecf20Sopenharmony_ci		.proc_handler	= proc_dointvec,
2388c2ecf20Sopenharmony_ci	},
2398c2ecf20Sopenharmony_ci	{ }
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* dir in /proc/sys/dev */
2438c2ecf20Sopenharmony_cistatic struct ctl_table mac_hid_dir[] = {
2448c2ecf20Sopenharmony_ci	{
2458c2ecf20Sopenharmony_ci		.procname	= "mac_hid",
2468c2ecf20Sopenharmony_ci		.maxlen		= 0,
2478c2ecf20Sopenharmony_ci		.mode		= 0555,
2488c2ecf20Sopenharmony_ci		.child		= mac_hid_files,
2498c2ecf20Sopenharmony_ci	},
2508c2ecf20Sopenharmony_ci	{ }
2518c2ecf20Sopenharmony_ci};
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* /proc/sys/dev itself, in case that is not there yet */
2548c2ecf20Sopenharmony_cistatic struct ctl_table mac_hid_root_dir[] = {
2558c2ecf20Sopenharmony_ci	{
2568c2ecf20Sopenharmony_ci		.procname	= "dev",
2578c2ecf20Sopenharmony_ci		.maxlen		= 0,
2588c2ecf20Sopenharmony_ci		.mode		= 0555,
2598c2ecf20Sopenharmony_ci		.child		= mac_hid_dir,
2608c2ecf20Sopenharmony_ci	},
2618c2ecf20Sopenharmony_ci	{ }
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic struct ctl_table_header *mac_hid_sysctl_header;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic int __init mac_hid_init(void)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir);
2698c2ecf20Sopenharmony_ci	if (!mac_hid_sysctl_header)
2708c2ecf20Sopenharmony_ci		return -ENOMEM;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_cimodule_init(mac_hid_init);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void __exit mac_hid_exit(void)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	unregister_sysctl_table(mac_hid_sysctl_header);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (mouse_emulate_buttons)
2818c2ecf20Sopenharmony_ci		mac_hid_stop_emulation();
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_cimodule_exit(mac_hid_exit);
284