blob: 36e9442a16b296c22cb19853e71f37e18578d3eb [file] [log] [blame]
/*
* Driver for DEC VSXXX-AA mouse (hockey-puck mouse, ball or two rollers)
* DEC VSXXX-GA mouse (rectangular mouse, with ball)
* DEC VSXXX-AB tablet (digitizer with hair cross or stylus)
*
* Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*
* The packet format was initially taken from a patch to GPM which is (C) 2001
* by Karsten Merker <merker@linuxtag.org>
* and Maciej W. Rozycki <macro@ds2.pg.gda.pl>
* Later on, I had access to the device's documentation (referenced below).
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Building an adaptor to DE9 / DB25 RS232
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* DISCLAIMER: Use this description AT YOUR OWN RISK! I'll not pay for
* anything if you break your mouse, your computer or whatever!
*
* In theory, this mouse is a simple RS232 device. In practice, it has got
* a quite uncommon plug and the requirement to additionally get a power
* supply at +5V and -12V.
*
* If you look at the socket/jack (_not_ at the plug), we use this pin
* numbering:
* _______
* / 7 6 5 \
* | 4 --- 3 |
* \ 2 1 /
* -------
*
* DEC socket DE9 DB25 Note
* 1 (GND) 5 7 -
* 2 (RxD) 2 3 -
* 3 (TxD) 3 2 -
* 4 (-12V) - - Somewhere from the PSU. At ATX, it's
* the thin blue wire at pin 12 of the
* ATX power connector. Only required for
* VSXXX-AA/-GA mice.
* 5 (+5V) - - PSU (red wires of ATX power connector
* on pin 4, 6, 19 or 20) or HDD power
* connector (also red wire).
* 6 (+12V) - - HDD power connector, yellow wire. Only
* required for VSXXX-AB digitizer.
* 7 (dev. avail.) - - The mouse shorts this one to pin 1.
* This way, the host computer can detect
* the mouse. To use it with the adaptor,
* simply don't connect this pin.
*
* So to get a working adaptor, you need to connect the mouse with three
* wires to a RS232 port and two or three additional wires for +5V, +12V and
* -12V to the PSU.
*
* Flow specification for the link is 4800, 8o1.
*
* The mice and tablet are described in "VCB02 Video Subsystem - Technical
* Manual", DEC EK-104AA-TM-001. You'll find it at MANX, a search engine
* specific for DEC documentation. Try
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/config.h>
#include <linux/serio.h>
#include <linux/init.h>
#define DRIVER_DESC "Driver for DEC VSXXX-AA and -GA mice and VSXXX-AB tablet"
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION (DRIVER_DESC);
MODULE_LICENSE ("GPL");
#undef VSXXXAA_DEBUG
#ifdef VSXXXAA_DEBUG
#define DBG(x...) printk (x)
#else
#define DBG(x...) do {} while (0)
#endif
#define VSXXXAA_INTRO_MASK 0x80
#define VSXXXAA_INTRO_HEAD 0x80
#define IS_HDR_BYTE(x) (((x) & VSXXXAA_INTRO_MASK) \
== VSXXXAA_INTRO_HEAD)
#define VSXXXAA_PACKET_MASK 0xe0
#define VSXXXAA_PACKET_REL 0x80
#define VSXXXAA_PACKET_ABS 0xc0
#define VSXXXAA_PACKET_POR 0xa0
#define MATCH_PACKET_TYPE(data, type) (((data) & VSXXXAA_PACKET_MASK) == (type))
struct vsxxxaa {
struct input_dev *dev;
struct serio *serio;
#define BUFLEN 15 /* At least 5 is needed for a full tablet packet */
unsigned char buf[BUFLEN];
unsigned char count;
unsigned char version;
unsigned char country;
unsigned char type;
char name[64];
char phys[32];
};
static void
vsxxxaa_drop_bytes (struct vsxxxaa *mouse, int num)
{
if (num >= mouse->count)
mouse->count = 0;
else {
memmove (mouse->buf, mouse->buf + num - 1, BUFLEN - num);
mouse->count -= num;
}
}
static void
vsxxxaa_queue_byte (struct vsxxxaa *mouse, unsigned char byte)
{
if (mouse->count == BUFLEN) {
printk (KERN_ERR "%s on %s: Dropping a byte of full buffer.\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes (mouse, 1);
}
DBG (KERN_INFO "Queueing byte 0x%02x\n", byte);
mouse->buf[mouse->count++] = byte;
}
static void
vsxxxaa_detection_done (struct vsxxxaa *mouse)
{
switch (mouse->type) {
case 0x02:
sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse");
break;
case 0x04:
sprintf (mouse->name, "DEC VSXXX-AB digitizer");
break;
default:
sprintf (mouse->name, "unknown DEC pointer device "
"(type = 0x%02x)", mouse->type);
break;
}
printk (KERN_INFO "Found %s version 0x%02x from country 0x%02x "
"on port %s\n", mouse->name, mouse->version,
mouse->country, mouse->phys);
}
/*
* Returns number of bytes to be dropped, 0 if packet is okay.
*/
static int
vsxxxaa_check_packet (struct vsxxxaa *mouse, int packet_len)
{
int i;
/* First byte must be a header byte */
if (!IS_HDR_BYTE (mouse->buf[0])) {
DBG ("vsck: len=%d, 1st=0x%02x\n", packet_len, mouse->buf[0]);
return 1;
}
/* Check all following bytes */
if (packet_len > 1) {
for (i = 1; i < packet_len; i++) {
if (IS_HDR_BYTE (mouse->buf[i])) {
printk (KERN_ERR "Need to drop %d bytes "
"of a broken packet.\n",
i - 1);
DBG (KERN_INFO "check: len=%d, b[%d]=0x%02x\n",
packet_len, i, mouse->buf[i]);
return i - 1;
}
}
}
return 0;
}
static __inline__ int
vsxxxaa_smells_like_packet (struct vsxxxaa *mouse, unsigned char type, size_t len)
{
return (mouse->count >= len) && MATCH_PACKET_TYPE (mouse->buf[0], type);
}
static void
vsxxxaa_handle_REL_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
int dx, dy;
/*
* Check for normal stream packets. This is three bytes,
* with the first byte's 3 MSB set to 100.
*
* [0]: 1 0 0 SignX SignY Left Middle Right
* [1]: 0 dx dx dx dx dx dx dx
* [2]: 0 dy dy dy dy dy dy dy
*/
/*
* Low 7 bit of byte 1 are abs(dx), bit 7 is
* 0, bit 4 of byte 0 is direction.
*/
dx = buf[1] & 0x7f;
dx *= ((buf[0] >> 4) & 0x01)? 1: -1;
/*
* Low 7 bit of byte 2 are abs(dy), bit 7 is
* 0, bit 3 of byte 0 is direction.
*/
dy = buf[2] & 0x7f;
dy *= ((buf[0] >> 3) & 0x01)? -1: 1;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0.
*/
left = (buf[0] & 0x04)? 1: 0;
middle = (buf[0] & 0x02)? 1: 0;
right = (buf[0] & 0x01)? 1: 0;
vsxxxaa_drop_bytes (mouse, 3);
DBG (KERN_INFO "%s on %s: dx=%d, dy=%d, buttons=%s%s%s\n",
mouse->name, mouse->phys, dx, dy,
left? "L": "l", middle? "M": "m", right? "R": "r");
/*
* Report what we've found so far...
*/
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, 0);
input_report_rel (dev, REL_X, dx);
input_report_rel (dev, REL_Y, dy);
input_sync (dev);
}
static void
vsxxxaa_handle_ABS_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right, touch;
int x, y;
/*
* Tablet position / button packet
*
* [0]: 1 1 0 B4 B3 B2 B1 Pr
* [1]: 0 0 X5 X4 X3 X2 X1 X0
* [2]: 0 0 X11 X10 X9 X8 X7 X6
* [3]: 0 0 Y5 Y4 Y3 Y2 Y1 Y0
* [4]: 0 0 Y11 Y10 Y9 Y8 Y7 Y6
*/
/*
* Get X/Y position. Y axis needs to be inverted since VSXXX-AB
* counts down->top while monitor counts top->bottom.
*/
x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
y = 1023 - y;
/*
* Get button state. It's bits <4..1> of byte 0.
*/
left = (buf[0] & 0x02)? 1: 0;
middle = (buf[0] & 0x04)? 1: 0;
right = (buf[0] & 0x08)? 1: 0;
touch = (buf[0] & 0x10)? 1: 0;
vsxxxaa_drop_bytes (mouse, 5);
DBG (KERN_INFO "%s on %s: x=%d, y=%d, buttons=%s%s%s%s\n",
mouse->name, mouse->phys, x, y,
left? "L": "l", middle? "M": "m",
right? "R": "r", touch? "T": "t");
/*
* Report what we've found so far...
*/
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, touch);
input_report_abs (dev, ABS_X, x);
input_report_abs (dev, ABS_Y, y);
input_sync (dev);
}
static void
vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse, struct pt_regs *regs)
{
struct input_dev *dev = mouse->dev;
unsigned char *buf = mouse->buf;
int left, middle, right;
unsigned char error;
/*
* Check for Power-On-Reset packets. These are sent out
* after plugging the mouse in, or when explicitely
* requested by sending 'T'.
*
* [0]: 1 0 1 0 R3 R2 R1 R0
* [1]: 0 M2 M1 M0 D3 D2 D1 D0
* [2]: 0 E6 E5 E4 E3 E2 E1 E0
* [3]: 0 0 0 0 0 Left Middle Right
*
* M: manufacturer location code
* R: revision code
* E: Error code. If it's in the range of 0x00..0x1f, only some
* minor problem occured. Errors >= 0x20 are considered bad
* and the device may not work properly...
* D: <0010> == mouse, <0100> == tablet
*/
mouse->version = buf[0] & 0x0f;
mouse->country = (buf[1] >> 4) & 0x07;
mouse->type = buf[1] & 0x0f;
error = buf[2] & 0x7f;
/*
* Get button state. It's the low three bits
* (for three buttons) of byte 0. Maybe even the bit <3>
* has some meaning if a tablet is attached.
*/
left = (buf[0] & 0x04)? 1: 0;
middle = (buf[0] & 0x02)? 1: 0;
right = (buf[0] & 0x01)? 1: 0;
vsxxxaa_drop_bytes (mouse, 4);
vsxxxaa_detection_done (mouse);
if (error <= 0x1f) {
/* No (serious) error. Report buttons */
input_regs (dev, regs);
input_report_key (dev, BTN_LEFT, left);
input_report_key (dev, BTN_MIDDLE, middle);
input_report_key (dev, BTN_RIGHT, right);
input_report_key (dev, BTN_TOUCH, 0);
input_sync (dev);
if (error != 0)
printk (KERN_INFO "Your %s on %s reports error=0x%02x\n",
mouse->name, mouse->phys, error);
}
/*
* If the mouse was hot-plugged, we need to force differential mode
* now... However, give it a second to recover from it's reset.
*/
printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
"incremental streaming mode and 72 samples/sec\n",
mouse->name, mouse->phys);
mouse->serio->write (mouse->serio, 'S'); /* Standard format */
mdelay (50);
mouse->serio->write (mouse->serio, 'R'); /* Incremental */
mdelay (50);
mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */
}
static void
vsxxxaa_parse_buffer (struct vsxxxaa *mouse, struct pt_regs *regs)
{
unsigned char *buf = mouse->buf;
int stray_bytes;
/*
* Parse buffer to death...
*/
do {
/*
* Out of sync? Throw away what we don't understand. Each
* packet starts with a byte whose bit 7 is set. Unhandled
* packets (ie. which we don't know about or simply b0rk3d
* data...) will get shifted out of the buffer after some
* activity on the mouse.
*/
while (mouse->count > 0 && !IS_HDR_BYTE(buf[0])) {
printk (KERN_ERR "%s on %s: Dropping a byte to regain "
"sync with mouse data stream...\n",
mouse->name, mouse->phys);
vsxxxaa_drop_bytes (mouse, 1);
}
/*
* Check for packets we know about.
*/
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_REL, 3)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 3);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_REL_packet (mouse, regs);
continue; /* More to parse? */
}
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_ABS, 5)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 5);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_ABS_packet (mouse, regs);
continue; /* More to parse? */
}
if (vsxxxaa_smells_like_packet (mouse, VSXXXAA_PACKET_POR, 4)) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet (mouse, 4);
if (stray_bytes > 0) {
printk (KERN_ERR "Dropping %d bytes now...\n",
stray_bytes);
vsxxxaa_drop_bytes (mouse, stray_bytes);
continue;
}
vsxxxaa_handle_POR_packet (mouse, regs);
continue; /* More to parse? */
}
break; /* No REL, ABS or POR packet found */
} while (1);
}
static irqreturn_t
vsxxxaa_interrupt (struct serio *serio, unsigned char data, unsigned int flags,
struct pt_regs *regs)
{
struct vsxxxaa *mouse = serio_get_drvdata (serio);
vsxxxaa_queue_byte (mouse, data);
vsxxxaa_parse_buffer (mouse, regs);
return IRQ_HANDLED;
}
static void
vsxxxaa_disconnect (struct serio *serio)
{
struct vsxxxaa *mouse = serio_get_drvdata (serio);
serio_close (serio);
serio_set_drvdata (serio, NULL);
input_unregister_device (mouse->dev);
kfree (mouse);
}
static int
vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
{
struct vsxxxaa *mouse;
struct input_dev *input_dev;
int err = -ENOMEM;
mouse = kzalloc (sizeof (struct vsxxxaa), GFP_KERNEL);
input_dev = input_allocate_device ();
if (!mouse || !input_dev)
goto fail;
mouse->dev = input_dev;
mouse->serio = serio;
sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer");
sprintf (mouse->phys, "%s/input0", serio->phys);
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->cdev.dev = &serio->dev;
input_dev->private = mouse;
set_bit (EV_KEY, input_dev->evbit); /* We have buttons */
set_bit (EV_REL, input_dev->evbit);
set_bit (EV_ABS, input_dev->evbit);
set_bit (BTN_LEFT, input_dev->keybit); /* We have 3 buttons */
set_bit (BTN_MIDDLE, input_dev->keybit);
set_bit (BTN_RIGHT, input_dev->keybit);
set_bit (BTN_TOUCH, input_dev->keybit); /* ...and Tablet */
set_bit (REL_X, input_dev->relbit);
set_bit (REL_Y, input_dev->relbit);
input_set_abs_params (input_dev, ABS_X, 0, 1023, 0, 0);
input_set_abs_params (input_dev, ABS_Y, 0, 1023, 0, 0);
serio_set_drvdata (serio, mouse);
err = serio_open (serio, drv);
if (err)
goto fail;
/*
* Request selftest. Standard packet format and differential
* mode will be requested after the device ID'ed successfully.
*/
serio->write (serio, 'T'); /* Test */
input_register_device (input_dev);
return 0;
fail: serio_set_drvdata (serio, NULL);
input_free_device (input_dev);
kfree (mouse);
return err;
}
static struct serio_device_id vsxxaa_serio_ids[] = {
{
.type = SERIO_RS232,
.proto = SERIO_VSXXXAA,
.id = SERIO_ANY,
.extra = SERIO_ANY,
},
{ 0 }
};
MODULE_DEVICE_TABLE(serio, vsxxaa_serio_ids);
static struct serio_driver vsxxxaa_drv = {
.driver = {
.name = "vsxxxaa",
},
.description = DRIVER_DESC,
.id_table = vsxxaa_serio_ids,
.connect = vsxxxaa_connect,
.interrupt = vsxxxaa_interrupt,
.disconnect = vsxxxaa_disconnect,
};
static int __init
vsxxxaa_init (void)
{
serio_register_driver(&vsxxxaa_drv);
return 0;
}
static void __exit
vsxxxaa_exit (void)
{
serio_unregister_driver(&vsxxxaa_drv);
}
module_init (vsxxxaa_init);
module_exit (vsxxxaa_exit);