xref: /onnv-gate/usr/src/uts/common/io/mouse8042.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7656SSherry.Moore@Sun.COM  * Common Development and Distribution License (the "License").
6*7656SSherry.Moore@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
220Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
230Sstevel@tonic-gate /*	  All Rights Reserved  	*/
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
26*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
270Sstevel@tonic-gate  * Use is subject to license terms.
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
300Sstevel@tonic-gate 
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate  * PS/2 type Mouse Module - Streams
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include <sys/param.h>
360Sstevel@tonic-gate #include <sys/types.h>
370Sstevel@tonic-gate #include <sys/kmem.h>
380Sstevel@tonic-gate #include <sys/signal.h>
390Sstevel@tonic-gate #include <sys/errno.h>
400Sstevel@tonic-gate #include <sys/file.h>
410Sstevel@tonic-gate #include <sys/termio.h>
420Sstevel@tonic-gate #include <sys/stream.h>
430Sstevel@tonic-gate #include <sys/stropts.h>
440Sstevel@tonic-gate #include <sys/strtty.h>
450Sstevel@tonic-gate #include <sys/debug.h>
460Sstevel@tonic-gate #include <sys/ddi.h>
470Sstevel@tonic-gate #include <sys/stat.h>
480Sstevel@tonic-gate #include <sys/cmn_err.h>
490Sstevel@tonic-gate #include <sys/sunddi.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #include <sys/promif.h>
520Sstevel@tonic-gate #include <sys/cred.h>
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #include <sys/i8042.h>
550Sstevel@tonic-gate #include <sys/note.h>
560Sstevel@tonic-gate 
570Sstevel@tonic-gate #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
580Sstevel@tonic-gate 
590Sstevel@tonic-gate #ifdef	DEBUG
600Sstevel@tonic-gate #define	MOUSE8042_DEBUG
610Sstevel@tonic-gate #endif
620Sstevel@tonic-gate 
630Sstevel@tonic-gate #define	MOUSE8042_INTERNAL_OPEN(minor)	(((minor) & 0x1) == 1)
640Sstevel@tonic-gate #define	MOUSE8042_MINOR_TO_INSTANCE(minor)	((minor) / 2)
650Sstevel@tonic-gate #define	MOUSE8042_INTERNAL_MINOR(minor)		((minor) + 1)
660Sstevel@tonic-gate 
670Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
680Sstevel@tonic-gate extern void consconfig_link(major_t major, minor_t minor);
690Sstevel@tonic-gate extern int consconfig_unlink(major_t major, minor_t minor);
700Sstevel@tonic-gate 
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /*
730Sstevel@tonic-gate  *
740Sstevel@tonic-gate  * Local Static Data
750Sstevel@tonic-gate  *
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /*
790Sstevel@tonic-gate  * We only support one instance.  Yes, it's theoretically possible to
800Sstevel@tonic-gate  * plug in more than one, but it's not worth the implementation cost.
810Sstevel@tonic-gate  *
820Sstevel@tonic-gate  * The introduction of USB keyboards might make it worth reassessing
830Sstevel@tonic-gate  * this decision, as they might free up the keyboard port for a second
840Sstevel@tonic-gate  * PS/2 style mouse.
850Sstevel@tonic-gate  */
860Sstevel@tonic-gate static dev_info_t *mouse8042_dip;
870Sstevel@tonic-gate 
880Sstevel@tonic-gate struct mouse_state {
890Sstevel@tonic-gate 	queue_t	*ms_rqp;
900Sstevel@tonic-gate 	queue_t	*ms_wqp;
910Sstevel@tonic-gate 	ddi_iblock_cookie_t	ms_iblock_cookie;
920Sstevel@tonic-gate 	ddi_acc_handle_t	ms_handle;
930Sstevel@tonic-gate 	uint8_t			*ms_addr;
940Sstevel@tonic-gate 	kmutex_t		ms_mutex;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	minor_t			ms_minor;
970Sstevel@tonic-gate 	boolean_t		ms_opened;
980Sstevel@tonic-gate };
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
1010Sstevel@tonic-gate int mouse8042_debug = 0;
1020Sstevel@tonic-gate int mouse8042_debug_minimal = 0;
1030Sstevel@tonic-gate #endif
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate static uint_t mouse8042_intr(caddr_t arg);
1060Sstevel@tonic-gate static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
1070Sstevel@tonic-gate 		cred_t *cred_p);
1080Sstevel@tonic-gate static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
1090Sstevel@tonic-gate static int mouse8042_wput(queue_t *q, mblk_t *mp);
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
1120Sstevel@tonic-gate 		void *arg, void **result);
1130Sstevel@tonic-gate static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1140Sstevel@tonic-gate static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate  * Streams module info.
1190Sstevel@tonic-gate  */
1200Sstevel@tonic-gate #define	MODULE_NAME	"mouse8042"
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate static struct module_info	mouse8042_minfo = {
1230Sstevel@tonic-gate 	23,		/* Module ID number */
1240Sstevel@tonic-gate 	MODULE_NAME,
1250Sstevel@tonic-gate 	0, INFPSZ,	/* minimum & maximum packet sizes */
1260Sstevel@tonic-gate 	256, 128	/* hi and low water marks */
1270Sstevel@tonic-gate };
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate static struct qinit mouse8042_rinit = {
1300Sstevel@tonic-gate 	NULL,		/* put */
1310Sstevel@tonic-gate 	NULL,		/* service */
1320Sstevel@tonic-gate 	mouse8042_open,
1330Sstevel@tonic-gate 	mouse8042_close,
1340Sstevel@tonic-gate 	NULL,		/* admin */
1350Sstevel@tonic-gate 	&mouse8042_minfo,
1360Sstevel@tonic-gate 	NULL		/* statistics */
1370Sstevel@tonic-gate };
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate static struct qinit mouse8042_winit = {
1400Sstevel@tonic-gate 	mouse8042_wput,	/* put */
1410Sstevel@tonic-gate 	NULL,		/* service */
1420Sstevel@tonic-gate 	NULL,		/* open */
1430Sstevel@tonic-gate 	NULL,		/* close */
1440Sstevel@tonic-gate 	NULL,		/* admin */
1450Sstevel@tonic-gate 	&mouse8042_minfo,
1460Sstevel@tonic-gate 	NULL		/* statistics */
1470Sstevel@tonic-gate };
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate static struct streamtab mouse8042_strinfo = {
1500Sstevel@tonic-gate 	&mouse8042_rinit,
1510Sstevel@tonic-gate 	&mouse8042_winit,
1520Sstevel@tonic-gate 	NULL,		/* muxrinit */
1530Sstevel@tonic-gate 	NULL,		/* muxwinit */
1540Sstevel@tonic-gate };
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate /*
1570Sstevel@tonic-gate  * Local Function Declarations
1580Sstevel@tonic-gate  */
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate static struct cb_ops	mouse8042_cb_ops = {
1610Sstevel@tonic-gate 	nodev,			/* open */
1620Sstevel@tonic-gate 	nodev,			/* close */
1630Sstevel@tonic-gate 	nodev,			/* strategy */
1640Sstevel@tonic-gate 	nodev,			/* print */
1650Sstevel@tonic-gate 	nodev,			/* dump */
1660Sstevel@tonic-gate 	nodev,			/* read */
1670Sstevel@tonic-gate 	nodev,			/* write */
1680Sstevel@tonic-gate 	nodev,			/* ioctl */
1690Sstevel@tonic-gate 	nodev,			/* devmap */
1700Sstevel@tonic-gate 	nodev,			/* mmap */
1710Sstevel@tonic-gate 	nodev,			/* segmap */
1720Sstevel@tonic-gate 	nochpoll,		/* poll */
1730Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
1740Sstevel@tonic-gate 	&mouse8042_strinfo,	/* streamtab  */
1750Sstevel@tonic-gate 	D_MP | D_NEW
1760Sstevel@tonic-gate };
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate static struct dev_ops	mouse8042_ops = {
1800Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
1810Sstevel@tonic-gate 	0,			/* refcnt  */
1820Sstevel@tonic-gate 	mouse8042_getinfo,	/* getinfo */
1830Sstevel@tonic-gate 	nulldev,		/* identify */
1840Sstevel@tonic-gate 	nulldev,		/* probe */
1850Sstevel@tonic-gate 	mouse8042_attach,	/* attach */
1860Sstevel@tonic-gate 	mouse8042_detach,	/* detach */
1870Sstevel@tonic-gate 	nodev,			/* reset */
1880Sstevel@tonic-gate 	&mouse8042_cb_ops,	/* driver operations */
189*7656SSherry.Moore@Sun.COM 	(struct bus_ops *)0,	/* bus operations */
190*7656SSherry.Moore@Sun.COM 	NULL,			/* power */
191*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1920Sstevel@tonic-gate };
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate /*
1950Sstevel@tonic-gate  * This is the loadable module wrapper.
1960Sstevel@tonic-gate  */
1970Sstevel@tonic-gate #include <sys/modctl.h>
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate /*
2020Sstevel@tonic-gate  * Module linkage information for the kernel.
2030Sstevel@tonic-gate  */
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate static struct modldrv modldrv = {
2060Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
207*7656SSherry.Moore@Sun.COM 	"PS/2 Mouse",
2080Sstevel@tonic-gate 	&mouse8042_ops,	/* driver ops */
2090Sstevel@tonic-gate };
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate static struct modlinkage modlinkage = {
2120Sstevel@tonic-gate 	MODREV_1,
2130Sstevel@tonic-gate 	(void *)&modldrv,
2140Sstevel@tonic-gate 	NULL
2150Sstevel@tonic-gate };
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate /*
2180Sstevel@tonic-gate  * This is the driver initialization routine.
2190Sstevel@tonic-gate  */
2200Sstevel@tonic-gate int
2210Sstevel@tonic-gate _init()
2220Sstevel@tonic-gate {
2230Sstevel@tonic-gate 	int	rv;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	rv = mod_install(&modlinkage);
2260Sstevel@tonic-gate 	return (rv);
2270Sstevel@tonic-gate }
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate int
2310Sstevel@tonic-gate _fini(void)
2320Sstevel@tonic-gate {
2330Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2340Sstevel@tonic-gate }
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate int
2380Sstevel@tonic-gate _info(struct modinfo *modinfop)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate static int
2440Sstevel@tonic-gate mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2450Sstevel@tonic-gate {
2460Sstevel@tonic-gate 	struct mouse_state *state;
247822Ssethg 	mblk_t *mp;
2480Sstevel@tonic-gate 	int instance = ddi_get_instance(dip);
2490Sstevel@tonic-gate 	static ddi_device_acc_attr_t attr = {
2500Sstevel@tonic-gate 		DDI_DEVICE_ATTR_V0,
2510Sstevel@tonic-gate 		DDI_NEVERSWAP_ACC,
2520Sstevel@tonic-gate 		DDI_STRICTORDER_ACC,
2530Sstevel@tonic-gate 	};
2540Sstevel@tonic-gate 	int rc;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
2580Sstevel@tonic-gate 	if (mouse8042_debug) {
2590Sstevel@tonic-gate 		cmn_err(CE_CONT, MODULE_NAME "_attach entry\n");
2600Sstevel@tonic-gate 	}
2610Sstevel@tonic-gate #endif
2620Sstevel@tonic-gate 
263822Ssethg 	if (cmd == DDI_RESUME) {
264822Ssethg 		state = (struct mouse_state *)ddi_get_driver_private(dip);
265822Ssethg 
266822Ssethg 		/*
267822Ssethg 		 * Send a 0xaa 0x00 upstream.
268822Ssethg 		 * This causes the vuid module to reset the mouse.
269822Ssethg 		 */
270822Ssethg 		if (state->ms_rqp != NULL) {
271822Ssethg 			if (mp = allocb(1, BPRI_MED)) {
272822Ssethg 				*mp->b_wptr++ = 0xaa;
273822Ssethg 				putnext(state->ms_rqp, mp);
274822Ssethg 			}
275822Ssethg 			if (mp = allocb(1, BPRI_MED)) {
276822Ssethg 				*mp->b_wptr++ = 0x0;
277822Ssethg 				putnext(state->ms_rqp, mp);
278822Ssethg 			}
279822Ssethg 		}
280822Ssethg 		return (DDI_SUCCESS);
281822Ssethg 	}
282822Ssethg 
2830Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
2840Sstevel@tonic-gate 		return (DDI_FAILURE);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	if (mouse8042_dip != NULL)
2870Sstevel@tonic-gate 		return (DDI_FAILURE);
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/* allocate and initialize state structure */
2900Sstevel@tonic-gate 	state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
2910Sstevel@tonic-gate 	state->ms_opened = B_FALSE;
2920Sstevel@tonic-gate 	ddi_set_driver_private(dip, state);
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	/*
2950Sstevel@tonic-gate 	 * In order to support virtual keyboard/mouse, we should distinguish
2960Sstevel@tonic-gate 	 * between internal virtual open and external physical open.
2970Sstevel@tonic-gate 	 *
2980Sstevel@tonic-gate 	 * When the physical devices are opened by application, they will
2990Sstevel@tonic-gate 	 * be unlinked from the virtual device and their data stream will
3000Sstevel@tonic-gate 	 * not be sent to the virtual device. When the opened physical
3010Sstevel@tonic-gate 	 * devices are closed, they will be relinked to the virtual devices.
3020Sstevel@tonic-gate 	 *
3030Sstevel@tonic-gate 	 * All these automatic switch between virtual and physical are
3040Sstevel@tonic-gate 	 * transparent.
3050Sstevel@tonic-gate 	 *
3060Sstevel@tonic-gate 	 * So we change minor node numbering scheme to be:
3070Sstevel@tonic-gate 	 * 	external node minor num == instance * 2
3080Sstevel@tonic-gate 	 *	internal node minor num == instance * 2 + 1
3090Sstevel@tonic-gate 	 */
310822Ssethg 	rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
3110Sstevel@tonic-gate 	    DDI_NT_MOUSE, NULL);
3120Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3130Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
3140Sstevel@tonic-gate 		cmn_err(CE_CONT,
3150Sstevel@tonic-gate 		    MODULE_NAME "_attach: ddi_create_minor_node failed\n");
3160Sstevel@tonic-gate #endif
3170Sstevel@tonic-gate 		goto fail_1;
3180Sstevel@tonic-gate 	}
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
321*7656SSherry.Moore@Sun.COM 	    instance * 2 + 1) != DDI_SUCCESS) {
3220Sstevel@tonic-gate 		goto fail_2;
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
326*7656SSherry.Moore@Sun.COM 	    (offset_t)0, (offset_t)0, &attr, &state->ms_handle);
3270Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3280Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
3290Sstevel@tonic-gate 		cmn_err(CE_WARN, MODULE_NAME "_attach:  can't map registers");
3300Sstevel@tonic-gate #endif
3310Sstevel@tonic-gate 		goto fail_2;
3320Sstevel@tonic-gate 	}
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
3350Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3360Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
3370Sstevel@tonic-gate 		cmn_err(CE_WARN,
3380Sstevel@tonic-gate 		    MODULE_NAME "_attach:  Can't get iblock cookie");
3390Sstevel@tonic-gate #endif
3400Sstevel@tonic-gate 		goto fail_3;
3410Sstevel@tonic-gate 	}
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
3440Sstevel@tonic-gate 	    state->ms_iblock_cookie);
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate 	rc = ddi_add_intr(dip, 0,
347*7656SSherry.Moore@Sun.COM 	    (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
348*7656SSherry.Moore@Sun.COM 	    mouse8042_intr, (caddr_t)state);
3490Sstevel@tonic-gate 	if (rc != DDI_SUCCESS) {
3500Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
3510Sstevel@tonic-gate 		cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt");
3520Sstevel@tonic-gate #endif
3530Sstevel@tonic-gate 		goto fail_3;
3540Sstevel@tonic-gate 	}
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	mouse8042_dip = dip;
3570Sstevel@tonic-gate 
3580Sstevel@tonic-gate 	/* Now that we're attached, announce our presence to the world. */
3590Sstevel@tonic-gate 	ddi_report_dev(dip);
3600Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
361*7656SSherry.Moore@Sun.COM 	cmn_err(CE_CONT, "?%s #%d\n", DRIVER_NAME(dip), ddi_get_instance(dip));
3620Sstevel@tonic-gate #endif
3630Sstevel@tonic-gate 	return (DDI_SUCCESS);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate fail_3:
3660Sstevel@tonic-gate 	ddi_regs_map_free(&state->ms_handle);
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate fail_2:
3690Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate fail_1:
3720Sstevel@tonic-gate 	kmem_free(state, sizeof (struct mouse_state));
3730Sstevel@tonic-gate 	return (rc);
3740Sstevel@tonic-gate }
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate /*ARGSUSED*/
3770Sstevel@tonic-gate static int
3780Sstevel@tonic-gate mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3790Sstevel@tonic-gate {
3800Sstevel@tonic-gate 	struct mouse_state *state;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	state = ddi_get_driver_private(dip);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	switch (cmd) {
385822Ssethg 	case DDI_SUSPEND:
386822Ssethg 		return (DDI_SUCCESS);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	case DDI_DETACH:
3890Sstevel@tonic-gate 		ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
3900Sstevel@tonic-gate 		mouse8042_dip = NULL;
3910Sstevel@tonic-gate 		mutex_destroy(&state->ms_mutex);
3920Sstevel@tonic-gate 		ddi_prop_remove_all(dip);
3930Sstevel@tonic-gate 		ddi_regs_map_free(&state->ms_handle);
3940Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
3950Sstevel@tonic-gate 		kmem_free(state, sizeof (struct mouse_state));
3960Sstevel@tonic-gate 		return (DDI_SUCCESS);
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	default:
3990Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
4000Sstevel@tonic-gate 		if (mouse8042_debug) {
4010Sstevel@tonic-gate 			cmn_err(CE_CONT,
4020Sstevel@tonic-gate 			    "mouse8042_detach: cmd = %d unknown\n", cmd);
4030Sstevel@tonic-gate 		}
4040Sstevel@tonic-gate #endif
4050Sstevel@tonic-gate 		return (DDI_FAILURE);
4060Sstevel@tonic-gate 	}
4070Sstevel@tonic-gate }
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate /* ARGSUSED */
4110Sstevel@tonic-gate static int
4120Sstevel@tonic-gate mouse8042_getinfo(
4130Sstevel@tonic-gate     dev_info_t *dip,
4140Sstevel@tonic-gate     ddi_info_cmd_t infocmd,
4150Sstevel@tonic-gate     void *arg,
4160Sstevel@tonic-gate     void **result)
4170Sstevel@tonic-gate {
4180Sstevel@tonic-gate 	dev_t dev = (dev_t)arg;
4190Sstevel@tonic-gate 	minor_t	minor = getminor(dev);
4200Sstevel@tonic-gate 	int	instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
4230Sstevel@tonic-gate 	if (mouse8042_debug)
4240Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_getinfo: call\n");
4250Sstevel@tonic-gate #endif
4260Sstevel@tonic-gate 	switch (infocmd) {
4270Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
4280Sstevel@tonic-gate 		if (mouse8042_dip == NULL)
4290Sstevel@tonic-gate 			return (DDI_FAILURE);
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 		*result = (void *)mouse8042_dip;
4320Sstevel@tonic-gate 		break;
4330Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
4340Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
4350Sstevel@tonic-gate 		break;
4360Sstevel@tonic-gate 	default:
4370Sstevel@tonic-gate 		return (DDI_FAILURE);
4380Sstevel@tonic-gate 	}
4390Sstevel@tonic-gate 	return (DDI_SUCCESS);
4400Sstevel@tonic-gate }
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate /*ARGSUSED*/
4430Sstevel@tonic-gate static int
4440Sstevel@tonic-gate mouse8042_open(
4450Sstevel@tonic-gate 	queue_t	*q,
4460Sstevel@tonic-gate 	dev_t	*devp,
4470Sstevel@tonic-gate 	int	flag,
4480Sstevel@tonic-gate 	int	sflag,
4490Sstevel@tonic-gate 	cred_t	*cred_p)
4500Sstevel@tonic-gate {
4510Sstevel@tonic-gate 	struct mouse_state *state;
4520Sstevel@tonic-gate 	minor_t	minor = getminor(*devp);
4530Sstevel@tonic-gate 	int rval;
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	if (mouse8042_dip == NULL)
4560Sstevel@tonic-gate 		return (ENXIO);
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 	state = ddi_get_driver_private(mouse8042_dip);
4590Sstevel@tonic-gate 
4600Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
4610Sstevel@tonic-gate 	if (mouse8042_debug)
4620Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_open:entered\n");
4630Sstevel@tonic-gate #endif
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	if (state->ms_opened) {
4680Sstevel@tonic-gate 		/*
4690Sstevel@tonic-gate 		 * Exit if the same minor node is already open
4700Sstevel@tonic-gate 		 */
4710Sstevel@tonic-gate 		if (state->ms_minor == minor) {
4720Sstevel@tonic-gate 			mutex_exit(&state->ms_mutex);
4730Sstevel@tonic-gate 			return (0);
4740Sstevel@tonic-gate 		}
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 		/*
4770Sstevel@tonic-gate 		 * Check whether it is switch between physical and virtual
4780Sstevel@tonic-gate 		 *
4790Sstevel@tonic-gate 		 * Opening from virtual while the device is being physically
4800Sstevel@tonic-gate 		 * opened by an application should not happen. So we ASSERT
4810Sstevel@tonic-gate 		 * this in DEBUG version, and return error in the non-DEBUG
4820Sstevel@tonic-gate 		 * case.
4830Sstevel@tonic-gate 		 */
4840Sstevel@tonic-gate 		ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 		if (MOUSE8042_INTERNAL_OPEN(minor)) {
4870Sstevel@tonic-gate 			mutex_exit(&state->ms_mutex);
4880Sstevel@tonic-gate 			return (EINVAL);
4890Sstevel@tonic-gate 		}
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 		/*
4920Sstevel@tonic-gate 		 * Opening the physical one while it is being underneath
4930Sstevel@tonic-gate 		 * the virtual one.
4940Sstevel@tonic-gate 		 *
4950Sstevel@tonic-gate 		 * consconfig_unlink is called to unlink this device from
4960Sstevel@tonic-gate 		 * the virtual one, thus the old stream serving for this
4970Sstevel@tonic-gate 		 * device under the virtual one is closed, and then the
4980Sstevel@tonic-gate 		 * lower driver's close routine (here is mouse8042_close)
4990Sstevel@tonic-gate 		 * is also called to accomplish the whole stream close.
5000Sstevel@tonic-gate 		 * Here we have to drop the lock because mouse8042_close
5010Sstevel@tonic-gate 		 * also needs the lock.
5020Sstevel@tonic-gate 		 *
5030Sstevel@tonic-gate 		 * For mouse, the old stream is:
5040Sstevel@tonic-gate 		 *	consms->["pushmod"->]"mouse_vp driver"
5050Sstevel@tonic-gate 		 *
5060Sstevel@tonic-gate 		 * After the consconfig_unlink returns, the old stream is closed
5070Sstevel@tonic-gate 		 * and we grab the lock again to reopen this device as normal.
5080Sstevel@tonic-gate 		 */
5090Sstevel@tonic-gate 		mutex_exit(&state->ms_mutex);
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 		/*
5120Sstevel@tonic-gate 		 * If unlink fails, fail the physical open.
5130Sstevel@tonic-gate 		 */
5140Sstevel@tonic-gate 		if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
5150Sstevel@tonic-gate 		    MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
5160Sstevel@tonic-gate 			return (rval);
5170Sstevel@tonic-gate 		}
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 		mutex_enter(&state->ms_mutex);
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 	q->q_ptr = (caddr_t)state;
5240Sstevel@tonic-gate 	WR(q)->q_ptr = (caddr_t)state;
5250Sstevel@tonic-gate 	state->ms_rqp = q;
5260Sstevel@tonic-gate 	state->ms_wqp = WR(q);
5270Sstevel@tonic-gate 
5280Sstevel@tonic-gate 	qprocson(q);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	state->ms_minor = minor;
5310Sstevel@tonic-gate 	state->ms_opened = B_TRUE;
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
5340Sstevel@tonic-gate 
5350Sstevel@tonic-gate 	return (0);
5360Sstevel@tonic-gate }
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate /*ARGSUSED*/
5400Sstevel@tonic-gate static int
5410Sstevel@tonic-gate mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
5420Sstevel@tonic-gate {
5430Sstevel@tonic-gate 	struct mouse_state *state;
5440Sstevel@tonic-gate 	minor_t	minor;
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	state = (struct mouse_state *)q->q_ptr;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
5490Sstevel@tonic-gate 	if (mouse8042_debug)
5500Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_close:entered\n");
5510Sstevel@tonic-gate #endif
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	qprocsoff(q);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	q->q_ptr = NULL;
5580Sstevel@tonic-gate 	WR(q)->q_ptr = NULL;
5590Sstevel@tonic-gate 	state->ms_rqp = NULL;
5600Sstevel@tonic-gate 	state->ms_wqp = NULL;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	state->ms_opened = B_FALSE;
5630Sstevel@tonic-gate 
5640Sstevel@tonic-gate 	minor = state->ms_minor;
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 	if (!MOUSE8042_INTERNAL_OPEN(minor)) {
5690Sstevel@tonic-gate 		/*
5700Sstevel@tonic-gate 		 * Closing physical PS/2 mouse
5710Sstevel@tonic-gate 		 *
5720Sstevel@tonic-gate 		 * Link it back to virtual mouse, and
5730Sstevel@tonic-gate 		 * mouse8042_open will be called as a result
574822Ssethg 		 * of the consconfig_link call.  Do NOT try
575822Ssethg 		 * this if the mouse is about to be detached!
5760Sstevel@tonic-gate 		 *
5770Sstevel@tonic-gate 		 * If linking back fails, this specific mouse
5780Sstevel@tonic-gate 		 * will not be available underneath the virtual
5790Sstevel@tonic-gate 		 * mouse, and can only be accessed via physical
5800Sstevel@tonic-gate 		 * open.
5810Sstevel@tonic-gate 		 */
5820Sstevel@tonic-gate 		consconfig_link(ddi_driver_major(mouse8042_dip),
5830Sstevel@tonic-gate 		    MOUSE8042_INTERNAL_MINOR(minor));
5840Sstevel@tonic-gate 	}
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 	return (0);
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate static void
5900Sstevel@tonic-gate mouse8042_iocnack(
5910Sstevel@tonic-gate     queue_t *qp,
5920Sstevel@tonic-gate     mblk_t *mp,
5930Sstevel@tonic-gate     struct iocblk *iocp,
5940Sstevel@tonic-gate     int error,
5950Sstevel@tonic-gate     int rval)
5960Sstevel@tonic-gate {
5970Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCNAK;
5980Sstevel@tonic-gate 	iocp->ioc_rval = rval;
5990Sstevel@tonic-gate 	iocp->ioc_error = error;
6000Sstevel@tonic-gate 	qreply(qp, mp);
6010Sstevel@tonic-gate }
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate static int
6040Sstevel@tonic-gate mouse8042_wput(queue_t *q, mblk_t *mp)
6050Sstevel@tonic-gate {
6060Sstevel@tonic-gate 	struct iocblk *iocbp;
6070Sstevel@tonic-gate 	mblk_t *bp;
6080Sstevel@tonic-gate 	mblk_t *next;
6090Sstevel@tonic-gate 	struct mouse_state *state;
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	state = (struct mouse_state *)q->q_ptr;
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
6140Sstevel@tonic-gate 	if (mouse8042_debug)
6150Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_wput:entered\n");
6160Sstevel@tonic-gate #endif
6170Sstevel@tonic-gate 	iocbp = (struct iocblk *)mp->b_rptr;
6180Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
6190Sstevel@tonic-gate 	case M_FLUSH:
6200Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
6210Sstevel@tonic-gate 		if (mouse8042_debug)
6220Sstevel@tonic-gate 			cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n");
6230Sstevel@tonic-gate #endif
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW)
6260Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
6270Sstevel@tonic-gate 		qreply(q, mp);
6280Sstevel@tonic-gate 		break;
6290Sstevel@tonic-gate 	case M_IOCTL:
6300Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
6310Sstevel@tonic-gate 		if (mouse8042_debug)
6320Sstevel@tonic-gate 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n");
6330Sstevel@tonic-gate #endif
6340Sstevel@tonic-gate 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
6350Sstevel@tonic-gate 		break;
6360Sstevel@tonic-gate 	case M_IOCDATA:
6370Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
6380Sstevel@tonic-gate 		if (mouse8042_debug)
6390Sstevel@tonic-gate 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n");
6400Sstevel@tonic-gate #endif
6410Sstevel@tonic-gate 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
6420Sstevel@tonic-gate 		break;
6430Sstevel@tonic-gate 	case M_DATA:
6440Sstevel@tonic-gate 		bp = mp;
6450Sstevel@tonic-gate 		do {
6460Sstevel@tonic-gate 			while (bp->b_rptr < bp->b_wptr) {
6470Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
6480Sstevel@tonic-gate 				if (mouse8042_debug) {
6490Sstevel@tonic-gate 					cmn_err(CE_CONT,
6500Sstevel@tonic-gate 					    "mouse8042:  send %2x\n",
6510Sstevel@tonic-gate 					    *bp->b_rptr);
6520Sstevel@tonic-gate 				}
6530Sstevel@tonic-gate 				if (mouse8042_debug_minimal) {
6540Sstevel@tonic-gate 					cmn_err(CE_CONT, ">a:%2x ",
6550Sstevel@tonic-gate 					    *bp->b_rptr);
6560Sstevel@tonic-gate 				}
6570Sstevel@tonic-gate #endif
6580Sstevel@tonic-gate 				ddi_put8(state->ms_handle,
659*7656SSherry.Moore@Sun.COM 				    state->ms_addr + I8042_INT_OUTPUT_DATA,
660*7656SSherry.Moore@Sun.COM 				    *bp->b_rptr++);
6610Sstevel@tonic-gate 			}
6620Sstevel@tonic-gate 			next = bp->b_cont;
6630Sstevel@tonic-gate 			freeb(bp);
6640Sstevel@tonic-gate 		} while ((bp = next) != NULL);
6650Sstevel@tonic-gate 		break;
6660Sstevel@tonic-gate 	default:
6670Sstevel@tonic-gate 		freemsg(mp);
6680Sstevel@tonic-gate 		break;
6690Sstevel@tonic-gate 	}
6700Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
6710Sstevel@tonic-gate 	if (mouse8042_debug)
6720Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_wput:leaving\n");
6730Sstevel@tonic-gate #endif
6740Sstevel@tonic-gate 	return (0);	/* ignored */
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate static uint_t
6780Sstevel@tonic-gate mouse8042_intr(caddr_t arg)
6790Sstevel@tonic-gate {
6800Sstevel@tonic-gate 	unsigned char    mdata;
6810Sstevel@tonic-gate 	mblk_t *mp;
6820Sstevel@tonic-gate 	struct mouse_state *state = (struct mouse_state *)arg;
6830Sstevel@tonic-gate 	int rc;
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	mutex_enter(&state->ms_mutex);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
6880Sstevel@tonic-gate 	if (mouse8042_debug)
6890Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_intr()\n");
6900Sstevel@tonic-gate #endif
6910Sstevel@tonic-gate 	rc = DDI_INTR_UNCLAIMED;
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	for (;;) {
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 		if (ddi_get8(state->ms_handle,
696*7656SSherry.Moore@Sun.COM 		    state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
6970Sstevel@tonic-gate 			break;
6980Sstevel@tonic-gate 		}
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 		mdata = ddi_get8(state->ms_handle,
701*7656SSherry.Moore@Sun.COM 		    state->ms_addr + I8042_INT_INPUT_DATA);
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate #if	defined(MOUSE8042_DEBUG)
7040Sstevel@tonic-gate 		if (mouse8042_debug)
7050Sstevel@tonic-gate 			cmn_err(CE_CONT, "mouse8042_intr:  got %2x\n", mdata);
7060Sstevel@tonic-gate 		if (mouse8042_debug_minimal)
7070Sstevel@tonic-gate 			cmn_err(CE_CONT, "<A:%2x ", mdata);
7080Sstevel@tonic-gate #endif
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 		rc = DDI_INTR_CLAIMED;
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 		if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
7130Sstevel@tonic-gate 			*mp->b_wptr++ = mdata;
7140Sstevel@tonic-gate 			putnext(state->ms_rqp, mp);
7150Sstevel@tonic-gate 		}
7160Sstevel@tonic-gate 	}
7170Sstevel@tonic-gate #ifdef MOUSE8042_DEBUG
7180Sstevel@tonic-gate 	if (mouse8042_debug)
7190Sstevel@tonic-gate 		cmn_err(CE_CONT, "mouse8042_intr() ok\n");
7200Sstevel@tonic-gate #endif
7210Sstevel@tonic-gate 	mutex_exit(&state->ms_mutex);
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	return (rc);
7240Sstevel@tonic-gate }
725