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
57656SSherry.Moore@Sun.COM * Common Development and Distribution License (the "License").
67656SSherry.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 /*
2610087SSeth.Goldberg@Sun.COM * Copyright 2009 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>
4510087SSeth.Goldberg@Sun.COM #include <sys/strsun.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/ddi.h>
480Sstevel@tonic-gate #include <sys/stat.h>
490Sstevel@tonic-gate #include <sys/cmn_err.h>
500Sstevel@tonic-gate #include <sys/sunddi.h>
510Sstevel@tonic-gate
520Sstevel@tonic-gate #include <sys/promif.h>
530Sstevel@tonic-gate #include <sys/cred.h>
540Sstevel@tonic-gate
550Sstevel@tonic-gate #include <sys/i8042.h>
560Sstevel@tonic-gate #include <sys/note.h>
5710087SSeth.Goldberg@Sun.COM #include <sys/mouse.h>
580Sstevel@tonic-gate
590Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip)
600Sstevel@tonic-gate
610Sstevel@tonic-gate #define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1)
620Sstevel@tonic-gate #define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2)
630Sstevel@tonic-gate #define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1)
640Sstevel@tonic-gate
6510087SSeth.Goldberg@Sun.COM #define MOUSE8042_RESET_TIMEOUT_USECS 500000 /* 500 ms */
6610087SSeth.Goldberg@Sun.COM
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
8810087SSeth.Goldberg@Sun.COM /*
8910087SSeth.Goldberg@Sun.COM * RESET states
9010087SSeth.Goldberg@Sun.COM */
9110087SSeth.Goldberg@Sun.COM typedef enum {
9210087SSeth.Goldberg@Sun.COM MSE_RESET_IDLE, /* No reset in progress */
9310087SSeth.Goldberg@Sun.COM MSE_RESET_PRE, /* Send reset, waiting for ACK */
9410087SSeth.Goldberg@Sun.COM MSE_RESET_ACK, /* Got ACK, waiting for 0xAA */
9510087SSeth.Goldberg@Sun.COM MSE_RESET_AA, /* Got 0xAA, waiting for 0x00 */
9610087SSeth.Goldberg@Sun.COM MSE_RESET_FAILED
9710087SSeth.Goldberg@Sun.COM } mouse8042_reset_state_e;
9810087SSeth.Goldberg@Sun.COM
990Sstevel@tonic-gate struct mouse_state {
1000Sstevel@tonic-gate queue_t *ms_rqp;
1010Sstevel@tonic-gate queue_t *ms_wqp;
1020Sstevel@tonic-gate ddi_iblock_cookie_t ms_iblock_cookie;
1030Sstevel@tonic-gate ddi_acc_handle_t ms_handle;
1040Sstevel@tonic-gate uint8_t *ms_addr;
1050Sstevel@tonic-gate kmutex_t ms_mutex;
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate minor_t ms_minor;
1080Sstevel@tonic-gate boolean_t ms_opened;
10910087SSeth.Goldberg@Sun.COM kmutex_t reset_mutex;
110*10803SSeth.Goldberg@Sun.COM kcondvar_t reset_cv;
11110087SSeth.Goldberg@Sun.COM mouse8042_reset_state_e reset_state;
11210087SSeth.Goldberg@Sun.COM timeout_id_t reset_tid;
11310087SSeth.Goldberg@Sun.COM int ready;
11410087SSeth.Goldberg@Sun.COM mblk_t *reply_mp;
115*10803SSeth.Goldberg@Sun.COM mblk_t *reset_ack_mp;
11610087SSeth.Goldberg@Sun.COM bufcall_id_t bc_id;
1170Sstevel@tonic-gate };
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate static uint_t mouse8042_intr(caddr_t arg);
1200Sstevel@tonic-gate static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
1210Sstevel@tonic-gate cred_t *cred_p);
1220Sstevel@tonic-gate static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
12310087SSeth.Goldberg@Sun.COM static int mouse8042_wsrv(queue_t *qp);
124*10803SSeth.Goldberg@Sun.COM static int mouse8042_wput(queue_t *q, mblk_t *mp);
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
1270Sstevel@tonic-gate void *arg, void **result);
1280Sstevel@tonic-gate static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1290Sstevel@tonic-gate static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate * Streams module info.
1340Sstevel@tonic-gate */
1350Sstevel@tonic-gate #define MODULE_NAME "mouse8042"
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate static struct module_info mouse8042_minfo = {
1380Sstevel@tonic-gate 23, /* Module ID number */
1390Sstevel@tonic-gate MODULE_NAME,
1400Sstevel@tonic-gate 0, INFPSZ, /* minimum & maximum packet sizes */
1410Sstevel@tonic-gate 256, 128 /* hi and low water marks */
1420Sstevel@tonic-gate };
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate static struct qinit mouse8042_rinit = {
1450Sstevel@tonic-gate NULL, /* put */
1460Sstevel@tonic-gate NULL, /* service */
1470Sstevel@tonic-gate mouse8042_open,
1480Sstevel@tonic-gate mouse8042_close,
1490Sstevel@tonic-gate NULL, /* admin */
1500Sstevel@tonic-gate &mouse8042_minfo,
1510Sstevel@tonic-gate NULL /* statistics */
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate static struct qinit mouse8042_winit = {
155*10803SSeth.Goldberg@Sun.COM mouse8042_wput, /* put */
15610087SSeth.Goldberg@Sun.COM mouse8042_wsrv, /* service */
1570Sstevel@tonic-gate NULL, /* open */
1580Sstevel@tonic-gate NULL, /* close */
1590Sstevel@tonic-gate NULL, /* admin */
1600Sstevel@tonic-gate &mouse8042_minfo,
1610Sstevel@tonic-gate NULL /* statistics */
1620Sstevel@tonic-gate };
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate static struct streamtab mouse8042_strinfo = {
1650Sstevel@tonic-gate &mouse8042_rinit,
1660Sstevel@tonic-gate &mouse8042_winit,
1670Sstevel@tonic-gate NULL, /* muxrinit */
1680Sstevel@tonic-gate NULL, /* muxwinit */
1690Sstevel@tonic-gate };
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate /*
1720Sstevel@tonic-gate * Local Function Declarations
1730Sstevel@tonic-gate */
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate static struct cb_ops mouse8042_cb_ops = {
1760Sstevel@tonic-gate nodev, /* open */
1770Sstevel@tonic-gate nodev, /* close */
1780Sstevel@tonic-gate nodev, /* strategy */
1790Sstevel@tonic-gate nodev, /* print */
1800Sstevel@tonic-gate nodev, /* dump */
1810Sstevel@tonic-gate nodev, /* read */
1820Sstevel@tonic-gate nodev, /* write */
1830Sstevel@tonic-gate nodev, /* ioctl */
1840Sstevel@tonic-gate nodev, /* devmap */
1850Sstevel@tonic-gate nodev, /* mmap */
1860Sstevel@tonic-gate nodev, /* segmap */
1870Sstevel@tonic-gate nochpoll, /* poll */
1880Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
1890Sstevel@tonic-gate &mouse8042_strinfo, /* streamtab */
1900Sstevel@tonic-gate D_MP | D_NEW
1910Sstevel@tonic-gate };
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate
1940Sstevel@tonic-gate static struct dev_ops mouse8042_ops = {
1950Sstevel@tonic-gate DEVO_REV, /* devo_rev, */
1960Sstevel@tonic-gate 0, /* refcnt */
1970Sstevel@tonic-gate mouse8042_getinfo, /* getinfo */
1980Sstevel@tonic-gate nulldev, /* identify */
1990Sstevel@tonic-gate nulldev, /* probe */
2000Sstevel@tonic-gate mouse8042_attach, /* attach */
2010Sstevel@tonic-gate mouse8042_detach, /* detach */
2020Sstevel@tonic-gate nodev, /* reset */
2030Sstevel@tonic-gate &mouse8042_cb_ops, /* driver operations */
2047656SSherry.Moore@Sun.COM (struct bus_ops *)0, /* bus operations */
2057656SSherry.Moore@Sun.COM NULL, /* power */
2067656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
2070Sstevel@tonic-gate };
2080Sstevel@tonic-gate
2090Sstevel@tonic-gate /*
2100Sstevel@tonic-gate * This is the loadable module wrapper.
2110Sstevel@tonic-gate */
2120Sstevel@tonic-gate #include <sys/modctl.h>
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate extern struct mod_ops mod_driverops;
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate * Module linkage information for the kernel.
2180Sstevel@tonic-gate */
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate static struct modldrv modldrv = {
2210Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
2227656SSherry.Moore@Sun.COM "PS/2 Mouse",
2230Sstevel@tonic-gate &mouse8042_ops, /* driver ops */
2240Sstevel@tonic-gate };
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate static struct modlinkage modlinkage = {
2270Sstevel@tonic-gate MODREV_1,
2280Sstevel@tonic-gate (void *)&modldrv,
2290Sstevel@tonic-gate NULL
2300Sstevel@tonic-gate };
2310Sstevel@tonic-gate
2320Sstevel@tonic-gate /*
2330Sstevel@tonic-gate * This is the driver initialization routine.
2340Sstevel@tonic-gate */
2350Sstevel@tonic-gate int
_init()2360Sstevel@tonic-gate _init()
2370Sstevel@tonic-gate {
2380Sstevel@tonic-gate int rv;
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate rv = mod_install(&modlinkage);
2410Sstevel@tonic-gate return (rv);
2420Sstevel@tonic-gate }
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate int
_fini(void)2460Sstevel@tonic-gate _fini(void)
2470Sstevel@tonic-gate {
2480Sstevel@tonic-gate return (mod_remove(&modlinkage));
2490Sstevel@tonic-gate }
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2530Sstevel@tonic-gate _info(struct modinfo *modinfop)
2540Sstevel@tonic-gate {
2550Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
2560Sstevel@tonic-gate }
2570Sstevel@tonic-gate
2580Sstevel@tonic-gate static int
mouse8042_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2590Sstevel@tonic-gate mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2600Sstevel@tonic-gate {
2610Sstevel@tonic-gate struct mouse_state *state;
262822Ssethg mblk_t *mp;
2630Sstevel@tonic-gate int instance = ddi_get_instance(dip);
2640Sstevel@tonic-gate static ddi_device_acc_attr_t attr = {
2650Sstevel@tonic-gate DDI_DEVICE_ATTR_V0,
2660Sstevel@tonic-gate DDI_NEVERSWAP_ACC,
2670Sstevel@tonic-gate DDI_STRICTORDER_ACC,
2680Sstevel@tonic-gate };
2690Sstevel@tonic-gate int rc;
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate
272822Ssethg if (cmd == DDI_RESUME) {
273822Ssethg state = (struct mouse_state *)ddi_get_driver_private(dip);
274822Ssethg
27510087SSeth.Goldberg@Sun.COM /* Ready to handle inbound data from mouse8042_intr */
27610087SSeth.Goldberg@Sun.COM state->ready = 1;
27710087SSeth.Goldberg@Sun.COM
278822Ssethg /*
279822Ssethg * Send a 0xaa 0x00 upstream.
280822Ssethg * This causes the vuid module to reset the mouse.
281822Ssethg */
282822Ssethg if (state->ms_rqp != NULL) {
283822Ssethg if (mp = allocb(1, BPRI_MED)) {
284822Ssethg *mp->b_wptr++ = 0xaa;
285822Ssethg putnext(state->ms_rqp, mp);
286822Ssethg }
287822Ssethg if (mp = allocb(1, BPRI_MED)) {
288822Ssethg *mp->b_wptr++ = 0x0;
289822Ssethg putnext(state->ms_rqp, mp);
290822Ssethg }
291822Ssethg }
292822Ssethg return (DDI_SUCCESS);
293822Ssethg }
294822Ssethg
2950Sstevel@tonic-gate if (cmd != DDI_ATTACH)
2960Sstevel@tonic-gate return (DDI_FAILURE);
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate if (mouse8042_dip != NULL)
2990Sstevel@tonic-gate return (DDI_FAILURE);
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate /* allocate and initialize state structure */
3020Sstevel@tonic-gate state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
3030Sstevel@tonic-gate state->ms_opened = B_FALSE;
30410087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE;
30510087SSeth.Goldberg@Sun.COM state->reset_tid = 0;
30610087SSeth.Goldberg@Sun.COM state->bc_id = 0;
3070Sstevel@tonic-gate ddi_set_driver_private(dip, state);
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate /*
3100Sstevel@tonic-gate * In order to support virtual keyboard/mouse, we should distinguish
3110Sstevel@tonic-gate * between internal virtual open and external physical open.
3120Sstevel@tonic-gate *
3130Sstevel@tonic-gate * When the physical devices are opened by application, they will
3140Sstevel@tonic-gate * be unlinked from the virtual device and their data stream will
3150Sstevel@tonic-gate * not be sent to the virtual device. When the opened physical
3160Sstevel@tonic-gate * devices are closed, they will be relinked to the virtual devices.
3170Sstevel@tonic-gate *
3180Sstevel@tonic-gate * All these automatic switch between virtual and physical are
3190Sstevel@tonic-gate * transparent.
3200Sstevel@tonic-gate *
3210Sstevel@tonic-gate * So we change minor node numbering scheme to be:
3220Sstevel@tonic-gate * external node minor num == instance * 2
3230Sstevel@tonic-gate * internal node minor num == instance * 2 + 1
3240Sstevel@tonic-gate */
325822Ssethg rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
3260Sstevel@tonic-gate DDI_NT_MOUSE, NULL);
3270Sstevel@tonic-gate if (rc != DDI_SUCCESS) {
3280Sstevel@tonic-gate goto fail_1;
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
3327656SSherry.Moore@Sun.COM instance * 2 + 1) != DDI_SUCCESS) {
3330Sstevel@tonic-gate goto fail_2;
3340Sstevel@tonic-gate }
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
3377656SSherry.Moore@Sun.COM (offset_t)0, (offset_t)0, &attr, &state->ms_handle);
3380Sstevel@tonic-gate if (rc != DDI_SUCCESS) {
3390Sstevel@tonic-gate goto fail_2;
3400Sstevel@tonic-gate }
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
3430Sstevel@tonic-gate if (rc != DDI_SUCCESS) {
3440Sstevel@tonic-gate goto fail_3;
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate
3470Sstevel@tonic-gate mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
3480Sstevel@tonic-gate state->ms_iblock_cookie);
34910087SSeth.Goldberg@Sun.COM mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER,
35010087SSeth.Goldberg@Sun.COM state->ms_iblock_cookie);
351*10803SSeth.Goldberg@Sun.COM cv_init(&state->reset_cv, NULL, CV_DRIVER, NULL);
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate rc = ddi_add_intr(dip, 0,
3547656SSherry.Moore@Sun.COM (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
3557656SSherry.Moore@Sun.COM mouse8042_intr, (caddr_t)state);
3560Sstevel@tonic-gate if (rc != DDI_SUCCESS) {
3570Sstevel@tonic-gate goto fail_3;
3580Sstevel@tonic-gate }
3590Sstevel@tonic-gate
3600Sstevel@tonic-gate mouse8042_dip = dip;
3610Sstevel@tonic-gate
36210087SSeth.Goldberg@Sun.COM /* Ready to handle inbound data from mouse8042_intr */
36310087SSeth.Goldberg@Sun.COM state->ready = 1;
36410087SSeth.Goldberg@Sun.COM
3650Sstevel@tonic-gate /* Now that we're attached, announce our presence to the world. */
3660Sstevel@tonic-gate ddi_report_dev(dip);
3670Sstevel@tonic-gate return (DDI_SUCCESS);
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate fail_3:
3700Sstevel@tonic-gate ddi_regs_map_free(&state->ms_handle);
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate fail_2:
3730Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
3740Sstevel@tonic-gate
3750Sstevel@tonic-gate fail_1:
3760Sstevel@tonic-gate kmem_free(state, sizeof (struct mouse_state));
3770Sstevel@tonic-gate return (rc);
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate /*ARGSUSED*/
3810Sstevel@tonic-gate static int
mouse8042_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3820Sstevel@tonic-gate mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3830Sstevel@tonic-gate {
3840Sstevel@tonic-gate struct mouse_state *state;
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate state = ddi_get_driver_private(dip);
3870Sstevel@tonic-gate
3880Sstevel@tonic-gate switch (cmd) {
389822Ssethg case DDI_SUSPEND:
39010087SSeth.Goldberg@Sun.COM /* Ignore all data from mouse8042_intr until we fully resume */
39110087SSeth.Goldberg@Sun.COM state->ready = 0;
392822Ssethg return (DDI_SUCCESS);
3930Sstevel@tonic-gate
3940Sstevel@tonic-gate case DDI_DETACH:
3950Sstevel@tonic-gate ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
3960Sstevel@tonic-gate mouse8042_dip = NULL;
397*10803SSeth.Goldberg@Sun.COM cv_destroy(&state->reset_cv);
39810087SSeth.Goldberg@Sun.COM mutex_destroy(&state->reset_mutex);
3990Sstevel@tonic-gate mutex_destroy(&state->ms_mutex);
4000Sstevel@tonic-gate ddi_prop_remove_all(dip);
4010Sstevel@tonic-gate ddi_regs_map_free(&state->ms_handle);
4020Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL);
4030Sstevel@tonic-gate kmem_free(state, sizeof (struct mouse_state));
4040Sstevel@tonic-gate return (DDI_SUCCESS);
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate default:
4070Sstevel@tonic-gate return (DDI_FAILURE);
4080Sstevel@tonic-gate }
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate /* ARGSUSED */
4130Sstevel@tonic-gate static int
mouse8042_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)4140Sstevel@tonic-gate mouse8042_getinfo(
4150Sstevel@tonic-gate dev_info_t *dip,
4160Sstevel@tonic-gate ddi_info_cmd_t infocmd,
4170Sstevel@tonic-gate void *arg,
4180Sstevel@tonic-gate void **result)
4190Sstevel@tonic-gate {
4200Sstevel@tonic-gate dev_t dev = (dev_t)arg;
4210Sstevel@tonic-gate minor_t minor = getminor(dev);
4220Sstevel@tonic-gate int instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
4230Sstevel@tonic-gate
4240Sstevel@tonic-gate switch (infocmd) {
4250Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
4260Sstevel@tonic-gate if (mouse8042_dip == NULL)
4270Sstevel@tonic-gate return (DDI_FAILURE);
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate *result = (void *)mouse8042_dip;
4300Sstevel@tonic-gate break;
4310Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
4320Sstevel@tonic-gate *result = (void *)(uintptr_t)instance;
4330Sstevel@tonic-gate break;
4340Sstevel@tonic-gate default:
4350Sstevel@tonic-gate return (DDI_FAILURE);
4360Sstevel@tonic-gate }
4370Sstevel@tonic-gate return (DDI_SUCCESS);
4380Sstevel@tonic-gate }
4390Sstevel@tonic-gate
4400Sstevel@tonic-gate /*ARGSUSED*/
4410Sstevel@tonic-gate static int
mouse8042_open(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * cred_p)4420Sstevel@tonic-gate mouse8042_open(
4430Sstevel@tonic-gate queue_t *q,
4440Sstevel@tonic-gate dev_t *devp,
4450Sstevel@tonic-gate int flag,
4460Sstevel@tonic-gate int sflag,
4470Sstevel@tonic-gate cred_t *cred_p)
4480Sstevel@tonic-gate {
4490Sstevel@tonic-gate struct mouse_state *state;
4500Sstevel@tonic-gate minor_t minor = getminor(*devp);
4510Sstevel@tonic-gate int rval;
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate if (mouse8042_dip == NULL)
4540Sstevel@tonic-gate return (ENXIO);
4550Sstevel@tonic-gate
4560Sstevel@tonic-gate state = ddi_get_driver_private(mouse8042_dip);
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate mutex_enter(&state->ms_mutex);
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate if (state->ms_opened) {
4610Sstevel@tonic-gate /*
4620Sstevel@tonic-gate * Exit if the same minor node is already open
4630Sstevel@tonic-gate */
4640Sstevel@tonic-gate if (state->ms_minor == minor) {
4650Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
4660Sstevel@tonic-gate return (0);
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate /*
4700Sstevel@tonic-gate * Check whether it is switch between physical and virtual
4710Sstevel@tonic-gate *
4720Sstevel@tonic-gate * Opening from virtual while the device is being physically
4730Sstevel@tonic-gate * opened by an application should not happen. So we ASSERT
4740Sstevel@tonic-gate * this in DEBUG version, and return error in the non-DEBUG
4750Sstevel@tonic-gate * case.
4760Sstevel@tonic-gate */
4770Sstevel@tonic-gate ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
4780Sstevel@tonic-gate
4790Sstevel@tonic-gate if (MOUSE8042_INTERNAL_OPEN(minor)) {
4800Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
4810Sstevel@tonic-gate return (EINVAL);
4820Sstevel@tonic-gate }
4830Sstevel@tonic-gate
4840Sstevel@tonic-gate /*
4850Sstevel@tonic-gate * Opening the physical one while it is being underneath
4860Sstevel@tonic-gate * the virtual one.
4870Sstevel@tonic-gate *
4880Sstevel@tonic-gate * consconfig_unlink is called to unlink this device from
4890Sstevel@tonic-gate * the virtual one, thus the old stream serving for this
4900Sstevel@tonic-gate * device under the virtual one is closed, and then the
4910Sstevel@tonic-gate * lower driver's close routine (here is mouse8042_close)
4920Sstevel@tonic-gate * is also called to accomplish the whole stream close.
4930Sstevel@tonic-gate * Here we have to drop the lock because mouse8042_close
4940Sstevel@tonic-gate * also needs the lock.
4950Sstevel@tonic-gate *
4960Sstevel@tonic-gate * For mouse, the old stream is:
4970Sstevel@tonic-gate * consms->["pushmod"->]"mouse_vp driver"
4980Sstevel@tonic-gate *
4990Sstevel@tonic-gate * After the consconfig_unlink returns, the old stream is closed
5000Sstevel@tonic-gate * and we grab the lock again to reopen this device as normal.
5010Sstevel@tonic-gate */
5020Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
5030Sstevel@tonic-gate
5040Sstevel@tonic-gate /*
5050Sstevel@tonic-gate * If unlink fails, fail the physical open.
5060Sstevel@tonic-gate */
5070Sstevel@tonic-gate if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
5080Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
5090Sstevel@tonic-gate return (rval);
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate
5120Sstevel@tonic-gate mutex_enter(&state->ms_mutex);
5130Sstevel@tonic-gate }
5140Sstevel@tonic-gate
5150Sstevel@tonic-gate
5160Sstevel@tonic-gate q->q_ptr = (caddr_t)state;
5170Sstevel@tonic-gate WR(q)->q_ptr = (caddr_t)state;
5180Sstevel@tonic-gate state->ms_rqp = q;
5190Sstevel@tonic-gate state->ms_wqp = WR(q);
5200Sstevel@tonic-gate
5210Sstevel@tonic-gate qprocson(q);
5220Sstevel@tonic-gate
5230Sstevel@tonic-gate state->ms_minor = minor;
5240Sstevel@tonic-gate state->ms_opened = B_TRUE;
5250Sstevel@tonic-gate
5260Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
5270Sstevel@tonic-gate
5280Sstevel@tonic-gate return (0);
5290Sstevel@tonic-gate }
5300Sstevel@tonic-gate
5310Sstevel@tonic-gate
5320Sstevel@tonic-gate /*ARGSUSED*/
5330Sstevel@tonic-gate static int
mouse8042_close(queue_t * q,int flag,cred_t * cred_p)5340Sstevel@tonic-gate mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate struct mouse_state *state;
5370Sstevel@tonic-gate minor_t minor;
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate state = (struct mouse_state *)q->q_ptr;
5400Sstevel@tonic-gate
541*10803SSeth.Goldberg@Sun.COM /*
542*10803SSeth.Goldberg@Sun.COM * Disable queue processing now, so that another reset cannot get in
543*10803SSeth.Goldberg@Sun.COM * after we wait for the current reset (if any) to complete.
544*10803SSeth.Goldberg@Sun.COM */
545*10803SSeth.Goldberg@Sun.COM qprocsoff(q);
5460Sstevel@tonic-gate
547*10803SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
548*10803SSeth.Goldberg@Sun.COM while (state->reset_state != MSE_RESET_IDLE) {
549*10803SSeth.Goldberg@Sun.COM /*
550*10803SSeth.Goldberg@Sun.COM * Waiting for the previous reset to finish is
551*10803SSeth.Goldberg@Sun.COM * non-interruptible. Some upper-level clients
552*10803SSeth.Goldberg@Sun.COM * cannot deal with EINTR and will not close the
553*10803SSeth.Goldberg@Sun.COM * STREAM properly, resulting in failure to reopen it
554*10803SSeth.Goldberg@Sun.COM * within the same process.
555*10803SSeth.Goldberg@Sun.COM */
556*10803SSeth.Goldberg@Sun.COM cv_wait(&state->reset_cv, &state->reset_mutex);
557*10803SSeth.Goldberg@Sun.COM }
5580Sstevel@tonic-gate
55910087SSeth.Goldberg@Sun.COM if (state->reset_tid != 0) {
56010087SSeth.Goldberg@Sun.COM (void) quntimeout(q, state->reset_tid);
56110087SSeth.Goldberg@Sun.COM state->reset_tid = 0;
56210087SSeth.Goldberg@Sun.COM }
563*10803SSeth.Goldberg@Sun.COM
564*10803SSeth.Goldberg@Sun.COM if (state->reply_mp != NULL) {
565*10803SSeth.Goldberg@Sun.COM freemsg(state->reply_mp);
566*10803SSeth.Goldberg@Sun.COM state->reply_mp = NULL;
567*10803SSeth.Goldberg@Sun.COM }
568*10803SSeth.Goldberg@Sun.COM
569*10803SSeth.Goldberg@Sun.COM if (state->reset_ack_mp != NULL) {
570*10803SSeth.Goldberg@Sun.COM freemsg(state->reset_ack_mp);
571*10803SSeth.Goldberg@Sun.COM state->reset_ack_mp = NULL;
572*10803SSeth.Goldberg@Sun.COM }
573*10803SSeth.Goldberg@Sun.COM
574*10803SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
575*10803SSeth.Goldberg@Sun.COM
576*10803SSeth.Goldberg@Sun.COM mutex_enter(&state->ms_mutex);
577*10803SSeth.Goldberg@Sun.COM
57810087SSeth.Goldberg@Sun.COM if (state->bc_id != 0) {
57910087SSeth.Goldberg@Sun.COM (void) qunbufcall(q, state->bc_id);
58010087SSeth.Goldberg@Sun.COM state->bc_id = 0;
58110087SSeth.Goldberg@Sun.COM }
58210087SSeth.Goldberg@Sun.COM
5830Sstevel@tonic-gate q->q_ptr = NULL;
5840Sstevel@tonic-gate WR(q)->q_ptr = NULL;
5850Sstevel@tonic-gate state->ms_rqp = NULL;
5860Sstevel@tonic-gate state->ms_wqp = NULL;
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate state->ms_opened = B_FALSE;
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate minor = state->ms_minor;
5910Sstevel@tonic-gate
5920Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
5930Sstevel@tonic-gate
5940Sstevel@tonic-gate if (!MOUSE8042_INTERNAL_OPEN(minor)) {
5950Sstevel@tonic-gate /*
5960Sstevel@tonic-gate * Closing physical PS/2 mouse
5970Sstevel@tonic-gate *
5980Sstevel@tonic-gate * Link it back to virtual mouse, and
5990Sstevel@tonic-gate * mouse8042_open will be called as a result
600822Ssethg * of the consconfig_link call. Do NOT try
601822Ssethg * this if the mouse is about to be detached!
6020Sstevel@tonic-gate *
6030Sstevel@tonic-gate * If linking back fails, this specific mouse
6040Sstevel@tonic-gate * will not be available underneath the virtual
6050Sstevel@tonic-gate * mouse, and can only be accessed via physical
6060Sstevel@tonic-gate * open.
6070Sstevel@tonic-gate */
6080Sstevel@tonic-gate consconfig_link(ddi_driver_major(mouse8042_dip),
6090Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor));
6100Sstevel@tonic-gate }
6110Sstevel@tonic-gate
6120Sstevel@tonic-gate return (0);
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate
6150Sstevel@tonic-gate static void
mouse8042_iocnack(queue_t * qp,mblk_t * mp,struct iocblk * iocp,int error,int rval)6160Sstevel@tonic-gate mouse8042_iocnack(
6170Sstevel@tonic-gate queue_t *qp,
6180Sstevel@tonic-gate mblk_t *mp,
6190Sstevel@tonic-gate struct iocblk *iocp,
6200Sstevel@tonic-gate int error,
6210Sstevel@tonic-gate int rval)
6220Sstevel@tonic-gate {
6230Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK;
6240Sstevel@tonic-gate iocp->ioc_rval = rval;
6250Sstevel@tonic-gate iocp->ioc_error = error;
6260Sstevel@tonic-gate qreply(qp, mp);
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate
62910087SSeth.Goldberg@Sun.COM static void
mouse8042_reset_timeout(void * argp)63010087SSeth.Goldberg@Sun.COM mouse8042_reset_timeout(void *argp)
63110087SSeth.Goldberg@Sun.COM {
63210087SSeth.Goldberg@Sun.COM struct mouse_state *state = (struct mouse_state *)argp;
63310087SSeth.Goldberg@Sun.COM mblk_t *mp;
63410087SSeth.Goldberg@Sun.COM
63510087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
63610087SSeth.Goldberg@Sun.COM
63710087SSeth.Goldberg@Sun.COM /*
63810087SSeth.Goldberg@Sun.COM * If the interrupt handler hasn't completed the reset handling
63910087SSeth.Goldberg@Sun.COM * (reset_state would be IDLE or FAILED in that case), then
64010087SSeth.Goldberg@Sun.COM * drop the 8042 lock, and send a faked retry reply upstream,
64110087SSeth.Goldberg@Sun.COM * then enable the queue for further message processing.
64210087SSeth.Goldberg@Sun.COM */
64310087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE &&
64410087SSeth.Goldberg@Sun.COM state->reset_state != MSE_RESET_FAILED) {
64510087SSeth.Goldberg@Sun.COM
64610087SSeth.Goldberg@Sun.COM state->reset_tid = 0;
64710087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE;
648*10803SSeth.Goldberg@Sun.COM cv_signal(&state->reset_cv);
64910087SSeth.Goldberg@Sun.COM
65010087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle, state->ms_addr +
65110087SSeth.Goldberg@Sun.COM I8042_UNLOCK);
65210087SSeth.Goldberg@Sun.COM
65310087SSeth.Goldberg@Sun.COM mp = state->reply_mp;
65410087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSERESEND;
65510087SSeth.Goldberg@Sun.COM state->reply_mp = NULL;
65610087SSeth.Goldberg@Sun.COM
65710087SSeth.Goldberg@Sun.COM if (state->ms_rqp != NULL)
65810087SSeth.Goldberg@Sun.COM putnext(state->ms_rqp, mp);
65910087SSeth.Goldberg@Sun.COM else
66010087SSeth.Goldberg@Sun.COM freemsg(mp);
66110087SSeth.Goldberg@Sun.COM
66210087SSeth.Goldberg@Sun.COM ASSERT(state->ms_wqp != NULL);
66310087SSeth.Goldberg@Sun.COM
66410087SSeth.Goldberg@Sun.COM enableok(state->ms_wqp);
66510087SSeth.Goldberg@Sun.COM qenable(state->ms_wqp);
66610087SSeth.Goldberg@Sun.COM }
66710087SSeth.Goldberg@Sun.COM
66810087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
66910087SSeth.Goldberg@Sun.COM }
67010087SSeth.Goldberg@Sun.COM
67110087SSeth.Goldberg@Sun.COM /*
67210087SSeth.Goldberg@Sun.COM * Returns 1 if the caller should put the message (bp) back on the queue
67310087SSeth.Goldberg@Sun.COM */
6740Sstevel@tonic-gate static int
mouse8042_initiate_reset(queue_t * q,mblk_t * mp,struct mouse_state * state)675*10803SSeth.Goldberg@Sun.COM mouse8042_initiate_reset(queue_t *q, mblk_t *mp, struct mouse_state *state)
6760Sstevel@tonic-gate {
67710087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
67810087SSeth.Goldberg@Sun.COM /*
67910087SSeth.Goldberg@Sun.COM * If we're in the middle of a reset, put the message back on the queue
68010087SSeth.Goldberg@Sun.COM * for processing later.
68110087SSeth.Goldberg@Sun.COM */
68210087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE) {
68310087SSeth.Goldberg@Sun.COM /*
68410087SSeth.Goldberg@Sun.COM * We noenable the queue again here in case it was backenabled
68510087SSeth.Goldberg@Sun.COM * by an upper-level module.
68610087SSeth.Goldberg@Sun.COM */
68710087SSeth.Goldberg@Sun.COM noenable(q);
68810087SSeth.Goldberg@Sun.COM
68910087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
69010087SSeth.Goldberg@Sun.COM return (1);
69110087SSeth.Goldberg@Sun.COM }
69210087SSeth.Goldberg@Sun.COM
69310087SSeth.Goldberg@Sun.COM /*
69410087SSeth.Goldberg@Sun.COM * Drop the reset state lock before allocating the response message and
69510087SSeth.Goldberg@Sun.COM * grabbing the 8042 exclusive-access lock (since those operations
69610087SSeth.Goldberg@Sun.COM * may take an extended period of time to complete).
69710087SSeth.Goldberg@Sun.COM */
69810087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
69910087SSeth.Goldberg@Sun.COM
700*10803SSeth.Goldberg@Sun.COM if (state->reply_mp == NULL)
701*10803SSeth.Goldberg@Sun.COM state->reply_mp = allocb(2, BPRI_MED);
702*10803SSeth.Goldberg@Sun.COM if (state->reset_ack_mp == NULL)
703*10803SSeth.Goldberg@Sun.COM state->reset_ack_mp = allocb(1, BPRI_MED);
704*10803SSeth.Goldberg@Sun.COM
705*10803SSeth.Goldberg@Sun.COM if (state->reply_mp == NULL || state->reset_ack_mp == NULL) {
70610087SSeth.Goldberg@Sun.COM /*
70710087SSeth.Goldberg@Sun.COM * Allocation failed -- set up a bufcall to enable the queue
70810087SSeth.Goldberg@Sun.COM * whenever there is enough memory to allocate the response
70910087SSeth.Goldberg@Sun.COM * message.
71010087SSeth.Goldberg@Sun.COM */
711*10803SSeth.Goldberg@Sun.COM state->bc_id = qbufcall(q, (state->reply_mp == NULL) ? 2 : 1,
712*10803SSeth.Goldberg@Sun.COM BPRI_MED, (void (*)(void *))qenable, q);
71310087SSeth.Goldberg@Sun.COM
71410087SSeth.Goldberg@Sun.COM if (state->bc_id == 0) {
71510087SSeth.Goldberg@Sun.COM /*
71610087SSeth.Goldberg@Sun.COM * If the qbufcall failed, we cannot proceed, so use the
71710087SSeth.Goldberg@Sun.COM * message we were sent to respond with an error.
71810087SSeth.Goldberg@Sun.COM */
71910087SSeth.Goldberg@Sun.COM *mp->b_rptr = MSEERROR;
72010087SSeth.Goldberg@Sun.COM mp->b_wptr = mp->b_rptr + 1;
72110087SSeth.Goldberg@Sun.COM qreply(q, mp);
72210087SSeth.Goldberg@Sun.COM return (0);
72310087SSeth.Goldberg@Sun.COM }
72410087SSeth.Goldberg@Sun.COM
72510087SSeth.Goldberg@Sun.COM return (1);
726*10803SSeth.Goldberg@Sun.COM } else {
727*10803SSeth.Goldberg@Sun.COM /* Bufcall completed successfully (or wasn't needed) */
728*10803SSeth.Goldberg@Sun.COM state->bc_id = 0;
72910087SSeth.Goldberg@Sun.COM }
73010087SSeth.Goldberg@Sun.COM
73110087SSeth.Goldberg@Sun.COM /*
73210087SSeth.Goldberg@Sun.COM * Gain exclusive access to the 8042 for the duration of the reset.
73310087SSeth.Goldberg@Sun.COM * The unlock will occur when the reset has either completed or timed
73410087SSeth.Goldberg@Sun.COM * out.
73510087SSeth.Goldberg@Sun.COM */
73610087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle,
73710087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_LOCK);
73810087SSeth.Goldberg@Sun.COM
73910087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
74010087SSeth.Goldberg@Sun.COM
74110087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_PRE;
74210087SSeth.Goldberg@Sun.COM noenable(q);
74310087SSeth.Goldberg@Sun.COM
74410087SSeth.Goldberg@Sun.COM state->reset_tid = qtimeout(q,
74510087SSeth.Goldberg@Sun.COM mouse8042_reset_timeout,
74610087SSeth.Goldberg@Sun.COM state,
74710087SSeth.Goldberg@Sun.COM drv_usectohz(
74810087SSeth.Goldberg@Sun.COM MOUSE8042_RESET_TIMEOUT_USECS));
74910087SSeth.Goldberg@Sun.COM
75010087SSeth.Goldberg@Sun.COM ddi_put8(state->ms_handle,
75110087SSeth.Goldberg@Sun.COM state->ms_addr +
75210087SSeth.Goldberg@Sun.COM I8042_INT_OUTPUT_DATA, MSERESET);
75310087SSeth.Goldberg@Sun.COM
75410087SSeth.Goldberg@Sun.COM mp->b_rptr++;
75510087SSeth.Goldberg@Sun.COM
75610087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
75710087SSeth.Goldberg@Sun.COM return (1);
75810087SSeth.Goldberg@Sun.COM }
75910087SSeth.Goldberg@Sun.COM
76010087SSeth.Goldberg@Sun.COM /*
76110087SSeth.Goldberg@Sun.COM * Returns 1 if the caller should stop processing messages
76210087SSeth.Goldberg@Sun.COM */
76310087SSeth.Goldberg@Sun.COM static int
mouse8042_process_data_msg(queue_t * q,mblk_t * mp,struct mouse_state * state)76410087SSeth.Goldberg@Sun.COM mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
76510087SSeth.Goldberg@Sun.COM {
7660Sstevel@tonic-gate mblk_t *bp;
7670Sstevel@tonic-gate mblk_t *next;
76810087SSeth.Goldberg@Sun.COM
76910087SSeth.Goldberg@Sun.COM bp = mp;
77010087SSeth.Goldberg@Sun.COM do {
77110087SSeth.Goldberg@Sun.COM while (bp->b_rptr < bp->b_wptr) {
77210087SSeth.Goldberg@Sun.COM /*
77310087SSeth.Goldberg@Sun.COM * Detect an attempt to reset the mouse. Lock out any
77410087SSeth.Goldberg@Sun.COM * further mouse writes until the reset has completed.
77510087SSeth.Goldberg@Sun.COM */
77610087SSeth.Goldberg@Sun.COM if (*bp->b_rptr == MSERESET) {
7770Sstevel@tonic-gate
77810087SSeth.Goldberg@Sun.COM /*
77910087SSeth.Goldberg@Sun.COM * If we couldn't allocate memory and we
78010087SSeth.Goldberg@Sun.COM * we couldn't register a bufcall,
781*10803SSeth.Goldberg@Sun.COM * mouse8042_initiate_reset returns 0 and
78210087SSeth.Goldberg@Sun.COM * has already used the message to send an
78310087SSeth.Goldberg@Sun.COM * error reply back upstream, so there is no
78410087SSeth.Goldberg@Sun.COM * need to deallocate or put this message back
78510087SSeth.Goldberg@Sun.COM * on the queue.
78610087SSeth.Goldberg@Sun.COM */
787*10803SSeth.Goldberg@Sun.COM if (mouse8042_initiate_reset(q, bp, state) == 0)
78810087SSeth.Goldberg@Sun.COM return (1);
7890Sstevel@tonic-gate
79010087SSeth.Goldberg@Sun.COM /*
79110087SSeth.Goldberg@Sun.COM * If there's no data remaining in this block,
79210087SSeth.Goldberg@Sun.COM * free this block and put the following blocks
79310087SSeth.Goldberg@Sun.COM * of this message back on the queue. If putting
79410087SSeth.Goldberg@Sun.COM * the rest of the message back on the queue
79510087SSeth.Goldberg@Sun.COM * fails, free the the message.
79610087SSeth.Goldberg@Sun.COM */
79710087SSeth.Goldberg@Sun.COM if (MBLKL(bp) == 0) {
79810087SSeth.Goldberg@Sun.COM next = bp->b_cont;
79910087SSeth.Goldberg@Sun.COM freeb(bp);
80010087SSeth.Goldberg@Sun.COM bp = next;
80110087SSeth.Goldberg@Sun.COM }
80210087SSeth.Goldberg@Sun.COM if (bp != NULL) {
80310087SSeth.Goldberg@Sun.COM if (!putbq(q, bp))
80410087SSeth.Goldberg@Sun.COM freemsg(bp);
80510087SSeth.Goldberg@Sun.COM }
80610087SSeth.Goldberg@Sun.COM
80710087SSeth.Goldberg@Sun.COM return (1);
80810087SSeth.Goldberg@Sun.COM
80910087SSeth.Goldberg@Sun.COM }
81010087SSeth.Goldberg@Sun.COM ddi_put8(state->ms_handle,
81110087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_INT_OUTPUT_DATA,
81210087SSeth.Goldberg@Sun.COM *bp->b_rptr++);
81310087SSeth.Goldberg@Sun.COM }
81410087SSeth.Goldberg@Sun.COM next = bp->b_cont;
81510087SSeth.Goldberg@Sun.COM freeb(bp);
81610087SSeth.Goldberg@Sun.COM } while ((bp = next) != NULL);
81710087SSeth.Goldberg@Sun.COM
81810087SSeth.Goldberg@Sun.COM return (0);
81910087SSeth.Goldberg@Sun.COM }
82010087SSeth.Goldberg@Sun.COM
82110087SSeth.Goldberg@Sun.COM static int
mouse8042_process_msg(queue_t * q,mblk_t * mp,struct mouse_state * state)82210087SSeth.Goldberg@Sun.COM mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
82310087SSeth.Goldberg@Sun.COM {
82410087SSeth.Goldberg@Sun.COM struct iocblk *iocbp;
82510087SSeth.Goldberg@Sun.COM int rv = 0;
82610087SSeth.Goldberg@Sun.COM
8270Sstevel@tonic-gate iocbp = (struct iocblk *)mp->b_rptr;
82810087SSeth.Goldberg@Sun.COM
8290Sstevel@tonic-gate switch (mp->b_datap->db_type) {
8300Sstevel@tonic-gate case M_FLUSH:
83110087SSeth.Goldberg@Sun.COM if (*mp->b_rptr & FLUSHW) {
8320Sstevel@tonic-gate flushq(q, FLUSHDATA);
83310087SSeth.Goldberg@Sun.COM *mp->b_rptr &= ~FLUSHW;
83410087SSeth.Goldberg@Sun.COM }
83510087SSeth.Goldberg@Sun.COM if (*mp->b_rptr & FLUSHR) {
83610087SSeth.Goldberg@Sun.COM qreply(q, mp);
83710087SSeth.Goldberg@Sun.COM } else
83810087SSeth.Goldberg@Sun.COM freemsg(mp);
8390Sstevel@tonic-gate break;
8400Sstevel@tonic-gate case M_IOCTL:
8410Sstevel@tonic-gate mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
8420Sstevel@tonic-gate break;
8430Sstevel@tonic-gate case M_IOCDATA:
8440Sstevel@tonic-gate mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
8450Sstevel@tonic-gate break;
8460Sstevel@tonic-gate case M_DATA:
84710087SSeth.Goldberg@Sun.COM rv = mouse8042_process_data_msg(q, mp, state);
8480Sstevel@tonic-gate break;
8490Sstevel@tonic-gate default:
8500Sstevel@tonic-gate freemsg(mp);
8510Sstevel@tonic-gate break;
8520Sstevel@tonic-gate }
85310087SSeth.Goldberg@Sun.COM
85410087SSeth.Goldberg@Sun.COM return (rv);
85510087SSeth.Goldberg@Sun.COM }
85610087SSeth.Goldberg@Sun.COM
857*10803SSeth.Goldberg@Sun.COM /*
858*10803SSeth.Goldberg@Sun.COM * This is the main mouse input routine. Commands and parameters
859*10803SSeth.Goldberg@Sun.COM * from upstream are sent to the mouse device immediately, unless
860*10803SSeth.Goldberg@Sun.COM * the mouse is in the process of being reset, in which case
861*10803SSeth.Goldberg@Sun.COM * commands are queued and executed later in the service procedure.
862*10803SSeth.Goldberg@Sun.COM */
863*10803SSeth.Goldberg@Sun.COM static int
mouse8042_wput(queue_t * q,mblk_t * mp)864*10803SSeth.Goldberg@Sun.COM mouse8042_wput(queue_t *q, mblk_t *mp)
865*10803SSeth.Goldberg@Sun.COM {
866*10803SSeth.Goldberg@Sun.COM struct mouse_state *state;
867*10803SSeth.Goldberg@Sun.COM state = (struct mouse_state *)q->q_ptr;
868*10803SSeth.Goldberg@Sun.COM
869*10803SSeth.Goldberg@Sun.COM /*
870*10803SSeth.Goldberg@Sun.COM * Process all messages immediately, unless a reset is in
871*10803SSeth.Goldberg@Sun.COM * progress. If a reset is in progress, deflect processing to
872*10803SSeth.Goldberg@Sun.COM * the service procedure.
873*10803SSeth.Goldberg@Sun.COM */
874*10803SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE)
875*10803SSeth.Goldberg@Sun.COM return (putq(q, mp));
876*10803SSeth.Goldberg@Sun.COM
877*10803SSeth.Goldberg@Sun.COM /*
878*10803SSeth.Goldberg@Sun.COM * If there are still messages outstanding in the queue that
879*10803SSeth.Goldberg@Sun.COM * the service procedure hasn't processed yet, put this
880*10803SSeth.Goldberg@Sun.COM * message in the queue also, to ensure proper message
881*10803SSeth.Goldberg@Sun.COM * ordering.
882*10803SSeth.Goldberg@Sun.COM */
883*10803SSeth.Goldberg@Sun.COM if (q->q_first)
884*10803SSeth.Goldberg@Sun.COM return (putq(q, mp));
885*10803SSeth.Goldberg@Sun.COM
886*10803SSeth.Goldberg@Sun.COM (void) mouse8042_process_msg(q, mp, state);
887*10803SSeth.Goldberg@Sun.COM
888*10803SSeth.Goldberg@Sun.COM return (0);
889*10803SSeth.Goldberg@Sun.COM }
890*10803SSeth.Goldberg@Sun.COM
89110087SSeth.Goldberg@Sun.COM static int
mouse8042_wsrv(queue_t * qp)89210087SSeth.Goldberg@Sun.COM mouse8042_wsrv(queue_t *qp)
89310087SSeth.Goldberg@Sun.COM {
89410087SSeth.Goldberg@Sun.COM mblk_t *mp;
89510087SSeth.Goldberg@Sun.COM struct mouse_state *state;
89610087SSeth.Goldberg@Sun.COM state = (struct mouse_state *)qp->q_ptr;
89710087SSeth.Goldberg@Sun.COM
89810087SSeth.Goldberg@Sun.COM while ((mp = getq(qp)) != NULL) {
89910087SSeth.Goldberg@Sun.COM if (mouse8042_process_msg(qp, mp, state) != 0)
90010087SSeth.Goldberg@Sun.COM break;
90110087SSeth.Goldberg@Sun.COM }
90210087SSeth.Goldberg@Sun.COM
90310087SSeth.Goldberg@Sun.COM return (0);
90410087SSeth.Goldberg@Sun.COM }
90510087SSeth.Goldberg@Sun.COM
90610087SSeth.Goldberg@Sun.COM /*
90710087SSeth.Goldberg@Sun.COM * Returns the next reset state, given the current state and the byte
90810087SSeth.Goldberg@Sun.COM * received from the mouse. Error and Resend codes are handled by the
90910087SSeth.Goldberg@Sun.COM * caller.
91010087SSeth.Goldberg@Sun.COM */
91110087SSeth.Goldberg@Sun.COM static mouse8042_reset_state_e
mouse8042_reset_fsm(mouse8042_reset_state_e reset_state,uint8_t mdata)91210087SSeth.Goldberg@Sun.COM mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata)
91310087SSeth.Goldberg@Sun.COM {
91410087SSeth.Goldberg@Sun.COM switch (reset_state) {
91510087SSeth.Goldberg@Sun.COM case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */
91610087SSeth.Goldberg@Sun.COM if (mdata == MSE_ACK) /* Got the ACK */
91710087SSeth.Goldberg@Sun.COM return (MSE_RESET_ACK);
91810087SSeth.Goldberg@Sun.COM break;
91910087SSeth.Goldberg@Sun.COM
92010087SSeth.Goldberg@Sun.COM case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */
92110087SSeth.Goldberg@Sun.COM if (mdata == MSE_AA) /* Got the 0xAA */
92210087SSeth.Goldberg@Sun.COM return (MSE_RESET_AA);
92310087SSeth.Goldberg@Sun.COM break;
92410087SSeth.Goldberg@Sun.COM
92510087SSeth.Goldberg@Sun.COM case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */
92610087SSeth.Goldberg@Sun.COM if (mdata == MSE_00)
92710087SSeth.Goldberg@Sun.COM return (MSE_RESET_IDLE);
92810087SSeth.Goldberg@Sun.COM break;
92910087SSeth.Goldberg@Sun.COM }
93010087SSeth.Goldberg@Sun.COM
93110087SSeth.Goldberg@Sun.COM return (reset_state);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate
9340Sstevel@tonic-gate static uint_t
mouse8042_intr(caddr_t arg)9350Sstevel@tonic-gate mouse8042_intr(caddr_t arg)
9360Sstevel@tonic-gate {
9370Sstevel@tonic-gate unsigned char mdata;
9380Sstevel@tonic-gate mblk_t *mp;
9390Sstevel@tonic-gate struct mouse_state *state = (struct mouse_state *)arg;
9400Sstevel@tonic-gate int rc;
9410Sstevel@tonic-gate
9420Sstevel@tonic-gate mutex_enter(&state->ms_mutex);
9430Sstevel@tonic-gate
9440Sstevel@tonic-gate rc = DDI_INTR_UNCLAIMED;
9450Sstevel@tonic-gate
9460Sstevel@tonic-gate for (;;) {
9470Sstevel@tonic-gate
9480Sstevel@tonic-gate if (ddi_get8(state->ms_handle,
9497656SSherry.Moore@Sun.COM state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
9500Sstevel@tonic-gate break;
9510Sstevel@tonic-gate }
9520Sstevel@tonic-gate
9530Sstevel@tonic-gate mdata = ddi_get8(state->ms_handle,
9547656SSherry.Moore@Sun.COM state->ms_addr + I8042_INT_INPUT_DATA);
9550Sstevel@tonic-gate
95610087SSeth.Goldberg@Sun.COM rc = DDI_INTR_CLAIMED;
95710087SSeth.Goldberg@Sun.COM
95810087SSeth.Goldberg@Sun.COM /*
95910087SSeth.Goldberg@Sun.COM * If we're not ready for this data, discard it.
96010087SSeth.Goldberg@Sun.COM */
96110087SSeth.Goldberg@Sun.COM if (!state->ready)
96210087SSeth.Goldberg@Sun.COM continue;
96310087SSeth.Goldberg@Sun.COM
96410087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
96510087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE) {
96610087SSeth.Goldberg@Sun.COM
96710087SSeth.Goldberg@Sun.COM if (mdata == MSEERROR || mdata == MSERESET) {
96810087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_FAILED;
96910087SSeth.Goldberg@Sun.COM } else {
97010087SSeth.Goldberg@Sun.COM state->reset_state =
97110087SSeth.Goldberg@Sun.COM mouse8042_reset_fsm(state->reset_state,
97210087SSeth.Goldberg@Sun.COM mdata);
97310087SSeth.Goldberg@Sun.COM }
97410087SSeth.Goldberg@Sun.COM
975*10803SSeth.Goldberg@Sun.COM if (state->reset_state == MSE_RESET_ACK) {
976*10803SSeth.Goldberg@Sun.COM
977*10803SSeth.Goldberg@Sun.COM /*
978*10803SSeth.Goldberg@Sun.COM * We received an ACK from the mouse, so
979*10803SSeth.Goldberg@Sun.COM * send it upstream immediately so that
980*10803SSeth.Goldberg@Sun.COM * consumers depending on the immediate
981*10803SSeth.Goldberg@Sun.COM * ACK don't time out.
982*10803SSeth.Goldberg@Sun.COM */
983*10803SSeth.Goldberg@Sun.COM if (state->reset_ack_mp != NULL) {
984*10803SSeth.Goldberg@Sun.COM
985*10803SSeth.Goldberg@Sun.COM mp = state->reset_ack_mp;
986*10803SSeth.Goldberg@Sun.COM
987*10803SSeth.Goldberg@Sun.COM state->reset_ack_mp = NULL;
988*10803SSeth.Goldberg@Sun.COM
989*10803SSeth.Goldberg@Sun.COM if (state->ms_rqp != NULL) {
990*10803SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_ACK;
991*10803SSeth.Goldberg@Sun.COM putnext(state->ms_rqp, mp);
992*10803SSeth.Goldberg@Sun.COM } else
993*10803SSeth.Goldberg@Sun.COM freemsg(mp);
994*10803SSeth.Goldberg@Sun.COM }
995*10803SSeth.Goldberg@Sun.COM
996*10803SSeth.Goldberg@Sun.COM if (state->ms_wqp != NULL) {
997*10803SSeth.Goldberg@Sun.COM enableok(state->ms_wqp);
998*10803SSeth.Goldberg@Sun.COM qenable(state->ms_wqp);
999*10803SSeth.Goldberg@Sun.COM }
1000*10803SSeth.Goldberg@Sun.COM
1001*10803SSeth.Goldberg@Sun.COM } else if (state->reset_state == MSE_RESET_IDLE ||
1002*10803SSeth.Goldberg@Sun.COM state->reset_state == MSE_RESET_FAILED) {
1003*10803SSeth.Goldberg@Sun.COM
100410087SSeth.Goldberg@Sun.COM /*
100510087SSeth.Goldberg@Sun.COM * If we transitioned back to the idle reset state (or
100610087SSeth.Goldberg@Sun.COM * the reset failed), disable the timeout, release the
100710087SSeth.Goldberg@Sun.COM * 8042 exclusive-access lock, then send the response
100810087SSeth.Goldberg@Sun.COM * the the upper-level modules. Finally, enable the
100910087SSeth.Goldberg@Sun.COM * queue and schedule queue service procedures so that
101010087SSeth.Goldberg@Sun.COM * upper-level modules can process the response.
101110087SSeth.Goldberg@Sun.COM * Otherwise, if we're still in the middle of the
101210087SSeth.Goldberg@Sun.COM * reset sequence, do not send the data up (since the
101310087SSeth.Goldberg@Sun.COM * response is sent at the end of the sequence, or
101410087SSeth.Goldberg@Sun.COM * on timeout/error).
101510087SSeth.Goldberg@Sun.COM */
10160Sstevel@tonic-gate
101710087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
101810087SSeth.Goldberg@Sun.COM (void) quntimeout(state->ms_wqp,
101910087SSeth.Goldberg@Sun.COM state->reset_tid);
102010087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex);
102110087SSeth.Goldberg@Sun.COM
102210087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle,
102310087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_UNLOCK);
102410087SSeth.Goldberg@Sun.COM
102510087SSeth.Goldberg@Sun.COM state->reset_tid = 0;
1026*10803SSeth.Goldberg@Sun.COM if (state->reply_mp != NULL) {
1027*10803SSeth.Goldberg@Sun.COM mp = state->reply_mp;
1028*10803SSeth.Goldberg@Sun.COM if (state->reset_state ==
1029*10803SSeth.Goldberg@Sun.COM MSE_RESET_FAILED) {
1030*10803SSeth.Goldberg@Sun.COM *mp->b_wptr++ = mdata;
1031*10803SSeth.Goldberg@Sun.COM } else {
1032*10803SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_AA;
1033*10803SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_00;
1034*10803SSeth.Goldberg@Sun.COM }
1035*10803SSeth.Goldberg@Sun.COM state->reply_mp = NULL;
103610087SSeth.Goldberg@Sun.COM } else {
1037*10803SSeth.Goldberg@Sun.COM mp = NULL;
103810087SSeth.Goldberg@Sun.COM }
103910087SSeth.Goldberg@Sun.COM
104010087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE;
1041*10803SSeth.Goldberg@Sun.COM cv_signal(&state->reset_cv);
104210087SSeth.Goldberg@Sun.COM
1043*10803SSeth.Goldberg@Sun.COM if (mp != NULL) {
1044*10803SSeth.Goldberg@Sun.COM if (state->ms_rqp != NULL)
1045*10803SSeth.Goldberg@Sun.COM putnext(state->ms_rqp, mp);
1046*10803SSeth.Goldberg@Sun.COM else
1047*10803SSeth.Goldberg@Sun.COM freemsg(mp);
1048*10803SSeth.Goldberg@Sun.COM }
104910087SSeth.Goldberg@Sun.COM
1050*10803SSeth.Goldberg@Sun.COM if (state->ms_wqp != NULL) {
1051*10803SSeth.Goldberg@Sun.COM enableok(state->ms_wqp);
1052*10803SSeth.Goldberg@Sun.COM qenable(state->ms_wqp);
1053*10803SSeth.Goldberg@Sun.COM }
105410087SSeth.Goldberg@Sun.COM }
105510087SSeth.Goldberg@Sun.COM
105610087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
105710087SSeth.Goldberg@Sun.COM mutex_exit(&state->ms_mutex);
105810087SSeth.Goldberg@Sun.COM return (rc);
105910087SSeth.Goldberg@Sun.COM }
106010087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex);
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
10630Sstevel@tonic-gate *mp->b_wptr++ = mdata;
10640Sstevel@tonic-gate putnext(state->ms_rqp, mp);
10650Sstevel@tonic-gate }
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate mutex_exit(&state->ms_mutex);
10680Sstevel@tonic-gate
10690Sstevel@tonic-gate return (rc);
10700Sstevel@tonic-gate }
1071