Commit d8a791b3 authored by Jan-Benedict Glaw's avatar Jan-Benedict Glaw Committed by Vojtech Pavlik

input: Add driver for DEC VSXXX mice.

parent fa7d2b88
......@@ -118,6 +118,19 @@ config MOUSE_RISCPC
To compile this driver as a module, choose M here: the
module will be called rpcmouse.
config MOUSE_VSXXXAA
tristate "DEC VSXXX-AA/GA mouse and tablet"
depends on INPUT && INPUT_MOUSE
select SERIO
help
Say Y (or M) if you want to use a DEC VSXXX-AA (hockey
puck) or a VSXXX-GA (rectangular) mouse. Theses mice are
typically used on DECstations or VAXstations, but can also
be used on any box capable of RS232 (with some adaptor
described in the source file). This driver should, in theory,
also work with the digitizer DEC produced, but it isn't tested
with that (I don't have the hardware yet).
config MOUSE_PC9800
tristate "NEC PC-9800 busmouse"
depends on X86_PC9800 && INPUT && INPUT_MOUSE && ISA
......
......@@ -13,5 +13,6 @@ obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
obj-$(CONFIG_MOUSE_PC9800) += 98busmouse.o
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
psmouse-objs := psmouse-base.o logips2pp.o synaptics.o
/*
* DEC VSXXX-AA and VSXXX-GA mouse driver.
*
* Copyright (C) 2003-2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
*
* The packet format was 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>
*/
/*
* 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 DB9 / 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 DB9 DB25 Note
* 1 (GND) 5 7 -
* 2 (RxD) 3 3 -
* 3 (TxD) 2 2 -
* 4 (-12V) - - Somewhere from the PSU. At ATX, it's
* the blue wire at pin 12 of the ATX
* power connector. Please note that the
* docs say this should be +12V! However,
* I measured -12V...
* 5 (+5V) - - PSU (red wire of ATX power connector
* on pin 4, 6, 19 or 20) or HDD power
* connector (also red wire)
* 6 (not conn.) - - -
* 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 additional wires for +5V and -12V to the
* PSU.
*
* Flow specification for the link is 4800, 8o1.
*/
/*
* TODO list:
* - Automatically attach to a given serial port (no need for inputattach).
*/
#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>
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
MODULE_DESCRIPTION ("Serial DEC VSXXX-AA/GA mouse / DEC tablet driver");
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 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->dev.name, mouse->dev.phys);
vsxxxaa_drop_bytes (mouse, 1);
}
mouse->buf[mouse->count++] = byte;
}
static void
vsxxxaa_report_mouse (struct vsxxxaa *mouse)
{
char *devtype;
switch (mouse->type) {
case 0x02: devtype = "DEC mouse"; break;
case 0x04: devtype = "DEC tablet"; break;
default: devtype = "unknown DEC device"; break;
}
printk (KERN_INFO "Found %s version 0x%x from country 0x%x "
"on port %s\n", devtype, mouse->version,
mouse->country, mouse->dev.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->dev.name, mouse->dev.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_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, extra;
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
*/
x = ((buf[2] & 0x3f) << 6) | (buf[1] & 0x3f);
y = ((buf[4] & 0x3f) << 6) | (buf[3] & 0x3f);
/*
* 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;
extra = (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->dev.name, mouse->dev.phys, x, y,
left? "L": "l", middle? "M": "m",
right? "R": "r", extra? "E": "e");
/*
* 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_EXTRA, extra);
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. I'm not sure about these, but gpm's sources,
* which support this mouse, too, tell about them:
* E = [0x00 .. 0x1f]: no error, byte #3 is button state
* E = 0x3d: button error, byte #3 tells which one.
* E = <else>: other error
* D: <0010> == mouse, <0100> == tablet
*
*/
mouse->version = buf[0] & 0x0f;
mouse->country = (buf[1] >> 4) & 0x07;
mouse->type = buf[1] & 0x07;
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_report_mouse (mouse);
if (error <= 0x1f) {
/* No 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_sync (dev);
} else {
printk (KERN_ERR "Your %s on %s reports an undefined error, "
"please check it...\n", mouse->dev.name,
mouse->dev.phys);
}
/*
* If the mouse was hot-plugged, we need to
* force differential mode now...
*/
printk (KERN_NOTICE "%s on %s: Forceing standard packet format and "
"streaming mode\n", mouse->dev.name, mouse->dev.phys);
mouse->serio->write (mouse->serio, 'S');
mouse->serio->write (mouse->serio, 'R');
}
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->dev.name, mouse->dev.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->private;
vsxxxaa_queue_byte (mouse, data);
vsxxxaa_parse_buffer (mouse, regs);
return IRQ_HANDLED;
}
static void
vsxxxaa_disconnect (struct serio *serio)
{
struct vsxxxaa *mouse = serio->private;
input_unregister_device (&mouse->dev);
serio_close (serio);
kfree (mouse);
}
static void
vsxxxaa_connect (struct serio *serio, struct serio_dev *dev)
{
struct vsxxxaa *mouse;
if ((serio->type & SERIO_TYPE) != SERIO_RS232)
return;
if ((serio->type & SERIO_PROTO) != SERIO_VSXXXAA)
return;
if (!(mouse = kmalloc (sizeof (struct vsxxxaa), GFP_KERNEL)))
return;
memset (mouse, 0, sizeof (struct vsxxxaa));
init_input_dev (&mouse->dev);
set_bit (EV_KEY, mouse->dev.evbit); /* We have buttons */
set_bit (EV_REL, mouse->dev.evbit); /* We can move */
set_bit (BTN_LEFT, mouse->dev.keybit); /* We have 3 buttons */
set_bit (BTN_MIDDLE, mouse->dev.keybit);
set_bit (BTN_RIGHT, mouse->dev.keybit);
set_bit (BTN_EXTRA, mouse->dev.keybit); /* ...and Tablet */
set_bit (REL_X, mouse->dev.relbit); /* We can move in */
set_bit (REL_Y, mouse->dev.relbit); /* two dimensions */
set_bit (ABS_X, mouse->dev.absbit); /* DEC tablet support */
set_bit (ABS_Y, mouse->dev.absbit);
mouse->dev.absmin[ABS_X] = 0;
mouse->dev.absmax[ABS_X] = 1023;
mouse->dev.absmin[ABS_Y] = 0;
mouse->dev.absmax[ABS_Y] = 1023;
mouse->dev.private = mouse;
serio->private = mouse;
sprintf (mouse->phys, "%s/input0", serio->phys);
mouse->dev.phys = mouse->phys;
mouse->dev.name = "DEC VSXXX-AA/GA mouse or DEC tablet";
mouse->dev.id.bustype = BUS_RS232;
mouse->serio = serio;
if (serio_open (serio, dev)) {
kfree (mouse);
return;
}
/*
* Request selftest and differential stream mode.
*/
mouse->serio->write (mouse->serio, 'T'); /* Test */
mouse->serio->write (mouse->serio, 'R'); /* Differential stream */
input_register_device (&mouse->dev);
printk (KERN_INFO "input: %s on %s\n", mouse->dev.name, serio->phys);
}
static struct serio_dev vsxxxaa_dev = {
.interrupt = vsxxxaa_interrupt,
.connect = vsxxxaa_connect,
.disconnect = vsxxxaa_disconnect
};
int __init
vsxxxaa_init (void)
{
serio_register_device (&vsxxxaa_dev);
return 0;
}
void __exit
vsxxxaa_exit (void)
{
serio_unregister_device (&vsxxxaa_dev);
}
module_init (vsxxxaa_init);
module_exit (vsxxxaa_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment