17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 519397407SSherry Moore * Common Development and Distribution License (the "License"). 619397407SSherry Moore * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 227c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 237c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 2615bfc6b7SSeth Goldberg * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 277c478bd9Sstevel@tonic-gate * Use is subject to license terms. 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate /* 327c478bd9Sstevel@tonic-gate * PS/2 type Mouse Module - Streams 337c478bd9Sstevel@tonic-gate */ 347c478bd9Sstevel@tonic-gate 357c478bd9Sstevel@tonic-gate #include <sys/param.h> 367c478bd9Sstevel@tonic-gate #include <sys/types.h> 377c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 387c478bd9Sstevel@tonic-gate #include <sys/signal.h> 397c478bd9Sstevel@tonic-gate #include <sys/errno.h> 407c478bd9Sstevel@tonic-gate #include <sys/file.h> 417c478bd9Sstevel@tonic-gate #include <sys/termio.h> 427c478bd9Sstevel@tonic-gate #include <sys/stream.h> 437c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 447c478bd9Sstevel@tonic-gate #include <sys/strtty.h> 4515bfc6b7SSeth Goldberg #include <sys/strsun.h> 467c478bd9Sstevel@tonic-gate #include <sys/debug.h> 477c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 487c478bd9Sstevel@tonic-gate #include <sys/stat.h> 497c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 507c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate #include <sys/promif.h> 537c478bd9Sstevel@tonic-gate #include <sys/cred.h> 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate #include <sys/i8042.h> 567c478bd9Sstevel@tonic-gate #include <sys/note.h> 5715bfc6b7SSeth Goldberg #include <sys/mouse.h> 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate #define DRIVER_NAME(dip) ddi_driver_name(dip) 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate #define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1) 627c478bd9Sstevel@tonic-gate #define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2) 637c478bd9Sstevel@tonic-gate #define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1) 647c478bd9Sstevel@tonic-gate 6515bfc6b7SSeth Goldberg #define MOUSE8042_RESET_TIMEOUT_USECS 500000 /* 500 ms */ 6615bfc6b7SSeth Goldberg 677c478bd9Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t); 687c478bd9Sstevel@tonic-gate extern void consconfig_link(major_t major, minor_t minor); 697c478bd9Sstevel@tonic-gate extern int consconfig_unlink(major_t major, minor_t minor); 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate /* 737c478bd9Sstevel@tonic-gate * 747c478bd9Sstevel@tonic-gate * Local Static Data 757c478bd9Sstevel@tonic-gate * 767c478bd9Sstevel@tonic-gate */ 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate /* 797c478bd9Sstevel@tonic-gate * We only support one instance. Yes, it's theoretically possible to 807c478bd9Sstevel@tonic-gate * plug in more than one, but it's not worth the implementation cost. 817c478bd9Sstevel@tonic-gate * 827c478bd9Sstevel@tonic-gate * The introduction of USB keyboards might make it worth reassessing 837c478bd9Sstevel@tonic-gate * this decision, as they might free up the keyboard port for a second 847c478bd9Sstevel@tonic-gate * PS/2 style mouse. 857c478bd9Sstevel@tonic-gate */ 867c478bd9Sstevel@tonic-gate static dev_info_t *mouse8042_dip; 877c478bd9Sstevel@tonic-gate 8815bfc6b7SSeth Goldberg /* 8915bfc6b7SSeth Goldberg * RESET states 9015bfc6b7SSeth Goldberg */ 9115bfc6b7SSeth Goldberg typedef enum { 9215bfc6b7SSeth Goldberg MSE_RESET_IDLE, /* No reset in progress */ 9315bfc6b7SSeth Goldberg MSE_RESET_PRE, /* Send reset, waiting for ACK */ 9415bfc6b7SSeth Goldberg MSE_RESET_ACK, /* Got ACK, waiting for 0xAA */ 9515bfc6b7SSeth Goldberg MSE_RESET_AA, /* Got 0xAA, waiting for 0x00 */ 9615bfc6b7SSeth Goldberg MSE_RESET_FAILED 9715bfc6b7SSeth Goldberg } mouse8042_reset_state_e; 9815bfc6b7SSeth Goldberg 997c478bd9Sstevel@tonic-gate struct mouse_state { 1007c478bd9Sstevel@tonic-gate queue_t *ms_rqp; 1017c478bd9Sstevel@tonic-gate queue_t *ms_wqp; 1027c478bd9Sstevel@tonic-gate ddi_iblock_cookie_t ms_iblock_cookie; 1037c478bd9Sstevel@tonic-gate ddi_acc_handle_t ms_handle; 1047c478bd9Sstevel@tonic-gate uint8_t *ms_addr; 1057c478bd9Sstevel@tonic-gate kmutex_t ms_mutex; 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate minor_t ms_minor; 1087c478bd9Sstevel@tonic-gate boolean_t ms_opened; 10915bfc6b7SSeth Goldberg kmutex_t reset_mutex; 110*5b81b7caSSeth Goldberg kcondvar_t reset_cv; 11115bfc6b7SSeth Goldberg mouse8042_reset_state_e reset_state; 11215bfc6b7SSeth Goldberg timeout_id_t reset_tid; 11315bfc6b7SSeth Goldberg int ready; 11415bfc6b7SSeth Goldberg mblk_t *reply_mp; 115*5b81b7caSSeth Goldberg mblk_t *reset_ack_mp; 11615bfc6b7SSeth Goldberg bufcall_id_t bc_id; 1177c478bd9Sstevel@tonic-gate }; 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate static uint_t mouse8042_intr(caddr_t arg); 1207c478bd9Sstevel@tonic-gate static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag, 1217c478bd9Sstevel@tonic-gate cred_t *cred_p); 1227c478bd9Sstevel@tonic-gate static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p); 12315bfc6b7SSeth Goldberg static int mouse8042_wsrv(queue_t *qp); 124*5b81b7caSSeth Goldberg static int mouse8042_wput(queue_t *q, mblk_t *mp); 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, 1277c478bd9Sstevel@tonic-gate void *arg, void **result); 1287c478bd9Sstevel@tonic-gate static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd); 1297c478bd9Sstevel@tonic-gate static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * Streams module info. 1347c478bd9Sstevel@tonic-gate */ 1357c478bd9Sstevel@tonic-gate #define MODULE_NAME "mouse8042" 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate static struct module_info mouse8042_minfo = { 1387c478bd9Sstevel@tonic-gate 23, /* Module ID number */ 1397c478bd9Sstevel@tonic-gate MODULE_NAME, 1407c478bd9Sstevel@tonic-gate 0, INFPSZ, /* minimum & maximum packet sizes */ 1417c478bd9Sstevel@tonic-gate 256, 128 /* hi and low water marks */ 1427c478bd9Sstevel@tonic-gate }; 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate static struct qinit mouse8042_rinit = { 1457c478bd9Sstevel@tonic-gate NULL, /* put */ 1467c478bd9Sstevel@tonic-gate NULL, /* service */ 1477c478bd9Sstevel@tonic-gate mouse8042_open, 1487c478bd9Sstevel@tonic-gate mouse8042_close, 1497c478bd9Sstevel@tonic-gate NULL, /* admin */ 1507c478bd9Sstevel@tonic-gate &mouse8042_minfo, 1517c478bd9Sstevel@tonic-gate NULL /* statistics */ 1527c478bd9Sstevel@tonic-gate }; 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate static struct qinit mouse8042_winit = { 155*5b81b7caSSeth Goldberg mouse8042_wput, /* put */ 15615bfc6b7SSeth Goldberg mouse8042_wsrv, /* service */ 1577c478bd9Sstevel@tonic-gate NULL, /* open */ 1587c478bd9Sstevel@tonic-gate NULL, /* close */ 1597c478bd9Sstevel@tonic-gate NULL, /* admin */ 1607c478bd9Sstevel@tonic-gate &mouse8042_minfo, 1617c478bd9Sstevel@tonic-gate NULL /* statistics */ 1627c478bd9Sstevel@tonic-gate }; 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate static struct streamtab mouse8042_strinfo = { 1657c478bd9Sstevel@tonic-gate &mouse8042_rinit, 1667c478bd9Sstevel@tonic-gate &mouse8042_winit, 1677c478bd9Sstevel@tonic-gate NULL, /* muxrinit */ 1687c478bd9Sstevel@tonic-gate NULL, /* muxwinit */ 1697c478bd9Sstevel@tonic-gate }; 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate /* 1727c478bd9Sstevel@tonic-gate * Local Function Declarations 1737c478bd9Sstevel@tonic-gate */ 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate static struct cb_ops mouse8042_cb_ops = { 1767c478bd9Sstevel@tonic-gate nodev, /* open */ 1777c478bd9Sstevel@tonic-gate nodev, /* close */ 1787c478bd9Sstevel@tonic-gate nodev, /* strategy */ 1797c478bd9Sstevel@tonic-gate nodev, /* print */ 1807c478bd9Sstevel@tonic-gate nodev, /* dump */ 1817c478bd9Sstevel@tonic-gate nodev, /* read */ 1827c478bd9Sstevel@tonic-gate nodev, /* write */ 1837c478bd9Sstevel@tonic-gate nodev, /* ioctl */ 1847c478bd9Sstevel@tonic-gate nodev, /* devmap */ 1857c478bd9Sstevel@tonic-gate nodev, /* mmap */ 1867c478bd9Sstevel@tonic-gate nodev, /* segmap */ 1877c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 1887c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 1897c478bd9Sstevel@tonic-gate &mouse8042_strinfo, /* streamtab */ 1907c478bd9Sstevel@tonic-gate D_MP | D_NEW 1917c478bd9Sstevel@tonic-gate }; 1927c478bd9Sstevel@tonic-gate 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate static struct dev_ops mouse8042_ops = { 1957c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 1967c478bd9Sstevel@tonic-gate 0, /* refcnt */ 1977c478bd9Sstevel@tonic-gate mouse8042_getinfo, /* getinfo */ 1987c478bd9Sstevel@tonic-gate nulldev, /* identify */ 1997c478bd9Sstevel@tonic-gate nulldev, /* probe */ 2007c478bd9Sstevel@tonic-gate mouse8042_attach, /* attach */ 2017c478bd9Sstevel@tonic-gate mouse8042_detach, /* detach */ 2027c478bd9Sstevel@tonic-gate nodev, /* reset */ 2037c478bd9Sstevel@tonic-gate &mouse8042_cb_ops, /* driver operations */ 20419397407SSherry Moore (struct bus_ops *)0, /* bus operations */ 20519397407SSherry Moore NULL, /* power */ 20619397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 2077c478bd9Sstevel@tonic-gate }; 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate /* 2107c478bd9Sstevel@tonic-gate * This is the loadable module wrapper. 2117c478bd9Sstevel@tonic-gate */ 2127c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops; 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * Module linkage information for the kernel. 2187c478bd9Sstevel@tonic-gate */ 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 2217c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 22219397407SSherry Moore "PS/2 Mouse", 2237c478bd9Sstevel@tonic-gate &mouse8042_ops, /* driver ops */ 2247c478bd9Sstevel@tonic-gate }; 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 2277c478bd9Sstevel@tonic-gate MODREV_1, 2287c478bd9Sstevel@tonic-gate (void *)&modldrv, 2297c478bd9Sstevel@tonic-gate NULL 2307c478bd9Sstevel@tonic-gate }; 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* 2337c478bd9Sstevel@tonic-gate * This is the driver initialization routine. 2347c478bd9Sstevel@tonic-gate */ 2357c478bd9Sstevel@tonic-gate int 2367c478bd9Sstevel@tonic-gate _init() 2377c478bd9Sstevel@tonic-gate { 2387c478bd9Sstevel@tonic-gate int rv; 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate rv = mod_install(&modlinkage); 2417c478bd9Sstevel@tonic-gate return (rv); 2427c478bd9Sstevel@tonic-gate } 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate int 2467c478bd9Sstevel@tonic-gate _fini(void) 2477c478bd9Sstevel@tonic-gate { 2487c478bd9Sstevel@tonic-gate return (mod_remove(&modlinkage)); 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate int 2537c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 2547c478bd9Sstevel@tonic-gate { 2557c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2567c478bd9Sstevel@tonic-gate } 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate static int 2597c478bd9Sstevel@tonic-gate mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2607c478bd9Sstevel@tonic-gate { 2617c478bd9Sstevel@tonic-gate struct mouse_state *state; 262fd9cb95cSsethg mblk_t *mp; 2637c478bd9Sstevel@tonic-gate int instance = ddi_get_instance(dip); 2647c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t attr = { 2657c478bd9Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 2667c478bd9Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 2677c478bd9Sstevel@tonic-gate DDI_STRICTORDER_ACC, 2687c478bd9Sstevel@tonic-gate }; 2697c478bd9Sstevel@tonic-gate int rc; 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate 272fd9cb95cSsethg if (cmd == DDI_RESUME) { 273fd9cb95cSsethg state = (struct mouse_state *)ddi_get_driver_private(dip); 274fd9cb95cSsethg 27515bfc6b7SSeth Goldberg /* Ready to handle inbound data from mouse8042_intr */ 27615bfc6b7SSeth Goldberg state->ready = 1; 27715bfc6b7SSeth Goldberg 278fd9cb95cSsethg /* 279fd9cb95cSsethg * Send a 0xaa 0x00 upstream. 280fd9cb95cSsethg * This causes the vuid module to reset the mouse. 281fd9cb95cSsethg */ 282fd9cb95cSsethg if (state->ms_rqp != NULL) { 283fd9cb95cSsethg if (mp = allocb(1, BPRI_MED)) { 284fd9cb95cSsethg *mp->b_wptr++ = 0xaa; 285fd9cb95cSsethg putnext(state->ms_rqp, mp); 286fd9cb95cSsethg } 287fd9cb95cSsethg if (mp = allocb(1, BPRI_MED)) { 288fd9cb95cSsethg *mp->b_wptr++ = 0x0; 289fd9cb95cSsethg putnext(state->ms_rqp, mp); 290fd9cb95cSsethg } 291fd9cb95cSsethg } 292fd9cb95cSsethg return (DDI_SUCCESS); 293fd9cb95cSsethg } 294fd9cb95cSsethg 2957c478bd9Sstevel@tonic-gate if (cmd != DDI_ATTACH) 2967c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate if (mouse8042_dip != NULL) 2997c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate /* allocate and initialize state structure */ 3027c478bd9Sstevel@tonic-gate state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP); 3037c478bd9Sstevel@tonic-gate state->ms_opened = B_FALSE; 30415bfc6b7SSeth Goldberg state->reset_state = MSE_RESET_IDLE; 30515bfc6b7SSeth Goldberg state->reset_tid = 0; 30615bfc6b7SSeth Goldberg state->bc_id = 0; 3077c478bd9Sstevel@tonic-gate ddi_set_driver_private(dip, state); 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* 3107c478bd9Sstevel@tonic-gate * In order to support virtual keyboard/mouse, we should distinguish 3117c478bd9Sstevel@tonic-gate * between internal virtual open and external physical open. 3127c478bd9Sstevel@tonic-gate * 3137c478bd9Sstevel@tonic-gate * When the physical devices are opened by application, they will 3147c478bd9Sstevel@tonic-gate * be unlinked from the virtual device and their data stream will 3157c478bd9Sstevel@tonic-gate * not be sent to the virtual device. When the opened physical 3167c478bd9Sstevel@tonic-gate * devices are closed, they will be relinked to the virtual devices. 3177c478bd9Sstevel@tonic-gate * 3187c478bd9Sstevel@tonic-gate * All these automatic switch between virtual and physical are 3197c478bd9Sstevel@tonic-gate * transparent. 3207c478bd9Sstevel@tonic-gate * 3217c478bd9Sstevel@tonic-gate * So we change minor node numbering scheme to be: 3227c478bd9Sstevel@tonic-gate * external node minor num == instance * 2 3237c478bd9Sstevel@tonic-gate * internal node minor num == instance * 2 + 1 3247c478bd9Sstevel@tonic-gate */ 325fd9cb95cSsethg rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2, 3267c478bd9Sstevel@tonic-gate DDI_NT_MOUSE, NULL); 3277c478bd9Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3287c478bd9Sstevel@tonic-gate goto fail_1; 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate 3317c478bd9Sstevel@tonic-gate if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR, 3327c478bd9Sstevel@tonic-gate instance * 2 + 1) != DDI_SUCCESS) { 3337c478bd9Sstevel@tonic-gate goto fail_2; 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr, 3377c478bd9Sstevel@tonic-gate (offset_t)0, (offset_t)0, &attr, &state->ms_handle); 3387c478bd9Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3397c478bd9Sstevel@tonic-gate goto fail_2; 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie); 3437c478bd9Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3447c478bd9Sstevel@tonic-gate goto fail_3; 3457c478bd9Sstevel@tonic-gate } 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER, 3487c478bd9Sstevel@tonic-gate state->ms_iblock_cookie); 34915bfc6b7SSeth Goldberg mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER, 35015bfc6b7SSeth Goldberg state->ms_iblock_cookie); 351*5b81b7caSSeth Goldberg cv_init(&state->reset_cv, NULL, CV_DRIVER, NULL); 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate rc = ddi_add_intr(dip, 0, 3547c478bd9Sstevel@tonic-gate (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL, 3557c478bd9Sstevel@tonic-gate mouse8042_intr, (caddr_t)state); 3567c478bd9Sstevel@tonic-gate if (rc != DDI_SUCCESS) { 3577c478bd9Sstevel@tonic-gate goto fail_3; 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate mouse8042_dip = dip; 3617c478bd9Sstevel@tonic-gate 36215bfc6b7SSeth Goldberg /* Ready to handle inbound data from mouse8042_intr */ 36315bfc6b7SSeth Goldberg state->ready = 1; 36415bfc6b7SSeth Goldberg 3657c478bd9Sstevel@tonic-gate /* Now that we're attached, announce our presence to the world. */ 3667c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 3677c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate fail_3: 3707c478bd9Sstevel@tonic-gate ddi_regs_map_free(&state->ms_handle); 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate fail_2: 3737c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate fail_1: 3767c478bd9Sstevel@tonic-gate kmem_free(state, sizeof (struct mouse_state)); 3777c478bd9Sstevel@tonic-gate return (rc); 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 3817c478bd9Sstevel@tonic-gate static int 3827c478bd9Sstevel@tonic-gate mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 3837c478bd9Sstevel@tonic-gate { 3847c478bd9Sstevel@tonic-gate struct mouse_state *state; 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate state = ddi_get_driver_private(dip); 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate switch (cmd) { 389fd9cb95cSsethg case DDI_SUSPEND: 39015bfc6b7SSeth Goldberg /* Ignore all data from mouse8042_intr until we fully resume */ 39115bfc6b7SSeth Goldberg state->ready = 0; 392fd9cb95cSsethg return (DDI_SUCCESS); 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate case DDI_DETACH: 3957c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, 0, state->ms_iblock_cookie); 3967c478bd9Sstevel@tonic-gate mouse8042_dip = NULL; 397*5b81b7caSSeth Goldberg cv_destroy(&state->reset_cv); 39815bfc6b7SSeth Goldberg mutex_destroy(&state->reset_mutex); 3997c478bd9Sstevel@tonic-gate mutex_destroy(&state->ms_mutex); 4007c478bd9Sstevel@tonic-gate ddi_prop_remove_all(dip); 4017c478bd9Sstevel@tonic-gate ddi_regs_map_free(&state->ms_handle); 4027c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 4037c478bd9Sstevel@tonic-gate kmem_free(state, sizeof (struct mouse_state)); 4047c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate default: 4077c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate /* ARGSUSED */ 4137c478bd9Sstevel@tonic-gate static int 4147c478bd9Sstevel@tonic-gate mouse8042_getinfo( 4157c478bd9Sstevel@tonic-gate dev_info_t *dip, 4167c478bd9Sstevel@tonic-gate ddi_info_cmd_t infocmd, 4177c478bd9Sstevel@tonic-gate void *arg, 4187c478bd9Sstevel@tonic-gate void **result) 4197c478bd9Sstevel@tonic-gate { 4207c478bd9Sstevel@tonic-gate dev_t dev = (dev_t)arg; 4217c478bd9Sstevel@tonic-gate minor_t minor = getminor(dev); 4227c478bd9Sstevel@tonic-gate int instance = MOUSE8042_MINOR_TO_INSTANCE(minor); 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate switch (infocmd) { 4257c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 4267c478bd9Sstevel@tonic-gate if (mouse8042_dip == NULL) 4277c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate *result = (void *)mouse8042_dip; 4307c478bd9Sstevel@tonic-gate break; 4317c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 4327c478bd9Sstevel@tonic-gate *result = (void *)(uintptr_t)instance; 4337c478bd9Sstevel@tonic-gate break; 4347c478bd9Sstevel@tonic-gate default: 4357c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 4367c478bd9Sstevel@tonic-gate } 4377c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 4387c478bd9Sstevel@tonic-gate } 4397c478bd9Sstevel@tonic-gate 4407c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4417c478bd9Sstevel@tonic-gate static int 4427c478bd9Sstevel@tonic-gate mouse8042_open( 4437c478bd9Sstevel@tonic-gate queue_t *q, 4447c478bd9Sstevel@tonic-gate dev_t *devp, 4457c478bd9Sstevel@tonic-gate int flag, 4467c478bd9Sstevel@tonic-gate int sflag, 4477c478bd9Sstevel@tonic-gate cred_t *cred_p) 4487c478bd9Sstevel@tonic-gate { 4497c478bd9Sstevel@tonic-gate struct mouse_state *state; 4507c478bd9Sstevel@tonic-gate minor_t minor = getminor(*devp); 4517c478bd9Sstevel@tonic-gate int rval; 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate if (mouse8042_dip == NULL) 4547c478bd9Sstevel@tonic-gate return (ENXIO); 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate state = ddi_get_driver_private(mouse8042_dip); 4577c478bd9Sstevel@tonic-gate 4587c478bd9Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate if (state->ms_opened) { 4617c478bd9Sstevel@tonic-gate /* 4627c478bd9Sstevel@tonic-gate * Exit if the same minor node is already open 4637c478bd9Sstevel@tonic-gate */ 4647c478bd9Sstevel@tonic-gate if (state->ms_minor == minor) { 4657c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 4667c478bd9Sstevel@tonic-gate return (0); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate /* 4707c478bd9Sstevel@tonic-gate * Check whether it is switch between physical and virtual 4717c478bd9Sstevel@tonic-gate * 4727c478bd9Sstevel@tonic-gate * Opening from virtual while the device is being physically 4737c478bd9Sstevel@tonic-gate * opened by an application should not happen. So we ASSERT 4747c478bd9Sstevel@tonic-gate * this in DEBUG version, and return error in the non-DEBUG 4757c478bd9Sstevel@tonic-gate * case. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate ASSERT(!MOUSE8042_INTERNAL_OPEN(minor)); 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate if (MOUSE8042_INTERNAL_OPEN(minor)) { 4807c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 4817c478bd9Sstevel@tonic-gate return (EINVAL); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate /* 4857c478bd9Sstevel@tonic-gate * Opening the physical one while it is being underneath 4867c478bd9Sstevel@tonic-gate * the virtual one. 4877c478bd9Sstevel@tonic-gate * 4887c478bd9Sstevel@tonic-gate * consconfig_unlink is called to unlink this device from 4897c478bd9Sstevel@tonic-gate * the virtual one, thus the old stream serving for this 4907c478bd9Sstevel@tonic-gate * device under the virtual one is closed, and then the 4917c478bd9Sstevel@tonic-gate * lower driver's close routine (here is mouse8042_close) 4927c478bd9Sstevel@tonic-gate * is also called to accomplish the whole stream close. 4937c478bd9Sstevel@tonic-gate * Here we have to drop the lock because mouse8042_close 4947c478bd9Sstevel@tonic-gate * also needs the lock. 4957c478bd9Sstevel@tonic-gate * 4967c478bd9Sstevel@tonic-gate * For mouse, the old stream is: 4977c478bd9Sstevel@tonic-gate * consms->["pushmod"->]"mouse_vp driver" 4987c478bd9Sstevel@tonic-gate * 4997c478bd9Sstevel@tonic-gate * After the consconfig_unlink returns, the old stream is closed 5007c478bd9Sstevel@tonic-gate * and we grab the lock again to reopen this device as normal. 5017c478bd9Sstevel@tonic-gate */ 5027c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * If unlink fails, fail the physical open. 5067c478bd9Sstevel@tonic-gate */ 5077c478bd9Sstevel@tonic-gate if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip), 5087c478bd9Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor))) != 0) { 5097c478bd9Sstevel@tonic-gate return (rval); 5107c478bd9Sstevel@tonic-gate } 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate q->q_ptr = (caddr_t)state; 5177c478bd9Sstevel@tonic-gate WR(q)->q_ptr = (caddr_t)state; 5187c478bd9Sstevel@tonic-gate state->ms_rqp = q; 5197c478bd9Sstevel@tonic-gate state->ms_wqp = WR(q); 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate qprocson(q); 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate state->ms_minor = minor; 5247c478bd9Sstevel@tonic-gate state->ms_opened = B_TRUE; 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate return (0); 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5337c478bd9Sstevel@tonic-gate static int 5347c478bd9Sstevel@tonic-gate mouse8042_close(queue_t *q, int flag, cred_t *cred_p) 5357c478bd9Sstevel@tonic-gate { 5367c478bd9Sstevel@tonic-gate struct mouse_state *state; 5377c478bd9Sstevel@tonic-gate minor_t minor; 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate state = (struct mouse_state *)q->q_ptr; 5407c478bd9Sstevel@tonic-gate 541*5b81b7caSSeth Goldberg /* 542*5b81b7caSSeth Goldberg * Disable queue processing now, so that another reset cannot get in 543*5b81b7caSSeth Goldberg * after we wait for the current reset (if any) to complete. 544*5b81b7caSSeth Goldberg */ 5457c478bd9Sstevel@tonic-gate qprocsoff(q); 5467c478bd9Sstevel@tonic-gate 547*5b81b7caSSeth Goldberg mutex_enter(&state->reset_mutex); 548*5b81b7caSSeth Goldberg while (state->reset_state != MSE_RESET_IDLE) { 549*5b81b7caSSeth Goldberg /* 550*5b81b7caSSeth Goldberg * Waiting for the previous reset to finish is 551*5b81b7caSSeth Goldberg * non-interruptible. Some upper-level clients 552*5b81b7caSSeth Goldberg * cannot deal with EINTR and will not close the 553*5b81b7caSSeth Goldberg * STREAM properly, resulting in failure to reopen it 554*5b81b7caSSeth Goldberg * within the same process. 555*5b81b7caSSeth Goldberg */ 556*5b81b7caSSeth Goldberg cv_wait(&state->reset_cv, &state->reset_mutex); 557*5b81b7caSSeth Goldberg } 558*5b81b7caSSeth Goldberg 55915bfc6b7SSeth Goldberg if (state->reset_tid != 0) { 56015bfc6b7SSeth Goldberg (void) quntimeout(q, state->reset_tid); 56115bfc6b7SSeth Goldberg state->reset_tid = 0; 56215bfc6b7SSeth Goldberg } 563*5b81b7caSSeth Goldberg 56415bfc6b7SSeth Goldberg if (state->reply_mp != NULL) { 56515bfc6b7SSeth Goldberg freemsg(state->reply_mp); 56615bfc6b7SSeth Goldberg state->reply_mp = NULL; 56715bfc6b7SSeth Goldberg } 56815bfc6b7SSeth Goldberg 569*5b81b7caSSeth Goldberg if (state->reset_ack_mp != NULL) { 570*5b81b7caSSeth Goldberg freemsg(state->reset_ack_mp); 571*5b81b7caSSeth Goldberg state->reset_ack_mp = NULL; 572*5b81b7caSSeth Goldberg } 573*5b81b7caSSeth Goldberg 574*5b81b7caSSeth Goldberg mutex_exit(&state->reset_mutex); 575*5b81b7caSSeth Goldberg 576*5b81b7caSSeth Goldberg mutex_enter(&state->ms_mutex); 577*5b81b7caSSeth Goldberg 578*5b81b7caSSeth Goldberg if (state->bc_id != 0) { 579*5b81b7caSSeth Goldberg (void) qunbufcall(q, state->bc_id); 580*5b81b7caSSeth Goldberg state->bc_id = 0; 581*5b81b7caSSeth Goldberg } 582*5b81b7caSSeth Goldberg 5837c478bd9Sstevel@tonic-gate q->q_ptr = NULL; 5847c478bd9Sstevel@tonic-gate WR(q)->q_ptr = NULL; 5857c478bd9Sstevel@tonic-gate state->ms_rqp = NULL; 5867c478bd9Sstevel@tonic-gate state->ms_wqp = NULL; 5877c478bd9Sstevel@tonic-gate 5887c478bd9Sstevel@tonic-gate state->ms_opened = B_FALSE; 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate minor = state->ms_minor; 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate if (!MOUSE8042_INTERNAL_OPEN(minor)) { 5957c478bd9Sstevel@tonic-gate /* 5967c478bd9Sstevel@tonic-gate * Closing physical PS/2 mouse 5977c478bd9Sstevel@tonic-gate * 5987c478bd9Sstevel@tonic-gate * Link it back to virtual mouse, and 5997c478bd9Sstevel@tonic-gate * mouse8042_open will be called as a result 600fd9cb95cSsethg * of the consconfig_link call. Do NOT try 601fd9cb95cSsethg * this if the mouse is about to be detached! 6027c478bd9Sstevel@tonic-gate * 6037c478bd9Sstevel@tonic-gate * If linking back fails, this specific mouse 6047c478bd9Sstevel@tonic-gate * will not be available underneath the virtual 6057c478bd9Sstevel@tonic-gate * mouse, and can only be accessed via physical 6067c478bd9Sstevel@tonic-gate * open. 6077c478bd9Sstevel@tonic-gate */ 6087c478bd9Sstevel@tonic-gate consconfig_link(ddi_driver_major(mouse8042_dip), 6097c478bd9Sstevel@tonic-gate MOUSE8042_INTERNAL_MINOR(minor)); 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate return (0); 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate static void 6167c478bd9Sstevel@tonic-gate mouse8042_iocnack( 6177c478bd9Sstevel@tonic-gate queue_t *qp, 6187c478bd9Sstevel@tonic-gate mblk_t *mp, 6197c478bd9Sstevel@tonic-gate struct iocblk *iocp, 6207c478bd9Sstevel@tonic-gate int error, 6217c478bd9Sstevel@tonic-gate int rval) 6227c478bd9Sstevel@tonic-gate { 6237c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 6247c478bd9Sstevel@tonic-gate iocp->ioc_rval = rval; 6257c478bd9Sstevel@tonic-gate iocp->ioc_error = error; 6267c478bd9Sstevel@tonic-gate qreply(qp, mp); 6277c478bd9Sstevel@tonic-gate } 6287c478bd9Sstevel@tonic-gate 62915bfc6b7SSeth Goldberg static void 63015bfc6b7SSeth Goldberg mouse8042_reset_timeout(void *argp) 6317c478bd9Sstevel@tonic-gate { 63215bfc6b7SSeth Goldberg struct mouse_state *state = (struct mouse_state *)argp; 63315bfc6b7SSeth Goldberg mblk_t *mp; 63415bfc6b7SSeth Goldberg 63515bfc6b7SSeth Goldberg mutex_enter(&state->reset_mutex); 63615bfc6b7SSeth Goldberg 63715bfc6b7SSeth Goldberg /* 63815bfc6b7SSeth Goldberg * If the interrupt handler hasn't completed the reset handling 63915bfc6b7SSeth Goldberg * (reset_state would be IDLE or FAILED in that case), then 64015bfc6b7SSeth Goldberg * drop the 8042 lock, and send a faked retry reply upstream, 64115bfc6b7SSeth Goldberg * then enable the queue for further message processing. 64215bfc6b7SSeth Goldberg */ 64315bfc6b7SSeth Goldberg if (state->reset_state != MSE_RESET_IDLE && 64415bfc6b7SSeth Goldberg state->reset_state != MSE_RESET_FAILED) { 64515bfc6b7SSeth Goldberg 64615bfc6b7SSeth Goldberg state->reset_tid = 0; 64715bfc6b7SSeth Goldberg state->reset_state = MSE_RESET_IDLE; 648*5b81b7caSSeth Goldberg cv_signal(&state->reset_cv); 64915bfc6b7SSeth Goldberg 65015bfc6b7SSeth Goldberg (void) ddi_get8(state->ms_handle, state->ms_addr + 65115bfc6b7SSeth Goldberg I8042_UNLOCK); 65215bfc6b7SSeth Goldberg 65315bfc6b7SSeth Goldberg mp = state->reply_mp; 65415bfc6b7SSeth Goldberg *mp->b_wptr++ = MSERESEND; 65515bfc6b7SSeth Goldberg state->reply_mp = NULL; 65615bfc6b7SSeth Goldberg 65715bfc6b7SSeth Goldberg if (state->ms_rqp != NULL) 65815bfc6b7SSeth Goldberg putnext(state->ms_rqp, mp); 65915bfc6b7SSeth Goldberg else 66015bfc6b7SSeth Goldberg freemsg(mp); 66115bfc6b7SSeth Goldberg 66215bfc6b7SSeth Goldberg ASSERT(state->ms_wqp != NULL); 66315bfc6b7SSeth Goldberg 66415bfc6b7SSeth Goldberg enableok(state->ms_wqp); 66515bfc6b7SSeth Goldberg qenable(state->ms_wqp); 66615bfc6b7SSeth Goldberg } 66715bfc6b7SSeth Goldberg 66815bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 66915bfc6b7SSeth Goldberg } 67015bfc6b7SSeth Goldberg 67115bfc6b7SSeth Goldberg /* 67215bfc6b7SSeth Goldberg * Returns 1 if the caller should put the message (bp) back on the queue 67315bfc6b7SSeth Goldberg */ 67415bfc6b7SSeth Goldberg static int 675*5b81b7caSSeth Goldberg mouse8042_initiate_reset(queue_t *q, mblk_t *mp, struct mouse_state *state) 67615bfc6b7SSeth Goldberg { 67715bfc6b7SSeth Goldberg mutex_enter(&state->reset_mutex); 67815bfc6b7SSeth Goldberg /* 67915bfc6b7SSeth Goldberg * If we're in the middle of a reset, put the message back on the queue 68015bfc6b7SSeth Goldberg * for processing later. 68115bfc6b7SSeth Goldberg */ 68215bfc6b7SSeth Goldberg if (state->reset_state != MSE_RESET_IDLE) { 68315bfc6b7SSeth Goldberg /* 68415bfc6b7SSeth Goldberg * We noenable the queue again here in case it was backenabled 68515bfc6b7SSeth Goldberg * by an upper-level module. 68615bfc6b7SSeth Goldberg */ 68715bfc6b7SSeth Goldberg noenable(q); 68815bfc6b7SSeth Goldberg 68915bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 69015bfc6b7SSeth Goldberg return (1); 69115bfc6b7SSeth Goldberg } 69215bfc6b7SSeth Goldberg 69315bfc6b7SSeth Goldberg /* 69415bfc6b7SSeth Goldberg * Drop the reset state lock before allocating the response message and 69515bfc6b7SSeth Goldberg * grabbing the 8042 exclusive-access lock (since those operations 69615bfc6b7SSeth Goldberg * may take an extended period of time to complete). 69715bfc6b7SSeth Goldberg */ 69815bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 69915bfc6b7SSeth Goldberg 700*5b81b7caSSeth Goldberg if (state->reply_mp == NULL) 701*5b81b7caSSeth Goldberg state->reply_mp = allocb(2, BPRI_MED); 702*5b81b7caSSeth Goldberg if (state->reset_ack_mp == NULL) 703*5b81b7caSSeth Goldberg state->reset_ack_mp = allocb(1, BPRI_MED); 704*5b81b7caSSeth Goldberg 705*5b81b7caSSeth Goldberg if (state->reply_mp == NULL || state->reset_ack_mp == NULL) { 70615bfc6b7SSeth Goldberg /* 70715bfc6b7SSeth Goldberg * Allocation failed -- set up a bufcall to enable the queue 70815bfc6b7SSeth Goldberg * whenever there is enough memory to allocate the response 70915bfc6b7SSeth Goldberg * message. 71015bfc6b7SSeth Goldberg */ 711*5b81b7caSSeth Goldberg state->bc_id = qbufcall(q, (state->reply_mp == NULL) ? 2 : 1, 712*5b81b7caSSeth Goldberg BPRI_MED, (void (*)(void *))qenable, q); 71315bfc6b7SSeth Goldberg 71415bfc6b7SSeth Goldberg if (state->bc_id == 0) { 71515bfc6b7SSeth Goldberg /* 71615bfc6b7SSeth Goldberg * If the qbufcall failed, we cannot proceed, so use the 71715bfc6b7SSeth Goldberg * message we were sent to respond with an error. 71815bfc6b7SSeth Goldberg */ 71915bfc6b7SSeth Goldberg *mp->b_rptr = MSEERROR; 72015bfc6b7SSeth Goldberg mp->b_wptr = mp->b_rptr + 1; 72115bfc6b7SSeth Goldberg qreply(q, mp); 72215bfc6b7SSeth Goldberg return (0); 72315bfc6b7SSeth Goldberg } 72415bfc6b7SSeth Goldberg 72515bfc6b7SSeth Goldberg return (1); 726*5b81b7caSSeth Goldberg } else { 727*5b81b7caSSeth Goldberg /* Bufcall completed successfully (or wasn't needed) */ 728*5b81b7caSSeth Goldberg state->bc_id = 0; 72915bfc6b7SSeth Goldberg } 73015bfc6b7SSeth Goldberg 73115bfc6b7SSeth Goldberg /* 73215bfc6b7SSeth Goldberg * Gain exclusive access to the 8042 for the duration of the reset. 73315bfc6b7SSeth Goldberg * The unlock will occur when the reset has either completed or timed 73415bfc6b7SSeth Goldberg * out. 73515bfc6b7SSeth Goldberg */ 73615bfc6b7SSeth Goldberg (void) ddi_get8(state->ms_handle, 73715bfc6b7SSeth Goldberg state->ms_addr + I8042_LOCK); 73815bfc6b7SSeth Goldberg 73915bfc6b7SSeth Goldberg mutex_enter(&state->reset_mutex); 74015bfc6b7SSeth Goldberg 74115bfc6b7SSeth Goldberg state->reset_state = MSE_RESET_PRE; 74215bfc6b7SSeth Goldberg noenable(q); 74315bfc6b7SSeth Goldberg 74415bfc6b7SSeth Goldberg state->reset_tid = qtimeout(q, 74515bfc6b7SSeth Goldberg mouse8042_reset_timeout, 74615bfc6b7SSeth Goldberg state, 74715bfc6b7SSeth Goldberg drv_usectohz( 74815bfc6b7SSeth Goldberg MOUSE8042_RESET_TIMEOUT_USECS)); 74915bfc6b7SSeth Goldberg 75015bfc6b7SSeth Goldberg ddi_put8(state->ms_handle, 75115bfc6b7SSeth Goldberg state->ms_addr + 75215bfc6b7SSeth Goldberg I8042_INT_OUTPUT_DATA, MSERESET); 75315bfc6b7SSeth Goldberg 75415bfc6b7SSeth Goldberg mp->b_rptr++; 75515bfc6b7SSeth Goldberg 75615bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 75715bfc6b7SSeth Goldberg return (1); 75815bfc6b7SSeth Goldberg } 75915bfc6b7SSeth Goldberg 76015bfc6b7SSeth Goldberg /* 76115bfc6b7SSeth Goldberg * Returns 1 if the caller should stop processing messages 76215bfc6b7SSeth Goldberg */ 76315bfc6b7SSeth Goldberg static int 76415bfc6b7SSeth Goldberg mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state) 76515bfc6b7SSeth Goldberg { 7667c478bd9Sstevel@tonic-gate mblk_t *bp; 7677c478bd9Sstevel@tonic-gate mblk_t *next; 7687c478bd9Sstevel@tonic-gate 7697c478bd9Sstevel@tonic-gate bp = mp; 7707c478bd9Sstevel@tonic-gate do { 7717c478bd9Sstevel@tonic-gate while (bp->b_rptr < bp->b_wptr) { 77215bfc6b7SSeth Goldberg /* 77315bfc6b7SSeth Goldberg * Detect an attempt to reset the mouse. Lock out any 77415bfc6b7SSeth Goldberg * further mouse writes until the reset has completed. 77515bfc6b7SSeth Goldberg */ 77615bfc6b7SSeth Goldberg if (*bp->b_rptr == MSERESET) { 77715bfc6b7SSeth Goldberg 77815bfc6b7SSeth Goldberg /* 77915bfc6b7SSeth Goldberg * If we couldn't allocate memory and we 78015bfc6b7SSeth Goldberg * we couldn't register a bufcall, 781*5b81b7caSSeth Goldberg * mouse8042_initiate_reset returns 0 and 78215bfc6b7SSeth Goldberg * has already used the message to send an 78315bfc6b7SSeth Goldberg * error reply back upstream, so there is no 78415bfc6b7SSeth Goldberg * need to deallocate or put this message back 78515bfc6b7SSeth Goldberg * on the queue. 78615bfc6b7SSeth Goldberg */ 787*5b81b7caSSeth Goldberg if (mouse8042_initiate_reset(q, bp, state) == 0) 78815bfc6b7SSeth Goldberg return (1); 78915bfc6b7SSeth Goldberg 79015bfc6b7SSeth Goldberg /* 79115bfc6b7SSeth Goldberg * If there's no data remaining in this block, 79215bfc6b7SSeth Goldberg * free this block and put the following blocks 79315bfc6b7SSeth Goldberg * of this message back on the queue. If putting 79415bfc6b7SSeth Goldberg * the rest of the message back on the queue 79515bfc6b7SSeth Goldberg * fails, free the the message. 79615bfc6b7SSeth Goldberg */ 79715bfc6b7SSeth Goldberg if (MBLKL(bp) == 0) { 79815bfc6b7SSeth Goldberg next = bp->b_cont; 79915bfc6b7SSeth Goldberg freeb(bp); 80015bfc6b7SSeth Goldberg bp = next; 8017c478bd9Sstevel@tonic-gate } 80215bfc6b7SSeth Goldberg if (bp != NULL) { 80315bfc6b7SSeth Goldberg if (!putbq(q, bp)) 80415bfc6b7SSeth Goldberg freemsg(bp); 8057c478bd9Sstevel@tonic-gate } 80615bfc6b7SSeth Goldberg 80715bfc6b7SSeth Goldberg return (1); 80815bfc6b7SSeth Goldberg 80915bfc6b7SSeth Goldberg } 8107c478bd9Sstevel@tonic-gate ddi_put8(state->ms_handle, 8117c478bd9Sstevel@tonic-gate state->ms_addr + I8042_INT_OUTPUT_DATA, 8127c478bd9Sstevel@tonic-gate *bp->b_rptr++); 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate next = bp->b_cont; 8157c478bd9Sstevel@tonic-gate freeb(bp); 8167c478bd9Sstevel@tonic-gate } while ((bp = next) != NULL); 81715bfc6b7SSeth Goldberg 81815bfc6b7SSeth Goldberg return (0); 81915bfc6b7SSeth Goldberg } 82015bfc6b7SSeth Goldberg 82115bfc6b7SSeth Goldberg static int 82215bfc6b7SSeth Goldberg mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state) 82315bfc6b7SSeth Goldberg { 82415bfc6b7SSeth Goldberg struct iocblk *iocbp; 82515bfc6b7SSeth Goldberg int rv = 0; 82615bfc6b7SSeth Goldberg 82715bfc6b7SSeth Goldberg iocbp = (struct iocblk *)mp->b_rptr; 82815bfc6b7SSeth Goldberg 82915bfc6b7SSeth Goldberg switch (mp->b_datap->db_type) { 83015bfc6b7SSeth Goldberg case M_FLUSH: 83115bfc6b7SSeth Goldberg if (*mp->b_rptr & FLUSHW) { 83215bfc6b7SSeth Goldberg flushq(q, FLUSHDATA); 83315bfc6b7SSeth Goldberg *mp->b_rptr &= ~FLUSHW; 83415bfc6b7SSeth Goldberg } 83515bfc6b7SSeth Goldberg if (*mp->b_rptr & FLUSHR) { 83615bfc6b7SSeth Goldberg qreply(q, mp); 83715bfc6b7SSeth Goldberg } else 83815bfc6b7SSeth Goldberg freemsg(mp); 83915bfc6b7SSeth Goldberg break; 84015bfc6b7SSeth Goldberg case M_IOCTL: 84115bfc6b7SSeth Goldberg mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 84215bfc6b7SSeth Goldberg break; 84315bfc6b7SSeth Goldberg case M_IOCDATA: 84415bfc6b7SSeth Goldberg mouse8042_iocnack(q, mp, iocbp, EINVAL, 0); 84515bfc6b7SSeth Goldberg break; 84615bfc6b7SSeth Goldberg case M_DATA: 84715bfc6b7SSeth Goldberg rv = mouse8042_process_data_msg(q, mp, state); 8487c478bd9Sstevel@tonic-gate break; 8497c478bd9Sstevel@tonic-gate default: 8507c478bd9Sstevel@tonic-gate freemsg(mp); 8517c478bd9Sstevel@tonic-gate break; 8527c478bd9Sstevel@tonic-gate } 85315bfc6b7SSeth Goldberg 85415bfc6b7SSeth Goldberg return (rv); 85515bfc6b7SSeth Goldberg } 85615bfc6b7SSeth Goldberg 857*5b81b7caSSeth Goldberg /* 858*5b81b7caSSeth Goldberg * This is the main mouse input routine. Commands and parameters 859*5b81b7caSSeth Goldberg * from upstream are sent to the mouse device immediately, unless 860*5b81b7caSSeth Goldberg * the mouse is in the process of being reset, in which case 861*5b81b7caSSeth Goldberg * commands are queued and executed later in the service procedure. 862*5b81b7caSSeth Goldberg */ 863*5b81b7caSSeth Goldberg static int 864*5b81b7caSSeth Goldberg mouse8042_wput(queue_t *q, mblk_t *mp) 865*5b81b7caSSeth Goldberg { 866*5b81b7caSSeth Goldberg struct mouse_state *state; 867*5b81b7caSSeth Goldberg state = (struct mouse_state *)q->q_ptr; 868*5b81b7caSSeth Goldberg 869*5b81b7caSSeth Goldberg /* 870*5b81b7caSSeth Goldberg * Process all messages immediately, unless a reset is in 871*5b81b7caSSeth Goldberg * progress. If a reset is in progress, deflect processing to 872*5b81b7caSSeth Goldberg * the service procedure. 873*5b81b7caSSeth Goldberg */ 874*5b81b7caSSeth Goldberg if (state->reset_state != MSE_RESET_IDLE) 875*5b81b7caSSeth Goldberg return (putq(q, mp)); 876*5b81b7caSSeth Goldberg 877*5b81b7caSSeth Goldberg /* 878*5b81b7caSSeth Goldberg * If there are still messages outstanding in the queue that 879*5b81b7caSSeth Goldberg * the service procedure hasn't processed yet, put this 880*5b81b7caSSeth Goldberg * message in the queue also, to ensure proper message 881*5b81b7caSSeth Goldberg * ordering. 882*5b81b7caSSeth Goldberg */ 883*5b81b7caSSeth Goldberg if (q->q_first) 884*5b81b7caSSeth Goldberg return (putq(q, mp)); 885*5b81b7caSSeth Goldberg 886*5b81b7caSSeth Goldberg (void) mouse8042_process_msg(q, mp, state); 887*5b81b7caSSeth Goldberg 888*5b81b7caSSeth Goldberg return (0); 889*5b81b7caSSeth Goldberg } 890*5b81b7caSSeth Goldberg 89115bfc6b7SSeth Goldberg static int 89215bfc6b7SSeth Goldberg mouse8042_wsrv(queue_t *qp) 89315bfc6b7SSeth Goldberg { 89415bfc6b7SSeth Goldberg mblk_t *mp; 89515bfc6b7SSeth Goldberg struct mouse_state *state; 89615bfc6b7SSeth Goldberg state = (struct mouse_state *)qp->q_ptr; 89715bfc6b7SSeth Goldberg 89815bfc6b7SSeth Goldberg while ((mp = getq(qp)) != NULL) { 89915bfc6b7SSeth Goldberg if (mouse8042_process_msg(qp, mp, state) != 0) 90015bfc6b7SSeth Goldberg break; 90115bfc6b7SSeth Goldberg } 90215bfc6b7SSeth Goldberg 90315bfc6b7SSeth Goldberg return (0); 90415bfc6b7SSeth Goldberg } 90515bfc6b7SSeth Goldberg 90615bfc6b7SSeth Goldberg /* 90715bfc6b7SSeth Goldberg * Returns the next reset state, given the current state and the byte 90815bfc6b7SSeth Goldberg * received from the mouse. Error and Resend codes are handled by the 90915bfc6b7SSeth Goldberg * caller. 91015bfc6b7SSeth Goldberg */ 91115bfc6b7SSeth Goldberg static mouse8042_reset_state_e 91215bfc6b7SSeth Goldberg mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata) 91315bfc6b7SSeth Goldberg { 91415bfc6b7SSeth Goldberg switch (reset_state) { 91515bfc6b7SSeth Goldberg case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */ 91615bfc6b7SSeth Goldberg if (mdata == MSE_ACK) /* Got the ACK */ 91715bfc6b7SSeth Goldberg return (MSE_RESET_ACK); 91815bfc6b7SSeth Goldberg break; 91915bfc6b7SSeth Goldberg 92015bfc6b7SSeth Goldberg case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */ 92115bfc6b7SSeth Goldberg if (mdata == MSE_AA) /* Got the 0xAA */ 92215bfc6b7SSeth Goldberg return (MSE_RESET_AA); 92315bfc6b7SSeth Goldberg break; 92415bfc6b7SSeth Goldberg 92515bfc6b7SSeth Goldberg case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */ 92615bfc6b7SSeth Goldberg if (mdata == MSE_00) 92715bfc6b7SSeth Goldberg return (MSE_RESET_IDLE); 92815bfc6b7SSeth Goldberg break; 92915bfc6b7SSeth Goldberg } 93015bfc6b7SSeth Goldberg 93115bfc6b7SSeth Goldberg return (reset_state); 9327c478bd9Sstevel@tonic-gate } 9337c478bd9Sstevel@tonic-gate 9347c478bd9Sstevel@tonic-gate static uint_t 9357c478bd9Sstevel@tonic-gate mouse8042_intr(caddr_t arg) 9367c478bd9Sstevel@tonic-gate { 9377c478bd9Sstevel@tonic-gate unsigned char mdata; 9387c478bd9Sstevel@tonic-gate mblk_t *mp; 9397c478bd9Sstevel@tonic-gate struct mouse_state *state = (struct mouse_state *)arg; 9407c478bd9Sstevel@tonic-gate int rc; 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate mutex_enter(&state->ms_mutex); 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate rc = DDI_INTR_UNCLAIMED; 9457c478bd9Sstevel@tonic-gate 9467c478bd9Sstevel@tonic-gate for (;;) { 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate if (ddi_get8(state->ms_handle, 9497c478bd9Sstevel@tonic-gate state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) { 9507c478bd9Sstevel@tonic-gate break; 9517c478bd9Sstevel@tonic-gate } 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate mdata = ddi_get8(state->ms_handle, 9547c478bd9Sstevel@tonic-gate state->ms_addr + I8042_INT_INPUT_DATA); 9557c478bd9Sstevel@tonic-gate 9567c478bd9Sstevel@tonic-gate rc = DDI_INTR_CLAIMED; 9577c478bd9Sstevel@tonic-gate 95815bfc6b7SSeth Goldberg /* 95915bfc6b7SSeth Goldberg * If we're not ready for this data, discard it. 96015bfc6b7SSeth Goldberg */ 96115bfc6b7SSeth Goldberg if (!state->ready) 96215bfc6b7SSeth Goldberg continue; 96315bfc6b7SSeth Goldberg 96415bfc6b7SSeth Goldberg mutex_enter(&state->reset_mutex); 96515bfc6b7SSeth Goldberg if (state->reset_state != MSE_RESET_IDLE) { 96615bfc6b7SSeth Goldberg 96715bfc6b7SSeth Goldberg if (mdata == MSEERROR || mdata == MSERESET) { 96815bfc6b7SSeth Goldberg state->reset_state = MSE_RESET_FAILED; 96915bfc6b7SSeth Goldberg } else { 97015bfc6b7SSeth Goldberg state->reset_state = 97115bfc6b7SSeth Goldberg mouse8042_reset_fsm(state->reset_state, 97215bfc6b7SSeth Goldberg mdata); 97315bfc6b7SSeth Goldberg } 97415bfc6b7SSeth Goldberg 975*5b81b7caSSeth Goldberg if (state->reset_state == MSE_RESET_ACK) { 976*5b81b7caSSeth Goldberg 977*5b81b7caSSeth Goldberg /* 978*5b81b7caSSeth Goldberg * We received an ACK from the mouse, so 979*5b81b7caSSeth Goldberg * send it upstream immediately so that 980*5b81b7caSSeth Goldberg * consumers depending on the immediate 981*5b81b7caSSeth Goldberg * ACK don't time out. 982*5b81b7caSSeth Goldberg */ 983*5b81b7caSSeth Goldberg if (state->reset_ack_mp != NULL) { 984*5b81b7caSSeth Goldberg 985*5b81b7caSSeth Goldberg mp = state->reset_ack_mp; 986*5b81b7caSSeth Goldberg 987*5b81b7caSSeth Goldberg state->reset_ack_mp = NULL; 988*5b81b7caSSeth Goldberg 989*5b81b7caSSeth Goldberg if (state->ms_rqp != NULL) { 990*5b81b7caSSeth Goldberg *mp->b_wptr++ = MSE_ACK; 991*5b81b7caSSeth Goldberg putnext(state->ms_rqp, mp); 992*5b81b7caSSeth Goldberg } else 993*5b81b7caSSeth Goldberg freemsg(mp); 994*5b81b7caSSeth Goldberg } 995*5b81b7caSSeth Goldberg 996*5b81b7caSSeth Goldberg if (state->ms_wqp != NULL) { 997*5b81b7caSSeth Goldberg enableok(state->ms_wqp); 998*5b81b7caSSeth Goldberg qenable(state->ms_wqp); 999*5b81b7caSSeth Goldberg } 1000*5b81b7caSSeth Goldberg 1001*5b81b7caSSeth Goldberg } else if (state->reset_state == MSE_RESET_IDLE || 1002*5b81b7caSSeth Goldberg state->reset_state == MSE_RESET_FAILED) { 1003*5b81b7caSSeth Goldberg 100415bfc6b7SSeth Goldberg /* 100515bfc6b7SSeth Goldberg * If we transitioned back to the idle reset state (or 100615bfc6b7SSeth Goldberg * the reset failed), disable the timeout, release the 100715bfc6b7SSeth Goldberg * 8042 exclusive-access lock, then send the response 100815bfc6b7SSeth Goldberg * the the upper-level modules. Finally, enable the 100915bfc6b7SSeth Goldberg * queue and schedule queue service procedures so that 101015bfc6b7SSeth Goldberg * upper-level modules can process the response. 101115bfc6b7SSeth Goldberg * Otherwise, if we're still in the middle of the 101215bfc6b7SSeth Goldberg * reset sequence, do not send the data up (since the 101315bfc6b7SSeth Goldberg * response is sent at the end of the sequence, or 101415bfc6b7SSeth Goldberg * on timeout/error). 101515bfc6b7SSeth Goldberg */ 101615bfc6b7SSeth Goldberg 101715bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 101815bfc6b7SSeth Goldberg (void) quntimeout(state->ms_wqp, 101915bfc6b7SSeth Goldberg state->reset_tid); 102015bfc6b7SSeth Goldberg mutex_enter(&state->reset_mutex); 102115bfc6b7SSeth Goldberg 102215bfc6b7SSeth Goldberg (void) ddi_get8(state->ms_handle, 102315bfc6b7SSeth Goldberg state->ms_addr + I8042_UNLOCK); 102415bfc6b7SSeth Goldberg 102515bfc6b7SSeth Goldberg state->reset_tid = 0; 1026*5b81b7caSSeth Goldberg if (state->reply_mp != NULL) { 102715bfc6b7SSeth Goldberg mp = state->reply_mp; 1028*5b81b7caSSeth Goldberg if (state->reset_state == 1029*5b81b7caSSeth Goldberg MSE_RESET_FAILED) { 103015bfc6b7SSeth Goldberg *mp->b_wptr++ = mdata; 103115bfc6b7SSeth Goldberg } else { 103215bfc6b7SSeth Goldberg *mp->b_wptr++ = MSE_AA; 103315bfc6b7SSeth Goldberg *mp->b_wptr++ = MSE_00; 103415bfc6b7SSeth Goldberg } 103515bfc6b7SSeth Goldberg state->reply_mp = NULL; 1036*5b81b7caSSeth Goldberg } else { 1037*5b81b7caSSeth Goldberg mp = NULL; 1038*5b81b7caSSeth Goldberg } 103915bfc6b7SSeth Goldberg 104015bfc6b7SSeth Goldberg state->reset_state = MSE_RESET_IDLE; 1041*5b81b7caSSeth Goldberg cv_signal(&state->reset_cv); 104215bfc6b7SSeth Goldberg 1043*5b81b7caSSeth Goldberg if (mp != NULL) { 104415bfc6b7SSeth Goldberg if (state->ms_rqp != NULL) 104515bfc6b7SSeth Goldberg putnext(state->ms_rqp, mp); 104615bfc6b7SSeth Goldberg else 104715bfc6b7SSeth Goldberg freemsg(mp); 1048*5b81b7caSSeth Goldberg } 104915bfc6b7SSeth Goldberg 1050*5b81b7caSSeth Goldberg if (state->ms_wqp != NULL) { 105115bfc6b7SSeth Goldberg enableok(state->ms_wqp); 105215bfc6b7SSeth Goldberg qenable(state->ms_wqp); 105315bfc6b7SSeth Goldberg } 1054*5b81b7caSSeth Goldberg } 105515bfc6b7SSeth Goldberg 105615bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 105715bfc6b7SSeth Goldberg mutex_exit(&state->ms_mutex); 105815bfc6b7SSeth Goldberg return (rc); 105915bfc6b7SSeth Goldberg } 106015bfc6b7SSeth Goldberg mutex_exit(&state->reset_mutex); 106115bfc6b7SSeth Goldberg 10627c478bd9Sstevel@tonic-gate if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) { 10637c478bd9Sstevel@tonic-gate *mp->b_wptr++ = mdata; 10647c478bd9Sstevel@tonic-gate putnext(state->ms_rqp, mp); 10657c478bd9Sstevel@tonic-gate } 10667c478bd9Sstevel@tonic-gate } 10677c478bd9Sstevel@tonic-gate mutex_exit(&state->ms_mutex); 10687c478bd9Sstevel@tonic-gate 10697c478bd9Sstevel@tonic-gate return (rc); 10707c478bd9Sstevel@tonic-gate } 1071