kopia lustrzana https://github.com/FreeSpacenav/spacenavd
merged the new serial device code, and deleted libsball and the old
inactive magellan code.pull/26/merge
rodzic
26c840da63
commit
486645527f
|
@ -1,5 +1,5 @@
|
|||
src = $(sort $(wildcard src/*.c) $(wildcard src/serial/*.c) $(wildcard src/magellan/*.c))
|
||||
hdr = $(wildcard src/*.h) $(wildcard src/serial/*.h) $(wildcard src/magellan/*.h)
|
||||
src = $(sort $(wildcard src/*.c))
|
||||
hdr = $(wildcard src/*.h)
|
||||
obj = $(src:.c=.o)
|
||||
dep = $(obj:.o=.d)
|
||||
bin = spacenavd
|
||||
|
|
628
src/dev_serial.c
628
src/dev_serial.c
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2007-2020 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
|
||||
|
@ -15,40 +15,648 @@ 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>
|
||||
#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 "serial/sball.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;
|
||||
int nbuttons;
|
||||
|
||||
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;
|
||||
|
||||
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(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);
|
||||
|
||||
|
||||
int open_dev_serial(struct device *dev)
|
||||
{
|
||||
if(!(dev->data = sball_open(dev->path))) {
|
||||
return -1;
|
||||
}
|
||||
dev->fd = sball_get_fd(dev->data);
|
||||
int fd, sz;
|
||||
char buf[128];
|
||||
struct sball *sb = 0;
|
||||
|
||||
if((fd = open(dev->path, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {
|
||||
fprintf(stderr, "sball_open: failed to open device: %s: %s\n", dev->path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!(sb = calloc(1, sizeof *sb))) {
|
||||
fprintf(stderr, "sball_open: failed to allocate sball object\n");
|
||||
goto err;
|
||||
}
|
||||
dev->data = sb;
|
||||
dev->fd = sb->fd = fd;
|
||||
dev->close = close_dev_serial;
|
||||
dev->read = read_dev_serial;
|
||||
return 0;
|
||||
|
||||
stty_save(sb);
|
||||
|
||||
if(stty_sball(sb) == -1) {
|
||||
goto err;
|
||||
}
|
||||
write(fd, "\r@RESET\r", 8);
|
||||
|
||||
if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0 && strstr(buf, "\r@1")) {
|
||||
/* we got a response, so it's a spaceball */
|
||||
make_printable(buf, sz);
|
||||
printf("Spaceball detected: %s\n", buf);
|
||||
|
||||
sb->nbuttons = guess_num_buttons(buf);
|
||||
sb->keymask = 0xffff >> (16 - sb->nbuttons);
|
||||
printf("%d buttons\n", sb->nbuttons);
|
||||
|
||||
/* 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);
|
||||
printf("Magellan SpaceMouse detected:\n%s\n", buf);
|
||||
|
||||
sb->nbuttons = guess_num_buttons(buf);
|
||||
sb->keymask = 0xffff >> (16 - sb->nbuttons);
|
||||
printf("%d buttons\n", sb->nbuttons);
|
||||
|
||||
/* set 3D mode, not-dominant-axis, pass through motion and button packets */
|
||||
write(fd, "m3\r", 3);
|
||||
|
||||
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) {
|
||||
sball_close(dev->data);
|
||||
stty_restore(dev->data);
|
||||
close(dev->fd);
|
||||
}
|
||||
dev->data = 0;
|
||||
}
|
||||
|
||||
static int read_dev_serial(struct device *dev, struct dev_input *inp)
|
||||
{
|
||||
if(!dev->data || !sball_get_input(dev->data, 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("sball_open: 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("sball_open: 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;
|
||||
|
||||
/*printf("magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/
|
||||
|
||||
switch(id) {
|
||||
case 'd':
|
||||
if(len != 24) {
|
||||
fprintf(stderr, "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;
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "magellan error: illegal command: %c%c\n", data[1], data[2]);
|
||||
} else if(data[0] == 2) {
|
||||
fprintf(stderr, "magellan error: framing error\n");
|
||||
} else {
|
||||
fprintf(stderr, "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;
|
||||
|
||||
/* 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:
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "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) {
|
||||
fprintf(stderr, "sball: invalid sb4k key packet, expected 2 bytes, got: %d\n", len);
|
||||
return -1;
|
||||
}
|
||||
/* spaceball 4000 key packet */
|
||||
if(!(sb->flags & SB4000)) {
|
||||
printf("Switching to spaceball 4000flx/5000flx-a mode (12 buttons) \n");
|
||||
sb->flags |= SB4000;
|
||||
sb->nbuttons = 12; /* might have guessed 8 before */
|
||||
sb->keymask = 0xfff;
|
||||
}
|
||||
/* 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':
|
||||
fprintf(stderr, "sball: error:");
|
||||
for(i=0; i<len; i++) {
|
||||
if(isprint((int)data[i])) {
|
||||
fprintf(stderr, " %c", data[i]);
|
||||
} else {
|
||||
fprintf(stderr, " %02xh", (unsigned int)data[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'M': /* ignore MSS responses */
|
||||
case '?': /* ignore unrecognized command errors */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* DEBUG */
|
||||
fprintf(stderr, "sball: got '%c' packet:", (char)id);
|
||||
for(i=0; i<len; i++) {
|
||||
fprintf(stderr, " %02x", (unsigned int)data[i]);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int guess_num_buttons(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) {
|
||||
return 2; /* spaceball 3003/3003C */
|
||||
}
|
||||
if(minor == 43 || minor == 45) {
|
||||
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.
|
||||
*/
|
||||
return 8; /* spaceball 1003/2003/2003c */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(strstr(verstr, "MAGELLAN")) {
|
||||
return 9; /* magellan spacemouse */
|
||||
}
|
||||
|
||||
if(strstr(verstr, "SPACEBALL")) {
|
||||
return 12; /* spaceball 5000 */
|
||||
}
|
||||
|
||||
if(strstr(verstr, "CadMan")) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
fprintf(stderr, "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(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);
|
||||
|
||||
inp->type = INP_BUTTON;
|
||||
inp->idx = i;
|
||||
inp->val = sb->keystate & bit ? 1 : 0;
|
||||
}
|
||||
bit <<= 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
/*
|
||||
serialmagellan - decoding serial magellan spaceball data.
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef SERIAL_CONSTANTS_H_
|
||||
#define SERIAL_CONSTANTS_H_
|
||||
|
||||
#define MAXPACKETSIZE 16
|
||||
#define VERSION_STRING_MAX 512
|
||||
#define DEVICE_NAME_MAX 64
|
||||
#define MAXREADSIZE 512
|
||||
|
||||
#endif
|
|
@ -1,423 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "magellan/smag.h"
|
||||
#include "magellan/smag_comm.h"
|
||||
#include "magellan/smag_event.h"
|
||||
#include "magellan/serialconstants.h"
|
||||
|
||||
static void gen_disp_events(int *newval);
|
||||
static void proc_disp_packet(void);
|
||||
static void gen_button_event(int button, int new_state);
|
||||
static void read_copy(void);
|
||||
static void proc_disp_packet(void);
|
||||
static void proc_bn_k_packet(void);
|
||||
static void proc_bn_c_packet(void);
|
||||
static void proc_bn_n_packet(void);
|
||||
static void proc_bn_q_packet(void);
|
||||
static void clean_input();
|
||||
|
||||
|
||||
static int dev_fd;
|
||||
|
||||
struct input_struct {
|
||||
char rbuf[MAXREADSIZE];
|
||||
int rbuf_sz;
|
||||
char packet_buf[MAXPACKETSIZE];
|
||||
int packet_buf_pos;
|
||||
struct smag_event *evhead;
|
||||
struct smag_event *evtail;
|
||||
} input;
|
||||
|
||||
static int first_byte_parity[16] = {
|
||||
0xE0, 0xA0, 0xA0, 0x60, 0xA0, 0x60, 0x60, 0xA0,
|
||||
0x90, 0x50, 0x50, 0x90, 0xD0, 0x90, 0x90, 0x50
|
||||
};
|
||||
|
||||
static int second_byte_parity[64] = {
|
||||
0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40,
|
||||
0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80,
|
||||
0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80,
|
||||
0x80, 0x40, 0x40, 0x80, 0xC0, 0x80, 0x80, 0x40,
|
||||
0xC0, 0x80, 0x80, 0x40, 0x80, 0x40, 0x40, 0x80,
|
||||
0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40,
|
||||
0x80, 0x40, 0x40, 0x80, 0x40, 0x80, 0x80, 0x40,
|
||||
0x40, 0x80, 0x80, 0x40, 0x80, 0x40, 0x00, 0x80
|
||||
};
|
||||
|
||||
void smag_init_device(int fd)
|
||||
{
|
||||
smag_write(fd, "", 0);
|
||||
smag_write(fd, "\r\rm0", 4);
|
||||
smag_write(fd, "pAA", 3);
|
||||
smag_write(fd, "q00", 3); /*default translation and rotation */
|
||||
smag_write(fd, "nM", 2); /*zero radius. 0-15 defaults to 13 */
|
||||
smag_write(fd, "z", 1); /*zero device */
|
||||
smag_write(fd, "c33", 3); /*set translation, rotation on and dominant axis off */
|
||||
smag_write(fd, "l2\r\0", 4);
|
||||
smag_write(fd, "\r\r", 2);
|
||||
smag_write(fd, "l300", 4);
|
||||
smag_write(fd, "b9", 2); /*these are beeps */
|
||||
smag_write(fd, "b9", 2);
|
||||
|
||||
usleep(SMAG_DELAY_USEC);
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
clean_input();
|
||||
}
|
||||
|
||||
static void read_copy(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<input.rbuf_sz; i++) {
|
||||
if(input.rbuf[i] == '\n' || input.rbuf[i] == '\r') {
|
||||
input.packet_buf[input.packet_buf_pos] = 0; /* terminate string */
|
||||
|
||||
if(input.packet_buf[0] == 'd' && input.packet_buf_pos == 15) {
|
||||
proc_disp_packet();
|
||||
} else if(input.packet_buf[0] == 'k' && input.packet_buf_pos == 4) {
|
||||
proc_bn_k_packet();
|
||||
} else if(input.packet_buf[0] == 'c' && input.packet_buf_pos == 3) {
|
||||
proc_bn_c_packet();
|
||||
} else if(input.packet_buf[0] == 'n' && input.packet_buf_pos == 2) {
|
||||
proc_bn_n_packet();
|
||||
} else if(input.packet_buf[0] == 'q' && input.packet_buf_pos == 3) {
|
||||
proc_bn_q_packet();
|
||||
} else {
|
||||
fprintf(stderr, "unknown packet %s\n", input.packet_buf);
|
||||
}
|
||||
input.packet_buf_pos = 0;
|
||||
} else {
|
||||
input.packet_buf[input.packet_buf_pos] = input.rbuf[i];
|
||||
input.packet_buf_pos++;
|
||||
if(input.packet_buf_pos == MAXPACKETSIZE) {
|
||||
input.packet_buf_pos = 0;
|
||||
fprintf(stderr, "packet buffer overrun\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int open_smag(const char *devfile)
|
||||
{
|
||||
if((dev_fd = smag_open_device(devfile)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
smag_set_port_magellan(dev_fd);
|
||||
smag_init_device(dev_fd);
|
||||
clean_input();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int close_smag()
|
||||
{
|
||||
smag_write(dev_fd, "l000", 4);
|
||||
close(dev_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read_smag(struct dev_input *inp)
|
||||
{
|
||||
/*need to return 1 if we fill in inp or 0 if no events */
|
||||
struct smag_event *ev;
|
||||
|
||||
input.rbuf_sz = smag_read(dev_fd, input.rbuf, MAXREADSIZE);
|
||||
if(input.rbuf_sz > 0) {
|
||||
read_copy();
|
||||
}
|
||||
ev = input.evhead;
|
||||
if(ev) {
|
||||
input.evhead = input.evhead->next;
|
||||
|
||||
*inp = ev->data;
|
||||
free_event(ev);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_fd_smag()
|
||||
{
|
||||
return dev_fd;
|
||||
}
|
||||
|
||||
void get_version_string(int fd, char *buf, int sz)
|
||||
{
|
||||
int bytesrd;
|
||||
char tmpbuf[MAXREADSIZE];
|
||||
|
||||
smag_write(fd, "\r\rm0", 4);
|
||||
smag_write(fd, "", 0);
|
||||
smag_write(fd, "\r\rm0", 4);
|
||||
smag_write(fd, "c03", 3);
|
||||
smag_write(fd, "z", 1);
|
||||
smag_write(fd, "Z", 1);
|
||||
smag_write(fd, "l000", 4);
|
||||
usleep(SMAG_DELAY_USEC);
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
clean_input();
|
||||
smag_write(fd, "vQ", 2);
|
||||
|
||||
bytesrd = smag_read(fd, tmpbuf, MAXREADSIZE);
|
||||
if(bytesrd > 0 && bytesrd < sz) {
|
||||
strcpy(buf, tmpbuf);
|
||||
}
|
||||
clean_input();
|
||||
}
|
||||
|
||||
|
||||
static void gen_disp_events(int *newval)
|
||||
{
|
||||
int i, pending;
|
||||
static int oldval[6] = {0, 0, 0, 0, 0, 0};
|
||||
struct smag_event *newev;
|
||||
|
||||
pending = 0;
|
||||
for(i=0; i<6; i++) {
|
||||
if(newval[i] == oldval[i]) {
|
||||
continue;
|
||||
}
|
||||
oldval[i] = newval[i];
|
||||
|
||||
newev = alloc_event();
|
||||
if(newev) {
|
||||
newev->data.type = INP_MOTION;
|
||||
newev->data.idx = i;
|
||||
newev->data.val = newval[i];
|
||||
newev->next = 0;
|
||||
|
||||
if(input.evhead) {
|
||||
input.evtail->next = newev;
|
||||
input.evtail = newev;
|
||||
} else
|
||||
input.evhead = input.evtail = newev;
|
||||
pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(pending) {
|
||||
newev = alloc_event();
|
||||
if(newev) {
|
||||
newev->data.type = INP_FLUSH;
|
||||
newev->next = 0;
|
||||
}
|
||||
|
||||
if(input.evhead) {
|
||||
input.evtail->next = newev;
|
||||
input.evtail = newev;
|
||||
} else {
|
||||
input.evhead = input.evtail = newev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_disp_packet(void)
|
||||
{
|
||||
int i, last_bytes, offset, values[6];
|
||||
short int accum_last, number, accum_last_adj;
|
||||
|
||||
accum_last = offset = 0;
|
||||
|
||||
for(i=1; i<13; i+=2) {
|
||||
/*first byte check */
|
||||
unsigned char low, up;
|
||||
|
||||
low = input.packet_buf[i] & 0x0F;
|
||||
up = input.packet_buf[i] & 0xF0;
|
||||
if(up != first_byte_parity[low]) {
|
||||
fprintf(stderr, "bad first packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*second byte check */
|
||||
low = input.packet_buf[i + 1] & 0x3F;
|
||||
up = input.packet_buf[i + 1] & 0xC0;
|
||||
if(up != second_byte_parity[low]) {
|
||||
fprintf(stderr, "bad second packet\n");
|
||||
return;
|
||||
}
|
||||
|
||||
number = (short int)((input.packet_buf[i] << 6 & 0x03C0) | (input.packet_buf[i + 1] & 0x3F));
|
||||
if(number > 512) {
|
||||
number -= 1024;
|
||||
}
|
||||
accum_last += number;
|
||||
|
||||
if(number < 0) {
|
||||
offset += ((int)(number + 1) / 64) - 1;
|
||||
} else {
|
||||
offset += (int)number / 64;
|
||||
}
|
||||
/*printf("%8i ", number); */
|
||||
values[(i + 1) / 2 - 1] = number;
|
||||
}
|
||||
|
||||
/*last byte of packet is a sum of 6 numbers and a factor of 64. use as a packet check.
|
||||
still not sure what the second to last byte is for. */
|
||||
accum_last_adj = accum_last & 0x003F;
|
||||
accum_last_adj += offset;
|
||||
if(accum_last_adj < 0) {
|
||||
accum_last_adj += 64;
|
||||
}
|
||||
if(accum_last_adj > 63) {
|
||||
accum_last_adj -= 64;
|
||||
}
|
||||
|
||||
last_bytes = (short int)(input.packet_buf[14] & 0x3F);
|
||||
|
||||
if(accum_last_adj != last_bytes) {
|
||||
printf(" bad packet\n");
|
||||
return;
|
||||
}
|
||||
gen_disp_events(values);
|
||||
return;
|
||||
}
|
||||
|
||||
static void gen_button_event(int button, int new_state)
|
||||
{
|
||||
struct smag_event *newev = alloc_event();
|
||||
|
||||
if(!newev) {
|
||||
return;
|
||||
}
|
||||
|
||||
newev->data.type = INP_BUTTON;
|
||||
newev->data.idx = button;
|
||||
newev->data.val = new_state;
|
||||
newev->next = 0;
|
||||
|
||||
if(input.evhead) {
|
||||
input.evtail->next = newev;
|
||||
input.evtail = newev;
|
||||
} else {
|
||||
input.evhead = input.evtail = newev;
|
||||
}
|
||||
}
|
||||
|
||||
static void proc_bn_k_packet(void)
|
||||
{
|
||||
static char old_state[5] = { 0, 0, 0, 0, 0 };
|
||||
|
||||
if(input.packet_buf[1] != old_state[1]) {
|
||||
if((input.packet_buf[1] & 0x01) != (old_state[1] & 0x01)) {
|
||||
gen_button_event(0, input.packet_buf[1] & 0x01);
|
||||
}
|
||||
if((input.packet_buf[1] & 0x02) != (old_state[1] & 0x02)) {
|
||||
gen_button_event(1, input.packet_buf[1] & 0x02);
|
||||
}
|
||||
if((input.packet_buf[1] & 0x04) != (old_state[1] & 0x04)) {
|
||||
gen_button_event(2, input.packet_buf[1] & 0x04);
|
||||
}
|
||||
if((input.packet_buf[1] & 0x08) != (old_state[1] & 0x08)) {
|
||||
gen_button_event(3, input.packet_buf[1] & 0x08);
|
||||
}
|
||||
}
|
||||
|
||||
if(input.packet_buf[2] != old_state[2]) {
|
||||
if((input.packet_buf[2] & 0x01) != (old_state[2] & 0x01)) {
|
||||
gen_button_event(4, input.packet_buf[2] & 0x01);
|
||||
}
|
||||
if((input.packet_buf[2] & 0x02) != (old_state[2] & 0x02)) {
|
||||
gen_button_event(5, input.packet_buf[2] & 0x02);
|
||||
}
|
||||
if((input.packet_buf[2] & 0x04) != (old_state[2] & 0x04)) {
|
||||
gen_button_event(6, input.packet_buf[2] & 0x04);
|
||||
}
|
||||
if((input.packet_buf[2] & 0x08) != (old_state[2] & 0x08)) {
|
||||
gen_button_event(7, input.packet_buf[2] & 0x08);
|
||||
}
|
||||
}
|
||||
|
||||
/*skipping asterisk button. asterisk function come in through other packets. */
|
||||
/*magellan plus has left and right (10, 11) buttons not magellan classic */
|
||||
/*not sure if we need to filter out lower button events for magellan classic */
|
||||
|
||||
if(input.packet_buf[3] != old_state[3]) {
|
||||
/*
|
||||
if (input.packet_buf[3] & 0x01)
|
||||
printf("button asterisk ");
|
||||
*/
|
||||
if((input.packet_buf[3] & 0x02) != (old_state[3] & 0x02)) {
|
||||
gen_button_event(8, input.packet_buf[3] & 0x02); /*left button */
|
||||
}
|
||||
if((input.packet_buf[3] & 0x04) != (old_state[3] & 0x04)) {
|
||||
gen_button_event(9, input.packet_buf[3] & 0x04); /*right button */
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(old_state, input.packet_buf);
|
||||
}
|
||||
|
||||
static void proc_bn_c_packet(void)
|
||||
{
|
||||
/*these are implemented at device and these signals are to keep the driver in sync */
|
||||
if(input.packet_buf[1] & 0x02) {
|
||||
printf("translation is on ");
|
||||
} else {
|
||||
printf("translation is off ");
|
||||
}
|
||||
|
||||
if(input.packet_buf[1] & 0x01) {
|
||||
printf("rotation is on ");
|
||||
} else {
|
||||
printf("rotation is off ");
|
||||
}
|
||||
|
||||
if(input.packet_buf[1] & 0x04) {
|
||||
printf("dominant axis is on ");
|
||||
} else {
|
||||
printf("dominant axis is off ");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
/*printf("%s\n", input.packet_buf); */
|
||||
}
|
||||
|
||||
static void proc_bn_n_packet(void)
|
||||
{
|
||||
int radius;
|
||||
|
||||
radius = (int)input.packet_buf[1] & 0x0F;
|
||||
printf("zero radius set to %i\n", radius);
|
||||
}
|
||||
|
||||
static void proc_bn_q_packet(void)
|
||||
{
|
||||
/* this has no effect on the device numbers. Driver is to implement any scale of numbers */
|
||||
int rotation, translation;
|
||||
|
||||
rotation = (int)input.packet_buf[1] & 0x07;
|
||||
translation = (int)input.packet_buf[2] & 0x07;
|
||||
printf("rotation = %i translation = %i\n", rotation, translation);
|
||||
}
|
||||
|
||||
|
||||
static void clean_input(void)
|
||||
{
|
||||
memset(input.rbuf, 0x00, MAXREADSIZE);
|
||||
input.rbuf_sz = 0;
|
||||
memset(input.packet_buf, 0x00, MAXPACKETSIZE);
|
||||
input.packet_buf_pos = 0;
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 "event.h"
|
||||
|
||||
int open_smag(const char *devfile);
|
||||
int close_smag();
|
||||
int read_smag(struct dev_input *inp);
|
||||
int get_fd_smag();
|
||||
|
||||
void get_version_string(int fd, char *buf, int sz);
|
||||
|
||||
void smag_init_device(int fd);
|
||||
void clearInput(void);
|
||||
void readCopy(void);
|
|
@ -1,153 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <termios.h>
|
||||
#include "magellan/smag_comm.h"
|
||||
|
||||
|
||||
int smag_open_device(const char *fname)
|
||||
{
|
||||
return open(fname, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY);
|
||||
}
|
||||
|
||||
int smag_set_port_spaceball(int fd)
|
||||
{
|
||||
int status;
|
||||
struct termios term;
|
||||
|
||||
if(tcgetattr(fd, &term) == -1) {
|
||||
perror("error tcgetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
term.c_cflag = CREAD | CS8 | CLOCAL | HUPCL;
|
||||
term.c_iflag |= IGNBRK | IGNPAR;
|
||||
term.c_oflag = 0;
|
||||
term.c_lflag = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_cc[VTIME] = 0;
|
||||
|
||||
cfsetispeed(&term, 9600);
|
||||
cfsetospeed(&term, 9600);
|
||||
if(tcsetattr(fd, TCSANOW, &term) == -1) {
|
||||
perror("error tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(ioctl(fd, TIOCMGET, &status) == -1) {
|
||||
perror("error TIOMCGET");
|
||||
return -1;
|
||||
}
|
||||
status |= TIOCM_DTR;
|
||||
status |= TIOCM_RTS;
|
||||
if(ioctl(fd, TIOCMSET, &status) == -1) {
|
||||
perror("error TIOCMSET");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smag_set_port_magellan(int fd)
|
||||
{
|
||||
int status;
|
||||
struct termios term;
|
||||
|
||||
if(tcgetattr(fd, &term) == -1) {
|
||||
perror("error tcgetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
term.c_cflag = CS8 | CSTOPB | CRTSCTS | CREAD | HUPCL | CLOCAL;
|
||||
term.c_iflag |= IGNBRK | IGNPAR;
|
||||
term.c_oflag = 0;
|
||||
term.c_lflag = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_cc[VTIME] = 0;
|
||||
|
||||
cfsetispeed(&term, 9600);
|
||||
cfsetospeed(&term, 9600);
|
||||
if(tcsetattr(fd, TCSANOW, &term) == -1) {
|
||||
perror("error tcsetattr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(ioctl(fd, TIOCMGET, &status) == -1) {
|
||||
perror("error TIOCMGET");
|
||||
return -1;
|
||||
}
|
||||
status |= TIOCM_DTR;
|
||||
status |= TIOCM_RTS;
|
||||
if(ioctl(fd, TIOCMSET, &status) == -1) {
|
||||
perror("error TIOCMSET");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LONG_DELAY 150000
|
||||
|
||||
void smag_write(int fd, const char *buf, int sz)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<sz; i++) {
|
||||
write(fd, buf + i, 1);
|
||||
usleep(SMAG_DELAY_USEC);
|
||||
}
|
||||
write(fd, "\r", 1);
|
||||
usleep(LONG_DELAY);
|
||||
}
|
||||
|
||||
int smag_read(int fd, char *buf, int sz)
|
||||
{
|
||||
int bytesrd = read(fd, buf, sz - 1);
|
||||
if(bytesrd < 1) {
|
||||
return 0;
|
||||
}
|
||||
buf[bytesrd] = 0;
|
||||
return bytesrd;
|
||||
}
|
||||
|
||||
int smag_wait_read(int fd, char *buf, int sz, int wait_sec)
|
||||
{
|
||||
int res;
|
||||
fd_set set;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&set);
|
||||
FD_SET(fd, &set);
|
||||
|
||||
tv.tv_sec = wait_sec;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
do {
|
||||
res = select(fd + 1, &set, 0, 0, &tv);
|
||||
} while(res == -1 && errno == EINTR);
|
||||
|
||||
return res == -1 ? -1 : smag_read(fd, buf, sz);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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/>.
|
||||
*/
|
||||
#ifndef SMAG_COMM_H_
|
||||
#define SMAG_COMM_H_
|
||||
|
||||
#define SMAG_DELAY_USEC 2000
|
||||
|
||||
int smag_open_device(const char *fname);
|
||||
int smag_set_port_spaceball(int fd);
|
||||
int smag_set_port_magellan(int fd);
|
||||
void smag_write(int fd, const char *buf, int sz);
|
||||
int smag_read(int fd, char *buf, int sz);
|
||||
int smag_wait_read(int fd, char *buf, int sz, int wait_sec);
|
||||
|
||||
#endif
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "magellan/smag.h"
|
||||
#include "magellan/smag_detect.h"
|
||||
#include "magellan/serialconstants.h"
|
||||
#include "magellan/smag_comm.h"
|
||||
|
||||
/*swap out /r for /n for string printing*/
|
||||
static void make_printable(char *str)
|
||||
{
|
||||
while(*str) {
|
||||
if(*str == '\r') {
|
||||
*str = '\n';
|
||||
}
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
int smag_detect(const char *fname, char *buf, int sz)
|
||||
{
|
||||
int fd, bytesrd, pos;
|
||||
char tmpbuf[MAXREADSIZE];
|
||||
|
||||
if((fd = smag_open_device(fname)) == -1) {
|
||||
fprintf(stderr, "%s: couldn't open device file: %s\n", __func__, fname);
|
||||
return -1;
|
||||
}
|
||||
if(smag_set_port_spaceball(fd) == -1) {
|
||||
close(fd);
|
||||
fprintf(stderr, "%s: couldn't setup port\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* first look for spaceball. should have data after open and port setup.
|
||||
* I was hoping that using the select inside serialWaitRead would allow me
|
||||
* to get rid of the following sleep. Removing the sleep causes port to freeze.
|
||||
*/
|
||||
sleep(1);
|
||||
|
||||
bytesrd = 0;
|
||||
pos = 0;
|
||||
|
||||
while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) {
|
||||
bytesrd += pos;
|
||||
}
|
||||
if(bytesrd > 0) {
|
||||
smag_write(fd, "hm", 2);
|
||||
while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) {
|
||||
bytesrd += pos;
|
||||
}
|
||||
|
||||
smag_write(fd, "\"", 1);
|
||||
while((pos = smag_wait_read(fd, tmpbuf + bytesrd, MAXREADSIZE - bytesrd, 1)) > 0) {
|
||||
bytesrd += pos;
|
||||
}
|
||||
|
||||
make_printable(tmpbuf);
|
||||
strncpy(buf, tmpbuf, sz);
|
||||
if(bytesrd < sz) {
|
||||
fprintf(stderr, "%s: buffer overrun\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*now if we are here we don't have a spaceball and now we need to check for a magellan */
|
||||
close(fd);
|
||||
pos = 0;
|
||||
|
||||
if((fd = smag_open_device(fname)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if(smag_set_port_magellan(fd) == -1) {
|
||||
return -1;
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
smag_init_device(fd);
|
||||
get_version_string(fd, tmpbuf, MAXREADSIZE);
|
||||
|
||||
make_printable(tmpbuf);
|
||||
strncpy(buf, tmpbuf, sz);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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/>.
|
||||
*/
|
||||
#ifndef SMAG_DETECT_H_
|
||||
#define SMAG_DETECT_H_
|
||||
|
||||
int smag_detect(const char *fname, char *buf, int sz);
|
||||
|
||||
#endif /* SMAG_DETECT_H_ */
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 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 <stdlib.h>
|
||||
#include "smag_event.h"
|
||||
|
||||
static int evpool_size;
|
||||
static struct smag_event *ev_free_list;
|
||||
|
||||
struct smag_event *alloc_event(void)
|
||||
{
|
||||
struct smag_event *ev;
|
||||
|
||||
if(ev_free_list) {
|
||||
ev = ev_free_list;
|
||||
ev_free_list = ev->next;
|
||||
} else {
|
||||
if((ev = malloc(sizeof *ev))) {
|
||||
evpool_size++;
|
||||
}
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
|
||||
void free_event(struct smag_event *ev)
|
||||
{
|
||||
if(evpool_size > 512) {
|
||||
free(ev);
|
||||
evpool_size--;
|
||||
} else {
|
||||
ev->next = ev_free_list;
|
||||
ev_free_list = ev;
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
serial magellan device support for spacenavd
|
||||
|
||||
Copyright (C) 2012 John Tsiombikas <nuclear@member.fsf.org>
|
||||
Copyright (C) 2010 Thomas Anderson <ta@nextgenengineering.com>
|
||||
|
||||
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/>.
|
||||
*/
|
||||
#ifndef SMAG_EVENT_H_
|
||||
#define SMAG_EVENT_H_
|
||||
|
||||
#include "event.h"
|
||||
|
||||
struct smag_event {
|
||||
struct dev_input data;
|
||||
struct smag_event *next;
|
||||
};
|
||||
|
||||
struct smag_event *alloc_event(void);
|
||||
void free_event(struct smag_event *ev);
|
||||
|
||||
#endif /* SMAG_EVENT_H_ */
|
|
@ -1,747 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 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/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright 1997-2001 John E. Stone (j.stone@acm.org)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _POSIX_SOURCE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sball.h"
|
||||
#include "sballserial.h"
|
||||
|
||||
struct event {
|
||||
struct dev_input data;
|
||||
struct event *next;
|
||||
};
|
||||
|
||||
static struct event *ev_free_list;
|
||||
static int evpool_size;
|
||||
|
||||
static struct event *alloc_event(void);
|
||||
static void free_event(struct event *ev);
|
||||
|
||||
|
||||
typedef struct {
|
||||
SBallCommHandle commhandle;
|
||||
unsigned char buf[256];
|
||||
char resetstring[256];
|
||||
int bufpos; /* current char position in packet buffer */
|
||||
int packtype; /* what kind of packet is it */
|
||||
int packlen; /* how many bytes do we ultimately expect? */
|
||||
int escapedchar; /* if set, we're processing an escape sequence */
|
||||
int erroroccured; /* if set, we've received an error packet or packets */
|
||||
int resetoccured; /* if set, ball was reset, so have to reinitialize it */
|
||||
int spaceball4000; /* if set, its a Spaceball 4000 */
|
||||
int leftymode4000; /* if set, Spaceball 4000 in "lefty" orientation */
|
||||
int trans[3]; /* last translational data received */
|
||||
int rot[3]; /* last rotational data received */
|
||||
int buttons; /* current button status */
|
||||
int timer; /* time since last packet was received */
|
||||
int usenullregion; /* software-implemented null region flag */
|
||||
int nulltrans[3]; /* translational null region values */
|
||||
int nullrot[3]; /* rotational null region values */
|
||||
|
||||
/* event list added for spacenavd integration */
|
||||
struct event *evhead, *evtail;
|
||||
} sballhandle;
|
||||
|
||||
|
||||
static void generate_motion_events(sballhandle *handle, int *prev_val, int *new_val, int timer);
|
||||
static void generate_button_events(sballhandle *handle, int prevstate, int newstate);
|
||||
|
||||
|
||||
/* Spaceball 1003/2003 recommended initialization string. */
|
||||
|
||||
/* Newer documentation suggests eliminating several of these */
|
||||
|
||||
/* settings during initialization, leaving them at factory values. */
|
||||
static char *initstring = "CB\rNT\rFTp\rFRp\rP@r@r\rMSSV\rZ\rBcCcC\r";
|
||||
|
||||
/* Reset spaceball and ideally determine model */
|
||||
static void sball_hwreset(sballhandle * handle)
|
||||
{
|
||||
/* Reset some state variables back to zero */
|
||||
handle->spaceball4000 = 0; /* re-determine which type it is */
|
||||
handle->leftymode4000 = 0; /* re-determine if its in lefty mode */
|
||||
|
||||
if(!handle->resetoccured) {
|
||||
#if defined(DEBUG)
|
||||
printf("Sending reset command to spaceball...\n");
|
||||
#endif
|
||||
handle->resetoccured = 1;
|
||||
sball_comm_write(handle->commhandle, "@\r"); /* force reset */
|
||||
}
|
||||
#if 0
|
||||
/* give the spaceball time to reset itself */
|
||||
sleep(2);
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG)
|
||||
printf("Sending initialization sequence to spaceball...\n");
|
||||
#endif
|
||||
|
||||
sball_comm_write(handle->commhandle, initstring); /* do remaining init */
|
||||
}
|
||||
|
||||
|
||||
SBallHandle sball_open(const char *sballname)
|
||||
{
|
||||
sballhandle *handle;
|
||||
|
||||
if(sballname == NULL)
|
||||
return NULL;
|
||||
|
||||
handle = (sballhandle *) malloc(sizeof(sballhandle));
|
||||
if(handle == NULL)
|
||||
return NULL;
|
||||
|
||||
/* clear all values in sballhandle to 0 */
|
||||
memset(handle, 0, sizeof(sballhandle));
|
||||
handle->packlen = 1;
|
||||
handle->resetoccured = 0;
|
||||
|
||||
if(sball_comm_open(sballname, &handle->commhandle) == -1) {
|
||||
free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sball_hwreset(handle);
|
||||
|
||||
return handle; /* successfull open */
|
||||
}
|
||||
|
||||
|
||||
int sball_close(SBallHandle voidhandle)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
if(handle == NULL)
|
||||
return -1;
|
||||
|
||||
sball_comm_close(&handle->commhandle);
|
||||
free(handle);
|
||||
return 0; /* successfull close */
|
||||
}
|
||||
|
||||
|
||||
static int sball_update(SBallHandle voidhandle)
|
||||
{
|
||||
int i, num, packs;
|
||||
|
||||
unsigned char rawbuf[1024];
|
||||
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
if(handle == NULL)
|
||||
return -1;
|
||||
|
||||
packs = 0; /* no packs received yet */
|
||||
|
||||
num = sball_comm_read(handle->commhandle, (char *)rawbuf, 1023);
|
||||
|
||||
if(num > 0) {
|
||||
for(i = 0; i < num; i++) {
|
||||
|
||||
|
||||
/* process potentially occuring escaped character sequences */
|
||||
if(rawbuf[i] == '^') {
|
||||
if(!handle->escapedchar) {
|
||||
handle->escapedchar = 1;
|
||||
continue; /* eat the escape character from buffer */
|
||||
}
|
||||
}
|
||||
|
||||
if(handle->escapedchar) {
|
||||
handle->escapedchar = 0;
|
||||
|
||||
switch(rawbuf[i]) {
|
||||
case '^': /* leave char in buffer unchanged */
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
case 'S':
|
||||
case 'M':
|
||||
rawbuf[i] &= 0x1F; /* convert character to unescaped form */
|
||||
break;
|
||||
|
||||
default:
|
||||
#if defined(DEBUG)
|
||||
printf("\nGot a bad escape sequence! 0x%02x", rawbuf[i]);
|
||||
if(isprint(rawbuf[i]))
|
||||
printf(" (%c)", rawbuf[i]);
|
||||
else
|
||||
printf(" (unprintable)");
|
||||
printf("\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* figure out what kind of packet we received */
|
||||
if(handle->bufpos == 0) {
|
||||
switch(rawbuf[i]) {
|
||||
case 'D': /* Displacement packet */
|
||||
handle->packtype = 'D';
|
||||
handle->packlen = 16; /* D packets are 15 bytes long */
|
||||
break;
|
||||
|
||||
case 'K': /* Button/Key packet */
|
||||
handle->packtype = 'K';
|
||||
handle->packlen = 4; /* K packets are 3 bytes long */
|
||||
break;
|
||||
|
||||
case '.': /* Spaceball 4000 FLX "advanced" button press event */
|
||||
handle->packtype = '.';
|
||||
handle->packlen = 4; /* . packets are 3 bytes long */
|
||||
break;
|
||||
|
||||
case 'C': /* Communications mode packet */
|
||||
handle->packtype = 'C';
|
||||
handle->packlen = 4;
|
||||
break;
|
||||
|
||||
case 'F': /* Spaceball sensitization mode packet */
|
||||
handle->packtype = 'F';
|
||||
handle->packlen = 4;
|
||||
break;
|
||||
|
||||
case 'M': /* Movement mode packet */
|
||||
handle->packtype = 'M';
|
||||
handle->packlen = 5;
|
||||
break;
|
||||
|
||||
case 'N': /* Null region packet */
|
||||
handle->packtype = 'N';
|
||||
handle->packlen = 3;
|
||||
break;
|
||||
|
||||
case 'P': /* Update rate packet */
|
||||
handle->packtype = 'P';
|
||||
handle->packlen = 6;
|
||||
break;
|
||||
|
||||
case '\v': /* XON at poweron */
|
||||
handle->packtype = '\v';
|
||||
handle->packlen = 1;
|
||||
break;
|
||||
|
||||
case '\n': /* carriage return at poweron */
|
||||
case '\r': /* carriage return at poweron */
|
||||
handle->packtype = '\r';
|
||||
handle->packlen = 1;
|
||||
break;
|
||||
|
||||
case '@': /* Spaceball Hard/Soft Reset packet */
|
||||
handle->resetoccured = 1;
|
||||
handle->packtype = '@';
|
||||
handle->packlen = 62; /* Resets aren't longer than 62 chars */
|
||||
break;
|
||||
|
||||
case 'E': /* Error packet */
|
||||
handle->packtype = 'E';
|
||||
handle->packlen = 8; /* E packets are up to 7 bytes long */
|
||||
break;
|
||||
|
||||
case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
|
||||
handle->packtype = 'Z';
|
||||
handle->packlen = 14; /* Z packets are hardware dependent */
|
||||
break;
|
||||
|
||||
default: /* Unknown packet! */
|
||||
#if defined(DEBUG)
|
||||
printf("\nUnknown packet (1): 0x%02x \n ", rawbuf[i]);
|
||||
printf(" char: ");
|
||||
if(isprint(rawbuf[i]))
|
||||
printf("%c", rawbuf[i]);
|
||||
else
|
||||
printf(" (unprintable)");
|
||||
printf("\n");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
handle->buf[handle->bufpos] = rawbuf[i];
|
||||
handle->bufpos++;
|
||||
|
||||
/* Reset packet processing */
|
||||
if(handle->packtype == '@') {
|
||||
if(rawbuf[i] != '\r')
|
||||
continue;
|
||||
else
|
||||
handle->packlen = handle->bufpos;
|
||||
}
|
||||
|
||||
/* Error packet processing */
|
||||
if(handle->packtype == 'E') {
|
||||
if(rawbuf[i] != '\r')
|
||||
continue;
|
||||
else
|
||||
handle->packlen = handle->bufpos;
|
||||
} else if(handle->bufpos != handle->packlen)
|
||||
continue;
|
||||
|
||||
switch(handle->packtype) {
|
||||
case 'D': /* ball displacement event */
|
||||
/* modified by John Tsiombikas for spacenavd integration */
|
||||
{
|
||||
unsigned int tx, ty, tz, rx, ry, rz;
|
||||
int i, prev_val[6], new_val[6];
|
||||
|
||||
/* number of 1/16ths of milliseconds since last */
|
||||
/* ball displacement packet */
|
||||
handle->timer = ((handle->buf[1]) << 8) | (handle->buf[2]);
|
||||
|
||||
tx = ((handle->buf[3]) << 8) | ((handle->buf[4]));
|
||||
ty = ((handle->buf[5]) << 8) | ((handle->buf[6]));
|
||||
tz = ((handle->buf[7]) << 8) | ((handle->buf[8]));
|
||||
rx = ((handle->buf[9]) << 8) | ((handle->buf[10]));
|
||||
ry = ((handle->buf[11]) << 8) | ((handle->buf[12]));
|
||||
rz = ((handle->buf[13]) << 8) | ((handle->buf[14]));
|
||||
|
||||
for(i=0; i<3; i++) {
|
||||
prev_val[i] = handle->trans[i];
|
||||
prev_val[i + 3] = handle->rot[i];
|
||||
}
|
||||
|
||||
new_val[0] = (((int)tx) << 16) >> 16;
|
||||
new_val[1] = (((int)ty) << 16) >> 16;
|
||||
new_val[2] = (((int)tz) << 16) >> 16;
|
||||
new_val[3] = (((int)rx) << 16) >> 16;
|
||||
new_val[4] = (((int)ry) << 16) >> 16;
|
||||
new_val[5] = (((int)rz) << 16) >> 16;
|
||||
|
||||
generate_motion_events(handle, prev_val, new_val, handle->timer);
|
||||
|
||||
for(i=0; i<3; i++) {
|
||||
handle->trans[i] = new_val[i];
|
||||
handle->rot[i] = new_val[i + 3];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'K': /* button press event */
|
||||
/* modified by John Tsiombikas for spacenavd integration */
|
||||
{
|
||||
int newstate;
|
||||
|
||||
/* Spaceball 2003A, 2003B, 2003 FLX, 3003 FLX, 4000 FLX */
|
||||
/* button packet. (4000 only for backwards compatibility) */
|
||||
/* The lowest 5 bits of the first byte are buttons 5-9 */
|
||||
/* Button '8' on a Spaceball 2003 is the rezero button */
|
||||
/* The lowest 4 bits of the second byte are buttons 1-4 */
|
||||
/* For Spaceball 2003, we'll map the buttons 1-7 normally */
|
||||
/* skip 8, as its a hardware "rezero button" on that device */
|
||||
/* and call the "pick" button "8". */
|
||||
/* On the Spaceball 3003, the "right" button also triggers */
|
||||
/* the "pick" bit. We OR the 2003/3003 rezero bits together */
|
||||
|
||||
/* if we have found a Spaceball 4000, then we ignore the 'K' */
|
||||
/* packets entirely, and only use the '.' packets. */
|
||||
if(handle->spaceball4000)
|
||||
break;
|
||||
|
||||
newstate = ((handle->buf[1] & 0x10) << 3) | /* 2003 pick button is "8" */
|
||||
((handle->buf[1] & 0x20) << 9) | /* 3003 rezero button */
|
||||
((handle->buf[1] & 0x08) << 11) | /* 2003 rezero button */
|
||||
((handle->buf[1] & 0x07) << 4) | /* 5,6,7 (2003/4000) */
|
||||
((handle->buf[2] & 0x30) << 8) | /* 3003 Left/Right buttons */
|
||||
((handle->buf[2] & 0x0F)); /* 1,2,3,4 (2003/4000) */
|
||||
|
||||
generate_button_events(handle, handle->buttons, newstate);
|
||||
handle->buttons = newstate;
|
||||
}
|
||||
break;
|
||||
|
||||
case '.': /* button press event (4000) */
|
||||
/* modified by John Tsiombikas for spacenavd integration */
|
||||
{
|
||||
int newstate;
|
||||
/* Spaceball 4000 FLX "expanded" button packet, with 12 buttons */
|
||||
|
||||
/* extra packet validity check, since we use this packet type */
|
||||
/* to override the 'K' button packets, and determine if its a */
|
||||
/* Spaceball 4000 or not... */
|
||||
if(handle->buf[3] != '\r') {
|
||||
break; /* if not terminated with a '\r', probably garbage */
|
||||
}
|
||||
|
||||
/* if we got a valid '.' packet, this must be a Spaceball 4000 */
|
||||
#if defined(DEBUG)
|
||||
if(!handle->spaceball4000)
|
||||
printf("\nDetected a Spaceball 4000 FLX\n");
|
||||
#endif
|
||||
handle->spaceball4000 = 1; /* Must be talking to a Spaceball 4000 */
|
||||
|
||||
/* Spaceball 4000 series "expanded" button press event */
|
||||
/* includes data for 12 buttons, and left/right orientation */
|
||||
newstate = (((~handle->buf[1]) & 0x20) << 10) | /* "left handed" mode */
|
||||
((handle->buf[1] & 0x1F) << 7) | /* 8,9,10,11,12 */
|
||||
((handle->buf[2] & 0x3F)) | /* 1,2,3,4,5,6 (4000) */
|
||||
((handle->buf[2] & 0x80) >> 1); /* 7 (4000) */
|
||||
|
||||
generate_button_events(handle, handle->buttons, newstate);
|
||||
handle->buttons = newstate;
|
||||
|
||||
#if defined(DEBUG)
|
||||
if(handle->leftymode4000 != ((handle->buf[1] & 0x20) == 0))
|
||||
printf("\nSpaceball 4000 mode changed to: %s\n",
|
||||
(((handle->buf[1] & 0x20) ==
|
||||
0) ? "left handed" : "right handed"));
|
||||
#endif
|
||||
/* set "lefty" orientation mode if "lefty bit" is _clear_ */
|
||||
if((handle->buf[1] & 0x20) == 0)
|
||||
handle->leftymode4000 = 1; /* left handed mode */
|
||||
else
|
||||
handle->leftymode4000 = 0; /* right handed mode */
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C': /* Communications mode packet */
|
||||
case 'F': /* Spaceball sensitization packet */
|
||||
case 'P': /* Spaceball update rate packet */
|
||||
case 'M': /* Spaceball movement mode packet */
|
||||
case 'N': /* Null region packet */
|
||||
case '\r': /* carriage return at poweron */
|
||||
case '\v': /* XON at poweron */
|
||||
/* eat and ignore these packets */
|
||||
break;
|
||||
|
||||
case '@': /* Reset packet */
|
||||
#ifdef DEBUG
|
||||
printf("Spaceball reset: ");
|
||||
for(j = 0; j < handle->packlen; j++) {
|
||||
if(isprint(handle->buf[j]))
|
||||
printf("%c", handle->buf[j]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
/* if we get a reset packet, we have to re-initialize */
|
||||
/* the device, and assume that its completely schizophrenic */
|
||||
/* at this moment, we must reset it again at this point */
|
||||
handle->resetoccured = 1;
|
||||
sball_hwreset(handle);
|
||||
break;
|
||||
|
||||
|
||||
case 'E': /* Error packet, hardware/software problem */
|
||||
handle->erroroccured++;
|
||||
#ifdef DEBUG
|
||||
printf("\nSpaceball Error!! ");
|
||||
printf("Error code: ");
|
||||
for(j = 0; j < handle->packlen; j++) {
|
||||
printf(" 0x%02x ", handle->buf[j]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
|
||||
/* We just ignore these... */
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
printf("Unknown packet (2): 0x%02x\n", handle->packtype);
|
||||
printf(" char: ");
|
||||
if(isprint(handle->packtype))
|
||||
printf("%c", handle->packtype);
|
||||
else
|
||||
printf(" (unprintable)");
|
||||
printf("\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
/* reset */
|
||||
handle->bufpos = 0;
|
||||
handle->packtype = 0;
|
||||
handle->packlen = 1;
|
||||
packs++;
|
||||
}
|
||||
}
|
||||
|
||||
return packs;
|
||||
}
|
||||
|
||||
|
||||
int sball_rezero(SBallHandle voidhandle)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
char outbuf[200];
|
||||
|
||||
if(handle == NULL)
|
||||
return -1;
|
||||
|
||||
sprintf(outbuf, "\rZ\r");
|
||||
sball_comm_write(handle->commhandle, outbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sball_init(SBallHandle voidhandle)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
/*char outbuf[200]; */
|
||||
|
||||
if(handle == NULL)
|
||||
return -1;
|
||||
|
||||
sball_update(handle);
|
||||
|
||||
#if 0
|
||||
sprintf(outbuf, "\r");
|
||||
sball_update(handle);
|
||||
sball_comm_write(handle->commhandle, outbuf);
|
||||
sball_rezero(handle);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void sball_set_nullregion(SBallHandle voidhandle,
|
||||
int nulltx, int nullty, int nulltz, int nullrx, int nullry, int nullrz)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
handle->nulltrans[0] = abs(nulltx);
|
||||
handle->nulltrans[1] = abs(nullty);
|
||||
handle->nulltrans[2] = abs(nulltz);
|
||||
|
||||
handle->nullrot[0] = abs(nullrx);
|
||||
handle->nullrot[1] = abs(nullry);
|
||||
handle->nullrot[2] = abs(nullrz);
|
||||
|
||||
handle->usenullregion = 1;
|
||||
}
|
||||
|
||||
|
||||
static int nullregion(int null, int val)
|
||||
{
|
||||
if(abs(val) > null) {
|
||||
return ((val > 0) ? (val - null) : (val + null));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sball_do_nullregion(SBallHandle voidhandle)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
handle->trans[0] = nullregion(handle->nulltrans[0], handle->trans[0]);
|
||||
handle->trans[1] = nullregion(handle->nulltrans[1], handle->trans[1]);
|
||||
handle->trans[2] = nullregion(handle->nulltrans[2], handle->trans[2]);
|
||||
handle->rot[0] = nullregion(handle->nullrot[0], handle->rot[0]);
|
||||
handle->rot[1] = nullregion(handle->nullrot[1], handle->rot[1]);
|
||||
handle->rot[2] = nullregion(handle->nullrot[2], handle->rot[2]);
|
||||
}
|
||||
|
||||
int sball_getstatus(SBallHandle voidhandle, int *tx, int *ty, int *tz,
|
||||
int *rx, int *ry, int *rz, int *buttons)
|
||||
{
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
int events;
|
||||
|
||||
if(handle == NULL)
|
||||
return -1;
|
||||
|
||||
events = sball_update(handle); /* check for new data */
|
||||
|
||||
/* perform null region processing */
|
||||
if(handle->usenullregion)
|
||||
sball_do_nullregion(voidhandle);
|
||||
|
||||
if(tx != NULL)
|
||||
*tx = handle->trans[0];
|
||||
if(ty != NULL)
|
||||
*ty = handle->trans[1];
|
||||
if(tz != NULL)
|
||||
*tz = handle->trans[2];
|
||||
|
||||
if(rx != NULL)
|
||||
*rx = handle->rot[0];
|
||||
if(ry != NULL)
|
||||
*ry = handle->rot[1];
|
||||
if(rz != NULL)
|
||||
*rz = handle->rot[2];
|
||||
|
||||
if(buttons != NULL)
|
||||
*buttons = handle->buttons;
|
||||
|
||||
/* no timer code yet */
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
/* everything from this point to the end of file was added by
|
||||
* John Tsiombikas for spacenavd integration.
|
||||
*/
|
||||
int sball_get_input(SBallHandle voidhandle, struct dev_input *inp)
|
||||
{
|
||||
struct event *ev;
|
||||
sballhandle *handle = voidhandle;
|
||||
|
||||
/* read pending packets from the device and append them in the event list */
|
||||
sball_update(handle);
|
||||
|
||||
/* if there are any events in the list, grab the first and return it */
|
||||
if((ev = handle->evhead)) {
|
||||
handle->evhead = handle->evhead->next;
|
||||
|
||||
*inp = ev->data;
|
||||
free_event(ev);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sball_get_fd(SBallHandle voidhandle)
|
||||
{
|
||||
sballhandle *sball = voidhandle;
|
||||
|
||||
return sball_comm_fd(sball->commhandle);
|
||||
}
|
||||
|
||||
static struct event *alloc_event(void)
|
||||
{
|
||||
struct event *ev;
|
||||
|
||||
if(ev_free_list) {
|
||||
ev = ev_free_list;
|
||||
ev_free_list = ev->next;
|
||||
} else {
|
||||
if((ev = malloc(sizeof *ev))) {
|
||||
evpool_size++;
|
||||
}
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
|
||||
static void free_event(struct event *ev)
|
||||
{
|
||||
if(evpool_size > 512) {
|
||||
free(ev);
|
||||
evpool_size--;
|
||||
} else {
|
||||
ev->next = ev_free_list;
|
||||
ev_free_list = ev;
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_motion_events(sballhandle *handle, int *prev_val, int *new_val, int timer)
|
||||
{
|
||||
int i, pending = 0;
|
||||
struct event *ev;
|
||||
|
||||
for(i=0; i<6; i++) {
|
||||
if(prev_val[i] == new_val[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if((ev = alloc_event())) {
|
||||
ev->data.type = INP_MOTION;
|
||||
ev->data.idx = i;
|
||||
ev->data.val = new_val[i];
|
||||
ev->next = 0;
|
||||
|
||||
if(handle->evhead) {
|
||||
handle->evtail->next = ev;
|
||||
handle->evtail = ev;
|
||||
} else {
|
||||
handle->evhead = handle->evtail = ev;
|
||||
}
|
||||
pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(pending) {
|
||||
if((ev = alloc_event())) {
|
||||
ev->data.type = INP_FLUSH;
|
||||
ev->next = 0;
|
||||
}
|
||||
|
||||
if(handle->evhead) {
|
||||
handle->evtail->next = ev;
|
||||
handle->evtail = ev;
|
||||
} else {
|
||||
handle->evhead = handle->evtail = ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_button_events(sballhandle *handle, int prevstate, int newstate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<16; i++) {
|
||||
int newbit = (newstate >> i) & 1;
|
||||
if(newbit != ((prevstate >> i) & 1)) {
|
||||
/* state changed, trigger event */
|
||||
struct event *ev = alloc_event();
|
||||
if(!ev) continue;
|
||||
|
||||
ev->data.type = INP_BUTTON;
|
||||
ev->data.idx = i;
|
||||
ev->data.val = newbit;
|
||||
ev->next = 0;
|
||||
|
||||
if(handle->evhead) {
|
||||
handle->evtail->next = ev;
|
||||
handle->evtail = ev;
|
||||
} else {
|
||||
handle->evhead = handle->evtail = ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 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/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright 1997-2001 John E. Stone (j.stone@acm.org)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !defined(SBALL_H)
|
||||
#define SBALL_H 1
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void *SBallHandle; /* Handle type, used by all sball API functions */
|
||||
|
||||
/* Spaceball Button bit-masks */
|
||||
#define SBALL_BUTTON_1 1 /* bit 0 */
|
||||
#define SBALL_BUTTON_2 2 /* bit 1 */
|
||||
#define SBALL_BUTTON_3 4 /* bit 2 */
|
||||
#define SBALL_BUTTON_4 8 /* bit 3 */
|
||||
#define SBALL_BUTTON_5 16 /* bit 4 */
|
||||
#define SBALL_BUTTON_6 32 /* bit 5 */
|
||||
#define SBALL_BUTTON_7 64 /* bit 6 */
|
||||
#define SBALL_BUTTON_8 128 /* bit 7 */
|
||||
#define SBALL_BUTTON_9 256 /* bit 8 */
|
||||
#define SBALL_BUTTON_10 512 /* bit 9 */
|
||||
#define SBALL_BUTTON_11 1024 /* bit 10 */
|
||||
#define SBALL_BUTTON_12 2048 /* bit 11 */
|
||||
|
||||
/* The Spaceball 3003 and 3003 FLX only have "left" and "right" buttons */
|
||||
#define SBALL_BUTTON_LEFT 4096 /* bit 12 */
|
||||
#define SBALL_BUTTON_RIGHT 8192 /* bit 13 */
|
||||
|
||||
/* The Spaceball 2003A and 2003B have a dedicated pick button on the ball */
|
||||
|
||||
/* The Spaceball 2003 FLX uses "button 9" as the pick button. */
|
||||
|
||||
/* All of them return this as "button 9" in their encoded button data */
|
||||
#define SBALL_BUTTON_PICK 128 /* bit 8 */
|
||||
|
||||
/* On Spaceball 2003A and 2003B, the Rezero is "button 8" on the device */
|
||||
|
||||
/* On the newer devices, there are dedicated rezero buttons */
|
||||
#define SBALL_BUTTON_REZERO 16384 /* bit 14 */
|
||||
|
||||
/* The Spaceball 4000 FLX has a configurable palm rest which can be in */
|
||||
|
||||
/* either "left" or "right" handed mode. When it is configured in "left" */
|
||||
|
||||
/* handed mode, the "lefty" bit is set, and coordinate systems need to be */
|
||||
|
||||
/* inverted on one axis. */
|
||||
#define SBALL_MODE_LEFTY 32768 /* bit 15 */
|
||||
|
||||
/*
|
||||
* sball_open()
|
||||
* Open a named serial port which a Spaceball is attached to.
|
||||
* Returns a handle which is used by all other sball API functions.
|
||||
* If the serial port open fails, or the sball does not pass initialization
|
||||
* tests, then a NULL is returned as the handle.
|
||||
*/
|
||||
SBallHandle sball_open(const char *sballname);
|
||||
|
||||
/*
|
||||
* sball_close()
|
||||
* Closes down the Spaceball serial port, frees allocated resources and
|
||||
* discards any unprocessed sball messages.
|
||||
*/
|
||||
int sball_close(SBallHandle voidhandle);
|
||||
|
||||
/*
|
||||
* sball_getstatus()
|
||||
* Polls the Spaceball serial port for new packets, performs any optional
|
||||
* postprocessing of Spaceball data such as null-region, scaling, and
|
||||
* value clamping. The most recent values for translation, rotation and
|
||||
* buttons are stored in the memory locations supplied by the caller.
|
||||
* Returns the number of events processed. If the number of events returned
|
||||
* is less than 1, either an error occured or there were no Spaceball
|
||||
* events to process.
|
||||
*/
|
||||
int sball_getstatus(SBallHandle voidhandle, int *tx, int *ty, int *tz, int *rx, int *ry, int *rz, int *buttons);
|
||||
|
||||
/* sball_get_input() - Added for spacenavd integration by John Tsiombikas.
|
||||
*
|
||||
* returns the first of any pending events through inp.
|
||||
* returns 1 if it got an event, 0 if there where none pending
|
||||
*/
|
||||
int sball_get_input(SBallHandle voidhandle, struct dev_input *inp);
|
||||
|
||||
/* sball_get_fd() - Added for spacenavd integration by John Tsiombikas.
|
||||
*
|
||||
* retreives the device file descriptor */
|
||||
int sball_get_fd(SBallHandle voidhandle);
|
||||
|
||||
/*
|
||||
* sball_rezero()
|
||||
* Forces the Orb to re-zero itself at the present twist/position.
|
||||
* All future event data is relative to this zero point.
|
||||
*/
|
||||
int sball_rezero(SBallHandle voidhandle);
|
||||
|
||||
/*
|
||||
* sball_init()
|
||||
* Performs a software re-initialization of the Spaceball, clearing
|
||||
* all unprocessed events. Initialization also forces the Orb to re-zero
|
||||
* itself.
|
||||
*/
|
||||
int sball_init(SBallHandle voidhandle);
|
||||
|
||||
/*
|
||||
* sball_set_nullregion()
|
||||
* Enables null-region processing on Spaceball output.
|
||||
* The null-region is the area (centered at 0) around which
|
||||
* each coordinate will report zero even when the Spaceball itself
|
||||
* reports a number whose absolute value is less than the null region
|
||||
* value for that coordinate. For example, if the null region on the
|
||||
* X translation coordinate is set to 50, all sball_getstatus() would report
|
||||
* 0 if X is less than 50 and greater than -50. If X is 51, sball_getstatus
|
||||
* would report 1. If X is -51, sball_getstatus() would report -1.
|
||||
* Null-regions help novice users gradually become accustomed to the
|
||||
* incredible sensitivity of the Spaceball, and make some applications
|
||||
* significantly easier to control. A resonable default nullregion for all
|
||||
* six axes is 65. Null regions should be tunable by the user, since its
|
||||
* likely that not all Spaceballs are quite identical, and it is guaranteed
|
||||
* that users have varying levels of manual dexterity.
|
||||
* Note that setting the null-region too high significantly reduces the
|
||||
* dynamic range of the output values from the Spaceball.
|
||||
*/
|
||||
void sball_set_nullregion(SBallHandle voidhandle, int nulltx, int nullty, int nulltz,
|
||||
int nullrx, int nullry, int nullrz);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 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/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright 1997-2001 John E. Stone (j.stone@acm.org)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _POSIX_SOURCE 1
|
||||
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include "sballserial.h" /* protos and types for this file */
|
||||
|
||||
typedef struct {
|
||||
int fd; /* serial port device file descriptor */
|
||||
} commstruct;
|
||||
|
||||
int sball_comm_open(const char *commname, SBallCommHandle * commhandleptr)
|
||||
{
|
||||
struct termios sballtermio;
|
||||
|
||||
commstruct *comm;
|
||||
|
||||
*commhandleptr = NULL;
|
||||
|
||||
comm = malloc(sizeof(commstruct));
|
||||
if(comm == NULL)
|
||||
return -1;
|
||||
|
||||
comm->fd = open(commname, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||
|
||||
if(comm->fd == -1) {
|
||||
free(comm);
|
||||
return -1; /* failed open of comm port */
|
||||
}
|
||||
tcgetattr(comm->fd, &sballtermio);
|
||||
|
||||
#if 0
|
||||
/* TIOCEXCL exclusive access by this process */
|
||||
#if defined(TIOCEXCL)
|
||||
if(ioctl(comm->fd, TIOCEXCL) < 0) {
|
||||
return -1; /* couldn't get exclusive use of port */
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
sballtermio.c_lflag = 0;
|
||||
sballtermio.c_lflag = 0;
|
||||
sballtermio.c_iflag = IGNBRK | IGNPAR;
|
||||
sballtermio.c_oflag = 0;
|
||||
sballtermio.c_cflag = CREAD | CS8 | CLOCAL | HUPCL;
|
||||
sballtermio.c_cc[VEOL] = '\r';
|
||||
sballtermio.c_cc[VERASE] = 0;
|
||||
sballtermio.c_cc[VKILL] = 0;
|
||||
sballtermio.c_cc[VMIN] = 0;
|
||||
sballtermio.c_cc[VTIME] = 0;
|
||||
|
||||
/* use of baud rate in cflag is deprecated according to the */
|
||||
/* single unix spec, also doesn't work in IRIX > 6.2 */
|
||||
cfsetispeed(&sballtermio, B9600);
|
||||
cfsetospeed(&sballtermio, B9600);
|
||||
|
||||
tcsetattr(comm->fd, TCSAFLUSH, &sballtermio);
|
||||
|
||||
*commhandleptr = (SBallCommHandle) comm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sball_comm_write(SBallCommHandle commhandle, const char *buf)
|
||||
{
|
||||
commstruct *comm = (commstruct *) commhandle;
|
||||
|
||||
if(comm == NULL)
|
||||
return -1;
|
||||
|
||||
return write(comm->fd, buf, strlen(buf));
|
||||
}
|
||||
|
||||
int sball_comm_read(SBallCommHandle commhandle, char *buf, int sz)
|
||||
{
|
||||
commstruct *comm = (commstruct *) commhandle;
|
||||
|
||||
if(comm == NULL)
|
||||
return -1;
|
||||
|
||||
return read(comm->fd, buf, sz);
|
||||
}
|
||||
|
||||
int sball_comm_close(SBallCommHandle * commhandleptr)
|
||||
{
|
||||
commstruct *comm = (commstruct *) (*commhandleptr);
|
||||
|
||||
if(comm == NULL)
|
||||
return -1;
|
||||
|
||||
close(comm->fd);
|
||||
|
||||
free(*commhandleptr);
|
||||
*commhandleptr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sball_comm_fd(SBallCommHandle commhandle)
|
||||
{
|
||||
return ((commstruct *) commhandle)->fd;
|
||||
}
|
||||
|
||||
/* end of unix code */
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
spacenavd - a free software replacement driver for 6dof space-mice.
|
||||
Copyright (C) 2007-2010 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/>.
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright 1997-2001 John E. Stone (j.stone@acm.org)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Machine/OS dependent serial port I/O routines.
|
||||
*
|
||||
* sball_comm_open() - open the serial port device for communication with
|
||||
* the sball. Settings are 9600,N,8,1, non-blocking,
|
||||
* no controlling tty.
|
||||
* sball_comm_read() - nonblocking read of up to size bytes
|
||||
* sball_comm_write() - blocking write of up to size bytes
|
||||
* sball_comm_close() - close the serial port device
|
||||
*/
|
||||
|
||||
typedef void *SBallCommHandle;
|
||||
|
||||
int sball_comm_open(const char *commname, SBallCommHandle * commhandleptr);
|
||||
|
||||
int sball_comm_write(SBallCommHandle commhandle, const char *buf);
|
||||
|
||||
int sball_comm_read(SBallCommHandle commhandle, char *buf, int sz);
|
||||
|
||||
int sball_comm_close(SBallCommHandle * commhandleptr);
|
||||
|
||||
int sball_comm_fd(SBallCommHandle commhandle);
|
Ładowanie…
Reference in New Issue