xref: /titanic_51/usr/src/uts/common/io/mouse8042.c (revision fc3af78a71855c71878866a294572d00e6720533)
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 2008 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/debug.h>
46 #include <sys/ddi.h>
47 #include <sys/stat.h>
48 #include <sys/cmn_err.h>
49 #include <sys/sunddi.h>
50 
51 #include <sys/promif.h>
52 #include <sys/cred.h>
53 
54 #include <sys/i8042.h>
55 #include <sys/note.h>
56 
57 #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
58 
59 #ifdef	DEBUG
60 #define	MOUSE8042_DEBUG
61 #endif
62 
63 #define	MOUSE8042_INTERNAL_OPEN(minor)	(((minor) & 0x1) == 1)
64 #define	MOUSE8042_MINOR_TO_INSTANCE(minor)	((minor) / 2)
65 #define	MOUSE8042_INTERNAL_MINOR(minor)		((minor) + 1)
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 struct mouse_state {
89 	queue_t	*ms_rqp;
90 	queue_t	*ms_wqp;
91 	ddi_iblock_cookie_t	ms_iblock_cookie;
92 	ddi_acc_handle_t	ms_handle;
93 	uint8_t			*ms_addr;
94 	kmutex_t		ms_mutex;
95 
96 	minor_t			ms_minor;
97 	boolean_t		ms_opened;
98 };
99 
100 #if	defined(MOUSE8042_DEBUG)
101 int mouse8042_debug = 0;
102 int mouse8042_debug_minimal = 0;
103 #endif
104 
105 static uint_t mouse8042_intr(caddr_t arg);
106 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
107 		cred_t *cred_p);
108 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
109 static int mouse8042_wput(queue_t *q, mblk_t *mp);
110 
111 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
112 		void *arg, void **result);
113 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
114 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
115 
116 
117 /*
118  * Streams module info.
119  */
120 #define	MODULE_NAME	"mouse8042"
121 
122 static struct module_info	mouse8042_minfo = {
123 	23,		/* Module ID number */
124 	MODULE_NAME,
125 	0, INFPSZ,	/* minimum & maximum packet sizes */
126 	256, 128	/* hi and low water marks */
127 };
128 
129 static struct qinit mouse8042_rinit = {
130 	NULL,		/* put */
131 	NULL,		/* service */
132 	mouse8042_open,
133 	mouse8042_close,
134 	NULL,		/* admin */
135 	&mouse8042_minfo,
136 	NULL		/* statistics */
137 };
138 
139 static struct qinit mouse8042_winit = {
140 	mouse8042_wput,	/* put */
141 	NULL,		/* service */
142 	NULL,		/* open */
143 	NULL,		/* close */
144 	NULL,		/* admin */
145 	&mouse8042_minfo,
146 	NULL		/* statistics */
147 };
148 
149 static struct streamtab mouse8042_strinfo = {
150 	&mouse8042_rinit,
151 	&mouse8042_winit,
152 	NULL,		/* muxrinit */
153 	NULL,		/* muxwinit */
154 };
155 
156 /*
157  * Local Function Declarations
158  */
159 
160 static struct cb_ops	mouse8042_cb_ops = {
161 	nodev,			/* open */
162 	nodev,			/* close */
163 	nodev,			/* strategy */
164 	nodev,			/* print */
165 	nodev,			/* dump */
166 	nodev,			/* read */
167 	nodev,			/* write */
168 	nodev,			/* ioctl */
169 	nodev,			/* devmap */
170 	nodev,			/* mmap */
171 	nodev,			/* segmap */
172 	nochpoll,		/* poll */
173 	ddi_prop_op,		/* cb_prop_op */
174 	&mouse8042_strinfo,	/* streamtab  */
175 	D_MP | D_NEW
176 };
177 
178 
179 static struct dev_ops	mouse8042_ops = {
180 	DEVO_REV,		/* devo_rev, */
181 	0,			/* refcnt  */
182 	mouse8042_getinfo,	/* getinfo */
183 	nulldev,		/* identify */
184 	nulldev,		/* probe */
185 	mouse8042_attach,	/* attach */
186 	mouse8042_detach,	/* detach */
187 	nodev,			/* reset */
188 	&mouse8042_cb_ops,	/* driver operations */
189 	(struct bus_ops *)0,	/* bus operations */
190 	NULL,			/* power */
191 	ddi_quiesce_not_needed,		/* quiesce */
192 };
193 
194 /*
195  * This is the loadable module wrapper.
196  */
197 #include <sys/modctl.h>
198 
199 extern struct mod_ops mod_driverops;
200 
201 /*
202  * Module linkage information for the kernel.
203  */
204 
205 static struct modldrv modldrv = {
206 	&mod_driverops, /* Type of module.  This one is a driver */
207 	"PS/2 Mouse",
208 	&mouse8042_ops,	/* driver ops */
209 };
210 
211 static struct modlinkage modlinkage = {
212 	MODREV_1,
213 	(void *)&modldrv,
214 	NULL
215 };
216 
217 /*
218  * This is the driver initialization routine.
219  */
220 int
221 _init()
222 {
223 	int	rv;
224 
225 	rv = mod_install(&modlinkage);
226 	return (rv);
227 }
228 
229 
230 int
231 _fini(void)
232 {
233 	return (mod_remove(&modlinkage));
234 }
235 
236 
237 int
238 _info(struct modinfo *modinfop)
239 {
240 	return (mod_info(&modlinkage, modinfop));
241 }
242 
243 static int
244 mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
245 {
246 	struct mouse_state *state;
247 	mblk_t *mp;
248 	int instance = ddi_get_instance(dip);
249 	static ddi_device_acc_attr_t attr = {
250 		DDI_DEVICE_ATTR_V0,
251 		DDI_NEVERSWAP_ACC,
252 		DDI_STRICTORDER_ACC,
253 	};
254 	int rc;
255 
256 
257 #ifdef MOUSE8042_DEBUG
258 	if (mouse8042_debug) {
259 		cmn_err(CE_CONT, MODULE_NAME "_attach entry\n");
260 	}
261 #endif
262 
263 	if (cmd == DDI_RESUME) {
264 		state = (struct mouse_state *)ddi_get_driver_private(dip);
265 
266 		/*
267 		 * Send a 0xaa 0x00 upstream.
268 		 * This causes the vuid module to reset the mouse.
269 		 */
270 		if (state->ms_rqp != NULL) {
271 			if (mp = allocb(1, BPRI_MED)) {
272 				*mp->b_wptr++ = 0xaa;
273 				putnext(state->ms_rqp, mp);
274 			}
275 			if (mp = allocb(1, BPRI_MED)) {
276 				*mp->b_wptr++ = 0x0;
277 				putnext(state->ms_rqp, mp);
278 			}
279 		}
280 		return (DDI_SUCCESS);
281 	}
282 
283 	if (cmd != DDI_ATTACH)
284 		return (DDI_FAILURE);
285 
286 	if (mouse8042_dip != NULL)
287 		return (DDI_FAILURE);
288 
289 	/* allocate and initialize state structure */
290 	state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
291 	state->ms_opened = B_FALSE;
292 	ddi_set_driver_private(dip, state);
293 
294 	/*
295 	 * In order to support virtual keyboard/mouse, we should distinguish
296 	 * between internal virtual open and external physical open.
297 	 *
298 	 * When the physical devices are opened by application, they will
299 	 * be unlinked from the virtual device and their data stream will
300 	 * not be sent to the virtual device. When the opened physical
301 	 * devices are closed, they will be relinked to the virtual devices.
302 	 *
303 	 * All these automatic switch between virtual and physical are
304 	 * transparent.
305 	 *
306 	 * So we change minor node numbering scheme to be:
307 	 * 	external node minor num == instance * 2
308 	 *	internal node minor num == instance * 2 + 1
309 	 */
310 	rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
311 	    DDI_NT_MOUSE, NULL);
312 	if (rc != DDI_SUCCESS) {
313 #if	defined(MOUSE8042_DEBUG)
314 		cmn_err(CE_CONT,
315 		    MODULE_NAME "_attach: ddi_create_minor_node failed\n");
316 #endif
317 		goto fail_1;
318 	}
319 
320 	if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
321 	    instance * 2 + 1) != DDI_SUCCESS) {
322 		goto fail_2;
323 	}
324 
325 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
326 	    (offset_t)0, (offset_t)0, &attr, &state->ms_handle);
327 	if (rc != DDI_SUCCESS) {
328 #if	defined(MOUSE8042_DEBUG)
329 		cmn_err(CE_WARN, MODULE_NAME "_attach:  can't map registers");
330 #endif
331 		goto fail_2;
332 	}
333 
334 	rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
335 	if (rc != DDI_SUCCESS) {
336 #if	defined(MOUSE8042_DEBUG)
337 		cmn_err(CE_WARN,
338 		    MODULE_NAME "_attach:  Can't get iblock cookie");
339 #endif
340 		goto fail_3;
341 	}
342 
343 	mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
344 	    state->ms_iblock_cookie);
345 
346 	rc = ddi_add_intr(dip, 0,
347 	    (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
348 	    mouse8042_intr, (caddr_t)state);
349 	if (rc != DDI_SUCCESS) {
350 #if	defined(MOUSE8042_DEBUG)
351 		cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt");
352 #endif
353 		goto fail_3;
354 	}
355 
356 	mouse8042_dip = dip;
357 
358 	/* Now that we're attached, announce our presence to the world. */
359 	ddi_report_dev(dip);
360 #if	defined(MOUSE8042_DEBUG)
361 	cmn_err(CE_CONT, "?%s #%d\n", DRIVER_NAME(dip), ddi_get_instance(dip));
362 #endif
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 		return (DDI_SUCCESS);
387 
388 	case DDI_DETACH:
389 		ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
390 		mouse8042_dip = NULL;
391 		mutex_destroy(&state->ms_mutex);
392 		ddi_prop_remove_all(dip);
393 		ddi_regs_map_free(&state->ms_handle);
394 		ddi_remove_minor_node(dip, NULL);
395 		kmem_free(state, sizeof (struct mouse_state));
396 		return (DDI_SUCCESS);
397 
398 	default:
399 #ifdef MOUSE8042_DEBUG
400 		if (mouse8042_debug) {
401 			cmn_err(CE_CONT,
402 			    "mouse8042_detach: cmd = %d unknown\n", cmd);
403 		}
404 #endif
405 		return (DDI_FAILURE);
406 	}
407 }
408 
409 
410 /* ARGSUSED */
411 static int
412 mouse8042_getinfo(
413     dev_info_t *dip,
414     ddi_info_cmd_t infocmd,
415     void *arg,
416     void **result)
417 {
418 	dev_t dev = (dev_t)arg;
419 	minor_t	minor = getminor(dev);
420 	int	instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
421 
422 #ifdef MOUSE8042_DEBUG
423 	if (mouse8042_debug)
424 		cmn_err(CE_CONT, "mouse8042_getinfo: call\n");
425 #endif
426 	switch (infocmd) {
427 	case DDI_INFO_DEVT2DEVINFO:
428 		if (mouse8042_dip == NULL)
429 			return (DDI_FAILURE);
430 
431 		*result = (void *)mouse8042_dip;
432 		break;
433 	case DDI_INFO_DEVT2INSTANCE:
434 		*result = (void *)(uintptr_t)instance;
435 		break;
436 	default:
437 		return (DDI_FAILURE);
438 	}
439 	return (DDI_SUCCESS);
440 }
441 
442 /*ARGSUSED*/
443 static int
444 mouse8042_open(
445 	queue_t	*q,
446 	dev_t	*devp,
447 	int	flag,
448 	int	sflag,
449 	cred_t	*cred_p)
450 {
451 	struct mouse_state *state;
452 	minor_t	minor = getminor(*devp);
453 	int rval;
454 
455 	if (mouse8042_dip == NULL)
456 		return (ENXIO);
457 
458 	state = ddi_get_driver_private(mouse8042_dip);
459 
460 #ifdef MOUSE8042_DEBUG
461 	if (mouse8042_debug)
462 		cmn_err(CE_CONT, "mouse8042_open:entered\n");
463 #endif
464 
465 	mutex_enter(&state->ms_mutex);
466 
467 	if (state->ms_opened) {
468 		/*
469 		 * Exit if the same minor node is already open
470 		 */
471 		if (state->ms_minor == minor) {
472 			mutex_exit(&state->ms_mutex);
473 			return (0);
474 		}
475 
476 		/*
477 		 * Check whether it is switch between physical and virtual
478 		 *
479 		 * Opening from virtual while the device is being physically
480 		 * opened by an application should not happen. So we ASSERT
481 		 * this in DEBUG version, and return error in the non-DEBUG
482 		 * case.
483 		 */
484 		ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
485 
486 		if (MOUSE8042_INTERNAL_OPEN(minor)) {
487 			mutex_exit(&state->ms_mutex);
488 			return (EINVAL);
489 		}
490 
491 		/*
492 		 * Opening the physical one while it is being underneath
493 		 * the virtual one.
494 		 *
495 		 * consconfig_unlink is called to unlink this device from
496 		 * the virtual one, thus the old stream serving for this
497 		 * device under the virtual one is closed, and then the
498 		 * lower driver's close routine (here is mouse8042_close)
499 		 * is also called to accomplish the whole stream close.
500 		 * Here we have to drop the lock because mouse8042_close
501 		 * also needs the lock.
502 		 *
503 		 * For mouse, the old stream is:
504 		 *	consms->["pushmod"->]"mouse_vp driver"
505 		 *
506 		 * After the consconfig_unlink returns, the old stream is closed
507 		 * and we grab the lock again to reopen this device as normal.
508 		 */
509 		mutex_exit(&state->ms_mutex);
510 
511 		/*
512 		 * If unlink fails, fail the physical open.
513 		 */
514 		if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
515 		    MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
516 			return (rval);
517 		}
518 
519 		mutex_enter(&state->ms_mutex);
520 	}
521 
522 
523 	q->q_ptr = (caddr_t)state;
524 	WR(q)->q_ptr = (caddr_t)state;
525 	state->ms_rqp = q;
526 	state->ms_wqp = WR(q);
527 
528 	qprocson(q);
529 
530 	state->ms_minor = minor;
531 	state->ms_opened = B_TRUE;
532 
533 	mutex_exit(&state->ms_mutex);
534 
535 	return (0);
536 }
537 
538 
539 /*ARGSUSED*/
540 static int
541 mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
542 {
543 	struct mouse_state *state;
544 	minor_t	minor;
545 
546 	state = (struct mouse_state *)q->q_ptr;
547 
548 #ifdef MOUSE8042_DEBUG
549 	if (mouse8042_debug)
550 		cmn_err(CE_CONT, "mouse8042_close:entered\n");
551 #endif
552 
553 	mutex_enter(&state->ms_mutex);
554 
555 	qprocsoff(q);
556 
557 	q->q_ptr = NULL;
558 	WR(q)->q_ptr = NULL;
559 	state->ms_rqp = NULL;
560 	state->ms_wqp = NULL;
561 
562 	state->ms_opened = B_FALSE;
563 
564 	minor = state->ms_minor;
565 
566 	mutex_exit(&state->ms_mutex);
567 
568 	if (!MOUSE8042_INTERNAL_OPEN(minor)) {
569 		/*
570 		 * Closing physical PS/2 mouse
571 		 *
572 		 * Link it back to virtual mouse, and
573 		 * mouse8042_open will be called as a result
574 		 * of the consconfig_link call.  Do NOT try
575 		 * this if the mouse is about to be detached!
576 		 *
577 		 * If linking back fails, this specific mouse
578 		 * will not be available underneath the virtual
579 		 * mouse, and can only be accessed via physical
580 		 * open.
581 		 */
582 		consconfig_link(ddi_driver_major(mouse8042_dip),
583 		    MOUSE8042_INTERNAL_MINOR(minor));
584 	}
585 
586 	return (0);
587 }
588 
589 static void
590 mouse8042_iocnack(
591     queue_t *qp,
592     mblk_t *mp,
593     struct iocblk *iocp,
594     int error,
595     int rval)
596 {
597 	mp->b_datap->db_type = M_IOCNAK;
598 	iocp->ioc_rval = rval;
599 	iocp->ioc_error = error;
600 	qreply(qp, mp);
601 }
602 
603 static int
604 mouse8042_wput(queue_t *q, mblk_t *mp)
605 {
606 	struct iocblk *iocbp;
607 	mblk_t *bp;
608 	mblk_t *next;
609 	struct mouse_state *state;
610 
611 	state = (struct mouse_state *)q->q_ptr;
612 
613 #ifdef MOUSE8042_DEBUG
614 	if (mouse8042_debug)
615 		cmn_err(CE_CONT, "mouse8042_wput:entered\n");
616 #endif
617 	iocbp = (struct iocblk *)mp->b_rptr;
618 	switch (mp->b_datap->db_type) {
619 	case M_FLUSH:
620 #ifdef MOUSE8042_DEBUG
621 		if (mouse8042_debug)
622 			cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n");
623 #endif
624 
625 		if (*mp->b_rptr & FLUSHW)
626 			flushq(q, FLUSHDATA);
627 		qreply(q, mp);
628 		break;
629 	case M_IOCTL:
630 #ifdef MOUSE8042_DEBUG
631 		if (mouse8042_debug)
632 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n");
633 #endif
634 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
635 		break;
636 	case M_IOCDATA:
637 #ifdef MOUSE8042_DEBUG
638 		if (mouse8042_debug)
639 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n");
640 #endif
641 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
642 		break;
643 	case M_DATA:
644 		bp = mp;
645 		do {
646 			while (bp->b_rptr < bp->b_wptr) {
647 #if	defined(MOUSE8042_DEBUG)
648 				if (mouse8042_debug) {
649 					cmn_err(CE_CONT,
650 					    "mouse8042:  send %2x\n",
651 					    *bp->b_rptr);
652 				}
653 				if (mouse8042_debug_minimal) {
654 					cmn_err(CE_CONT, ">a:%2x ",
655 					    *bp->b_rptr);
656 				}
657 #endif
658 				ddi_put8(state->ms_handle,
659 				    state->ms_addr + I8042_INT_OUTPUT_DATA,
660 				    *bp->b_rptr++);
661 			}
662 			next = bp->b_cont;
663 			freeb(bp);
664 		} while ((bp = next) != NULL);
665 		break;
666 	default:
667 		freemsg(mp);
668 		break;
669 	}
670 #ifdef MOUSE8042_DEBUG
671 	if (mouse8042_debug)
672 		cmn_err(CE_CONT, "mouse8042_wput:leaving\n");
673 #endif
674 	return (0);	/* ignored */
675 }
676 
677 static uint_t
678 mouse8042_intr(caddr_t arg)
679 {
680 	unsigned char    mdata;
681 	mblk_t *mp;
682 	struct mouse_state *state = (struct mouse_state *)arg;
683 	int rc;
684 
685 	mutex_enter(&state->ms_mutex);
686 
687 #if	defined(MOUSE8042_DEBUG)
688 	if (mouse8042_debug)
689 		cmn_err(CE_CONT, "mouse8042_intr()\n");
690 #endif
691 	rc = DDI_INTR_UNCLAIMED;
692 
693 	for (;;) {
694 
695 		if (ddi_get8(state->ms_handle,
696 		    state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
697 			break;
698 		}
699 
700 		mdata = ddi_get8(state->ms_handle,
701 		    state->ms_addr + I8042_INT_INPUT_DATA);
702 
703 #if	defined(MOUSE8042_DEBUG)
704 		if (mouse8042_debug)
705 			cmn_err(CE_CONT, "mouse8042_intr:  got %2x\n", mdata);
706 		if (mouse8042_debug_minimal)
707 			cmn_err(CE_CONT, "<A:%2x ", mdata);
708 #endif
709 
710 		rc = DDI_INTR_CLAIMED;
711 
712 		if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
713 			*mp->b_wptr++ = mdata;
714 			putnext(state->ms_rqp, mp);
715 		}
716 	}
717 #ifdef MOUSE8042_DEBUG
718 	if (mouse8042_debug)
719 		cmn_err(CE_CONT, "mouse8042_intr() ok\n");
720 #endif
721 	mutex_exit(&state->ms_mutex);
722 
723 	return (rc);
724 }
725