1031 lines
22 KiB
C
1031 lines
22 KiB
C
/*
|
|
* Copyright © 2001 Keith Packard
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
* documentation for any purpose is hereby granted without fee, provided that
|
|
* the above copyright notice appear in all copies and that both that
|
|
* copyright notice and this permission notice appear in supporting
|
|
* documentation, and that the name of Keith Packard not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Keith Packard makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
|
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <kdrive-config.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <termios.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xpoll.h>
|
|
#include "inputstr.h"
|
|
#include "scrnintstr.h"
|
|
#include "kdrive.h"
|
|
|
|
#undef DEBUG
|
|
#undef DEBUG_BYTES
|
|
#define KBUFIO_SIZE 256
|
|
#define MOUSE_TIMEOUT 100
|
|
|
|
typedef struct _kbufio {
|
|
int fd;
|
|
unsigned char buf[KBUFIO_SIZE];
|
|
int avail;
|
|
int used;
|
|
} Kbufio;
|
|
|
|
static Bool
|
|
MouseWaitForReadable (int fd, int timeout)
|
|
{
|
|
fd_set set;
|
|
struct timeval tv, *tp;
|
|
int n;
|
|
CARD32 done;
|
|
|
|
done = GetTimeInMillis () + timeout;
|
|
for (;;)
|
|
{
|
|
FD_ZERO (&set);
|
|
FD_SET (fd, &set);
|
|
if (timeout == -1)
|
|
tp = 0;
|
|
else
|
|
{
|
|
tv.tv_sec = timeout / 1000;
|
|
tv.tv_usec = (timeout % 1000) * 1000;
|
|
tp = &tv;
|
|
}
|
|
n = select (fd + 1, &set, 0, 0, tp);
|
|
if (n > 0)
|
|
return TRUE;
|
|
if (n < 0 && (errno == EAGAIN || errno == EINTR))
|
|
{
|
|
timeout = (int) (done - GetTimeInMillis ());
|
|
if (timeout > 0)
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
MouseReadByte (Kbufio *b, int timeout)
|
|
{
|
|
int n;
|
|
if (b->avail <= b->used)
|
|
{
|
|
if (timeout && !MouseWaitForReadable (b->fd, timeout))
|
|
{
|
|
#ifdef DEBUG_BYTES
|
|
ErrorF ("\tTimeout %d\n", timeout);
|
|
#endif
|
|
return -1;
|
|
}
|
|
n = read (b->fd, b->buf, KBUFIO_SIZE);
|
|
if (n <= 0)
|
|
return -1;
|
|
b->avail = n;
|
|
b->used = 0;
|
|
}
|
|
#ifdef DEBUG_BYTES
|
|
ErrorF ("\tget %02x\n", b->buf[b->used]);
|
|
#endif
|
|
return b->buf[b->used++];
|
|
}
|
|
|
|
#if NOTUSED
|
|
static int
|
|
MouseFlush (Kbufio *b, char *buf, int size)
|
|
{
|
|
CARD32 now = GetTimeInMillis ();
|
|
CARD32 done = now + 100;
|
|
int c;
|
|
int n = 0;
|
|
|
|
while ((c = MouseReadByte (b, done - now)) != -1)
|
|
{
|
|
if (buf)
|
|
{
|
|
if (n == size)
|
|
{
|
|
memmove (buf, buf + 1, size - 1);
|
|
n--;
|
|
}
|
|
buf[n++] = c;
|
|
}
|
|
now = GetTimeInMillis ();
|
|
if ((INT32) (now - done) >= 0)
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
MousePeekByte (Kbufio *b, int timeout)
|
|
{
|
|
int c;
|
|
|
|
c = MouseReadByte (b, timeout);
|
|
if (c != -1)
|
|
--b->used;
|
|
return c;
|
|
}
|
|
#endif /* NOTUSED */
|
|
|
|
static Bool
|
|
MouseWaitForWritable (int fd, int timeout)
|
|
{
|
|
fd_set set;
|
|
struct timeval tv, *tp;
|
|
int n;
|
|
|
|
FD_ZERO (&set);
|
|
FD_SET (fd, &set);
|
|
if (timeout == -1)
|
|
tp = 0;
|
|
else
|
|
{
|
|
tv.tv_sec = timeout / 1000;
|
|
tv.tv_usec = (timeout % 1000) * 1000;
|
|
tp = &tv;
|
|
}
|
|
n = select (fd + 1, 0, &set, 0, tp);
|
|
if (n > 0)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
MouseWriteByte (int fd, unsigned char c, int timeout)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef DEBUG_BYTES
|
|
ErrorF ("\tput %02x\n", c);
|
|
#endif
|
|
for (;;)
|
|
{
|
|
ret = write (fd, &c, 1);
|
|
if (ret == 1)
|
|
return TRUE;
|
|
if (ret == 0)
|
|
return FALSE;
|
|
if (errno != EWOULDBLOCK)
|
|
return FALSE;
|
|
if (!MouseWaitForWritable (fd, timeout))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static Bool
|
|
MouseWriteBytes (int fd, unsigned char *c, int n, int timeout)
|
|
{
|
|
while (n--)
|
|
if (!MouseWriteByte (fd, *c++, timeout))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
#define MAX_MOUSE 10 /* maximum length of mouse protocol */
|
|
#define MAX_SKIP 16 /* number of error bytes before switching */
|
|
#define MAX_VALID 4 /* number of valid packets before accepting */
|
|
|
|
typedef struct _kmouseProt {
|
|
char *name;
|
|
Bool (*Complete) (KdPointerInfo *pi, unsigned char *ev, int ne);
|
|
int (*Valid) (KdPointerInfo *pi, unsigned char *ev, int ne);
|
|
Bool (*Parse) (KdPointerInfo *pi, unsigned char *ev, int ne);
|
|
Bool (*Init) (KdPointerInfo *pi);
|
|
unsigned char headerMask, headerValid;
|
|
unsigned char dataMask, dataValid;
|
|
Bool tty;
|
|
unsigned int c_iflag;
|
|
unsigned int c_oflag;
|
|
unsigned int c_lflag;
|
|
unsigned int c_cflag;
|
|
unsigned int speed;
|
|
unsigned char *init;
|
|
unsigned long state;
|
|
} KmouseProt;
|
|
|
|
typedef enum _kmouseStage {
|
|
MouseBroken, MouseTesting, MouseWorking
|
|
} KmouseStage;
|
|
|
|
typedef struct _kmouse {
|
|
Kbufio iob;
|
|
const KmouseProt *prot;
|
|
int i_prot;
|
|
KmouseStage stage; /* protocol verification stage */
|
|
Bool tty; /* mouse device is a tty */
|
|
int valid; /* sequential valid events */
|
|
int tested; /* bytes scanned during Testing phase */
|
|
int invalid;/* total invalid bytes for this protocol */
|
|
unsigned long state; /* private per protocol, init to prot->state */
|
|
} Kmouse;
|
|
|
|
static int mouseValid (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
const KmouseProt *prot = km->prot;
|
|
int i;
|
|
|
|
for (i = 0; i < ne; i++)
|
|
if ((ev[i] & prot->headerMask) == prot->headerValid)
|
|
break;
|
|
if (i != 0)
|
|
return i;
|
|
for (i = 1; i < ne; i++)
|
|
if ((ev[i] & prot->dataMask) != prot->dataValid)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static Bool threeComplete (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
return ne == 3;
|
|
}
|
|
|
|
static Bool fourComplete (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
return ne == 4;
|
|
}
|
|
|
|
static Bool fiveComplete (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
return ne == 5;
|
|
}
|
|
|
|
static Bool MouseReasonable (KdPointerInfo *pi, unsigned long flags, int dx, int dy)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
|
|
if (km->stage == MouseWorking)
|
|
return TRUE;
|
|
if (dx < -50 || dx > 50)
|
|
{
|
|
#ifdef DEBUG
|
|
ErrorF ("Large X %d\n", dx);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
if (dy < -50 || dy > 50)
|
|
{
|
|
#ifdef DEBUG
|
|
ErrorF ("Large Y %d\n", dy);
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Standard PS/2 mouse protocol
|
|
*/
|
|
static Bool ps2Parse (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int dx, dy, dz;
|
|
unsigned long flags;
|
|
unsigned long flagsrelease = 0;
|
|
|
|
flags = KD_MOUSE_DELTA;
|
|
if (ev[0] & 4)
|
|
flags |= KD_BUTTON_2;
|
|
if (ev[0] & 2)
|
|
flags |= KD_BUTTON_3;
|
|
if (ev[0] & 1)
|
|
flags |= KD_BUTTON_1;
|
|
|
|
if (ne > 3)
|
|
{
|
|
dz = (int) (signed char) ev[3];
|
|
if (dz < 0)
|
|
{
|
|
flags |= KD_BUTTON_4;
|
|
flagsrelease = KD_BUTTON_4;
|
|
}
|
|
else if (dz > 0)
|
|
{
|
|
flags |= KD_BUTTON_5;
|
|
flagsrelease = KD_BUTTON_5;
|
|
}
|
|
}
|
|
|
|
dx = ev[1];
|
|
if (ev[0] & 0x10)
|
|
dx -= 256;
|
|
dy = ev[2];
|
|
if (ev[0] & 0x20)
|
|
dy -= 256;
|
|
dy = -dy;
|
|
if (!MouseReasonable (pi, flags, dx, dy))
|
|
return FALSE;
|
|
if (km->stage == MouseWorking)
|
|
{
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
if (flagsrelease)
|
|
{
|
|
flags &= ~flagsrelease;
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool ps2Init (KdPointerInfo *pi);
|
|
|
|
static const KmouseProt ps2Prot = {
|
|
"ps/2",
|
|
threeComplete, mouseValid, ps2Parse, ps2Init,
|
|
0x08, 0x08, 0x00, 0x00,
|
|
FALSE
|
|
};
|
|
|
|
static const KmouseProt imps2Prot = {
|
|
"imps/2",
|
|
fourComplete, mouseValid, ps2Parse, ps2Init,
|
|
0x08, 0x08, 0x00, 0x00,
|
|
FALSE
|
|
};
|
|
|
|
static const KmouseProt exps2Prot = {
|
|
"exps/2",
|
|
fourComplete, mouseValid, ps2Parse, ps2Init,
|
|
0x08, 0x08, 0x00, 0x00,
|
|
FALSE
|
|
};
|
|
|
|
/*
|
|
* Once the mouse is known to speak ps/2 protocol, go and find out
|
|
* what advanced capabilities it has and turn them on
|
|
*/
|
|
|
|
/* these extracted from FreeBSD 4.3 sys/dev/kbd/atkbdcreg.h */
|
|
|
|
/* aux device commands (sent to KBD_DATA_PORT) */
|
|
#define PSMC_SET_SCALING11 0x00e6
|
|
#define PSMC_SET_SCALING21 0x00e7
|
|
#define PSMC_SET_RESOLUTION 0x00e8
|
|
#define PSMC_SEND_DEV_STATUS 0x00e9
|
|
#define PSMC_SET_STREAM_MODE 0x00ea
|
|
#define PSMC_SEND_DEV_DATA 0x00eb
|
|
#define PSMC_SET_REMOTE_MODE 0x00f0
|
|
#define PSMC_SEND_DEV_ID 0x00f2
|
|
#define PSMC_SET_SAMPLING_RATE 0x00f3
|
|
#define PSMC_ENABLE_DEV 0x00f4
|
|
#define PSMC_DISABLE_DEV 0x00f5
|
|
#define PSMC_SET_DEFAULTS 0x00f6
|
|
#define PSMC_RESET_DEV 0x00ff
|
|
|
|
/* PSMC_SET_RESOLUTION argument */
|
|
#define PSMD_RES_LOW 0 /* typically 25ppi */
|
|
#define PSMD_RES_MEDIUM_LOW 1 /* typically 50ppi */
|
|
#define PSMD_RES_MEDIUM_HIGH 2 /* typically 100ppi (default) */
|
|
#define PSMD_RES_HIGH 3 /* typically 200ppi */
|
|
#define PSMD_MAX_RESOLUTION PSMD_RES_HIGH
|
|
|
|
/* PSMC_SET_SAMPLING_RATE */
|
|
#define PSMD_MAX_RATE 255 /* FIXME: not sure if it's possible */
|
|
|
|
/* aux device ID */
|
|
#define PSM_MOUSE_ID 0
|
|
#define PSM_BALLPOINT_ID 2
|
|
#define PSM_INTELLI_ID 3
|
|
#define PSM_EXPLORER_ID 4
|
|
#define PSM_4DMOUSE_ID 6
|
|
#define PSM_4DPLUS_ID 8
|
|
|
|
static unsigned char ps2_init[] = {
|
|
PSMC_ENABLE_DEV,
|
|
0,
|
|
};
|
|
|
|
#define NINIT_PS2 1
|
|
|
|
static unsigned char wheel_3button_init[] = {
|
|
PSMC_SET_SAMPLING_RATE, 200,
|
|
PSMC_SET_SAMPLING_RATE, 100,
|
|
PSMC_SET_SAMPLING_RATE, 80,
|
|
PSMC_SEND_DEV_ID,
|
|
0,
|
|
};
|
|
|
|
#define NINIT_IMPS2 4
|
|
|
|
static unsigned char wheel_5button_init[] = {
|
|
PSMC_SET_SAMPLING_RATE, 200,
|
|
PSMC_SET_SAMPLING_RATE, 100,
|
|
PSMC_SET_SAMPLING_RATE, 80,
|
|
PSMC_SET_SAMPLING_RATE, 200,
|
|
PSMC_SET_SAMPLING_RATE, 200,
|
|
PSMC_SET_SAMPLING_RATE, 80,
|
|
PSMC_SEND_DEV_ID,
|
|
0
|
|
};
|
|
|
|
#define NINIT_EXPS2 7
|
|
|
|
static unsigned char intelli_init[] = {
|
|
PSMC_SET_SAMPLING_RATE, 200,
|
|
PSMC_SET_SAMPLING_RATE, 100,
|
|
PSMC_SET_SAMPLING_RATE, 80,
|
|
0
|
|
};
|
|
|
|
#define NINIT_INTELLI 3
|
|
|
|
static int
|
|
ps2SkipInit (KdPointerInfo *pi, int ninit, Bool ret_next)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int c = -1;
|
|
int skipping;
|
|
Bool waiting;
|
|
|
|
skipping = 0;
|
|
waiting = FALSE;
|
|
while (ninit || ret_next)
|
|
{
|
|
c = MouseReadByte (&km->iob, MOUSE_TIMEOUT);
|
|
if (c == -1)
|
|
break;
|
|
/* look for ACK */
|
|
if (c == 0xfa)
|
|
{
|
|
ninit--;
|
|
if (ret_next)
|
|
waiting = TRUE;
|
|
}
|
|
/* look for packet start -- not the response */
|
|
else if ((c & 0x08) == 0x08)
|
|
waiting = FALSE;
|
|
else if (waiting)
|
|
break;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static Bool
|
|
ps2Init (KdPointerInfo *pi)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int skipping;
|
|
Bool waiting;
|
|
int id;
|
|
unsigned char *init;
|
|
int ninit;
|
|
|
|
/* Send Intellimouse initialization sequence */
|
|
MouseWriteBytes (km->iob.fd, intelli_init, strlen ((char *) intelli_init), 100);
|
|
/*
|
|
* Send ID command
|
|
*/
|
|
if (!MouseWriteByte (km->iob.fd, PSMC_SEND_DEV_ID, 100))
|
|
return FALSE;
|
|
skipping = 0;
|
|
waiting = FALSE;
|
|
id = ps2SkipInit (pi, 0, TRUE);
|
|
switch (id) {
|
|
case 3:
|
|
init = wheel_3button_init;
|
|
ninit = NINIT_IMPS2;
|
|
km->prot = &imps2Prot;
|
|
break;
|
|
case 4:
|
|
init = wheel_5button_init;
|
|
ninit = NINIT_EXPS2;
|
|
km->prot = &exps2Prot;
|
|
break;
|
|
default:
|
|
init = ps2_init;
|
|
ninit = NINIT_PS2;
|
|
km->prot = &ps2Prot;
|
|
break;
|
|
}
|
|
if (init)
|
|
MouseWriteBytes (km->iob.fd, init, strlen ((char *) init), 100);
|
|
/*
|
|
* Flush out the available data to eliminate responses to the
|
|
* initialization string. Make sure any partial event is
|
|
* skipped
|
|
*/
|
|
(void) ps2SkipInit (pi, ninit, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
static Bool busParse (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int dx, dy;
|
|
unsigned long flags;
|
|
|
|
flags = KD_MOUSE_DELTA;
|
|
dx = (signed char) ev[1];
|
|
dy = -(signed char) ev[2];
|
|
if ((ev[0] & 4) == 0)
|
|
flags |= KD_BUTTON_1;
|
|
if ((ev[0] & 2) == 0)
|
|
flags |= KD_BUTTON_2;
|
|
if ((ev[0] & 1) == 0)
|
|
flags |= KD_BUTTON_3;
|
|
if (!MouseReasonable (pi, flags, dx, dy))
|
|
return FALSE;
|
|
if (km->stage == MouseWorking)
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static const KmouseProt busProt = {
|
|
"bus",
|
|
threeComplete, mouseValid, busParse, 0,
|
|
0xf8, 0x00, 0x00, 0x00,
|
|
FALSE
|
|
};
|
|
|
|
/*
|
|
* Standard MS serial protocol, three bytes
|
|
*/
|
|
|
|
static Bool msParse (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int dx, dy;
|
|
unsigned long flags;
|
|
|
|
flags = KD_MOUSE_DELTA;
|
|
|
|
if (ev[0] & 0x20)
|
|
flags |= KD_BUTTON_1;
|
|
if (ev[0] & 0x10)
|
|
flags |= KD_BUTTON_3;
|
|
|
|
dx = (signed char)(((ev[0] & 0x03) << 6) | (ev[1] & 0x3F));
|
|
dy = (signed char)(((ev[0] & 0x0C) << 4) | (ev[2] & 0x3F));
|
|
if (!MouseReasonable (pi, flags, dx, dy))
|
|
return FALSE;
|
|
if (km->stage == MouseWorking)
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static const KmouseProt msProt = {
|
|
"ms",
|
|
threeComplete, mouseValid, msParse, 0,
|
|
0xc0, 0x40, 0xc0, 0x00,
|
|
TRUE,
|
|
IGNPAR,
|
|
0,
|
|
0,
|
|
CS7 | CSTOPB | CREAD | CLOCAL,
|
|
B1200,
|
|
};
|
|
|
|
/*
|
|
* Logitech mice send 3 or 4 bytes, the only way to tell is to look at the
|
|
* first byte of a synchronized protocol stream and see if it's got
|
|
* any bits turned on that can't occur in that fourth byte
|
|
*/
|
|
static Bool logiComplete (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
|
|
if ((ev[0] & 0x40) == 0x40)
|
|
return ne == 3;
|
|
if (km->stage != MouseBroken && (ev[0] & ~0x23) == 0)
|
|
return ne == 1;
|
|
return FALSE;
|
|
}
|
|
|
|
static int logiValid (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
const KmouseProt *prot = km->prot;
|
|
int i;
|
|
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
if ((ev[i] & 0x40) == 0x40)
|
|
break;
|
|
if (km->stage != MouseBroken && (ev[i] & ~0x23) == 0)
|
|
break;
|
|
}
|
|
if (i != 0)
|
|
return i;
|
|
for (i = 1; i < ne; i++)
|
|
if ((ev[i] & prot->dataMask) != prot->dataValid)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static Bool logiParse (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int dx, dy;
|
|
unsigned long flags;
|
|
|
|
flags = KD_MOUSE_DELTA;
|
|
|
|
if (ne == 3)
|
|
{
|
|
if (ev[0] & 0x20)
|
|
flags |= KD_BUTTON_1;
|
|
if (ev[0] & 0x10)
|
|
flags |= KD_BUTTON_3;
|
|
|
|
dx = (signed char)(((ev[0] & 0x03) << 6) | (ev[1] & 0x3F));
|
|
dy = (signed char)(((ev[0] & 0x0C) << 4) | (ev[2] & 0x3F));
|
|
flags |= km->state & KD_BUTTON_2;
|
|
}
|
|
else
|
|
{
|
|
if (ev[0] & 0x20)
|
|
flags |= KD_BUTTON_2;
|
|
dx = 0;
|
|
dy = 0;
|
|
flags |= km->state & (KD_BUTTON_1|KD_BUTTON_3);
|
|
}
|
|
|
|
if (!MouseReasonable (pi, flags, dx, dy))
|
|
return FALSE;
|
|
if (km->stage == MouseWorking)
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static const KmouseProt logiProt = {
|
|
"logitech",
|
|
logiComplete, logiValid, logiParse, 0,
|
|
0xc0, 0x40, 0xc0, 0x00,
|
|
TRUE,
|
|
IGNPAR,
|
|
0,
|
|
0,
|
|
CS7 | CSTOPB | CREAD | CLOCAL,
|
|
B1200,
|
|
};
|
|
|
|
/*
|
|
* Mouse systems protocol, 5 bytes
|
|
*/
|
|
static Bool mscParse (KdPointerInfo *pi, unsigned char *ev, int ne)
|
|
{
|
|
Kmouse *km = pi->driverPrivate;
|
|
int dx, dy;
|
|
unsigned long flags;
|
|
|
|
flags = KD_MOUSE_DELTA;
|
|
|
|
if (!(ev[0] & 0x4))
|
|
flags |= KD_BUTTON_1;
|
|
if (!(ev[0] & 0x2))
|
|
flags |= KD_BUTTON_2;
|
|
if (!(ev[0] & 0x1))
|
|
flags |= KD_BUTTON_3;
|
|
dx = (signed char)(ev[1]) + (signed char)(ev[3]);
|
|
dy = - ((signed char)(ev[2]) + (signed char)(ev[4]));
|
|
|
|
if (!MouseReasonable (pi, flags, dx, dy))
|
|
return FALSE;
|
|
if (km->stage == MouseWorking)
|
|
KdEnqueuePointerEvent (pi, flags, dx, dy, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static const KmouseProt mscProt = {
|
|
"msc",
|
|
fiveComplete, mouseValid, mscParse, 0,
|
|
0xf8, 0x80, 0x00, 0x00,
|
|
TRUE,
|
|
IGNPAR,
|
|
0,
|
|
0,
|
|
CS8 | CSTOPB | CREAD | CLOCAL,
|
|
B1200,
|
|
};
|
|
|
|
/*
|
|
* Use logitech before ms -- they're the same except that
|
|
* logitech sometimes has a fourth byte
|
|
*/
|
|
static const KmouseProt *kmouseProts[] = {
|
|
&ps2Prot, &imps2Prot, &exps2Prot, &busProt, &logiProt, &msProt, &mscProt,
|
|
};
|
|
|
|
#define NUM_PROT (sizeof (kmouseProts) / sizeof (kmouseProts[0]))
|
|
|
|
static void
|
|
MouseInitProtocol (Kmouse *km)
|
|
{
|
|
int ret;
|
|
struct termios t;
|
|
|
|
if (km->prot->tty)
|
|
{
|
|
ret = tcgetattr (km->iob.fd, &t);
|
|
|
|
if (ret >= 0)
|
|
{
|
|
t.c_iflag = km->prot->c_iflag;
|
|
t.c_oflag = km->prot->c_oflag;
|
|
t.c_lflag = km->prot->c_lflag;
|
|
t.c_cflag = km->prot->c_cflag;
|
|
cfsetispeed (&t, km->prot->speed);
|
|
cfsetospeed (&t, km->prot->speed);
|
|
ret = tcsetattr (km->iob.fd, TCSANOW, &t);
|
|
}
|
|
}
|
|
km->stage = MouseBroken;
|
|
km->valid = 0;
|
|
km->tested = 0;
|
|
km->invalid = 0;
|
|
km->state = km->prot->state;
|
|
}
|
|
|
|
static void
|
|
MouseFirstProtocol (Kmouse *km, char *prot)
|
|
{
|
|
if (prot)
|
|
{
|
|
for (km->i_prot = 0; km->i_prot < NUM_PROT; km->i_prot++)
|
|
if (!strcmp (prot, kmouseProts[km->i_prot]->name))
|
|
break;
|
|
if (km->i_prot == NUM_PROT)
|
|
{
|
|
int i;
|
|
ErrorF ("Unknown mouse protocol \"%s\". Pick one of:", prot);
|
|
for (i = 0; i < NUM_PROT; i++)
|
|
ErrorF (" %s", kmouseProts[i]->name);
|
|
ErrorF ("\n");
|
|
}
|
|
else
|
|
{
|
|
km->prot = kmouseProts[km->i_prot];
|
|
if (km->tty && !km->prot->tty)
|
|
ErrorF ("Mouse device is serial port, protocol %s is not serial protocol\n",
|
|
prot);
|
|
else if (!km->tty && km->prot->tty)
|
|
ErrorF ("Mouse device is not serial port, protocol %s is serial protocol\n",
|
|
prot);
|
|
}
|
|
}
|
|
if (!km->prot)
|
|
{
|
|
for (km->i_prot = 0; kmouseProts[km->i_prot]->tty != km->tty; km->i_prot++)
|
|
;
|
|
km->prot = kmouseProts[km->i_prot];
|
|
}
|
|
MouseInitProtocol (km);
|
|
}
|
|
|
|
static void
|
|
MouseNextProtocol (Kmouse *km)
|
|
{
|
|
do
|
|
{
|
|
if (!km->prot)
|
|
km->i_prot = 0;
|
|
else
|
|
if (++km->i_prot == NUM_PROT) km->i_prot = 0;
|
|
km->prot = kmouseProts[km->i_prot];
|
|
} while (km->prot->tty != km->tty);
|
|
MouseInitProtocol (km);
|
|
ErrorF ("Switching to mouse protocol \"%s\"\n", km->prot->name);
|
|
}
|
|
|
|
static void
|
|
MouseRead (int mousePort, void *closure)
|
|
{
|
|
KdPointerInfo *pi = closure;
|
|
Kmouse *km = pi->driverPrivate;
|
|
unsigned char event[MAX_MOUSE];
|
|
int ne;
|
|
int c;
|
|
int i;
|
|
int timeout;
|
|
|
|
timeout = 0;
|
|
ne = 0;
|
|
for(;;)
|
|
{
|
|
c = MouseReadByte (&km->iob, timeout);
|
|
if (c == -1)
|
|
{
|
|
if (ne)
|
|
{
|
|
km->invalid += ne + km->tested;
|
|
km->valid = 0;
|
|
km->tested = 0;
|
|
km->stage = MouseBroken;
|
|
}
|
|
break;
|
|
}
|
|
event[ne++] = c;
|
|
i = (*km->prot->Valid) (pi, event, ne);
|
|
if (i != 0)
|
|
{
|
|
#ifdef DEBUG
|
|
ErrorF ("Mouse protocol %s broken %d of %d bytes bad\n",
|
|
km->prot->name, i > 0 ? i : ne, ne);
|
|
#endif
|
|
if (i > 0 && i < ne)
|
|
{
|
|
ne -= i;
|
|
memmove (event, event + i, ne);
|
|
}
|
|
else
|
|
{
|
|
i = ne;
|
|
ne = 0;
|
|
}
|
|
km->invalid += i + km->tested;
|
|
km->valid = 0;
|
|
km->tested = 0;
|
|
if (km->stage == MouseWorking)
|
|
km->i_prot--;
|
|
km->stage = MouseBroken;
|
|
if (km->invalid > MAX_SKIP)
|
|
{
|
|
MouseNextProtocol (km);
|
|
ne = 0;
|
|
}
|
|
timeout = 0;
|
|
}
|
|
else
|
|
{
|
|
if ((*km->prot->Complete) (pi, event, ne))
|
|
{
|
|
if ((*km->prot->Parse) (pi, event, ne))
|
|
{
|
|
switch (km->stage)
|
|
{
|
|
case MouseBroken:
|
|
#ifdef DEBUG
|
|
ErrorF ("Mouse protocol %s seems OK\n",
|
|
km->prot->name);
|
|
#endif
|
|
/* do not zero invalid to accumulate invalid bytes */
|
|
km->valid = 0;
|
|
km->tested = 0;
|
|
km->stage = MouseTesting;
|
|
/* fall through ... */
|
|
case MouseTesting:
|
|
km->valid++;
|
|
km->tested += ne;
|
|
if (km->valid > MAX_VALID)
|
|
{
|
|
#ifdef DEBUG
|
|
ErrorF ("Mouse protocol %s working\n",
|
|
km->prot->name);
|
|
#endif
|
|
km->stage = MouseWorking;
|
|
km->invalid = 0;
|
|
km->tested = 0;
|
|
km->valid = 0;
|
|
if (km->prot->Init && !(*km->prot->Init) (pi))
|
|
km->stage = MouseBroken;
|
|
}
|
|
break;
|
|
case MouseWorking:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
km->invalid += ne + km->tested;
|
|
km->valid = 0;
|
|
km->tested = 0;
|
|
km->stage = MouseBroken;
|
|
}
|
|
ne = 0;
|
|
timeout = 0;
|
|
}
|
|
else
|
|
timeout = MOUSE_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
int MouseInputType;
|
|
|
|
char *kdefaultMouse[] = {
|
|
"/dev/input/mice",
|
|
"/dev/mouse",
|
|
"/dev/psaux",
|
|
"/dev/adbmouse",
|
|
"/dev/ttyS0",
|
|
"/dev/ttyS1",
|
|
};
|
|
|
|
#define NUM_DEFAULT_MOUSE (sizeof (kdefaultMouse) / sizeof (kdefaultMouse[0]))
|
|
|
|
static Status
|
|
MouseInit (KdPointerInfo *pi)
|
|
{
|
|
int i;
|
|
int fd;
|
|
Kmouse *km;
|
|
|
|
if (!pi)
|
|
return BadImplementation;
|
|
|
|
if (!pi->path || strcmp(pi->path, "auto") == 0) {
|
|
for (i = 0; i < NUM_DEFAULT_MOUSE; i++) {
|
|
fd = open (kdefaultMouse[i], 2);
|
|
if (fd >= 0) {
|
|
pi->path = strdup (kdefaultMouse[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fd = open (pi->path, 2);
|
|
}
|
|
|
|
if (fd < 0)
|
|
return BadMatch;
|
|
|
|
close(fd);
|
|
|
|
km = (Kmouse *) malloc(sizeof (Kmouse));
|
|
if (km) {
|
|
km->iob.avail = km->iob.used = 0;
|
|
MouseFirstProtocol(km, pi->protocol ? pi->protocol : "exps/2");
|
|
/* MouseFirstProtocol sets state to MouseBroken for later protocol
|
|
* checks. Skip these checks if a protocol was supplied */
|
|
if (pi->protocol)
|
|
km->state = MouseWorking;
|
|
km->i_prot = 0;
|
|
km->tty = isatty (fd);
|
|
km->iob.fd = -1;
|
|
pi->driverPrivate = km;
|
|
}
|
|
else {
|
|
close (fd);
|
|
return BadAlloc;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
static Status
|
|
MouseEnable (KdPointerInfo *pi)
|
|
{
|
|
Kmouse *km;
|
|
|
|
if (!pi || !pi->driverPrivate || !pi->path)
|
|
return BadImplementation;
|
|
|
|
km = pi->driverPrivate;
|
|
|
|
km->iob.fd = open(pi->path, 2);
|
|
if (km->iob.fd < 0)
|
|
return BadMatch;
|
|
|
|
if (!KdRegisterFd (km->iob.fd, MouseRead, pi))
|
|
{
|
|
close(km->iob.fd);
|
|
return BadAlloc;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
static void
|
|
MouseDisable (KdPointerInfo *pi)
|
|
{
|
|
Kmouse *km;
|
|
if (!pi || !pi->driverPrivate)
|
|
return;
|
|
|
|
km = pi->driverPrivate;
|
|
KdUnregisterFd (pi, km->iob.fd, TRUE);
|
|
}
|
|
|
|
static void
|
|
MouseFini (KdPointerInfo *pi)
|
|
{
|
|
free(pi->driverPrivate);
|
|
pi->driverPrivate = NULL;
|
|
}
|
|
|
|
KdPointerDriver LinuxMouseDriver = {
|
|
"mouse",
|
|
MouseInit,
|
|
MouseEnable,
|
|
MouseDisable,
|
|
MouseFini,
|
|
NULL,
|
|
};
|