#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/tty.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include "wsmoused.h"
#include "mouse_protocols.h"
extern int debug;
extern int nodaemon;
extern int background;
extern mouse_t mouse;
static const unsigned short mousecflags[] = {
(CS7 | CREAD | CLOCAL | HUPCL),
(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL),
(CS8 | CSTOPB | CREAD | CLOCAL | HUPCL),
(CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL),
(CS7 | CREAD | CLOCAL | HUPCL),
(CS8 | CREAD | CLOCAL | HUPCL),
(CS7 | CREAD | CLOCAL | HUPCL),
(CS7 | CREAD | CLOCAL | HUPCL),
(CS7 | CREAD | CLOCAL | HUPCL),
};
static const unsigned char proto[][7] = {
{0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00},
{0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff},
{0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff},
{0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff},
{0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00},
{0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff},
{0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00},
{0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00},
{0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00},
};
const char *mouse_names[] = {
"microsoft",
"mousesystems",
"logitech",
"mmseries",
"mouseman",
"mmhitab",
"glidepoint",
"intellimouse",
"thinkingmouse",
NULL
};
static unsigned char cur_proto[7];
static const symtab_t pnpprod[] = {
{"KML0001", P_THINKING},
{"MSH0001", P_IMSERIAL},
{"MSH0004", P_IMSERIAL},
{"KYEEZ00", P_MS},
{"KYE0001", P_MS},
{"KYE0003", P_IMSERIAL},
{"LGI800C", P_IMSERIAL},
{"LGI8050", P_IMSERIAL},
{"LGI8051", P_IMSERIAL},
{"LGI8001", P_LOGIMAN},
{"PNP0F01", P_MS},
{"PNP0F04", P_MSC},
{"PNP0F05", P_MSC},
{"PNP0F08", P_LOGIMAN},
{"PNP0F09", P_MS},
{"PNP0F0A", P_MS},
{"PNP0F0B", P_MS},
{"PNP0F0C", P_MS},
{"PNP0F0F", P_MS},
{"PNP0F17", P_LOGIMAN},
{NULL, -1},
};
static const symtab_t *
gettoken(const symtab_t * tab, char *s, int len)
{
int i;
for (i = 0; tab[i].name != NULL; ++i) {
if (strncmp(tab[i].name, s, len) == 0)
break;
}
return &tab[i];
}
const char *
mouse_name(int type)
{
return (type < 0 ||
(uint)type >= sizeof(mouse_names) / sizeof(mouse_names[0])) ?
"unknown" : mouse_names[type];
}
void
SetMouseSpeed(int old, unsigned int cflag)
{
struct termios tty;
char *c;
if (tcgetattr(mouse.mfd, &tty) == -1) {
debug("Warning: %s unable to get status of mouse fd (%s)\n",
mouse.portname, strerror(errno));
return;
}
tty.c_iflag = IGNBRK | IGNPAR;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_cflag = (tcflag_t) cflag;
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 1;
switch (old) {
case 9600:
cfsetispeed(&tty, B9600);
cfsetospeed(&tty, B9600);
break;
case 4800:
cfsetispeed(&tty, B4800);
cfsetospeed(&tty, B4800);
break;
case 2400:
cfsetispeed(&tty, B2400);
cfsetospeed(&tty, B2400);
break;
case 1200:
default:
cfsetispeed(&tty, B1200);
cfsetospeed(&tty, B1200);
}
if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) == -1)
logerr(1, "unable to get mouse status. Exiting...\n");
c = "*n";
cfsetispeed(&tty, B1200);
cfsetospeed(&tty, B1200);
if (mouse.proto == P_LOGIMAN || mouse.proto == P_LOGI) {
if (write(mouse.mfd, c, 2) != 2)
logerr(1, "unable to write to mouse. Exiting...\n");
}
usleep(100000);
if (tcsetattr(mouse.mfd, TCSADRAIN, &tty) == -1)
logerr(1, "unable to get mouse status. Exiting...\n");
}
int
FlushInput(int fd)
{
struct pollfd pfd[1];
char c[4];
if (tcflush(fd, TCIFLUSH) == 0)
return 0;
pfd[0].fd = fd;
pfd[0].events = POLLIN;
while (poll(pfd, 1, 0) > 0)
read(fd, &c, sizeof(c));
return 0;
}
static int
pnpgets(int mouse_fd, char *buf)
{
struct pollfd pfd[1];
int i;
char c;
pfd[0].fd = mouse_fd;
pfd[0].events = POLLIN;
#if 0
ioctl(mouse_fd, TIOCMGET, &i);
i |= TIOCM_DTR;
i &= ~TIOCM_RTS;
ioctl(mouse_fd, TIOCMSET, &i);
usleep(200000);
if ((ioctl(mouse_fd, TIOCMGET, &i) == -1) || ((i & TIOCM_DSR) == 0))
goto disconnect_idle;
SetMouseSpeed(1200, (CS7 | CREAD | CLOCAL | HUPCL));
i = TIOCM_DTR | TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIC, &i);
usleep(200000);
i = TIOCM_DTR;
ioctl(mouse_fd, TIOCMBIS, &i);
usleep(200000);
FlushInput(mouse_fd);
i = TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIS, &i);
if (poll(pfd, 1, 200000 / 1000) <= 0) {
i = TIOCM_DTR | TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIC, &i);
usleep(200000);
FlushInput(mouse_fd);
i = TIOCM_DTR | TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIS, &i);
if (poll(pfd, 1, 200000 / 1000) <= 0)
goto connect_idle;
}
#else
SetMouseSpeed(1200, (CS7 | CREAD | CLOCAL | HUPCL));
ioctl(mouse_fd, TIOCMGET, &i);
i |= TIOCM_DTR;
i &= ~TIOCM_RTS;
ioctl(mouse_fd, TIOCMSET, &i);
usleep(200000);
FlushInput(mouse_fd);
i = TIOCM_DTR | TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIS, &i);
if (poll(pfd, 1, 200000 / 1000) <= 0)
goto connect_idle;
#endif
i = 0;
usleep(200000);
while (read(mouse_fd, &c, 1) == 1) {
if ((c == 0x08) || (c == 0x28)) {
buf[i++] = c;
break;
}
}
if (i <= 0) {
goto connect_idle;
}
++c;
for (;;) {
if (poll(pfd, 1, 200000 / 1000) <= 0)
break;
read(mouse_fd, &buf[i], 1);
if (buf[i++] == c)
break;
if (i >= 256)
break;
}
if (buf[i - 1] != c)
goto connect_idle;
return i;
#if 0
disconnect_idle:
i = TIOCM_DTR | TIOCM_RTS;
ioctl(mouse_fd, TIOCMBIS, &i);
#endif
connect_idle:
return 0;
}
static int
pnpparse(pnpid_t * id, char *buf, int len)
{
char s[3];
int offset, sum = 0, i, j;
id->revision = 0;
id->eisaid = NULL;
id->serial = NULL;
id->class = NULL;
id->compat = NULL;
id->description = NULL;
id->neisaid = 0;
id->nserial = 0;
id->nclass = 0;
id->ncompat = 0;
id->ndescription = 0;
offset = 0x28 - buf[0];
for (i = 0; i < len - 3; ++i) {
sum += buf[i];
buf[i] += offset;
}
sum += buf[len - 1];
for (; i < len; ++i)
buf[i] += offset;
debug("Mouse: PnP ID string: '%*.*s'\n", len, len, buf);
buf[1] -= offset;
buf[2] -= offset;
id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
debug("Mouse: PnP rev %d.%02d\n", id->revision / 100, id->revision % 100);
id->eisaid = &buf[3];
id->neisaid = 7;
i = 10;
if (buf[i] == '\\') {
for (j = ++i; i < len; ++i) {
if (buf[i] == '\\')
break;
}
if (i >= len)
i -= 3;
if (i - j == 8) {
id->serial = &buf[j];
id->nserial = 8;
}
}
if (buf[i] == '\\') {
for (j = ++i; i < len; ++i) {
if (buf[i] == '\\')
break;
}
if (i >= len)
i -= 3;
if (i > j + 1) {
id->class = &buf[j];
id->nclass = i - j;
}
}
if (buf[i] == '\\') {
for (j = ++i; i < len; ++i) {
if (buf[i] == '\\')
break;
}
if (buf[j] == '*')
++j;
if (i >= len)
i -= 3;
if (i > j + 1) {
id->compat = &buf[j];
id->ncompat = i - j;
}
}
if (buf[i] == '\\') {
for (j = ++i; i < len; ++i) {
if (buf[i] == ';')
break;
}
if (i >= len)
i -= 3;
if (i > j + 1) {
id->description = &buf[j];
id->ndescription = i - j;
}
}
if ((id->nserial > 0) || (id->nclass > 0) ||
(id->ncompat > 0) || (id->ndescription > 0)) {
#if 0
debug("Mouse: PnP checksum: 0x%02X\n", sum);
#endif
snprintf(s, sizeof s, "%02X", sum & 0x0ff);
if (strncmp(s, &buf[len - 3], 2) != 0) {
#if 0
return FALSE;
#endif
}
}
return 1;
}
static const symtab_t *
pnpproto(pnpid_t * id)
{
const symtab_t *t;
int i, j;
if (id->nclass > 0)
if (strncmp(id->class, "MOUSE", id->nclass) != 0)
return NULL;
if (id->neisaid > 0) {
t = gettoken(pnpprod, id->eisaid, id->neisaid);
if (t->val != -1)
return t;
}
if (id->ncompat <= 0)
return NULL;
for (i = 0; i < id->ncompat; ++i) {
for (j = i; id->compat[i] != ','; ++i)
if (i >= id->ncompat)
break;
if (i > j) {
t = gettoken(pnpprod, id->compat + j, i - j);
if (t->val != -1)
return t;
}
}
return NULL;
}
void
mouse_init(void)
{
struct pollfd pfd[1];
char *s;
char c;
int i;
pfd[0].fd = mouse.mfd;
pfd[0].events = POLLIN;
switch (mouse.proto) {
case P_LOGI:
SetMouseSpeed(9600, mousecflags[mouse.proto]);
SetMouseSpeed(4800, mousecflags[mouse.proto]);
SetMouseSpeed(2400, mousecflags[mouse.proto]);
#if 0
SetMouseSpeed(1200, mousecflags[mouse.proto]);
#endif
write(mouse.mfd, "S", 1);
SetMouseSpeed(1200, mousecflags[P_MM]);
if (mouse.rate <= 0)
write(mouse.mfd, "O", 1);
else if (mouse.rate <= 15)
write(mouse.mfd, "J", 1);
else if (mouse.rate <= 27)
write(mouse.mfd, "K", 1);
else if (mouse.rate <= 42)
write(mouse.mfd, "L", 1);
else if (mouse.rate <= 60)
write(mouse.mfd, "R", 1);
else if (mouse.rate <= 85)
write(mouse.mfd, "M", 1);
else if (mouse.rate <= 125)
write(mouse.mfd, "Q", 1);
else
write(mouse.mfd, "N", 1);
break;
case P_LOGIMAN:
SetMouseSpeed(1200, mousecflags[mouse.proto]);
write(mouse.mfd, "*X", 2);
SetMouseSpeed(1200, mousecflags[mouse.proto]);
break;
case P_MMHIT:
SetMouseSpeed(1200, mousecflags[mouse.proto]);
write(mouse.mfd, "z8", 2);
usleep(50000);
write(mouse.mfd, "zb", 2);
usleep(50000);
write(mouse.mfd, "@", 1);
usleep(50000);
write(mouse.mfd, "R", 1);
usleep(50000);
write(mouse.mfd, "I\x20", 2);
usleep(50000);
write(mouse.mfd, "E", 1);
usleep(50000);
if (mouse.resolution == MOUSE_RES_LOW)
c = 'g';
else if (mouse.resolution == MOUSE_RES_MEDIUMLOW)
c = 'e';
else if (mouse.resolution == MOUSE_RES_MEDIUMHIGH)
c = 'h';
else if (mouse.resolution == MOUSE_RES_HIGH)
c = 'd';
else if (mouse.resolution <= 40)
c = 'g';
else if (mouse.resolution <= 100)
c = 'd';
else if (mouse.resolution <= 200)
c = 'e';
else if (mouse.resolution <= 500)
c = 'h';
else if (mouse.resolution <= 1000)
c = 'j';
else
c = 'd';
write(mouse.mfd, &c, 1);
usleep(50000);
write(mouse.mfd, "\021", 1);
break;
case P_THINKING:
SetMouseSpeed(1200, mousecflags[mouse.proto]);
usleep(200000);
i = FREAD;
ioctl(mouse.mfd, TIOCFLUSH, &i);
for (s = "E5E5"; *s; ++s) {
write(mouse.mfd, s, 1);
if (poll(pfd, 1, INFTIM) <= 0)
break;
read(mouse.mfd, &c, 1);
debug("%c", c);
if (c != *s)
break;
}
break;
case P_MSC:
SetMouseSpeed(1200, mousecflags[mouse.proto]);
#if 0
if (mouse.flags & ClearDTR) {
i = TIOCM_DTR;
ioctl(mouse.mfd, TIOCMBIC, &i);
}
if (mouse.flags & ClearRTS) {
i = TIOCM_RTS;
ioctl(mouse.mfd, TIOCMBIC, &i);
}
#endif
break;
default:
SetMouseSpeed(1200, mousecflags[mouse.proto]);
break;
}
}
int
mouse_identify(void)
{
char pnpbuf[256];
pnpid_t pnpid;
const symtab_t *t;
int len;
if (mouse.proto != P_UNKNOWN)
bcopy(proto[mouse.proto], cur_proto, sizeof(cur_proto));
else {
if (mouse.flags & NoPnP)
return mouse.proto;
if (((len = pnpgets(mouse.mfd, pnpbuf)) <= 0)
|| !pnpparse(&pnpid, pnpbuf, len))
return mouse.proto;
debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
pnpid.neisaid, pnpid.neisaid,
pnpid.eisaid, pnpid.ncompat,
pnpid.ncompat, pnpid.compat,
pnpid.ndescription, pnpid.ndescription,
pnpid.description);
t = pnpproto(&pnpid);
if (t != NULL) {
mouse.proto = t->val;
bcopy(proto[mouse.proto], cur_proto, sizeof(cur_proto));
} else
mouse.proto = P_UNKNOWN;
}
debug("proto params: %02x %02x %02x %02x %d %02x %02x",
cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
cur_proto[4], cur_proto[5], cur_proto[6]);
return mouse.proto;
}
int
mouse_protocol(u_char rBuf, mousestatus_t * act)
{
static int butmapmss[4] = {
0,
MOUSE_BUTTON3DOWN,
MOUSE_BUTTON1DOWN,
MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
};
static int butmapmss2[4] = {
0,
MOUSE_BUTTON4DOWN,
MOUSE_BUTTON2DOWN,
MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
};
static int butmapintelli[4] = {
0,
MOUSE_BUTTON2DOWN,
MOUSE_BUTTON4DOWN,
MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
};
static int butmapmsc[8] = {
0,
MOUSE_BUTTON3DOWN,
MOUSE_BUTTON2DOWN,
MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
MOUSE_BUTTON1DOWN,
MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
};
static int butmaphit[8] = {
0,
MOUSE_BUTTON3DOWN,
MOUSE_BUTTON2DOWN,
MOUSE_BUTTON1DOWN,
MOUSE_BUTTON4DOWN,
MOUSE_BUTTON5DOWN,
MOUSE_BUTTON6DOWN,
MOUSE_BUTTON7DOWN,
};
static int pBufP = 0;
static unsigned char pBuf[8];
debug("received char 0x%x", (int) rBuf);
if (pBufP != 0 && ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80)) {
pBufP = 0;
}
if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
return 0;
if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1]) {
if ((rBuf & cur_proto[5]) != cur_proto[6]) {
pBufP = 0;
return 0;
}
switch (mouse.proto) {
case P_IMSERIAL:
act->dx = act->dy = 0;
act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
act->obutton = act->button;
act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
break;
default:
act->dx = act->dy = act->dz = act->dw = 0;
act->obutton = act->button;
act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
| (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
break;
}
act->flags = ((act->dx || act->dy || act->dz || act->dw) ?
MOUSE_POSCHANGED : 0) | (act->obutton ^ act->button);
pBufP = 0;
return act->flags;
}
if (pBufP >= cur_proto[4])
pBufP = 0;
pBuf[pBufP++] = rBuf;
if (pBufP != cur_proto[4])
return 0;
debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
cur_proto[4],
pBuf[0], pBuf[1], pBuf[2], pBuf[3],
pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
act->dz = 0;
act->dw = 0;
act->obutton = act->button;
switch (mouse.proto) {
case P_MS:
case P_LOGIMAN:
case P_GLIDEPOINT:
case P_THINKING:
case P_IMSERIAL:
act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
| butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
act->dx = (char) (((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
act->dy = (char) (((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
break;
case P_MSC:
act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
act->dx = (char) (pBuf[1]) + (char) (pBuf[3]);
act->dy = -((char) (pBuf[2]) + (char) (pBuf[4]));
break;
case P_MMHIT:
act->button = butmaphit[pBuf[0] & 0x07];
act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : -pBuf[1];
act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? -pBuf[2] : pBuf[2];
break;
case P_MM:
case P_LOGI:
act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : -pBuf[1];
act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? -pBuf[2] : pBuf[2];
break;
default:
return 0;
}
act->flags = ((act->dx || act->dy || act->dz || act->dw) ?
MOUSE_POSCHANGED : 0) | (act->obutton ^ act->button);
return act->flags;
}