detection works
[sball] / src / sball.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <termios.h>
9 #include <sys/select.h>
10 #include <sys/time.h>
11
12 struct sball {
13         int fd;
14
15         int (*decode)(struct sball *sb, char *line);
16 };
17
18 static int stty_sball(int fd);
19 static int stty_mag(int fd);
20
21 static int decode_sball(struct sball *sb, char *line);
22 static int decode_mag(struct sball *sb, char *line);
23
24 static void make_printable(char *buf, int len);
25 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec);
26
27 struct sball *sball_open(const char *dev)
28 {
29         int fd, sz;
30         char buf[128];
31         struct sball *sb = 0;
32
33         if((fd = open(dev, O_RDWR | O_NOCTTY)) == -1) {
34                 fprintf(stderr, "sball_open: failed to open device: %s: %s\n", dev, strerror(errno));
35                 return 0;
36         }
37
38         if(!(sb = malloc(sizeof *sb))) {
39                 fprintf(stderr, "sball_open: failed to allocate sball object\n");
40                 goto err;
41         }
42         sb->fd = fd;
43
44         if(stty_sball(fd) == -1) {
45                 goto err;
46         }
47
48         if((sz = read_timeout(fd, buf, sizeof buf - 1, 2000000)) > 0) {
49                 /* we got a response, so it's a spaceball */
50                 make_printable(buf, sz);
51                 printf("Spaceball detected: %s\n", buf);
52
53                 sb->decode = decode_sball;
54                 return sb;
55         }
56
57         /* try as a magellan spacemouse */
58         if(stty_mag(fd) == -1) {
59                 goto err;
60         }
61         write(fd, "vQ\r", 3);
62
63         if((sz = read_timeout(fd, buf, sizeof buf - 1, 250000)) > 0) {
64                 /* we got a response, it's a magellan spacemouse */
65                 make_printable(buf, sz);
66                 printf("Magellan SpaceMouse detected: %s\n", buf);
67
68                 sb->decode = decode_mag;
69                 return sb;
70         }
71
72 err:
73         close(fd);
74         free(sb);
75         return 0;
76 }
77
78 void sball_close(struct sball *sb)
79 {
80         if(!sb) return;
81         close(sb->fd);
82 }
83
84
85 /* Labtec spaceball: 9600 8n1 XON/XOFF */
86 static int stty_sball(int fd)
87 {
88         struct termios term;
89
90         if(tcgetattr(fd, &term) == -1) {
91                 perror("sball_open: tcgetattr");
92                 return -1;
93         }
94
95         term.c_oflag = 0;
96         term.c_lflag = 0;
97         term.c_cc[VMIN] = 0;
98         term.c_cc[VTIME] = 1;
99
100         term.c_cflag = CLOCAL | CREAD | CS8 | HUPCL;
101         term.c_iflag = IGNBRK | IGNPAR | IXON | IXOFF;
102
103         cfsetispeed(&term, B9600);
104         cfsetospeed(&term, B9600);
105
106         if(tcsetattr(fd, TCSANOW, &term) == -1) {
107                 perror("sball_open: tcsetattr");
108                 return -1;
109         }
110
111         return 0;
112 }
113
114 /* Logicad magellan spacemouse: 9600 8n2 CTS/RTS */
115 static int stty_mag(int fd)
116 {
117         struct termios term;
118
119         if(tcgetattr(fd, &term) == -1) {
120                 perror("sball_open: tcgetattr");
121                 return -1;
122         }
123
124         term.c_oflag = 0;
125         term.c_lflag = 0;
126         term.c_cc[VMIN] = 0;
127         term.c_cc[VTIME] = 1;
128
129         term.c_cflag = CLOCAL | CREAD | CS8 | CSTOPB | HUPCL | CRTSCTS;
130         term.c_iflag = IGNBRK | IGNPAR | ICRNL;
131
132         cfsetispeed(&term, B9600);
133         cfsetospeed(&term, B9600);
134
135         if(tcsetattr(fd, TCSANOW, &term) == -1) {
136                 perror("sball_open: tcsetattr");
137                 return -1;
138         }
139
140         return 0;
141 }
142
143
144 static int decode_sball(struct sball *sb, char *line)
145 {
146         return -1;
147 }
148
149 static int decode_mag(struct sball *sb, char *line)
150 {
151         return -1;
152 }
153
154 static void make_printable(char *buf, int len)
155 {
156         int i, c;
157         char *wr = buf;
158
159         for(i=0; i<len; i++) {
160                 c = *buf++;
161                 if(c == '\r') {
162                         *wr++ = '\n';
163                         if(*buf == '\n') buf++;
164                 } else {
165                         *wr++ = c;
166                 }
167         }
168         *wr = 0;
169 }
170
171 static int read_timeout(int fd, char *buf, int bufsz, long tm_usec)
172 {
173         int res;
174         long usec, sz = 0;
175         struct timeval tv0, tv;
176         fd_set rdset;
177
178         if(!buf || bufsz <= 0) return -1;
179
180         usec = tm_usec;
181         gettimeofday(&tv0, 0);
182
183         while(sz < bufsz && usec > 0) {
184                 tv.tv_sec = usec / 1000000;
185                 tv.tv_usec = usec % 1000000;
186
187                 FD_ZERO(&rdset);
188                 FD_SET(fd, &rdset);
189                 if((res = select(fd + 1, &rdset, 0, 0, &tv)) > 0 && FD_ISSET(fd, &rdset)) {
190                         sz += read(fd, buf + sz, bufsz - sz);
191                         buf[sz] = 0;
192                         tm_usec = usec = 128000;        /* wait 128ms for the rest of the message to appear */
193                         gettimeofday(&tv0, 0);
194                         continue;
195                 }
196                 if(res == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) {
197                         break;
198                 }
199                 gettimeofday(&tv, 0);
200                 usec = tm_usec - ((tv.tv_sec - tv0.tv_sec) * 1000000 + (tv.tv_usec - tv0.tv_usec));
201         }
202
203         return sz > 0 ? sz : -1;
204 }
205