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