xref: /plan9-contrib/sys/src/cmd/aux/mouse.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
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
31 usage(void)
32 {
33 	fprint(2, "%s: usage: %s [device]\n", argv0, argv0);
34 	exits("usage");
35 }
36 
37 static void
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
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
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
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
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
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
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 	alarm(0);
135 }
136 
137 /*
138  *  check for a types M, M3, & W
139  *
140  *  we talk to all these mice using 1200 baud
141  */
142 int
143 MorW(int ctl, int data)
144 {
145 	char buf[256];
146 	int c;
147 
148 	/*
149 	 * set up for type M, V or W
150 	 * flush any pending data
151 	 */
152 	setupeia(ctl, "b1200", "l7");
153 	toggleRTS(ctl);
154 	while(slowread(data, buf, sizeof(buf), "flush: ") > 0)
155 		;
156 	toggleRTS(ctl);
157 
158 	/*
159 	 * see if there's any data from the mouse
160 	 * (type M, V and W mice)
161 	 */
162 	c = slowread(data, buf, sizeof(buf), "check M: ");
163 
164 	/*
165 	 * type M, V and W mice return "M" or "M3" after reset.
166 	 * check for type W by sending a 'Send Standard Configuration'
167 	 * command, "*?".
168 	 *
169 	 * the second check is a kludge for some type W mice on next's
170 	 * that send a garbage character back before the "M3".
171 	 */
172 	if((c > 0 && buf[0] == 'M') || (c > 1 && buf[1] == 'M')){
173 		timedwrite(data, "*?", 2);
174 		c = slowread(data, buf, sizeof(buf), "check W: ");
175 		/*
176 		 * 4 bytes back
177 		 * indicates a type W mouse
178 		 */
179 		if(c == 4){
180 			if(buf[1] & (1<<4))
181 				can9600 = 1;
182 			setupeia(ctl, "b1200", "l8");
183 			timedwrite(data, "*U", 2);
184 			slowread(data, buf, sizeof(buf), "check W: ");
185 			return 'W';
186 		}
187 		return 'M';
188 	}
189 	return 0;
190 }
191 
192 /*
193  *  check for type C by seeing if it responds to the status
194  *  command "s".  the mouse is at an unknown speed so we
195  *  have to check all possible speeds.
196  */
197 int
198 C(int ctl, int data)
199 {
200 	char **s;
201 	int c;
202 	char buf[256];
203 
204 	sleep(100);
205 	for(s = speeds; *s; s++){
206 		DEBUG print("%s\n", *s);
207 		setupeia(ctl, *s, "l8");
208 		timedwrite(data, "s", 1);
209 		c = slowread(data, buf, sizeof(buf), "check C: ");
210 		if(c >= 1 && (*buf & 0xBF) == 0x0F){
211 			sleep(100);
212 			timedwrite(data, "*n", 2);
213 			sleep(100);
214 			setupeia(ctl, "b1200", "l8");
215 			timedwrite(data, "s", 1);
216 			c = slowread(data, buf, sizeof(buf), "recheck C: ");
217 			if(c >= 1 && (*buf & 0xBF) == 0x0F){
218 				timedwrite(data, "U", 1);
219 				return 'C';
220 			}
221 		}
222 		sleep(100);
223 	}
224 
225 	return 0;
226 }
227 
228 char *bauderr = "mouse: can't set baud rate, mouse at 1200\n";
229 
230 void
231 Cbaud(int ctl, int data, int baud)
232 {
233 	char buf[32];
234 
235 	switch(baud){
236 	case 0:
237 	case 1200:
238 		return;
239 	case 2400:
240 		buf[1] = 'o';
241 		break;
242 	case 4800:
243 		buf[1] = 'p';
244 		break;
245 	case 9600:
246 		buf[1] = 'q';
247 		break;
248 	default:
249 		fprint(2, bauderr);
250 		return;
251 	}
252 
253 	buf[0] = '*';
254 	buf[2] = 0;
255 	sleep(100);
256 	timedwrite(data, buf, 2);
257 	sleep(100);
258 	timedwrite(data, buf, 2);
259 	sprint(buf, "b%d", baud);
260 	setupeia(ctl, buf, "l8");
261 }
262 
263 void
264 Wbaud(int ctl, int data, int baud)
265 {
266 	char buf[32];
267 
268 	switch(baud){
269 	case 0:
270 	case 1200:
271 		return;
272 	case 9600:
273 		if(can9600)
274 			break;
275 		/* fall through */
276 	default:
277 		fprint(2, bauderr);
278 		return;
279 	}
280 	timedwrite(data, "*q", 2);
281 	setupeia(ctl, "b9600", "l8");
282 	slowread(data, buf, sizeof(buf), "setbaud: ");
283 }
284 
285 void
286 main(int argc, char *argv[])
287 {
288 	char *p;
289 	int baud;
290 	int tries, conf, ctl, data, def, type;
291 	char buf[256];
292 
293 	def = 0;
294 	baud = 0;
295 	ARGBEGIN{
296 	case 'b':
297 		baud = atoi(ARGF());
298 		break;
299 	case 'd':
300 		p = ARGF();
301 		def = *p;
302 		break;
303 	case 'n':
304 		dontset = 1;
305 		break;
306 	case 'D':
307 		debug = 1;
308 		break;
309 	default:
310 		usage();
311 	}ARGEND
312 
313 	p = "0";
314 	if(argc)
315 		p = *argv;
316 
317 	if((conf = open("/dev/mousectl", OWRITE)) == -1){
318 		fprint(2, "%s: can't open /dev/mousectl - %r\n", argv0);
319 		if(dontset == 0)
320 			exits("open /dev/mousectl");
321 	}
322 
323 	if(strncmp(p, "ps2", 3) == 0){
324 		if(write(conf, p, strlen(p)) < 0){
325 			fprint(2, "%s: error setting mouse type - %r\n", argv0);
326 			exits("write conf");
327 		}
328 		exits(0);
329 	}
330 
331 	type = 0;
332 	for(tries = 0; type == 0 && tries < 6; tries++){
333 		if(tries)
334 			fprint(2, "%s: Unknown mouse type, retrying...\n", argv0);
335 		sprint(buf, "#t/eia%sctl", p);
336 		if((ctl = open(buf, ORDWR)) == -1){
337 			fprint(2, "%s: can't open %s - %r\n", argv0, buf);
338 			exits("open ctl");
339 		}
340 		sprint(buf, "#t/eia%s", p);
341 		if((data = open(buf, ORDWR)) == -1){
342 			fprint(2, "%s: can't open %s - %r\n", argv0, buf);
343 			exits("open data");
344 		}
345 
346 		notify(catch);
347 
348 		type = MorW(ctl, data);
349 		if(type == 0)
350 			type = C(ctl, data);
351 		if(type == 0){
352 			/* with the default we can't assume anything */
353 			baud = 0;
354 
355 			/* try the default */
356 			switch(def){
357 			case 'C':
358 				setupeia(ctl, "b1200", "l8");
359 				break;
360 			case 'M':
361 				setupeia(ctl, "b1200", "l7");
362 				break;
363 			}
364 
365 			type = def;
366 		}
367 
368 		sprint(buf, "serial %s", p);
369 		switch(type){
370 		case 0:
371 			close(data);
372 			close(ctl);
373 			continue;
374 		case 'C':
375 			DEBUG print("Logitech 5 byte mouse\n");
376 			Cbaud(ctl, data, baud);
377 			break;
378 		case 'W':
379 			DEBUG print("Type W mouse\n");
380 			Wbaud(ctl, data, baud);
381 			break;
382 		case 'M':
383 			DEBUG print("Microsoft compatible mouse\n");
384 			strcat(buf, " M");
385 			break;
386 		}
387 	}
388 
389 	if(type == 0){
390 		fprint(2, "%s: Unknown mouse type, giving up\n", argv0);
391 		exits("no mouse");
392 	}
393 
394 	DEBUG fprint(2, "mouse configured as '%s'\n", buf);
395 	if(dontset == 0 && write(conf, buf, strlen(buf)) < 0){
396 		fprint(2, "%s: error setting mouse type - %r\n", argv0);
397 		exits("write conf");
398 	}
399 
400 	exits(0);
401 }
402