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 /* 26*10087SSeth.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> 45*10087SSeth.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> 57*10087SSeth.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 65*10087SSeth.Goldberg@Sun.COM #define MOUSE8042_RESET_TIMEOUT_USECS 500000 /* 500 ms */ 66*10087SSeth.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 88*10087SSeth.Goldberg@Sun.COM /* 89*10087SSeth.Goldberg@Sun.COM * RESET states 90*10087SSeth.Goldberg@Sun.COM */ 91*10087SSeth.Goldberg@Sun.COM typedef enum { 92*10087SSeth.Goldberg@Sun.COM MSE_RESET_IDLE, /* No reset in progress */ 93*10087SSeth.Goldberg@Sun.COM MSE_RESET_PRE, /* Send reset, waiting for ACK */ 94*10087SSeth.Goldberg@Sun.COM MSE_RESET_ACK, /* Got ACK, waiting for 0xAA */ 95*10087SSeth.Goldberg@Sun.COM MSE_RESET_AA, /* Got 0xAA, waiting for 0x00 */ 96*10087SSeth.Goldberg@Sun.COM MSE_RESET_FAILED 97*10087SSeth.Goldberg@Sun.COM } mouse8042_reset_state_e; 98*10087SSeth.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; 109*10087SSeth.Goldberg@Sun.COM kmutex_t reset_mutex; 110*10087SSeth.Goldberg@Sun.COM mouse8042_reset_state_e reset_state; 111*10087SSeth.Goldberg@Sun.COM timeout_id_t reset_tid; 112*10087SSeth.Goldberg@Sun.COM int ready; 113*10087SSeth.Goldberg@Sun.COM mblk_t *reply_mp; 114*10087SSeth.Goldberg@Sun.COM bufcall_id_t bc_id; 1150Sstevel@tonic-gate }; 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate static uint_t mouse8042_intr(caddr_t arg); 1180Sstevel@tonic-gate static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag, 1190Sstevel@tonic-gate cred_t *cred_p); 1200Sstevel@tonic-gate static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p); 121*10087SSeth.Goldberg@Sun.COM static int mouse8042_wsrv(queue_t *qp); 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 1240Sstevel@tonic-gate void *arg, void **result); 1250Sstevel@tonic-gate static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd); 1260Sstevel@tonic-gate static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd); 1270Sstevel@tonic-gate 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate /* 1300Sstevel@tonic-gate * Streams module info. 1310Sstevel@tonic-gate */ 1320Sstevel@tonic-gate #define MODULE_NAME "mouse8042" 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate static struct module_info mouse8042_minfo = { 1350Sstevel@tonic-gate 23, /* Module ID number */ 1360Sstevel@tonic-gate MODULE_NAME, 1370Sstevel@tonic-gate 0, INFPSZ, /* minimum & maximum packet sizes */ 1380Sstevel@tonic-gate 256, 128 /* hi and low water marks */ 1390Sstevel@tonic-gate }; 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate static struct qinit mouse8042_rinit = { 1420Sstevel@tonic-gate NULL, /* put */ 1430Sstevel@tonic-gate NULL, /* service */ 1440Sstevel@tonic-gate mouse8042_open, 1450Sstevel@tonic-gate mouse8042_close, 1460Sstevel@tonic-gate NULL, /* admin */ 1470Sstevel@tonic-gate &mouse8042_minfo, 1480Sstevel@tonic-gate NULL /* statistics */ 1490Sstevel@tonic-gate }; 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate static struct qinit mouse8042_winit = { 152*10087SSeth.Goldberg@Sun.COM putq, /* put */ 153*10087SSeth.Goldberg@Sun.COM mouse8042_wsrv, /* service */ 1540Sstevel@tonic-gate NULL, /* open */ 1550Sstevel@tonic-gate NULL, /* close */ 1560Sstevel@tonic-gate NULL, /* admin */ 1570Sstevel@tonic-gate &mouse8042_minfo, 1580Sstevel@tonic-gate NULL /* statistics */ 1590Sstevel@tonic-gate }; 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate static struct streamtab mouse8042_strinfo = { 1620Sstevel@tonic-gate &mouse8042_rinit, 1630Sstevel@tonic-gate &mouse8042_winit, 1640Sstevel@tonic-gate NULL, /* muxrinit */ 1650Sstevel@tonic-gate NULL, /* muxwinit */ 1660Sstevel@tonic-gate }; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate /* 1690Sstevel@tonic-gate * Local Function Declarations 1700Sstevel@tonic-gate */ 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate static struct cb_ops mouse8042_cb_ops = { 1730Sstevel@tonic-gate nodev, /* open */ 1740Sstevel@tonic-gate nodev, /* close */ 1750Sstevel@tonic-gate nodev, /* strategy */ 1760Sstevel@tonic-gate nodev, /* print */ 1770Sstevel@tonic-gate nodev, /* dump */ 1780Sstevel@tonic-gate nodev, /* read */ 1790Sstevel@tonic-gate nodev, /* write */ 1800Sstevel@tonic-gate nodev, /* ioctl */ 1810Sstevel@tonic-gate nodev, /* devmap */ 1820Sstevel@tonic-gate nodev, /* mmap */ 1830Sstevel@tonic-gate nodev, /* segmap */ 1840Sstevel@tonic-gate nochpoll, /* poll */ 1850Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 1860Sstevel@tonic-gate &mouse8042_strinfo, /* streamtab */ 1870Sstevel@tonic-gate D_MP | D_NEW 1880Sstevel@tonic-gate }; 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate static struct dev_ops mouse8042_ops = { 1920Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 1930Sstevel@tonic-gate 0, /* refcnt */ 1940Sstevel@tonic-gate mouse8042_getinfo, /* getinfo */ 1950Sstevel@tonic-gate nulldev, /* identify */ 1960Sstevel@tonic-gate nulldev, /* probe */ 1970Sstevel@tonic-gate mouse8042_attach, /* attach */ 1980Sstevel@tonic-gate mouse8042_detach, /* detach */ 1990Sstevel@tonic-gate nodev, /* reset */ 2000Sstevel@tonic-gate &mouse8042_cb_ops, /* driver operations */ 2017656SSherry.Moore@Sun.COM (struct bus_ops *)0, /* bus operations */ 2027656SSherry.Moore@Sun.COM NULL, /* power */ 2037656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */ 2040Sstevel@tonic-gate }; 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate /* 2070Sstevel@tonic-gate * This is the loadable module wrapper. 2080Sstevel@tonic-gate */ 2090Sstevel@tonic-gate #include <sys/modctl.h> 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate extern struct mod_ops mod_driverops; 2120Sstevel@tonic-gate 2130Sstevel@tonic-gate /* 2140Sstevel@tonic-gate * Module linkage information for the kernel. 2150Sstevel@tonic-gate */ 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate static struct modldrv modldrv = { 2180Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 2197656SSherry.Moore@Sun.COM "PS/2 Mouse", 2200Sstevel@tonic-gate &mouse8042_ops, /* driver ops */ 2210Sstevel@tonic-gate }; 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate static struct modlinkage modlinkage = { 2240Sstevel@tonic-gate MODREV_1, 2250Sstevel@tonic-gate (void *)&modldrv, 2260Sstevel@tonic-gate NULL 2270Sstevel@tonic-gate }; 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate /* 2300Sstevel@tonic-gate * This is the driver initialization routine. 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate int 2330Sstevel@tonic-gate _init() 2340Sstevel@tonic-gate { 2350Sstevel@tonic-gate int rv; 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate rv = mod_install(&modlinkage); 2380Sstevel@tonic-gate return (rv); 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate int 2430Sstevel@tonic-gate _fini(void) 2440Sstevel@tonic-gate { 2450Sstevel@tonic-gate return (mod_remove(&modlinkage)); 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate int 2500Sstevel@tonic-gate _info(struct modinfo *modinfop) 2510Sstevel@tonic-gate { 2520Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2530Sstevel@tonic-gate } 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate static int 2560Sstevel@tonic-gate mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2570Sstevel@tonic-gate { 2580Sstevel@tonic-gate struct mouse_state *state; 259822Ssethg mblk_t *mp; 2600Sstevel@tonic-gate int instance = ddi_get_instance(dip); 2610Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 2620Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 2630Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 2640Sstevel@tonic-gate DDI_STRICTORDER_ACC, 2650Sstevel@tonic-gate }; 2660Sstevel@tonic-gate int rc; 2670Sstevel@tonic-gate 2680Sstevel@tonic-gate 269822Ssethg if (cmd == DDI_RESUME) { 270822Ssethg state = (struct mouse_state *)ddi_get_driver_private(dip); 271822Ssethg 272*10087SSeth.Goldberg@Sun.COM /* Ready to handle inbound data from mouse8042_intr */ 273*10087SSeth.Goldberg@Sun.COM state->ready = 1; 274*10087SSeth.Goldberg@Sun.COM 275822Ssethg /* 276822Ssethg * Send a 0xaa 0x00 upstream. 277822Ssethg * This causes the vuid module to reset the mouse. 278822Ssethg */ 279822Ssethg if (state->ms_rqp != NULL) { 280822Ssethg if (mp = allocb(1, BPRI_MED)) { 281822Ssethg *mp->b_wptr++ = 0xaa; 282822Ssethg putnext(state->ms_rqp, mp); 283822Ssethg } 284822Ssethg if (mp = allocb(1, BPRI_MED)) { 285822Ssethg *mp->b_wptr++ = 0x0; 286822Ssethg putnext(state->ms_rqp, mp); 287822Ssethg } 288822Ssethg } 289822Ssethg return (DDI_SUCCESS); 290822Ssethg } 291822Ssethg 2920Sstevel@tonic-gate if (cmd != DDI_ATTACH) 2930Sstevel@tonic-gate return (DDI_FAILURE); 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate if (mouse8042_dip != NULL) 2960Sstevel@tonic-gate return (DDI_FAILURE); 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate /* allocate and initialize state structure */ 2990Sstevel@tonic-gate state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP); 3000Sstevel@tonic-gate state->ms_opened = B_FALSE; 301*10087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE; 302*10087SSeth.Goldberg@Sun.COM state->reset_tid = 0; 303*10087SSeth.Goldberg@Sun.COM state->bc_id = 0; 3040Sstevel@tonic-gate ddi_set_driver_private(dip, state); 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate /* 3070Sstevel@tonic-gate * In order to support virtual keyboard/mouse, we should distinguish 3080Sstevel@tonic-gate * between internal virtual open and external physical open. 3090Sstevel@tonic-gate * 3100Sstevel@tonic-gate * When the physical devices are opened by application, they will 3110Sstevel@tonic-gate * be unlinked from the virtual device and their data stream will 3120Sstevel@tonic-gate * not be sent to the virtual device. When the opened physical 3130Sstevel@tonic-gate * devices are closed, they will be relinked to the virtual devices. 3140Sstevel@tonic-gate * 3150Sstevel@tonic-gate * All these automatic switch between virtual and physical are 3160Sstevel@tonic-gate * transparent. 3170Sstevel@tonic-gate * 3180Sstevel@tonic-gate * So we change minor node numbering scheme to be: 3190Sstevel@tonic-gate * external node minor num == instance * 2 3200Sstevel@tonic-gate * internal node minor num == instance * 2 + 1 3210Sstevel@tonic-gate */ 322822Ssethg rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2, 3230Sstevel@tonic-gate DDI_NT_MOUSE, NULL); 3240Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3250Sstevel@tonic-gate goto fail_1; 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR, 3297656SSherry.Moore@Sun.COM instance * 2 + 1) != DDI_SUCCESS) { 3300Sstevel@tonic-gate goto fail_2; 3310Sstevel@tonic-gate } 3320Sstevel@tonic-gate 3330Sstevel@tonic-gate rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr, 3347656SSherry.Moore@Sun.COM (offset_t)0, (offset_t)0, &attr, &state->ms_handle); 3350Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3360Sstevel@tonic-gate goto fail_2; 3370Sstevel@tonic-gate } 3380Sstevel@tonic-gate 3390Sstevel@tonic-gate rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie); 3400Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3410Sstevel@tonic-gate goto fail_3; 3420Sstevel@tonic-gate } 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER, 3450Sstevel@tonic-gate state->ms_iblock_cookie); 346*10087SSeth.Goldberg@Sun.COM mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER, 347*10087SSeth.Goldberg@Sun.COM state->ms_iblock_cookie); 3480Sstevel@tonic-gate 3490Sstevel@tonic-gate rc = ddi_add_intr(dip, 0, 3507656SSherry.Moore@Sun.COM (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, 3517656SSherry.Moore@Sun.COM mouse8042_intr, (caddr_t)state); 3520Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3530Sstevel@tonic-gate goto fail_3; 3540Sstevel@tonic-gate } 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate mouse8042_dip = dip; 3570Sstevel@tonic-gate 358*10087SSeth.Goldberg@Sun.COM /* Ready to handle inbound data from mouse8042_intr */ 359*10087SSeth.Goldberg@Sun.COM state->ready = 1; 360*10087SSeth.Goldberg@Sun.COM 3610Sstevel@tonic-gate /* Now that we're attached, announce our presence to the world. */ 3620Sstevel@tonic-gate ddi_report_dev(dip); 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: 386*10087SSeth.Goldberg@Sun.COM /* Ignore all data from mouse8042_intr until we fully resume */ 387*10087SSeth.Goldberg@Sun.COM state->ready = 0; 388822Ssethg return (DDI_SUCCESS); 3890Sstevel@tonic-gate 3900Sstevel@tonic-gate case DDI_DETACH: 3910Sstevel@tonic-gate ddi_remove_intr(dip, 0, state->ms_iblock_cookie); 3920Sstevel@tonic-gate mouse8042_dip = NULL; 393*10087SSeth.Goldberg@Sun.COM mutex_destroy(&state->reset_mutex); 3940Sstevel@tonic-gate mutex_destroy(&state->ms_mutex); 3950Sstevel@tonic-gate ddi_prop_remove_all(dip); 3960Sstevel@tonic-gate ddi_regs_map_free(&state->ms_handle); 3970Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 3980Sstevel@tonic-gate kmem_free(state, sizeof (struct mouse_state)); 3990Sstevel@tonic-gate return (DDI_SUCCESS); 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate default: 4020Sstevel@tonic-gate return (DDI_FAILURE); 4030Sstevel@tonic-gate } 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate /* ARGSUSED */ 4080Sstevel@tonic-gate static int 4090Sstevel@tonic-gate mouse8042_getinfo( 4100Sstevel@tonic-gate dev_info_t *dip, 4110Sstevel@tonic-gate ddi_info_cmd_t infocmd, 4120Sstevel@tonic-gate void *arg, 4130Sstevel@tonic-gate void **result) 4140Sstevel@tonic-gate { 4150Sstevel@tonic-gate dev_t dev = (dev_t)arg; 4160Sstevel@tonic-gate minor_t minor = getminor(dev); 4170Sstevel@tonic-gate int instance = MOUSE8042_MINOR_TO_INSTANCE(minor); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate switch (infocmd) { 4200Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 4210Sstevel@tonic-gate if (mouse8042_dip == NULL) 4220Sstevel@tonic-gate return (DDI_FAILURE); 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate *result = (void *)mouse8042_dip; 4250Sstevel@tonic-gate break; 4260Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 4270Sstevel@tonic-gate *result = (void *)(uintptr_t)instance; 4280Sstevel@tonic-gate break; 4290Sstevel@tonic-gate default: 4300Sstevel@tonic-gate return (DDI_FAILURE); 4310Sstevel@tonic-gate } 4320Sstevel@tonic-gate return (DDI_SUCCESS); 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate /*ARGSUSED*/ 4360Sstevel@tonic-gate static int 4370Sstevel@tonic-gate mouse8042_open( 4380Sstevel@tonic-gate queue_t *q, 4390Sstevel@tonic-gate dev_t *devp, 4400Sstevel@tonic-gate int flag, 4410Sstevel@tonic-gate int sflag, 4420Sstevel@tonic-gate cred_t *cred_p) 4430Sstevel@tonic-gate { 4440Sstevel@tonic-gate struct mouse_state *state; 4450Sstevel@tonic-gate minor_t minor = getminor(*devp); 4460Sstevel@tonic-gate int rval; 4470Sstevel@tonic-gate 4480Sstevel@tonic-gate if (mouse8042_dip == NULL) 4490Sstevel@tonic-gate return (ENXIO); 4500Sstevel@tonic-gate 4510Sstevel@tonic-gate state = ddi_get_driver_private(mouse8042_dip); 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate if (state->ms_opened) { 4560Sstevel@tonic-gate /* 4570Sstevel@tonic-gate * Exit if the same minor node is already open 4580Sstevel@tonic-gate */ 4590Sstevel@tonic-gate if (state->ms_minor == minor) { 4600Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 4610Sstevel@tonic-gate return (0); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate /* 4650Sstevel@tonic-gate * Check whether it is switch between physical and virtual 4660Sstevel@tonic-gate * 4670Sstevel@tonic-gate * Opening from virtual while the device is being physically 4680Sstevel@tonic-gate * opened by an application should not happen. So we ASSERT 4690Sstevel@tonic-gate * this in DEBUG version, and return error in the non-DEBUG 4700Sstevel@tonic-gate * case. 4710Sstevel@tonic-gate */ 4720Sstevel@tonic-gate ASSERT(!MOUSE8042_INTERNAL_OPEN(minor)); 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate if (MOUSE8042_INTERNAL_OPEN(minor)) { 4750Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 4760Sstevel@tonic-gate return (EINVAL); 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate 4790Sstevel@tonic-gate /* 4800Sstevel@tonic-gate * Opening the physical one while it is being underneath 4810Sstevel@tonic-gate * the virtual one. 4820Sstevel@tonic-gate * 4830Sstevel@tonic-gate * consconfig_unlink is called to unlink this device from 4840Sstevel@tonic-gate * the virtual one, thus the old stream serving for this 4850Sstevel@tonic-gate * device under the virtual one is closed, and then the 4860Sstevel@tonic-gate * lower driver's close routine (here is mouse8042_close) 4870Sstevel@tonic-gate * is also called to accomplish the whole stream close. 4880Sstevel@tonic-gate * Here we have to drop the lock because mouse8042_close 4890Sstevel@tonic-gate * also needs the lock. 4900Sstevel@tonic-gate * 4910Sstevel@tonic-gate * For mouse, the old stream is: 4920Sstevel@tonic-gate * consms->["pushmod"->]"mouse_vp driver" 4930Sstevel@tonic-gate * 4940Sstevel@tonic-gate * After the consconfig_unlink returns, the old stream is closed 4950Sstevel@tonic-gate * and we grab the lock again to reopen this device as normal. 4960Sstevel@tonic-gate */ 4970Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 4980Sstevel@tonic-gate 4990Sstevel@tonic-gate /* 5000Sstevel@tonic-gate * If unlink fails, fail the physical open. 5010Sstevel@tonic-gate */ 5020Sstevel@tonic-gate if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip), 5030Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor))) != 0) { 5040Sstevel@tonic-gate return (rval); 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate 5070Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate 5110Sstevel@tonic-gate q->q_ptr = (caddr_t)state; 5120Sstevel@tonic-gate WR(q)->q_ptr = (caddr_t)state; 5130Sstevel@tonic-gate state->ms_rqp = q; 5140Sstevel@tonic-gate state->ms_wqp = WR(q); 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate qprocson(q); 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate state->ms_minor = minor; 5190Sstevel@tonic-gate state->ms_opened = B_TRUE; 5200Sstevel@tonic-gate 5210Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate return (0); 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate /*ARGSUSED*/ 5280Sstevel@tonic-gate static int 5290Sstevel@tonic-gate mouse8042_close(queue_t *q, int flag, cred_t *cred_p) 5300Sstevel@tonic-gate { 5310Sstevel@tonic-gate struct mouse_state *state; 5320Sstevel@tonic-gate minor_t minor; 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate state = (struct mouse_state *)q->q_ptr; 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate qprocsoff(q); 5390Sstevel@tonic-gate 540*10087SSeth.Goldberg@Sun.COM if (state->reset_tid != 0) { 541*10087SSeth.Goldberg@Sun.COM (void) quntimeout(q, state->reset_tid); 542*10087SSeth.Goldberg@Sun.COM state->reset_tid = 0; 543*10087SSeth.Goldberg@Sun.COM } 544*10087SSeth.Goldberg@Sun.COM if (state->bc_id != 0) { 545*10087SSeth.Goldberg@Sun.COM (void) qunbufcall(q, state->bc_id); 546*10087SSeth.Goldberg@Sun.COM state->bc_id = 0; 547*10087SSeth.Goldberg@Sun.COM } 548*10087SSeth.Goldberg@Sun.COM if (state->reply_mp != NULL) { 549*10087SSeth.Goldberg@Sun.COM freemsg(state->reply_mp); 550*10087SSeth.Goldberg@Sun.COM state->reply_mp = NULL; 551*10087SSeth.Goldberg@Sun.COM } 552*10087SSeth.Goldberg@Sun.COM 5530Sstevel@tonic-gate q->q_ptr = NULL; 5540Sstevel@tonic-gate WR(q)->q_ptr = NULL; 5550Sstevel@tonic-gate state->ms_rqp = NULL; 5560Sstevel@tonic-gate state->ms_wqp = NULL; 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate state->ms_opened = B_FALSE; 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate minor = state->ms_minor; 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 5630Sstevel@tonic-gate 5640Sstevel@tonic-gate if (!MOUSE8042_INTERNAL_OPEN(minor)) { 5650Sstevel@tonic-gate /* 5660Sstevel@tonic-gate * Closing physical PS/2 mouse 5670Sstevel@tonic-gate * 5680Sstevel@tonic-gate * Link it back to virtual mouse, and 5690Sstevel@tonic-gate * mouse8042_open will be called as a result 570822Ssethg * of the consconfig_link call. Do NOT try 571822Ssethg * this if the mouse is about to be detached! 5720Sstevel@tonic-gate * 5730Sstevel@tonic-gate * If linking back fails, this specific mouse 5740Sstevel@tonic-gate * will not be available underneath the virtual 5750Sstevel@tonic-gate * mouse, and can only be accessed via physical 5760Sstevel@tonic-gate * open. 5770Sstevel@tonic-gate */ 5780Sstevel@tonic-gate consconfig_link(ddi_driver_major(mouse8042_dip), 5790Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor)); 5800Sstevel@tonic-gate } 5810Sstevel@tonic-gate 5820Sstevel@tonic-gate return (0); 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate 5850Sstevel@tonic-gate static void 5860Sstevel@tonic-gate mouse8042_iocnack( 5870Sstevel@tonic-gate queue_t *qp, 5880Sstevel@tonic-gate mblk_t *mp, 5890Sstevel@tonic-gate struct iocblk *iocp, 5900Sstevel@tonic-gate int error, 5910Sstevel@tonic-gate int rval) 5920Sstevel@tonic-gate { 5930Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 5940Sstevel@tonic-gate iocp->ioc_rval = rval; 5950Sstevel@tonic-gate iocp->ioc_error = error; 5960Sstevel@tonic-gate qreply(qp, mp); 5970Sstevel@tonic-gate } 5980Sstevel@tonic-gate 599*10087SSeth.Goldberg@Sun.COM static void 600*10087SSeth.Goldberg@Sun.COM mouse8042_reset_timeout(void *argp) 601*10087SSeth.Goldberg@Sun.COM { 602*10087SSeth.Goldberg@Sun.COM struct mouse_state *state = (struct mouse_state *)argp; 603*10087SSeth.Goldberg@Sun.COM mblk_t *mp; 604*10087SSeth.Goldberg@Sun.COM 605*10087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex); 606*10087SSeth.Goldberg@Sun.COM 607*10087SSeth.Goldberg@Sun.COM /* 608*10087SSeth.Goldberg@Sun.COM * If the interrupt handler hasn't completed the reset handling 609*10087SSeth.Goldberg@Sun.COM * (reset_state would be IDLE or FAILED in that case), then 610*10087SSeth.Goldberg@Sun.COM * drop the 8042 lock, and send a faked retry reply upstream, 611*10087SSeth.Goldberg@Sun.COM * then enable the queue for further message processing. 612*10087SSeth.Goldberg@Sun.COM */ 613*10087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE && 614*10087SSeth.Goldberg@Sun.COM state->reset_state != MSE_RESET_FAILED) { 615*10087SSeth.Goldberg@Sun.COM 616*10087SSeth.Goldberg@Sun.COM state->reset_tid = 0; 617*10087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE; 618*10087SSeth.Goldberg@Sun.COM 619*10087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle, state->ms_addr + 620*10087SSeth.Goldberg@Sun.COM I8042_UNLOCK); 621*10087SSeth.Goldberg@Sun.COM 622*10087SSeth.Goldberg@Sun.COM mp = state->reply_mp; 623*10087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSERESEND; 624*10087SSeth.Goldberg@Sun.COM state->reply_mp = NULL; 625*10087SSeth.Goldberg@Sun.COM 626*10087SSeth.Goldberg@Sun.COM if (state->ms_rqp != NULL) 627*10087SSeth.Goldberg@Sun.COM putnext(state->ms_rqp, mp); 628*10087SSeth.Goldberg@Sun.COM else 629*10087SSeth.Goldberg@Sun.COM freemsg(mp); 630*10087SSeth.Goldberg@Sun.COM 631*10087SSeth.Goldberg@Sun.COM ASSERT(state->ms_wqp != NULL); 632*10087SSeth.Goldberg@Sun.COM 633*10087SSeth.Goldberg@Sun.COM enableok(state->ms_wqp); 634*10087SSeth.Goldberg@Sun.COM qenable(state->ms_wqp); 635*10087SSeth.Goldberg@Sun.COM } 636*10087SSeth.Goldberg@Sun.COM 637*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 638*10087SSeth.Goldberg@Sun.COM } 639*10087SSeth.Goldberg@Sun.COM 640*10087SSeth.Goldberg@Sun.COM /* 641*10087SSeth.Goldberg@Sun.COM * Returns 1 if the caller should put the message (bp) back on the queue 642*10087SSeth.Goldberg@Sun.COM */ 6430Sstevel@tonic-gate static int 644*10087SSeth.Goldberg@Sun.COM mouse8042_process_reset(queue_t *q, mblk_t *mp, struct mouse_state *state) 6450Sstevel@tonic-gate { 646*10087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex); 647*10087SSeth.Goldberg@Sun.COM /* 648*10087SSeth.Goldberg@Sun.COM * If we're in the middle of a reset, put the message back on the queue 649*10087SSeth.Goldberg@Sun.COM * for processing later. 650*10087SSeth.Goldberg@Sun.COM */ 651*10087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE) { 652*10087SSeth.Goldberg@Sun.COM /* 653*10087SSeth.Goldberg@Sun.COM * We noenable the queue again here in case it was backenabled 654*10087SSeth.Goldberg@Sun.COM * by an upper-level module. 655*10087SSeth.Goldberg@Sun.COM */ 656*10087SSeth.Goldberg@Sun.COM noenable(q); 657*10087SSeth.Goldberg@Sun.COM 658*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 659*10087SSeth.Goldberg@Sun.COM return (1); 660*10087SSeth.Goldberg@Sun.COM } 661*10087SSeth.Goldberg@Sun.COM 662*10087SSeth.Goldberg@Sun.COM /* 663*10087SSeth.Goldberg@Sun.COM * Drop the reset state lock before allocating the response message and 664*10087SSeth.Goldberg@Sun.COM * grabbing the 8042 exclusive-access lock (since those operations 665*10087SSeth.Goldberg@Sun.COM * may take an extended period of time to complete). 666*10087SSeth.Goldberg@Sun.COM */ 667*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 668*10087SSeth.Goldberg@Sun.COM 669*10087SSeth.Goldberg@Sun.COM state->reply_mp = allocb(3, BPRI_MED); 670*10087SSeth.Goldberg@Sun.COM if (state->reply_mp == NULL) { 671*10087SSeth.Goldberg@Sun.COM /* 672*10087SSeth.Goldberg@Sun.COM * Allocation failed -- set up a bufcall to enable the queue 673*10087SSeth.Goldberg@Sun.COM * whenever there is enough memory to allocate the response 674*10087SSeth.Goldberg@Sun.COM * message. 675*10087SSeth.Goldberg@Sun.COM */ 676*10087SSeth.Goldberg@Sun.COM state->bc_id = qbufcall(q, 3, BPRI_MED, 677*10087SSeth.Goldberg@Sun.COM (void (*)(void *))qenable, q); 678*10087SSeth.Goldberg@Sun.COM 679*10087SSeth.Goldberg@Sun.COM if (state->bc_id == 0) { 680*10087SSeth.Goldberg@Sun.COM /* 681*10087SSeth.Goldberg@Sun.COM * If the qbufcall failed, we cannot proceed, so use the 682*10087SSeth.Goldberg@Sun.COM * message we were sent to respond with an error. 683*10087SSeth.Goldberg@Sun.COM */ 684*10087SSeth.Goldberg@Sun.COM *mp->b_rptr = MSEERROR; 685*10087SSeth.Goldberg@Sun.COM mp->b_wptr = mp->b_rptr + 1; 686*10087SSeth.Goldberg@Sun.COM qreply(q, mp); 687*10087SSeth.Goldberg@Sun.COM return (0); 688*10087SSeth.Goldberg@Sun.COM } 689*10087SSeth.Goldberg@Sun.COM 690*10087SSeth.Goldberg@Sun.COM return (1); 691*10087SSeth.Goldberg@Sun.COM } 692*10087SSeth.Goldberg@Sun.COM 693*10087SSeth.Goldberg@Sun.COM /* 694*10087SSeth.Goldberg@Sun.COM * Gain exclusive access to the 8042 for the duration of the reset. 695*10087SSeth.Goldberg@Sun.COM * The unlock will occur when the reset has either completed or timed 696*10087SSeth.Goldberg@Sun.COM * out. 697*10087SSeth.Goldberg@Sun.COM */ 698*10087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle, 699*10087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_LOCK); 700*10087SSeth.Goldberg@Sun.COM 701*10087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex); 702*10087SSeth.Goldberg@Sun.COM 703*10087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_PRE; 704*10087SSeth.Goldberg@Sun.COM noenable(q); 705*10087SSeth.Goldberg@Sun.COM 706*10087SSeth.Goldberg@Sun.COM state->reset_tid = qtimeout(q, 707*10087SSeth.Goldberg@Sun.COM mouse8042_reset_timeout, 708*10087SSeth.Goldberg@Sun.COM state, 709*10087SSeth.Goldberg@Sun.COM drv_usectohz( 710*10087SSeth.Goldberg@Sun.COM MOUSE8042_RESET_TIMEOUT_USECS)); 711*10087SSeth.Goldberg@Sun.COM 712*10087SSeth.Goldberg@Sun.COM ddi_put8(state->ms_handle, 713*10087SSeth.Goldberg@Sun.COM state->ms_addr + 714*10087SSeth.Goldberg@Sun.COM I8042_INT_OUTPUT_DATA, MSERESET); 715*10087SSeth.Goldberg@Sun.COM 716*10087SSeth.Goldberg@Sun.COM mp->b_rptr++; 717*10087SSeth.Goldberg@Sun.COM 718*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 719*10087SSeth.Goldberg@Sun.COM return (1); 720*10087SSeth.Goldberg@Sun.COM } 721*10087SSeth.Goldberg@Sun.COM 722*10087SSeth.Goldberg@Sun.COM /* 723*10087SSeth.Goldberg@Sun.COM * Returns 1 if the caller should stop processing messages 724*10087SSeth.Goldberg@Sun.COM */ 725*10087SSeth.Goldberg@Sun.COM static int 726*10087SSeth.Goldberg@Sun.COM mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state) 727*10087SSeth.Goldberg@Sun.COM { 7280Sstevel@tonic-gate mblk_t *bp; 7290Sstevel@tonic-gate mblk_t *next; 730*10087SSeth.Goldberg@Sun.COM 731*10087SSeth.Goldberg@Sun.COM bp = mp; 732*10087SSeth.Goldberg@Sun.COM do { 733*10087SSeth.Goldberg@Sun.COM while (bp->b_rptr < bp->b_wptr) { 734*10087SSeth.Goldberg@Sun.COM /* 735*10087SSeth.Goldberg@Sun.COM * Detect an attempt to reset the mouse. Lock out any 736*10087SSeth.Goldberg@Sun.COM * further mouse writes until the reset has completed. 737*10087SSeth.Goldberg@Sun.COM */ 738*10087SSeth.Goldberg@Sun.COM if (*bp->b_rptr == MSERESET) { 7390Sstevel@tonic-gate 740*10087SSeth.Goldberg@Sun.COM /* 741*10087SSeth.Goldberg@Sun.COM * If we couldn't allocate memory and we 742*10087SSeth.Goldberg@Sun.COM * we couldn't register a bufcall, 743*10087SSeth.Goldberg@Sun.COM * mouse8042_process_reset returns 0 and 744*10087SSeth.Goldberg@Sun.COM * has already used the message to send an 745*10087SSeth.Goldberg@Sun.COM * error reply back upstream, so there is no 746*10087SSeth.Goldberg@Sun.COM * need to deallocate or put this message back 747*10087SSeth.Goldberg@Sun.COM * on the queue. 748*10087SSeth.Goldberg@Sun.COM */ 749*10087SSeth.Goldberg@Sun.COM if (mouse8042_process_reset(q, bp, state) == 0) 750*10087SSeth.Goldberg@Sun.COM return (1); 7510Sstevel@tonic-gate 752*10087SSeth.Goldberg@Sun.COM /* 753*10087SSeth.Goldberg@Sun.COM * If there's no data remaining in this block, 754*10087SSeth.Goldberg@Sun.COM * free this block and put the following blocks 755*10087SSeth.Goldberg@Sun.COM * of this message back on the queue. If putting 756*10087SSeth.Goldberg@Sun.COM * the rest of the message back on the queue 757*10087SSeth.Goldberg@Sun.COM * fails, free the the message. 758*10087SSeth.Goldberg@Sun.COM */ 759*10087SSeth.Goldberg@Sun.COM if (MBLKL(bp) == 0) { 760*10087SSeth.Goldberg@Sun.COM next = bp->b_cont; 761*10087SSeth.Goldberg@Sun.COM freeb(bp); 762*10087SSeth.Goldberg@Sun.COM bp = next; 763*10087SSeth.Goldberg@Sun.COM } 764*10087SSeth.Goldberg@Sun.COM if (bp != NULL) { 765*10087SSeth.Goldberg@Sun.COM if (!putbq(q, bp)) 766*10087SSeth.Goldberg@Sun.COM freemsg(bp); 767*10087SSeth.Goldberg@Sun.COM } 768*10087SSeth.Goldberg@Sun.COM 769*10087SSeth.Goldberg@Sun.COM return (1); 770*10087SSeth.Goldberg@Sun.COM 771*10087SSeth.Goldberg@Sun.COM } 772*10087SSeth.Goldberg@Sun.COM ddi_put8(state->ms_handle, 773*10087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_INT_OUTPUT_DATA, 774*10087SSeth.Goldberg@Sun.COM *bp->b_rptr++); 775*10087SSeth.Goldberg@Sun.COM } 776*10087SSeth.Goldberg@Sun.COM next = bp->b_cont; 777*10087SSeth.Goldberg@Sun.COM freeb(bp); 778*10087SSeth.Goldberg@Sun.COM } while ((bp = next) != NULL); 779*10087SSeth.Goldberg@Sun.COM 780*10087SSeth.Goldberg@Sun.COM return (0); 781*10087SSeth.Goldberg@Sun.COM } 782*10087SSeth.Goldberg@Sun.COM 783*10087SSeth.Goldberg@Sun.COM static int 784*10087SSeth.Goldberg@Sun.COM mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state) 785*10087SSeth.Goldberg@Sun.COM { 786*10087SSeth.Goldberg@Sun.COM struct iocblk *iocbp; 787*10087SSeth.Goldberg@Sun.COM int rv = 0; 788*10087SSeth.Goldberg@Sun.COM 7890Sstevel@tonic-gate iocbp = (struct iocblk *)mp->b_rptr; 790*10087SSeth.Goldberg@Sun.COM 7910Sstevel@tonic-gate switch (mp->b_datap->db_type) { 7920Sstevel@tonic-gate case M_FLUSH: 793*10087SSeth.Goldberg@Sun.COM if (*mp->b_rptr & FLUSHW) { 7940Sstevel@tonic-gate flushq(q, FLUSHDATA); 795*10087SSeth.Goldberg@Sun.COM *mp->b_rptr &= ~FLUSHW; 796*10087SSeth.Goldberg@Sun.COM } 797*10087SSeth.Goldberg@Sun.COM if (*mp->b_rptr & FLUSHR) { 798*10087SSeth.Goldberg@Sun.COM qreply(q, mp); 799*10087SSeth.Goldberg@Sun.COM } else 800*10087SSeth.Goldberg@Sun.COM freemsg(mp); 8010Sstevel@tonic-gate break; 8020Sstevel@tonic-gate case M_IOCTL: 8030Sstevel@tonic-gate mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 8040Sstevel@tonic-gate break; 8050Sstevel@tonic-gate case M_IOCDATA: 8060Sstevel@tonic-gate mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 8070Sstevel@tonic-gate break; 8080Sstevel@tonic-gate case M_DATA: 809*10087SSeth.Goldberg@Sun.COM rv = mouse8042_process_data_msg(q, mp, state); 8100Sstevel@tonic-gate break; 8110Sstevel@tonic-gate default: 8120Sstevel@tonic-gate freemsg(mp); 8130Sstevel@tonic-gate break; 8140Sstevel@tonic-gate } 815*10087SSeth.Goldberg@Sun.COM 816*10087SSeth.Goldberg@Sun.COM return (rv); 817*10087SSeth.Goldberg@Sun.COM } 818*10087SSeth.Goldberg@Sun.COM 819*10087SSeth.Goldberg@Sun.COM static int 820*10087SSeth.Goldberg@Sun.COM mouse8042_wsrv(queue_t *qp) 821*10087SSeth.Goldberg@Sun.COM { 822*10087SSeth.Goldberg@Sun.COM mblk_t *mp; 823*10087SSeth.Goldberg@Sun.COM struct mouse_state *state; 824*10087SSeth.Goldberg@Sun.COM state = (struct mouse_state *)qp->q_ptr; 825*10087SSeth.Goldberg@Sun.COM 826*10087SSeth.Goldberg@Sun.COM while ((mp = getq(qp)) != NULL) { 827*10087SSeth.Goldberg@Sun.COM if (mouse8042_process_msg(qp, mp, state) != 0) 828*10087SSeth.Goldberg@Sun.COM break; 829*10087SSeth.Goldberg@Sun.COM } 830*10087SSeth.Goldberg@Sun.COM 831*10087SSeth.Goldberg@Sun.COM return (0); 832*10087SSeth.Goldberg@Sun.COM } 833*10087SSeth.Goldberg@Sun.COM 834*10087SSeth.Goldberg@Sun.COM /* 835*10087SSeth.Goldberg@Sun.COM * Returns the next reset state, given the current state and the byte 836*10087SSeth.Goldberg@Sun.COM * received from the mouse. Error and Resend codes are handled by the 837*10087SSeth.Goldberg@Sun.COM * caller. 838*10087SSeth.Goldberg@Sun.COM */ 839*10087SSeth.Goldberg@Sun.COM static mouse8042_reset_state_e 840*10087SSeth.Goldberg@Sun.COM mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata) 841*10087SSeth.Goldberg@Sun.COM { 842*10087SSeth.Goldberg@Sun.COM switch (reset_state) { 843*10087SSeth.Goldberg@Sun.COM case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */ 844*10087SSeth.Goldberg@Sun.COM if (mdata == MSE_ACK) /* Got the ACK */ 845*10087SSeth.Goldberg@Sun.COM return (MSE_RESET_ACK); 846*10087SSeth.Goldberg@Sun.COM break; 847*10087SSeth.Goldberg@Sun.COM 848*10087SSeth.Goldberg@Sun.COM case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */ 849*10087SSeth.Goldberg@Sun.COM if (mdata == MSE_AA) /* Got the 0xAA */ 850*10087SSeth.Goldberg@Sun.COM return (MSE_RESET_AA); 851*10087SSeth.Goldberg@Sun.COM break; 852*10087SSeth.Goldberg@Sun.COM 853*10087SSeth.Goldberg@Sun.COM case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */ 854*10087SSeth.Goldberg@Sun.COM if (mdata == MSE_00) 855*10087SSeth.Goldberg@Sun.COM return (MSE_RESET_IDLE); 856*10087SSeth.Goldberg@Sun.COM break; 857*10087SSeth.Goldberg@Sun.COM } 858*10087SSeth.Goldberg@Sun.COM 859*10087SSeth.Goldberg@Sun.COM return (reset_state); 8600Sstevel@tonic-gate } 8610Sstevel@tonic-gate 8620Sstevel@tonic-gate static uint_t 8630Sstevel@tonic-gate mouse8042_intr(caddr_t arg) 8640Sstevel@tonic-gate { 8650Sstevel@tonic-gate unsigned char mdata; 8660Sstevel@tonic-gate mblk_t *mp; 8670Sstevel@tonic-gate struct mouse_state *state = (struct mouse_state *)arg; 8680Sstevel@tonic-gate int rc; 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate rc = DDI_INTR_UNCLAIMED; 8730Sstevel@tonic-gate 8740Sstevel@tonic-gate for (;;) { 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate if (ddi_get8(state->ms_handle, 8777656SSherry.Moore@Sun.COM state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) { 8780Sstevel@tonic-gate break; 8790Sstevel@tonic-gate } 8800Sstevel@tonic-gate 8810Sstevel@tonic-gate mdata = ddi_get8(state->ms_handle, 8827656SSherry.Moore@Sun.COM state->ms_addr + I8042_INT_INPUT_DATA); 8830Sstevel@tonic-gate 884*10087SSeth.Goldberg@Sun.COM rc = DDI_INTR_CLAIMED; 885*10087SSeth.Goldberg@Sun.COM 886*10087SSeth.Goldberg@Sun.COM /* 887*10087SSeth.Goldberg@Sun.COM * If we're not ready for this data, discard it. 888*10087SSeth.Goldberg@Sun.COM */ 889*10087SSeth.Goldberg@Sun.COM if (!state->ready) 890*10087SSeth.Goldberg@Sun.COM continue; 891*10087SSeth.Goldberg@Sun.COM 892*10087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex); 893*10087SSeth.Goldberg@Sun.COM if (state->reset_state != MSE_RESET_IDLE) { 894*10087SSeth.Goldberg@Sun.COM 895*10087SSeth.Goldberg@Sun.COM if (mdata == MSEERROR || mdata == MSERESET) { 896*10087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_FAILED; 897*10087SSeth.Goldberg@Sun.COM } else { 898*10087SSeth.Goldberg@Sun.COM state->reset_state = 899*10087SSeth.Goldberg@Sun.COM mouse8042_reset_fsm(state->reset_state, 900*10087SSeth.Goldberg@Sun.COM mdata); 901*10087SSeth.Goldberg@Sun.COM } 902*10087SSeth.Goldberg@Sun.COM 903*10087SSeth.Goldberg@Sun.COM /* 904*10087SSeth.Goldberg@Sun.COM * If we transitioned back to the idle reset state (or 905*10087SSeth.Goldberg@Sun.COM * the reset failed), disable the timeout, release the 906*10087SSeth.Goldberg@Sun.COM * 8042 exclusive-access lock, then send the response 907*10087SSeth.Goldberg@Sun.COM * the the upper-level modules. Finally, enable the 908*10087SSeth.Goldberg@Sun.COM * queue and schedule queue service procedures so that 909*10087SSeth.Goldberg@Sun.COM * upper-level modules can process the response. 910*10087SSeth.Goldberg@Sun.COM * Otherwise, if we're still in the middle of the 911*10087SSeth.Goldberg@Sun.COM * reset sequence, do not send the data up (since the 912*10087SSeth.Goldberg@Sun.COM * response is sent at the end of the sequence, or 913*10087SSeth.Goldberg@Sun.COM * on timeout/error). 914*10087SSeth.Goldberg@Sun.COM */ 915*10087SSeth.Goldberg@Sun.COM if (state->reset_state == MSE_RESET_IDLE || 916*10087SSeth.Goldberg@Sun.COM state->reset_state == MSE_RESET_FAILED) { 9170Sstevel@tonic-gate 918*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 919*10087SSeth.Goldberg@Sun.COM (void) quntimeout(state->ms_wqp, 920*10087SSeth.Goldberg@Sun.COM state->reset_tid); 921*10087SSeth.Goldberg@Sun.COM mutex_enter(&state->reset_mutex); 922*10087SSeth.Goldberg@Sun.COM 923*10087SSeth.Goldberg@Sun.COM (void) ddi_get8(state->ms_handle, 924*10087SSeth.Goldberg@Sun.COM state->ms_addr + I8042_UNLOCK); 925*10087SSeth.Goldberg@Sun.COM 926*10087SSeth.Goldberg@Sun.COM state->reset_tid = 0; 927*10087SSeth.Goldberg@Sun.COM mp = state->reply_mp; 928*10087SSeth.Goldberg@Sun.COM if (state->reset_state == MSE_RESET_FAILED) { 929*10087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = mdata; 930*10087SSeth.Goldberg@Sun.COM } else { 931*10087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_ACK; 932*10087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_AA; 933*10087SSeth.Goldberg@Sun.COM *mp->b_wptr++ = MSE_00; 934*10087SSeth.Goldberg@Sun.COM } 935*10087SSeth.Goldberg@Sun.COM state->reply_mp = NULL; 936*10087SSeth.Goldberg@Sun.COM 937*10087SSeth.Goldberg@Sun.COM state->reset_state = MSE_RESET_IDLE; 938*10087SSeth.Goldberg@Sun.COM 939*10087SSeth.Goldberg@Sun.COM if (state->ms_rqp != NULL) 940*10087SSeth.Goldberg@Sun.COM putnext(state->ms_rqp, mp); 941*10087SSeth.Goldberg@Sun.COM else 942*10087SSeth.Goldberg@Sun.COM freemsg(mp); 943*10087SSeth.Goldberg@Sun.COM 944*10087SSeth.Goldberg@Sun.COM enableok(state->ms_wqp); 945*10087SSeth.Goldberg@Sun.COM qenable(state->ms_wqp); 946*10087SSeth.Goldberg@Sun.COM } 947*10087SSeth.Goldberg@Sun.COM 948*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 949*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->ms_mutex); 950*10087SSeth.Goldberg@Sun.COM return (rc); 951*10087SSeth.Goldberg@Sun.COM } 952*10087SSeth.Goldberg@Sun.COM mutex_exit(&state->reset_mutex); 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) { 9550Sstevel@tonic-gate *mp->b_wptr++ = mdata; 9560Sstevel@tonic-gate putnext(state->ms_rqp, mp); 9570Sstevel@tonic-gate } 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate return (rc); 9620Sstevel@tonic-gate } 963