1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers) |
4 | * DEC VSXXX-GA mouse (rectangular mouse, with ball) |
5 | * DEC VSXXX-AB tablet (digitizer with hair cross or stylus) |
6 | * |
7 | * Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de> |
8 | * |
9 | * The packet format was initially taken from a patch to GPM which is (C) 2001 |
10 | * by Karsten Merker <merker@linuxtag.org> |
11 | * and Maciej W. Rozycki <macro@ds2.pg.gda.pl> |
12 | * Later on, I had access to the device's documentation (referenced below). |
13 | */ |
14 | |
15 | /* |
16 | * Building an adaptor to DE9 / DB25 RS232 |
17 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
18 | * |
19 | * DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for |
20 | * anything if you break your mouse, your computer or whatever! |
21 | * |
22 | * In theory, this mouse is a simple RS232 device. In practice, it has got |
23 | * a quite uncommon plug and the requirement to additionally get a power |
24 | * supply at +5V and -12V. |
25 | * |
26 | * If you look at the socket/jack (_not_ at the plug), we use this pin |
27 | * numbering: |
28 | * _______ |
29 | * / 7 6 5 \ |
30 | * | 4 --- 3 | |
31 | * \ 2 1 / |
32 | * ------- |
33 | * |
34 | * DEC socket DE9 DB25 Note |
35 | * 1 (GND) 5 7 - |
36 | * 2 (RxD) 2 3 - |
37 | * 3 (TxD) 3 2 - |
38 | * 4 (-12V) - - Somewhere from the PSU. At ATX, it's |
39 | * the thin blue wire at pin 12 of the |
40 | * ATX power connector. Only required for |
41 | * VSXXX-AA/-GA mice. |
42 | * 5 (+5V) - - PSU (red wires of ATX power connector |
43 | * on pin 4, 6, 19 or 20) or HDD power |
44 | * connector (also red wire). |
45 | * 6 (+12V) - - HDD power connector, yellow wire. Only |
46 | * required for VSXXX-AB digitizer. |
47 | * 7 (dev. avail.) - - The mouse shorts this one to pin 1. |
48 | * This way, the host computer can detect |
49 | * the mouse. To use it with the adaptor, |
50 | * simply don't connect this pin. |
51 | * |
52 | * So to get a working adaptor, you need to connect the mouse with three |
53 | * wires to a RS232 port and two or three additional wires for +5V, +12V and |
54 | * -12V to the PSU. |
55 | * |
56 | * Flow specification for the link is 4800, 8o1. |
57 | * |
58 | * The mice and tablet are described in "VCB02 Video Subsystem - Technical |
59 | * Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine |
60 | * specific for DEC documentation. Try |
61 | * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1 |
62 | */ |
63 | |
64 | #include <linux/delay.h> |
65 | #include <linux/module.h> |
66 | #include <linux/slab.h> |
67 | #include <linux/interrupt.h> |
68 | #include <linux/input.h> |
69 | #include <linux/serio.h> |
70 | |
71 | #define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet" |
72 | |
73 | MODULE_AUTHOR("Jan-Benedict Glaw <jbglaw@lug-owl.de>" ); |
74 | MODULE_DESCRIPTION(DRIVER_DESC); |
75 | MODULE_LICENSE("GPL" ); |
76 | |
77 | #undef VSXXXAA_DEBUG |
78 | #ifdef VSXXXAA_DEBUG |
79 | #define DBG(x...) printk(x) |
80 | #else |
81 | #define DBG(x...) do {} while (0) |
82 | #endif |
83 | |
84 | #define VSXXXAA_INTRO_MASK 0x80 |
85 | #define VSXXXAA_INTRO_HEAD 0x80 |
86 | #define IS_HDR_BYTE(x) \ |
87 | (((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD) |
88 | |
89 | #define VSXXXAA_PACKET_MASK 0xe0 |
90 | #define VSXXXAA_PACKET_REL 0x80 |
91 | #define VSXXXAA_PACKET_ABS 0xc0 |
92 | #define VSXXXAA_PACKET_POR 0xa0 |
93 | #define MATCH_PACKET_TYPE(data, type) \ |
94 | (((data) & VSXXXAA_PACKET_MASK) == (type)) |
95 | |
96 | |
97 | |
98 | struct vsxxxaa { |
99 | struct input_dev *dev; |
100 | struct serio *serio; |
101 | #define BUFLEN 15 /* At least 5 is needed for a full tablet packet */ |
102 | unsigned char buf[BUFLEN]; |
103 | unsigned char count; |
104 | unsigned char version; |
105 | unsigned char country; |
106 | unsigned char type; |
107 | char name[64]; |
108 | char phys[32]; |
109 | }; |
110 | |
111 | static void vsxxxaa_drop_bytes(struct vsxxxaa *mouse, int num) |
112 | { |
113 | if (num >= mouse->count) { |
114 | mouse->count = 0; |
115 | } else { |
116 | memmove(mouse->buf, mouse->buf + num, BUFLEN - num); |
117 | mouse->count -= num; |
118 | } |
119 | } |
120 | |
121 | static void vsxxxaa_queue_byte(struct vsxxxaa *mouse, unsigned char byte) |
122 | { |
123 | if (mouse->count == BUFLEN) { |
124 | printk(KERN_ERR "%s on %s: Dropping a byte of full buffer.\n" , |
125 | mouse->name, mouse->phys); |
126 | vsxxxaa_drop_bytes(mouse, num: 1); |
127 | } |
128 | |
129 | DBG(KERN_INFO "Queueing byte 0x%02x\n" , byte); |
130 | |
131 | mouse->buf[mouse->count++] = byte; |
132 | } |
133 | |
134 | static void vsxxxaa_detection_done(struct vsxxxaa *mouse) |
135 | { |
136 | switch (mouse->type) { |
137 | case 0x02: |
138 | strscpy(p: mouse->name, q: "DEC VSXXX-AA/-GA mouse" , |
139 | size: sizeof(mouse->name)); |
140 | break; |
141 | |
142 | case 0x04: |
143 | strscpy(p: mouse->name, q: "DEC VSXXX-AB digitizer" , |
144 | size: sizeof(mouse->name)); |
145 | break; |
146 | |
147 | default: |
148 | snprintf(buf: mouse->name, size: sizeof(mouse->name), |
149 | fmt: "unknown DEC pointer device (type = 0x%02x)" , |
150 | mouse->type); |
151 | break; |
152 | } |
153 | |
154 | printk(KERN_INFO |
155 | "Found %s version 0x%02x from country 0x%02x on port %s\n" , |
156 | mouse->name, mouse->version, mouse->country, mouse->phys); |
157 | } |
158 | |
159 | /* |
160 | * Returns number of bytes to be dropped, 0 if packet is okay. |
161 | */ |
162 | static int vsxxxaa_check_packet(struct vsxxxaa *mouse, int packet_len) |
163 | { |
164 | int i; |
165 | |
166 | /* First byte must be a header byte */ |
167 | if (!IS_HDR_BYTE(mouse->buf[0])) { |
168 | DBG("vsck: len=%d, 1st=0x%02x\n" , packet_len, mouse->buf[0]); |
169 | return 1; |
170 | } |
171 | |
172 | /* Check all following bytes */ |
173 | for (i = 1; i < packet_len; i++) { |
174 | if (IS_HDR_BYTE(mouse->buf[i])) { |
175 | printk(KERN_ERR |
176 | "Need to drop %d bytes of a broken packet.\n" , |
177 | i - 1); |
178 | DBG(KERN_INFO "check: len=%d, b[%d]=0x%02x\n" , |
179 | packet_len, i, mouse->buf[i]); |
180 | return i - 1; |
181 | } |
182 | } |
183 | |
184 | return 0; |
185 | } |
186 | |
187 | static inline int vsxxxaa_smells_like_packet(struct vsxxxaa *mouse, |
188 | unsigned char type, size_t len) |
189 | { |
190 | return mouse->count >= len && MATCH_PACKET_TYPE(mouse->buf[0], type); |
191 | } |
192 | |
193 | static void vsxxxaa_handle_REL_packet(struct vsxxxaa *mouse) |
194 | { |
195 | struct input_dev *dev = mouse->dev; |
196 | unsigned char *buf = mouse->buf; |
197 | int left, middle, right; |
198 | int dx, dy; |
199 | |
200 | /* |
201 | * Check for normal stream packets. This is three bytes, |
202 | * with the first byte's 3 MSB set to 100. |
203 | * |
204 | * [0]: 1 0 0 SignX SignY Left Middle Right |
205 | * [1]: 0 dx dx dx dx dx dx dx |
206 | * [2]: 0 dy dy dy dy dy dy dy |
207 | */ |
208 | |
209 | /* |
210 | * Low 7 bit of byte 1 are abs(dx), bit 7 is |
211 | * 0, bit 4 of byte 0 is direction. |
212 | */ |
213 | dx = buf[1] & 0x7f; |
214 | dx *= ((buf[0] >> 4) & 0x01) ? 1 : -1; |
215 | |
216 | /* |
217 | * Low 7 bit of byte 2 are abs(dy), bit 7 is |
218 | * 0, bit 3 of byte 0 is direction. |
219 | */ |
220 | dy = buf[2] & 0x7f; |
221 | dy *= ((buf[0] >> 3) & 0x01) ? -1 : 1; |
222 | |
223 | /* |
224 | * Get button state. It's the low three bits |
225 | * (for three buttons) of byte 0. |
226 | */ |
227 | left = buf[0] & 0x04; |
228 | middle = buf[0] & 0x02; |
229 | right = buf[0] & 0x01; |
230 | |
231 | vsxxxaa_drop_bytes(mouse, num: 3); |
232 | |
233 | DBG(KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n" , |
234 | mouse->name, mouse->phys, dx, dy, |
235 | left ? "L" : "l" , middle ? "M" : "m" , right ? "R" : "r" ); |
236 | |
237 | /* |
238 | * Report what we've found so far... |
239 | */ |
240 | input_report_key(dev, BTN_LEFT, value: left); |
241 | input_report_key(dev, BTN_MIDDLE, value: middle); |
242 | input_report_key(dev, BTN_RIGHT, value: right); |
243 | input_report_key(dev, BTN_TOUCH, value: 0); |
244 | input_report_rel(dev, REL_X, value: dx); |
245 | input_report_rel(dev, REL_Y, value: dy); |
246 | input_sync(dev); |
247 | } |
248 | |
249 | static void vsxxxaa_handle_ABS_packet(struct vsxxxaa *mouse) |
250 | { |
251 | struct input_dev *dev = mouse->dev; |
252 | unsigned char *buf = mouse->buf; |
253 | int left, middle, right, touch; |
254 | int x, y; |
255 | |
256 | /* |
257 | * Tablet position / button packet |
258 | * |
259 | * [0]: 1 1 0 B4 B3 B2 B1 Pr |
260 | * [1]: 0 0 X5 X4 X3 X2 X1 X0 |
261 | * [2]: 0 0 X11 X10 X9 X8 X7 X6 |
262 | * [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0 |
263 | * [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6 |
264 | */ |
265 | |
266 | /* |
267 | * Get X/Y position. Y axis needs to be inverted since VSXXX-AB |
268 | * counts down->top while monitor counts top->bottom. |
269 | */ |
270 | x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f); |
271 | y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f); |
272 | y = 1023 - y; |
273 | |
274 | /* |
275 | * Get button state. It's bits <4..1> of byte 0. |
276 | */ |
277 | left = buf[0] & 0x02; |
278 | middle = buf[0] & 0x04; |
279 | right = buf[0] & 0x08; |
280 | touch = buf[0] & 0x10; |
281 | |
282 | vsxxxaa_drop_bytes(mouse, num: 5); |
283 | |
284 | DBG(KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n" , |
285 | mouse->name, mouse->phys, x, y, |
286 | left ? "L" : "l" , middle ? "M" : "m" , |
287 | right ? "R" : "r" , touch ? "T" : "t" ); |
288 | |
289 | /* |
290 | * Report what we've found so far... |
291 | */ |
292 | input_report_key(dev, BTN_LEFT, value: left); |
293 | input_report_key(dev, BTN_MIDDLE, value: middle); |
294 | input_report_key(dev, BTN_RIGHT, value: right); |
295 | input_report_key(dev, BTN_TOUCH, value: touch); |
296 | input_report_abs(dev, ABS_X, value: x); |
297 | input_report_abs(dev, ABS_Y, value: y); |
298 | input_sync(dev); |
299 | } |
300 | |
301 | static void vsxxxaa_handle_POR_packet(struct vsxxxaa *mouse) |
302 | { |
303 | struct input_dev *dev = mouse->dev; |
304 | unsigned char *buf = mouse->buf; |
305 | int left, middle, right; |
306 | unsigned char error; |
307 | |
308 | /* |
309 | * Check for Power-On-Reset packets. These are sent out |
310 | * after plugging the mouse in, or when explicitly |
311 | * requested by sending 'T'. |
312 | * |
313 | * [0]: 1 0 1 0 R3 R2 R1 R0 |
314 | * [1]: 0 M2 M1 M0 D3 D2 D1 D0 |
315 | * [2]: 0 E6 E5 E4 E3 E2 E1 E0 |
316 | * [3]: 0 0 0 0 0 Left Middle Right |
317 | * |
318 | * M: manufacturer location code |
319 | * R: revision code |
320 | * E: Error code. If it's in the range of 0x00..0x1f, only some |
321 | * minor problem occurred. Errors >= 0x20 are considered bad |
322 | * and the device may not work properly... |
323 | * D: <0010> == mouse, <0100> == tablet |
324 | */ |
325 | |
326 | mouse->version = buf[0] & 0x0f; |
327 | mouse->country = (buf[1] >> 4) & 0x07; |
328 | mouse->type = buf[1] & 0x0f; |
329 | error = buf[2] & 0x7f; |
330 | |
331 | /* |
332 | * Get button state. It's the low three bits |
333 | * (for three buttons) of byte 0. Maybe even the bit <3> |
334 | * has some meaning if a tablet is attached. |
335 | */ |
336 | left = buf[0] & 0x04; |
337 | middle = buf[0] & 0x02; |
338 | right = buf[0] & 0x01; |
339 | |
340 | vsxxxaa_drop_bytes(mouse, num: 4); |
341 | vsxxxaa_detection_done(mouse); |
342 | |
343 | if (error <= 0x1f) { |
344 | /* No (serious) error. Report buttons */ |
345 | input_report_key(dev, BTN_LEFT, value: left); |
346 | input_report_key(dev, BTN_MIDDLE, value: middle); |
347 | input_report_key(dev, BTN_RIGHT, value: right); |
348 | input_report_key(dev, BTN_TOUCH, value: 0); |
349 | input_sync(dev); |
350 | |
351 | if (error != 0) |
352 | printk(KERN_INFO "Your %s on %s reports error=0x%02x\n" , |
353 | mouse->name, mouse->phys, error); |
354 | |
355 | } |
356 | |
357 | /* |
358 | * If the mouse was hot-plugged, we need to force differential mode |
359 | * now... However, give it a second to recover from it's reset. |
360 | */ |
361 | printk(KERN_NOTICE |
362 | "%s on %s: Forcing standard packet format, " |
363 | "incremental streaming mode and 72 samples/sec\n" , |
364 | mouse->name, mouse->phys); |
365 | serio_write(serio: mouse->serio, data: 'S'); /* Standard format */ |
366 | mdelay(50); |
367 | serio_write(serio: mouse->serio, data: 'R'); /* Incremental */ |
368 | mdelay(50); |
369 | serio_write(serio: mouse->serio, data: 'L'); /* 72 samples/sec */ |
370 | } |
371 | |
372 | static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse) |
373 | { |
374 | unsigned char *buf = mouse->buf; |
375 | int stray_bytes; |
376 | |
377 | /* |
378 | * Parse buffer to death... |
379 | */ |
380 | do { |
381 | /* |
382 | * Out of sync? Throw away what we don't understand. Each |
383 | * packet starts with a byte whose bit 7 is set. Unhandled |
384 | * packets (ie. which we don't know about or simply b0rk3d |
385 | * data...) will get shifted out of the buffer after some |
386 | * activity on the mouse. |
387 | */ |
388 | while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) { |
389 | printk(KERN_ERR "%s on %s: Dropping a byte to regain " |
390 | "sync with mouse data stream...\n" , |
391 | mouse->name, mouse->phys); |
392 | vsxxxaa_drop_bytes(mouse, num: 1); |
393 | } |
394 | |
395 | /* |
396 | * Check for packets we know about. |
397 | */ |
398 | |
399 | if (vsxxxaa_smells_like_packet(mouse, VSXXXAA_PACKET_REL, len: 3)) { |
400 | /* Check for broken packet */ |
401 | stray_bytes = vsxxxaa_check_packet(mouse, packet_len: 3); |
402 | if (!stray_bytes) |
403 | vsxxxaa_handle_REL_packet(mouse); |
404 | |
405 | } else if (vsxxxaa_smells_like_packet(mouse, |
406 | VSXXXAA_PACKET_ABS, len: 5)) { |
407 | /* Check for broken packet */ |
408 | stray_bytes = vsxxxaa_check_packet(mouse, packet_len: 5); |
409 | if (!stray_bytes) |
410 | vsxxxaa_handle_ABS_packet(mouse); |
411 | |
412 | } else if (vsxxxaa_smells_like_packet(mouse, |
413 | VSXXXAA_PACKET_POR, len: 4)) { |
414 | /* Check for broken packet */ |
415 | stray_bytes = vsxxxaa_check_packet(mouse, packet_len: 4); |
416 | if (!stray_bytes) |
417 | vsxxxaa_handle_POR_packet(mouse); |
418 | |
419 | } else { |
420 | break; /* No REL, ABS or POR packet found */ |
421 | } |
422 | |
423 | if (stray_bytes > 0) { |
424 | printk(KERN_ERR "Dropping %d bytes now...\n" , |
425 | stray_bytes); |
426 | vsxxxaa_drop_bytes(mouse, num: stray_bytes); |
427 | } |
428 | |
429 | } while (1); |
430 | } |
431 | |
432 | static irqreturn_t vsxxxaa_interrupt(struct serio *serio, |
433 | unsigned char data, unsigned int flags) |
434 | { |
435 | struct vsxxxaa *mouse = serio_get_drvdata(serio); |
436 | |
437 | vsxxxaa_queue_byte(mouse, byte: data); |
438 | vsxxxaa_parse_buffer(mouse); |
439 | |
440 | return IRQ_HANDLED; |
441 | } |
442 | |
443 | static void vsxxxaa_disconnect(struct serio *serio) |
444 | { |
445 | struct vsxxxaa *mouse = serio_get_drvdata(serio); |
446 | |
447 | serio_close(serio); |
448 | serio_set_drvdata(serio, NULL); |
449 | input_unregister_device(mouse->dev); |
450 | kfree(objp: mouse); |
451 | } |
452 | |
453 | static int vsxxxaa_connect(struct serio *serio, struct serio_driver *drv) |
454 | { |
455 | struct vsxxxaa *mouse; |
456 | struct input_dev *input_dev; |
457 | int err = -ENOMEM; |
458 | |
459 | mouse = kzalloc(size: sizeof(struct vsxxxaa), GFP_KERNEL); |
460 | input_dev = input_allocate_device(); |
461 | if (!mouse || !input_dev) |
462 | goto fail1; |
463 | |
464 | mouse->dev = input_dev; |
465 | mouse->serio = serio; |
466 | strlcat(p: mouse->name, q: "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer" , |
467 | avail: sizeof(mouse->name)); |
468 | snprintf(buf: mouse->phys, size: sizeof(mouse->phys), fmt: "%s/input0" , serio->phys); |
469 | |
470 | input_dev->name = mouse->name; |
471 | input_dev->phys = mouse->phys; |
472 | input_dev->id.bustype = BUS_RS232; |
473 | input_dev->dev.parent = &serio->dev; |
474 | |
475 | __set_bit(EV_KEY, input_dev->evbit); /* We have buttons */ |
476 | __set_bit(EV_REL, input_dev->evbit); |
477 | __set_bit(EV_ABS, input_dev->evbit); |
478 | __set_bit(BTN_LEFT, input_dev->keybit); /* We have 3 buttons */ |
479 | __set_bit(BTN_MIDDLE, input_dev->keybit); |
480 | __set_bit(BTN_RIGHT, input_dev->keybit); |
481 | __set_bit(BTN_TOUCH, input_dev->keybit); /* ...and Tablet */ |
482 | __set_bit(REL_X, input_dev->relbit); |
483 | __set_bit(REL_Y, input_dev->relbit); |
484 | input_set_abs_params(dev: input_dev, ABS_X, min: 0, max: 1023, fuzz: 0, flat: 0); |
485 | input_set_abs_params(dev: input_dev, ABS_Y, min: 0, max: 1023, fuzz: 0, flat: 0); |
486 | |
487 | serio_set_drvdata(serio, data: mouse); |
488 | |
489 | err = serio_open(serio, drv); |
490 | if (err) |
491 | goto fail2; |
492 | |
493 | /* |
494 | * Request selftest. Standard packet format and differential |
495 | * mode will be requested after the device ID'ed successfully. |
496 | */ |
497 | serio_write(serio, data: 'T'); /* Test */ |
498 | |
499 | err = input_register_device(input_dev); |
500 | if (err) |
501 | goto fail3; |
502 | |
503 | return 0; |
504 | |
505 | fail3: serio_close(serio); |
506 | fail2: serio_set_drvdata(serio, NULL); |
507 | fail1: input_free_device(dev: input_dev); |
508 | kfree(objp: mouse); |
509 | return err; |
510 | } |
511 | |
512 | static struct serio_device_id vsxxaa_serio_ids[] = { |
513 | { |
514 | .type = SERIO_RS232, |
515 | .proto = SERIO_VSXXXAA, |
516 | .id = SERIO_ANY, |
517 | .extra = SERIO_ANY, |
518 | }, |
519 | { 0 } |
520 | }; |
521 | |
522 | MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids); |
523 | |
524 | static struct serio_driver vsxxxaa_drv = { |
525 | .driver = { |
526 | .name = "vsxxxaa" , |
527 | }, |
528 | .description = DRIVER_DESC, |
529 | .id_table = vsxxaa_serio_ids, |
530 | .connect = vsxxxaa_connect, |
531 | .interrupt = vsxxxaa_interrupt, |
532 | .disconnect = vsxxxaa_disconnect, |
533 | }; |
534 | |
535 | module_serio_driver(vsxxxaa_drv); |
536 | |