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