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
vuid_set_timeout(queue_t * const qp,clock_t time)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
vuid_cancel_timeout(queue_t * const qp)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
vuidmice_send_wheel_event(queue_t * const qp,uchar_t event_id,uchar_t event_pair_type,uchar_t event_pair,int event_value)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
sendButtonEvent(queue_t * const qp)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
put1(queue_t * const qp,int c)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
vuidmice_start_wdc_or_setres(queue_t * qp)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
VUID_OPEN(queue_t * const qp)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
VUID_CLOSE(queue_t * const qp)264 VUID_CLOSE(queue_t *const qp)
265 {
266 	if (STATEP->init_tid != 0)
267 		vuid_cancel_timeout(qp);
268 }
269 
270 static void
VUID_INIT_TIMEOUT(void * q)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
VUID_QUEUE(queue_t * const qp,mblk_t * mp)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 				 * A response with length 1 from the mouse
626 				 * driver can be either an ACK (the first part
627 				 * of the reset reply) or either MSEERROR or
628 				 * MSERESEND.  Issue another reset if either
629 				 * of the latter are received.  For mice that
630 				 * are not connected, MSERESEND is received
631 				 * quickly.
632 				 */
633 
634 				if (code == MSE_ACK)
635 					break;
636 
637 				if (++STATEP->init_count >=
638 				    PS2_MAX_INIT_COUNT) {
639 					STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
640 					STATEP->state = PS2_WAIT_FOR_AA;
641 				} else {
642 					put1(WR(qp), MSERESET);
643 				}
644 
645 				break;
646 
647 			} else if (length != 2) {
648 				break;
649 			}
650 
651 			/*
652 			 * The only possible 2-byte reply from mouse8042 is
653 			 * 0xAA 0x00.  If the mouse doesn't send that, mouse8042
654 			 * will send a 1-byte error message, handled above by
655 			 * resetting the mouse.
656 			 */
657 
658 			/* Skip past the 0x00 (since `code' contains 0xAA) */
659 			mp->b_rptr += 1;
660 
661 			/* Reset completed successfully */
662 
663 			STATEP->state = PS2_WAIT_SETRES0_ACK1;
664 
665 			/* Set timeout for set res */
666 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
667 
668 			/* Begin Logitech autodetect sequence */
669 			put1(WR(qp), MSESETRES);
670 			break;
671 
672 		case PS2_WAIT_SETRES0_ACK1:
673 			if (code != MSE_ACK) {
674 				break;
675 			}
676 			STATEP->state = PS2_WAIT_SETRES0_ACK2;
677 			put1(WR(qp), 0);
678 			break;
679 
680 		case PS2_WAIT_SETRES0_ACK2:
681 		case PS2_WAIT_SCALE1_1_ACK:
682 		case PS2_WAIT_SCALE1_2_ACK:
683 			if (code != MSE_ACK) {
684 				break;
685 			}
686 			STATEP->state++;
687 			put1(WR(qp), MSESCALE1);
688 			break;
689 
690 		case PS2_WAIT_SCALE1_3_ACK:
691 			if (code != MSE_ACK) {
692 				break;
693 			}
694 
695 			/* Set res and scale have been ok */
696 			vuid_cancel_timeout(qp);
697 
698 			STATEP->state = PS2_WAIT_STATREQ_ACK;
699 
700 			/* Set timeout for status request */
701 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
702 
703 			put1(WR(qp), MSESTATREQ);
704 
705 			break;
706 
707 		case PS2_WAIT_STATREQ_ACK:
708 			if (code != MSE_ACK) {
709 				break;
710 			}
711 			STATEP->state = PS2_WAIT_STATUS_1;
712 			break;
713 
714 		case PS2_WAIT_STATUS_1:
715 			STATEP->state = PS2_WAIT_STATUS_BUTTONS;
716 			break;
717 
718 		case PS2_WAIT_STATUS_BUTTONS:
719 			if (code != 0) {
720 				STATEP->nbuttons = (uchar_t)code;
721 				STATEP->state = (uchar_t)PS2_WAIT_STATUS_REV;
722 			} else {
723 #if	defined(VUID3PS2)
724 				/*
725 				 * It seems that there are some 3-button mice
726 				 * that don't play the Logitech autodetect
727 				 * game.  One is a Mouse Systems mouse OEM'ed
728 				 * by Intergraph.
729 				 *
730 				 * Until we find out how to autodetect these
731 				 * mice, we'll assume that if we're being
732 				 * compiled as vuid3ps2 and the mouse doesn't
733 				 * play the autodetect game, it's a 3-button
734 				 * mouse.  This effectively disables
735 				 * autodetect for mice using vuid3ps2, but
736 				 * since vuid3ps2 is used only on x86 where
737 				 * we currently assume manual configuration,
738 				 * this shouldn't be a problem.  At some point
739 				 * in the future when we *do* start using
740 				 * autodetect on x86, we should probably define
741 				 * VUIDPS2 instead of VUID3PS2.  Even then,
742 				 * we could leave this code so that *some*
743 				 * mice could use autodetect and others not.
744 				 */
745 				STATEP->nbuttons = 3;
746 #else
747 				STATEP->nbuttons = 2;
748 #endif
749 				STATEP->state = PS2_WAIT_STATUS_3;
750 			}
751 			break;
752 
753 		case PS2_WAIT_STATUS_REV:
754 			/*FALLTHROUGH*/
755 
756 		case PS2_WAIT_STATUS_3:
757 
758 			/* Status request completed successfully */
759 			vuid_cancel_timeout(qp);
760 
761 			vuidmice_start_wdc_or_setres(qp);
762 			break;
763 
764 		case PS2_WAIT_WHEEL_SMPL1_CMD_ACK:
765 			if (code != MSE_ACK) {
766 				break;
767 			}
768 			STATEP->state = PS2_WAIT_WHEEL_SMPL1_RATE_ACK;
769 			put1(WR(qp), 200);
770 			break;
771 		case PS2_WAIT_WHEEL_SMPL1_RATE_ACK:
772 			if (code != MSE_ACK) {
773 				break;
774 			}
775 			STATEP->state = PS2_WAIT_WHEEL_SMPL2_CMD_ACK;
776 			put1(WR(qp), MSECHGMOD);
777 			break;
778 
779 		case PS2_WAIT_WHEEL_SMPL2_CMD_ACK:
780 			if (code != MSE_ACK) {
781 				break;
782 			}
783 			STATEP->state = PS2_WAIT_WHEEL_SMPL2_RATE_ACK;
784 			put1(WR(qp), 100);
785 			break;
786 
787 		case PS2_WAIT_WHEEL_SMPL2_RATE_ACK:
788 			if (code != MSE_ACK) {
789 				break;
790 			}
791 			STATEP->state = PS2_WAIT_WHEEL_SMPL3_CMD_ACK;
792 			put1(WR(qp), MSECHGMOD);
793 			break;
794 
795 		case PS2_WAIT_WHEEL_SMPL3_CMD_ACK:
796 			if (code != MSE_ACK) {
797 				break;
798 			}
799 			STATEP->state = PS2_WAIT_WHEEL_SMPL3_RATE_ACK;
800 			put1(WR(qp), 80);
801 			break;
802 
803 		case PS2_WAIT_WHEEL_SMPL3_RATE_ACK:
804 			if (code != MSE_ACK) {
805 				break;
806 			}
807 
808 			/* Set sample rate completed successfully */
809 			vuid_cancel_timeout(qp);
810 
811 			STATEP->state = PS2_WAIT_WHEEL_DEV_CMD;
812 
813 			/* Set timeout for get dev */
814 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
815 
816 			put1(WR(qp), MSEGETDEV);
817 			break;
818 
819 		case PS2_WAIT_WHEEL_DEV_CMD:
820 			if (code != MSE_ACK) {
821 				break;
822 			}
823 			STATEP->state = PS2_WAIT_WHEEL_DEV_ACK;
824 			break;
825 
826 		case PS2_WAIT_WHEEL_DEV_ACK:
827 
828 			/* Get dev completed successfully */
829 			vuid_cancel_timeout(qp);
830 
831 			if (code != 0x03) {
832 				STATEP->state = PS2_WAIT_SETRES3_ACK1;
833 
834 				/* Set timeout for set res */
835 				vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
836 
837 				put1(WR(qp), MSESETRES);
838 
839 				break;
840 			}
841 
842 			STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL;
843 
844 			/*
845 			 * Found wheel. By default enable the wheel.
846 			 */
847 			STATEP->wheel_state_bf |= VUID_WHEEL_STATE_ENABLED;
848 
849 			STATEP->state = PS2_WAIT_WHEEL5_SMPL1_CMD_ACK;
850 
851 			/* Set timeout for set sample rate */
852 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
853 
854 			/* We're on a roll - try for wheel+5 */
855 			put1(WR(qp), MSECHGMOD);
856 
857 			break;
858 
859 		case PS2_WAIT_WHEEL5_SMPL1_CMD_ACK:
860 			if (code != MSE_ACK) {
861 				break;
862 			}
863 			STATEP->state = PS2_WAIT_WHEEL5_SMPL1_RATE_ACK;
864 			put1(WR(qp), 200);
865 			break;
866 
867 		case PS2_WAIT_WHEEL5_SMPL1_RATE_ACK:
868 			if (code != MSE_ACK) {
869 				break;
870 			}
871 			STATEP->state = PS2_WAIT_WHEEL5_SMPL2_CMD_ACK;
872 			put1(WR(qp), MSECHGMOD);
873 			break;
874 
875 		case PS2_WAIT_WHEEL5_SMPL2_CMD_ACK:
876 			if (code != MSE_ACK) {
877 				break;
878 			}
879 			STATEP->state = PS2_WAIT_WHEEL5_SMPL2_RATE_ACK;
880 			put1(WR(qp), 200);
881 			break;
882 
883 		case PS2_WAIT_WHEEL5_SMPL2_RATE_ACK:
884 			if (code != MSE_ACK) {
885 				break;
886 			}
887 			STATEP->state = PS2_WAIT_WHEEL5_SMPL3_CMD_ACK;
888 			put1(WR(qp), MSECHGMOD);
889 			break;
890 
891 		case PS2_WAIT_WHEEL5_SMPL3_CMD_ACK:
892 			if (code != MSE_ACK) {
893 				break;
894 			}
895 			STATEP->state = PS2_WAIT_WHEEL5_SMPL3_RATE_ACK;
896 			put1(WR(qp), 80);
897 			break;
898 
899 		case PS2_WAIT_WHEEL5_SMPL3_RATE_ACK:
900 			if (code != MSE_ACK) {
901 				break;
902 			}
903 
904 			/* Set sample rate completed successfully */
905 			vuid_cancel_timeout(qp);
906 
907 			STATEP->state = PS2_WAIT_WHEEL5_DEV_CMD;
908 
909 			/* Set timeout for wheel5 get dev */
910 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
911 
912 			put1(WR(qp), MSEGETDEV);
913 
914 			break;
915 
916 		case PS2_WAIT_WHEEL5_DEV_CMD:
917 			if (code != MSE_ACK) {
918 				break;
919 			}
920 			STATEP->state = PS2_WAIT_WHEEL5_DEV_ACK;
921 			break;
922 
923 		case PS2_WAIT_WHEEL5_DEV_ACK:
924 			if (code == 0x04) {
925 				STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL5;
926 				STATEP->nbuttons	= 5;
927 
928 				/*
929 				 * Found wheel. By default enable the wheel.
930 				 */
931 				STATEP->wheel_state_bf |=
932 				    VUID_WHEEL_STATE_ENABLED <<
933 				    MOUSE_MODE_WHEEL;
934 			}
935 
936 			/* Wheel5 get dev completed successfully */
937 			vuid_cancel_timeout(qp);
938 
939 			/* FALLTHROUGH */
940 
941 		case PS2_WAIT_SETRES3_CMD:
942 			STATEP->state = PS2_WAIT_SETRES3_ACK1;
943 
944 			/* Set timeout for set res */
945 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
946 
947 			put1(WR(qp), MSESETRES);
948 
949 			break;
950 
951 		case PS2_WAIT_SETRES3_ACK1:
952 			if (code != MSE_ACK) {
953 				break;
954 			}
955 			STATEP->state = PS2_WAIT_SETRES3_ACK2;
956 			put1(WR(qp), 3);
957 			break;
958 
959 		case PS2_WAIT_SETRES3_ACK2:
960 			if (code != MSE_ACK) {
961 				break;
962 			}
963 
964 			/* Set res completed successfully */
965 			vuid_cancel_timeout(qp);
966 
967 			STATEP->state = PS2_WAIT_STREAM_ACK;
968 
969 			/* Set timeout for enable */
970 			vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
971 
972 			put1(WR(qp), MSESTREAM);
973 
974 			break;
975 
976 		case PS2_WAIT_STREAM_ACK:
977 			if (code != MSE_ACK) {
978 				break;
979 			}
980 			STATEP->state = PS2_WAIT_ON_ACK;
981 			put1(WR(qp), MSEON);
982 			break;
983 
984 		case PS2_WAIT_ON_ACK:
985 			if (code != MSE_ACK) {
986 				break;
987 			}
988 
989 			/* Enable completed successfully */
990 
991 			/*
992 			 * The entire initialization sequence
993 			 * is complete.  Now, we can clear the
994 			 * init_count retry counter.
995 			 */
996 			STATEP->init_count = 0;
997 			vuid_cancel_timeout(qp);
998 
999 
1000 			STATEP->state = PS2_START;
1001 			break;
1002 		}
1003 	}
1004 	freemsg(mp);
1005 }
1006