xref: /illumos-gate/usr/src/uts/common/io/mouse8042.c (revision 15bfc6b75fa0acab2947977e84e0a9f492aca328)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
519397407SSherry Moore  * Common Development and Distribution License (the "License").
619397407SSherry Moore  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
227c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
237c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
26*15bfc6b7SSeth Goldberg  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
277c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /*
327c478bd9Sstevel@tonic-gate  * PS/2 type Mouse Module - Streams
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <sys/param.h>
367c478bd9Sstevel@tonic-gate #include <sys/types.h>
377c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
387c478bd9Sstevel@tonic-gate #include <sys/signal.h>
397c478bd9Sstevel@tonic-gate #include <sys/errno.h>
407c478bd9Sstevel@tonic-gate #include <sys/file.h>
417c478bd9Sstevel@tonic-gate #include <sys/termio.h>
427c478bd9Sstevel@tonic-gate #include <sys/stream.h>
437c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
447c478bd9Sstevel@tonic-gate #include <sys/strtty.h>
45*15bfc6b7SSeth Goldberg #include <sys/strsun.h>
467c478bd9Sstevel@tonic-gate #include <sys/debug.h>
477c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
497c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
507c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include <sys/promif.h>
537c478bd9Sstevel@tonic-gate #include <sys/cred.h>
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate #include <sys/i8042.h>
567c478bd9Sstevel@tonic-gate #include <sys/note.h>
57*15bfc6b7SSeth Goldberg #include <sys/mouse.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate #define	MOUSE8042_INTERNAL_OPEN(minor)	(((minor) & 0x1) == 1)
627c478bd9Sstevel@tonic-gate #define	MOUSE8042_MINOR_TO_INSTANCE(minor)	((minor) / 2)
637c478bd9Sstevel@tonic-gate #define	MOUSE8042_INTERNAL_MINOR(minor)		((minor) + 1)
647c478bd9Sstevel@tonic-gate 
65*15bfc6b7SSeth Goldberg #define	MOUSE8042_RESET_TIMEOUT_USECS	500000	/* 500 ms */
66*15bfc6b7SSeth Goldberg 
677c478bd9Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
687c478bd9Sstevel@tonic-gate extern void consconfig_link(major_t major, minor_t minor);
697c478bd9Sstevel@tonic-gate extern int consconfig_unlink(major_t major, minor_t minor);
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate /*
737c478bd9Sstevel@tonic-gate  *
747c478bd9Sstevel@tonic-gate  * Local Static Data
757c478bd9Sstevel@tonic-gate  *
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate /*
797c478bd9Sstevel@tonic-gate  * We only support one instance.  Yes, it's theoretically possible to
807c478bd9Sstevel@tonic-gate  * plug in more than one, but it's not worth the implementation cost.
817c478bd9Sstevel@tonic-gate  *
827c478bd9Sstevel@tonic-gate  * The introduction of USB keyboards might make it worth reassessing
837c478bd9Sstevel@tonic-gate  * this decision, as they might free up the keyboard port for a second
847c478bd9Sstevel@tonic-gate  * PS/2 style mouse.
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate static dev_info_t *mouse8042_dip;
877c478bd9Sstevel@tonic-gate 
88*15bfc6b7SSeth Goldberg /*
89*15bfc6b7SSeth Goldberg  * RESET states
90*15bfc6b7SSeth Goldberg  */
91*15bfc6b7SSeth Goldberg typedef enum {
92*15bfc6b7SSeth Goldberg 	MSE_RESET_IDLE,	/* No reset in progress */
93*15bfc6b7SSeth Goldberg 	MSE_RESET_PRE,	/* Send reset, waiting for ACK */
94*15bfc6b7SSeth Goldberg 	MSE_RESET_ACK,	/* Got ACK, waiting for 0xAA */
95*15bfc6b7SSeth Goldberg 	MSE_RESET_AA,	/* Got 0xAA, waiting for 0x00 */
96*15bfc6b7SSeth Goldberg 	MSE_RESET_FAILED
97*15bfc6b7SSeth Goldberg } mouse8042_reset_state_e;
98*15bfc6b7SSeth Goldberg 
997c478bd9Sstevel@tonic-gate struct mouse_state {
1007c478bd9Sstevel@tonic-gate 	queue_t	*ms_rqp;
1017c478bd9Sstevel@tonic-gate 	queue_t	*ms_wqp;
1027c478bd9Sstevel@tonic-gate 	ddi_iblock_cookie_t	ms_iblock_cookie;
1037c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t	ms_handle;
1047c478bd9Sstevel@tonic-gate 	uint8_t			*ms_addr;
1057c478bd9Sstevel@tonic-gate 	kmutex_t		ms_mutex;
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	minor_t			ms_minor;
1087c478bd9Sstevel@tonic-gate 	boolean_t		ms_opened;
109*15bfc6b7SSeth Goldberg 	kmutex_t		reset_mutex;
110*15bfc6b7SSeth Goldberg 	mouse8042_reset_state_e	reset_state;
111*15bfc6b7SSeth Goldberg 	timeout_id_t		reset_tid;
112*15bfc6b7SSeth Goldberg 	int			ready;
113*15bfc6b7SSeth Goldberg 	mblk_t			*reply_mp;
114*15bfc6b7SSeth Goldberg 	bufcall_id_t		bc_id;
1157c478bd9Sstevel@tonic-gate };
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate static uint_t mouse8042_intr(caddr_t arg);
1187c478bd9Sstevel@tonic-gate static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
1197c478bd9Sstevel@tonic-gate 		cred_t *cred_p);
1207c478bd9Sstevel@tonic-gate static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
121*15bfc6b7SSeth Goldberg static int mouse8042_wsrv(queue_t *qp);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
1247c478bd9Sstevel@tonic-gate 		void *arg, void **result);
1257c478bd9Sstevel@tonic-gate static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1267c478bd9Sstevel@tonic-gate static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * Streams module info.
1317c478bd9Sstevel@tonic-gate  */
1327c478bd9Sstevel@tonic-gate #define	MODULE_NAME	"mouse8042"
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate static struct module_info	mouse8042_minfo = {
1357c478bd9Sstevel@tonic-gate 	23,		/* Module ID number */
1367c478bd9Sstevel@tonic-gate 	MODULE_NAME,
1377c478bd9Sstevel@tonic-gate 	0, INFPSZ,	/* minimum & maximum packet sizes */
1387c478bd9Sstevel@tonic-gate 	256, 128	/* hi and low water marks */
1397c478bd9Sstevel@tonic-gate };
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate static struct qinit mouse8042_rinit = {
1427c478bd9Sstevel@tonic-gate 	NULL,		/* put */
1437c478bd9Sstevel@tonic-gate 	NULL,		/* service */
1447c478bd9Sstevel@tonic-gate 	mouse8042_open,
1457c478bd9Sstevel@tonic-gate 	mouse8042_close,
1467c478bd9Sstevel@tonic-gate 	NULL,		/* admin */
1477c478bd9Sstevel@tonic-gate 	&mouse8042_minfo,
1487c478bd9Sstevel@tonic-gate 	NULL		/* statistics */
1497c478bd9Sstevel@tonic-gate };
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate static struct qinit mouse8042_winit = {
152*15bfc6b7SSeth Goldberg 	putq,		/* put */
153*15bfc6b7SSeth Goldberg 	mouse8042_wsrv,	/* service */
1547c478bd9Sstevel@tonic-gate 	NULL,		/* open */
1557c478bd9Sstevel@tonic-gate 	NULL,		/* close */
1567c478bd9Sstevel@tonic-gate 	NULL,		/* admin */
1577c478bd9Sstevel@tonic-gate 	&mouse8042_minfo,
1587c478bd9Sstevel@tonic-gate 	NULL		/* statistics */
1597c478bd9Sstevel@tonic-gate };
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate static struct streamtab mouse8042_strinfo = {
1627c478bd9Sstevel@tonic-gate 	&mouse8042_rinit,
1637c478bd9Sstevel@tonic-gate 	&mouse8042_winit,
1647c478bd9Sstevel@tonic-gate 	NULL,		/* muxrinit */
1657c478bd9Sstevel@tonic-gate 	NULL,		/* muxwinit */
1667c478bd9Sstevel@tonic-gate };
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*
1697c478bd9Sstevel@tonic-gate  * Local Function Declarations
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate static struct cb_ops	mouse8042_cb_ops = {
1737c478bd9Sstevel@tonic-gate 	nodev,			/* open */
1747c478bd9Sstevel@tonic-gate 	nodev,			/* close */
1757c478bd9Sstevel@tonic-gate 	nodev,			/* strategy */
1767c478bd9Sstevel@tonic-gate 	nodev,			/* print */
1777c478bd9Sstevel@tonic-gate 	nodev,			/* dump */
1787c478bd9Sstevel@tonic-gate 	nodev,			/* read */
1797c478bd9Sstevel@tonic-gate 	nodev,			/* write */
1807c478bd9Sstevel@tonic-gate 	nodev,			/* ioctl */
1817c478bd9Sstevel@tonic-gate 	nodev,			/* devmap */
1827c478bd9Sstevel@tonic-gate 	nodev,			/* mmap */
1837c478bd9Sstevel@tonic-gate 	nodev,			/* segmap */
1847c478bd9Sstevel@tonic-gate 	nochpoll,		/* poll */
1857c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
1867c478bd9Sstevel@tonic-gate 	&mouse8042_strinfo,	/* streamtab  */
1877c478bd9Sstevel@tonic-gate 	D_MP | D_NEW
1887c478bd9Sstevel@tonic-gate };
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate static struct dev_ops	mouse8042_ops = {
1927c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
1937c478bd9Sstevel@tonic-gate 	0,			/* refcnt  */
1947c478bd9Sstevel@tonic-gate 	mouse8042_getinfo,	/* getinfo */
1957c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
1967c478bd9Sstevel@tonic-gate 	nulldev,		/* probe */
1977c478bd9Sstevel@tonic-gate 	mouse8042_attach,	/* attach */
1987c478bd9Sstevel@tonic-gate 	mouse8042_detach,	/* detach */
1997c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
2007c478bd9Sstevel@tonic-gate 	&mouse8042_cb_ops,	/* driver operations */
20119397407SSherry Moore 	(struct bus_ops *)0,	/* bus operations */
20219397407SSherry Moore 	NULL,			/* power */
20319397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
2047c478bd9Sstevel@tonic-gate };
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * This is the loadable module wrapper.
2087c478bd9Sstevel@tonic-gate  */
2097c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate /*
2147c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
2157c478bd9Sstevel@tonic-gate  */
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2187c478bd9Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
21919397407SSherry Moore 	"PS/2 Mouse",
2207c478bd9Sstevel@tonic-gate 	&mouse8042_ops,	/* driver ops */
2217c478bd9Sstevel@tonic-gate };
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2247c478bd9Sstevel@tonic-gate 	MODREV_1,
2257c478bd9Sstevel@tonic-gate 	(void *)&modldrv,
2267c478bd9Sstevel@tonic-gate 	NULL
2277c478bd9Sstevel@tonic-gate };
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate  * This is the driver initialization routine.
2317c478bd9Sstevel@tonic-gate  */
2327c478bd9Sstevel@tonic-gate int
2337c478bd9Sstevel@tonic-gate _init()
2347c478bd9Sstevel@tonic-gate {
2357c478bd9Sstevel@tonic-gate 	int	rv;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	rv = mod_install(&modlinkage);
2387c478bd9Sstevel@tonic-gate 	return (rv);
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate int
2437c478bd9Sstevel@tonic-gate _fini(void)
2447c478bd9Sstevel@tonic-gate {
2457c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate int
2507c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2517c478bd9Sstevel@tonic-gate {
2527c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate static int
2567c478bd9Sstevel@tonic-gate mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2577c478bd9Sstevel@tonic-gate {
2587c478bd9Sstevel@tonic-gate 	struct mouse_state *state;
259fd9cb95cSsethg 	mblk_t *mp;
2607c478bd9Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
2617c478bd9Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
2627c478bd9Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
2637c478bd9Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
2647c478bd9Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
2657c478bd9Sstevel@tonic-gate 	};
2667c478bd9Sstevel@tonic-gate 	int rc;
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate 
269fd9cb95cSsethg 	if (cmd == DDI_RESUME) {
270fd9cb95cSsethg 		state = (struct mouse_state *)ddi_get_driver_private(dip);
271fd9cb95cSsethg 
272*15bfc6b7SSeth Goldberg 		/* Ready to handle inbound data from mouse8042_intr */
273*15bfc6b7SSeth Goldberg 		state->ready = 1;
274*15bfc6b7SSeth Goldberg 
275fd9cb95cSsethg 		/*
276fd9cb95cSsethg 		 * Send a 0xaa 0x00 upstream.
277fd9cb95cSsethg 		 * This causes the vuid module to reset the mouse.
278fd9cb95cSsethg 		 */
279fd9cb95cSsethg 		if (state->ms_rqp != NULL) {
280fd9cb95cSsethg 			if (mp = allocb(1, BPRI_MED)) {
281fd9cb95cSsethg 				*mp->b_wptr++ = 0xaa;
282fd9cb95cSsethg 				putnext(state->ms_rqp, mp);
283fd9cb95cSsethg 			}
284fd9cb95cSsethg 			if (mp = allocb(1, BPRI_MED)) {
285fd9cb95cSsethg 				*mp->b_wptr++ = 0x0;
286fd9cb95cSsethg 				putnext(state->ms_rqp, mp);
287fd9cb95cSsethg 			}
288fd9cb95cSsethg 		}
289fd9cb95cSsethg 		return (DDI_SUCCESS);
290fd9cb95cSsethg 	}
291fd9cb95cSsethg 
2927c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
2937c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	if (mouse8042_dip != NULL)
2967c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	/* allocate and initialize state structure */
2997c478bd9Sstevel@tonic-gate 	state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
3007c478bd9Sstevel@tonic-gate 	state->ms_opened = B_FALSE;
301*15bfc6b7SSeth Goldberg 	state->reset_state = MSE_RESET_IDLE;
302*15bfc6b7SSeth Goldberg 	state->reset_tid = 0;
303*15bfc6b7SSeth Goldberg 	state->bc_id = 0;
3047c478bd9Sstevel@tonic-gate 	ddi_set_driver_private(dip, state);
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	/*
3077c478bd9Sstevel@tonic-gate 	 * In order to support virtual keyboard/mouse, we should distinguish
3087c478bd9Sstevel@tonic-gate 	 * between internal virtual open and external physical open.
3097c478bd9Sstevel@tonic-gate 	 *
3107c478bd9Sstevel@tonic-gate 	 * When the physical devices are opened by application, they will
3117c478bd9Sstevel@tonic-gate 	 * be unlinked from the virtual device and their data stream will
3127c478bd9Sstevel@tonic-gate 	 * not be sent to the virtual device. When the opened physical
3137c478bd9Sstevel@tonic-gate 	 * devices are closed, they will be relinked to the virtual devices.
3147c478bd9Sstevel@tonic-gate 	 *
3157c478bd9Sstevel@tonic-gate 	 * All these automatic switch between virtual and physical are
3167c478bd9Sstevel@tonic-gate 	 * transparent.
3177c478bd9Sstevel@tonic-gate 	 *
3187c478bd9Sstevel@tonic-gate 	 * So we change minor node numbering scheme to be:
3197c478bd9Sstevel@tonic-gate 	 * 	external node minor num == instance * 2
3207c478bd9Sstevel@tonic-gate 	 *	internal node minor num == instance * 2 + 1
3217c478bd9Sstevel@tonic-gate 	 */
322fd9cb95cSsethg 	rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
3237c478bd9Sstevel@tonic-gate 	    DDI_NT_MOUSE, NULL);
3247c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3257c478bd9Sstevel@tonic-gate 		goto fail_1;
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
3297c478bd9Sstevel@tonic-gate 	    instance * 2 + 1) != DDI_SUCCESS) {
3307c478bd9Sstevel@tonic-gate 		goto fail_2;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
3347c478bd9Sstevel@tonic-gate 	    (offset_t)0, (offset_t)0, &attr, &state->ms_handle);
3357c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3367c478bd9Sstevel@tonic-gate 		goto fail_2;
3377c478bd9Sstevel@tonic-gate 	}
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
3407c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3417c478bd9Sstevel@tonic-gate 		goto fail_3;
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
3457c478bd9Sstevel@tonic-gate 	    state->ms_iblock_cookie);
346*15bfc6b7SSeth Goldberg 	mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER,
347*15bfc6b7SSeth Goldberg 	    state->ms_iblock_cookie);
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 0,
3507c478bd9Sstevel@tonic-gate 	    (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
3517c478bd9Sstevel@tonic-gate 	    mouse8042_intr, (caddr_t)state);
3527c478bd9Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3537c478bd9Sstevel@tonic-gate 		goto fail_3;
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	mouse8042_dip = dip;
3577c478bd9Sstevel@tonic-gate 
358*15bfc6b7SSeth Goldberg 	/* Ready to handle inbound data from mouse8042_intr */
359*15bfc6b7SSeth Goldberg 	state->ready = 1;
360*15bfc6b7SSeth Goldberg 
3617c478bd9Sstevel@tonic-gate 	/* Now that we're attached, announce our presence to the world. */
3627c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
3637c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate fail_3:
3667c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&state->ms_handle);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate fail_2:
3697c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate fail_1:
3727c478bd9Sstevel@tonic-gate 	kmem_free(state, sizeof (struct mouse_state));
3737c478bd9Sstevel@tonic-gate 	return (rc);
3747c478bd9Sstevel@tonic-gate }
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3777c478bd9Sstevel@tonic-gate static int
3787c478bd9Sstevel@tonic-gate mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3797c478bd9Sstevel@tonic-gate {
3807c478bd9Sstevel@tonic-gate 	struct mouse_state *state;
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	state = ddi_get_driver_private(dip);
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	switch (cmd) {
385fd9cb95cSsethg 	case DDI_SUSPEND:
386*15bfc6b7SSeth Goldberg 		/* Ignore all data from mouse8042_intr until we fully resume */
387*15bfc6b7SSeth Goldberg 		state->ready = 0;
388fd9cb95cSsethg 		return (DDI_SUCCESS);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
3917c478bd9Sstevel@tonic-gate 		ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
3927c478bd9Sstevel@tonic-gate 		mouse8042_dip = NULL;
393*15bfc6b7SSeth Goldberg 		mutex_destroy(&state->reset_mutex);
3947c478bd9Sstevel@tonic-gate 		mutex_destroy(&state->ms_mutex);
3957c478bd9Sstevel@tonic-gate 		ddi_prop_remove_all(dip);
3967c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&state->ms_handle);
3977c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
3987c478bd9Sstevel@tonic-gate 		kmem_free(state, sizeof (struct mouse_state));
3997c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4007c478bd9Sstevel@tonic-gate 
4017c478bd9Sstevel@tonic-gate 	default:
4027c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate /* ARGSUSED */
4087c478bd9Sstevel@tonic-gate static int
4097c478bd9Sstevel@tonic-gate mouse8042_getinfo(
4107c478bd9Sstevel@tonic-gate     dev_info_t *dip,
4117c478bd9Sstevel@tonic-gate     ddi_info_cmd_t infocmd,
4127c478bd9Sstevel@tonic-gate     void *arg,
4137c478bd9Sstevel@tonic-gate     void **result)
4147c478bd9Sstevel@tonic-gate {
4157c478bd9Sstevel@tonic-gate 	dev_t dev = (dev_t)arg;
4167c478bd9Sstevel@tonic-gate 	minor_t	minor = getminor(dev);
4177c478bd9Sstevel@tonic-gate 	int	instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	switch (infocmd) {
4207c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
4217c478bd9Sstevel@tonic-gate 		if (mouse8042_dip == NULL)
4227c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4237c478bd9Sstevel@tonic-gate 
4247c478bd9Sstevel@tonic-gate 		*result = (void *)mouse8042_dip;
4257c478bd9Sstevel@tonic-gate 		break;
4267c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
4277c478bd9Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
4287c478bd9Sstevel@tonic-gate 		break;
4297c478bd9Sstevel@tonic-gate 	default:
4307c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4317c478bd9Sstevel@tonic-gate 	}
4327c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4337c478bd9Sstevel@tonic-gate }
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4367c478bd9Sstevel@tonic-gate static int
4377c478bd9Sstevel@tonic-gate mouse8042_open(
4387c478bd9Sstevel@tonic-gate 	queue_t	*q,
4397c478bd9Sstevel@tonic-gate 	dev_t	*devp,
4407c478bd9Sstevel@tonic-gate 	int	flag,
4417c478bd9Sstevel@tonic-gate 	int	sflag,
4427c478bd9Sstevel@tonic-gate 	cred_t	*cred_p)
4437c478bd9Sstevel@tonic-gate {
4447c478bd9Sstevel@tonic-gate 	struct mouse_state *state;
4457c478bd9Sstevel@tonic-gate 	minor_t	minor = getminor(*devp);
4467c478bd9Sstevel@tonic-gate 	int rval;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	if (mouse8042_dip == NULL)
4497c478bd9Sstevel@tonic-gate 		return (ENXIO);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	state = ddi_get_driver_private(mouse8042_dip);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	if (state->ms_opened) {
4567c478bd9Sstevel@tonic-gate 		/*
4577c478bd9Sstevel@tonic-gate 		 * Exit if the same minor node is already open
4587c478bd9Sstevel@tonic-gate 		 */
4597c478bd9Sstevel@tonic-gate 		if (state->ms_minor == minor) {
4607c478bd9Sstevel@tonic-gate 			mutex_exit(&state->ms_mutex);
4617c478bd9Sstevel@tonic-gate 			return (0);
4627c478bd9Sstevel@tonic-gate 		}
4637c478bd9Sstevel@tonic-gate 
4647c478bd9Sstevel@tonic-gate 		/*
4657c478bd9Sstevel@tonic-gate 		 * Check whether it is switch between physical and virtual
4667c478bd9Sstevel@tonic-gate 		 *
4677c478bd9Sstevel@tonic-gate 		 * Opening from virtual while the device is being physically
4687c478bd9Sstevel@tonic-gate 		 * opened by an application should not happen. So we ASSERT
4697c478bd9Sstevel@tonic-gate 		 * this in DEBUG version, and return error in the non-DEBUG
4707c478bd9Sstevel@tonic-gate 		 * case.
4717c478bd9Sstevel@tonic-gate 		 */
4727c478bd9Sstevel@tonic-gate 		ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 		if (MOUSE8042_INTERNAL_OPEN(minor)) {
4757c478bd9Sstevel@tonic-gate 			mutex_exit(&state->ms_mutex);
4767c478bd9Sstevel@tonic-gate 			return (EINVAL);
4777c478bd9Sstevel@tonic-gate 		}
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 		/*
4807c478bd9Sstevel@tonic-gate 		 * Opening the physical one while it is being underneath
4817c478bd9Sstevel@tonic-gate 		 * the virtual one.
4827c478bd9Sstevel@tonic-gate 		 *
4837c478bd9Sstevel@tonic-gate 		 * consconfig_unlink is called to unlink this device from
4847c478bd9Sstevel@tonic-gate 		 * the virtual one, thus the old stream serving for this
4857c478bd9Sstevel@tonic-gate 		 * device under the virtual one is closed, and then the
4867c478bd9Sstevel@tonic-gate 		 * lower driver's close routine (here is mouse8042_close)
4877c478bd9Sstevel@tonic-gate 		 * is also called to accomplish the whole stream close.
4887c478bd9Sstevel@tonic-gate 		 * Here we have to drop the lock because mouse8042_close
4897c478bd9Sstevel@tonic-gate 		 * also needs the lock.
4907c478bd9Sstevel@tonic-gate 		 *
4917c478bd9Sstevel@tonic-gate 		 * For mouse, the old stream is:
4927c478bd9Sstevel@tonic-gate 		 *	consms->["pushmod"->]"mouse_vp driver"
4937c478bd9Sstevel@tonic-gate 		 *
4947c478bd9Sstevel@tonic-gate 		 * After the consconfig_unlink returns, the old stream is closed
4957c478bd9Sstevel@tonic-gate 		 * and we grab the lock again to reopen this device as normal.
4967c478bd9Sstevel@tonic-gate 		 */
4977c478bd9Sstevel@tonic-gate 		mutex_exit(&state->ms_mutex);
4987c478bd9Sstevel@tonic-gate 
4997c478bd9Sstevel@tonic-gate 		/*
5007c478bd9Sstevel@tonic-gate 		 * If unlink fails, fail the physical open.
5017c478bd9Sstevel@tonic-gate 		 */
5027c478bd9Sstevel@tonic-gate 		if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
5037c478bd9Sstevel@tonic-gate 		    MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
5047c478bd9Sstevel@tonic-gate 			return (rval);
5057c478bd9Sstevel@tonic-gate 		}
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 		mutex_enter(&state->ms_mutex);
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	q->q_ptr = (caddr_t)state;
5127c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = (caddr_t)state;
5137c478bd9Sstevel@tonic-gate 	state->ms_rqp = q;
5147c478bd9Sstevel@tonic-gate 	state->ms_wqp = WR(q);
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	qprocson(q);
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate 	state->ms_minor = minor;
5197c478bd9Sstevel@tonic-gate 	state->ms_opened = B_TRUE;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
5227c478bd9Sstevel@tonic-gate 
5237c478bd9Sstevel@tonic-gate 	return (0);
5247c478bd9Sstevel@tonic-gate }
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5287c478bd9Sstevel@tonic-gate static int
5297c478bd9Sstevel@tonic-gate mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
5307c478bd9Sstevel@tonic-gate {
5317c478bd9Sstevel@tonic-gate 	struct mouse_state *state;
5327c478bd9Sstevel@tonic-gate 	minor_t	minor;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	state = (struct mouse_state *)q->q_ptr;
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	qprocsoff(q);
5397c478bd9Sstevel@tonic-gate 
540*15bfc6b7SSeth Goldberg 	if (state->reset_tid != 0) {
541*15bfc6b7SSeth Goldberg 		(void) quntimeout(q, state->reset_tid);
542*15bfc6b7SSeth Goldberg 		state->reset_tid = 0;
543*15bfc6b7SSeth Goldberg 	}
544*15bfc6b7SSeth Goldberg 	if (state->bc_id != 0) {
545*15bfc6b7SSeth Goldberg 		(void) qunbufcall(q, state->bc_id);
546*15bfc6b7SSeth Goldberg 		state->bc_id = 0;
547*15bfc6b7SSeth Goldberg 	}
548*15bfc6b7SSeth Goldberg 	if (state->reply_mp != NULL) {
549*15bfc6b7SSeth Goldberg 		freemsg(state->reply_mp);
550*15bfc6b7SSeth Goldberg 		state->reply_mp = NULL;
551*15bfc6b7SSeth Goldberg 	}
552*15bfc6b7SSeth Goldberg 
5537c478bd9Sstevel@tonic-gate 	q->q_ptr = NULL;
5547c478bd9Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
5557c478bd9Sstevel@tonic-gate 	state->ms_rqp = NULL;
5567c478bd9Sstevel@tonic-gate 	state->ms_wqp = NULL;
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	state->ms_opened = B_FALSE;
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 	minor = state->ms_minor;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 	if (!MOUSE8042_INTERNAL_OPEN(minor)) {
5657c478bd9Sstevel@tonic-gate 		/*
5667c478bd9Sstevel@tonic-gate 		 * Closing physical PS/2 mouse
5677c478bd9Sstevel@tonic-gate 		 *
5687c478bd9Sstevel@tonic-gate 		 * Link it back to virtual mouse, and
5697c478bd9Sstevel@tonic-gate 		 * mouse8042_open will be called as a result
570fd9cb95cSsethg 		 * of the consconfig_link call.  Do NOT try
571fd9cb95cSsethg 		 * this if the mouse is about to be detached!
5727c478bd9Sstevel@tonic-gate 		 *
5737c478bd9Sstevel@tonic-gate 		 * If linking back fails, this specific mouse
5747c478bd9Sstevel@tonic-gate 		 * will not be available underneath the virtual
5757c478bd9Sstevel@tonic-gate 		 * mouse, and can only be accessed via physical
5767c478bd9Sstevel@tonic-gate 		 * open.
5777c478bd9Sstevel@tonic-gate 		 */
5787c478bd9Sstevel@tonic-gate 		consconfig_link(ddi_driver_major(mouse8042_dip),
5797c478bd9Sstevel@tonic-gate 		    MOUSE8042_INTERNAL_MINOR(minor));
5807c478bd9Sstevel@tonic-gate 	}
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	return (0);
5837c478bd9Sstevel@tonic-gate }
5847c478bd9Sstevel@tonic-gate 
5857c478bd9Sstevel@tonic-gate static void
5867c478bd9Sstevel@tonic-gate mouse8042_iocnack(
5877c478bd9Sstevel@tonic-gate     queue_t *qp,
5887c478bd9Sstevel@tonic-gate     mblk_t *mp,
5897c478bd9Sstevel@tonic-gate     struct iocblk *iocp,
5907c478bd9Sstevel@tonic-gate     int error,
5917c478bd9Sstevel@tonic-gate     int rval)
5927c478bd9Sstevel@tonic-gate {
5937c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCNAK;
5947c478bd9Sstevel@tonic-gate 	iocp->ioc_rval = rval;
5957c478bd9Sstevel@tonic-gate 	iocp->ioc_error = error;
5967c478bd9Sstevel@tonic-gate 	qreply(qp, mp);
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate 
599*15bfc6b7SSeth Goldberg static void
600*15bfc6b7SSeth Goldberg mouse8042_reset_timeout(void *argp)
6017c478bd9Sstevel@tonic-gate {
602*15bfc6b7SSeth Goldberg 	struct mouse_state *state = (struct mouse_state *)argp;
603*15bfc6b7SSeth Goldberg 	mblk_t *mp;
604*15bfc6b7SSeth Goldberg 
605*15bfc6b7SSeth Goldberg 	mutex_enter(&state->reset_mutex);
606*15bfc6b7SSeth Goldberg 
607*15bfc6b7SSeth Goldberg 	/*
608*15bfc6b7SSeth Goldberg 	 * If the interrupt handler hasn't completed the reset handling
609*15bfc6b7SSeth Goldberg 	 * (reset_state would be IDLE or FAILED in that case), then
610*15bfc6b7SSeth Goldberg 	 * drop the 8042 lock, and send a faked retry reply upstream,
611*15bfc6b7SSeth Goldberg 	 * then enable the queue for further message processing.
612*15bfc6b7SSeth Goldberg 	 */
613*15bfc6b7SSeth Goldberg 	if (state->reset_state != MSE_RESET_IDLE &&
614*15bfc6b7SSeth Goldberg 	    state->reset_state != MSE_RESET_FAILED) {
615*15bfc6b7SSeth Goldberg 
616*15bfc6b7SSeth Goldberg 		state->reset_tid = 0;
617*15bfc6b7SSeth Goldberg 		state->reset_state = MSE_RESET_IDLE;
618*15bfc6b7SSeth Goldberg 
619*15bfc6b7SSeth Goldberg 		(void) ddi_get8(state->ms_handle, state->ms_addr +
620*15bfc6b7SSeth Goldberg 		    I8042_UNLOCK);
621*15bfc6b7SSeth Goldberg 
622*15bfc6b7SSeth Goldberg 		mp = state->reply_mp;
623*15bfc6b7SSeth Goldberg 		*mp->b_wptr++ = MSERESEND;
624*15bfc6b7SSeth Goldberg 		state->reply_mp = NULL;
625*15bfc6b7SSeth Goldberg 
626*15bfc6b7SSeth Goldberg 		if (state->ms_rqp != NULL)
627*15bfc6b7SSeth Goldberg 			putnext(state->ms_rqp, mp);
628*15bfc6b7SSeth Goldberg 		else
629*15bfc6b7SSeth Goldberg 			freemsg(mp);
630*15bfc6b7SSeth Goldberg 
631*15bfc6b7SSeth Goldberg 		ASSERT(state->ms_wqp != NULL);
632*15bfc6b7SSeth Goldberg 
633*15bfc6b7SSeth Goldberg 		enableok(state->ms_wqp);
634*15bfc6b7SSeth Goldberg 		qenable(state->ms_wqp);
635*15bfc6b7SSeth Goldberg 	}
636*15bfc6b7SSeth Goldberg 
637*15bfc6b7SSeth Goldberg 	mutex_exit(&state->reset_mutex);
638*15bfc6b7SSeth Goldberg }
639*15bfc6b7SSeth Goldberg 
640*15bfc6b7SSeth Goldberg /*
641*15bfc6b7SSeth Goldberg  * Returns 1 if the caller should put the message (bp) back on the queue
642*15bfc6b7SSeth Goldberg  */
643*15bfc6b7SSeth Goldberg static int
644*15bfc6b7SSeth Goldberg mouse8042_process_reset(queue_t *q, mblk_t *mp, struct mouse_state *state)
645*15bfc6b7SSeth Goldberg {
646*15bfc6b7SSeth Goldberg 	mutex_enter(&state->reset_mutex);
647*15bfc6b7SSeth Goldberg 	/*
648*15bfc6b7SSeth Goldberg 	 * If we're in the middle of a reset, put the message back on the queue
649*15bfc6b7SSeth Goldberg 	 * for processing later.
650*15bfc6b7SSeth Goldberg 	 */
651*15bfc6b7SSeth Goldberg 	if (state->reset_state != MSE_RESET_IDLE) {
652*15bfc6b7SSeth Goldberg 		/*
653*15bfc6b7SSeth Goldberg 		 * We noenable the queue again here in case it was backenabled
654*15bfc6b7SSeth Goldberg 		 * by an upper-level module.
655*15bfc6b7SSeth Goldberg 		 */
656*15bfc6b7SSeth Goldberg 		noenable(q);
657*15bfc6b7SSeth Goldberg 
658*15bfc6b7SSeth Goldberg 		mutex_exit(&state->reset_mutex);
659*15bfc6b7SSeth Goldberg 		return (1);
660*15bfc6b7SSeth Goldberg 	}
661*15bfc6b7SSeth Goldberg 
662*15bfc6b7SSeth Goldberg 	/*
663*15bfc6b7SSeth Goldberg 	 * Drop the reset state lock before allocating the response message and
664*15bfc6b7SSeth Goldberg 	 * grabbing the 8042 exclusive-access lock (since those operations
665*15bfc6b7SSeth Goldberg 	 * may take an extended period of time to complete).
666*15bfc6b7SSeth Goldberg 	 */
667*15bfc6b7SSeth Goldberg 	mutex_exit(&state->reset_mutex);
668*15bfc6b7SSeth Goldberg 
669*15bfc6b7SSeth Goldberg 	state->reply_mp = allocb(3, BPRI_MED);
670*15bfc6b7SSeth Goldberg 	if (state->reply_mp == NULL) {
671*15bfc6b7SSeth Goldberg 		/*
672*15bfc6b7SSeth Goldberg 		 * Allocation failed -- set up a bufcall to enable the queue
673*15bfc6b7SSeth Goldberg 		 * whenever there is enough memory to allocate the response
674*15bfc6b7SSeth Goldberg 		 * message.
675*15bfc6b7SSeth Goldberg 		 */
676*15bfc6b7SSeth Goldberg 		state->bc_id = qbufcall(q, 3, BPRI_MED,
677*15bfc6b7SSeth Goldberg 		    (void (*)(void *))qenable, q);
678*15bfc6b7SSeth Goldberg 
679*15bfc6b7SSeth Goldberg 		if (state->bc_id == 0) {
680*15bfc6b7SSeth Goldberg 			/*
681*15bfc6b7SSeth Goldberg 			 * If the qbufcall failed, we cannot proceed, so use the
682*15bfc6b7SSeth Goldberg 			 * message we were sent to respond with an error.
683*15bfc6b7SSeth Goldberg 			 */
684*15bfc6b7SSeth Goldberg 			*mp->b_rptr = MSEERROR;
685*15bfc6b7SSeth Goldberg 			mp->b_wptr = mp->b_rptr + 1;
686*15bfc6b7SSeth Goldberg 			qreply(q, mp);
687*15bfc6b7SSeth Goldberg 			return (0);
688*15bfc6b7SSeth Goldberg 		}
689*15bfc6b7SSeth Goldberg 
690*15bfc6b7SSeth Goldberg 		return (1);
691*15bfc6b7SSeth Goldberg 	}
692*15bfc6b7SSeth Goldberg 
693*15bfc6b7SSeth Goldberg 	/*
694*15bfc6b7SSeth Goldberg 	 * Gain exclusive access to the 8042 for the duration of the reset.
695*15bfc6b7SSeth Goldberg 	 * The unlock will occur when the reset has either completed or timed
696*15bfc6b7SSeth Goldberg 	 * out.
697*15bfc6b7SSeth Goldberg 	 */
698*15bfc6b7SSeth Goldberg 	(void) ddi_get8(state->ms_handle,
699*15bfc6b7SSeth Goldberg 	    state->ms_addr + I8042_LOCK);
700*15bfc6b7SSeth Goldberg 
701*15bfc6b7SSeth Goldberg 	mutex_enter(&state->reset_mutex);
702*15bfc6b7SSeth Goldberg 
703*15bfc6b7SSeth Goldberg 	state->reset_state = MSE_RESET_PRE;
704*15bfc6b7SSeth Goldberg 	noenable(q);
705*15bfc6b7SSeth Goldberg 
706*15bfc6b7SSeth Goldberg 	state->reset_tid = qtimeout(q,
707*15bfc6b7SSeth Goldberg 	    mouse8042_reset_timeout,
708*15bfc6b7SSeth Goldberg 	    state,
709*15bfc6b7SSeth Goldberg 	    drv_usectohz(
710*15bfc6b7SSeth Goldberg 	    MOUSE8042_RESET_TIMEOUT_USECS));
711*15bfc6b7SSeth Goldberg 
712*15bfc6b7SSeth Goldberg 	ddi_put8(state->ms_handle,
713*15bfc6b7SSeth Goldberg 	    state->ms_addr +
714*15bfc6b7SSeth Goldberg 	    I8042_INT_OUTPUT_DATA, MSERESET);
715*15bfc6b7SSeth Goldberg 
716*15bfc6b7SSeth Goldberg 	mp->b_rptr++;
717*15bfc6b7SSeth Goldberg 
718*15bfc6b7SSeth Goldberg 	mutex_exit(&state->reset_mutex);
719*15bfc6b7SSeth Goldberg 	return (1);
720*15bfc6b7SSeth Goldberg }
721*15bfc6b7SSeth Goldberg 
722*15bfc6b7SSeth Goldberg /*
723*15bfc6b7SSeth Goldberg  * Returns 1 if the caller should stop processing messages
724*15bfc6b7SSeth Goldberg  */
725*15bfc6b7SSeth Goldberg static int
726*15bfc6b7SSeth Goldberg mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
727*15bfc6b7SSeth Goldberg {
7287c478bd9Sstevel@tonic-gate 	mblk_t *bp;
7297c478bd9Sstevel@tonic-gate 	mblk_t *next;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 	bp = mp;
7327c478bd9Sstevel@tonic-gate 	do {
7337c478bd9Sstevel@tonic-gate 		while (bp->b_rptr < bp->b_wptr) {
734*15bfc6b7SSeth Goldberg 			/*
735*15bfc6b7SSeth Goldberg 			 * Detect an attempt to reset the mouse.  Lock out any
736*15bfc6b7SSeth Goldberg 			 * further mouse writes until the reset has completed.
737*15bfc6b7SSeth Goldberg 			 */
738*15bfc6b7SSeth Goldberg 			if (*bp->b_rptr == MSERESET) {
739*15bfc6b7SSeth Goldberg 
740*15bfc6b7SSeth Goldberg 				/*
741*15bfc6b7SSeth Goldberg 				 * If we couldn't allocate memory and we
742*15bfc6b7SSeth Goldberg 				 * we couldn't register a bufcall,
743*15bfc6b7SSeth Goldberg 				 * mouse8042_process_reset returns 0 and
744*15bfc6b7SSeth Goldberg 				 * has already used the message to send an
745*15bfc6b7SSeth Goldberg 				 * error reply back upstream, so there is no
746*15bfc6b7SSeth Goldberg 				 * need to deallocate or put this message back
747*15bfc6b7SSeth Goldberg 				 * on the queue.
748*15bfc6b7SSeth Goldberg 				 */
749*15bfc6b7SSeth Goldberg 				if (mouse8042_process_reset(q, bp, state) == 0)
750*15bfc6b7SSeth Goldberg 					return (1);
751*15bfc6b7SSeth Goldberg 
752*15bfc6b7SSeth Goldberg 				/*
753*15bfc6b7SSeth Goldberg 				 * If there's no data remaining in this block,
754*15bfc6b7SSeth Goldberg 				 * free this block and put the following blocks
755*15bfc6b7SSeth Goldberg 				 * of this message back on the queue. If putting
756*15bfc6b7SSeth Goldberg 				 * the rest of the message back on the queue
757*15bfc6b7SSeth Goldberg 				 * fails, free the the message.
758*15bfc6b7SSeth Goldberg 				 */
759*15bfc6b7SSeth Goldberg 				if (MBLKL(bp) == 0) {
760*15bfc6b7SSeth Goldberg 					next = bp->b_cont;
761*15bfc6b7SSeth Goldberg 					freeb(bp);
762*15bfc6b7SSeth Goldberg 					bp = next;
7637c478bd9Sstevel@tonic-gate 				}
764*15bfc6b7SSeth Goldberg 				if (bp != NULL) {
765*15bfc6b7SSeth Goldberg 					if (!putbq(q, bp))
766*15bfc6b7SSeth Goldberg 						freemsg(bp);
7677c478bd9Sstevel@tonic-gate 				}
768*15bfc6b7SSeth Goldberg 
769*15bfc6b7SSeth Goldberg 				return (1);
770*15bfc6b7SSeth Goldberg 
771*15bfc6b7SSeth Goldberg 			}
7727c478bd9Sstevel@tonic-gate 			ddi_put8(state->ms_handle,
7737c478bd9Sstevel@tonic-gate 			    state->ms_addr + I8042_INT_OUTPUT_DATA,
7747c478bd9Sstevel@tonic-gate 			    *bp->b_rptr++);
7757c478bd9Sstevel@tonic-gate 		}
7767c478bd9Sstevel@tonic-gate 		next = bp->b_cont;
7777c478bd9Sstevel@tonic-gate 		freeb(bp);
7787c478bd9Sstevel@tonic-gate 	} while ((bp = next) != NULL);
779*15bfc6b7SSeth Goldberg 
780*15bfc6b7SSeth Goldberg 	return (0);
781*15bfc6b7SSeth Goldberg }
782*15bfc6b7SSeth Goldberg 
783*15bfc6b7SSeth Goldberg static int
784*15bfc6b7SSeth Goldberg mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
785*15bfc6b7SSeth Goldberg {
786*15bfc6b7SSeth Goldberg 	struct iocblk *iocbp;
787*15bfc6b7SSeth Goldberg 	int rv = 0;
788*15bfc6b7SSeth Goldberg 
789*15bfc6b7SSeth Goldberg 	iocbp = (struct iocblk *)mp->b_rptr;
790*15bfc6b7SSeth Goldberg 
791*15bfc6b7SSeth Goldberg 	switch (mp->b_datap->db_type) {
792*15bfc6b7SSeth Goldberg 	case M_FLUSH:
793*15bfc6b7SSeth Goldberg 		if (*mp->b_rptr & FLUSHW) {
794*15bfc6b7SSeth Goldberg 			flushq(q, FLUSHDATA);
795*15bfc6b7SSeth Goldberg 			*mp->b_rptr &= ~FLUSHW;
796*15bfc6b7SSeth Goldberg 		}
797*15bfc6b7SSeth Goldberg 		if (*mp->b_rptr & FLUSHR) {
798*15bfc6b7SSeth Goldberg 			qreply(q, mp);
799*15bfc6b7SSeth Goldberg 		} else
800*15bfc6b7SSeth Goldberg 			freemsg(mp);
801*15bfc6b7SSeth Goldberg 		break;
802*15bfc6b7SSeth Goldberg 	case M_IOCTL:
803*15bfc6b7SSeth Goldberg 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
804*15bfc6b7SSeth Goldberg 		break;
805*15bfc6b7SSeth Goldberg 	case M_IOCDATA:
806*15bfc6b7SSeth Goldberg 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
807*15bfc6b7SSeth Goldberg 		break;
808*15bfc6b7SSeth Goldberg 	case M_DATA:
809*15bfc6b7SSeth Goldberg 		rv = mouse8042_process_data_msg(q, mp, state);
8107c478bd9Sstevel@tonic-gate 		break;
8117c478bd9Sstevel@tonic-gate 	default:
8127c478bd9Sstevel@tonic-gate 		freemsg(mp);
8137c478bd9Sstevel@tonic-gate 		break;
8147c478bd9Sstevel@tonic-gate 	}
815*15bfc6b7SSeth Goldberg 
816*15bfc6b7SSeth Goldberg 	return (rv);
817*15bfc6b7SSeth Goldberg }
818*15bfc6b7SSeth Goldberg 
819*15bfc6b7SSeth Goldberg static int
820*15bfc6b7SSeth Goldberg mouse8042_wsrv(queue_t *qp)
821*15bfc6b7SSeth Goldberg {
822*15bfc6b7SSeth Goldberg 	mblk_t *mp;
823*15bfc6b7SSeth Goldberg 	struct mouse_state *state;
824*15bfc6b7SSeth Goldberg 	state = (struct mouse_state *)qp->q_ptr;
825*15bfc6b7SSeth Goldberg 
826*15bfc6b7SSeth Goldberg 	while ((mp = getq(qp)) != NULL) {
827*15bfc6b7SSeth Goldberg 		if (mouse8042_process_msg(qp, mp, state) != 0)
828*15bfc6b7SSeth Goldberg 			break;
829*15bfc6b7SSeth Goldberg 	}
830*15bfc6b7SSeth Goldberg 
831*15bfc6b7SSeth Goldberg 	return (0);
832*15bfc6b7SSeth Goldberg }
833*15bfc6b7SSeth Goldberg 
834*15bfc6b7SSeth Goldberg /*
835*15bfc6b7SSeth Goldberg  * Returns the next reset state, given the current state and the byte
836*15bfc6b7SSeth Goldberg  * received from the mouse.  Error and Resend codes are handled by the
837*15bfc6b7SSeth Goldberg  * caller.
838*15bfc6b7SSeth Goldberg  */
839*15bfc6b7SSeth Goldberg static mouse8042_reset_state_e
840*15bfc6b7SSeth Goldberg mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata)
841*15bfc6b7SSeth Goldberg {
842*15bfc6b7SSeth Goldberg 	switch (reset_state) {
843*15bfc6b7SSeth Goldberg 	case MSE_RESET_PRE:	/* RESET sent, now we expect an ACK */
844*15bfc6b7SSeth Goldberg 		if (mdata == MSE_ACK)	/* Got the ACK */
845*15bfc6b7SSeth Goldberg 			return (MSE_RESET_ACK);
846*15bfc6b7SSeth Goldberg 		break;
847*15bfc6b7SSeth Goldberg 
848*15bfc6b7SSeth Goldberg 	case MSE_RESET_ACK:	/* ACK received; now we expect 0xAA */
849*15bfc6b7SSeth Goldberg 		if (mdata == MSE_AA)	/* Got the 0xAA */
850*15bfc6b7SSeth Goldberg 			return (MSE_RESET_AA);
851*15bfc6b7SSeth Goldberg 		break;
852*15bfc6b7SSeth Goldberg 
853*15bfc6b7SSeth Goldberg 	case MSE_RESET_AA: 	/* 0xAA received; now we expect 0x00 */
854*15bfc6b7SSeth Goldberg 		if (mdata == MSE_00)
855*15bfc6b7SSeth Goldberg 			return (MSE_RESET_IDLE);
856*15bfc6b7SSeth Goldberg 		break;
857*15bfc6b7SSeth Goldberg 	}
858*15bfc6b7SSeth Goldberg 
859*15bfc6b7SSeth Goldberg 	return (reset_state);
8607c478bd9Sstevel@tonic-gate }
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate static uint_t
8637c478bd9Sstevel@tonic-gate mouse8042_intr(caddr_t arg)
8647c478bd9Sstevel@tonic-gate {
8657c478bd9Sstevel@tonic-gate 	unsigned char    mdata;
8667c478bd9Sstevel@tonic-gate 	mblk_t *mp;
8677c478bd9Sstevel@tonic-gate 	struct mouse_state *state = (struct mouse_state *)arg;
8687c478bd9Sstevel@tonic-gate 	int rc;
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	rc = DDI_INTR_UNCLAIMED;
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate 	for (;;) {
8757c478bd9Sstevel@tonic-gate 
8767c478bd9Sstevel@tonic-gate 		if (ddi_get8(state->ms_handle,
8777c478bd9Sstevel@tonic-gate 		    state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
8787c478bd9Sstevel@tonic-gate 			break;
8797c478bd9Sstevel@tonic-gate 		}
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 		mdata = ddi_get8(state->ms_handle,
8827c478bd9Sstevel@tonic-gate 		    state->ms_addr + I8042_INT_INPUT_DATA);
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 		rc = DDI_INTR_CLAIMED;
8857c478bd9Sstevel@tonic-gate 
886*15bfc6b7SSeth Goldberg 		/*
887*15bfc6b7SSeth Goldberg 		 * If we're not ready for this data, discard it.
888*15bfc6b7SSeth Goldberg 		 */
889*15bfc6b7SSeth Goldberg 		if (!state->ready)
890*15bfc6b7SSeth Goldberg 			continue;
891*15bfc6b7SSeth Goldberg 
892*15bfc6b7SSeth Goldberg 		mutex_enter(&state->reset_mutex);
893*15bfc6b7SSeth Goldberg 		if (state->reset_state != MSE_RESET_IDLE) {
894*15bfc6b7SSeth Goldberg 
895*15bfc6b7SSeth Goldberg 			if (mdata == MSEERROR || mdata == MSERESET) {
896*15bfc6b7SSeth Goldberg 				state->reset_state = MSE_RESET_FAILED;
897*15bfc6b7SSeth Goldberg 			} else {
898*15bfc6b7SSeth Goldberg 				state->reset_state =
899*15bfc6b7SSeth Goldberg 				    mouse8042_reset_fsm(state->reset_state,
900*15bfc6b7SSeth Goldberg 				    mdata);
901*15bfc6b7SSeth Goldberg 			}
902*15bfc6b7SSeth Goldberg 
903*15bfc6b7SSeth Goldberg 			/*
904*15bfc6b7SSeth Goldberg 			 * If we transitioned back to the idle reset state (or
905*15bfc6b7SSeth Goldberg 			 * the reset failed), disable the timeout, release the
906*15bfc6b7SSeth Goldberg 			 * 8042 exclusive-access lock, then send the response
907*15bfc6b7SSeth Goldberg 			 * the the upper-level modules. Finally, enable the
908*15bfc6b7SSeth Goldberg 			 * queue and schedule queue service procedures so that
909*15bfc6b7SSeth Goldberg 			 * upper-level modules can process the response.
910*15bfc6b7SSeth Goldberg 			 * Otherwise, if we're still in the middle of the
911*15bfc6b7SSeth Goldberg 			 * reset sequence, do not send the data up (since the
912*15bfc6b7SSeth Goldberg 			 * response is sent at the end of the sequence, or
913*15bfc6b7SSeth Goldberg 			 * on timeout/error).
914*15bfc6b7SSeth Goldberg 			 */
915*15bfc6b7SSeth Goldberg 			if (state->reset_state == MSE_RESET_IDLE ||
916*15bfc6b7SSeth Goldberg 			    state->reset_state == MSE_RESET_FAILED) {
917*15bfc6b7SSeth Goldberg 
918*15bfc6b7SSeth Goldberg 				mutex_exit(&state->reset_mutex);
919*15bfc6b7SSeth Goldberg 				(void) quntimeout(state->ms_wqp,
920*15bfc6b7SSeth Goldberg 				    state->reset_tid);
921*15bfc6b7SSeth Goldberg 				mutex_enter(&state->reset_mutex);
922*15bfc6b7SSeth Goldberg 
923*15bfc6b7SSeth Goldberg 				(void) ddi_get8(state->ms_handle,
924*15bfc6b7SSeth Goldberg 				    state->ms_addr + I8042_UNLOCK);
925*15bfc6b7SSeth Goldberg 
926*15bfc6b7SSeth Goldberg 				state->reset_tid = 0;
927*15bfc6b7SSeth Goldberg 				mp = state->reply_mp;
928*15bfc6b7SSeth Goldberg 				if (state->reset_state == MSE_RESET_FAILED) {
929*15bfc6b7SSeth Goldberg 					*mp->b_wptr++ = mdata;
930*15bfc6b7SSeth Goldberg 				} else {
931*15bfc6b7SSeth Goldberg 					*mp->b_wptr++ = MSE_ACK;
932*15bfc6b7SSeth Goldberg 					*mp->b_wptr++ = MSE_AA;
933*15bfc6b7SSeth Goldberg 					*mp->b_wptr++ = MSE_00;
934*15bfc6b7SSeth Goldberg 				}
935*15bfc6b7SSeth Goldberg 				state->reply_mp = NULL;
936*15bfc6b7SSeth Goldberg 
937*15bfc6b7SSeth Goldberg 				state->reset_state = MSE_RESET_IDLE;
938*15bfc6b7SSeth Goldberg 
939*15bfc6b7SSeth Goldberg 				if (state->ms_rqp != NULL)
940*15bfc6b7SSeth Goldberg 					putnext(state->ms_rqp, mp);
941*15bfc6b7SSeth Goldberg 				else
942*15bfc6b7SSeth Goldberg 					freemsg(mp);
943*15bfc6b7SSeth Goldberg 
944*15bfc6b7SSeth Goldberg 				enableok(state->ms_wqp);
945*15bfc6b7SSeth Goldberg 				qenable(state->ms_wqp);
946*15bfc6b7SSeth Goldberg 			}
947*15bfc6b7SSeth Goldberg 
948*15bfc6b7SSeth Goldberg 			mutex_exit(&state->reset_mutex);
949*15bfc6b7SSeth Goldberg 			mutex_exit(&state->ms_mutex);
950*15bfc6b7SSeth Goldberg 			return (rc);
951*15bfc6b7SSeth Goldberg 		}
952*15bfc6b7SSeth Goldberg 		mutex_exit(&state->reset_mutex);
953*15bfc6b7SSeth Goldberg 
9547c478bd9Sstevel@tonic-gate 		if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
9557c478bd9Sstevel@tonic-gate 			*mp->b_wptr++ = mdata;
9567c478bd9Sstevel@tonic-gate 			putnext(state->ms_rqp, mp);
9577c478bd9Sstevel@tonic-gate 		}
9587c478bd9Sstevel@tonic-gate 	}
9597c478bd9Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	return (rc);
9627c478bd9Sstevel@tonic-gate }
963