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