1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) 1999-2001 Vojtech Pavlik |
4 | * |
5 | * Based on the work of: |
6 | * David Thompson |
7 | * Joseph Krahn |
8 | */ |
9 | |
10 | /* |
11 | * SpaceTec SpaceBall 2003/3003/4000 FLX driver for Linux |
12 | */ |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/module.h> |
17 | #include <linux/input.h> |
18 | #include <linux/serio.h> |
19 | #include <asm/unaligned.h> |
20 | |
21 | #define DRIVER_DESC "SpaceTec SpaceBall 2003/3003/4000 FLX driver" |
22 | |
23 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>" ); |
24 | MODULE_DESCRIPTION(DRIVER_DESC); |
25 | MODULE_LICENSE("GPL" ); |
26 | |
27 | /* |
28 | * Constants. |
29 | */ |
30 | |
31 | #define SPACEBALL_MAX_LENGTH 128 |
32 | #define SPACEBALL_MAX_ID 9 |
33 | |
34 | #define SPACEBALL_1003 1 |
35 | #define SPACEBALL_2003B 3 |
36 | #define SPACEBALL_2003C 4 |
37 | #define SPACEBALL_3003C 7 |
38 | #define SPACEBALL_4000FLX 8 |
39 | #define SPACEBALL_4000FLX_L 9 |
40 | |
41 | static int spaceball_axes[] = { ABS_X, ABS_Z, ABS_Y, ABS_RX, ABS_RZ, ABS_RY }; |
42 | static char *spaceball_names[] = { |
43 | "?" , "SpaceTec SpaceBall 1003" , "SpaceTec SpaceBall 2003" , "SpaceTec SpaceBall 2003B" , |
44 | "SpaceTec SpaceBall 2003C" , "SpaceTec SpaceBall 3003" , "SpaceTec SpaceBall SpaceController" , |
45 | "SpaceTec SpaceBall 3003C" , "SpaceTec SpaceBall 4000FLX" , "SpaceTec SpaceBall 4000FLX Lefty" }; |
46 | |
47 | /* |
48 | * Per-Ball data. |
49 | */ |
50 | |
51 | struct spaceball { |
52 | struct input_dev *dev; |
53 | int idx; |
54 | int escape; |
55 | unsigned char data[SPACEBALL_MAX_LENGTH]; |
56 | char phys[32]; |
57 | }; |
58 | |
59 | /* |
60 | * spaceball_process_packet() decodes packets the driver receives from the |
61 | * SpaceBall. |
62 | */ |
63 | |
64 | static void spaceball_process_packet(struct spaceball* spaceball) |
65 | { |
66 | struct input_dev *dev = spaceball->dev; |
67 | unsigned char *data = spaceball->data; |
68 | int i; |
69 | |
70 | if (spaceball->idx < 2) return; |
71 | |
72 | switch (spaceball->data[0]) { |
73 | |
74 | case 'D': /* Ball data */ |
75 | if (spaceball->idx != 15) return; |
76 | /* |
77 | * Skip first three bytes; read six axes worth of data. |
78 | * Axis values are signed 16-bit big-endian. |
79 | */ |
80 | data += 3; |
81 | for (i = 0; i < ARRAY_SIZE(spaceball_axes); i++) { |
82 | input_report_abs(dev, code: spaceball_axes[i], |
83 | value: (__s16)get_unaligned_be16(p: &data[i * 2])); |
84 | } |
85 | break; |
86 | |
87 | case 'K': /* Button data */ |
88 | if (spaceball->idx != 3) return; |
89 | input_report_key(dev, BTN_1, value: (data[2] & 0x01) || (data[2] & 0x20)); |
90 | input_report_key(dev, BTN_2, value: data[2] & 0x02); |
91 | input_report_key(dev, BTN_3, value: data[2] & 0x04); |
92 | input_report_key(dev, BTN_4, value: data[2] & 0x08); |
93 | input_report_key(dev, BTN_5, value: data[1] & 0x01); |
94 | input_report_key(dev, BTN_6, value: data[1] & 0x02); |
95 | input_report_key(dev, BTN_7, value: data[1] & 0x04); |
96 | input_report_key(dev, BTN_8, value: data[1] & 0x10); |
97 | break; |
98 | |
99 | case '.': /* Advanced button data */ |
100 | if (spaceball->idx != 3) return; |
101 | input_report_key(dev, BTN_1, value: data[2] & 0x01); |
102 | input_report_key(dev, BTN_2, value: data[2] & 0x02); |
103 | input_report_key(dev, BTN_3, value: data[2] & 0x04); |
104 | input_report_key(dev, BTN_4, value: data[2] & 0x08); |
105 | input_report_key(dev, BTN_5, value: data[2] & 0x10); |
106 | input_report_key(dev, BTN_6, value: data[2] & 0x20); |
107 | input_report_key(dev, BTN_7, value: data[2] & 0x80); |
108 | input_report_key(dev, BTN_8, value: data[1] & 0x01); |
109 | input_report_key(dev, BTN_9, value: data[1] & 0x02); |
110 | input_report_key(dev, BTN_A, value: data[1] & 0x04); |
111 | input_report_key(dev, BTN_B, value: data[1] & 0x08); |
112 | input_report_key(dev, BTN_C, value: data[1] & 0x10); |
113 | input_report_key(dev, BTN_MODE, value: data[1] & 0x20); |
114 | break; |
115 | |
116 | case 'E': /* Device error */ |
117 | spaceball->data[spaceball->idx - 1] = 0; |
118 | printk(KERN_ERR "spaceball: Device error. [%s]\n" , spaceball->data + 1); |
119 | break; |
120 | |
121 | case '?': /* Bad command packet */ |
122 | spaceball->data[spaceball->idx - 1] = 0; |
123 | printk(KERN_ERR "spaceball: Bad command. [%s]\n" , spaceball->data + 1); |
124 | break; |
125 | } |
126 | |
127 | input_sync(dev); |
128 | } |
129 | |
130 | /* |
131 | * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, |
132 | * and end in 0x0d. It uses '^' as an escape for CR, XOFF and XON characters which |
133 | * can occur in the axis values. |
134 | */ |
135 | |
136 | static irqreturn_t spaceball_interrupt(struct serio *serio, |
137 | unsigned char data, unsigned int flags) |
138 | { |
139 | struct spaceball *spaceball = serio_get_drvdata(serio); |
140 | |
141 | switch (data) { |
142 | case 0xd: |
143 | spaceball_process_packet(spaceball); |
144 | spaceball->idx = 0; |
145 | spaceball->escape = 0; |
146 | break; |
147 | case '^': |
148 | if (!spaceball->escape) { |
149 | spaceball->escape = 1; |
150 | break; |
151 | } |
152 | spaceball->escape = 0; |
153 | fallthrough; |
154 | case 'M': |
155 | case 'Q': |
156 | case 'S': |
157 | if (spaceball->escape) { |
158 | spaceball->escape = 0; |
159 | data &= 0x1f; |
160 | } |
161 | fallthrough; |
162 | default: |
163 | if (spaceball->escape) |
164 | spaceball->escape = 0; |
165 | if (spaceball->idx < SPACEBALL_MAX_LENGTH) |
166 | spaceball->data[spaceball->idx++] = data; |
167 | break; |
168 | } |
169 | return IRQ_HANDLED; |
170 | } |
171 | |
172 | /* |
173 | * spaceball_disconnect() is the opposite of spaceball_connect() |
174 | */ |
175 | |
176 | static void spaceball_disconnect(struct serio *serio) |
177 | { |
178 | struct spaceball* spaceball = serio_get_drvdata(serio); |
179 | |
180 | serio_close(serio); |
181 | serio_set_drvdata(serio, NULL); |
182 | input_unregister_device(spaceball->dev); |
183 | kfree(objp: spaceball); |
184 | } |
185 | |
186 | /* |
187 | * spaceball_connect() is the routine that is called when someone adds a |
188 | * new serio device that supports Spaceball protocol and registers it as |
189 | * an input device. |
190 | */ |
191 | |
192 | static int spaceball_connect(struct serio *serio, struct serio_driver *drv) |
193 | { |
194 | struct spaceball *spaceball; |
195 | struct input_dev *input_dev; |
196 | int err = -ENOMEM; |
197 | int i, id; |
198 | |
199 | if ((id = serio->id.id) > SPACEBALL_MAX_ID) |
200 | return -ENODEV; |
201 | |
202 | spaceball = kmalloc(size: sizeof(struct spaceball), GFP_KERNEL); |
203 | input_dev = input_allocate_device(); |
204 | if (!spaceball || !input_dev) |
205 | goto fail1; |
206 | |
207 | spaceball->dev = input_dev; |
208 | snprintf(buf: spaceball->phys, size: sizeof(spaceball->phys), fmt: "%s/input0" , serio->phys); |
209 | |
210 | input_dev->name = spaceball_names[id]; |
211 | input_dev->phys = spaceball->phys; |
212 | input_dev->id.bustype = BUS_RS232; |
213 | input_dev->id.vendor = SERIO_SPACEBALL; |
214 | input_dev->id.product = id; |
215 | input_dev->id.version = 0x0100; |
216 | input_dev->dev.parent = &serio->dev; |
217 | |
218 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
219 | |
220 | switch (id) { |
221 | case SPACEBALL_4000FLX: |
222 | case SPACEBALL_4000FLX_L: |
223 | input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_9); |
224 | input_dev->keybit[BIT_WORD(BTN_A)] |= BIT_MASK(BTN_A) | |
225 | BIT_MASK(BTN_B) | BIT_MASK(BTN_C) | |
226 | BIT_MASK(BTN_MODE); |
227 | fallthrough; |
228 | default: |
229 | input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_2) | |
230 | BIT_MASK(BTN_3) | BIT_MASK(BTN_4) | |
231 | BIT_MASK(BTN_5) | BIT_MASK(BTN_6) | |
232 | BIT_MASK(BTN_7) | BIT_MASK(BTN_8); |
233 | fallthrough; |
234 | case SPACEBALL_3003C: |
235 | input_dev->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_1) | |
236 | BIT_MASK(BTN_8); |
237 | } |
238 | |
239 | for (i = 0; i < 3; i++) { |
240 | input_set_abs_params(dev: input_dev, ABS_X + i, min: -8000, max: 8000, fuzz: 8, flat: 40); |
241 | input_set_abs_params(dev: input_dev, ABS_RX + i, min: -1600, max: 1600, fuzz: 2, flat: 8); |
242 | } |
243 | |
244 | serio_set_drvdata(serio, data: spaceball); |
245 | |
246 | err = serio_open(serio, drv); |
247 | if (err) |
248 | goto fail2; |
249 | |
250 | err = input_register_device(spaceball->dev); |
251 | if (err) |
252 | goto fail3; |
253 | |
254 | return 0; |
255 | |
256 | fail3: serio_close(serio); |
257 | fail2: serio_set_drvdata(serio, NULL); |
258 | fail1: input_free_device(dev: input_dev); |
259 | kfree(objp: spaceball); |
260 | return err; |
261 | } |
262 | |
263 | /* |
264 | * The serio driver structure. |
265 | */ |
266 | |
267 | static const struct serio_device_id spaceball_serio_ids[] = { |
268 | { |
269 | .type = SERIO_RS232, |
270 | .proto = SERIO_SPACEBALL, |
271 | .id = SERIO_ANY, |
272 | .extra = SERIO_ANY, |
273 | }, |
274 | { 0 } |
275 | }; |
276 | |
277 | MODULE_DEVICE_TABLE(serio, spaceball_serio_ids); |
278 | |
279 | static struct serio_driver spaceball_drv = { |
280 | .driver = { |
281 | .name = "spaceball" , |
282 | }, |
283 | .description = DRIVER_DESC, |
284 | .id_table = spaceball_serio_ids, |
285 | .interrupt = spaceball_interrupt, |
286 | .connect = spaceball_connect, |
287 | .disconnect = spaceball_disconnect, |
288 | }; |
289 | |
290 | module_serio_driver(spaceball_drv); |
291 | |