162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/macintosh/mac_hid.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * HID support stuff for Macintosh computers. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2000 Franz Sirl. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file will soon be removed in favor of an uinput userspace tool. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/proc_fs.h> 1462306a36Sopenharmony_ci#include <linux/sysctl.h> 1562306a36Sopenharmony_ci#include <linux/input.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int mouse_emulate_buttons; 2262306a36Sopenharmony_cistatic int mouse_button2_keycode = KEY_RIGHTCTRL; /* right control key */ 2362306a36Sopenharmony_cistatic int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic struct input_dev *mac_hid_emumouse_dev; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic DEFINE_MUTEX(mac_hid_emumouse_mutex); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic int mac_hid_create_emumouse(void) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci static struct lock_class_key mac_hid_emumouse_dev_event_class; 3262306a36Sopenharmony_ci static struct lock_class_key mac_hid_emumouse_dev_mutex_class; 3362306a36Sopenharmony_ci int err; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci mac_hid_emumouse_dev = input_allocate_device(); 3662306a36Sopenharmony_ci if (!mac_hid_emumouse_dev) 3762306a36Sopenharmony_ci return -ENOMEM; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci lockdep_set_class(&mac_hid_emumouse_dev->event_lock, 4062306a36Sopenharmony_ci &mac_hid_emumouse_dev_event_class); 4162306a36Sopenharmony_ci lockdep_set_class(&mac_hid_emumouse_dev->mutex, 4262306a36Sopenharmony_ci &mac_hid_emumouse_dev_mutex_class); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci mac_hid_emumouse_dev->name = "Macintosh mouse button emulation"; 4562306a36Sopenharmony_ci mac_hid_emumouse_dev->id.bustype = BUS_ADB; 4662306a36Sopenharmony_ci mac_hid_emumouse_dev->id.vendor = 0x0001; 4762306a36Sopenharmony_ci mac_hid_emumouse_dev->id.product = 0x0001; 4862306a36Sopenharmony_ci mac_hid_emumouse_dev->id.version = 0x0100; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci mac_hid_emumouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 5162306a36Sopenharmony_ci mac_hid_emumouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = 5262306a36Sopenharmony_ci BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); 5362306a36Sopenharmony_ci mac_hid_emumouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci err = input_register_device(mac_hid_emumouse_dev); 5662306a36Sopenharmony_ci if (err) { 5762306a36Sopenharmony_ci input_free_device(mac_hid_emumouse_dev); 5862306a36Sopenharmony_ci mac_hid_emumouse_dev = NULL; 5962306a36Sopenharmony_ci return err; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return 0; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void mac_hid_destroy_emumouse(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci input_unregister_device(mac_hid_emumouse_dev); 6862306a36Sopenharmony_ci mac_hid_emumouse_dev = NULL; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic bool mac_hid_emumouse_filter(struct input_handle *handle, 7262306a36Sopenharmony_ci unsigned int type, unsigned int code, 7362306a36Sopenharmony_ci int value) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci unsigned int btn; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (type != EV_KEY) 7862306a36Sopenharmony_ci return false; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (code == mouse_button2_keycode) 8162306a36Sopenharmony_ci btn = BTN_MIDDLE; 8262306a36Sopenharmony_ci else if (code == mouse_button3_keycode) 8362306a36Sopenharmony_ci btn = BTN_RIGHT; 8462306a36Sopenharmony_ci else 8562306a36Sopenharmony_ci return false; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci input_report_key(mac_hid_emumouse_dev, btn, value); 8862306a36Sopenharmony_ci input_sync(mac_hid_emumouse_dev); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return true; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int mac_hid_emumouse_connect(struct input_handler *handler, 9462306a36Sopenharmony_ci struct input_dev *dev, 9562306a36Sopenharmony_ci const struct input_device_id *id) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct input_handle *handle; 9862306a36Sopenharmony_ci int error; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Don't bind to ourselves */ 10162306a36Sopenharmony_ci if (dev == mac_hid_emumouse_dev) 10262306a36Sopenharmony_ci return -ENODEV; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); 10562306a36Sopenharmony_ci if (!handle) 10662306a36Sopenharmony_ci return -ENOMEM; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci handle->dev = dev; 10962306a36Sopenharmony_ci handle->handler = handler; 11062306a36Sopenharmony_ci handle->name = "mac-button-emul"; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci error = input_register_handle(handle); 11362306a36Sopenharmony_ci if (error) { 11462306a36Sopenharmony_ci printk(KERN_ERR 11562306a36Sopenharmony_ci "mac_hid: Failed to register button emulation handle, " 11662306a36Sopenharmony_ci "error %d\n", error); 11762306a36Sopenharmony_ci goto err_free; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci error = input_open_device(handle); 12162306a36Sopenharmony_ci if (error) { 12262306a36Sopenharmony_ci printk(KERN_ERR 12362306a36Sopenharmony_ci "mac_hid: Failed to open input device, error %d\n", 12462306a36Sopenharmony_ci error); 12562306a36Sopenharmony_ci goto err_unregister; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err_unregister: 13162306a36Sopenharmony_ci input_unregister_handle(handle); 13262306a36Sopenharmony_ci err_free: 13362306a36Sopenharmony_ci kfree(handle); 13462306a36Sopenharmony_ci return error; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void mac_hid_emumouse_disconnect(struct input_handle *handle) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci input_close_device(handle); 14062306a36Sopenharmony_ci input_unregister_handle(handle); 14162306a36Sopenharmony_ci kfree(handle); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic const struct input_device_id mac_hid_emumouse_ids[] = { 14562306a36Sopenharmony_ci { 14662306a36Sopenharmony_ci .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 14762306a36Sopenharmony_ci .evbit = { BIT_MASK(EV_KEY) }, 14862306a36Sopenharmony_ci }, 14962306a36Sopenharmony_ci { }, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic struct input_handler mac_hid_emumouse_handler = { 15562306a36Sopenharmony_ci .filter = mac_hid_emumouse_filter, 15662306a36Sopenharmony_ci .connect = mac_hid_emumouse_connect, 15762306a36Sopenharmony_ci .disconnect = mac_hid_emumouse_disconnect, 15862306a36Sopenharmony_ci .name = "mac-button-emul", 15962306a36Sopenharmony_ci .id_table = mac_hid_emumouse_ids, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int mac_hid_start_emulation(void) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int err; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci err = mac_hid_create_emumouse(); 16762306a36Sopenharmony_ci if (err) 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci err = input_register_handler(&mac_hid_emumouse_handler); 17162306a36Sopenharmony_ci if (err) { 17262306a36Sopenharmony_ci mac_hid_destroy_emumouse(); 17362306a36Sopenharmony_ci return err; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void mac_hid_stop_emulation(void) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci input_unregister_handler(&mac_hid_emumouse_handler); 18262306a36Sopenharmony_ci mac_hid_destroy_emumouse(); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int mac_hid_toggle_emumouse(struct ctl_table *table, int write, 18662306a36Sopenharmony_ci void *buffer, size_t *lenp, loff_t *ppos) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int *valp = table->data; 18962306a36Sopenharmony_ci int old_val = *valp; 19062306a36Sopenharmony_ci int rc; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci rc = mutex_lock_killable(&mac_hid_emumouse_mutex); 19362306a36Sopenharmony_ci if (rc) 19462306a36Sopenharmony_ci return rc; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci rc = proc_dointvec(table, write, buffer, lenp, ppos); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (rc == 0 && write && *valp != old_val) { 19962306a36Sopenharmony_ci if (*valp == 1) 20062306a36Sopenharmony_ci rc = mac_hid_start_emulation(); 20162306a36Sopenharmony_ci else if (*valp == 0) 20262306a36Sopenharmony_ci mac_hid_stop_emulation(); 20362306a36Sopenharmony_ci else 20462306a36Sopenharmony_ci rc = -EINVAL; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Restore the old value in case of error */ 20862306a36Sopenharmony_ci if (rc) 20962306a36Sopenharmony_ci *valp = old_val; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci mutex_unlock(&mac_hid_emumouse_mutex); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return rc; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* file(s) in /proc/sys/dev/mac_hid */ 21762306a36Sopenharmony_cistatic struct ctl_table mac_hid_files[] = { 21862306a36Sopenharmony_ci { 21962306a36Sopenharmony_ci .procname = "mouse_button_emulation", 22062306a36Sopenharmony_ci .data = &mouse_emulate_buttons, 22162306a36Sopenharmony_ci .maxlen = sizeof(int), 22262306a36Sopenharmony_ci .mode = 0644, 22362306a36Sopenharmony_ci .proc_handler = mac_hid_toggle_emumouse, 22462306a36Sopenharmony_ci }, 22562306a36Sopenharmony_ci { 22662306a36Sopenharmony_ci .procname = "mouse_button2_keycode", 22762306a36Sopenharmony_ci .data = &mouse_button2_keycode, 22862306a36Sopenharmony_ci .maxlen = sizeof(int), 22962306a36Sopenharmony_ci .mode = 0644, 23062306a36Sopenharmony_ci .proc_handler = proc_dointvec, 23162306a36Sopenharmony_ci }, 23262306a36Sopenharmony_ci { 23362306a36Sopenharmony_ci .procname = "mouse_button3_keycode", 23462306a36Sopenharmony_ci .data = &mouse_button3_keycode, 23562306a36Sopenharmony_ci .maxlen = sizeof(int), 23662306a36Sopenharmony_ci .mode = 0644, 23762306a36Sopenharmony_ci .proc_handler = proc_dointvec, 23862306a36Sopenharmony_ci }, 23962306a36Sopenharmony_ci { } 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic struct ctl_table_header *mac_hid_sysctl_header; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int __init mac_hid_init(void) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci mac_hid_sysctl_header = register_sysctl("dev/mac_hid", mac_hid_files); 24762306a36Sopenharmony_ci if (!mac_hid_sysctl_header) 24862306a36Sopenharmony_ci return -ENOMEM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_cimodule_init(mac_hid_init); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void __exit mac_hid_exit(void) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci unregister_sysctl_table(mac_hid_sysctl_header); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (mouse_emulate_buttons) 25962306a36Sopenharmony_ci mac_hid_stop_emulation(); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_cimodule_exit(mac_hid_exit); 262