xref: /titanic_50/usr/src/uts/common/io/mouse8042.c (revision 7a286c471efbab8562f7655a82931904703fffe0)
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 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 /*
26  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 
31 /*
32  * PS/2 type Mouse Module - Streams
33  */
34 
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/kmem.h>
38 #include <sys/signal.h>
39 #include <sys/errno.h>
40 #include <sys/file.h>
41 #include <sys/termio.h>
42 #include <sys/stream.h>
43 #include <sys/stropts.h>
44 #include <sys/strtty.h>
45 #include <sys/strsun.h>
46 #include <sys/debug.h>
47 #include <sys/ddi.h>
48 #include <sys/stat.h>
49 #include <sys/cmn_err.h>
50 #include <sys/sunddi.h>
51 
52 #include <sys/promif.h>
53 #include <sys/cred.h>
54 
55 #include <sys/i8042.h>
56 #include <sys/note.h>
57 #include <sys/mouse.h>
58 
59 #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
60 
61 #define	MOUSE8042_INTERNAL_OPEN(minor)	(((minor) & 0x1) == 1)
62 #define	MOUSE8042_MINOR_TO_INSTANCE(minor)	((minor) / 2)
63 #define	MOUSE8042_INTERNAL_MINOR(minor)		((minor) + 1)
64 
65 #define	MOUSE8042_RESET_TIMEOUT_USECS	500000	/* 500 ms */
66 
67 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
68 extern void consconfig_link(major_t major, minor_t minor);
69 extern int consconfig_unlink(major_t major, minor_t minor);
70 
71 
72 /*
73  *
74  * Local Static Data
75  *
76  */
77 
78 /*
79  * We only support one instance.  Yes, it's theoretically possible to
80  * plug in more than one, but it's not worth the implementation cost.
81  *
82  * The introduction of USB keyboards might make it worth reassessing
83  * this decision, as they might free up the keyboard port for a second
84  * PS/2 style mouse.
85  */
86 static dev_info_t *mouse8042_dip;
87 
88 /*
89  * RESET states
90  */
91 typedef enum {
92 	MSE_RESET_IDLE,	/* No reset in progress */
93 	MSE_RESET_PRE,	/* Send reset, waiting for ACK */
94 	MSE_RESET_ACK,	/* Got ACK, waiting for 0xAA */
95 	MSE_RESET_AA,	/* Got 0xAA, waiting for 0x00 */
96 	MSE_RESET_FAILED
97 } mouse8042_reset_state_e;
98 
99 struct mouse_state {
100 	queue_t	*ms_rqp;
101 	queue_t	*ms_wqp;
102 	ddi_iblock_cookie_t	ms_iblock_cookie;
103 	ddi_acc_handle_t	ms_handle;
104 	uint8_t			*ms_addr;
105 	kmutex_t		ms_mutex;
106 
107 	minor_t			ms_minor;
108 	boolean_t		ms_opened;
109 	kmutex_t		reset_mutex;
110 	mouse8042_reset_state_e	reset_state;
111 	timeout_id_t		reset_tid;
112 	int			ready;
113 	mblk_t			*reply_mp;
114 	bufcall_id_t		bc_id;
115 };
116 
117 static uint_t mouse8042_intr(caddr_t arg);
118 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
119 		cred_t *cred_p);
120 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
121 static int mouse8042_wsrv(queue_t *qp);
122 
123 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
124 		void *arg, void **result);
125 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
126 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
127 
128 
129 /*
130  * Streams module info.
131  */
132 #define	MODULE_NAME	"mouse8042"
133 
134 static struct module_info	mouse8042_minfo = {
135 	23,		/* Module ID number */
136 	MODULE_NAME,
137 	0, INFPSZ,	/* minimum & maximum packet sizes */
138 	256, 128	/* hi and low water marks */
139 };
140 
141 static struct qinit mouse8042_rinit = {
142 	NULL,		/* put */
143 	NULL,		/* service */
144 	mouse8042_open,
145 	mouse8042_close,
146 	NULL,		/* admin */
147 	&mouse8042_minfo,
148 	NULL		/* statistics */
149 };
150 
151 static struct qinit mouse8042_winit = {
152 	putq,		/* put */
153 	mouse8042_wsrv,	/* service */
154 	NULL,		/* open */
155 	NULL,		/* close */
156 	NULL,		/* admin */
157 	&mouse8042_minfo,
158 	NULL		/* statistics */
159 };
160 
161 static struct streamtab mouse8042_strinfo = {
162 	&mouse8042_rinit,
163 	&mouse8042_winit,
164 	NULL,		/* muxrinit */
165 	NULL,		/* muxwinit */
166 };
167 
168 /*
169  * Local Function Declarations
170  */
171 
172 static struct cb_ops	mouse8042_cb_ops = {
173 	nodev,			/* open */
174 	nodev,			/* close */
175 	nodev,			/* strategy */
176 	nodev,			/* print */
177 	nodev,			/* dump */
178 	nodev,			/* read */
179 	nodev,			/* write */
180 	nodev,			/* ioctl */
181 	nodev,			/* devmap */
182 	nodev,			/* mmap */
183 	nodev,			/* segmap */
184 	nochpoll,		/* poll */
185 	ddi_prop_op,		/* cb_prop_op */
186 	&mouse8042_strinfo,	/* streamtab  */
187 	D_MP | D_NEW
188 };
189 
190 
191 static struct dev_ops	mouse8042_ops = {
192 	DEVO_REV,		/* devo_rev, */
193 	0,			/* refcnt  */
194 	mouse8042_getinfo,	/* getinfo */
195 	nulldev,		/* identify */
196 	nulldev,		/* probe */
197 	mouse8042_attach,	/* attach */
198 	mouse8042_detach,	/* detach */
199 	nodev,			/* reset */
200 	&mouse8042_cb_ops,	/* driver operations */
201 	(struct bus_ops *)0,	/* bus operations */
202 	NULL,			/* power */
203 	ddi_quiesce_not_needed,		/* quiesce */
204 };
205 
206 /*
207  * This is the loadable module wrapper.
208  */
209 #include <sys/modctl.h>
210 
211 extern struct mod_ops mod_driverops;
212 
213 /*
214  * Module linkage information for the kernel.
215  */
216 
217 static struct modldrv modldrv = {
218 	&mod_driverops, /* Type of module.  This one is a driver */
219 	"PS/2 Mouse",
220 	&mouse8042_ops,	/* driver ops */
221 };
222 
223 static struct modlinkage modlinkage = {
224 	MODREV_1,
225 	(void *)&modldrv,
226 	NULL
227 };
228 
229 /*
230  * This is the driver initialization routine.
231  */
232 int
233 _init()
234 {
235 	int	rv;
236 
237 	rv = mod_install(&modlinkage);
238 	return (rv);
239 }
240 
241 
242 int
243 _fini(void)
244 {
245 	return (mod_remove(&modlinkage));
246 }
247 
248 
249 int
250 _info(struct modinfo *modinfop)
251 {
252 	return (mod_info(&modlinkage, modinfop));
253 }
254 
255 static int
256 mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
257 {
258 	struct mouse_state *state;
259 	mblk_t *mp;
260 	int instance = ddi_get_instance(dip);
261 	static ddi_device_acc_attr_t attr = {
262 		DDI_DEVICE_ATTR_V0,
263 		DDI_NEVERSWAP_ACC,
264 		DDI_STRICTORDER_ACC,
265 	};
266 	int rc;
267 
268 
269 	if (cmd == DDI_RESUME) {
270 		state = (struct mouse_state *)ddi_get_driver_private(dip);
271 
272 		/* Ready to handle inbound data from mouse8042_intr */
273 		state->ready = 1;
274 
275 		/*
276 		 * Send a 0xaa 0x00 upstream.
277 		 * This causes the vuid module to reset the mouse.
278 		 */
279 		if (state->ms_rqp != NULL) {
280 			if (mp = allocb(1, BPRI_MED)) {
281 				*mp->b_wptr++ = 0xaa;
282 				putnext(state->ms_rqp, mp);
283 			}
284 			if (mp = allocb(1, BPRI_MED)) {
285 				*mp->b_wptr++ = 0x0;
286 				putnext(state->ms_rqp, mp);
287 			}
288 		}
289 		return (DDI_SUCCESS);
290 	}
291 
292 	if (cmd != DDI_ATTACH)
293 		return (DDI_FAILURE);
294 
295 	if (mouse8042_dip != NULL)
296 		return (DDI_FAILURE);
297 
298 	/* allocate and initialize state structure */
299 	state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
300 	state->ms_opened = B_FALSE;
301 	state->reset_state = MSE_RESET_IDLE;
302 	state->reset_tid = 0;
303 	state->bc_id = 0;
304 	ddi_set_driver_private(dip, state);
305 
306 	/*
307 	 * In order to support virtual keyboard/mouse, we should distinguish
308 	 * between internal virtual open and external physical open.
309 	 *
310 	 * When the physical devices are opened by application, they will
311 	 * be unlinked from the virtual device and their data stream will
312 	 * not be sent to the virtual device. When the opened physical
313 	 * devices are closed, they will be relinked to the virtual devices.
314 	 *
315 	 * All these automatic switch between virtual and physical are
316 	 * transparent.
317 	 *
318 	 * So we change minor node numbering scheme to be:
319 	 * 	external node minor num == instance * 2
320 	 *	internal node minor num == instance * 2 + 1
321 	 */
322 	rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
323 	    DDI_NT_MOUSE, NULL);
324 	if (rc != DDI_SUCCESS) {
325 		goto fail_1;
326 	}
327 
328 	if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
329 	    instance * 2 + 1) != DDI_SUCCESS) {
330 		goto fail_2;
331 	}
332 
333 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
334 	    (offset_t)0, (offset_t)0, &attr, &state->ms_handle);
335 	if (rc != DDI_SUCCESS) {
336 		goto fail_2;
337 	}
338 
339 	rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
340 	if (rc != DDI_SUCCESS) {
341 		goto fail_3;
342 	}
343 
344 	mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
345 	    state->ms_iblock_cookie);
346 	mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER,
347 	    state->ms_iblock_cookie);
348 
349 	rc = ddi_add_intr(dip, 0,
350 	    (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
351 	    mouse8042_intr, (caddr_t)state);
352 	if (rc != DDI_SUCCESS) {
353 		goto fail_3;
354 	}
355 
356 	mouse8042_dip = dip;
357 
358 	/* Ready to handle inbound data from mouse8042_intr */
359 	state->ready = 1;
360 
361 	/* Now that we're attached, announce our presence to the world. */
362 	ddi_report_dev(dip);
363 	return (DDI_SUCCESS);
364 
365 fail_3:
366 	ddi_regs_map_free(&state->ms_handle);
367 
368 fail_2:
369 	ddi_remove_minor_node(dip, NULL);
370 
371 fail_1:
372 	kmem_free(state, sizeof (struct mouse_state));
373 	return (rc);
374 }
375 
376 /*ARGSUSED*/
377 static int
378 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
379 {
380 	struct mouse_state *state;
381 
382 	state = ddi_get_driver_private(dip);
383 
384 	switch (cmd) {
385 	case DDI_SUSPEND:
386 		/* Ignore all data from mouse8042_intr until we fully resume */
387 		state->ready = 0;
388 		return (DDI_SUCCESS);
389 
390 	case DDI_DETACH:
391 		ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
392 		mouse8042_dip = NULL;
393 		mutex_destroy(&state->reset_mutex);
394 		mutex_destroy(&state->ms_mutex);
395 		ddi_prop_remove_all(dip);
396 		ddi_regs_map_free(&state->ms_handle);
397 		ddi_remove_minor_node(dip, NULL);
398 		kmem_free(state, sizeof (struct mouse_state));
399 		return (DDI_SUCCESS);
400 
401 	default:
402 		return (DDI_FAILURE);
403 	}
404 }
405 
406 
407 /* ARGSUSED */
408 static int
409 mouse8042_getinfo(
410     dev_info_t *dip,
411     ddi_info_cmd_t infocmd,
412     void *arg,
413     void **result)
414 {
415 	dev_t dev = (dev_t)arg;
416 	minor_t	minor = getminor(dev);
417 	int	instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
418 
419 	switch (infocmd) {
420 	case DDI_INFO_DEVT2DEVINFO:
421 		if (mouse8042_dip == NULL)
422 			return (DDI_FAILURE);
423 
424 		*result = (void *)mouse8042_dip;
425 		break;
426 	case DDI_INFO_DEVT2INSTANCE:
427 		*result = (void *)(uintptr_t)instance;
428 		break;
429 	default:
430 		return (DDI_FAILURE);
431 	}
432 	return (DDI_SUCCESS);
433 }
434 
435 /*ARGSUSED*/
436 static int
437 mouse8042_open(
438 	queue_t	*q,
439 	dev_t	*devp,
440 	int	flag,
441 	int	sflag,
442 	cred_t	*cred_p)
443 {
444 	struct mouse_state *state;
445 	minor_t	minor = getminor(*devp);
446 	int rval;
447 
448 	if (mouse8042_dip == NULL)
449 		return (ENXIO);
450 
451 	state = ddi_get_driver_private(mouse8042_dip);
452 
453 	mutex_enter(&state->ms_mutex);
454 
455 	if (state->ms_opened) {
456 		/*
457 		 * Exit if the same minor node is already open
458 		 */
459 		if (state->ms_minor == minor) {
460 			mutex_exit(&state->ms_mutex);
461 			return (0);
462 		}
463 
464 		/*
465 		 * Check whether it is switch between physical and virtual
466 		 *
467 		 * Opening from virtual while the device is being physically
468 		 * opened by an application should not happen. So we ASSERT
469 		 * this in DEBUG version, and return error in the non-DEBUG
470 		 * case.
471 		 */
472 		ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
473 
474 		if (MOUSE8042_INTERNAL_OPEN(minor)) {
475 			mutex_exit(&state->ms_mutex);
476 			return (EINVAL);
477 		}
478 
479 		/*
480 		 * Opening the physical one while it is being underneath
481 		 * the virtual one.
482 		 *
483 		 * consconfig_unlink is called to unlink this device from
484 		 * the virtual one, thus the old stream serving for this
485 		 * device under the virtual one is closed, and then the
486 		 * lower driver's close routine (here is mouse8042_close)
487 		 * is also called to accomplish the whole stream close.
488 		 * Here we have to drop the lock because mouse8042_close
489 		 * also needs the lock.
490 		 *
491 		 * For mouse, the old stream is:
492 		 *	consms->["pushmod"->]"mouse_vp driver"
493 		 *
494 		 * After the consconfig_unlink returns, the old stream is closed
495 		 * and we grab the lock again to reopen this device as normal.
496 		 */
497 		mutex_exit(&state->ms_mutex);
498 
499 		/*
500 		 * If unlink fails, fail the physical open.
501 		 */
502 		if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
503 		    MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
504 			return (rval);
505 		}
506 
507 		mutex_enter(&state->ms_mutex);
508 	}
509 
510 
511 	q->q_ptr = (caddr_t)state;
512 	WR(q)->q_ptr = (caddr_t)state;
513 	state->ms_rqp = q;
514 	state->ms_wqp = WR(q);
515 
516 	qprocson(q);
517 
518 	state->ms_minor = minor;
519 	state->ms_opened = B_TRUE;
520 
521 	mutex_exit(&state->ms_mutex);
522 
523 	return (0);
524 }
525 
526 
527 /*ARGSUSED*/
528 static int
529 mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
530 {
531 	struct mouse_state *state;
532 	minor_t	minor;
533 
534 	state = (struct mouse_state *)q->q_ptr;
535 
536 	mutex_enter(&state->ms_mutex);
537 
538 	qprocsoff(q);
539 
540 	if (state->reset_tid != 0) {
541 		(void) quntimeout(q, state->reset_tid);
542 		state->reset_tid = 0;
543 	}
544 	if (state->bc_id != 0) {
545 		(void) qunbufcall(q, state->bc_id);
546 		state->bc_id = 0;
547 	}
548 	if (state->reply_mp != NULL) {
549 		freemsg(state->reply_mp);
550 		state->reply_mp = NULL;
551 	}
552 
553 	q->q_ptr = NULL;
554 	WR(q)->q_ptr = NULL;
555 	state->ms_rqp = NULL;
556 	state->ms_wqp = NULL;
557 
558 	state->ms_opened = B_FALSE;
559 
560 	minor = state->ms_minor;
561 
562 	mutex_exit(&state->ms_mutex);
563 
564 	if (!MOUSE8042_INTERNAL_OPEN(minor)) {
565 		/*
566 		 * Closing physical PS/2 mouse
567 		 *
568 		 * Link it back to virtual mouse, and
569 		 * mouse8042_open will be called as a result
570 		 * of the consconfig_link call.  Do NOT try
571 		 * this if the mouse is about to be detached!
572 		 *
573 		 * If linking back fails, this specific mouse
574 		 * will not be available underneath the virtual
575 		 * mouse, and can only be accessed via physical
576 		 * open.
577 		 */
578 		consconfig_link(ddi_driver_major(mouse8042_dip),
579 		    MOUSE8042_INTERNAL_MINOR(minor));
580 	}
581 
582 	return (0);
583 }
584 
585 static void
586 mouse8042_iocnack(
587     queue_t *qp,
588     mblk_t *mp,
589     struct iocblk *iocp,
590     int error,
591     int rval)
592 {
593 	mp->b_datap->db_type = M_IOCNAK;
594 	iocp->ioc_rval = rval;
595 	iocp->ioc_error = error;
596 	qreply(qp, mp);
597 }
598 
599 static void
600 mouse8042_reset_timeout(void *argp)
601 {
602 	struct mouse_state *state = (struct mouse_state *)argp;
603 	mblk_t *mp;
604 
605 	mutex_enter(&state->reset_mutex);
606 
607 	/*
608 	 * If the interrupt handler hasn't completed the reset handling
609 	 * (reset_state would be IDLE or FAILED in that case), then
610 	 * drop the 8042 lock, and send a faked retry reply upstream,
611 	 * then enable the queue for further message processing.
612 	 */
613 	if (state->reset_state != MSE_RESET_IDLE &&
614 	    state->reset_state != MSE_RESET_FAILED) {
615 
616 		state->reset_tid = 0;
617 		state->reset_state = MSE_RESET_IDLE;
618 
619 		(void) ddi_get8(state->ms_handle, state->ms_addr +
620 		    I8042_UNLOCK);
621 
622 		mp = state->reply_mp;
623 		*mp->b_wptr++ = MSERESEND;
624 		state->reply_mp = NULL;
625 
626 		if (state->ms_rqp != NULL)
627 			putnext(state->ms_rqp, mp);
628 		else
629 			freemsg(mp);
630 
631 		ASSERT(state->ms_wqp != NULL);
632 
633 		enableok(state->ms_wqp);
634 		qenable(state->ms_wqp);
635 	}
636 
637 	mutex_exit(&state->reset_mutex);
638 }
639 
640 /*
641  * Returns 1 if the caller should put the message (bp) back on the queue
642  */
643 static int
644 mouse8042_process_reset(queue_t *q, mblk_t *mp, struct mouse_state *state)
645 {
646 	mutex_enter(&state->reset_mutex);
647 	/*
648 	 * If we're in the middle of a reset, put the message back on the queue
649 	 * for processing later.
650 	 */
651 	if (state->reset_state != MSE_RESET_IDLE) {
652 		/*
653 		 * We noenable the queue again here in case it was backenabled
654 		 * by an upper-level module.
655 		 */
656 		noenable(q);
657 
658 		mutex_exit(&state->reset_mutex);
659 		return (1);
660 	}
661 
662 	/*
663 	 * Drop the reset state lock before allocating the response message and
664 	 * grabbing the 8042 exclusive-access lock (since those operations
665 	 * may take an extended period of time to complete).
666 	 */
667 	mutex_exit(&state->reset_mutex);
668 
669 	state->reply_mp = allocb(3, BPRI_MED);
670 	if (state->reply_mp == NULL) {
671 		/*
672 		 * Allocation failed -- set up a bufcall to enable the queue
673 		 * whenever there is enough memory to allocate the response
674 		 * message.
675 		 */
676 		state->bc_id = qbufcall(q, 3, BPRI_MED,
677 		    (void (*)(void *))qenable, q);
678 
679 		if (state->bc_id == 0) {
680 			/*
681 			 * If the qbufcall failed, we cannot proceed, so use the
682 			 * message we were sent to respond with an error.
683 			 */
684 			*mp->b_rptr = MSEERROR;
685 			mp->b_wptr = mp->b_rptr + 1;
686 			qreply(q, mp);
687 			return (0);
688 		}
689 
690 		return (1);
691 	}
692 
693 	/*
694 	 * Gain exclusive access to the 8042 for the duration of the reset.
695 	 * The unlock will occur when the reset has either completed or timed
696 	 * out.
697 	 */
698 	(void) ddi_get8(state->ms_handle,
699 	    state->ms_addr + I8042_LOCK);
700 
701 	mutex_enter(&state->reset_mutex);
702 
703 	state->reset_state = MSE_RESET_PRE;
704 	noenable(q);
705 
706 	state->reset_tid = qtimeout(q,
707 	    mouse8042_reset_timeout,
708 	    state,
709 	    drv_usectohz(
710 	    MOUSE8042_RESET_TIMEOUT_USECS));
711 
712 	ddi_put8(state->ms_handle,
713 	    state->ms_addr +
714 	    I8042_INT_OUTPUT_DATA, MSERESET);
715 
716 	mp->b_rptr++;
717 
718 	mutex_exit(&state->reset_mutex);
719 	return (1);
720 }
721 
722 /*
723  * Returns 1 if the caller should stop processing messages
724  */
725 static int
726 mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
727 {
728 	mblk_t *bp;
729 	mblk_t *next;
730 
731 	bp = mp;
732 	do {
733 		while (bp->b_rptr < bp->b_wptr) {
734 			/*
735 			 * Detect an attempt to reset the mouse.  Lock out any
736 			 * further mouse writes until the reset has completed.
737 			 */
738 			if (*bp->b_rptr == MSERESET) {
739 
740 				/*
741 				 * If we couldn't allocate memory and we
742 				 * we couldn't register a bufcall,
743 				 * mouse8042_process_reset returns 0 and
744 				 * has already used the message to send an
745 				 * error reply back upstream, so there is no
746 				 * need to deallocate or put this message back
747 				 * on the queue.
748 				 */
749 				if (mouse8042_process_reset(q, bp, state) == 0)
750 					return (1);
751 
752 				/*
753 				 * If there's no data remaining in this block,
754 				 * free this block and put the following blocks
755 				 * of this message back on the queue. If putting
756 				 * the rest of the message back on the queue
757 				 * fails, free the the message.
758 				 */
759 				if (MBLKL(bp) == 0) {
760 					next = bp->b_cont;
761 					freeb(bp);
762 					bp = next;
763 				}
764 				if (bp != NULL) {
765 					if (!putbq(q, bp))
766 						freemsg(bp);
767 				}
768 
769 				return (1);
770 
771 			}
772 			ddi_put8(state->ms_handle,
773 			    state->ms_addr + I8042_INT_OUTPUT_DATA,
774 			    *bp->b_rptr++);
775 		}
776 		next = bp->b_cont;
777 		freeb(bp);
778 	} while ((bp = next) != NULL);
779 
780 	return (0);
781 }
782 
783 static int
784 mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
785 {
786 	struct iocblk *iocbp;
787 	int rv = 0;
788 
789 	iocbp = (struct iocblk *)mp->b_rptr;
790 
791 	switch (mp->b_datap->db_type) {
792 	case M_FLUSH:
793 		if (*mp->b_rptr & FLUSHW) {
794 			flushq(q, FLUSHDATA);
795 			*mp->b_rptr &= ~FLUSHW;
796 		}
797 		if (*mp->b_rptr & FLUSHR) {
798 			qreply(q, mp);
799 		} else
800 			freemsg(mp);
801 		break;
802 	case M_IOCTL:
803 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
804 		break;
805 	case M_IOCDATA:
806 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
807 		break;
808 	case M_DATA:
809 		rv = mouse8042_process_data_msg(q, mp, state);
810 		break;
811 	default:
812 		freemsg(mp);
813 		break;
814 	}
815 
816 	return (rv);
817 }
818 
819 static int
820 mouse8042_wsrv(queue_t *qp)
821 {
822 	mblk_t *mp;
823 	struct mouse_state *state;
824 	state = (struct mouse_state *)qp->q_ptr;
825 
826 	while ((mp = getq(qp)) != NULL) {
827 		if (mouse8042_process_msg(qp, mp, state) != 0)
828 			break;
829 	}
830 
831 	return (0);
832 }
833 
834 /*
835  * Returns the next reset state, given the current state and the byte
836  * received from the mouse.  Error and Resend codes are handled by the
837  * caller.
838  */
839 static mouse8042_reset_state_e
840 mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata)
841 {
842 	switch (reset_state) {
843 	case MSE_RESET_PRE:	/* RESET sent, now we expect an ACK */
844 		if (mdata == MSE_ACK)	/* Got the ACK */
845 			return (MSE_RESET_ACK);
846 		break;
847 
848 	case MSE_RESET_ACK:	/* ACK received; now we expect 0xAA */
849 		if (mdata == MSE_AA)	/* Got the 0xAA */
850 			return (MSE_RESET_AA);
851 		break;
852 
853 	case MSE_RESET_AA: 	/* 0xAA received; now we expect 0x00 */
854 		if (mdata == MSE_00)
855 			return (MSE_RESET_IDLE);
856 		break;
857 	}
858 
859 	return (reset_state);
860 }
861 
862 static uint_t
863 mouse8042_intr(caddr_t arg)
864 {
865 	unsigned char    mdata;
866 	mblk_t *mp;
867 	struct mouse_state *state = (struct mouse_state *)arg;
868 	int rc;
869 
870 	mutex_enter(&state->ms_mutex);
871 
872 	rc = DDI_INTR_UNCLAIMED;
873 
874 	for (;;) {
875 
876 		if (ddi_get8(state->ms_handle,
877 		    state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
878 			break;
879 		}
880 
881 		mdata = ddi_get8(state->ms_handle,
882 		    state->ms_addr + I8042_INT_INPUT_DATA);
883 
884 		rc = DDI_INTR_CLAIMED;
885 
886 		/*
887 		 * If we're not ready for this data, discard it.
888 		 */
889 		if (!state->ready)
890 			continue;
891 
892 		mutex_enter(&state->reset_mutex);
893 		if (state->reset_state != MSE_RESET_IDLE) {
894 
895 			if (mdata == MSEERROR || mdata == MSERESET) {
896 				state->reset_state = MSE_RESET_FAILED;
897 			} else {
898 				state->reset_state =
899 				    mouse8042_reset_fsm(state->reset_state,
900 				    mdata);
901 			}
902 
903 			/*
904 			 * If we transitioned back to the idle reset state (or
905 			 * the reset failed), disable the timeout, release the
906 			 * 8042 exclusive-access lock, then send the response
907 			 * the the upper-level modules. Finally, enable the
908 			 * queue and schedule queue service procedures so that
909 			 * upper-level modules can process the response.
910 			 * Otherwise, if we're still in the middle of the
911 			 * reset sequence, do not send the data up (since the
912 			 * response is sent at the end of the sequence, or
913 			 * on timeout/error).
914 			 */
915 			if (state->reset_state == MSE_RESET_IDLE ||
916 			    state->reset_state == MSE_RESET_FAILED) {
917 
918 				mutex_exit(&state->reset_mutex);
919 				(void) quntimeout(state->ms_wqp,
920 				    state->reset_tid);
921 				mutex_enter(&state->reset_mutex);
922 
923 				(void) ddi_get8(state->ms_handle,
924 				    state->ms_addr + I8042_UNLOCK);
925 
926 				state->reset_tid = 0;
927 				mp = state->reply_mp;
928 				if (state->reset_state == MSE_RESET_FAILED) {
929 					*mp->b_wptr++ = mdata;
930 				} else {
931 					*mp->b_wptr++ = MSE_ACK;
932 					*mp->b_wptr++ = MSE_AA;
933 					*mp->b_wptr++ = MSE_00;
934 				}
935 				state->reply_mp = NULL;
936 
937 				state->reset_state = MSE_RESET_IDLE;
938 
939 				if (state->ms_rqp != NULL)
940 					putnext(state->ms_rqp, mp);
941 				else
942 					freemsg(mp);
943 
944 				enableok(state->ms_wqp);
945 				qenable(state->ms_wqp);
946 			}
947 
948 			mutex_exit(&state->reset_mutex);
949 			mutex_exit(&state->ms_mutex);
950 			return (rc);
951 		}
952 		mutex_exit(&state->reset_mutex);
953 
954 		if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
955 			*mp->b_wptr++ = mdata;
956 			putnext(state->ms_rqp, mp);
957 		}
958 	}
959 	mutex_exit(&state->ms_mutex);
960 
961 	return (rc);
962 }
963