1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Apple Motion Sensor driver (joystick emulation) |
4 | * |
5 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) |
6 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | |
11 | #include <linux/types.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/init.h> |
14 | #include <linux/delay.h> |
15 | |
16 | #include "ams.h" |
17 | |
18 | static bool joystick; |
19 | module_param(joystick, bool, S_IRUGO); |
20 | MODULE_PARM_DESC(joystick, "Enable the input class device on module load" ); |
21 | |
22 | static bool invert; |
23 | module_param(invert, bool, S_IWUSR | S_IRUGO); |
24 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis" ); |
25 | |
26 | static DEFINE_MUTEX(ams_input_mutex); |
27 | |
28 | static void ams_idev_poll(struct input_dev *idev) |
29 | { |
30 | s8 x, y, z; |
31 | |
32 | mutex_lock(&ams_info.lock); |
33 | |
34 | ams_sensors(x: &x, y: &y, z: &z); |
35 | |
36 | x -= ams_info.xcalib; |
37 | y -= ams_info.ycalib; |
38 | z -= ams_info.zcalib; |
39 | |
40 | input_report_abs(dev: idev, ABS_X, value: invert ? -x : x); |
41 | input_report_abs(dev: idev, ABS_Y, value: invert ? -y : y); |
42 | input_report_abs(dev: idev, ABS_Z, value: z); |
43 | |
44 | input_sync(dev: idev); |
45 | |
46 | mutex_unlock(lock: &ams_info.lock); |
47 | } |
48 | |
49 | /* Call with ams_info.lock held! */ |
50 | static int ams_input_enable(void) |
51 | { |
52 | struct input_dev *input; |
53 | s8 x, y, z; |
54 | int error; |
55 | |
56 | ams_sensors(x: &x, y: &y, z: &z); |
57 | ams_info.xcalib = x; |
58 | ams_info.ycalib = y; |
59 | ams_info.zcalib = z; |
60 | |
61 | input = input_allocate_device(); |
62 | if (!input) |
63 | return -ENOMEM; |
64 | |
65 | input->name = "Apple Motion Sensor" ; |
66 | input->id.bustype = ams_info.bustype; |
67 | input->id.vendor = 0; |
68 | input->dev.parent = &ams_info.of_dev->dev; |
69 | |
70 | input_set_abs_params(dev: input, ABS_X, min: -50, max: 50, fuzz: 3, flat: 0); |
71 | input_set_abs_params(dev: input, ABS_Y, min: -50, max: 50, fuzz: 3, flat: 0); |
72 | input_set_abs_params(dev: input, ABS_Z, min: -50, max: 50, fuzz: 3, flat: 0); |
73 | input_set_capability(dev: input, EV_KEY, BTN_TOUCH); |
74 | |
75 | error = input_setup_polling(dev: input, poll_fn: ams_idev_poll); |
76 | if (error) |
77 | goto err_free_input; |
78 | |
79 | input_set_poll_interval(dev: input, interval: 25); |
80 | |
81 | error = input_register_device(input); |
82 | if (error) |
83 | goto err_free_input; |
84 | |
85 | ams_info.idev = input; |
86 | joystick = true; |
87 | |
88 | return 0; |
89 | |
90 | err_free_input: |
91 | input_free_device(dev: input); |
92 | return error; |
93 | } |
94 | |
95 | static void ams_input_disable(void) |
96 | { |
97 | if (ams_info.idev) { |
98 | input_unregister_device(ams_info.idev); |
99 | ams_info.idev = NULL; |
100 | } |
101 | |
102 | joystick = false; |
103 | } |
104 | |
105 | static ssize_t ams_input_show_joystick(struct device *dev, |
106 | struct device_attribute *attr, char *buf) |
107 | { |
108 | return sprintf(buf, fmt: "%d\n" , joystick); |
109 | } |
110 | |
111 | static ssize_t ams_input_store_joystick(struct device *dev, |
112 | struct device_attribute *attr, const char *buf, size_t count) |
113 | { |
114 | unsigned long enable; |
115 | int error = 0; |
116 | int ret; |
117 | |
118 | ret = kstrtoul(s: buf, base: 0, res: &enable); |
119 | if (ret) |
120 | return ret; |
121 | if (enable > 1) |
122 | return -EINVAL; |
123 | |
124 | mutex_lock(&ams_input_mutex); |
125 | |
126 | if (enable != joystick) { |
127 | if (enable) |
128 | error = ams_input_enable(); |
129 | else |
130 | ams_input_disable(); |
131 | } |
132 | |
133 | mutex_unlock(lock: &ams_input_mutex); |
134 | |
135 | return error ? error : count; |
136 | } |
137 | |
138 | static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, |
139 | ams_input_show_joystick, ams_input_store_joystick); |
140 | |
141 | int ams_input_init(void) |
142 | { |
143 | if (joystick) |
144 | ams_input_enable(); |
145 | |
146 | return device_create_file(device: &ams_info.of_dev->dev, entry: &dev_attr_joystick); |
147 | } |
148 | |
149 | void ams_input_exit(void) |
150 | { |
151 | device_remove_file(dev: &ams_info.of_dev->dev, attr: &dev_attr_joystick); |
152 | |
153 | mutex_lock(&ams_input_mutex); |
154 | ams_input_disable(); |
155 | mutex_unlock(lock: &ams_input_mutex); |
156 | } |
157 | |