9 #include <sys/select.h>
12 #define INP_BUF_SZ 256
20 int (*proc_input)(struct sball *sb);
23 static int stty_sball(int fd);
24 static int stty_mag(int fd);
26 static int proc_sball(struct sball *sb);
27 static int proc_mag(struct sball *sb);
29 static int mag_parsepkt(int id, char *data, int len);
31 static void make_printable(char *buf, int len);
32 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec);
34 struct sball *sball_open(const char *dev)
40 if((fd = open(dev, O_RDWR | O_NOCTTY)) == -1) {
41 fprintf(stderr, "sball_open: failed to open device: %s: %s\n", dev, strerror(errno));
45 if(!(sb = malloc(sizeof *sb))) {
46 fprintf(stderr, "sball_open: failed to allocate sball object\n");
51 if(stty_sball(fd) == -1) {
55 if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0) {
56 /* we got a response, so it's a spaceball */
57 make_printable(buf, sz);
58 printf("Spaceball detected: %s\n", buf);
59 /* TODO: improve detection, verify it's a correct reset response */
61 sb->proc_input = proc_sball;
65 /* try as a magellan spacemouse */
66 if(stty_mag(fd) == -1) {
71 if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0) {
72 /* we got a response, it's a magellan spacemouse */
73 make_printable(buf, sz);
74 printf("Magellan SpaceMouse detected: %s\n", buf);
76 /* set 3D mode, not-dominant-axis, pass through motion and button packets */
79 sb->proc_input = proc_mag;
89 void sball_close(struct sball *sb)
95 int sball_fd(struct sball *sb)
100 int sball_read(struct sball *sb)
104 while((sz = read(sb->fd, sb->buf + sb->len, INP_BUF_SZ - sb->len - 1)) > 0) {
109 /* if we fill the input buffer, make a last attempt to parse it, and discard
110 * it so we can receive more
112 if(sb->len >= INP_BUF_SZ) {
120 /* Labtec spaceball: 9600 8n1 XON/XOFF */
121 static int stty_sball(int fd)
125 if(tcgetattr(fd, &term) == -1) {
126 perror("sball_open: tcgetattr");
133 term.c_cc[VTIME] = 1;
135 term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
136 term.c_iflag = IGNBRK | IGNPAR | IXON | IXOFF;
138 cfsetispeed(&term, B9600);
139 cfsetospeed(&term, B9600);
141 if(tcsetattr(fd, TCSANOW, &term) == -1) {
142 perror("sball_open: tcsetattr");
149 /* Logicad magellan spacemouse: 9600 8n2 CTS/RTS */
150 static int stty_mag(int fd)
154 if(tcgetattr(fd, &term) == -1) {
155 perror("sball_open: tcgetattr");
162 term.c_cc[VTIME] = 1;
164 term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL | CRTSCTS;
165 term.c_iflag = IGNBRK | IGNPAR;
167 cfsetispeed(&term, B9600);
168 cfsetospeed(&term, B9600);
170 if(tcsetattr(fd, TCSANOW, &term) == -1) {
171 perror("sball_open: tcsetattr");
179 static int proc_sball(struct sball *sb)
184 static int proc_mag(struct sball *sb)
187 char *bptr = sb->buf;
188 char *start = sb->buf;
189 char *end = sb->buf + sb->len;
191 /* see if we have a CR in the buffer */
195 mag_parsepkt(*start, start + 1, bptr - start - 1);
202 sz = start - sb->buf;
204 memmove(sb->buf, start, sz);
210 static int mag_parsepkt(int id, char *data, int len)
213 unsigned int keystate;
215 /*printf("magellan packet: %c - %s (%d bytes)\n", (char)id, data, len);*/
220 fprintf(stderr, "magellan: invalid data packet, expected 24 bytes, got: %d\n", len);
224 mot[i] = ((((int)data[0] & 0xf) << 12) | (((int)data[1] & 0xf) << 8) |
225 (((int)data[2] & 0xf) << 4) | (data[3] & 0xf)) - 0x8000;
228 printf("motion: T %+4d %+4d %+4d R %+4d %+4d %+4d\n", mot[0], mot[1], mot[2], mot[3], mot[4], mot[5]);
233 fprintf(stderr, "magellan: invalid keyboard pakcet, expected 3 bytes, got: %d\n", len);
236 keystate = (data[0] & 0xf) | ((data[1] & 0xf) << 4) | (((unsigned int)data[2] & 0xf) << 8);
237 printf("keystate: ");
238 for(i=0; i<12; i++) {
240 int hex = b < 10 ? b + '0' : b - 10 + 'a';
241 putchar(keystate & (1 << b) ? hex : '-');
248 fprintf(stderr, "magellan error: illegal command: %c%c\n", data[1], data[2]);
249 } else if(data[0] == 2) {
250 fprintf(stderr, "magellan error: framing error\n");
252 fprintf(stderr, "magellan error: unknown device error\n");
262 static void make_printable(char *buf, int len)
267 for(i=0; i<len; i++) {
271 if(*buf == '\n') buf++;
279 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec)
283 struct timeval tv0, tv;
286 if(!buf || bufsz <= 0) return -1;
289 gettimeofday(&tv0, 0);
291 while(sz < bufsz && usec > 0) {
292 tv.tv_sec = usec / 1000000;
293 tv.tv_usec = usec % 1000000;
297 if((res = select(fd + 1, &rdset, 0, 0, &tv)) > 0 && FD_ISSET(fd, &rdset)) {
298 sz += read(fd, buf + sz, bufsz - sz);
300 tm_usec = usec = 128000; /* wait 128ms for the rest of the message to appear */
301 gettimeofday(&tv0, 0);
304 if(res == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
307 gettimeofday(&tv, 0);
308 usec = tm_usec - ((tv.tv_sec - tv0.tv_sec) * 1000000 + (tv.tv_usec - tv0.tv_usec));
311 return sz > 0 ? sz : -1;