// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 ) .
*/
/*
* 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 + 5 V and - 12 V .
*
* 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 ( - 12 V ) - - 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 ( + 5 V ) - - PSU ( red wires of ATX power connector
* on pin 4 , 6 , 19 or 20 ) or HDD power
* connector ( also red wire ) .
* 6 ( + 12 V ) - - 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 + 5 V , + 12 V and
* - 12 V to the PSU .
*
* Flow specification for the link is 4800 , 8 o1 .
*
* The mice and tablet are described in " VCB02 Video Subsystem - Technical
* Manual " , DEC EK - 104 AA - 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/serio.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 0 x80
#define VSXXXAA_INTRO_HEAD 0 x80
#define IS_HDR_BYTE(x) \
(((x) & VSXXXAA_INTRO_MASK) == VSXXXAA_INTRO_HEAD)
#define VSXXXAA_PACKET_MASK 0 xe0
#define VSXXXAA_PACKET_REL 0 x80
#define VSXXXAA_PACKET_ABS 0 xc0
#define VSXXXAA_PACKET_POR 0 xa0
#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, 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 0 x02:
strscpy(mouse->name, "DEC VSXXX-AA/-GA mouse" ,
sizeof (mouse->name));
break ;
case 0 x04:
strscpy(mouse->name, "DEC VSXXX-AB digitizer" ,
sizeof (mouse->name));
break ;
default :
snprintf(mouse->name, sizeof (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 */
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 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 ] & 0 x7f;
dx *= ((buf[0 ] >> 4 ) & 0 x01) ? 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 ] & 0 x7f;
dy *= ((buf[0 ] >> 3 ) & 0 x01) ? -1 : 1 ;
/*
* Get button state . It ' s the low three bits
* ( for three buttons ) of byte 0 .
*/
left = buf[0 ] & 0 x04;
middle = buf[0 ] & 0 x02;
right = buf[0 ] & 0 x01;
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_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 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 ] & 0 x3f) << 6 ) | (buf[1 ] & 0 x3f);
y = ((buf[4 ] & 0 x3f) << 6 ) | (buf[3 ] & 0 x3f);
y = 1023 - y;
/*
* Get button state . It ' s bits < 4 . . 1 > of byte 0 .
*/
left = buf[0 ] & 0 x02;
middle = buf[0 ] & 0 x04;
right = buf[0 ] & 0 x08;
touch = buf[0 ] & 0 x10;
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_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 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 explicitly
* 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 0 x00 . . 0 x1f , only some
* minor problem occurred . Errors > = 0 x20 are considered bad
* and the device may not work properly . . .
* D : < 0010 > = = mouse , < 0100 > = = tablet
*/
mouse->version = buf[0 ] & 0 x0f;
mouse->country = (buf[1 ] >> 4 ) & 0 x07;
mouse->type = buf[1 ] & 0 x0f;
error = buf[2 ] & 0 x7f;
/*
* 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 ] & 0 x04;
middle = buf[0 ] & 0 x02;
right = buf[0 ] & 0 x01;
vsxxxaa_drop_bytes(mouse, 4 );
vsxxxaa_detection_done(mouse);
if (error <= 0 x1f) {
/* No (serious) error. Report buttons */
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: Forcing standard packet format, "
"incremental streaming mode and 72 samples/sec\n" ,
mouse->name, mouse->phys);
serio_write(mouse->serio, 'S' ); /* Standard format */
mdelay(50 );
serio_write(mouse->serio, 'R' ); /* Incremental */
mdelay(50 );
serio_write(mouse->serio, 'L' ); /* 72 samples/sec */
}
static void vsxxxaa_parse_buffer(struct vsxxxaa *mouse)
{
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)
vsxxxaa_handle_REL_packet(mouse);
} else if (vsxxxaa_smells_like_packet(mouse,
VSXXXAA_PACKET_ABS, 5 )) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet(mouse, 5 );
if (!stray_bytes)
vsxxxaa_handle_ABS_packet(mouse);
} else if (vsxxxaa_smells_like_packet(mouse,
VSXXXAA_PACKET_POR, 4 )) {
/* Check for broken packet */
stray_bytes = vsxxxaa_check_packet(mouse, 4 );
if (!stray_bytes)
vsxxxaa_handle_POR_packet(mouse);
} else {
break ; /* No REL, ABS or POR packet found */
}
if (stray_bytes > 0 ) {
printk(KERN_ERR "Dropping %d bytes now...\n" ,
stray_bytes);
vsxxxaa_drop_bytes(mouse, stray_bytes);
}
} while (1 );
}
static irqreturn_t vsxxxaa_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
struct vsxxxaa *mouse = serio_get_drvdata(serio);
vsxxxaa_queue_byte(mouse, data);
vsxxxaa_parse_buffer(mouse);
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 (*mouse), GFP_KERNEL);
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
mouse->dev = input_dev;
mouse->serio = serio;
strlcat(mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer" ,
sizeof (mouse->name));
snprintf(mouse->phys, sizeof (mouse->phys), "%s/input0" , serio->phys);
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
input_dev->id.bustype = BUS_RS232;
input_dev->dev.parent = &serio->dev;
__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 fail2;
/*
* Request selftest . Standard packet format and differential
* mode will be requested after the device ID ' ed successfully .
*/
serio_write(serio, 'T' ); /* Test */
err = input_register_device(input_dev);
if (err)
goto fail3;
return 0 ;
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: 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,
};
module_serio_driver(vsxxxaa_drv);
Messung V0.5 in Prozent C=94 H=95 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
¤
*© Formatika GbR, Deutschland