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