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 18 MODULE_LICENSE("GPL"); 19 20 static int mouse_emulate_buttons; 21 static int mouse_button2_keycode = KEY_RIGHTCTRL; /* right control key */ 22 static int mouse_button3_keycode = KEY_RIGHTALT; /* right option key */ 23 24 static struct input_dev *mac_hid_emumouse_dev; 25 26 static DEFINE_MUTEX(mac_hid_emumouse_mutex); 27 28 static 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 64 static void mac_hid_destroy_emumouse(void) 65 { 66 input_unregister_device(mac_hid_emumouse_dev); 67 mac_hid_emumouse_dev = NULL; 68 } 69 70 static 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 92 static 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 136 static 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 143 static 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 151 MODULE_DEVICE_TABLE(input, mac_hid_emumouse_ids); 152 153 static 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 161 static 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 178 static void mac_hid_stop_emulation(void) 179 { 180 input_unregister_handler(&mac_hid_emumouse_handler); 181 mac_hid_destroy_emumouse(); 182 } 183 184 static int mac_hid_toggle_emumouse(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 */ 217 static 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 */ 243 static 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 */ 254 static 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 264 static struct ctl_table_header *mac_hid_sysctl_header; 265 266 static 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 } 274 module_init(mac_hid_init); 275 276 static 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 } 283 module_exit(mac_hid_exit); 284