1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * sun4v console driver 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <sys/errno.h> 34*0Sstevel@tonic-gate #include <sys/stat.h> 35*0Sstevel@tonic-gate #include <sys/kmem.h> 36*0Sstevel@tonic-gate #include <sys/conf.h> 37*0Sstevel@tonic-gate #include <sys/termios.h> 38*0Sstevel@tonic-gate #include <sys/modctl.h> 39*0Sstevel@tonic-gate #include <sys/kbio.h> 40*0Sstevel@tonic-gate #include <sys/stropts.h> 41*0Sstevel@tonic-gate #include <sys/stream.h> 42*0Sstevel@tonic-gate #include <sys/strsun.h> 43*0Sstevel@tonic-gate #include <sys/sysmacros.h> 44*0Sstevel@tonic-gate #include <sys/promif.h> 45*0Sstevel@tonic-gate #include <sys/ddi.h> 46*0Sstevel@tonic-gate #include <sys/sunddi.h> 47*0Sstevel@tonic-gate #include <sys/cyclic.h> 48*0Sstevel@tonic-gate #include <sys/intr.h> 49*0Sstevel@tonic-gate #include <sys/spl.h> 50*0Sstevel@tonic-gate #include <sys/qcn.h> 51*0Sstevel@tonic-gate #include <sys/hypervisor_api.h> 52*0Sstevel@tonic-gate 53*0Sstevel@tonic-gate /* dev_ops and cb_ops for device driver */ 54*0Sstevel@tonic-gate static int qcn_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 55*0Sstevel@tonic-gate static int qcn_attach(dev_info_t *, ddi_attach_cmd_t); 56*0Sstevel@tonic-gate static int qcn_detach(dev_info_t *, ddi_detach_cmd_t); 57*0Sstevel@tonic-gate static int qcn_open(queue_t *, dev_t *, int, int, cred_t *); 58*0Sstevel@tonic-gate static int qcn_close(queue_t *, int, cred_t *); 59*0Sstevel@tonic-gate static int qcn_wput(queue_t *, mblk_t *); 60*0Sstevel@tonic-gate static int qcn_wsrv(queue_t *); 61*0Sstevel@tonic-gate static int qcn_rsrv(queue_t *); 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gate /* other internal qcn routines */ 64*0Sstevel@tonic-gate static void qcn_ioctl(queue_t *, mblk_t *); 65*0Sstevel@tonic-gate static void qcn_reioctl(void *); 66*0Sstevel@tonic-gate static void qcn_ack(mblk_t *, mblk_t *, uint_t); 67*0Sstevel@tonic-gate static void qcn_start(void); 68*0Sstevel@tonic-gate static int qcn_transmit(queue_t *, mblk_t *); 69*0Sstevel@tonic-gate static void qcn_flush(void); 70*0Sstevel@tonic-gate static uint_t qcn_hi_intr(caddr_t arg); 71*0Sstevel@tonic-gate static uint_t qcn_soft_intr(caddr_t arg1, caddr_t arg2); 72*0Sstevel@tonic-gate 73*0Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t); 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate static qcn_t *qcn_state; 76*0Sstevel@tonic-gate static uchar_t qcn_stopped = B_FALSE; 77*0Sstevel@tonic-gate static int qcn_timeout_period = 20; /* time out in seconds */ 78*0Sstevel@tonic-gate size_t qcn_input_dropped; /* dropped input character counter */ 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate #ifdef QCN_POLLING 81*0Sstevel@tonic-gate static void qcn_poll_handler(void *unused); 82*0Sstevel@tonic-gate static cyc_time_t qcn_poll_time; 83*0Sstevel@tonic-gate static cyc_handler_t qcn_poll_cychandler = { 84*0Sstevel@tonic-gate qcn_poll_handler, 85*0Sstevel@tonic-gate NULL, 86*0Sstevel@tonic-gate CY_LOW_LEVEL /* XXX need softint to make this high */ 87*0Sstevel@tonic-gate }; 88*0Sstevel@tonic-gate static cyclic_id_t qcn_poll_cycid = CYCLIC_NONE; 89*0Sstevel@tonic-gate static uint64_t qcn_poll_interval = 5; /* milli sec */ 90*0Sstevel@tonic-gate static uint64_t sb_interval = 0; 91*0Sstevel@tonic-gate #endif 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate #define QCN_MI_IDNUM 0xABCE 94*0Sstevel@tonic-gate #define QCN_MI_HIWAT 8192 95*0Sstevel@tonic-gate #define QCN_MI_LOWAT 128 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate /* streams structures */ 98*0Sstevel@tonic-gate static struct module_info minfo = { 99*0Sstevel@tonic-gate QCN_MI_IDNUM, /* mi_idnum */ 100*0Sstevel@tonic-gate "qcn", /* mi_idname */ 101*0Sstevel@tonic-gate 0, /* mi_minpsz */ 102*0Sstevel@tonic-gate INFPSZ, /* mi_maxpsz */ 103*0Sstevel@tonic-gate QCN_MI_HIWAT, /* mi_hiwat */ 104*0Sstevel@tonic-gate QCN_MI_LOWAT /* mi_lowat */ 105*0Sstevel@tonic-gate }; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate static struct qinit rinit = { 108*0Sstevel@tonic-gate putq, /* qi_putp */ 109*0Sstevel@tonic-gate qcn_rsrv, /* qi_srvp */ 110*0Sstevel@tonic-gate qcn_open, /* qi_qopen */ 111*0Sstevel@tonic-gate qcn_close, /* qi_qclose */ 112*0Sstevel@tonic-gate NULL, /* qi_qadmin */ 113*0Sstevel@tonic-gate &minfo, /* qi_minfo */ 114*0Sstevel@tonic-gate NULL /* qi_mstat */ 115*0Sstevel@tonic-gate }; 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate static struct qinit winit = { 118*0Sstevel@tonic-gate qcn_wput, /* qi_putp */ 119*0Sstevel@tonic-gate qcn_wsrv, /* qi_srvp */ 120*0Sstevel@tonic-gate qcn_open, /* qi_qopen */ 121*0Sstevel@tonic-gate qcn_close, /* qi_qclose */ 122*0Sstevel@tonic-gate NULL, /* qi_qadmin */ 123*0Sstevel@tonic-gate &minfo, /* qi_minfo */ 124*0Sstevel@tonic-gate NULL /* qi_mstat */ 125*0Sstevel@tonic-gate }; 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate static struct streamtab qcnstrinfo = { 128*0Sstevel@tonic-gate &rinit, 129*0Sstevel@tonic-gate &winit, 130*0Sstevel@tonic-gate NULL, 131*0Sstevel@tonic-gate NULL 132*0Sstevel@tonic-gate }; 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate /* standard device driver structures */ 135*0Sstevel@tonic-gate static struct cb_ops qcn_cb_ops = { 136*0Sstevel@tonic-gate nulldev, /* open() */ 137*0Sstevel@tonic-gate nulldev, /* close() */ 138*0Sstevel@tonic-gate nodev, /* strategy() */ 139*0Sstevel@tonic-gate nodev, /* print() */ 140*0Sstevel@tonic-gate nodev, /* dump() */ 141*0Sstevel@tonic-gate nodev, /* read() */ 142*0Sstevel@tonic-gate nodev, /* write() */ 143*0Sstevel@tonic-gate nodev, /* ioctl() */ 144*0Sstevel@tonic-gate nodev, /* devmap() */ 145*0Sstevel@tonic-gate nodev, /* mmap() */ 146*0Sstevel@tonic-gate nodev, /* segmap() */ 147*0Sstevel@tonic-gate nochpoll, /* poll() */ 148*0Sstevel@tonic-gate ddi_prop_op, /* prop_op() */ 149*0Sstevel@tonic-gate &qcnstrinfo, /* cb_str */ 150*0Sstevel@tonic-gate D_NEW | D_MP /* cb_flag */ 151*0Sstevel@tonic-gate }; 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gate static struct dev_ops qcn_ops = { 154*0Sstevel@tonic-gate DEVO_REV, 155*0Sstevel@tonic-gate 0, /* refcnt */ 156*0Sstevel@tonic-gate qcn_getinfo, /* getinfo() */ 157*0Sstevel@tonic-gate nulldev, /* identify() */ 158*0Sstevel@tonic-gate nulldev, /* probe() */ 159*0Sstevel@tonic-gate qcn_attach, /* attach() */ 160*0Sstevel@tonic-gate qcn_detach, /* detach() */ 161*0Sstevel@tonic-gate nodev, /* reset() */ 162*0Sstevel@tonic-gate &qcn_cb_ops, /* cb_ops */ 163*0Sstevel@tonic-gate (struct bus_ops *)NULL, /* bus_ops */ 164*0Sstevel@tonic-gate NULL /* power() */ 165*0Sstevel@tonic-gate }; 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate static struct modldrv modldrv = { 168*0Sstevel@tonic-gate &mod_driverops, 169*0Sstevel@tonic-gate "sun4v console driver v%I%", 170*0Sstevel@tonic-gate &qcn_ops 171*0Sstevel@tonic-gate }; 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 174*0Sstevel@tonic-gate MODREV_1, 175*0Sstevel@tonic-gate (void*)&modldrv, 176*0Sstevel@tonic-gate NULL 177*0Sstevel@tonic-gate }; 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate /* driver configuration routines */ 181*0Sstevel@tonic-gate int 182*0Sstevel@tonic-gate _init(void) 183*0Sstevel@tonic-gate { 184*0Sstevel@tonic-gate int error; 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate qcn_state = kmem_zalloc(sizeof (qcn_t), KM_SLEEP); 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate error = mod_install(&modlinkage); 189*0Sstevel@tonic-gate if (error != 0) 190*0Sstevel@tonic-gate kmem_free(qcn_state, sizeof (qcn_t)); 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate return (error); 193*0Sstevel@tonic-gate } 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate int 196*0Sstevel@tonic-gate _fini(void) 197*0Sstevel@tonic-gate { 198*0Sstevel@tonic-gate /* can't remove console driver */ 199*0Sstevel@tonic-gate return (EBUSY); 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate int 203*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 204*0Sstevel@tonic-gate { 205*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate static int 209*0Sstevel@tonic-gate qcn_add_intrs(void) 210*0Sstevel@tonic-gate { 211*0Sstevel@tonic-gate dev_info_t *devinfo = qcn_state->qcn_dip; 212*0Sstevel@tonic-gate int actual, count = 0; 213*0Sstevel@tonic-gate int x, y, rc, inum = 0; 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate /* get number of interrupts */ 217*0Sstevel@tonic-gate rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &count); 218*0Sstevel@tonic-gate if ((rc != DDI_SUCCESS) || (count == 0)) { 219*0Sstevel@tonic-gate return (DDI_FAILURE); 220*0Sstevel@tonic-gate } 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gate /* Allocate an array of interrupt handles */ 223*0Sstevel@tonic-gate qcn_state->qcn_intr_size = count * sizeof (ddi_intr_handle_t); 224*0Sstevel@tonic-gate qcn_state->qcn_htable = kmem_zalloc(qcn_state->qcn_intr_size, KM_SLEEP); 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate /* call ddi_intr_alloc() */ 227*0Sstevel@tonic-gate rc = ddi_intr_alloc(devinfo, qcn_state->qcn_htable, 228*0Sstevel@tonic-gate DDI_INTR_TYPE_FIXED, inum, count, &actual, 229*0Sstevel@tonic-gate DDI_INTR_ALLOC_STRICT); 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate if ((rc != DDI_SUCCESS) || (actual == 0)) { 232*0Sstevel@tonic-gate kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 233*0Sstevel@tonic-gate return (DDI_FAILURE); 234*0Sstevel@tonic-gate } 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate if (actual < count) { 237*0Sstevel@tonic-gate for (x = 0; x < actual; x++) { 238*0Sstevel@tonic-gate (void) ddi_intr_free(qcn_state->qcn_htable[x]); 239*0Sstevel@tonic-gate } 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 242*0Sstevel@tonic-gate return (DDI_FAILURE); 243*0Sstevel@tonic-gate } 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gate qcn_state->qcn_intr_cnt = actual; 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate /* Get intr priority */ 248*0Sstevel@tonic-gate if (ddi_intr_get_pri(qcn_state->qcn_htable[0], 249*0Sstevel@tonic-gate &qcn_state->qcn_intr_pri) != DDI_SUCCESS) { 250*0Sstevel@tonic-gate for (x = 0; x < actual; x++) { 251*0Sstevel@tonic-gate (void) ddi_intr_free(qcn_state->qcn_htable[x]); 252*0Sstevel@tonic-gate } 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate kmem_free(qcn_state->qcn_htable, qcn_state->qcn_intr_size); 255*0Sstevel@tonic-gate return (DDI_FAILURE); 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate /* Call ddi_intr_add_handler() */ 259*0Sstevel@tonic-gate for (x = 0; x < actual; x++) { 260*0Sstevel@tonic-gate if (ddi_intr_add_handler(qcn_state->qcn_htable[x], 261*0Sstevel@tonic-gate (ddi_intr_handler_t *)qcn_hi_intr, 262*0Sstevel@tonic-gate (caddr_t)qcn_state, NULL) != DDI_SUCCESS) { 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate for (y = 0; y < x; y++) { 265*0Sstevel@tonic-gate (void) ddi_intr_remove_handler( 266*0Sstevel@tonic-gate qcn_state->qcn_htable[y]); 267*0Sstevel@tonic-gate } 268*0Sstevel@tonic-gate 269*0Sstevel@tonic-gate for (y = 0; y < actual; y++) { 270*0Sstevel@tonic-gate (void) ddi_intr_free(qcn_state->qcn_htable[y]); 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate kmem_free(qcn_state->qcn_htable, 274*0Sstevel@tonic-gate qcn_state->qcn_intr_size); 275*0Sstevel@tonic-gate return (DDI_FAILURE); 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate return (DDI_SUCCESS); 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gate static void 283*0Sstevel@tonic-gate qcn_remove_intrs(void) 284*0Sstevel@tonic-gate { 285*0Sstevel@tonic-gate int x; 286*0Sstevel@tonic-gate for (x = 0; x < qcn_state->qcn_intr_cnt; x++) { 287*0Sstevel@tonic-gate (void) ddi_intr_disable(qcn_state->qcn_htable[x]); 288*0Sstevel@tonic-gate (void) ddi_intr_remove_handler(qcn_state->qcn_htable[x]); 289*0Sstevel@tonic-gate (void) ddi_intr_free(qcn_state->qcn_htable[x]); 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate static void 294*0Sstevel@tonic-gate qcn_intr_enable(void) 295*0Sstevel@tonic-gate { 296*0Sstevel@tonic-gate int x; 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate for (x = 0; x < qcn_state->qcn_intr_cnt; x++) { 299*0Sstevel@tonic-gate (void) ddi_intr_enable(qcn_state->qcn_htable[x]); 300*0Sstevel@tonic-gate } 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate 303*0Sstevel@tonic-gate /* 304*0Sstevel@tonic-gate * qcn_attach is called at startup time. 305*0Sstevel@tonic-gate * There is only once instance of this driver. 306*0Sstevel@tonic-gate */ 307*0Sstevel@tonic-gate static int 308*0Sstevel@tonic-gate qcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 309*0Sstevel@tonic-gate { 310*0Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *, char *, 311*0Sstevel@tonic-gate int, minor_t); 312*0Sstevel@tonic-gate uint_t soft_prip; 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate #ifdef QCN_POLLING 315*0Sstevel@tonic-gate char *binding_name; 316*0Sstevel@tonic-gate #endif 317*0Sstevel@tonic-gate if (cmd != DDI_ATTACH) 318*0Sstevel@tonic-gate return (DDI_FAILURE); 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate if (ddi_create_internal_pathname(dip, "qcn", 321*0Sstevel@tonic-gate S_IFCHR, 0) != DDI_SUCCESS) 322*0Sstevel@tonic-gate return (DDI_FAILURE); 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate qcn_state->qcn_soft_pend = 0; 325*0Sstevel@tonic-gate qcn_state->qcn_hangup = 0; 326*0Sstevel@tonic-gate qcn_state->qcn_rbuf_overflow = 0; 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate /* prepare some data structures in soft state */ 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate qcn_state->qcn_dip = dip; 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate qcn_state->qcn_polling = 0; 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate #ifdef QCN_POLLING 335*0Sstevel@tonic-gate /* 336*0Sstevel@tonic-gate * This test is for the sole purposes of allowing 337*0Sstevel@tonic-gate * the console to work on older firmware releases. 338*0Sstevel@tonic-gate */ 339*0Sstevel@tonic-gate binding_name = ddi_binding_name(qcn_state->qcn_dip); 340*0Sstevel@tonic-gate if (strcmp(binding_name, "qcn") == 0) 341*0Sstevel@tonic-gate qcn_state->qcn_polling = 1; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate if (qcn_state->qcn_polling) { 344*0Sstevel@tonic-gate qcn_poll_time.cyt_when = 0ull; 345*0Sstevel@tonic-gate qcn_poll_time.cyt_interval = 346*0Sstevel@tonic-gate qcn_poll_interval * 1000ull * 1000ull; 347*0Sstevel@tonic-gate mutex_enter(&cpu_lock); 348*0Sstevel@tonic-gate qcn_poll_cycid = cyclic_add(&qcn_poll_cychandler, 349*0Sstevel@tonic-gate &qcn_poll_time); 350*0Sstevel@tonic-gate mutex_exit(&cpu_lock); 351*0Sstevel@tonic-gate } 352*0Sstevel@tonic-gate #endif 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate if (!qcn_state->qcn_polling) { 355*0Sstevel@tonic-gate if (qcn_add_intrs() != DDI_SUCCESS) { 356*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_attach: add_intr failed\n"); 357*0Sstevel@tonic-gate return (DDI_FAILURE); 358*0Sstevel@tonic-gate } 359*0Sstevel@tonic-gate if (ddi_intr_add_softint(dip, &qcn_state->qcn_softint_hdl, 360*0Sstevel@tonic-gate DDI_INTR_SOFTPRI_MAX, qcn_soft_intr, 361*0Sstevel@tonic-gate (caddr_t)qcn_state) != DDI_SUCCESS) { 362*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_attach: add_soft_intr failed\n"); 363*0Sstevel@tonic-gate qcn_remove_intrs(); 364*0Sstevel@tonic-gate return (DDI_FAILURE); 365*0Sstevel@tonic-gate } 366*0Sstevel@tonic-gate if (ddi_intr_get_softint_pri(qcn_state->qcn_softint_hdl, 367*0Sstevel@tonic-gate &soft_prip) != DDI_SUCCESS) { 368*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_attach: softint_pri failed\n"); 369*0Sstevel@tonic-gate qcn_remove_intrs(); 370*0Sstevel@tonic-gate return (DDI_FAILURE); 371*0Sstevel@tonic-gate } 372*0Sstevel@tonic-gate qcn_state->qcn_soft_pri = 373*0Sstevel@tonic-gate (ddi_iblock_cookie_t)(uint64_t)soft_prip; 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate mutex_init(&qcn_state->qcn_hi_lock, NULL, MUTEX_DRIVER, 376*0Sstevel@tonic-gate (void *)(qcn_state->qcn_intr_pri)); 377*0Sstevel@tonic-gate mutex_init(&qcn_state->qcn_softlock, NULL, MUTEX_DRIVER, 378*0Sstevel@tonic-gate (void *)(qcn_state->qcn_soft_pri)); 379*0Sstevel@tonic-gate } 380*0Sstevel@tonic-gate 381*0Sstevel@tonic-gate mutex_init(&qcn_state->qcn_lock, NULL, MUTEX_DRIVER, NULL); 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate /* 384*0Sstevel@tonic-gate * Enable interrupts 385*0Sstevel@tonic-gate */ 386*0Sstevel@tonic-gate if (!qcn_state->qcn_polling) { 387*0Sstevel@tonic-gate qcn_intr_enable(); 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate #ifdef QCN_DEBUG 390*0Sstevel@tonic-gate prom_printf("qcn_attach(): qcn driver attached\n"); 391*0Sstevel@tonic-gate #endif 392*0Sstevel@tonic-gate 393*0Sstevel@tonic-gate return (DDI_SUCCESS); 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate } 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate /* ARGSUSED */ 398*0Sstevel@tonic-gate static int 399*0Sstevel@tonic-gate qcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 400*0Sstevel@tonic-gate { 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate if (cmd != DDI_DETACH) 403*0Sstevel@tonic-gate return (DDI_FAILURE); 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate #ifdef QCN_DEBUG 407*0Sstevel@tonic-gate prom_printf("qcn_detach(): QCN driver detached\n"); 408*0Sstevel@tonic-gate #endif 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate #ifdef QCN_POLLING 411*0Sstevel@tonic-gate if (qcn_state->qcn_polling) { 412*0Sstevel@tonic-gate mutex_enter(&cpu_lock); 413*0Sstevel@tonic-gate if (qcn_poll_cycid != CYCLIC_NONE) 414*0Sstevel@tonic-gate cyclic_remove(qcn_poll_cycid); 415*0Sstevel@tonic-gate qcn_poll_cycid = CYCLIC_NONE; 416*0Sstevel@tonic-gate mutex_exit(&cpu_lock); 417*0Sstevel@tonic-gate } 418*0Sstevel@tonic-gate #endif 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate if (!qcn_state->qcn_polling) 421*0Sstevel@tonic-gate qcn_remove_intrs(); 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate return (DDI_SUCCESS); 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gate /* ARGSUSED */ 427*0Sstevel@tonic-gate static int 428*0Sstevel@tonic-gate qcn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 429*0Sstevel@tonic-gate { 430*0Sstevel@tonic-gate int error = DDI_FAILURE; 431*0Sstevel@tonic-gate int instance = 0; 432*0Sstevel@tonic-gate switch (infocmd) { 433*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 434*0Sstevel@tonic-gate if (qcn_state) { 435*0Sstevel@tonic-gate #ifdef QCN_DEBUG 436*0Sstevel@tonic-gate prom_printf("qcn_getinfo(): devt2dip %lx\n", arg); 437*0Sstevel@tonic-gate #endif 438*0Sstevel@tonic-gate *result = (void *)qcn_state->qcn_dip; 439*0Sstevel@tonic-gate error = DDI_SUCCESS; 440*0Sstevel@tonic-gate } 441*0Sstevel@tonic-gate break; 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 444*0Sstevel@tonic-gate #ifdef QCN_DEBUG 445*0Sstevel@tonic-gate prom_printf("qcn_getinfo(): devt2instance %lx\n", arg); 446*0Sstevel@tonic-gate #endif 447*0Sstevel@tonic-gate if (getminor((dev_t)arg) == 0) { 448*0Sstevel@tonic-gate *result = (void *)instance; 449*0Sstevel@tonic-gate error = DDI_SUCCESS; 450*0Sstevel@tonic-gate } 451*0Sstevel@tonic-gate break; 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gate return (error); 455*0Sstevel@tonic-gate } 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate /* streams open & close */ 458*0Sstevel@tonic-gate /* ARGSUSED */ 459*0Sstevel@tonic-gate static int 460*0Sstevel@tonic-gate qcn_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp) 461*0Sstevel@tonic-gate { 462*0Sstevel@tonic-gate tty_common_t *tty; 463*0Sstevel@tonic-gate int unit = getminor(*devp); 464*0Sstevel@tonic-gate 465*0Sstevel@tonic-gate #ifdef QCN_DEBUG 466*0Sstevel@tonic-gate prom_printf("qcn_open(): minor %x\n", unit); 467*0Sstevel@tonic-gate #endif 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate if (unit != 0) 470*0Sstevel@tonic-gate return (ENXIO); 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate /* stream already open */ 473*0Sstevel@tonic-gate if (q->q_ptr != NULL) 474*0Sstevel@tonic-gate return (DDI_SUCCESS); 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate if (!qcn_state) { 477*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_open: console was not configured by " 478*0Sstevel@tonic-gate "autoconfig\n"); 479*0Sstevel@tonic-gate return (ENXIO); 480*0Sstevel@tonic-gate } 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 483*0Sstevel@tonic-gate tty = &(qcn_state->qcn_tty); 484*0Sstevel@tonic-gate 485*0Sstevel@tonic-gate tty->t_readq = q; 486*0Sstevel@tonic-gate tty->t_writeq = WR(q); 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate /* Link the RD and WR Q's */ 489*0Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = (caddr_t)qcn_state; 490*0Sstevel@tonic-gate qcn_state->qcn_readq = RD(q); 491*0Sstevel@tonic-gate qcn_state->qcn_writeq = WR(q); 492*0Sstevel@tonic-gate qprocson(q); 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate #ifdef QCN_DEBUG 497*0Sstevel@tonic-gate prom_printf("qcn_open: opened as dev %lx\n", *devp); 498*0Sstevel@tonic-gate #endif 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate return (DDI_SUCCESS); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate /* ARGSUSED */ 504*0Sstevel@tonic-gate static int 505*0Sstevel@tonic-gate qcn_close(queue_t *q, int flag, cred_t *credp) 506*0Sstevel@tonic-gate { 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate ASSERT(qcn_state == q->q_ptr); 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate if (qcn_state->qcn_wbufcid != 0) { 511*0Sstevel@tonic-gate unbufcall(qcn_state->qcn_wbufcid); 512*0Sstevel@tonic-gate } 513*0Sstevel@tonic-gate ttycommon_close(&qcn_state->qcn_tty); 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate qprocsoff(q); 516*0Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = NULL; 517*0Sstevel@tonic-gate qcn_state->qcn_readq = NULL; 518*0Sstevel@tonic-gate qcn_state->qcn_writeq = NULL; 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate return (DDI_SUCCESS); 521*0Sstevel@tonic-gate } 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate /* 524*0Sstevel@tonic-gate * Put procedure for write queue. 525*0Sstevel@tonic-gate * Respond to M_IOCTL, M_DATA and M_FLUSH messages here; 526*0Sstevel@tonic-gate * It put's the data onto internal qcn_output_q. 527*0Sstevel@tonic-gate */ 528*0Sstevel@tonic-gate static int 529*0Sstevel@tonic-gate qcn_wput(queue_t *q, mblk_t *mp) 530*0Sstevel@tonic-gate { 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate #ifdef QCN_DEBUG 533*0Sstevel@tonic-gate struct iocblk *iocp; 534*0Sstevel@tonic-gate int i; 535*0Sstevel@tonic-gate #endif 536*0Sstevel@tonic-gate 537*0Sstevel@tonic-gate ASSERT(qcn_state == q->q_ptr); 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate if (!mp->b_datap) { 540*0Sstevel@tonic-gate cmn_err(CE_PANIC, "qcn_wput: null datap"); 541*0Sstevel@tonic-gate } 542*0Sstevel@tonic-gate 543*0Sstevel@tonic-gate #ifdef QCN_DEBUG 544*0Sstevel@tonic-gate prom_printf("qcn_wput(): QCN wput q=%X mp=%X rd=%X wr=%X type=%X\n", 545*0Sstevel@tonic-gate q, mp, mp->b_rptr, mp->b_wptr, mp->b_datap->db_type); 546*0Sstevel@tonic-gate #endif 547*0Sstevel@tonic-gate 548*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate switch (mp->b_datap->db_type) { 551*0Sstevel@tonic-gate case M_IOCTL: 552*0Sstevel@tonic-gate case M_CTL: 553*0Sstevel@tonic-gate #ifdef QCN_DEBUG 554*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 555*0Sstevel@tonic-gate prom_printf("qcn_wput(): M_IOCTL cmd=%X TIOC=%X\n", 556*0Sstevel@tonic-gate iocp->ioc_cmd, TIOC); 557*0Sstevel@tonic-gate #endif 558*0Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 559*0Sstevel@tonic-gate case TCSETSW: 560*0Sstevel@tonic-gate case TCSETSF: 561*0Sstevel@tonic-gate case TCSETAW: 562*0Sstevel@tonic-gate case TCSETAF: 563*0Sstevel@tonic-gate case TCSBRK: 564*0Sstevel@tonic-gate /* 565*0Sstevel@tonic-gate * The change do not take effect until all 566*0Sstevel@tonic-gate * output queued before them is drained. 567*0Sstevel@tonic-gate * Put this message on the queue, so that 568*0Sstevel@tonic-gate * "qcn_start" will see it when it's done 569*0Sstevel@tonic-gate * with the output before it. Poke the start 570*0Sstevel@tonic-gate * routine, just in case. 571*0Sstevel@tonic-gate */ 572*0Sstevel@tonic-gate (void) putq(q, mp); 573*0Sstevel@tonic-gate qcn_start(); 574*0Sstevel@tonic-gate break; 575*0Sstevel@tonic-gate default: 576*0Sstevel@tonic-gate qcn_ioctl(q, mp); 577*0Sstevel@tonic-gate } 578*0Sstevel@tonic-gate break; 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate case M_FLUSH: 581*0Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) { 582*0Sstevel@tonic-gate flushq(q, FLUSHDATA); 583*0Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; 584*0Sstevel@tonic-gate } 585*0Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) { 586*0Sstevel@tonic-gate flushq(RD(q), FLUSHDATA); 587*0Sstevel@tonic-gate qreply(q, mp); 588*0Sstevel@tonic-gate } else { 589*0Sstevel@tonic-gate freemsg(mp); 590*0Sstevel@tonic-gate } 591*0Sstevel@tonic-gate break; 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate case M_STOP: 594*0Sstevel@tonic-gate qcn_stopped = B_TRUE; 595*0Sstevel@tonic-gate freemsg(mp); 596*0Sstevel@tonic-gate break; 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate case M_START: 599*0Sstevel@tonic-gate qcn_stopped = B_FALSE; 600*0Sstevel@tonic-gate freemsg(mp); 601*0Sstevel@tonic-gate qenable(q); /* Start up delayed messages */ 602*0Sstevel@tonic-gate break; 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate case M_DATA: 605*0Sstevel@tonic-gate /* 606*0Sstevel@tonic-gate * Queue the message up to be transmitted, 607*0Sstevel@tonic-gate * and poke the start routine. 608*0Sstevel@tonic-gate */ 609*0Sstevel@tonic-gate #ifdef QCN_DEBUG 610*0Sstevel@tonic-gate if (mp->b_rptr < mp->b_wptr) { 611*0Sstevel@tonic-gate prom_printf("qcn_wput(): DATA q=%X mp=%X rd=%X wr=%X\n", 612*0Sstevel@tonic-gate q, mp, mp->b_rptr, mp->b_wptr); 613*0Sstevel@tonic-gate prom_printf("qcn_wput(): ["); 614*0Sstevel@tonic-gate for (i = 0; i < mp->b_wptr-mp->b_rptr; i++) { 615*0Sstevel@tonic-gate prom_printf("%c", *(mp->b_rptr+i)); 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate prom_printf("]\n"); 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate #endif /* QCN_DEBUG */ 620*0Sstevel@tonic-gate (void) putq(q, mp); 621*0Sstevel@tonic-gate qcn_start(); 622*0Sstevel@tonic-gate break; 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gate default: 625*0Sstevel@tonic-gate freemsg(mp); 626*0Sstevel@tonic-gate } 627*0Sstevel@tonic-gate 628*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 629*0Sstevel@tonic-gate return (0); 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate /* 633*0Sstevel@tonic-gate * Process an "ioctl" message sent down to us. 634*0Sstevel@tonic-gate */ 635*0Sstevel@tonic-gate static void 636*0Sstevel@tonic-gate qcn_ioctl(queue_t *q, mblk_t *mp) 637*0Sstevel@tonic-gate { 638*0Sstevel@tonic-gate struct iocblk *iocp; 639*0Sstevel@tonic-gate tty_common_t *tty; 640*0Sstevel@tonic-gate mblk_t *datamp; 641*0Sstevel@tonic-gate int data_size; 642*0Sstevel@tonic-gate int error = 0; 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate #ifdef QCN_DEBUG 645*0Sstevel@tonic-gate prom_printf("qcn_ioctl(): q=%X mp=%X\n", q, mp); 646*0Sstevel@tonic-gate #endif 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 649*0Sstevel@tonic-gate tty = &(qcn_state->qcn_tty); 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate if (tty->t_iocpending != NULL) { 652*0Sstevel@tonic-gate freemsg(tty->t_iocpending); 653*0Sstevel@tonic-gate tty->t_iocpending = NULL; 654*0Sstevel@tonic-gate } 655*0Sstevel@tonic-gate data_size = ttycommon_ioctl(tty, q, mp, &error); 656*0Sstevel@tonic-gate if (data_size != 0) { 657*0Sstevel@tonic-gate if (qcn_state->qcn_wbufcid) 658*0Sstevel@tonic-gate unbufcall(qcn_state->qcn_wbufcid); 659*0Sstevel@tonic-gate /* call qcn_reioctl() */ 660*0Sstevel@tonic-gate qcn_state->qcn_wbufcid = 661*0Sstevel@tonic-gate bufcall(data_size, BPRI_HI, qcn_reioctl, qcn_state); 662*0Sstevel@tonic-gate return; 663*0Sstevel@tonic-gate } 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate if (error < 0) { 666*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 667*0Sstevel@tonic-gate /* 668*0Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here. 669*0Sstevel@tonic-gate */ 670*0Sstevel@tonic-gate error = 0; 671*0Sstevel@tonic-gate switch (iocp->ioc_cmd) { 672*0Sstevel@tonic-gate case TCSBRK: 673*0Sstevel@tonic-gate case TIOCSBRK: 674*0Sstevel@tonic-gate case TIOCCBRK: 675*0Sstevel@tonic-gate case TIOCMSET: 676*0Sstevel@tonic-gate case TIOCMBIS: 677*0Sstevel@tonic-gate case TIOCMBIC: 678*0Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) 679*0Sstevel@tonic-gate qcn_ack(mp, NULL, 0); 680*0Sstevel@tonic-gate else 681*0Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL); 682*0Sstevel@tonic-gate break; 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate case TIOCMGET: 685*0Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED); 686*0Sstevel@tonic-gate if (datamp == NULL) { 687*0Sstevel@tonic-gate error = EAGAIN; 688*0Sstevel@tonic-gate break; 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate *(int *)datamp->b_rptr = 0; 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) 694*0Sstevel@tonic-gate qcn_ack(mp, datamp, sizeof (int)); 695*0Sstevel@tonic-gate else 696*0Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (int), NULL, datamp); 697*0Sstevel@tonic-gate break; 698*0Sstevel@tonic-gate 699*0Sstevel@tonic-gate default: 700*0Sstevel@tonic-gate error = EINVAL; 701*0Sstevel@tonic-gate break; 702*0Sstevel@tonic-gate } 703*0Sstevel@tonic-gate } 704*0Sstevel@tonic-gate if (error != 0) { 705*0Sstevel@tonic-gate iocp->ioc_count = 0; 706*0Sstevel@tonic-gate iocp->ioc_error = error; 707*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 708*0Sstevel@tonic-gate } 709*0Sstevel@tonic-gate qreply(q, mp); 710*0Sstevel@tonic-gate } 711*0Sstevel@tonic-gate 712*0Sstevel@tonic-gate static void 713*0Sstevel@tonic-gate qcn_reioctl(void *unit) 714*0Sstevel@tonic-gate { 715*0Sstevel@tonic-gate queue_t *q; 716*0Sstevel@tonic-gate mblk_t *mp; 717*0Sstevel@tonic-gate qcn_t *qcnp = (qcn_t *)unit; 718*0Sstevel@tonic-gate 719*0Sstevel@tonic-gate if (!qcnp->qcn_wbufcid) 720*0Sstevel@tonic-gate return; 721*0Sstevel@tonic-gate 722*0Sstevel@tonic-gate qcnp->qcn_wbufcid = 0; 723*0Sstevel@tonic-gate if ((q = qcnp->qcn_tty.t_writeq) == NULL) 724*0Sstevel@tonic-gate return; 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate if ((mp = qcnp->qcn_tty.t_iocpending) == NULL) 727*0Sstevel@tonic-gate return; 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate qcnp->qcn_tty.t_iocpending = NULL; 730*0Sstevel@tonic-gate qcn_ioctl(q, mp); 731*0Sstevel@tonic-gate } 732*0Sstevel@tonic-gate 733*0Sstevel@tonic-gate static void 734*0Sstevel@tonic-gate qcn_ack(mblk_t *mp, mblk_t *dp, uint_t size) 735*0Sstevel@tonic-gate { 736*0Sstevel@tonic-gate struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 737*0Sstevel@tonic-gate 738*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 739*0Sstevel@tonic-gate iocp->ioc_count = size; 740*0Sstevel@tonic-gate iocp->ioc_error = 0; 741*0Sstevel@tonic-gate iocp->ioc_rval = 0; 742*0Sstevel@tonic-gate if (mp->b_cont != NULL) 743*0Sstevel@tonic-gate freeb(mp->b_cont); 744*0Sstevel@tonic-gate if (dp != NULL) { 745*0Sstevel@tonic-gate mp->b_cont = dp; 746*0Sstevel@tonic-gate dp->b_wptr += size; 747*0Sstevel@tonic-gate } else 748*0Sstevel@tonic-gate mp->b_cont = NULL; 749*0Sstevel@tonic-gate } 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate static void 752*0Sstevel@tonic-gate qcn_start(void) 753*0Sstevel@tonic-gate { 754*0Sstevel@tonic-gate 755*0Sstevel@tonic-gate queue_t *q; 756*0Sstevel@tonic-gate mblk_t *mp; 757*0Sstevel@tonic-gate int rv; 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&qcn_state->qcn_lock)); 760*0Sstevel@tonic-gate 761*0Sstevel@tonic-gate /* 762*0Sstevel@tonic-gate * read stream queue and remove data from the queue and 763*0Sstevel@tonic-gate * transmit them if possible 764*0Sstevel@tonic-gate */ 765*0Sstevel@tonic-gate q = qcn_state->qcn_writeq; 766*0Sstevel@tonic-gate ASSERT(q != NULL); 767*0Sstevel@tonic-gate while (mp = getq(q)) { 768*0Sstevel@tonic-gate if (mp->b_datap->db_type == M_IOCTL) { 769*0Sstevel@tonic-gate /* 770*0Sstevel@tonic-gate * These are those IOCTLs queued up 771*0Sstevel@tonic-gate * do it now 772*0Sstevel@tonic-gate */ 773*0Sstevel@tonic-gate qcn_ioctl(q, mp); 774*0Sstevel@tonic-gate continue; 775*0Sstevel@tonic-gate } 776*0Sstevel@tonic-gate /* 777*0Sstevel@tonic-gate * M_DATA 778*0Sstevel@tonic-gate */ 779*0Sstevel@tonic-gate rv = qcn_transmit(q, mp); 780*0Sstevel@tonic-gate if (rv == EBUSY || rv == EAGAIN) 781*0Sstevel@tonic-gate return; 782*0Sstevel@tonic-gate } 783*0Sstevel@tonic-gate } 784*0Sstevel@tonic-gate 785*0Sstevel@tonic-gate static int 786*0Sstevel@tonic-gate qcn_transmit(queue_t *q, mblk_t *mp) 787*0Sstevel@tonic-gate { 788*0Sstevel@tonic-gate caddr_t buf; 789*0Sstevel@tonic-gate mblk_t *bp; 790*0Sstevel@tonic-gate size_t len; 791*0Sstevel@tonic-gate long i; 792*0Sstevel@tonic-gate 793*0Sstevel@tonic-gate #ifdef QCN_DEBUG 794*0Sstevel@tonic-gate prom_printf("qcn_transmit(): q=%X mp=%X\n", q, mp); 795*0Sstevel@tonic-gate #endif 796*0Sstevel@tonic-gate do { 797*0Sstevel@tonic-gate bp = mp; 798*0Sstevel@tonic-gate len = bp->b_wptr - bp->b_rptr; 799*0Sstevel@tonic-gate buf = (caddr_t)bp->b_rptr; 800*0Sstevel@tonic-gate 801*0Sstevel@tonic-gate for (i = 0; i < len; i++) { 802*0Sstevel@tonic-gate if (hv_cnputchar(buf[i]) == -1) 803*0Sstevel@tonic-gate break; 804*0Sstevel@tonic-gate } 805*0Sstevel@tonic-gate if (i != len) { 806*0Sstevel@tonic-gate bp->b_rptr += i; 807*0Sstevel@tonic-gate (void) putbq(q, mp); 808*0Sstevel@tonic-gate return (EAGAIN); 809*0Sstevel@tonic-gate } 810*0Sstevel@tonic-gate mp = bp->b_cont; 811*0Sstevel@tonic-gate freeb(bp); 812*0Sstevel@tonic-gate } while (mp != NULL); 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate return (0); 815*0Sstevel@tonic-gate } 816*0Sstevel@tonic-gate 817*0Sstevel@tonic-gate /* 818*0Sstevel@tonic-gate * called when SC first establishes console connection 819*0Sstevel@tonic-gate * drop all the data on the output queue 820*0Sstevel@tonic-gate */ 821*0Sstevel@tonic-gate static void 822*0Sstevel@tonic-gate qcn_flush(void) 823*0Sstevel@tonic-gate { 824*0Sstevel@tonic-gate queue_t *q; 825*0Sstevel@tonic-gate mblk_t *mp; 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&qcn_state->qcn_lock)); 828*0Sstevel@tonic-gate 829*0Sstevel@tonic-gate q = qcn_state->qcn_writeq; 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate prom_printf("qcn_flush(): WARNING console output is dropped time=%x\n", 832*0Sstevel@tonic-gate gethrestime_sec()); 833*0Sstevel@tonic-gate while (mp = getq(q)) 834*0Sstevel@tonic-gate freemsg(mp); 835*0Sstevel@tonic-gate } 836*0Sstevel@tonic-gate 837*0Sstevel@tonic-gate static void 838*0Sstevel@tonic-gate qcn_trigger_softint(void) 839*0Sstevel@tonic-gate { 840*0Sstevel@tonic-gate 841*0Sstevel@tonic-gate if (mutex_tryenter(&qcn_state->qcn_softlock)) { 842*0Sstevel@tonic-gate if (!qcn_state->qcn_soft_pend) { 843*0Sstevel@tonic-gate qcn_state->qcn_soft_pend = 1; 844*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_softlock); 845*0Sstevel@tonic-gate (void) ddi_intr_trigger_softint( 846*0Sstevel@tonic-gate qcn_state->qcn_softint_hdl, NULL); 847*0Sstevel@tonic-gate } else 848*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_softlock); 849*0Sstevel@tonic-gate } 850*0Sstevel@tonic-gate } 851*0Sstevel@tonic-gate 852*0Sstevel@tonic-gate /*ARGSUSED*/ 853*0Sstevel@tonic-gate static uint_t 854*0Sstevel@tonic-gate qcn_soft_intr(caddr_t arg1, caddr_t arg2) 855*0Sstevel@tonic-gate { 856*0Sstevel@tonic-gate mblk_t *mp; 857*0Sstevel@tonic-gate int cc; 858*0Sstevel@tonic-gate 859*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_softlock); 860*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_hi_lock); 861*0Sstevel@tonic-gate if ((cc = RING_CNT(qcn_state)) <= 0) { 862*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_hi_lock); 863*0Sstevel@tonic-gate goto out; 864*0Sstevel@tonic-gate } 865*0Sstevel@tonic-gate 866*0Sstevel@tonic-gate if ((mp = allocb(cc, BPRI_MED)) == NULL) { 867*0Sstevel@tonic-gate qcn_input_dropped += cc; 868*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_intr: allocb" 869*0Sstevel@tonic-gate "failed (console input dropped)"); 870*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_hi_lock); 871*0Sstevel@tonic-gate goto out; 872*0Sstevel@tonic-gate } 873*0Sstevel@tonic-gate 874*0Sstevel@tonic-gate do { 875*0Sstevel@tonic-gate /* put console input onto stream */ 876*0Sstevel@tonic-gate *(char *)mp->b_wptr++ = RING_GET(qcn_state); 877*0Sstevel@tonic-gate } while (--cc); 878*0Sstevel@tonic-gate 879*0Sstevel@tonic-gate if (qcn_state->qcn_rbuf_overflow) { 880*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_hi_lock); 881*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn: Ring buffer overflow\n"); 882*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_hi_lock); 883*0Sstevel@tonic-gate qcn_state->qcn_rbuf_overflow = 0; 884*0Sstevel@tonic-gate } 885*0Sstevel@tonic-gate 886*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_hi_lock); 887*0Sstevel@tonic-gate if (qcn_state->qcn_readq) { 888*0Sstevel@tonic-gate putnext(qcn_state->qcn_readq, mp); 889*0Sstevel@tonic-gate } 890*0Sstevel@tonic-gate out: 891*0Sstevel@tonic-gate /* 892*0Sstevel@tonic-gate * If there are pending transmits because hypervisor 893*0Sstevel@tonic-gate * returned EWOULDBLOCK poke start now. 894*0Sstevel@tonic-gate */ 895*0Sstevel@tonic-gate 896*0Sstevel@tonic-gate if (qcn_state->qcn_writeq != NULL) { 897*0Sstevel@tonic-gate if (qcn_state->qcn_hangup) { 898*0Sstevel@tonic-gate (void) putctl(qcn_state->qcn_readq, M_HANGUP); 899*0Sstevel@tonic-gate flushq(qcn_state->qcn_writeq, FLUSHDATA); 900*0Sstevel@tonic-gate qcn_state->qcn_hangup = 0; 901*0Sstevel@tonic-gate } else { 902*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 903*0Sstevel@tonic-gate qcn_start(); 904*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 905*0Sstevel@tonic-gate } 906*0Sstevel@tonic-gate } 907*0Sstevel@tonic-gate qcn_state->qcn_soft_pend = 0; 908*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_softlock); 909*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 910*0Sstevel@tonic-gate } 911*0Sstevel@tonic-gate 912*0Sstevel@tonic-gate /*ARGSUSED*/ 913*0Sstevel@tonic-gate static uint_t 914*0Sstevel@tonic-gate qcn_hi_intr(caddr_t arg) 915*0Sstevel@tonic-gate { 916*0Sstevel@tonic-gate int64_t rv; 917*0Sstevel@tonic-gate uint8_t buf; 918*0Sstevel@tonic-gate 919*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_hi_lock); 920*0Sstevel@tonic-gate /* LINTED: E_CONSTANT_CONDITION */ 921*0Sstevel@tonic-gate while (1) { 922*0Sstevel@tonic-gate rv = hv_cngetchar(&buf); 923*0Sstevel@tonic-gate if (rv == H_BREAK) { 924*0Sstevel@tonic-gate if (abort_enable != KIOCABORTALTERNATE) 925*0Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 926*0Sstevel@tonic-gate } 927*0Sstevel@tonic-gate 928*0Sstevel@tonic-gate if (rv == H_HUP) { 929*0Sstevel@tonic-gate qcn_state->qcn_hangup = 1; 930*0Sstevel@tonic-gate } 931*0Sstevel@tonic-gate 932*0Sstevel@tonic-gate if (rv != H_EOK) 933*0Sstevel@tonic-gate goto out; 934*0Sstevel@tonic-gate 935*0Sstevel@tonic-gate if (abort_enable == KIOCABORTALTERNATE) { 936*0Sstevel@tonic-gate if (abort_charseq_recognize(buf)) { 937*0Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 938*0Sstevel@tonic-gate } 939*0Sstevel@tonic-gate } 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate /* put console input onto stream */ 942*0Sstevel@tonic-gate if (RING_POK(qcn_state, 1)) { 943*0Sstevel@tonic-gate RING_PUT(qcn_state, buf); 944*0Sstevel@tonic-gate } else { 945*0Sstevel@tonic-gate qcn_state->qcn_rbuf_overflow++; 946*0Sstevel@tonic-gate } 947*0Sstevel@tonic-gate } 948*0Sstevel@tonic-gate out: 949*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_hi_lock); 950*0Sstevel@tonic-gate qcn_trigger_softint(); 951*0Sstevel@tonic-gate 952*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 953*0Sstevel@tonic-gate } 954*0Sstevel@tonic-gate 955*0Sstevel@tonic-gate #ifdef QCN_POLLING 956*0Sstevel@tonic-gate /*ARGSUSED*/ 957*0Sstevel@tonic-gate static void 958*0Sstevel@tonic-gate qcn_poll_handler(void *unused) 959*0Sstevel@tonic-gate { 960*0Sstevel@tonic-gate mblk_t *mp; 961*0Sstevel@tonic-gate int64_t rv; 962*0Sstevel@tonic-gate uint8_t buf; 963*0Sstevel@tonic-gate int qcn_writeq_flush = 0; 964*0Sstevel@tonic-gate 965*0Sstevel@tonic-gate /* LINTED: E_CONSTANT_CONDITION */ 966*0Sstevel@tonic-gate while (1) { 967*0Sstevel@tonic-gate rv = hv_cngetchar(&buf); 968*0Sstevel@tonic-gate if (rv == H_BREAK) { 969*0Sstevel@tonic-gate if (abort_enable != KIOCABORTALTERNATE) 970*0Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 971*0Sstevel@tonic-gate } 972*0Sstevel@tonic-gate 973*0Sstevel@tonic-gate if (rv == H_HUP) { 974*0Sstevel@tonic-gate if (qcn_state->qcn_readq) { 975*0Sstevel@tonic-gate (void) putctl(qcn_state->qcn_readq, M_HANGUP); 976*0Sstevel@tonic-gate qcn_writeq_flush = 1; 977*0Sstevel@tonic-gate } 978*0Sstevel@tonic-gate goto out; 979*0Sstevel@tonic-gate } 980*0Sstevel@tonic-gate 981*0Sstevel@tonic-gate if (rv != H_EOK) 982*0Sstevel@tonic-gate goto out; 983*0Sstevel@tonic-gate 984*0Sstevel@tonic-gate if (abort_enable == KIOCABORTALTERNATE) { 985*0Sstevel@tonic-gate if (abort_charseq_recognize(buf)) { 986*0Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 987*0Sstevel@tonic-gate } 988*0Sstevel@tonic-gate } 989*0Sstevel@tonic-gate 990*0Sstevel@tonic-gate /* put console input onto stream */ 991*0Sstevel@tonic-gate if (qcn_state->qcn_readq) { 992*0Sstevel@tonic-gate if ((mp = allocb(1, BPRI_MED)) == NULL) { 993*0Sstevel@tonic-gate qcn_input_dropped++; 994*0Sstevel@tonic-gate cmn_err(CE_WARN, "qcn_intr: allocb" 995*0Sstevel@tonic-gate "failed (console input dropped)"); 996*0Sstevel@tonic-gate return; 997*0Sstevel@tonic-gate } 998*0Sstevel@tonic-gate *(char *)mp->b_wptr++ = buf; 999*0Sstevel@tonic-gate putnext(qcn_state->qcn_readq, mp); 1000*0Sstevel@tonic-gate } 1001*0Sstevel@tonic-gate } 1002*0Sstevel@tonic-gate out: 1003*0Sstevel@tonic-gate /* 1004*0Sstevel@tonic-gate * If there are pending transmits because hypervisor 1005*0Sstevel@tonic-gate * returned EWOULDBLOCK poke start now. 1006*0Sstevel@tonic-gate */ 1007*0Sstevel@tonic-gate 1008*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 1009*0Sstevel@tonic-gate if (qcn_state->qcn_writeq != NULL) { 1010*0Sstevel@tonic-gate if (qcn_writeq_flush) { 1011*0Sstevel@tonic-gate flushq(qcn_state->qcn_writeq, FLUSHDATA); 1012*0Sstevel@tonic-gate } else { 1013*0Sstevel@tonic-gate qcn_start(); 1014*0Sstevel@tonic-gate } 1015*0Sstevel@tonic-gate } 1016*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 1017*0Sstevel@tonic-gate } 1018*0Sstevel@tonic-gate #endif 1019*0Sstevel@tonic-gate 1020*0Sstevel@tonic-gate /* 1021*0Sstevel@tonic-gate * Check for abort character sequence, copied from zs_async.c 1022*0Sstevel@tonic-gate */ 1023*0Sstevel@tonic-gate #define CNTRL(c) ((c)&037) 1024*0Sstevel@tonic-gate 1025*0Sstevel@tonic-gate static boolean_t 1026*0Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch) 1027*0Sstevel@tonic-gate { 1028*0Sstevel@tonic-gate static int state = 0; 1029*0Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') }; 1030*0Sstevel@tonic-gate 1031*0Sstevel@tonic-gate if (ch == sequence[state]) { 1032*0Sstevel@tonic-gate if (++state >= sizeof (sequence)) { 1033*0Sstevel@tonic-gate state = 0; 1034*0Sstevel@tonic-gate return (B_TRUE); 1035*0Sstevel@tonic-gate } 1036*0Sstevel@tonic-gate } else { 1037*0Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0; 1038*0Sstevel@tonic-gate } 1039*0Sstevel@tonic-gate return (B_FALSE); 1040*0Sstevel@tonic-gate } 1041*0Sstevel@tonic-gate 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate static int 1044*0Sstevel@tonic-gate qcn_rsrv(queue_t *q) 1045*0Sstevel@tonic-gate { 1046*0Sstevel@tonic-gate mblk_t *mp; 1047*0Sstevel@tonic-gate 1048*0Sstevel@tonic-gate if (qcn_stopped == B_TRUE) 1049*0Sstevel@tonic-gate return (0); 1050*0Sstevel@tonic-gate 1051*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 1052*0Sstevel@tonic-gate 1053*0Sstevel@tonic-gate while ((mp = getq(q)) != NULL) { 1054*0Sstevel@tonic-gate if (canputnext(q)) 1055*0Sstevel@tonic-gate putnext(q, mp); 1056*0Sstevel@tonic-gate else if (mp->b_datap->db_type >= QPCTL) 1057*0Sstevel@tonic-gate (void) putbq(q, mp); 1058*0Sstevel@tonic-gate } 1059*0Sstevel@tonic-gate 1060*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 1061*0Sstevel@tonic-gate 1062*0Sstevel@tonic-gate return (0); 1063*0Sstevel@tonic-gate } 1064*0Sstevel@tonic-gate 1065*0Sstevel@tonic-gate /* ARGSUSED */ 1066*0Sstevel@tonic-gate static int 1067*0Sstevel@tonic-gate qcn_wsrv(queue_t *q) 1068*0Sstevel@tonic-gate { 1069*0Sstevel@tonic-gate if (qcn_stopped == B_TRUE) 1070*0Sstevel@tonic-gate return (0); 1071*0Sstevel@tonic-gate 1072*0Sstevel@tonic-gate mutex_enter(&qcn_state->qcn_lock); 1073*0Sstevel@tonic-gate 1074*0Sstevel@tonic-gate if (qcn_state->qcn_writeq != NULL) 1075*0Sstevel@tonic-gate qcn_start(); 1076*0Sstevel@tonic-gate 1077*0Sstevel@tonic-gate mutex_exit(&qcn_state->qcn_lock); 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate return (0); 1080*0Sstevel@tonic-gate } 1081