1/* 2 * drivers/macintosh/mac_hid.c 3 * 4 * HID support stuff for Macintosh computers. 5 * 6 * Copyright (C) 2000 Franz Sirl. 7 * 8 * This file will soon be removed in favor of an uinput userspace tool. 9 */ 10 11#include <linux/init.h> 12#include <linux/proc_fs.h> 13#include <linux/sysctl.h> 14#include <linux/input.h> 15#include <linux/module.h> 16#include <linux/slab.h> 17 18MODULE_LICENSE("GPL"); 19 20static int mouse_emulate_buttons; 21static int mouse_button2_keycode = KEY_RIGHTCTRL; /* right control key */ 22static int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */ 23 24static struct input_dev *mac_hid_emumouse_dev; 25 26static DEFINE_MUTEX(mac_hid_emumouse_mutex); 27 28static int mac_hid_create_emumouse(void) 29{ 30 static struct lock_class_key mac_hid_emumouse_dev_event_class; 31 static struct lock_class_key mac_hid_emumouse_dev_mutex_class; 32 int err; 33 34 mac_hid_emumouse_dev = input_allocate_device(); 35 if (!mac_hid_emumouse_dev) 36 return -ENOMEM; 37 38 lockdep_set_class(&mac_hid_emumouse_dev->event_lock, 39 &mac_hid_emumouse_dev_event_class); 40 lockdep_set_class(&mac_hid_emumouse_dev->mutex, 41 &mac_hid_emumouse_dev_mutex_class); 42 43 mac_hid_emumouse_dev->name = "Macintosh mouse button emulation"; 44 mac_hid_emumouse_dev->id.bustype = BUS_ADB; 45 mac_hid_emumouse_dev->id.vendor = 0x0001; 46 mac_hid_emumouse_dev->id.product = 0x0001; 47 mac_hid_emumouse_dev->id.version = 0x0100; 48 49 mac_hid_emumouse_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 50 mac_hid_emumouse_dev->keybit[BIT_WORD(BTN_MOUSE)] = 51 BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); 52 mac_hid_emumouse_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 53 54 err = input_register_device(mac_hid_emumouse_dev); 55 if (err) { 56 input_free_device(mac_hid_emumouse_dev); 57 mac_hid_emumouse_dev = NULL; 58 return err; 59 } 60 61 return 0; 62} 63 64static void mac_hid_destroy_emumouse(void) 65{ 66 input_unregister_device(mac_hid_emumouse_dev); 67 mac_hid_emumouse_dev = NULL; 68} 69 70static bool mac_hid_emumouse_filter(struct input_handle *handle, 71 unsigned int type, unsigned int code, 72 int value) 73{ 74 unsigned int btn; 75 76 if (type != EV_KEY) 77 return false; 78 79 if (code == mouse_button2_keycode) 80 btn = BTN_MIDDLE; 81 else if (code == mouse_button3_keycode) 82 btn = BTN_RIGHT; 83 else 84 return false; 85 86 input_report_key(mac_hid_emumouse_dev, btn, value); 87 input_sync(mac_hid_emumouse_dev); 88 89 return true; 90} 91 92static int mac_hid_emumouse_connect(struct input_handler *handler, 93 struct input_dev *dev, 94 const struct input_device_id *id) 95{ 96 struct input_handle *handle; 97 int error; 98 99 /* Don't bind to ourselves */ 100 if (dev == mac_hid_emumouse_dev) 101 return -ENODEV; 102 103 handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); 104 if (!handle) 105 return -ENOMEM; 106 107 handle->dev = dev; 108 handle->handler = handler; 109 handle->name = "mac-button-emul"; 110 111 error = input_register_handle(handle); 112 if (error) { 113 printk(KERN_ERR 114 "mac_hid: Failed to register button emulation handle, " 115 "error %d\n", error); 116 goto err_free; 117 } 118 119 error = input_open_device(handle); 120 if (error) { 121 printk(KERN_ERR 122 "mac_hid: Failed to open input device, error %d\n", 123 error); 124 goto err_unregister; 125 } 126 127 return 0; 128 129 err_unregister: 130 input_unregister_handle(handle); 131 err_free: 132 kfree(handle); 133 return error; 134} 135 136static void mac_hid_emumouse_disconnect(struct input_handle *handle) 137{ 138 input_close_device(handle); 139 input_unregister_handle(handle); 140 kfree(handle); 141} 142 143static const struct input_device_id mac_hid_emumouse_ids[] = { 144 { 145 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 146 .evbit = { BIT_MASK(EV_KEY) }, 147 }, 148 { }, 149}; 150 151MODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids); 152 153static struct input_handler mac_hid_emumouse_handler = { 154 .filter = mac_hid_emumouse_filter, 155 .connect = mac_hid_emumouse_connect, 156 .disconnect = mac_hid_emumouse_disconnect, 157 .name = "mac-button-emul", 158 .id_table = mac_hid_emumouse_ids, 159}; 160 161static int mac_hid_start_emulation(void) 162{ 163 int err; 164 165 err = mac_hid_create_emumouse(); 166 if (err) 167 return err; 168 169 err = input_register_handler(&mac_hid_emumouse_handler); 170 if (err) { 171 mac_hid_destroy_emumouse(); 172 return err; 173 } 174 175 return 0; 176} 177 178static void mac_hid_stop_emulation(void) 179{ 180 input_unregister_handler(&mac_hid_emumouse_handler); 181 mac_hid_destroy_emumouse(); 182} 183 184static int mac_hid_toggle_emumouse(struct ctl_table *table, int write, 185 void __user *buffer, size_t *lenp, 186 loff_t *ppos) 187{ 188 int *valp = table->data; 189 int old_val = *valp; 190 int rc; 191 192 rc = mutex_lock_killable(&mac_hid_emumouse_mutex); 193 if (rc) 194 return rc; 195 196 rc = proc_dointvec(table, write, buffer, lenp, ppos); 197 198 if (rc == 0 && write && *valp != old_val) { 199 if (*valp == 1) 200 rc = mac_hid_start_emulation(); 201 else if (*valp == 0) 202 mac_hid_stop_emulation(); 203 else 204 rc = -EINVAL; 205 } 206 207 /* Restore the old value in case of error */ 208 if (rc) 209 *valp = old_val; 210 211 mutex_unlock(&mac_hid_emumouse_mutex); 212 213 return rc; 214} 215 216/* file(s) in /proc/sys/dev/mac_hid */ 217static struct ctl_table mac_hid_files[] = { 218 { 219 .procname = "mouse_button_emulation", 220 .data = &mouse_emulate_buttons, 221 .maxlen = sizeof(int), 222 .mode = 0644, 223 .proc_handler = mac_hid_toggle_emumouse, 224 }, 225 { 226 .procname = "mouse_button2_keycode", 227 .data = &mouse_button2_keycode, 228 .maxlen = sizeof(int), 229 .mode = 0644, 230 .proc_handler = proc_dointvec, 231 }, 232 { 233 .procname = "mouse_button3_keycode", 234 .data = &mouse_button3_keycode, 235 .maxlen = sizeof(int), 236 .mode = 0644, 237 .proc_handler = proc_dointvec, 238 }, 239 { } 240}; 241 242/* dir in /proc/sys/dev */ 243static struct ctl_table mac_hid_dir[] = { 244 { 245 .procname = "mac_hid", 246 .maxlen = 0, 247 .mode = 0555, 248 .child = mac_hid_files, 249 }, 250 { } 251}; 252 253/* /proc/sys/dev itself, in case that is not there yet */ 254static struct ctl_table mac_hid_root_dir[] = { 255 { 256 .procname = "dev", 257 .maxlen = 0, 258 .mode = 0555, 259 .child = mac_hid_dir, 260 }, 261 { } 262}; 263 264static struct ctl_table_header *mac_hid_sysctl_header; 265 266static int __init mac_hid_init(void) 267{ 268 mac_hid_sysctl_header = register_sysctl_table(mac_hid_root_dir); 269 if (!mac_hid_sysctl_header) 270 return -ENOMEM; 271 272 return 0; 273} 274module_init(mac_hid_init); 275 276static void __exit mac_hid_exit(void) 277{ 278 unregister_sysctl_table(mac_hid_sysctl_header); 279 280 if (mouse_emulate_buttons) 281 mac_hid_stop_emulation(); 282} 283module_exit(mac_hid_exit); 284