1 #include <u.h>
2 #include <libc.h>
3
4 enum
5 {
6 Sleep500 = 500,
7 Sleep1000 = 1000,
8 Sleep2000 = 2000,
9
10 TIMEOUT = 5000, /* timeout for writes */
11 };
12
13 char *speeds[] =
14 {
15 "b1200",
16 "b2400",
17 "b4800",
18 "b9600",
19 0,
20 };
21
22 int button2;
23
24 #define DEBUG if(debug)
25
26 int can9600; /* true if type W mouse can be set to 9600 */
27 int debug;
28 int dontset; /* true if we shouldn't try to set the mouse type */
29
30 static void
usage(void)31 usage(void)
32 {
33 fprint(2, "%s: usage: %s [device]\n", argv0, argv0);
34 exits("usage");
35 }
36
37 static void
catch(void * a,char * msg)38 catch(void *a, char *msg)
39 {
40 USED(a, msg);
41 if(strstr(msg, "alarm"))
42 noted(NCONT);
43 noted(NDFLT);
44 }
45
46 static void
dumpbuf(char * buf,int nbytes,char * s)47 dumpbuf(char *buf, int nbytes, char *s)
48 {
49 print(s);
50 while(nbytes-- > 0)
51 print("#%ux ", *buf++ & 0xFF);
52 print("\n");
53 }
54
55 static long
timedwrite(int fd,void * p,int n)56 timedwrite(int fd, void *p, int n)
57 {
58 long rv;
59
60 alarm(TIMEOUT);
61 rv = write(fd, p, n);
62 alarm(0);
63 if(rv < 0){
64 fprint(2, "%s: timed out\n", argv0);
65 exits("timeout");
66 }
67 return rv;
68 }
69
70 static int
readbyte(int fd)71 readbyte(int fd)
72 {
73 uchar c;
74 char buf[ERRMAX];
75
76 alarm(200);
77 if(read(fd, &c, sizeof(c)) == -1){
78 alarm(0);
79 errstr(buf, sizeof buf);
80 if(strcmp(buf, "interrupted") == 0)
81 return -1;
82 fprint(2, "%s: readbyte failed - %s\n", argv0, buf);
83 exits("read");
84 }
85 alarm(0);
86 return c;
87 }
88
89 static int
slowread(int fd,char * buf,int nbytes,char * msg)90 slowread(int fd, char *buf, int nbytes, char *msg)
91 {
92 char *p;
93 int c;
94
95 for(p = buf; nbytes > 1 && (c = readbyte(fd)) != -1; *p++ = c, nbytes--)
96 ;
97 *p = 0;
98 DEBUG dumpbuf(buf, p-buf, msg);
99 return p-buf;
100 }
101
102 static void
toggleRTS(int fd)103 toggleRTS(int fd)
104 {
105 /*
106 *
107 * reset the mouse (toggle RTS)
108 * must be >100mS
109 */
110 timedwrite(fd, "d1", 2);
111 timedwrite(fd, "r1", 2);
112 sleep(Sleep500);
113 timedwrite(fd, "d0", 2);
114 timedwrite(fd, "r0", 2);
115 sleep(Sleep500);
116 timedwrite(fd, "d1", 2);
117 timedwrite(fd, "r1", 2);
118 sleep(Sleep500);
119 }
120
121 static void
setupeia(int fd,char * baud,char * bits)122 setupeia(int fd, char *baud, char *bits)
123 {
124 alarm(TIMEOUT);
125 /*
126 * set the speed to 1200/2400/4800/9600 baud,
127 * 7/8-bit data, one stop bit and no parity
128 */
129 DEBUG print("setupeia(%s,%s)\n", baud, bits);
130 timedwrite(fd, baud, strlen(baud));
131 timedwrite(fd, bits, strlen(bits));
132 timedwrite(fd, "s1", 2);
133 timedwrite(fd, "pn", 2);
134 timedwrite(fd, "i1", 2);
135 alarm(0);
136 }
137
138 /*
139 * check for a types M, M3, & W
140 *
141 * we talk to all these mice using 1200 baud
142 */
143 int
MorW(int ctl,int data)144 MorW(int ctl, int data)
145 {
146 char buf[256];
147 int c;
148
149 /*
150 * set up for type M, V or W
151 * flush any pending data
152 */
153 setupeia(ctl, "b1200", "l7");
154 toggleRTS(ctl);
155 while(slowread(data, buf, sizeof(buf), "flush: ") > 0)
156 ;
157 toggleRTS(ctl);
158
159 /*
160 * see if there's any data from the mouse
161 * (type M, V and W mice)
162 */
163 c = slowread(data, buf, sizeof(buf), "check M: ");
164
165 /*
166 * type M, V and W mice return "M" or "M3" after reset.
167 * check for type W by sending a 'Send Standard Configuration'
168 * command, "*?".
169 *
170 * the second check is a kludge for some type W mice on next's
171 * that send a garbage character back before the "M3".
172 */
173 if((c > 0 && buf[0] == 'M') || (c > 1 && buf[1] == 'M')){
174 timedwrite(data, "*?", 2);
175 c = slowread(data, buf, sizeof(buf), "check W: ");
176 /*
177 * 4 bytes back
178 * indicates a type W mouse
179 */
180 if(c == 4){
181 if(buf[1] & (1<<4))
182 can9600 = 1;
183 setupeia(ctl, "b1200", "l8");
184 timedwrite(data, "*U", 2);
185 slowread(data, buf, sizeof(buf), "check W: ");
186 return 'W';
187 }
188 return 'M';
189 }
190 return 0;
191 }
192
193 /*
194 * check for type C by seeing if it responds to the status
195 * command "s". the mouse is at an unknown speed so we
196 * have to check all possible speeds.
197 */
198 int
C(int ctl,int data)199 C(int ctl, int data)
200 {
201 char **s;
202 int c;
203 char buf[256];
204
205 sleep(100);
206 for(s = speeds; *s; s++){
207 DEBUG print("%s\n", *s);
208 setupeia(ctl, *s, "l8");
209 timedwrite(data, "s", 1);
210 c = slowread(data, buf, sizeof(buf), "check C: ");
211 if(c >= 1 && (*buf & 0xBF) == 0x0F){
212 sleep(100);
213 timedwrite(data, "*n", 2);
214 sleep(100);
215 setupeia(ctl, "b1200", "l8");
216 timedwrite(data, "s", 1);
217 c = slowread(data, buf, sizeof(buf), "recheck C: ");
218 if(c >= 1 && (*buf & 0xBF) == 0x0F){
219 timedwrite(data, "U", 1);
220 return 'C';
221 }
222 }
223 sleep(100);
224 }
225
226 return 0;
227 }
228
229 char *bauderr = "mouse: can't set baud rate, mouse at 1200\n";
230
231 void
Cbaud(int ctl,int data,int baud)232 Cbaud(int ctl, int data, int baud)
233 {
234 char buf[32];
235
236 switch(baud){
237 case 0:
238 case 1200:
239 return;
240 case 2400:
241 buf[1] = 'o';
242 break;
243 case 4800:
244 buf[1] = 'p';
245 break;
246 case 9600:
247 buf[1] = 'q';
248 break;
249 default:
250 fprint(2, bauderr);
251 return;
252 }
253
254 buf[0] = '*';
255 buf[2] = 0;
256 sleep(100);
257 timedwrite(data, buf, 2);
258 sleep(100);
259 timedwrite(data, buf, 2);
260 sprint(buf, "b%d", baud);
261 setupeia(ctl, buf, "l8");
262 }
263
264 void
Wbaud(int ctl,int data,int baud)265 Wbaud(int ctl, int data, int baud)
266 {
267 char buf[32];
268
269 switch(baud){
270 case 0:
271 case 1200:
272 return;
273 case 9600:
274 if(can9600)
275 break;
276 /* fall through */
277 default:
278 fprint(2, bauderr);
279 return;
280 }
281 timedwrite(data, "*q", 2);
282 setupeia(ctl, "b9600", "l8");
283 slowread(data, buf, sizeof(buf), "setbaud: ");
284 }
285
286 void
main(int argc,char * argv[])287 main(int argc, char *argv[])
288 {
289 char *p;
290 int baud;
291 int tries, conf, ctl, data, def, type;
292 char buf[256];
293
294 def = 0;
295 baud = 0;
296 ARGBEGIN{
297 case 'b':
298 baud = atoi(ARGF());
299 break;
300 case 'd':
301 p = ARGF();
302 def = *p;
303 break;
304 case 'n':
305 dontset = 1;
306 break;
307 case 'D':
308 debug = 1;
309 break;
310 default:
311 usage();
312 }ARGEND
313
314 p = "0";
315 if(argc)
316 p = *argv;
317
318 if((conf = open("/dev/mousectl", OWRITE)) == -1){
319 fprint(2, "%s: can't open /dev/mousectl - %r\n", argv0);
320 if(dontset == 0)
321 exits("open /dev/mousectl");
322 }
323
324 if(strncmp(p, "ps2", 3) == 0){
325 if(write(conf, p, strlen(p)) < 0){
326 fprint(2, "%s: error setting mouse type - %r\n", argv0);
327 exits("write conf");
328 }
329 exits(0);
330 }
331
332 type = 0;
333 for(tries = 0; type == 0 && tries < 6; tries++){
334 if(tries)
335 fprint(2, "%s: Unknown mouse type, retrying...\n", argv0);
336 sprint(buf, "#t/eia%sctl", p);
337 if((ctl = open(buf, ORDWR)) == -1){
338 fprint(2, "%s: can't open %s - %r\n", argv0, buf);
339 exits("open ctl");
340 }
341 sprint(buf, "#t/eia%s", p);
342 if((data = open(buf, ORDWR)) == -1){
343 fprint(2, "%s: can't open %s - %r\n", argv0, buf);
344 exits("open data");
345 }
346
347 notify(catch);
348
349 type = MorW(ctl, data);
350 if(type == 0)
351 type = C(ctl, data);
352 if(type == 0){
353 /* with the default we can't assume anything */
354 baud = 0;
355
356 /* try the default */
357 switch(def){
358 case 'C':
359 setupeia(ctl, "b1200", "l8");
360 break;
361 case 'M':
362 setupeia(ctl, "b1200", "l7");
363 break;
364 }
365
366 type = def;
367 }
368
369 sprint(buf, "serial %s", p);
370 switch(type){
371 case 0:
372 close(data);
373 close(ctl);
374 continue;
375 case 'C':
376 DEBUG print("Logitech 5 byte mouse\n");
377 Cbaud(ctl, data, baud);
378 break;
379 case 'W':
380 DEBUG print("Type W mouse\n");
381 Wbaud(ctl, data, baud);
382 break;
383 case 'M':
384 DEBUG print("Microsoft compatible mouse\n");
385 strcat(buf, " M");
386 break;
387 }
388 }
389
390 if(type == 0){
391 fprint(2, "%s: Unknown mouse type, giving up\n", argv0);
392 exits("no mouse");
393 }
394
395 DEBUG fprint(2, "mouse configured as '%s'\n", buf);
396 if(dontset == 0 && write(conf, buf, strlen(buf)) < 0){
397 fprint(2, "%s: error setting mouse type - %r\n", argv0);
398 exits("write conf");
399 }
400
401 exits(0);
402 }
403