kopia lustrzana https://github.com/FreeSpacenav/spacenavd
735 wiersze
17 KiB
C
735 wiersze
17 KiB
C
/*
|
|
spacenavd - a free software replacement driver for 6dof space-mice.
|
|
Copyright (C) 2007-2023 John Tsiombikas <nuclear@member.fsf.org>
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_ALLOCA_H
|
|
#include <alloca.h>
|
|
#endif
|
|
#ifdef HAVE_MALLOC_H
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <sys/select.h>
|
|
#include <sys/time.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "dev_serial.h"
|
|
#include "dev.h"
|
|
#include "event.h"
|
|
#include "logger.h"
|
|
#include "proto.h"
|
|
|
|
#if defined(__i386__) || defined(__ia64__) || defined(WIN32) || \
|
|
(defined(__alpha__) || defined(__alpha)) || \
|
|
defined(__arm__) || \
|
|
(defined(__mips__) && defined(__MIPSEL__)) || \
|
|
defined(__SYMBIAN32__) || \
|
|
defined(__x86_64__) || \
|
|
defined(__LITTLE_ENDIAN__)
|
|
#define SBALL_LITTLE_ENDIAN
|
|
#else
|
|
#define SBALL_BIG_ENDIAN
|
|
#endif
|
|
|
|
#define INP_BUF_SZ 256
|
|
|
|
#define EVQUEUE_SZ 64
|
|
|
|
enum {
|
|
SB4000 = 1,
|
|
FLIPXY = 2
|
|
};
|
|
|
|
struct sball {
|
|
int fd;
|
|
unsigned int flags;
|
|
|
|
char buf[INP_BUF_SZ];
|
|
int len;
|
|
|
|
short mot[6];
|
|
unsigned int keystate, keymask;
|
|
|
|
struct termios saved_term;
|
|
int saved_mstat;
|
|
|
|
struct dev_input evqueue[EVQUEUE_SZ];
|
|
int evq_rd, evq_wr;
|
|
|
|
struct device *dev;
|
|
|
|
int (*parse)(struct sball*, int, char*, int);
|
|
};
|
|
|
|
|
|
static void close_dev_serial(struct device *dev);
|
|
static int read_dev_serial(struct device *dev, struct dev_input *inp);
|
|
|
|
static int stty_sball(struct sball *sb);
|
|
static int stty_mag(struct sball *sb);
|
|
static void stty_save(struct sball *sb);
|
|
static void stty_restore(struct sball *sb);
|
|
|
|
static int proc_input(struct sball *sb);
|
|
|
|
static int mag_parsepkt(struct sball *sb, int id, char *data, int len);
|
|
static int sball_parsepkt(struct sball *sb, int id, char *data, int len);
|
|
|
|
static int guess_num_buttons(struct device *dev, const char *verstr);
|
|
|
|
static void make_printable(char *buf, int len);
|
|
static int read_timeout(int fd, char *buf, int bufsz, long tm_usec);
|
|
|
|
static void enqueue_motion(struct sball *sb, int axis, int val);
|
|
static void gen_button_events(struct sball *sb, unsigned int prev);
|
|
|
|
static char *memstr(char *buf, int len, const char *str);
|
|
|
|
|
|
int open_dev_serial(struct device *dev)
|
|
{
|
|
int fd, sz;
|
|
char buf[128];
|
|
struct sball *sb = 0;
|
|
|
|
if((fd = open(dev->path, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {
|
|
logmsg(LOG_ERR, "open_dev_serial: failed to open device: %s: %s\n", dev->path, strerror(errno));
|
|
return -1;
|
|
}
|
|
if(!isatty(fd)) {
|
|
logmsg(LOG_ERR, "open_dev_serial: refusing to use %s: not a TTY\n", dev->path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
if(!(sb = calloc(1, sizeof *sb))) {
|
|
logmsg(LOG_ERR, "open_dev_serial: failed to allocate sball object\n");
|
|
goto err;
|
|
}
|
|
sb->dev = dev;
|
|
dev->data = sb;
|
|
dev->fd = sb->fd = fd;
|
|
dev->num_axes = 6;
|
|
dev->close = close_dev_serial;
|
|
dev->read = read_dev_serial;
|
|
|
|
stty_save(sb);
|
|
|
|
if(stty_sball(sb) == -1) {
|
|
goto err;
|
|
}
|
|
|
|
/* Apparently some spaceballs take some time to initialize, and it's
|
|
* necessary to wait for a little while before we start sending commands.
|
|
*/
|
|
sleep(1);
|
|
|
|
write(fd, "\r@RESET\r", 8);
|
|
|
|
if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && memstr(buf, sz, "@1")) {
|
|
/* we got a response, so it's a spaceball */
|
|
make_printable(buf, sz);
|
|
logmsg(LOG_INFO, "Spaceball detected: %s\n", buf);
|
|
strcpy(dev->name, "Spaceball");
|
|
|
|
dev->num_buttons = guess_num_buttons(dev, buf);
|
|
sb->keymask = 0xffff >> (16 - dev->num_buttons);
|
|
logmsg(LOG_INFO, "%d buttons\n", dev->num_buttons);
|
|
|
|
/* set binary mode and enable automatic data packet sending. also request
|
|
* a key event to find out as soon as possible if this is a 4000flx with
|
|
* 12 buttons
|
|
*/
|
|
write(fd, "\rCB\rMSSV\rk\r", 11);
|
|
|
|
sb->parse = sball_parsepkt;
|
|
return 0;
|
|
}
|
|
|
|
/* try as a magellan spacemouse */
|
|
if(stty_mag(sb) == -1) {
|
|
goto err;
|
|
}
|
|
write(fd, "vQ\r", 3);
|
|
|
|
if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0 && buf[0] == 'v') {
|
|
make_printable(buf, sz);
|
|
logmsg(LOG_INFO, "Magellan SpaceMouse detected:\n%s\n", buf);
|
|
strcpy(dev->name, "Magellan SpaceMouse");
|
|
|
|
dev->num_buttons = guess_num_buttons(dev, buf);
|
|
sb->keymask = 0xffff >> (16 - dev->num_buttons);
|
|
logmsg(LOG_INFO, "%d buttons\n", dev->num_buttons);
|
|
|
|
/* set 3D mode, not-dominant-axis, pass through motion and button packets */
|
|
write(fd, "m3\r", 3);
|
|
/* also attempt the compress mode-set command with extended keys enabled */
|
|
write(fd, "c3B\r", 4);
|
|
|
|
sb->parse = mag_parsepkt;
|
|
return 0;
|
|
}
|
|
|
|
err:
|
|
stty_restore(sb);
|
|
close(fd);
|
|
free(sb);
|
|
return -1;
|
|
}
|
|
|
|
static void close_dev_serial(struct device *dev)
|
|
{
|
|
if(dev->data) {
|
|
stty_restore(dev->data);
|
|
close(dev->fd);
|
|
}
|
|
dev->data = 0;
|
|
}
|
|
|
|
static int read_dev_serial(struct device *dev, struct dev_input *inp)
|
|
{
|
|
int sz;
|
|
struct sball *sb = dev->data;
|
|
|
|
if(!sb) return -1;
|
|
|
|
while((sz = read(sb->fd, sb->buf + sb->len, INP_BUF_SZ - sb->len - 1)) > 0) {
|
|
sb->len += sz;
|
|
proc_input(sb);
|
|
}
|
|
|
|
/* if we fill the input buffer, make a last attempt to parse it, and discard
|
|
* it so we can receive more
|
|
*/
|
|
if(sb->len >= INP_BUF_SZ) {
|
|
proc_input(sb);
|
|
sb->len = 0;
|
|
}
|
|
|
|
if(sb->evq_rd != sb->evq_wr) {
|
|
*inp = sb->evqueue[sb->evq_rd];
|
|
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* Labtec spaceball: 9600 8n1 XON/XOFF
|
|
* Can't use canonical mode to assemble input into lines for the spaceball,
|
|
* because binary data received for motion events can include newlines which
|
|
* would be eaten up by the line discipline. Therefore we'll rely on VTIME=1 to
|
|
* hopefully get more than 1 byte at a time. Alternatively we could request
|
|
* printable reports, but I don't feel like implementing that.
|
|
*/
|
|
static int stty_sball(struct sball *sb)
|
|
{
|
|
int mstat;
|
|
struct termios term;
|
|
|
|
term = sb->saved_term;
|
|
term.c_oflag = 0;
|
|
term.c_lflag = 0;
|
|
term.c_cc[VMIN] = 0;
|
|
term.c_cc[VTIME] = 1;
|
|
|
|
term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
|
|
term.c_iflag = IGNBRK | IGNPAR | IXON | IXOFF;
|
|
|
|
cfsetispeed(&term, B9600);
|
|
cfsetospeed(&term, B9600);
|
|
|
|
if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) {
|
|
perror("open_dev_serial: tcsetattr");
|
|
return -1;
|
|
}
|
|
tcflush(sb->fd, TCIOFLUSH);
|
|
|
|
mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS;
|
|
ioctl(sb->fd, TIOCMGET, &mstat);
|
|
return 0;
|
|
}
|
|
|
|
/* Logicad magellan spacemouse: 9600 8n2 CTS/RTS
|
|
* Since the magellan devices don't seem to send any newlines, we can rely on
|
|
* canonical mode to feed us nice whole lines at a time.
|
|
*/
|
|
static int stty_mag(struct sball *sb)
|
|
{
|
|
int mstat;
|
|
struct termios term;
|
|
|
|
term = sb->saved_term;
|
|
term.c_oflag = 0;
|
|
term.c_lflag = ICANON;
|
|
term.c_cc[VMIN] = 0;
|
|
term.c_cc[VTIME] = 0;
|
|
term.c_cc[VEOF] = 0;
|
|
term.c_cc[VEOL] = '\r';
|
|
term.c_cc[VEOL2] = 0;
|
|
term.c_cc[VERASE] = 0;
|
|
term.c_cc[VKILL] = 0;
|
|
|
|
term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL;
|
|
#ifdef CCTS_OFLOW
|
|
term.c_cflag |= CCTS_OFLOW;
|
|
#elif defined(CRTSCTS)
|
|
term.c_cflag |= CRTSCTS;
|
|
#endif
|
|
term.c_iflag = IGNBRK | IGNPAR;
|
|
|
|
cfsetispeed(&term, B9600);
|
|
cfsetospeed(&term, B9600);
|
|
|
|
if(tcsetattr(sb->fd, TCSAFLUSH, &term) == -1) {
|
|
perror("open_dev_serial: tcsetattr");
|
|
return -1;
|
|
}
|
|
tcflush(sb->fd, TCIOFLUSH);
|
|
|
|
mstat = sb->saved_mstat | TIOCM_DTR | TIOCM_RTS;
|
|
ioctl(sb->fd, TIOCMGET, &mstat);
|
|
return 0;
|
|
}
|
|
|
|
static void stty_save(struct sball *sb)
|
|
{
|
|
tcgetattr(sb->fd, &sb->saved_term);
|
|
ioctl(sb->fd, TIOCMGET, &sb->saved_mstat);
|
|
}
|
|
|
|
static void stty_restore(struct sball *sb)
|
|
{
|
|
tcsetattr(sb->fd, TCSAFLUSH, &sb->saved_term);
|
|
tcflush(sb->fd, TCIOFLUSH);
|
|
ioctl(sb->fd, TIOCMSET, &sb->saved_mstat);
|
|
}
|
|
|
|
|
|
static int proc_input(struct sball *sb)
|
|
{
|
|
int sz;
|
|
char *bptr = sb->buf;
|
|
char *start = sb->buf;
|
|
char *end = sb->buf + sb->len;
|
|
|
|
/* see if we have a CR in the buffer */
|
|
while(bptr < end) {
|
|
if(*bptr == '\r') {
|
|
*bptr = 0;
|
|
sb->parse(sb, *start, start + 1, bptr - start - 1);
|
|
start = ++bptr;
|
|
} else {
|
|
bptr++;
|
|
}
|
|
}
|
|
|
|
sz = start - sb->buf;
|
|
if(sz > 0) {
|
|
memmove(sb->buf, start, sz);
|
|
sb->len -= sz;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mag_parsepkt(struct sball *sb, int id, char *data, int len)
|
|
{
|
|
int i, prev, motion_pending = 0;
|
|
unsigned int prev_key;
|
|
|
|
/*logmsg(LOG_DEBUG, "magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/
|
|
|
|
switch(id) {
|
|
case 'd':
|
|
if(len != 24) {
|
|
logmsg(LOG_WARNING, "magellan: invalid data packet, expected 24 bytes, got: %d\n", len);
|
|
return -1;
|
|
}
|
|
for(i=0; i<6; i++) {
|
|
prev = sb->mot[i];
|
|
sb->mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) |
|
|
(((int)data[2] & 0xf) << 4) | (data[3] & 0xf)) - 0x8000;
|
|
data += 4;
|
|
|
|
/* flip the Z axis sign to match the spaceball */
|
|
if(i == 2 || i == 5) {
|
|
sb->mot[i] = -sb->mot[i];
|
|
}
|
|
|
|
if(sb->mot[i] != prev) {
|
|
enqueue_motion(sb, i, sb->mot[i]);
|
|
motion_pending++;
|
|
}
|
|
}
|
|
if(motion_pending) {
|
|
enqueue_motion(sb, -1, 0);
|
|
}
|
|
break;
|
|
|
|
case 'k':
|
|
if(len < 3) {
|
|
logmsg(LOG_WARNING, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len);
|
|
return -1;
|
|
}
|
|
prev_key = sb->keystate;
|
|
sb->keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8);
|
|
if(len > 3) {
|
|
sb->keystate |= ((unsigned int)data[3] & 0xf) << 12;
|
|
}
|
|
|
|
if(sb->keystate != prev_key) {
|
|
gen_button_events(sb, prev_key);
|
|
}
|
|
break;
|
|
|
|
case 'e':
|
|
if(data[0] == 1) {
|
|
logmsg(LOG_WARNING, "magellan error: illegal command: %c%c\n", data[1], data[2]);
|
|
} else if(data[0] == 2) {
|
|
logmsg(LOG_WARNING, "magellan error: framing error\n");
|
|
} else {
|
|
logmsg(LOG_WARNING, "magellan error: unknown device error\n");
|
|
}
|
|
return -1;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sball_parsepkt(struct sball *sb, int id, char *data, int len)
|
|
{
|
|
int i, prev, motion_pending = 0;
|
|
char c, *rd, *wr;
|
|
unsigned int prev_key;
|
|
char *errbuf, *errbuf_end;
|
|
|
|
errbuf = alloca(len * 16 + 32);
|
|
|
|
/* decode data packet, replacing escaped values with the correct ones */
|
|
rd = wr = data;
|
|
while(rd < data + len) {
|
|
if((c = *rd++) == '^') {
|
|
switch(*rd++) {
|
|
case 'Q':
|
|
*wr++ = 0x11; /* XON */
|
|
break;
|
|
case 'S':
|
|
*wr++ = 0x13; /* XOFF */
|
|
break;
|
|
case 'M':
|
|
*wr++ = 13; /* CR */
|
|
break;
|
|
case '^':
|
|
*wr++ = '^';
|
|
break;
|
|
default:
|
|
logmsg(LOG_WARNING, "sball decode: ignoring invalid escape code: %xh\n", (unsigned int)c);
|
|
}
|
|
} else {
|
|
*wr++ = c;
|
|
}
|
|
}
|
|
len = wr - data; /* update the decoded length */
|
|
|
|
switch(id) {
|
|
case 'D':
|
|
if(len != 14) {
|
|
logmsg(LOG_WARNING, "sball: invalid data packet, expected 14 bytes, got: %d\n", len);
|
|
return -1;
|
|
}
|
|
|
|
#ifndef SBALL_BIG_ENDIAN
|
|
rd = data;
|
|
for(i=0; i<6; i++) {
|
|
rd += 2;
|
|
c = rd[0];
|
|
rd[0] = rd[1];
|
|
rd[1] = c;
|
|
}
|
|
#endif
|
|
|
|
for(i=0; i<6; i++) {
|
|
char *dest = (char*)(sb->mot + i);
|
|
data += 2;
|
|
prev = sb->mot[i];
|
|
*dest++ = data[0];
|
|
*dest++ = data[1];
|
|
|
|
if(sb->mot[i] != prev) {
|
|
enqueue_motion(sb, i, sb->mot[i]);
|
|
motion_pending++;
|
|
}
|
|
}
|
|
if(motion_pending) {
|
|
enqueue_motion(sb, -1, 0);
|
|
}
|
|
break;
|
|
|
|
case 'K':
|
|
if(len != 2) {
|
|
logmsg(LOG_WARNING, "sball: invalid key packet, expected 2 bytes, got: %d\n", len);
|
|
return -1;
|
|
}
|
|
if(sb->flags & SB4000) break; /* ignore K packets from spaceball 4000 devices */
|
|
|
|
prev_key = sb->keystate;
|
|
/* data[1] bits 0-3 -> buttons 0,1,2,3
|
|
* data[1] bits 4,5 (3003 L/R) -> buttons 0, 1
|
|
* data[0] bits 0-2 -> buttons 4,5,6
|
|
* data[0] bit 4 is (2003 pick) -> button 7
|
|
*/
|
|
sb->keystate = ((data[1] & 0xf) | ((data[1] >> 4) & 3) | ((data[0] & 7) << 4) |
|
|
((data[0] & 0x10) << 3)) & sb->keymask;
|
|
|
|
if(sb->keystate != prev_key) {
|
|
gen_button_events(sb, prev_key);
|
|
}
|
|
break;
|
|
|
|
case '.':
|
|
if(len != 2) {
|
|
logmsg(LOG_WARNING, "sball: invalid sb4k key packet, expected 2 bytes, got: %d\n", len);
|
|
return -1;
|
|
}
|
|
/* spaceball 4000 key packet */
|
|
if(!(sb->flags & SB4000)) {
|
|
logmsg(LOG_INFO, "Switching to spaceball 4000flx/5000flx-a mode (12 buttons) \n");
|
|
sb->flags |= SB4000;
|
|
sb->dev->num_buttons = 12; /* might have guessed 8 before */
|
|
sb->keymask = 0xfff;
|
|
strcpy(sb->dev->name, "Spaceball 4000FLX");
|
|
sb->dev->type = DEV_SB4000;
|
|
}
|
|
/* update orientation flag (actually don't bother) */
|
|
/*
|
|
if(data[0] & 0x20) {
|
|
sb->flags |= FLIPXY;
|
|
} else {
|
|
sb->flags &= ~FLIPXY;
|
|
}
|
|
*/
|
|
|
|
prev_key = sb->keystate;
|
|
/* data[1] bits 0-5 -> buttons 0,1,2,3,4,5
|
|
* data[1] bit 7 -> button 6
|
|
* data[0] bits 0-4 -> buttons 7,8,9,10,11
|
|
*/
|
|
sb->keystate = (data[1] & 0x3f) | ((data[1] & 0x80) >> 1) | ((data[0] & 0x1f) << 7);
|
|
if(sb->keystate != prev_key) {
|
|
gen_button_events(sb, prev_key);
|
|
}
|
|
break;
|
|
|
|
case 'E':
|
|
strcpy(errbuf, "sball: error:");
|
|
errbuf_end = errbuf + 13;
|
|
for(i=0; i<len; i++) {
|
|
if(isprint((int)data[i])) {
|
|
errbuf_end += sprintf(errbuf_end, " %c", data[i]);
|
|
} else {
|
|
errbuf_end += sprintf(errbuf_end, " %02xh", (unsigned int)data[i]);
|
|
}
|
|
}
|
|
logmsg(LOG_WARNING, errbuf);
|
|
break;
|
|
|
|
case 'M': /* ignore MSS responses */
|
|
case '?': /* ignore unrecognized command errors */
|
|
break;
|
|
|
|
default:
|
|
/* DEBUG */
|
|
errbuf_end = errbuf + sprintf(errbuf, "sball: got '%c' packet:", (char)id);
|
|
for(i=0; i<len; i++) {
|
|
errbuf_end += sprintf(errbuf_end, " %02x", (unsigned int)data[i]);
|
|
}
|
|
strcpy(errbuf_end, "\n");
|
|
logmsg(LOG_WARNING, errbuf);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int guess_num_buttons(struct device *dev, const char *verstr)
|
|
{
|
|
int major, minor;
|
|
const char *s;
|
|
|
|
if((s = strstr(verstr, "Firmware version"))) { /* spaceball */
|
|
/* try to guess based on firmware number */
|
|
if(sscanf(s + 17, "%d.%d", &major, &minor) == 2 && major == 2) {
|
|
if(minor == 35 || minor == 62 || minor == 63) {
|
|
dev->type = DEV_SB3003;
|
|
strcpy(dev->name, "Spaceball 3003/3003C");
|
|
return 2; /* spaceball 3003/3003C */
|
|
}
|
|
if(minor == 43 || minor == 45) {
|
|
dev->type = DEV_SB4000;
|
|
strcpy(dev->name, "Spaceball 4000FLX/5000FLX-A");
|
|
return 12; /* spaceball 4000flx/5000flx-a */
|
|
}
|
|
if(minor == 2 || minor == 13 || minor == 15 || minor == 42) {
|
|
/* 2.42 is also used by spaceball 4000flx. we'll guess 2003c for
|
|
* now, and change the buttons to 12 first time we get a '.'
|
|
* packet. I'll also request a key report during init to make
|
|
* sure this happens as soon as possible, before clients have a
|
|
* chance to connect.
|
|
*/
|
|
dev->type = DEV_SB2003;
|
|
strcpy(dev->name, "Spaceball 1003/2003/2003C");
|
|
return 8; /* spaceball 1003/2003/2003c */
|
|
}
|
|
}
|
|
}
|
|
|
|
if(strstr(verstr, "MAGELLAN")) {
|
|
dev->type = DEV_SM;
|
|
strcpy(dev->name, "Magellan SpaceMouse");
|
|
return 11; /* magellan spacemouse (assume ext buttons on plus/xt) */
|
|
}
|
|
|
|
if(strstr(verstr, "SPACEBALL")) {
|
|
dev->type = DEV_SM5000;
|
|
strcpy(dev->name, "Spaceball 5000");
|
|
return 12; /* spaceball 5000 */
|
|
}
|
|
|
|
if(strstr(verstr, "CadMan")) {
|
|
dev->type = DEV_SMCADMAN;
|
|
strcpy(dev->name, "CadMan");
|
|
return 4;
|
|
}
|
|
|
|
logmsg(LOG_DEBUG, "Can't guess number of buttons, default to 8, report this as a bug!\n");
|
|
return 8;
|
|
}
|
|
|
|
static void make_printable(char *buf, int len)
|
|
{
|
|
int i, c;
|
|
char *wr = buf;
|
|
|
|
for(i=0; i<len; i++) {
|
|
c = *buf++;
|
|
if(c == '\r') {
|
|
*wr++ = '\n';
|
|
while(*buf == '\n' || *buf == '\r') buf++;
|
|
} else {
|
|
*wr++ = c;
|
|
}
|
|
}
|
|
*wr = 0;
|
|
}
|
|
|
|
static int read_timeout(int fd, char *buf, int bufsz, long tm_usec)
|
|
{
|
|
int res;
|
|
long usec, sz = 0;
|
|
struct timeval tv0, tv;
|
|
fd_set rdset;
|
|
|
|
if(!buf || bufsz <= 0) return -1;
|
|
|
|
usec = tm_usec;
|
|
gettimeofday(&tv0, 0);
|
|
|
|
while(sz < bufsz && usec > 0) {
|
|
tv.tv_sec = usec / 1000000;
|
|
tv.tv_usec = usec % 1000000;
|
|
|
|
FD_ZERO(&rdset);
|
|
FD_SET(fd, &rdset);
|
|
if((res = select(fd + 1, &rdset, 0, 0, &tv)) > 0 && FD_ISSET(fd, &rdset)) {
|
|
sz += read(fd, buf + sz, bufsz - sz);
|
|
buf[sz] = 0;
|
|
tm_usec = usec = 128000; /* wait 128ms for the rest of the message to appear */
|
|
gettimeofday(&tv0, 0);
|
|
continue;
|
|
}
|
|
if(res == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
|
|
break;
|
|
}
|
|
gettimeofday(&tv, 0);
|
|
usec = tm_usec - ((tv.tv_sec - tv0.tv_sec) * 1000000 + (tv.tv_usec - tv0.tv_usec));
|
|
}
|
|
|
|
return sz > 0 ? sz : -1;
|
|
}
|
|
|
|
static void enqueue_motion(struct sball *sb, int axis, int val)
|
|
{
|
|
struct dev_input *inp = sb->evqueue + sb->evq_wr;
|
|
|
|
sb->evq_wr = (sb->evq_wr + 1) & (EVQUEUE_SZ - 1);
|
|
if(sb->evq_wr == sb->evq_rd) {
|
|
/* overflow, drop the oldest event */
|
|
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
|
}
|
|
|
|
if(axis >= 0) {
|
|
inp->type = INP_MOTION;
|
|
inp->idx = axis;
|
|
inp->val = val;
|
|
} else {
|
|
inp->type = INP_FLUSH;
|
|
}
|
|
}
|
|
|
|
static void gen_button_events(struct sball *sb, unsigned int prev)
|
|
{
|
|
int i;
|
|
unsigned int bit = 1;
|
|
unsigned int diff = sb->keystate ^ prev;
|
|
struct dev_input *inp;
|
|
|
|
for(i=0; i<16; i++) {
|
|
if(diff & bit) {
|
|
inp = sb->evqueue + sb->evq_wr;
|
|
sb->evq_wr = (sb->evq_wr + 1) & (EVQUEUE_SZ - 1);
|
|
if(sb->evq_wr == sb->evq_rd) {
|
|
/* overflow, drop the oldest event */
|
|
sb->evq_rd = (sb->evq_rd + 1) & (EVQUEUE_SZ - 1);
|
|
}
|
|
|
|
inp->type = INP_BUTTON;
|
|
inp->idx = i;
|
|
inp->val = sb->keystate & bit ? 1 : 0;
|
|
}
|
|
bit <<= 1;
|
|
}
|
|
}
|
|
|
|
static char *memstr(char *buf, int len, const char *str)
|
|
{
|
|
int i, slen = strlen(str);
|
|
for(i=0; i<len - slen; i++) {
|
|
if(memcmp(buf + i, str, slen) == 0) {
|
|
return buf + i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|