xref: /illumos-gate/usr/src/uts/common/io/vuidmice/vuidps2.c (revision e44e85a7f9935f0428e188393e3da61b17e83884)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * 			2/3/5 Button PS/2 Mouse Protocol
28  *
29  * This module dynamically determines the number of buttons on the mouse.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/stream.h>
34 #include <sys/vuid_event.h>
35 #include "vuidmice.h"
36 #include <sys/vuid_wheel.h>
37 #include <sys/mouse.h>
38 #include <sys/strsun.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 
42 /*
43  * BUT(1)		LEFT   BUTTON
44  * BUT(2)		MIDDLE BUTTON (if present)
45  * BUT(3)		RIGHT  BUTTON
46  */
47 
48 #define	PS2_BUTTONMASK		7		/* mask byte zero with this */
49 
50 #define	PS2_BUTTON_L		(uchar_t)0x01	/* Left button pressed */
51 #define	PS2_BUTTON_R		(uchar_t)0x02	/* Right button pressed */
52 #define	PS2_BUTTON_M		(uchar_t)0x04	/* Middle button pressed */
53 #define	PS2_DATA_XSIGN		(uchar_t)0x10	/* X data sign bit */
54 #define	PS2_DATA_YSIGN		(uchar_t)0x20	/* Y data sign bit */
55 
56 #define	PS2_START			0	/* Beginning of packet	*/
57 #define	PS2_BUTTON			1	/* Got button status	*/
58 #define	PS2_MAYBE_REATTACH		2	/* Got button status	*/
59 #define	PS2_DELTA_Y			3	/* Got delta X		*/
60 #define	PS2_WHEEL_DELTA_Z		4
61 #define	PS2_WHEEL5_DELTA_Z		5
62 #define	PS2_WAIT_RESET_ACK		6
63 #define	PS2_WAIT_RESET_AA		7
64 #define	PS2_WAIT_RESET_00		8
65 #define	PS2_WAIT_SETRES0_ACK1		9
66 #define	PS2_WAIT_SETRES0_ACK2		10	/* -+ must be consecutive */
67 #define	PS2_WAIT_SCALE1_1_ACK		11	/*  | */
68 #define	PS2_WAIT_SCALE1_2_ACK		12	/*  | */
69 #define	PS2_WAIT_SCALE1_3_ACK		13	/* -+ */
70 #define	PS2_WAIT_STATREQ_ACK		14
71 #define	PS2_WAIT_STATUS_1		15
72 #define	PS2_WAIT_STATUS_BUTTONS		16
73 #define	PS2_WAIT_STATUS_REV		17
74 #define	PS2_WAIT_STATUS_3		18
75 #define	PS2_WAIT_WHEEL_SMPL1_CMD_ACK	19	/* Set the sample rate to 200 */
76 #define	PS2_WAIT_WHEEL_SMPL1_RATE_ACK	20
77 #define	PS2_WAIT_WHEEL_SMPL2_CMD_ACK	21	/* Set the sample rate to 200 */
78 #define	PS2_WAIT_WHEEL_SMPL2_RATE_ACK	22
79 #define	PS2_WAIT_WHEEL_SMPL3_CMD_ACK	23	/* Set the sample rate to 80 */
80 #define	PS2_WAIT_WHEEL_SMPL3_RATE_ACK	24
81 #define	PS2_WAIT_WHEEL_DEV_CMD		25
82 #define	PS2_WAIT_WHEEL_DEV_ACK		26	/* Detected wheel mouse */
83 #define	PS2_WAIT_WHEEL5_SMPL1_CMD_ACK	27	/* Set the sample rate to 200 */
84 #define	PS2_WAIT_WHEEL5_SMPL1_RATE_ACK	28
85 #define	PS2_WAIT_WHEEL5_SMPL2_CMD_ACK	29	/* Set the sample rate to 200 */
86 #define	PS2_WAIT_WHEEL5_SMPL2_RATE_ACK	30
87 #define	PS2_WAIT_WHEEL5_SMPL3_CMD_ACK	31	/* Set the sample rate to 100 */
88 #define	PS2_WAIT_WHEEL5_SMPL3_RATE_ACK	32
89 #define	PS2_WAIT_WHEEL5_DEV_CMD		33
90 #define	PS2_WAIT_WHEEL5_DEV_ACK		34	/* Detected 5 button mouse */
91 #define	PS2_WAIT_SETRES3_CMD		35
92 #define	PS2_WAIT_SETRES3_ACK1		36
93 #define	PS2_WAIT_SETRES3_ACK2		37
94 #define	PS2_WAIT_STREAM_ACK		38
95 #define	PS2_WAIT_ON_ACK			39
96 
97 #define	MSE_AA		0xaa
98 #define	MSE_00		0x00
99 
100 #define	MOUSE_MODE_PLAIN	0	/* Normal PS/2 mouse - 3 byte msgs */
101 #define	MOUSE_MODE_WHEEL	1	/* Wheel mouse - 4 byte msgs */
102 #define	MOUSE_MODE_WHEEL5	2	/* Wheel + 5 btn mouse - 4 byte msgs */
103 
104 #define	PS2_FLAG_NO_EXTN	0x08	/* Mouse doesn't obey extended cmds */
105 #define	PS2_FLAG_INIT_DONE	0x01	/* Mouse has been inited successfully */
106 #define	PS2_FLAG_INIT_TIMEOUT	0x02	/* Mouse init timeout */
107 
108 /*
109  * The RESET command takes more time
110  * before the PS/2 mouse is ready
111  */
112 #define	PS2_INIT_TMOUT_RESET	500000	/* 500ms for RESET command */
113 #define	PS2_INIT_TMOUT_PER_CMD	200000	/* 200ms for each command-response */
114 #define	PS2_INIT_TMOUT_PER_GROUP	500000 /* 500ms for group commands */
115 
116 #define	PS2_MAX_INIT_COUNT	5
117 
118 
119 static void vuidmice_send_wheel_event(queue_t *const, uchar_t,
120 		uchar_t, uchar_t, int);
121 extern void VUID_PUTNEXT(queue_t *const, uchar_t, uchar_t, uchar_t, int);
122 extern void uniqtime32(struct timeval32 *);
123 static void VUID_INIT_TIMEOUT(void *q);
124 
125 /*
126  * We apply timeout to nearly each command-response
127  * during initialization:
128  *
129  * Set timeout for RESET
130  * Set timeout for SET RESOLUTION
131  * Set timeout for SET SCALE
132  * Set timeout for SET SAMPLE RATE
133  * Set timeout for STATUS REQUEST
134  * Set timeout for GET DEV
135  * Set timeout for SET STREAM MODE and ENABLE.
136  *
137  * But for simplicity, sometimes we just apply the timeout
138  * to a function with group commands (e.g. wheel-mouse detection).
139  *
140  */
141 static void
142 vuid_set_timeout(queue_t *const qp, clock_t time)
143 {
144 	ASSERT(STATEP->init_tid == 0);
145 	STATEP->init_tid = qtimeout(qp, VUID_INIT_TIMEOUT,
146 	    qp, drv_usectohz(time));
147 }
148 
149 static void
150 vuid_cancel_timeout(queue_t *const qp)
151 {
152 	ASSERT(STATEP->init_tid != 0);
153 	(void) quntimeout(qp, STATEP->init_tid);
154 	STATEP->init_tid = 0;
155 }
156 
157 /*
158  * vuidmice_send_wheel_event
159  *	Convert wheel data to firm_events
160  */
161 static void
162 vuidmice_send_wheel_event(queue_t *const qp, uchar_t event_id,
163     uchar_t event_pair_type, uchar_t event_pair, int event_value)
164 {
165 	mblk_t		*bp;
166 	Firm_event	*fep;
167 
168 	if ((bp = allocb((int)sizeof (Firm_event), BPRI_HI)) == NULL) {
169 
170 		return;
171 	}
172 
173 	fep = (void *)bp->b_wptr;
174 	fep->id = vuid_id_addr(vuid_first(VUID_WHEEL)) |
175 	    vuid_id_offset(event_id);
176 	fep->pair_type = event_pair_type;
177 	fep->pair = event_pair;
178 	fep->value = event_value;
179 	uniqtime32(&fep->time);
180 	bp->b_wptr += sizeof (Firm_event);
181 
182 	if (canput(qp->q_next)) {
183 		putnext(qp, bp);
184 	} else {
185 		(void) putbq(qp, bp); /* read side is blocked */
186 	}
187 }
188 
189 
190 static void
191 sendButtonEvent(queue_t *const qp)
192 {
193 	static int bmap[3] = {1, 3, 2};
194 	uint_t b;
195 
196 	/* for each button, see if it has changed */
197 	for (b = 0; b < STATEP->nbuttons; b++) {
198 		uchar_t	mask = 0x1 << b;
199 
200 		if ((STATEP->buttons & mask) != (STATEP->oldbuttons & mask))
201 			VUID_PUTNEXT(qp, (uchar_t)BUT(bmap[b]), FE_PAIR_NONE, 0,
202 			    (STATEP->buttons & mask ? 1 : 0));
203 	}
204 }
205 
206 void
207 put1(queue_t *const qp, int c)
208 {
209 	mblk_t *bp;
210 
211 	if (bp = allocb(1, BPRI_MED)) {
212 		*bp->b_wptr++ = (char)c;
213 		putnext(qp, bp);
214 	}
215 }
216 
217 int
218 VUID_OPEN(queue_t *const qp)
219 {
220 	STATEP->format = VUID_FIRM_EVENT;
221 	STATEP->vuid_mouse_mode = MOUSE_MODE_PLAIN;
222 	STATEP->inited = 0;
223 	STATEP->nbuttons = 3;
224 
225 	STATEP->state = PS2_WAIT_RESET_ACK;
226 
227 	/* Set timeout for reset */
228 	vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
229 
230 	put1(WR(qp), MSERESET);
231 
232 	while ((STATEP->state != PS2_START) &&
233 	    !(STATEP->inited & PS2_FLAG_INIT_TIMEOUT)) {
234 		if (qwait_sig(qp) == 0)
235 			break;
236 	}
237 
238 	/*
239 	 * Later the PS/2 mouse maybe re-attach, so here
240 	 * clear the init_count.
241 	 */
242 	STATEP->init_count = 0;
243 
244 	return (0);
245 }
246 
247 void
248 VUID_CLOSE(queue_t *const qp)
249 {
250 	if (STATEP->init_tid != 0)
251 		vuid_cancel_timeout(qp);
252 }
253 
254 static void
255 VUID_INIT_TIMEOUT(void *q)
256 {
257 	queue_t	*qp = q;
258 
259 	STATEP->init_tid = 0;
260 
261 	/*
262 	 * Some mice do not even send an error in response to
263 	 * the wheel mouse sample commands, so if we're in any of
264 	 * the PS2_WAIT_WHEEL_SMPL* states, and there has been
265 	 * a timeout, assume the mouse cannot handle the extended
266 	 * (wheel mouse) commands.
267 	 */
268 	if ((STATEP->state == PS2_WAIT_WHEEL_SMPL1_CMD_ACK) ||
269 	    (STATEP->state == PS2_WAIT_WHEEL_SMPL1_RATE_ACK) ||
270 	    (STATEP->state == PS2_WAIT_WHEEL_SMPL2_RATE_ACK) ||
271 	    (STATEP->state == PS2_WAIT_WHEEL_SMPL3_RATE_ACK)) {
272 		/*
273 		 * We overload 'inited' to mark the PS/2 mouse
274 		 * as one which doesn't respond to extended commands.
275 		 */
276 
277 		STATEP->inited |= PS2_FLAG_NO_EXTN;
278 	}
279 
280 	if (++STATEP->init_count >= PS2_MAX_INIT_COUNT) {
281 		STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
282 		return;
283 	}
284 
285 
286 	STATEP->state = PS2_WAIT_RESET_ACK;
287 
288 	vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
289 
290 	/* try again */
291 	put1(WR(qp), MSERESET);
292 }
293 
294 void
295 VUID_QUEUE(queue_t *const qp, mblk_t *mp)
296 {
297 	int code;
298 	clock_t now;
299 	clock_t elapsed;
300 	clock_t mouse_timeout;
301 
302 	mouse_timeout = drv_usectohz(250000);
303 	now = ddi_get_lbolt();
304 	elapsed = now - STATEP->last_event_lbolt;
305 	STATEP->last_event_lbolt = now;
306 
307 	while (mp->b_rptr < mp->b_wptr) {
308 		code = *mp->b_rptr++;
309 
310 		switch (STATEP->state) {
311 
312 		/*
313 		 * Start state. We stay here if the start code is not
314 		 * received thus forcing us back into sync. When we get a
315 		 * start code the button mask comes with it forcing us to
316 		 * to the next state.
317 		 */
318 restart:
319 		case PS2_START:
320 
321 			/*
322 			 * 3-byte packet format
323 			 *
324 			 * Bit   7   6    5	4	3   2	1	0
325 			 * Byte ---- ---- ----- ----- -- ------ ------ ------
326 			 * 1    Y_Ov X_Ov Y_Sgn X_Sgn  1 MdlBtn RgtBtn LftBtn
327 			 * 2    |<--------------X Movement----------------->|
328 			 * 3    |<--------------Y Movement----------------->|
329 			 *
330 			 * 4-byte wheel packet format
331 			 *
332 			 * Bit   7    6   5	4	3   2	1	0
333 			 * Byte ---- ---- ----- ----- -- ------ ------ ------
334 			 * 1    Y_Ov X_Ov Y_Sgn X_Sgn  1 MdlBtn RgtBtn LftBtn
335 			 * 2    |<--------------X Movement----------------->|
336 			 * 3    |<--------------Y Movement----------------->|
337 			 * 4    |<--------------Z Movement----------------->|
338 			 *
339 			 * 4-byte wheel+5 packet format
340 			 *
341 			 * Bit   7    6   5	4	3   2	1	0
342 			 * Byte ---- ---- ----- ----- -- ------ ------ ------
343 			 * 1    Y_Ov X_Ov Y_Sgn X_Sgn  1 MdlBtn RgtBtn LftBtn
344 			 * 2    |<--------------X Movement----------------->|
345 			 * 3    |<--------------Y Movement----------------->|
346 			 * 4	0    0   5_Btn 4_Btn Z3   Z2	Z1	Z0
347 			 */
348 
349 			if (!(STATEP->inited & PS2_FLAG_INIT_DONE)) {
350 				STATEP->sync_byte = code & 0x8;
351 				STATEP->inited |= PS2_FLAG_INIT_DONE;
352 			}
353 		/*
354 		 * the PS/2 mouse data format doesn't have any sort of sync
355 		 * data to make sure we are in sync with the packet stream,
356 		 * but the Technical Reference manual states that bits 2 & 3
357 		 * of the first byte are reserved.  Logitech uses bit 2 for
358 		 * the middle button.  We HOPE that noone uses bit 3 though,
359 		 * and decide we're out of sync if bit 3 is not set here.
360 		 */
361 
362 			if ((code ^ STATEP->sync_byte) & 0x08) {
363 				/* bit 3 not set */
364 				STATEP->state = PS2_START;
365 				break;			/* toss the code */
366 			}
367 
368 			/* get the button values */
369 			STATEP->buttons = code & PS2_BUTTONMASK;
370 			if (STATEP->buttons != STATEP->oldbuttons) {
371 				sendButtonEvent(qp);
372 				STATEP->oldbuttons = STATEP->buttons;
373 			}
374 
375 			/* bit 5 indicates Y value is negative (the sign bit) */
376 			if (code & PS2_DATA_YSIGN)
377 				STATEP->deltay = -1 & ~0xff;
378 			else
379 				STATEP->deltay = 0;
380 
381 			/* bit 4 is X sign bit */
382 			if (code & PS2_DATA_XSIGN)
383 				STATEP->deltax = -1 & ~0xff;
384 			else
385 				STATEP->deltax = 0;
386 
387 			if (code == MSE_AA)
388 				STATEP->state = PS2_MAYBE_REATTACH;
389 			else
390 				STATEP->state = PS2_BUTTON;
391 
392 			break;
393 
394 		case PS2_MAYBE_REATTACH:
395 			if (code == MSE_00) {
396 				STATEP->state = PS2_WAIT_RESET_ACK;
397 				vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
398 				put1(WR(qp), MSERESET);
399 				break;
400 			}
401 			/*FALLTHROUGH*/
402 
403 		case PS2_BUTTON:
404 			/*
405 			 * Now for the 7 bits of delta x.  "Or" in
406 			 * the sign bit and continue.  This is ac-
407 			 * tually a signed 9 bit number, but I just
408 			 * truncate it to a signed char in order to
409 			 * avoid changing and retesting all of the
410 			 * mouse-related modules for this patch.
411 			 */
412 			if (elapsed > mouse_timeout)
413 				goto restart;
414 			STATEP->deltax |= code & 0xff;
415 			STATEP->state = PS2_DELTA_Y;
416 			break;
417 
418 		case PS2_DELTA_Y:
419 			/*
420 			 * This byte is delta Y.  If this is a plain mouse,
421 			 * we're done.  Wheel mice have two different flavors
422 			 * of fourth byte.
423 			 */
424 
425 			if (elapsed > mouse_timeout) {
426 				goto restart;
427 			}
428 			STATEP->deltay |= code & 0xff;
429 
430 			if (STATEP->vuid_mouse_mode == MOUSE_MODE_WHEEL) {
431 				STATEP->state = PS2_WHEEL_DELTA_Z;
432 				break;
433 			} else if (STATEP->vuid_mouse_mode ==
434 			    MOUSE_MODE_WHEEL5) {
435 				STATEP->state = PS2_WHEEL5_DELTA_Z;
436 				break;
437 			}
438 			goto packet_complete;
439 
440 		case PS2_WHEEL5_DELTA_Z:
441 			if (code & 0x10) {
442 				/* fourth physical button */
443 				VUID_PUTNEXT(qp, (uchar_t)BUT(4),
444 				    FE_PAIR_NONE, 0, 1);
445 				VUID_PUTNEXT(qp, (uchar_t)BUT(4),
446 				    FE_PAIR_NONE, 0, 0);
447 			} else if (code & 0x20) {
448 				/* fifth physical button */
449 				VUID_PUTNEXT(qp, (uchar_t)BUT(5),
450 				    FE_PAIR_NONE, 0, 1);
451 				VUID_PUTNEXT(qp, (uchar_t)BUT(5),
452 				    FE_PAIR_NONE, 0, 0);
453 			}
454 			/*FALLTHROUGH*/
455 
456 		case PS2_WHEEL_DELTA_Z:
457 			/*
458 			 * Check whether reporting vertical wheel
459 			 * movements is enabled
460 			 */
461 			code &= 0xf;
462 
463 			if (STATEP->wheel_state_bf & (1 <<
464 			    VUIDMICE_VERTICAL_WHEEL_ID)) {
465 				/*
466 				 * PS/2 mouse reports -ve values
467 				 * when the wheel is scrolled up. So
468 				 * we need to convert it into +ve as
469 				 * X interprets a +ve value as wheel up event.
470 				 * Same is true for the horizontal wheel also.
471 				 * The mouse reports 0xf when scrolled up
472 				 * and 0x1 when scrolled down. This observation
473 				 * is based on Logitech, HCL,
474 				 * Microsoft and Black Cat mouse only
475 				 */
476 				if (code == 0xf) {
477 					/* negative Z - wheel up */
478 					code |= 0xfffffff0;
479 					vuidmice_send_wheel_event(qp, 0,
480 					    FE_PAIR_NONE, 0, -code);
481 				} else if (code == 0x01) {
482 					/* positive Z - wheel down */
483 					vuidmice_send_wheel_event(qp, 0,
484 					    FE_PAIR_NONE, 0, -code);
485 				}
486 			}
487 
488 			/*
489 			 * Check whether reporting horizontal wheel
490 			 * movements is enabled
491 			 */
492 			if (STATEP->wheel_state_bf &
493 			    (1 << VUIDMICE_HORIZONTAL_WHEEL_ID)) {
494 
495 				/*
496 				 * The mouse return -7 and +7 when it
497 				 * is scrolled horizontally
498 				 */
499 				if (code == 0x09) {
500 					/* negative Z - wheel left */
501 					vuidmice_send_wheel_event(qp, 1,
502 					    FE_PAIR_NONE, 0, 1);
503 				} else if (code == 0x07) {
504 					/* positive Z - wheel right */
505 					vuidmice_send_wheel_event(qp, 1,
506 					    FE_PAIR_NONE, 0, -1);
507 				}
508 			}
509 
510 packet_complete:
511 			STATEP->state = PS2_START;
512 			/*
513 			 * If we can peek at the next mouse character, and
514 			 * its not the start of the next packet, don't use
515 			 * this packet.
516 			 */
517 			if (mp->b_wptr > mp->b_rptr &&
518 			    ((mp->b_rptr[0] ^ STATEP->sync_byte) & 0x08)) {
519 				/*
520 				 * bit 3 not set
521 				 */
522 				break;
523 			}
524 
525 			/*
526 			 * send the info to the next level --
527 			 * need to send multiple events if we have both
528 			 * a delta *AND* button event(s)
529 			 */
530 
531 			/* motion has occurred ... */
532 			if (STATEP->deltax)
533 				VUID_PUTNEXT(qp, (uchar_t)LOC_X_DELTA,
534 				    FE_PAIR_ABSOLUTE, (uchar_t)LOC_X_ABSOLUTE,
535 				    STATEP->deltax);
536 
537 			if (STATEP->deltay)
538 				VUID_PUTNEXT(qp, (uchar_t)LOC_Y_DELTA,
539 				    FE_PAIR_ABSOLUTE, (uchar_t)LOC_Y_ABSOLUTE,
540 				    STATEP->deltay);
541 
542 			STATEP->deltax = STATEP->deltay = 0;
543 			break;
544 
545 		case PS2_WAIT_RESET_ACK:
546 			if (code != MSE_ACK) {
547 				break;
548 			}
549 
550 			/*
551 			 * On Dell latitude D800, we find that the MSE_ACK is
552 			 * coming up even after timeout in VUID_OPEN during
553 			 * early boot. So here (PS2_WAIT_RESET_ACK) we check
554 			 * if timeout happened before, if true, we reset the
555 			 * timeout to restart the initialization.
556 			 */
557 			if (STATEP->inited & PS2_FLAG_INIT_TIMEOUT) {
558 				STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
559 				vuid_set_timeout(qp, PS2_INIT_TMOUT_RESET);
560 			}
561 
562 			STATEP->state = PS2_WAIT_RESET_AA;
563 			break;
564 
565 		case PS2_WAIT_RESET_AA:
566 			if (code != MSE_AA) {
567 				break;
568 			}
569 			STATEP->state = PS2_WAIT_RESET_00;
570 			break;
571 
572 		case PS2_WAIT_RESET_00:
573 			if (code != MSE_00) {
574 				break;
575 			}
576 
577 			/* Reset has been ok */
578 			vuid_cancel_timeout(qp);
579 
580 			STATEP->state = PS2_WAIT_SETRES0_ACK1;
581 
582 			/* Set timeout for set res */
583 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
584 
585 			put1(WR(qp), MSESETRES);
586 			break;
587 
588 		case PS2_WAIT_SETRES0_ACK1:
589 			if (code != MSE_ACK) {
590 				break;
591 			}
592 			STATEP->state = PS2_WAIT_SETRES0_ACK2;
593 			put1(WR(qp), 0);
594 			break;
595 
596 		case PS2_WAIT_SETRES0_ACK2:
597 		case PS2_WAIT_SCALE1_1_ACK:
598 		case PS2_WAIT_SCALE1_2_ACK:
599 			if (code != MSE_ACK) {
600 				break;
601 			}
602 			STATEP->state++;
603 			put1(WR(qp), MSESCALE1);
604 			break;
605 
606 		case PS2_WAIT_SCALE1_3_ACK:
607 			if (code != MSE_ACK) {
608 				break;
609 			}
610 
611 			/* Set res and scale have been ok */
612 			vuid_cancel_timeout(qp);
613 
614 			STATEP->state = PS2_WAIT_STATREQ_ACK;
615 
616 			/* Set timeout for status request */
617 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
618 
619 			put1(WR(qp), MSESTATREQ);
620 
621 			break;
622 
623 		case PS2_WAIT_STATREQ_ACK:
624 			if (code != MSE_ACK) {
625 				break;
626 			}
627 			STATEP->state = PS2_WAIT_STATUS_1;
628 			break;
629 
630 		case PS2_WAIT_STATUS_1:
631 			STATEP->state = PS2_WAIT_STATUS_BUTTONS;
632 			break;
633 
634 		case PS2_WAIT_STATUS_BUTTONS:
635 			if (code != 0) {
636 				STATEP->nbuttons = (uchar_t)code;
637 				STATEP->state = (uchar_t)PS2_WAIT_STATUS_REV;
638 			} else {
639 #if	defined(VUID3PS2)
640 				/*
641 				 * It seems that there are some 3-button mice
642 				 * that don't play the Logitech autodetect
643 				 * game.  One is a Mouse Systems mouse OEM'ed
644 				 * by Intergraph.
645 				 *
646 				 * Until we find out how to autodetect these
647 				 * mice, we'll assume that if we're being
648 				 * compiled as vuid3ps2 and the mouse doesn't
649 				 * play the autodetect game, it's a 3-button
650 				 * mouse.  This effectively disables
651 				 * autodetect for mice using vuid3ps2, but
652 				 * since vuid3ps2 is used only on x86 where
653 				 * we currently assume manual configuration,
654 				 * this shouldn't be a problem.  At some point
655 				 * in the future when we *do* start using
656 				 * autodetect on x86, we should probably define
657 				 * VUIDPS2 instead of VUID3PS2.  Even then,
658 				 * we could leave this code so that *some*
659 				 * mice could use autodetect and others not.
660 				 */
661 				STATEP->nbuttons = 3;
662 #else
663 				STATEP->nbuttons = 2;
664 #endif
665 				STATEP->state = PS2_WAIT_STATUS_3;
666 			}
667 			break;
668 
669 		case PS2_WAIT_STATUS_REV:
670 			/*FALLTHROUGH*/
671 
672 		case PS2_WAIT_STATUS_3:
673 
674 			/* Status request has been ok */
675 			vuid_cancel_timeout(qp);
676 
677 			/* Set timeout for set res or sample rate */
678 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
679 
680 			/*
681 			 * Start the wheel-mouse detection code.  First, we look
682 			 * for standard wheel mice.  If we set the sample rate
683 			 * to 200, 100, and then 80 and finally request the
684 			 * device ID, a wheel mouse will return an ID of 0x03.
685 			 * After that, we'll try for the wheel+5 variety.  The
686 			 * incantation in this case is 200, 200, and 80.  We'll
687 			 * get 0x04 back in that case.
688 			 */
689 			if (STATEP->inited & PS2_FLAG_NO_EXTN) {
690 				STATEP->state = PS2_WAIT_SETRES3_ACK1;
691 				put1(WR(qp), MSESETRES);
692 			} else {
693 				STATEP->state = PS2_WAIT_WHEEL_SMPL1_CMD_ACK;
694 				put1(WR(qp), MSECHGMOD);
695 			}
696 
697 			break;
698 		case PS2_WAIT_WHEEL_SMPL1_CMD_ACK:
699 			if (code != MSE_ACK) {
700 				break;
701 			}
702 			STATEP->state = PS2_WAIT_WHEEL_SMPL1_RATE_ACK;
703 			put1(WR(qp), 200);
704 			break;
705 		case PS2_WAIT_WHEEL_SMPL1_RATE_ACK:
706 			if (code != MSE_ACK) {
707 				break;
708 			}
709 			STATEP->state = PS2_WAIT_WHEEL_SMPL2_CMD_ACK;
710 			put1(WR(qp), MSECHGMOD);
711 			break;
712 
713 		case PS2_WAIT_WHEEL_SMPL2_CMD_ACK:
714 			if (code != MSE_ACK) {
715 				break;
716 			}
717 			STATEP->state = PS2_WAIT_WHEEL_SMPL2_RATE_ACK;
718 			put1(WR(qp), 100);
719 			break;
720 
721 		case PS2_WAIT_WHEEL_SMPL2_RATE_ACK:
722 			if (code != MSE_ACK) {
723 				break;
724 			}
725 			STATEP->state = PS2_WAIT_WHEEL_SMPL3_CMD_ACK;
726 			put1(WR(qp), MSECHGMOD);
727 			break;
728 
729 		case PS2_WAIT_WHEEL_SMPL3_CMD_ACK:
730 			if (code != MSE_ACK) {
731 				break;
732 			}
733 			STATEP->state = PS2_WAIT_WHEEL_SMPL3_RATE_ACK;
734 			put1(WR(qp), 80);
735 			break;
736 
737 		case PS2_WAIT_WHEEL_SMPL3_RATE_ACK:
738 			if (code != MSE_ACK) {
739 				break;
740 			}
741 
742 			/* Set sample rate has been ok */
743 			vuid_cancel_timeout(qp);
744 
745 			STATEP->state = PS2_WAIT_WHEEL_DEV_CMD;
746 
747 			/* Set timeout for get dev */
748 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
749 
750 			put1(WR(qp), MSEGETDEV);
751 			break;
752 
753 		case PS2_WAIT_WHEEL_DEV_CMD:
754 			if (code != MSE_ACK) {
755 				break;
756 			}
757 			STATEP->state = PS2_WAIT_WHEEL_DEV_ACK;
758 			break;
759 
760 		case PS2_WAIT_WHEEL_DEV_ACK:
761 
762 			/* Get dev has been ok */
763 			vuid_cancel_timeout(qp);
764 
765 			if (code != 0x03) {
766 				STATEP->state = PS2_WAIT_SETRES3_ACK1;
767 
768 				/* Set timeout for set res */
769 				vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
770 
771 				put1(WR(qp), MSESETRES);
772 
773 				break;
774 			}
775 
776 			STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL;
777 
778 			/*
779 			 * Found wheel. By default enable the wheel.
780 			 */
781 			STATEP->wheel_state_bf |= VUID_WHEEL_STATE_ENABLED;
782 
783 			STATEP->state = PS2_WAIT_WHEEL5_SMPL1_CMD_ACK;
784 
785 			/* Set timeout for set sample rate */
786 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
787 
788 			/* We're on a roll - try for wheel+5 */
789 			put1(WR(qp), MSECHGMOD);
790 
791 			break;
792 
793 		case PS2_WAIT_WHEEL5_SMPL1_CMD_ACK:
794 			if (code != MSE_ACK) {
795 				break;
796 			}
797 			STATEP->state = PS2_WAIT_WHEEL5_SMPL1_RATE_ACK;
798 			put1(WR(qp), 200);
799 			break;
800 
801 		case PS2_WAIT_WHEEL5_SMPL1_RATE_ACK:
802 			if (code != MSE_ACK) {
803 				break;
804 			}
805 			STATEP->state = PS2_WAIT_WHEEL5_SMPL2_CMD_ACK;
806 			put1(WR(qp), MSECHGMOD);
807 			break;
808 
809 		case PS2_WAIT_WHEEL5_SMPL2_CMD_ACK:
810 			if (code != MSE_ACK) {
811 				break;
812 			}
813 			STATEP->state = PS2_WAIT_WHEEL5_SMPL2_RATE_ACK;
814 			put1(WR(qp), 200);
815 			break;
816 
817 		case PS2_WAIT_WHEEL5_SMPL2_RATE_ACK:
818 			if (code != MSE_ACK) {
819 				break;
820 			}
821 			STATEP->state = PS2_WAIT_WHEEL5_SMPL3_CMD_ACK;
822 			put1(WR(qp), MSECHGMOD);
823 			break;
824 
825 		case PS2_WAIT_WHEEL5_SMPL3_CMD_ACK:
826 			if (code != MSE_ACK) {
827 				break;
828 			}
829 			STATEP->state = PS2_WAIT_WHEEL5_SMPL3_RATE_ACK;
830 			put1(WR(qp), 80);
831 			break;
832 
833 		case PS2_WAIT_WHEEL5_SMPL3_RATE_ACK:
834 			if (code != MSE_ACK) {
835 				break;
836 			}
837 
838 			/* Set sample rate has been ok */
839 			vuid_cancel_timeout(qp);
840 
841 			STATEP->state = PS2_WAIT_WHEEL5_DEV_CMD;
842 
843 			/* Set timeout for wheel5 get dev */
844 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
845 
846 			put1(WR(qp), MSEGETDEV);
847 
848 			break;
849 
850 		case PS2_WAIT_WHEEL5_DEV_CMD:
851 			if (code != MSE_ACK) {
852 				break;
853 			}
854 			STATEP->state = PS2_WAIT_WHEEL5_DEV_ACK;
855 			break;
856 
857 		case PS2_WAIT_WHEEL5_DEV_ACK:
858 			if (code == 0x04) {
859 				STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL5;
860 				STATEP->nbuttons	= 5;
861 
862 				/*
863 				 * Found wheel. By default enable the wheel.
864 				 */
865 				STATEP->wheel_state_bf |=
866 				    VUID_WHEEL_STATE_ENABLED <<
867 				    MOUSE_MODE_WHEEL;
868 			}
869 
870 			/* Wheel5 get dev has been ok */
871 			vuid_cancel_timeout(qp);
872 
873 			/* FALLTHROUGH */
874 
875 		case PS2_WAIT_SETRES3_CMD:
876 			STATEP->state = PS2_WAIT_SETRES3_ACK1;
877 
878 			/* Set timeout for set res */
879 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
880 
881 			put1(WR(qp), MSESETRES);
882 
883 			break;
884 
885 		case PS2_WAIT_SETRES3_ACK1:
886 			if (code != MSE_ACK) {
887 				break;
888 			}
889 			STATEP->state = PS2_WAIT_SETRES3_ACK2;
890 			put1(WR(qp), 3);
891 			break;
892 
893 		case PS2_WAIT_SETRES3_ACK2:
894 			if (code != MSE_ACK) {
895 				break;
896 			}
897 
898 			/* Set res has been ok */
899 			vuid_cancel_timeout(qp);
900 
901 			STATEP->state = PS2_WAIT_STREAM_ACK;
902 
903 			/* Set timeout for enable */
904 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
905 
906 			put1(WR(qp), MSESTREAM);
907 
908 			break;
909 
910 		case PS2_WAIT_STREAM_ACK:
911 			if (code != MSE_ACK) {
912 				break;
913 			}
914 			STATEP->state = PS2_WAIT_ON_ACK;
915 			put1(WR(qp), MSEON);
916 			break;
917 
918 		case PS2_WAIT_ON_ACK:
919 			if (code != MSE_ACK) {
920 				break;
921 			}
922 
923 			/* Enable has been ok */
924 			vuid_cancel_timeout(qp);
925 
926 			STATEP->state = PS2_START;
927 			break;
928 		}
929 	}
930 	freemsg(mp);
931 }
932