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