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