xref: /plan9/sys/src/cmd/aux/mouse.c (revision 39734e7ed1eb944f5e7b41936007d0d38b560d7f)
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