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 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 23*0Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 24*0Sstevel@tonic-gate /* All Rights Reserved */ 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate /* 27*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28*0Sstevel@tonic-gate * Use is subject to license terms. 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate /* 34*0Sstevel@tonic-gate * Serial I/O driver for 8250/16450/16550A/16650/16750 chips. 35*0Sstevel@tonic-gate */ 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include <sys/param.h> 38*0Sstevel@tonic-gate #include <sys/types.h> 39*0Sstevel@tonic-gate #include <sys/signal.h> 40*0Sstevel@tonic-gate #include <sys/stream.h> 41*0Sstevel@tonic-gate #include <sys/termio.h> 42*0Sstevel@tonic-gate #include <sys/errno.h> 43*0Sstevel@tonic-gate #include <sys/file.h> 44*0Sstevel@tonic-gate #include <sys/cmn_err.h> 45*0Sstevel@tonic-gate #include <sys/stropts.h> 46*0Sstevel@tonic-gate #include <sys/strsubr.h> 47*0Sstevel@tonic-gate #include <sys/strtty.h> 48*0Sstevel@tonic-gate #include <sys/debug.h> 49*0Sstevel@tonic-gate #include <sys/kbio.h> 50*0Sstevel@tonic-gate #include <sys/cred.h> 51*0Sstevel@tonic-gate #include <sys/stat.h> 52*0Sstevel@tonic-gate #include <sys/consdev.h> 53*0Sstevel@tonic-gate #include <sys/mkdev.h> 54*0Sstevel@tonic-gate #include <sys/kmem.h> 55*0Sstevel@tonic-gate #include <sys/cred.h> 56*0Sstevel@tonic-gate #include <sys/strsun.h> 57*0Sstevel@tonic-gate #ifdef DEBUG 58*0Sstevel@tonic-gate #include <sys/promif.h> 59*0Sstevel@tonic-gate #endif 60*0Sstevel@tonic-gate #include <sys/modctl.h> 61*0Sstevel@tonic-gate #include <sys/ddi.h> 62*0Sstevel@tonic-gate #include <sys/sunddi.h> 63*0Sstevel@tonic-gate #include <sys/asy.h> 64*0Sstevel@tonic-gate #include <sys/policy.h> 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate /* 67*0Sstevel@tonic-gate * set the RX FIFO trigger_level to half the RX FIFO size for now 68*0Sstevel@tonic-gate * we may want to make this configurable later. 69*0Sstevel@tonic-gate */ 70*0Sstevel@tonic-gate static int asy_trig_level = FIFO_TRIG_8; 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate int asy_drain_check = 15000000; /* tunable: exit drain check time */ 73*0Sstevel@tonic-gate int asy_min_dtr_low = 500000; /* tunable: minimum DTR down time */ 74*0Sstevel@tonic-gate int asy_min_utbrk = 100000; /* tunable: minumum untimed brk time */ 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate int asymaxchip = ASY16750; /* tunable: limit chip support we look for */ 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate /* 79*0Sstevel@tonic-gate * Just in case someone has a chip with broken loopback mode, we provide a 80*0Sstevel@tonic-gate * means to disable the loopback test. By default, we only loopback test 81*0Sstevel@tonic-gate * UARTs which look like they have FIFOs bigger than 16 bytes. 82*0Sstevel@tonic-gate * Set to 0 to suppress test, or to 2 to enable test on any size FIFO. 83*0Sstevel@tonic-gate */ 84*0Sstevel@tonic-gate int asy_fifo_test = 1; /* tunable: set to 0, 1, or 2 */ 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate /* 87*0Sstevel@tonic-gate * Allow ability to switch off testing of the scratch register. 88*0Sstevel@tonic-gate * Some UART emulators might not have it. This will also disable the test 89*0Sstevel@tonic-gate * for Exar/Startech ST16C650, as that requires use of the SCR register. 90*0Sstevel@tonic-gate */ 91*0Sstevel@tonic-gate int asy_scr_test = 1; /* tunable: set to 0 to disable SCR reg test */ 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate /* 94*0Sstevel@tonic-gate * As we don't yet support on-chip flow control, it's a bad idea to put a 95*0Sstevel@tonic-gate * large number of characters in the TX FIFO, since if other end tells us 96*0Sstevel@tonic-gate * to stop transmitting, we can only stop filling the TX FIFO, but it will 97*0Sstevel@tonic-gate * still carry on draining by itself, so remote end still gets what's left 98*0Sstevel@tonic-gate * in the FIFO. 99*0Sstevel@tonic-gate */ 100*0Sstevel@tonic-gate int asy_max_tx_fifo = 16; /* tunable: max fill of TX FIFO */ 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate #define async_stopc async_ttycommon.t_stopc 103*0Sstevel@tonic-gate #define async_startc async_ttycommon.t_startc 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate #define ASY_INIT 1 106*0Sstevel@tonic-gate #define ASY_NOINIT 0 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate /* enum value for sw and hw flow control action */ 109*0Sstevel@tonic-gate typedef enum { 110*0Sstevel@tonic-gate FLOW_CHECK, 111*0Sstevel@tonic-gate FLOW_STOP, 112*0Sstevel@tonic-gate FLOW_START 113*0Sstevel@tonic-gate } async_flowc_action; 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate #ifdef DEBUG 116*0Sstevel@tonic-gate #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */ 117*0Sstevel@tonic-gate #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */ 118*0Sstevel@tonic-gate #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */ 119*0Sstevel@tonic-gate #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */ 120*0Sstevel@tonic-gate #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */ 121*0Sstevel@tonic-gate #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */ 122*0Sstevel@tonic-gate #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */ 123*0Sstevel@tonic-gate #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */ 124*0Sstevel@tonic-gate #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */ 125*0Sstevel@tonic-gate #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */ 126*0Sstevel@tonic-gate #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */ 127*0Sstevel@tonic-gate #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */ 128*0Sstevel@tonic-gate #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */ 129*0Sstevel@tonic-gate #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */ 130*0Sstevel@tonic-gate #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */ 131*0Sstevel@tonic-gate #define ASY_DEBUG(x) (debug & (x)) 132*0Sstevel@tonic-gate static int debug = 0; 133*0Sstevel@tonic-gate #else 134*0Sstevel@tonic-gate #define ASY_DEBUG(x) B_FALSE 135*0Sstevel@tonic-gate #endif 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate /* pnpISA compressed device ids */ 138*0Sstevel@tonic-gate #define pnpMTS0219 0xb6930219 /* Multitech MT5634ZTX modem */ 139*0Sstevel@tonic-gate 140*0Sstevel@tonic-gate /* 141*0Sstevel@tonic-gate * PPS (Pulse Per Second) support. 142*0Sstevel@tonic-gate */ 143*0Sstevel@tonic-gate void ddi_hardpps(); 144*0Sstevel@tonic-gate /* 145*0Sstevel@tonic-gate * This is protected by the asy_excl_hi of the port on which PPS event 146*0Sstevel@tonic-gate * handling is enabled. Note that only one port should have this enabled at 147*0Sstevel@tonic-gate * any one time. Enabling PPS handling on multiple ports will result in 148*0Sstevel@tonic-gate * unpredictable (but benign) results. 149*0Sstevel@tonic-gate */ 150*0Sstevel@tonic-gate static struct ppsclockev asy_ppsev; 151*0Sstevel@tonic-gate 152*0Sstevel@tonic-gate #ifdef PPSCLOCKLED 153*0Sstevel@tonic-gate /* XXX Use these to observe PPS latencies and jitter on a scope */ 154*0Sstevel@tonic-gate #define LED_ON 155*0Sstevel@tonic-gate #define LED_OFF 156*0Sstevel@tonic-gate #else 157*0Sstevel@tonic-gate #define LED_ON 158*0Sstevel@tonic-gate #define LED_OFF 159*0Sstevel@tonic-gate #endif 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate static int max_asy_instance = -1; 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate static uint_t asysoftintr(caddr_t intarg); 164*0Sstevel@tonic-gate static uint_t asyintr(caddr_t argasy); 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate /* The async interrupt entry points */ 169*0Sstevel@tonic-gate static void async_txint(struct asycom *asy); 170*0Sstevel@tonic-gate static void async_rxint(struct asycom *asy, uchar_t lsr); 171*0Sstevel@tonic-gate static void async_msint(struct asycom *asy); 172*0Sstevel@tonic-gate static void async_softint(struct asycom *asy); 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gate static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp); 175*0Sstevel@tonic-gate static void async_reioctl(void *unit); 176*0Sstevel@tonic-gate static void async_iocdata(queue_t *q, mblk_t *mp); 177*0Sstevel@tonic-gate static void async_restart(void *arg); 178*0Sstevel@tonic-gate static void async_start(struct asyncline *async); 179*0Sstevel@tonic-gate static void async_nstart(struct asyncline *async, int mode); 180*0Sstevel@tonic-gate static void async_resume(struct asyncline *async); 181*0Sstevel@tonic-gate static void asy_program(struct asycom *asy, int mode); 182*0Sstevel@tonic-gate static void asyinit(struct asycom *asy); 183*0Sstevel@tonic-gate static void asy_waiteot(struct asycom *asy); 184*0Sstevel@tonic-gate static void asyputchar(struct cons_polledio_arg *, uchar_t c); 185*0Sstevel@tonic-gate static int asygetchar(struct cons_polledio_arg *); 186*0Sstevel@tonic-gate static boolean_t asyischar(struct cons_polledio_arg *); 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate static int asymctl(struct asycom *, int, int); 189*0Sstevel@tonic-gate static int asytodm(int, int); 190*0Sstevel@tonic-gate static int dmtoasy(int); 191*0Sstevel@tonic-gate /*PRINTFLIKE2*/ 192*0Sstevel@tonic-gate static void asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2); 193*0Sstevel@tonic-gate static void asy_parse_mode(dev_info_t *devi, struct asycom *asy); 194*0Sstevel@tonic-gate static void asy_soft_state_free(struct asycom *); 195*0Sstevel@tonic-gate static char *asy_hw_name(struct asycom *asy); 196*0Sstevel@tonic-gate static void async_hold_utbrk(void *arg); 197*0Sstevel@tonic-gate static void async_resume_utbrk(struct asyncline *async); 198*0Sstevel@tonic-gate static void async_dtr_free(struct asyncline *async); 199*0Sstevel@tonic-gate static int asy_identify_chip(dev_info_t *devi, struct asycom *asy); 200*0Sstevel@tonic-gate static void asy_reset_fifo(struct asycom *asy, uchar_t flags); 201*0Sstevel@tonic-gate static int asy_getproperty(dev_info_t *devi, struct asycom *asy, 202*0Sstevel@tonic-gate const char *property); 203*0Sstevel@tonic-gate static boolean_t async_flowcontrol_sw_input(struct asycom *asy, 204*0Sstevel@tonic-gate async_flowc_action onoff, int type); 205*0Sstevel@tonic-gate static void async_flowcontrol_sw_output(struct asycom *asy, 206*0Sstevel@tonic-gate async_flowc_action onoff); 207*0Sstevel@tonic-gate static void async_flowcontrol_hw_input(struct asycom *asy, 208*0Sstevel@tonic-gate async_flowc_action onoff, int type); 209*0Sstevel@tonic-gate static void async_flowcontrol_hw_output(struct asycom *asy, 210*0Sstevel@tonic-gate async_flowc_action onoff); 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate #define GET_PROP(devi, pname, pflag, pval, plen) \ 213*0Sstevel@tonic-gate (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \ 214*0Sstevel@tonic-gate (pflag), (pname), (caddr_t)(pval), (plen))) 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate static ddi_iblock_cookie_t asy_soft_iblock; 217*0Sstevel@tonic-gate ddi_softintr_t asy_softintr_id; 218*0Sstevel@tonic-gate static int asy_addedsoft = 0; 219*0Sstevel@tonic-gate int asysoftpend; /* soft interrupt pending */ 220*0Sstevel@tonic-gate kmutex_t asy_soft_lock; /* lock protecting asysoftpend */ 221*0Sstevel@tonic-gate kmutex_t asy_glob_lock; /* lock protecting global data manipulation */ 222*0Sstevel@tonic-gate void *asy_soft_state; 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate /* Standard COM port I/O addresses */ 225*0Sstevel@tonic-gate static const int standard_com_ports[] = { 226*0Sstevel@tonic-gate COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR 227*0Sstevel@tonic-gate }; 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate static int *com_ports; 230*0Sstevel@tonic-gate static uint_t num_com_ports; 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate /* 233*0Sstevel@tonic-gate * Baud rate table. Indexed by #defines found in sys/termios.h 234*0Sstevel@tonic-gate */ 235*0Sstevel@tonic-gate ushort_t asyspdtab[] = { 236*0Sstevel@tonic-gate 0, /* 0 baud rate */ 237*0Sstevel@tonic-gate 0x900, /* 50 baud rate */ 238*0Sstevel@tonic-gate 0x600, /* 75 baud rate */ 239*0Sstevel@tonic-gate 0x417, /* 110 baud rate (%0.026) */ 240*0Sstevel@tonic-gate 0x359, /* 134 baud rate (%0.058) */ 241*0Sstevel@tonic-gate 0x300, /* 150 baud rate */ 242*0Sstevel@tonic-gate 0x240, /* 200 baud rate */ 243*0Sstevel@tonic-gate 0x180, /* 300 baud rate */ 244*0Sstevel@tonic-gate 0x0c0, /* 600 baud rate */ 245*0Sstevel@tonic-gate 0x060, /* 1200 baud rate */ 246*0Sstevel@tonic-gate 0x040, /* 1800 baud rate */ 247*0Sstevel@tonic-gate 0x030, /* 2400 baud rate */ 248*0Sstevel@tonic-gate 0x018, /* 4800 baud rate */ 249*0Sstevel@tonic-gate 0x00c, /* 9600 baud rate */ 250*0Sstevel@tonic-gate 0x006, /* 19200 baud rate */ 251*0Sstevel@tonic-gate 0x003, /* 38400 baud rate */ 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate 0x002, /* 57600 baud rate */ 254*0Sstevel@tonic-gate 0x0, /* 76800 baud rate not supported */ 255*0Sstevel@tonic-gate 0x001, /* 115200 baud rate */ 256*0Sstevel@tonic-gate 0x0, /* 153600 baud rate not supported */ 257*0Sstevel@tonic-gate 0x0, /* 0x8002 (SMC chip) 230400 baud rate not supported */ 258*0Sstevel@tonic-gate 0x0, /* 307200 baud rate not supported */ 259*0Sstevel@tonic-gate 0x0, /* 0x8001 (SMC chip) 460800 baud rate not supported */ 260*0Sstevel@tonic-gate 0x0, /* unused */ 261*0Sstevel@tonic-gate 0x0, /* unused */ 262*0Sstevel@tonic-gate 0x0, /* unused */ 263*0Sstevel@tonic-gate 0x0, /* unused */ 264*0Sstevel@tonic-gate 0x0, /* unused */ 265*0Sstevel@tonic-gate 0x0, /* unused */ 266*0Sstevel@tonic-gate 0x0, /* unused */ 267*0Sstevel@tonic-gate 0x0, /* unused */ 268*0Sstevel@tonic-gate 0x0, /* unused */ 269*0Sstevel@tonic-gate }; 270*0Sstevel@tonic-gate 271*0Sstevel@tonic-gate static int asyrsrv(queue_t *q); 272*0Sstevel@tonic-gate static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 273*0Sstevel@tonic-gate static int asyclose(queue_t *q, int flag, cred_t *credp); 274*0Sstevel@tonic-gate static int asywput(queue_t *q, mblk_t *mp); 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate struct module_info asy_info = { 277*0Sstevel@tonic-gate 0, 278*0Sstevel@tonic-gate "asy", 279*0Sstevel@tonic-gate 0, 280*0Sstevel@tonic-gate INFPSZ, 281*0Sstevel@tonic-gate 4096, 282*0Sstevel@tonic-gate 128 283*0Sstevel@tonic-gate }; 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate static struct qinit asy_rint = { 286*0Sstevel@tonic-gate putq, 287*0Sstevel@tonic-gate asyrsrv, 288*0Sstevel@tonic-gate asyopen, 289*0Sstevel@tonic-gate asyclose, 290*0Sstevel@tonic-gate NULL, 291*0Sstevel@tonic-gate &asy_info, 292*0Sstevel@tonic-gate NULL 293*0Sstevel@tonic-gate }; 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate static struct qinit asy_wint = { 296*0Sstevel@tonic-gate asywput, 297*0Sstevel@tonic-gate NULL, 298*0Sstevel@tonic-gate NULL, 299*0Sstevel@tonic-gate NULL, 300*0Sstevel@tonic-gate NULL, 301*0Sstevel@tonic-gate &asy_info, 302*0Sstevel@tonic-gate NULL 303*0Sstevel@tonic-gate }; 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate struct streamtab asy_str_info = { 306*0Sstevel@tonic-gate &asy_rint, 307*0Sstevel@tonic-gate &asy_wint, 308*0Sstevel@tonic-gate NULL, 309*0Sstevel@tonic-gate NULL 310*0Sstevel@tonic-gate }; 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 313*0Sstevel@tonic-gate void **result); 314*0Sstevel@tonic-gate static int asyprobe(dev_info_t *); 315*0Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t); 316*0Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t); 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate static struct cb_ops cb_asy_ops = { 319*0Sstevel@tonic-gate nodev, /* cb_open */ 320*0Sstevel@tonic-gate nodev, /* cb_close */ 321*0Sstevel@tonic-gate nodev, /* cb_strategy */ 322*0Sstevel@tonic-gate nodev, /* cb_print */ 323*0Sstevel@tonic-gate nodev, /* cb_dump */ 324*0Sstevel@tonic-gate nodev, /* cb_read */ 325*0Sstevel@tonic-gate nodev, /* cb_write */ 326*0Sstevel@tonic-gate nodev, /* cb_ioctl */ 327*0Sstevel@tonic-gate nodev, /* cb_devmap */ 328*0Sstevel@tonic-gate nodev, /* cb_mmap */ 329*0Sstevel@tonic-gate nodev, /* cb_segmap */ 330*0Sstevel@tonic-gate nochpoll, /* cb_chpoll */ 331*0Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 332*0Sstevel@tonic-gate &asy_str_info, /* cb_stream */ 333*0Sstevel@tonic-gate D_MP /* cb_flag */ 334*0Sstevel@tonic-gate }; 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate struct dev_ops asy_ops = { 337*0Sstevel@tonic-gate DEVO_REV, /* devo_rev */ 338*0Sstevel@tonic-gate 0, /* devo_refcnt */ 339*0Sstevel@tonic-gate asyinfo, /* devo_getinfo */ 340*0Sstevel@tonic-gate nulldev, /* devo_identify */ 341*0Sstevel@tonic-gate asyprobe, /* devo_probe */ 342*0Sstevel@tonic-gate asyattach, /* devo_attach */ 343*0Sstevel@tonic-gate asydetach, /* devo_detach */ 344*0Sstevel@tonic-gate nodev, /* devo_reset */ 345*0Sstevel@tonic-gate &cb_asy_ops, /* devo_cb_ops */ 346*0Sstevel@tonic-gate }; 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate static struct modldrv modldrv = { 349*0Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 350*0Sstevel@tonic-gate "ASY driver %I%", 351*0Sstevel@tonic-gate &asy_ops, /* driver ops */ 352*0Sstevel@tonic-gate }; 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 355*0Sstevel@tonic-gate MODREV_1, 356*0Sstevel@tonic-gate (void *)&modldrv, 357*0Sstevel@tonic-gate NULL 358*0Sstevel@tonic-gate }; 359*0Sstevel@tonic-gate 360*0Sstevel@tonic-gate int 361*0Sstevel@tonic-gate _init(void) 362*0Sstevel@tonic-gate { 363*0Sstevel@tonic-gate int i; 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2); 366*0Sstevel@tonic-gate if (i == 0) { 367*0Sstevel@tonic-gate mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL); 368*0Sstevel@tonic-gate if ((i = mod_install(&modlinkage)) != 0) { 369*0Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 370*0Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 371*0Sstevel@tonic-gate } else { 372*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n", 373*0Sstevel@tonic-gate modldrv.drv_linkinfo, debug); 374*0Sstevel@tonic-gate } 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate return (i); 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate int 380*0Sstevel@tonic-gate _fini(void) 381*0Sstevel@tonic-gate { 382*0Sstevel@tonic-gate int i; 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate if ((i = mod_remove(&modlinkage)) == 0) { 385*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n", 386*0Sstevel@tonic-gate modldrv.drv_linkinfo); 387*0Sstevel@tonic-gate ASSERT(max_asy_instance == -1); 388*0Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 389*0Sstevel@tonic-gate if (asy_addedsoft) 390*0Sstevel@tonic-gate ddi_remove_softintr(asy_softintr_id); 391*0Sstevel@tonic-gate asy_addedsoft = 0; 392*0Sstevel@tonic-gate /* free "motherboard-serial-ports" property if allocated */ 393*0Sstevel@tonic-gate if (com_ports != NULL && com_ports != (int *)standard_com_ports) 394*0Sstevel@tonic-gate ddi_prop_free(com_ports); 395*0Sstevel@tonic-gate com_ports = NULL; 396*0Sstevel@tonic-gate mutex_destroy(&asy_soft_lock); 397*0Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 398*0Sstevel@tonic-gate } 399*0Sstevel@tonic-gate return (i); 400*0Sstevel@tonic-gate } 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate int 403*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 404*0Sstevel@tonic-gate { 405*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate static int 409*0Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 410*0Sstevel@tonic-gate { 411*0Sstevel@tonic-gate int instance; 412*0Sstevel@tonic-gate struct asycom *asy; 413*0Sstevel@tonic-gate struct asyncline *async; 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate if (cmd != DDI_DETACH) 416*0Sstevel@tonic-gate return (DDI_FAILURE); 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */ 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 421*0Sstevel@tonic-gate if (asy == NULL) 422*0Sstevel@tonic-gate return (DDI_FAILURE); 423*0Sstevel@tonic-gate async = asy->asy_priv; 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.", 426*0Sstevel@tonic-gate instance, asy_hw_name(asy)); 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate /* cancel DTR hold timeout */ 429*0Sstevel@tonic-gate if (async->async_dtrtid != 0) { 430*0Sstevel@tonic-gate (void) untimeout(async->async_dtrtid); 431*0Sstevel@tonic-gate async->async_dtrtid = 0; 432*0Sstevel@tonic-gate } 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate /* remove all minor device node(s) for this device */ 435*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 438*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 439*0Sstevel@tonic-gate cv_destroy(&async->async_flags_cv); 440*0Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock); 441*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 442*0Sstevel@tonic-gate asy_soft_state_free(asy); 443*0Sstevel@tonic-gate DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", instance); 444*0Sstevel@tonic-gate return (DDI_SUCCESS); 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate /* 448*0Sstevel@tonic-gate * asyprobe 449*0Sstevel@tonic-gate * We don't bother probing for the hardware, as since Solaris 2.6, device 450*0Sstevel@tonic-gate * nodes are only created for auto-detected hardware or nodes explicitly 451*0Sstevel@tonic-gate * created by the user, e.g. via the DCA. However, we should check the 452*0Sstevel@tonic-gate * device node is at least vaguely usable, i.e. we have a block of 8 i/o 453*0Sstevel@tonic-gate * ports. This prevents attempting to attach to bogus serial ports which 454*0Sstevel@tonic-gate * some BIOSs still partially report when they are disabled in the BIOS. 455*0Sstevel@tonic-gate */ 456*0Sstevel@tonic-gate static int 457*0Sstevel@tonic-gate asyprobe(dev_info_t *devi) 458*0Sstevel@tonic-gate { 459*0Sstevel@tonic-gate int instance; 460*0Sstevel@tonic-gate int ret = DDI_PROBE_FAILURE; 461*0Sstevel@tonic-gate int regnum; 462*0Sstevel@tonic-gate int reglen, nregs; 463*0Sstevel@tonic-gate struct reglist { 464*0Sstevel@tonic-gate uint_t bustype; 465*0Sstevel@tonic-gate int base; 466*0Sstevel@tonic-gate int size; 467*0Sstevel@tonic-gate } *reglist = NULL; 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate instance = ddi_get_instance(devi); 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate /* Retrieve "reg" property */ 472*0Sstevel@tonic-gate 473*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 474*0Sstevel@tonic-gate "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 475*0Sstevel@tonic-gate cmn_err(CE_WARN, "asyprobe: \"reg\" property not found " 476*0Sstevel@tonic-gate "in devices property list"); 477*0Sstevel@tonic-gate goto probedone; 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate 480*0Sstevel@tonic-gate /* find I/O bus register property */ 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate nregs = reglen / sizeof (struct reglist); 483*0Sstevel@tonic-gate for (regnum = 0; regnum < nregs; regnum++) { 484*0Sstevel@tonic-gate if (reglist[regnum].bustype == 1) 485*0Sstevel@tonic-gate break; 486*0Sstevel@tonic-gate } 487*0Sstevel@tonic-gate if (regnum >= nregs) { 488*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, 489*0Sstevel@tonic-gate "asy%dprobe: No I/O register property", instance); 490*0Sstevel@tonic-gate goto probedone; 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate if (reglist[regnum].size < 8) { /* not enough registers for a UART */ 494*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, 495*0Sstevel@tonic-gate "asy%dprobe: Invalid I/O register property", instance); 496*0Sstevel@tonic-gate goto probedone; 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate ret = DDI_PROBE_DONTCARE; /* OK, looks like it might be usable */ 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate probedone: 502*0Sstevel@tonic-gate if (reglist != NULL) 503*0Sstevel@tonic-gate kmem_free(reglist, reglen); 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "asy%dprobe: ret=%s\n", instance, 506*0Sstevel@tonic-gate ret == DDI_PROBE_DONTCARE ? "DDI_PROBE_DONTCARE" : 507*0Sstevel@tonic-gate "DDI_PROBE_FAILURE"); 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate return (ret); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate static int 513*0Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 514*0Sstevel@tonic-gate { 515*0Sstevel@tonic-gate int instance; 516*0Sstevel@tonic-gate int mcr; 517*0Sstevel@tonic-gate int ret; 518*0Sstevel@tonic-gate int regnum = 0; 519*0Sstevel@tonic-gate int i; 520*0Sstevel@tonic-gate struct asycom *asy; 521*0Sstevel@tonic-gate char name[40]; 522*0Sstevel@tonic-gate int status; 523*0Sstevel@tonic-gate static ddi_device_acc_attr_t ioattr = { 524*0Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 525*0Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 526*0Sstevel@tonic-gate DDI_STRICTORDER_ACC, 527*0Sstevel@tonic-gate }; 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate if (cmd != DDI_ATTACH) 530*0Sstevel@tonic-gate return (DDI_FAILURE); 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */ 533*0Sstevel@tonic-gate ret = ddi_soft_state_zalloc(asy_soft_state, instance); 534*0Sstevel@tonic-gate if (ret != DDI_SUCCESS) 535*0Sstevel@tonic-gate return (DDI_FAILURE); 536*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 537*0Sstevel@tonic-gate ASSERT(asy != NULL); /* can't fail - we only just allocated it */ 538*0Sstevel@tonic-gate asy->asy_unit = instance; 539*0Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 540*0Sstevel@tonic-gate if (instance > max_asy_instance) 541*0Sstevel@tonic-gate max_asy_instance = instance; 542*0Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate /*CSTYLED*/ 545*0Sstevel@tonic-gate { 546*0Sstevel@tonic-gate int reglen, nregs; 547*0Sstevel@tonic-gate int i; 548*0Sstevel@tonic-gate struct { 549*0Sstevel@tonic-gate uint_t bustype; 550*0Sstevel@tonic-gate int base; 551*0Sstevel@tonic-gate int size; 552*0Sstevel@tonic-gate } *reglist; 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate /* new probe */ 555*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 556*0Sstevel@tonic-gate "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 557*0Sstevel@tonic-gate cmn_err(CE_WARN, "asyattach: reg property not found " 558*0Sstevel@tonic-gate "in devices property list"); 559*0Sstevel@tonic-gate asy_soft_state_free(asy); 560*0Sstevel@tonic-gate return (DDI_PROBE_FAILURE); 561*0Sstevel@tonic-gate } 562*0Sstevel@tonic-gate regnum = -1; 563*0Sstevel@tonic-gate nregs = reglen / sizeof (*reglist); 564*0Sstevel@tonic-gate for (i = 0; i < nregs; i++) { 565*0Sstevel@tonic-gate switch (reglist[i].bustype) { 566*0Sstevel@tonic-gate case 1: /* I/O bus reg property */ 567*0Sstevel@tonic-gate if (regnum == -1) /* only use the first one */ 568*0Sstevel@tonic-gate regnum = i; 569*0Sstevel@tonic-gate break; 570*0Sstevel@tonic-gate 571*0Sstevel@tonic-gate case pnpMTS0219: /* Multitech MT5634ZTX modem */ 572*0Sstevel@tonic-gate /* Venus chipset can't do loopback test */ 573*0Sstevel@tonic-gate asy->asy_flags2 |= ASY2_NO_LOOPBACK; 574*0Sstevel@tonic-gate break; 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gate default: 577*0Sstevel@tonic-gate break; 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate kmem_free(reglist, reglen); 581*0Sstevel@tonic-gate } 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate if (regnum < 0 || 584*0Sstevel@tonic-gate ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr, 585*0Sstevel@tonic-gate (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle) 586*0Sstevel@tonic-gate != DDI_SUCCESS) { 587*0Sstevel@tonic-gate cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p", 588*0Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate asy_soft_state_free(asy); 591*0Sstevel@tonic-gate return (DDI_FAILURE); 592*0Sstevel@tonic-gate } 593*0Sstevel@tonic-gate 594*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n", 595*0Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 598*0Sstevel@tonic-gate if (com_ports == NULL) { /* need to initialize com_ports */ 599*0Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0, 600*0Sstevel@tonic-gate "motherboard-serial-ports", &com_ports, &num_com_ports) != 601*0Sstevel@tonic-gate DDI_PROP_SUCCESS) { 602*0Sstevel@tonic-gate /* Use our built-in COM[1234] values */ 603*0Sstevel@tonic-gate com_ports = (int *)standard_com_ports; 604*0Sstevel@tonic-gate num_com_ports = sizeof (standard_com_ports) / 605*0Sstevel@tonic-gate sizeof (standard_com_ports[0]); 606*0Sstevel@tonic-gate } 607*0Sstevel@tonic-gate if (num_com_ports > 10) { 608*0Sstevel@tonic-gate /* We run out of single digits for device properties */ 609*0Sstevel@tonic-gate num_com_ports = 10; 610*0Sstevel@tonic-gate cmn_err(CE_WARN, 611*0Sstevel@tonic-gate "More than %d motherboard-serial-ports", 612*0Sstevel@tonic-gate num_com_ports); 613*0Sstevel@tonic-gate } 614*0Sstevel@tonic-gate } 615*0Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gate /* 618*0Sstevel@tonic-gate * Lookup the i/o address to see if this is a standard COM port 619*0Sstevel@tonic-gate * in which case we assign it the correct tty[a-d] to match the 620*0Sstevel@tonic-gate * COM port number, or some other i/o address in which case it 621*0Sstevel@tonic-gate * will be assigned /dev/term/[0123...] in some rather arbitrary 622*0Sstevel@tonic-gate * fashion. 623*0Sstevel@tonic-gate */ 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate for (i = 0; i < num_com_ports; i++) { 626*0Sstevel@tonic-gate if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) { 627*0Sstevel@tonic-gate asy->asy_com_port = i + 1; 628*0Sstevel@tonic-gate break; 629*0Sstevel@tonic-gate } 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate /* 633*0Sstevel@tonic-gate * It appears that there was async hardware that on reset 634*0Sstevel@tonic-gate * did not clear ICR. Hence when we get to 635*0Sstevel@tonic-gate * ddi_get_iblock_cookie below, this hardware would cause 636*0Sstevel@tonic-gate * the system to hang if there was input available. 637*0Sstevel@tonic-gate */ 638*0Sstevel@tonic-gate 639*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00); 640*0Sstevel@tonic-gate 641*0Sstevel@tonic-gate /* establish default usage */ 642*0Sstevel@tonic-gate asy->asy_mcr |= RTS|DTR; /* do use RTS/DTR after open */ 643*0Sstevel@tonic-gate asy->asy_lcr = STOP1|BITS8; /* default to 1 stop 8 bits */ 644*0Sstevel@tonic-gate asy->asy_bidx = B9600; /* default to 9600 */ 645*0Sstevel@tonic-gate #ifdef DEBUG 646*0Sstevel@tonic-gate asy->asy_msint_cnt = 0; /* # of times in async_msint */ 647*0Sstevel@tonic-gate #endif 648*0Sstevel@tonic-gate mcr = 0; /* don't enable until open */ 649*0Sstevel@tonic-gate 650*0Sstevel@tonic-gate if (asy->asy_com_port != 0) { 651*0Sstevel@tonic-gate /* 652*0Sstevel@tonic-gate * For motherboard ports, emulate tty eeprom properties. 653*0Sstevel@tonic-gate * Actually, we can't tell if a port is motherboard or not, 654*0Sstevel@tonic-gate * so for "motherboard ports", read standard DOS COM ports. 655*0Sstevel@tonic-gate */ 656*0Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "ignore-cd")) { 657*0Sstevel@tonic-gate case 0: /* *-ignore-cd=False */ 658*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 659*0Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD\n", instance); 660*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 661*0Sstevel@tonic-gate break; 662*0Sstevel@tonic-gate case 1: /* *-ignore-cd=True */ 663*0Sstevel@tonic-gate /*FALLTHRU*/ 664*0Sstevel@tonic-gate default: /* *-ignore-cd not defined */ 665*0Sstevel@tonic-gate /* 666*0Sstevel@tonic-gate * We set rather silly defaults of soft carrier on 667*0Sstevel@tonic-gate * and DTR/RTS raised here because it might be that 668*0Sstevel@tonic-gate * one of the motherboard ports is the system console. 669*0Sstevel@tonic-gate */ 670*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 671*0Sstevel@tonic-gate "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n", 672*0Sstevel@tonic-gate instance); 673*0Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 674*0Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 675*0Sstevel@tonic-gate break; 676*0Sstevel@tonic-gate } 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate /* Property for not raising DTR/RTS */ 679*0Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "rts-dtr-off")) { 680*0Sstevel@tonic-gate case 0: /* *-rts-dtr-off=False */ 681*0Sstevel@tonic-gate asy->asy_flags |= ASY_RTS_DTR_OFF; /* OFF */ 682*0Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 683*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: " 684*0Sstevel@tonic-gate "ASY_RTS_DTR_OFF set and DTR & RTS set\n", 685*0Sstevel@tonic-gate instance); 686*0Sstevel@tonic-gate break; 687*0Sstevel@tonic-gate case 1: /* *-rts-dtr-off=True */ 688*0Sstevel@tonic-gate /*FALLTHRU*/ 689*0Sstevel@tonic-gate default: /* *-rts-dtr-off undefined */ 690*0Sstevel@tonic-gate break; 691*0Sstevel@tonic-gate } 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gate /* Parse property for tty modes */ 694*0Sstevel@tonic-gate asy_parse_mode(devi, asy); 695*0Sstevel@tonic-gate } else { 696*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 697*0Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n", 698*0Sstevel@tonic-gate instance); 699*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate /* 703*0Sstevel@tonic-gate * Initialize the port with default settings. 704*0Sstevel@tonic-gate */ 705*0Sstevel@tonic-gate 706*0Sstevel@tonic-gate asy->asy_fifo_buf = 1; 707*0Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF; 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gate /* 710*0Sstevel@tonic-gate * Get icookie for mutexes initialization 711*0Sstevel@tonic-gate */ 712*0Sstevel@tonic-gate if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != 713*0Sstevel@tonic-gate DDI_SUCCESS) || 714*0Sstevel@tonic-gate (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED, 715*0Sstevel@tonic-gate &asy_soft_iblock) != DDI_SUCCESS)) { 716*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 717*0Sstevel@tonic-gate cmn_err(CE_CONT, 718*0Sstevel@tonic-gate "asy%d: could not hook interrupt for UART @ %p\n", 719*0Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 720*0Sstevel@tonic-gate asy_soft_state_free(asy); 721*0Sstevel@tonic-gate return (DDI_FAILURE); 722*0Sstevel@tonic-gate } 723*0Sstevel@tonic-gate 724*0Sstevel@tonic-gate /* 725*0Sstevel@tonic-gate * Initialize mutexes before accessing the hardware 726*0Sstevel@tonic-gate */ 727*0Sstevel@tonic-gate mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, asy_soft_iblock); 728*0Sstevel@tonic-gate mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER, 729*0Sstevel@tonic-gate (void *)asy->asy_iblock); 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 732*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gate if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 735*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 736*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 737*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 738*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 739*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 740*0Sstevel@tonic-gate cmn_err(CE_CONT, "Cannot identify UART chip at %p\n", 741*0Sstevel@tonic-gate (void *)asy->asy_ioaddr); 742*0Sstevel@tonic-gate asy_soft_state_free(asy); 743*0Sstevel@tonic-gate return (DDI_FAILURE); 744*0Sstevel@tonic-gate } 745*0Sstevel@tonic-gate 746*0Sstevel@tonic-gate /* disable all interrupts */ 747*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 748*0Sstevel@tonic-gate /* select baud rate generator */ 749*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB); 750*0Sstevel@tonic-gate /* Set the baud rate to 9600 */ 751*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL), 752*0Sstevel@tonic-gate asyspdtab[asy->asy_bidx] & 0xff); 753*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH), 754*0Sstevel@tonic-gate (asyspdtab[asy->asy_bidx] >> 8) & 0xff); 755*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 756*0Sstevel@tonic-gate asy->asy_lcr); 757*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr); 758*0Sstevel@tonic-gate 759*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 760*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 761*0Sstevel@tonic-gate 762*0Sstevel@tonic-gate /* 763*0Sstevel@tonic-gate * Set up the other components of the asycom structure for this port. 764*0Sstevel@tonic-gate */ 765*0Sstevel@tonic-gate asy->asy_dip = devi; 766*0Sstevel@tonic-gate 767*0Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 768*0Sstevel@tonic-gate if (asy_addedsoft == 0) { /* install the soft interrupt handler */ 769*0Sstevel@tonic-gate if (ddi_add_softintr(devi, DDI_SOFTINT_MED, 770*0Sstevel@tonic-gate &asy_softintr_id, NULL, 0, asysoftintr, 771*0Sstevel@tonic-gate (caddr_t)0) != DDI_SUCCESS) { 772*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 773*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 774*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 775*0Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 776*0Sstevel@tonic-gate cmn_err(CE_CONT, 777*0Sstevel@tonic-gate "Can not set soft interrupt for ASY driver\n"); 778*0Sstevel@tonic-gate asy_soft_state_free(asy); 779*0Sstevel@tonic-gate return (DDI_FAILURE); 780*0Sstevel@tonic-gate } 781*0Sstevel@tonic-gate mutex_init(&asy_soft_lock, NULL, MUTEX_DRIVER, 782*0Sstevel@tonic-gate (void *)asy->asy_iblock); 783*0Sstevel@tonic-gate asy_addedsoft++; 784*0Sstevel@tonic-gate } 785*0Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 786*0Sstevel@tonic-gate 787*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 788*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 789*0Sstevel@tonic-gate 790*0Sstevel@tonic-gate /* 791*0Sstevel@tonic-gate * Install interrupt handler for this device. 792*0Sstevel@tonic-gate */ 793*0Sstevel@tonic-gate if (ddi_add_intr(devi, 0, NULL, 0, asyintr, 794*0Sstevel@tonic-gate (caddr_t)asy) != DDI_SUCCESS) { 795*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 796*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 797*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 798*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 799*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 800*0Sstevel@tonic-gate cmn_err(CE_CONT, 801*0Sstevel@tonic-gate "Can not set device interrupt for ASY driver\n"); 802*0Sstevel@tonic-gate asy_soft_state_free(asy); 803*0Sstevel@tonic-gate return (DDI_FAILURE); 804*0Sstevel@tonic-gate } 805*0Sstevel@tonic-gate 806*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 807*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate asyinit(asy); /* initialize the asyncline structure */ 810*0Sstevel@tonic-gate 811*0Sstevel@tonic-gate /* create minor device nodes for this device */ 812*0Sstevel@tonic-gate if (asy->asy_com_port != 0) { 813*0Sstevel@tonic-gate /* 814*0Sstevel@tonic-gate * For DOS COM ports, add letter suffix so 815*0Sstevel@tonic-gate * devfsadm can create correct link names. 816*0Sstevel@tonic-gate */ 817*0Sstevel@tonic-gate name[0] = asy->asy_com_port + 'a' - 1; 818*0Sstevel@tonic-gate name[1] = '\0'; 819*0Sstevel@tonic-gate } else { 820*0Sstevel@tonic-gate /* 821*0Sstevel@tonic-gate * ISA port which isn't a standard DOS COM 822*0Sstevel@tonic-gate * port needs no further qualification. 823*0Sstevel@tonic-gate */ 824*0Sstevel@tonic-gate name[0] = '\0'; 825*0Sstevel@tonic-gate } 826*0Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, instance, 827*0Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, NULL); 828*0Sstevel@tonic-gate if (status == DDI_SUCCESS) { 829*0Sstevel@tonic-gate (void) strcat(name, ",cu"); 830*0Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, 831*0Sstevel@tonic-gate OUTLINE | instance, 832*0Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO : 833*0Sstevel@tonic-gate DDI_NT_SERIAL_DO, NULL); 834*0Sstevel@tonic-gate } 835*0Sstevel@tonic-gate 836*0Sstevel@tonic-gate if (status != DDI_SUCCESS) { 837*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 838*0Sstevel@tonic-gate 839*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 840*0Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock); 841*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 842*0Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 843*0Sstevel@tonic-gate cv_destroy(&async->async_flags_cv); 844*0Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 845*0Sstevel@tonic-gate asy_soft_state_free(asy); 846*0Sstevel@tonic-gate return (DDI_FAILURE); 847*0Sstevel@tonic-gate } 848*0Sstevel@tonic-gate 849*0Sstevel@tonic-gate /* 850*0Sstevel@tonic-gate * Fill in the polled I/O structure. 851*0Sstevel@tonic-gate */ 852*0Sstevel@tonic-gate asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0; 853*0Sstevel@tonic-gate asy->polledio.cons_polledio_argument = (struct cons_polledio_arg *)asy; 854*0Sstevel@tonic-gate asy->polledio.cons_polledio_putchar = asyputchar; 855*0Sstevel@tonic-gate asy->polledio.cons_polledio_getchar = asygetchar; 856*0Sstevel@tonic-gate asy->polledio.cons_polledio_ischar = asyischar; 857*0Sstevel@tonic-gate asy->polledio.cons_polledio_enter = NULL; 858*0Sstevel@tonic-gate asy->polledio.cons_polledio_exit = NULL; 859*0Sstevel@tonic-gate 860*0Sstevel@tonic-gate ddi_report_dev(devi); 861*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dattach: done\n", instance); 862*0Sstevel@tonic-gate return (DDI_SUCCESS); 863*0Sstevel@tonic-gate } 864*0Sstevel@tonic-gate 865*0Sstevel@tonic-gate /*ARGSUSED*/ 866*0Sstevel@tonic-gate static int 867*0Sstevel@tonic-gate asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 868*0Sstevel@tonic-gate void **result) 869*0Sstevel@tonic-gate { 870*0Sstevel@tonic-gate dev_t dev = (dev_t)arg; 871*0Sstevel@tonic-gate int instance, error; 872*0Sstevel@tonic-gate struct asycom *asy; 873*0Sstevel@tonic-gate 874*0Sstevel@tonic-gate instance = UNIT(dev); 875*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 876*0Sstevel@tonic-gate if (asy == NULL) 877*0Sstevel@tonic-gate return (DDI_FAILURE); 878*0Sstevel@tonic-gate 879*0Sstevel@tonic-gate switch (infocmd) { 880*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 881*0Sstevel@tonic-gate if (asy->asy_dip == NULL) 882*0Sstevel@tonic-gate error = DDI_FAILURE; 883*0Sstevel@tonic-gate else { 884*0Sstevel@tonic-gate *result = (void *) asy->asy_dip; 885*0Sstevel@tonic-gate error = DDI_SUCCESS; 886*0Sstevel@tonic-gate } 887*0Sstevel@tonic-gate break; 888*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 889*0Sstevel@tonic-gate *result = (void *)(intptr_t)instance; 890*0Sstevel@tonic-gate error = DDI_SUCCESS; 891*0Sstevel@tonic-gate break; 892*0Sstevel@tonic-gate default: 893*0Sstevel@tonic-gate error = DDI_FAILURE; 894*0Sstevel@tonic-gate } 895*0Sstevel@tonic-gate return (error); 896*0Sstevel@tonic-gate } 897*0Sstevel@tonic-gate 898*0Sstevel@tonic-gate /* asy_getproperty -- walk through all name variants until we find a match */ 899*0Sstevel@tonic-gate 900*0Sstevel@tonic-gate static int 901*0Sstevel@tonic-gate asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property) 902*0Sstevel@tonic-gate { 903*0Sstevel@tonic-gate int len; 904*0Sstevel@tonic-gate int ret; 905*0Sstevel@tonic-gate char letter = asy->asy_com_port + 'a' - 1; /* for ttya */ 906*0Sstevel@tonic-gate char number = asy->asy_com_port + '0'; /* for COM1 */ 907*0Sstevel@tonic-gate char val[40]; 908*0Sstevel@tonic-gate char name[40]; 909*0Sstevel@tonic-gate 910*0Sstevel@tonic-gate /* Property for ignoring DCD */ 911*0Sstevel@tonic-gate (void) sprintf(name, "tty%c-%s", letter, property); 912*0Sstevel@tonic-gate len = sizeof (val); 913*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 914*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 915*0Sstevel@tonic-gate (void) sprintf(name, "com%c-%s", number, property); 916*0Sstevel@tonic-gate len = sizeof (val); 917*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, 918*0Sstevel@tonic-gate &len); 919*0Sstevel@tonic-gate } 920*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 921*0Sstevel@tonic-gate (void) sprintf(name, "tty0%c-%s", number, property); 922*0Sstevel@tonic-gate len = sizeof (val); 923*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, 924*0Sstevel@tonic-gate &len); 925*0Sstevel@tonic-gate } 926*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 927*0Sstevel@tonic-gate (void) sprintf(name, "port-%c-%s", letter, property); 928*0Sstevel@tonic-gate len = sizeof (val); 929*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, 930*0Sstevel@tonic-gate &len); 931*0Sstevel@tonic-gate } 932*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 933*0Sstevel@tonic-gate return (-1); /* property non-existant */ 934*0Sstevel@tonic-gate if (val[0] == 'f' || val[0] == 'F' || val[0] == '0') 935*0Sstevel@tonic-gate return (0); /* property false/0 */ 936*0Sstevel@tonic-gate return (1); /* property true/!0 */ 937*0Sstevel@tonic-gate } 938*0Sstevel@tonic-gate 939*0Sstevel@tonic-gate /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */ 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate static void 942*0Sstevel@tonic-gate asy_soft_state_free(struct asycom *asy) 943*0Sstevel@tonic-gate { 944*0Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 945*0Sstevel@tonic-gate /* If we were the max_asy_instance, work out new value */ 946*0Sstevel@tonic-gate if (asy->asy_unit == max_asy_instance) { 947*0Sstevel@tonic-gate while (--max_asy_instance >= 0) { 948*0Sstevel@tonic-gate if (ddi_get_soft_state(asy_soft_state, 949*0Sstevel@tonic-gate max_asy_instance) != NULL) 950*0Sstevel@tonic-gate break; 951*0Sstevel@tonic-gate } 952*0Sstevel@tonic-gate } 953*0Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 954*0Sstevel@tonic-gate 955*0Sstevel@tonic-gate if (asy->asy_priv != NULL) { 956*0Sstevel@tonic-gate kmem_free(asy->asy_priv, sizeof (struct asyncline)); 957*0Sstevel@tonic-gate asy->asy_priv = NULL; 958*0Sstevel@tonic-gate } 959*0Sstevel@tonic-gate ddi_soft_state_free(asy_soft_state, asy->asy_unit); 960*0Sstevel@tonic-gate } 961*0Sstevel@tonic-gate 962*0Sstevel@tonic-gate static char * 963*0Sstevel@tonic-gate asy_hw_name(struct asycom *asy) 964*0Sstevel@tonic-gate { 965*0Sstevel@tonic-gate switch (asy->asy_hwtype) { 966*0Sstevel@tonic-gate case ASY8250A: 967*0Sstevel@tonic-gate return ("8250A/16450"); 968*0Sstevel@tonic-gate case ASY16550: 969*0Sstevel@tonic-gate return ("16550"); 970*0Sstevel@tonic-gate case ASY16550A: 971*0Sstevel@tonic-gate return ("16550A"); 972*0Sstevel@tonic-gate case ASY16650: 973*0Sstevel@tonic-gate return ("16650"); 974*0Sstevel@tonic-gate case ASY16750: 975*0Sstevel@tonic-gate return ("16750"); 976*0Sstevel@tonic-gate default: 977*0Sstevel@tonic-gate DEBUGNOTE2(ASY_DEBUG_INIT, 978*0Sstevel@tonic-gate "asy%d: asy_hw_name: unknown asy_hwtype: %d", 979*0Sstevel@tonic-gate asy->asy_unit, asy->asy_hwtype); 980*0Sstevel@tonic-gate return ("?"); 981*0Sstevel@tonic-gate } 982*0Sstevel@tonic-gate } 983*0Sstevel@tonic-gate 984*0Sstevel@tonic-gate static int 985*0Sstevel@tonic-gate asy_identify_chip(dev_info_t *devi, struct asycom *asy) 986*0Sstevel@tonic-gate { 987*0Sstevel@tonic-gate int ret; 988*0Sstevel@tonic-gate int mcr; 989*0Sstevel@tonic-gate dev_t dev; 990*0Sstevel@tonic-gate uint_t hwtype; 991*0Sstevel@tonic-gate 992*0Sstevel@tonic-gate if (asy_scr_test) { 993*0Sstevel@tonic-gate /* Check scratch register works. */ 994*0Sstevel@tonic-gate 995*0Sstevel@tonic-gate /* write to scratch register */ 996*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, SCRTEST); 997*0Sstevel@tonic-gate /* make sure that pattern doesn't just linger on the bus */ 998*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 0x00); 999*0Sstevel@tonic-gate /* read data back from scratch register */ 1000*0Sstevel@tonic-gate ret = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR); 1001*0Sstevel@tonic-gate if (ret != SCRTEST) { 1002*0Sstevel@tonic-gate /* 1003*0Sstevel@tonic-gate * Scratch register not working. 1004*0Sstevel@tonic-gate * Probably not an async chip. 1005*0Sstevel@tonic-gate * 8250 and 8250B don't have scratch registers, 1006*0Sstevel@tonic-gate * but only worked in ancient PC XT's anyway. 1007*0Sstevel@tonic-gate */ 1008*0Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p " 1009*0Sstevel@tonic-gate "scratch register: expected 0x5a, got 0x%02x\n", 1010*0Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr, ret); 1011*0Sstevel@tonic-gate return (DDI_FAILURE); 1012*0Sstevel@tonic-gate } 1013*0Sstevel@tonic-gate } 1014*0Sstevel@tonic-gate /* 1015*0Sstevel@tonic-gate * Use 16550 fifo reset sequence specified in NS application 1016*0Sstevel@tonic-gate * note. Disable fifos until chip is initialized. 1017*0Sstevel@tonic-gate */ 1018*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1019*0Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, 0x00); /* clear */ 1020*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1021*0Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON); /* enable */ 1022*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1023*0Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON | FIFORXFLSH); 1024*0Sstevel@tonic-gate /* reset */ 1025*0Sstevel@tonic-gate if (asymaxchip >= ASY16650 && asy_scr_test) { 1026*0Sstevel@tonic-gate /* 1027*0Sstevel@tonic-gate * Reset 16650 enhanced regs also, in case we have one of these 1028*0Sstevel@tonic-gate */ 1029*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1030*0Sstevel@tonic-gate EFRACCESS); 1031*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 1032*0Sstevel@tonic-gate 0); 1033*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1034*0Sstevel@tonic-gate STOP1|BITS8); 1035*0Sstevel@tonic-gate } 1036*0Sstevel@tonic-gate 1037*0Sstevel@tonic-gate /* 1038*0Sstevel@tonic-gate * See what sort of FIFO we have. 1039*0Sstevel@tonic-gate * Try enabling it and see what chip makes of this. 1040*0Sstevel@tonic-gate */ 1041*0Sstevel@tonic-gate 1042*0Sstevel@tonic-gate asy->asy_fifor = 0; 1043*0Sstevel@tonic-gate asy->asy_hwtype = asymaxchip; /* just for asy_reset_fifo() */ 1044*0Sstevel@tonic-gate if (asymaxchip >= ASY16550A) 1045*0Sstevel@tonic-gate asy->asy_fifor |= 1046*0Sstevel@tonic-gate FIFO_ON | FIFODMA | (asy_trig_level & 0xff); 1047*0Sstevel@tonic-gate if (asymaxchip >= ASY16650) 1048*0Sstevel@tonic-gate asy->asy_fifor |= FIFOEXTRA1 | FIFOEXTRA2; 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 1051*0Sstevel@tonic-gate 1052*0Sstevel@tonic-gate mcr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 1053*0Sstevel@tonic-gate ret = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR); 1054*0Sstevel@tonic-gate DEBUGCONT4(ASY_DEBUG_CHIP, 1055*0Sstevel@tonic-gate "asy%d: probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n", 1056*0Sstevel@tonic-gate asy->asy_unit, asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, 1057*0Sstevel@tonic-gate ret, mcr); 1058*0Sstevel@tonic-gate switch (ret & 0xf0) { 1059*0Sstevel@tonic-gate case 0x40: 1060*0Sstevel@tonic-gate hwtype = ASY16550; /* 16550 with broken FIFO */ 1061*0Sstevel@tonic-gate asy->asy_fifor = 0; 1062*0Sstevel@tonic-gate break; 1063*0Sstevel@tonic-gate case 0xc0: 1064*0Sstevel@tonic-gate hwtype = ASY16550A; 1065*0Sstevel@tonic-gate asy->asy_fifo_buf = 16; 1066*0Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 1067*0Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2); 1068*0Sstevel@tonic-gate break; 1069*0Sstevel@tonic-gate case 0xe0: 1070*0Sstevel@tonic-gate hwtype = ASY16650; 1071*0Sstevel@tonic-gate asy->asy_fifo_buf = 32; 1072*0Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 1073*0Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1); 1074*0Sstevel@tonic-gate break; 1075*0Sstevel@tonic-gate case 0xf0: 1076*0Sstevel@tonic-gate /* 1077*0Sstevel@tonic-gate * Note we get 0xff if chip didn't return us anything, 1078*0Sstevel@tonic-gate * e.g. if there's no chip there. 1079*0Sstevel@tonic-gate */ 1080*0Sstevel@tonic-gate if (ret == 0xff) { 1081*0Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p " 1082*0Sstevel@tonic-gate "interrupt register: got 0xff\n", 1083*0Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr); 1084*0Sstevel@tonic-gate return (DDI_FAILURE); 1085*0Sstevel@tonic-gate } 1086*0Sstevel@tonic-gate /*FALLTHRU*/ 1087*0Sstevel@tonic-gate case 0xd0: 1088*0Sstevel@tonic-gate hwtype = ASY16750; 1089*0Sstevel@tonic-gate asy->asy_fifo_buf = 64; 1090*0Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 1091*0Sstevel@tonic-gate break; 1092*0Sstevel@tonic-gate default: 1093*0Sstevel@tonic-gate hwtype = ASY8250A; /* No FIFO */ 1094*0Sstevel@tonic-gate asy->asy_fifor = 0; 1095*0Sstevel@tonic-gate } 1096*0Sstevel@tonic-gate 1097*0Sstevel@tonic-gate if (hwtype > asymaxchip) { 1098*0Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p " 1099*0Sstevel@tonic-gate "unexpected probe result: " 1100*0Sstevel@tonic-gate "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n", 1101*0Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr, 1102*0Sstevel@tonic-gate asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr); 1103*0Sstevel@tonic-gate return (DDI_FAILURE); 1104*0Sstevel@tonic-gate } 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate /* 1107*0Sstevel@tonic-gate * Now reset the FIFO operation appropriate for the chip type. 1108*0Sstevel@tonic-gate * Note we must call asy_reset_fifo() before any possible 1109*0Sstevel@tonic-gate * downgrade of the asy->asy_hwtype, or it may not disable 1110*0Sstevel@tonic-gate * the more advanced features we specifically want downgraded. 1111*0Sstevel@tonic-gate */ 1112*0Sstevel@tonic-gate asy_reset_fifo(asy, 0); 1113*0Sstevel@tonic-gate asy->asy_hwtype = hwtype; 1114*0Sstevel@tonic-gate 1115*0Sstevel@tonic-gate /* 1116*0Sstevel@tonic-gate * Check for Exar/Startech ST16C650, which will still look like a 1117*0Sstevel@tonic-gate * 16550A until we enable its enhanced mode. 1118*0Sstevel@tonic-gate */ 1119*0Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550A && asymaxchip >= ASY16650 && 1120*0Sstevel@tonic-gate asy_scr_test) { 1121*0Sstevel@tonic-gate /* Enable enhanced mode register access */ 1122*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1123*0Sstevel@tonic-gate EFRACCESS); 1124*0Sstevel@tonic-gate /* zero scratch register (not scratch register if enhanced) */ 1125*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, 0); 1126*0Sstevel@tonic-gate /* Disable enhanced mode register access */ 1127*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1128*0Sstevel@tonic-gate STOP1|BITS8); 1129*0Sstevel@tonic-gate /* read back scratch register */ 1130*0Sstevel@tonic-gate ret = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR); 1131*0Sstevel@tonic-gate if (ret == SCRTEST) { 1132*0Sstevel@tonic-gate /* looks like we have an ST16650 -- enable it */ 1133*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1134*0Sstevel@tonic-gate EFRACCESS); 1135*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 1136*0Sstevel@tonic-gate ENHENABLE); 1137*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1138*0Sstevel@tonic-gate STOP1|BITS8); 1139*0Sstevel@tonic-gate asy->asy_hwtype = ASY16650; 1140*0Sstevel@tonic-gate asy->asy_fifo_buf = 32; 1141*0Sstevel@tonic-gate asy->asy_fifor |= 0x10; /* 24 byte txfifo trigger */ 1142*0Sstevel@tonic-gate asy_reset_fifo(asy, 0); 1143*0Sstevel@tonic-gate } 1144*0Sstevel@tonic-gate } 1145*0Sstevel@tonic-gate 1146*0Sstevel@tonic-gate /* 1147*0Sstevel@tonic-gate * If we think we might have a FIFO larger than 16 characters, 1148*0Sstevel@tonic-gate * measure FIFO size and check it against expected. 1149*0Sstevel@tonic-gate */ 1150*0Sstevel@tonic-gate if (asy_fifo_test > 0 && 1151*0Sstevel@tonic-gate !(asy->asy_flags2 & ASY2_NO_LOOPBACK) && 1152*0Sstevel@tonic-gate (asy->asy_fifo_buf > 16 || 1153*0Sstevel@tonic-gate (asy_fifo_test > 1 && asy->asy_use_fifo == FIFO_ON) || 1154*0Sstevel@tonic-gate ASY_DEBUG(ASY_DEBUG_CHIP))) { 1155*0Sstevel@tonic-gate int i; 1156*0Sstevel@tonic-gate 1157*0Sstevel@tonic-gate /* Set baud rate to 57600 (fairly arbitrary choice) */ 1158*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1159*0Sstevel@tonic-gate DLAB); 1160*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 1161*0Sstevel@tonic-gate asyspdtab[B57600] & 0xff); 1162*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 1163*0Sstevel@tonic-gate (asyspdtab[B57600] >> 8) & 0xff); 1164*0Sstevel@tonic-gate /* Set 8 bits, 1 stop bit */ 1165*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1166*0Sstevel@tonic-gate STOP1|BITS8); 1167*0Sstevel@tonic-gate /* Set loopback mode */ 1168*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 1169*0Sstevel@tonic-gate DTR | RTS | ASY_LOOP | OUT1 | OUT2); 1170*0Sstevel@tonic-gate 1171*0Sstevel@tonic-gate /* Overfill fifo */ 1172*0Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 2; i++) { 1173*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1174*0Sstevel@tonic-gate asy->asy_ioaddr + DAT, i); 1175*0Sstevel@tonic-gate } 1176*0Sstevel@tonic-gate /* 1177*0Sstevel@tonic-gate * Now there's an interesting question here about which 1178*0Sstevel@tonic-gate * FIFO we're testing the size of, RX or TX. We just 1179*0Sstevel@tonic-gate * filled the TX FIFO much faster than it can empty, 1180*0Sstevel@tonic-gate * although it is possible one or two characters may 1181*0Sstevel@tonic-gate * have gone from it to the TX shift register. 1182*0Sstevel@tonic-gate * We wait for enough time for all the characters to 1183*0Sstevel@tonic-gate * move into the RX FIFO and any excess characters to 1184*0Sstevel@tonic-gate * have been lost, and then read all the RX FIFO. So 1185*0Sstevel@tonic-gate * the answer we finally get will be the size which is 1186*0Sstevel@tonic-gate * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical 1187*0Sstevel@tonic-gate * one is actually the TX FIFO, because if we overfill 1188*0Sstevel@tonic-gate * it in normal operation, the excess characters are 1189*0Sstevel@tonic-gate * lost with no warning. 1190*0Sstevel@tonic-gate */ 1191*0Sstevel@tonic-gate /* 1192*0Sstevel@tonic-gate * Wait for characters to move into RX FIFO. 1193*0Sstevel@tonic-gate * In theory, 200 * asy->asy_fifo_buf * 2 should be 1194*0Sstevel@tonic-gate * enough. However, in practice it isn't always, so we 1195*0Sstevel@tonic-gate * increase to 400 so some slow 16550A's finish, and we 1196*0Sstevel@tonic-gate * increase to 3 so we spot more characters coming back 1197*0Sstevel@tonic-gate * than we sent, in case that should ever happen. 1198*0Sstevel@tonic-gate */ 1199*0Sstevel@tonic-gate delay(drv_usectohz(400 * asy->asy_fifo_buf * 3)); 1200*0Sstevel@tonic-gate 1201*0Sstevel@tonic-gate /* Now see how many characters we can read back */ 1202*0Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 3; i++) { 1203*0Sstevel@tonic-gate ret = ddi_io_get8(asy->asy_iohandle, 1204*0Sstevel@tonic-gate asy->asy_ioaddr + LSR); 1205*0Sstevel@tonic-gate if (!(ret & RCA)) 1206*0Sstevel@tonic-gate break; /* FIFO emptied */ 1207*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, 1208*0Sstevel@tonic-gate asy->asy_ioaddr + DAT); /* lose another */ 1209*0Sstevel@tonic-gate } 1210*0Sstevel@tonic-gate 1211*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_CHIP, 1212*0Sstevel@tonic-gate "asy%d FIFO size: expected=%d, measured=%d\n", 1213*0Sstevel@tonic-gate asy->asy_unit, asy->asy_fifo_buf, i); 1214*0Sstevel@tonic-gate 1215*0Sstevel@tonic-gate hwtype = asy->asy_hwtype; 1216*0Sstevel@tonic-gate if (i < asy->asy_fifo_buf) { 1217*0Sstevel@tonic-gate /* 1218*0Sstevel@tonic-gate * FIFO is somewhat smaller than we anticipated. 1219*0Sstevel@tonic-gate * If we have 16 characters usable, then this 1220*0Sstevel@tonic-gate * UART will probably work well enough in 1221*0Sstevel@tonic-gate * 16550A mode. If less than 16 characters, 1222*0Sstevel@tonic-gate * then we'd better not use it at all. 1223*0Sstevel@tonic-gate * UARTs with busted FIFOs do crop up. 1224*0Sstevel@tonic-gate */ 1225*0Sstevel@tonic-gate if (i >= 16 && asy->asy_fifo_buf >= 16) { 1226*0Sstevel@tonic-gate /* fall back to a 16550A */ 1227*0Sstevel@tonic-gate hwtype = ASY16550A; 1228*0Sstevel@tonic-gate asy->asy_fifo_buf = 16; 1229*0Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2); 1230*0Sstevel@tonic-gate } else { 1231*0Sstevel@tonic-gate /* fall back to no FIFO at all */ 1232*0Sstevel@tonic-gate hwtype = ASY16550; 1233*0Sstevel@tonic-gate asy->asy_fifo_buf = 1; 1234*0Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF; 1235*0Sstevel@tonic-gate asy->asy_fifor &= 1236*0Sstevel@tonic-gate ~(FIFO_ON | FIFOEXTRA1 | FIFOEXTRA2); 1237*0Sstevel@tonic-gate } 1238*0Sstevel@tonic-gate } 1239*0Sstevel@tonic-gate /* 1240*0Sstevel@tonic-gate * We will need to reprogram the FIFO if we changed 1241*0Sstevel@tonic-gate * our mind about how to drive it above, and in any 1242*0Sstevel@tonic-gate * case, it would be a good idea to flush any garbage 1243*0Sstevel@tonic-gate * out incase the loopback test left anything behind. 1244*0Sstevel@tonic-gate * Again as earlier above, we must call asy_reset_fifo() 1245*0Sstevel@tonic-gate * before any possible downgrade of asy->asy_hwtype. 1246*0Sstevel@tonic-gate */ 1247*0Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16650 && hwtype < ASY16650) { 1248*0Sstevel@tonic-gate /* Disable 16650 enhanced mode */ 1249*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1250*0Sstevel@tonic-gate EFRACCESS); 1251*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 1252*0Sstevel@tonic-gate 0); 1253*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1254*0Sstevel@tonic-gate STOP1|BITS8); 1255*0Sstevel@tonic-gate } 1256*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 1257*0Sstevel@tonic-gate asy->asy_hwtype = hwtype; 1258*0Sstevel@tonic-gate 1259*0Sstevel@tonic-gate /* Clear loopback mode and restore DTR/RTS */ 1260*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr); 1261*0Sstevel@tonic-gate } 1262*0Sstevel@tonic-gate 1263*0Sstevel@tonic-gate DEBUGNOTE3(ASY_DEBUG_CHIP, "asy%d %s @ %p", 1264*0Sstevel@tonic-gate asy->asy_unit, asy_hw_name(asy), (void *)asy->asy_ioaddr); 1265*0Sstevel@tonic-gate 1266*0Sstevel@tonic-gate /* Make UART type visible in device tree for prtconf, etc */ 1267*0Sstevel@tonic-gate dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit); 1268*0Sstevel@tonic-gate (void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy)); 1269*0Sstevel@tonic-gate 1270*0Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550) /* for broken 16550's, */ 1271*0Sstevel@tonic-gate asy->asy_hwtype = ASY8250A; /* drive them as 8250A */ 1272*0Sstevel@tonic-gate 1273*0Sstevel@tonic-gate return (DDI_SUCCESS); 1274*0Sstevel@tonic-gate } 1275*0Sstevel@tonic-gate 1276*0Sstevel@tonic-gate /* 1277*0Sstevel@tonic-gate * asyinit() initializes the TTY protocol-private data for this channel 1278*0Sstevel@tonic-gate * before enabling the interrupts. 1279*0Sstevel@tonic-gate */ 1280*0Sstevel@tonic-gate static void 1281*0Sstevel@tonic-gate asyinit(struct asycom *asy) 1282*0Sstevel@tonic-gate { 1283*0Sstevel@tonic-gate struct asyncline *async; 1284*0Sstevel@tonic-gate 1285*0Sstevel@tonic-gate asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP); 1286*0Sstevel@tonic-gate async = asy->asy_priv; 1287*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1288*0Sstevel@tonic-gate async->async_common = asy; 1289*0Sstevel@tonic-gate cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL); 1290*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1291*0Sstevel@tonic-gate } 1292*0Sstevel@tonic-gate 1293*0Sstevel@tonic-gate /*ARGSUSED3*/ 1294*0Sstevel@tonic-gate static int 1295*0Sstevel@tonic-gate asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 1296*0Sstevel@tonic-gate { 1297*0Sstevel@tonic-gate struct asycom *asy; 1298*0Sstevel@tonic-gate struct asyncline *async; 1299*0Sstevel@tonic-gate int mcr; 1300*0Sstevel@tonic-gate int unit; 1301*0Sstevel@tonic-gate int len; 1302*0Sstevel@tonic-gate struct termios *termiosp; 1303*0Sstevel@tonic-gate 1304*0Sstevel@tonic-gate unit = UNIT(*dev); 1305*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dopen\n", unit); 1306*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, unit); 1307*0Sstevel@tonic-gate if (asy == NULL) 1308*0Sstevel@tonic-gate return (ENXIO); /* unit not configured */ 1309*0Sstevel@tonic-gate async = asy->asy_priv; 1310*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1311*0Sstevel@tonic-gate 1312*0Sstevel@tonic-gate again: 1313*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1314*0Sstevel@tonic-gate 1315*0Sstevel@tonic-gate /* 1316*0Sstevel@tonic-gate * Block waiting for carrier to come up, unless this is a no-delay open. 1317*0Sstevel@tonic-gate */ 1318*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) { 1319*0Sstevel@tonic-gate /* 1320*0Sstevel@tonic-gate * Set the default termios settings (cflag). 1321*0Sstevel@tonic-gate * Others are set in ldterm. 1322*0Sstevel@tonic-gate */ 1323*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1324*0Sstevel@tonic-gate 1325*0Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 1326*0Sstevel@tonic-gate 0, "ttymodes", 1327*0Sstevel@tonic-gate (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS && 1328*0Sstevel@tonic-gate len == sizeof (struct termios)) { 1329*0Sstevel@tonic-gate async->async_ttycommon.t_cflag = termiosp->c_cflag; 1330*0Sstevel@tonic-gate kmem_free(termiosp, len); 1331*0Sstevel@tonic-gate } else 1332*0Sstevel@tonic-gate cmn_err(CE_WARN, 1333*0Sstevel@tonic-gate "asy: couldn't get ttymodes property!"); 1334*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1335*0Sstevel@tonic-gate 1336*0Sstevel@tonic-gate /* eeprom mode support - respect properties */ 1337*0Sstevel@tonic-gate if (asy->asy_cflag) 1338*0Sstevel@tonic-gate async->async_ttycommon.t_cflag = asy->asy_cflag; 1339*0Sstevel@tonic-gate 1340*0Sstevel@tonic-gate async->async_ttycommon.t_iflag = 0; 1341*0Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 1342*0Sstevel@tonic-gate async->async_ttycommon.t_size.ws_row = 0; 1343*0Sstevel@tonic-gate async->async_ttycommon.t_size.ws_col = 0; 1344*0Sstevel@tonic-gate async->async_ttycommon.t_size.ws_xpixel = 0; 1345*0Sstevel@tonic-gate async->async_ttycommon.t_size.ws_ypixel = 0; 1346*0Sstevel@tonic-gate async->async_dev = *dev; 1347*0Sstevel@tonic-gate async->async_wbufcid = 0; 1348*0Sstevel@tonic-gate 1349*0Sstevel@tonic-gate async->async_startc = CSTART; 1350*0Sstevel@tonic-gate async->async_stopc = CSTOP; 1351*0Sstevel@tonic-gate asy_program(asy, ASY_INIT); 1352*0Sstevel@tonic-gate } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 1353*0Sstevel@tonic-gate secpolicy_excl_open(cr) != 0) { 1354*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1355*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1356*0Sstevel@tonic-gate return (EBUSY); 1357*0Sstevel@tonic-gate } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 1358*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1359*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1360*0Sstevel@tonic-gate return (EBUSY); 1361*0Sstevel@tonic-gate } 1362*0Sstevel@tonic-gate 1363*0Sstevel@tonic-gate if (*dev & OUTLINE) 1364*0Sstevel@tonic-gate async->async_flags |= ASYNC_OUT; 1365*0Sstevel@tonic-gate 1366*0Sstevel@tonic-gate /* Raise DTR on every open, but delay if it was just lowered. */ 1367*0Sstevel@tonic-gate while (async->async_flags & ASYNC_DTR_DELAY) { 1368*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 1369*0Sstevel@tonic-gate "asy%dopen: waiting for the ASYNC_DTR_DELAY to be clear\n", 1370*0Sstevel@tonic-gate unit); 1371*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1372*0Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, 1373*0Sstevel@tonic-gate &asy->asy_excl) == 0) { 1374*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 1375*0Sstevel@tonic-gate "asy%dopen: interrupted by signal, exiting\n", 1376*0Sstevel@tonic-gate unit); 1377*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1378*0Sstevel@tonic-gate return (EINTR); 1379*0Sstevel@tonic-gate } 1380*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1381*0Sstevel@tonic-gate } 1382*0Sstevel@tonic-gate 1383*0Sstevel@tonic-gate mcr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 1384*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 1385*0Sstevel@tonic-gate mcr|(asy->asy_mcr&DTR)); 1386*0Sstevel@tonic-gate 1387*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT, 1388*0Sstevel@tonic-gate "asy%dopen: \"Raise DTR on every open\": make mcr = %x, " 1389*0Sstevel@tonic-gate "make TS_SOFTCAR = %s\n", 1390*0Sstevel@tonic-gate unit, mcr|(asy->asy_mcr&DTR), 1391*0Sstevel@tonic-gate (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF"); 1392*0Sstevel@tonic-gate if (asy->asy_flags & ASY_IGNORE_CD) { 1393*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 1394*0Sstevel@tonic-gate "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n", 1395*0Sstevel@tonic-gate unit); 1396*0Sstevel@tonic-gate async->async_ttycommon.t_flags |= TS_SOFTCAR; 1397*0Sstevel@tonic-gate } 1398*0Sstevel@tonic-gate else 1399*0Sstevel@tonic-gate async->async_ttycommon.t_flags &= ~TS_SOFTCAR; 1400*0Sstevel@tonic-gate 1401*0Sstevel@tonic-gate /* 1402*0Sstevel@tonic-gate * Check carrier. 1403*0Sstevel@tonic-gate */ 1404*0Sstevel@tonic-gate asy->asy_msr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 1405*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, " 1406*0Sstevel@tonic-gate "MSR & DCD is %s\n", 1407*0Sstevel@tonic-gate unit, 1408*0Sstevel@tonic-gate (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear", 1409*0Sstevel@tonic-gate (asy->asy_msr & DCD) ? "set" : "clear"); 1410*0Sstevel@tonic-gate if (asy->asy_msr & DCD) 1411*0Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON; 1412*0Sstevel@tonic-gate else 1413*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON; 1414*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1415*0Sstevel@tonic-gate 1416*0Sstevel@tonic-gate /* 1417*0Sstevel@tonic-gate * If FNDELAY and FNONBLOCK are clear, block until carrier up. 1418*0Sstevel@tonic-gate * Quit on interrupt. 1419*0Sstevel@tonic-gate */ 1420*0Sstevel@tonic-gate if (!(flag & (FNDELAY|FNONBLOCK)) && 1421*0Sstevel@tonic-gate !(async->async_ttycommon.t_cflag & CLOCAL)) { 1422*0Sstevel@tonic-gate if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) && 1423*0Sstevel@tonic-gate !(async->async_ttycommon.t_flags & TS_SOFTCAR)) || 1424*0Sstevel@tonic-gate ((async->async_flags & ASYNC_OUT) && 1425*0Sstevel@tonic-gate !(*dev & OUTLINE))) { 1426*0Sstevel@tonic-gate async->async_flags |= ASYNC_WOPEN; 1427*0Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, 1428*0Sstevel@tonic-gate &asy->asy_excl) == B_FALSE) { 1429*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN; 1430*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1431*0Sstevel@tonic-gate return (EINTR); 1432*0Sstevel@tonic-gate } 1433*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN; 1434*0Sstevel@tonic-gate goto again; 1435*0Sstevel@tonic-gate } 1436*0Sstevel@tonic-gate } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 1437*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1438*0Sstevel@tonic-gate return (EBUSY); 1439*0Sstevel@tonic-gate } 1440*0Sstevel@tonic-gate 1441*0Sstevel@tonic-gate async->async_ttycommon.t_readq = rq; 1442*0Sstevel@tonic-gate async->async_ttycommon.t_writeq = WR(rq); 1443*0Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 1444*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1445*0Sstevel@tonic-gate /* 1446*0Sstevel@tonic-gate * Caution here -- qprocson sets the pointers that are used by canput 1447*0Sstevel@tonic-gate * called by async_softint. ASYNC_ISOPEN must *not* be set until those 1448*0Sstevel@tonic-gate * pointers are valid. 1449*0Sstevel@tonic-gate */ 1450*0Sstevel@tonic-gate qprocson(rq); 1451*0Sstevel@tonic-gate async->async_flags |= ASYNC_ISOPEN; 1452*0Sstevel@tonic-gate async->async_polltid = 0; 1453*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dopen: done\n", unit); 1454*0Sstevel@tonic-gate return (0); 1455*0Sstevel@tonic-gate } 1456*0Sstevel@tonic-gate 1457*0Sstevel@tonic-gate static void 1458*0Sstevel@tonic-gate async_progress_check(void *arg) 1459*0Sstevel@tonic-gate { 1460*0Sstevel@tonic-gate struct asyncline *async = arg; 1461*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 1462*0Sstevel@tonic-gate mblk_t *bp; 1463*0Sstevel@tonic-gate 1464*0Sstevel@tonic-gate /* 1465*0Sstevel@tonic-gate * We define "progress" as either waiting on a timed break or delay, or 1466*0Sstevel@tonic-gate * having had at least one transmitter interrupt. If none of these are 1467*0Sstevel@tonic-gate * true, then just terminate the output and wake up that close thread. 1468*0Sstevel@tonic-gate */ 1469*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1470*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1471*0Sstevel@tonic-gate if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 1472*0Sstevel@tonic-gate async->async_ocnt = 0; 1473*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 1474*0Sstevel@tonic-gate async->async_timer = 0; 1475*0Sstevel@tonic-gate bp = async->async_xmitblk; 1476*0Sstevel@tonic-gate async->async_xmitblk = NULL; 1477*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1478*0Sstevel@tonic-gate if (bp != NULL) 1479*0Sstevel@tonic-gate freeb(bp); 1480*0Sstevel@tonic-gate /* 1481*0Sstevel@tonic-gate * Since this timer is running, we know that we're in exit(2). 1482*0Sstevel@tonic-gate * That means that the user can't possibly be waiting on any 1483*0Sstevel@tonic-gate * valid ioctl(2) completion anymore, and we should just flush 1484*0Sstevel@tonic-gate * everything. 1485*0Sstevel@tonic-gate */ 1486*0Sstevel@tonic-gate flushq(async->async_ttycommon.t_writeq, FLUSHALL); 1487*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 1488*0Sstevel@tonic-gate } else { 1489*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS; 1490*0Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async, 1491*0Sstevel@tonic-gate drv_usectohz(asy_drain_check)); 1492*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1493*0Sstevel@tonic-gate } 1494*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1495*0Sstevel@tonic-gate } 1496*0Sstevel@tonic-gate 1497*0Sstevel@tonic-gate /* 1498*0Sstevel@tonic-gate * Release DTR so that asyopen() can raise it. 1499*0Sstevel@tonic-gate */ 1500*0Sstevel@tonic-gate static void 1501*0Sstevel@tonic-gate async_dtr_free(struct asyncline *async) 1502*0Sstevel@tonic-gate { 1503*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 1504*0Sstevel@tonic-gate 1505*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, 1506*0Sstevel@tonic-gate "async_dtr_free, clearing ASYNC_DTR_DELAY\n"); 1507*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1508*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_DTR_DELAY; 1509*0Sstevel@tonic-gate async->async_dtrtid = 0; 1510*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 1511*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1512*0Sstevel@tonic-gate } 1513*0Sstevel@tonic-gate 1514*0Sstevel@tonic-gate /* 1515*0Sstevel@tonic-gate * Close routine. 1516*0Sstevel@tonic-gate */ 1517*0Sstevel@tonic-gate /*ARGSUSED2*/ 1518*0Sstevel@tonic-gate static int 1519*0Sstevel@tonic-gate asyclose(queue_t *q, int flag, cred_t *credp) 1520*0Sstevel@tonic-gate { 1521*0Sstevel@tonic-gate struct asyncline *async; 1522*0Sstevel@tonic-gate struct asycom *asy; 1523*0Sstevel@tonic-gate int icr, lcr; 1524*0Sstevel@tonic-gate #ifdef DEBUG 1525*0Sstevel@tonic-gate int instance; 1526*0Sstevel@tonic-gate #endif 1527*0Sstevel@tonic-gate 1528*0Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 1529*0Sstevel@tonic-gate ASSERT(async != NULL); 1530*0Sstevel@tonic-gate #ifdef DEBUG 1531*0Sstevel@tonic-gate instance = UNIT(async->async_dev); 1532*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose\n", instance); 1533*0Sstevel@tonic-gate #endif 1534*0Sstevel@tonic-gate asy = async->async_common; 1535*0Sstevel@tonic-gate 1536*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1537*0Sstevel@tonic-gate async->async_flags |= ASYNC_CLOSING; 1538*0Sstevel@tonic-gate 1539*0Sstevel@tonic-gate /* 1540*0Sstevel@tonic-gate * Turn off PPS handling early to avoid events occuring during 1541*0Sstevel@tonic-gate * close. Also reset the DCD edge monitoring bit. 1542*0Sstevel@tonic-gate */ 1543*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1544*0Sstevel@tonic-gate asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 1545*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1546*0Sstevel@tonic-gate 1547*0Sstevel@tonic-gate /* 1548*0Sstevel@tonic-gate * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 1549*0Sstevel@tonic-gate * untimed (TIOCSBRK). For the timed case, these are enqueued on our 1550*0Sstevel@tonic-gate * write queue and there's a timer running, so we don't have to worry 1551*0Sstevel@tonic-gate * about them. For the untimed case, though, the user obviously made a 1552*0Sstevel@tonic-gate * mistake, because these are handled immediately. We'll terminate the 1553*0Sstevel@tonic-gate * break now and honor his implicit request by discarding the rest of 1554*0Sstevel@tonic-gate * the data. 1555*0Sstevel@tonic-gate */ 1556*0Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) { 1557*0Sstevel@tonic-gate if (async->async_utbrktid != 0) { 1558*0Sstevel@tonic-gate (void) untimeout(async->async_utbrktid); 1559*0Sstevel@tonic-gate async->async_utbrktid = 0; 1560*0Sstevel@tonic-gate } 1561*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1562*0Sstevel@tonic-gate lcr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 1563*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1564*0Sstevel@tonic-gate asy->asy_ioaddr + LCR, (lcr & ~SETBREAK)); 1565*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1566*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND; 1567*0Sstevel@tonic-gate goto nodrain; 1568*0Sstevel@tonic-gate } 1569*0Sstevel@tonic-gate 1570*0Sstevel@tonic-gate /* 1571*0Sstevel@tonic-gate * If the user told us not to delay the close ("non-blocking"), then 1572*0Sstevel@tonic-gate * don't bother trying to drain. 1573*0Sstevel@tonic-gate * 1574*0Sstevel@tonic-gate * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 1575*0Sstevel@tonic-gate * getting an M_START (since these messages aren't enqueued), and the 1576*0Sstevel@tonic-gate * only other way to clear the stop condition is by loss of DCD, which 1577*0Sstevel@tonic-gate * would discard the queue data. Thus, we drop the output data if 1578*0Sstevel@tonic-gate * ASYNC_STOPPED is set. 1579*0Sstevel@tonic-gate */ 1580*0Sstevel@tonic-gate if ((flag & (FNDELAY|FNONBLOCK)) || 1581*0Sstevel@tonic-gate (async->async_flags & ASYNC_STOPPED)) { 1582*0Sstevel@tonic-gate goto nodrain; 1583*0Sstevel@tonic-gate } 1584*0Sstevel@tonic-gate 1585*0Sstevel@tonic-gate /* 1586*0Sstevel@tonic-gate * If there's any pending output, then we have to try to drain it. 1587*0Sstevel@tonic-gate * There are two main cases to be handled: 1588*0Sstevel@tonic-gate * - called by close(2): need to drain until done or until 1589*0Sstevel@tonic-gate * a signal is received. No timeout. 1590*0Sstevel@tonic-gate * - called by exit(2): need to drain while making progress 1591*0Sstevel@tonic-gate * or until a timeout occurs. No signals. 1592*0Sstevel@tonic-gate * 1593*0Sstevel@tonic-gate * If we can't rely on receiving a signal to get us out of a hung 1594*0Sstevel@tonic-gate * session, then we have to use a timer. In this case, we set a timer 1595*0Sstevel@tonic-gate * to check for progress in sending the output data -- all that we ask 1596*0Sstevel@tonic-gate * (at each interval) is that there's been some progress made. Since 1597*0Sstevel@tonic-gate * the interrupt routine grabs buffers from the write queue, we can't 1598*0Sstevel@tonic-gate * trust changes in async_ocnt. Instead, we use a progress flag. 1599*0Sstevel@tonic-gate * 1600*0Sstevel@tonic-gate * Note that loss of carrier will cause the output queue to be flushed, 1601*0Sstevel@tonic-gate * and we'll wake up again and finish normally. 1602*0Sstevel@tonic-gate */ 1603*0Sstevel@tonic-gate if (!ddi_can_receive_sig() && asy_drain_check != 0) { 1604*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS; 1605*0Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async, 1606*0Sstevel@tonic-gate drv_usectohz(asy_drain_check)); 1607*0Sstevel@tonic-gate } 1608*0Sstevel@tonic-gate while (async->async_ocnt > 0 || 1609*0Sstevel@tonic-gate async->async_ttycommon.t_writeq->q_first != NULL || 1610*0Sstevel@tonic-gate (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 1611*0Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0) 1612*0Sstevel@tonic-gate break; 1613*0Sstevel@tonic-gate } 1614*0Sstevel@tonic-gate if (async->async_timer != 0) { 1615*0Sstevel@tonic-gate (void) untimeout(async->async_timer); 1616*0Sstevel@tonic-gate async->async_timer = 0; 1617*0Sstevel@tonic-gate } 1618*0Sstevel@tonic-gate 1619*0Sstevel@tonic-gate nodrain: 1620*0Sstevel@tonic-gate async->async_ocnt = 0; 1621*0Sstevel@tonic-gate if (async->async_xmitblk != NULL) 1622*0Sstevel@tonic-gate freeb(async->async_xmitblk); 1623*0Sstevel@tonic-gate async->async_xmitblk = NULL; 1624*0Sstevel@tonic-gate 1625*0Sstevel@tonic-gate /* 1626*0Sstevel@tonic-gate * If line has HUPCL set or is incompletely opened fix up the modem 1627*0Sstevel@tonic-gate * lines. 1628*0Sstevel@tonic-gate */ 1629*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 1630*0Sstevel@tonic-gate "asy%dclose: next check HUPCL flag\n", instance); 1631*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1632*0Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & HUPCL) || 1633*0Sstevel@tonic-gate (async->async_flags & ASYNC_WOPEN)) { 1634*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM, 1635*0Sstevel@tonic-gate "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n", 1636*0Sstevel@tonic-gate instance, 1637*0Sstevel@tonic-gate async->async_ttycommon.t_cflag & HUPCL, 1638*0Sstevel@tonic-gate async->async_ttycommon.t_cflag & ASYNC_WOPEN); 1639*0Sstevel@tonic-gate async->async_flags |= ASYNC_DTR_DELAY; 1640*0Sstevel@tonic-gate 1641*0Sstevel@tonic-gate /* turn off DTR, RTS but NOT interrupt to 386 */ 1642*0Sstevel@tonic-gate if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) { 1643*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM, 1644*0Sstevel@tonic-gate "asy%dclose: ASY_IGNORE_CD flag = %x, " 1645*0Sstevel@tonic-gate "ASY_RTS_DTR_OFF flag = %x\n", 1646*0Sstevel@tonic-gate instance, 1647*0Sstevel@tonic-gate asy->asy_flags & ASY_IGNORE_CD, 1648*0Sstevel@tonic-gate asy->asy_flags & ASY_RTS_DTR_OFF); 1649*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1650*0Sstevel@tonic-gate asy->asy_ioaddr + MCR, asy->asy_mcr|OUT2); 1651*0Sstevel@tonic-gate } else { 1652*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 1653*0Sstevel@tonic-gate "asy%dclose: Dropping DTR and RTS\n", instance); 1654*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1655*0Sstevel@tonic-gate asy->asy_ioaddr + MCR, OUT2); 1656*0Sstevel@tonic-gate } 1657*0Sstevel@tonic-gate async->async_dtrtid = 1658*0Sstevel@tonic-gate timeout((void (*)())async_dtr_free, 1659*0Sstevel@tonic-gate (caddr_t)async, drv_usectohz(asy_min_dtr_low)); 1660*0Sstevel@tonic-gate } 1661*0Sstevel@tonic-gate /* 1662*0Sstevel@tonic-gate * If nobody's using it now, turn off receiver interrupts. 1663*0Sstevel@tonic-gate */ 1664*0Sstevel@tonic-gate if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { 1665*0Sstevel@tonic-gate icr = ddi_io_get8(asy->asy_iohandle, 1666*0Sstevel@tonic-gate asy->asy_ioaddr + ICR); 1667*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 1668*0Sstevel@tonic-gate (icr & ~RIEN)); 1669*0Sstevel@tonic-gate } 1670*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1671*0Sstevel@tonic-gate out: 1672*0Sstevel@tonic-gate ttycommon_close(&async->async_ttycommon); 1673*0Sstevel@tonic-gate 1674*0Sstevel@tonic-gate /* 1675*0Sstevel@tonic-gate * Cancel outstanding "bufcall" request. 1676*0Sstevel@tonic-gate */ 1677*0Sstevel@tonic-gate if (async->async_wbufcid != 0) { 1678*0Sstevel@tonic-gate unbufcall(async->async_wbufcid); 1679*0Sstevel@tonic-gate async->async_wbufcid = 0; 1680*0Sstevel@tonic-gate } 1681*0Sstevel@tonic-gate 1682*0Sstevel@tonic-gate /* Note that qprocsoff can't be done until after interrupts are off */ 1683*0Sstevel@tonic-gate qprocsoff(q); 1684*0Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = NULL; 1685*0Sstevel@tonic-gate async->async_ttycommon.t_readq = NULL; 1686*0Sstevel@tonic-gate async->async_ttycommon.t_writeq = NULL; 1687*0Sstevel@tonic-gate 1688*0Sstevel@tonic-gate /* 1689*0Sstevel@tonic-gate * Clear out device state, except persistant device property flags. 1690*0Sstevel@tonic-gate */ 1691*0Sstevel@tonic-gate async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF); 1692*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 1693*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1694*0Sstevel@tonic-gate 1695*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose: done\n", instance); 1696*0Sstevel@tonic-gate return (0); 1697*0Sstevel@tonic-gate } 1698*0Sstevel@tonic-gate 1699*0Sstevel@tonic-gate static boolean_t 1700*0Sstevel@tonic-gate asy_isbusy(struct asycom *asy) 1701*0Sstevel@tonic-gate { 1702*0Sstevel@tonic-gate struct asyncline *async; 1703*0Sstevel@tonic-gate 1704*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_isbusy\n"); 1705*0Sstevel@tonic-gate async = asy->asy_priv; 1706*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 1707*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 1708*0Sstevel@tonic-gate return ((async->async_ocnt > 0) || 1709*0Sstevel@tonic-gate ((ddi_io_get8(asy->asy_iohandle, 1710*0Sstevel@tonic-gate asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0)); 1711*0Sstevel@tonic-gate } 1712*0Sstevel@tonic-gate 1713*0Sstevel@tonic-gate static void 1714*0Sstevel@tonic-gate asy_waiteot(struct asycom *asy) 1715*0Sstevel@tonic-gate { 1716*0Sstevel@tonic-gate /* 1717*0Sstevel@tonic-gate * Wait for the current transmission block and the 1718*0Sstevel@tonic-gate * current fifo data to transmit. Once this is done 1719*0Sstevel@tonic-gate * we may go on. 1720*0Sstevel@tonic-gate */ 1721*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_waiteot\n"); 1722*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 1723*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 1724*0Sstevel@tonic-gate while (asy_isbusy(asy)) { 1725*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 1726*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1727*0Sstevel@tonic-gate drv_usecwait(10000); /* wait .01 */ 1728*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 1729*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 1730*0Sstevel@tonic-gate } 1731*0Sstevel@tonic-gate } 1732*0Sstevel@tonic-gate 1733*0Sstevel@tonic-gate /* asy_reset_fifo -- flush fifos and [re]program fifo control register */ 1734*0Sstevel@tonic-gate static void 1735*0Sstevel@tonic-gate asy_reset_fifo(struct asycom *asy, uchar_t flush) 1736*0Sstevel@tonic-gate { 1737*0Sstevel@tonic-gate uchar_t lcr; 1738*0Sstevel@tonic-gate 1739*0Sstevel@tonic-gate /* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */ 1740*0Sstevel@tonic-gate 1741*0Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) { 1742*0Sstevel@tonic-gate lcr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 1743*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 1744*0Sstevel@tonic-gate lcr | DLAB); 1745*0Sstevel@tonic-gate } 1746*0Sstevel@tonic-gate 1747*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 1748*0Sstevel@tonic-gate asy->asy_fifor | flush); 1749*0Sstevel@tonic-gate 1750*0Sstevel@tonic-gate /* Clear DLAB */ 1751*0Sstevel@tonic-gate 1752*0Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) { 1753*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr); 1754*0Sstevel@tonic-gate } 1755*0Sstevel@tonic-gate } 1756*0Sstevel@tonic-gate 1757*0Sstevel@tonic-gate /* 1758*0Sstevel@tonic-gate * Program the ASY port. Most of the async operation is based on the values 1759*0Sstevel@tonic-gate * of 'c_iflag' and 'c_cflag'. 1760*0Sstevel@tonic-gate */ 1761*0Sstevel@tonic-gate 1762*0Sstevel@tonic-gate #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \ 1763*0Sstevel@tonic-gate (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD)) 1764*0Sstevel@tonic-gate 1765*0Sstevel@tonic-gate static void 1766*0Sstevel@tonic-gate asy_program(struct asycom *asy, int mode) 1767*0Sstevel@tonic-gate { 1768*0Sstevel@tonic-gate struct asyncline *async; 1769*0Sstevel@tonic-gate int baudrate, c_flag; 1770*0Sstevel@tonic-gate int icr, lcr; 1771*0Sstevel@tonic-gate int flush_reg; 1772*0Sstevel@tonic-gate int ocflags; 1773*0Sstevel@tonic-gate #ifdef DEBUG 1774*0Sstevel@tonic-gate int instance; 1775*0Sstevel@tonic-gate #endif 1776*0Sstevel@tonic-gate 1777*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 1778*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 1779*0Sstevel@tonic-gate 1780*0Sstevel@tonic-gate async = asy->asy_priv; 1781*0Sstevel@tonic-gate #ifdef DEBUG 1782*0Sstevel@tonic-gate instance = UNIT(async->async_dev); 1783*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_PROCS, 1784*0Sstevel@tonic-gate "asy%d_program: mode = 0x%08X, enter\n", instance, mode); 1785*0Sstevel@tonic-gate #endif 1786*0Sstevel@tonic-gate 1787*0Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 1788*0Sstevel@tonic-gate 1789*0Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~(CIBAUD); 1790*0Sstevel@tonic-gate 1791*0Sstevel@tonic-gate if (baudrate > CBAUD) { 1792*0Sstevel@tonic-gate async->async_ttycommon.t_cflag |= CIBAUDEXT; 1793*0Sstevel@tonic-gate async->async_ttycommon.t_cflag |= 1794*0Sstevel@tonic-gate (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); 1795*0Sstevel@tonic-gate } else { 1796*0Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~CIBAUDEXT; 1797*0Sstevel@tonic-gate async->async_ttycommon.t_cflag |= 1798*0Sstevel@tonic-gate ((baudrate << IBSHIFT) & CIBAUD); 1799*0Sstevel@tonic-gate } 1800*0Sstevel@tonic-gate 1801*0Sstevel@tonic-gate c_flag = async->async_ttycommon.t_cflag & 1802*0Sstevel@tonic-gate (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT); 1803*0Sstevel@tonic-gate 1804*0Sstevel@tonic-gate /* disable interrupts */ 1805*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 1806*0Sstevel@tonic-gate 1807*0Sstevel@tonic-gate ocflags = asy->asy_ocflag; 1808*0Sstevel@tonic-gate 1809*0Sstevel@tonic-gate /* flush/reset the status registers */ 1810*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR); 1811*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 1812*0Sstevel@tonic-gate asy->asy_msr = flush_reg = ddi_io_get8(asy->asy_iohandle, 1813*0Sstevel@tonic-gate asy->asy_ioaddr + MSR); 1814*0Sstevel@tonic-gate /* 1815*0Sstevel@tonic-gate * The device is programmed in the open sequence, if we 1816*0Sstevel@tonic-gate * have to hardware handshake, then this is a good time 1817*0Sstevel@tonic-gate * to check if the device can receive any data. 1818*0Sstevel@tonic-gate */ 1819*0Sstevel@tonic-gate 1820*0Sstevel@tonic-gate if ((CRTSCTS & async->async_ttycommon.t_cflag) && !(flush_reg & CTS)) { 1821*0Sstevel@tonic-gate async_flowcontrol_hw_output(asy, FLOW_STOP); 1822*0Sstevel@tonic-gate } else { 1823*0Sstevel@tonic-gate /* 1824*0Sstevel@tonic-gate * We can not use async_flowcontrol_hw_output(asy, FLOW_START) 1825*0Sstevel@tonic-gate * here, because if CRTSCTS is clear, we need clear 1826*0Sstevel@tonic-gate * ASYNC_HW_OUT_FLW bit. 1827*0Sstevel@tonic-gate */ 1828*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW; 1829*0Sstevel@tonic-gate } 1830*0Sstevel@tonic-gate 1831*0Sstevel@tonic-gate /* 1832*0Sstevel@tonic-gate * If IXON is not set, clear ASYNC_SW_OUT_FLW; 1833*0Sstevel@tonic-gate * If IXON is set, no matter what IXON flag is before this 1834*0Sstevel@tonic-gate * function call to asy_program, 1835*0Sstevel@tonic-gate * we will use the old ASYNC_SW_OUT_FLW status. 1836*0Sstevel@tonic-gate * Because of handling IXON in the driver, we also should re-calculate 1837*0Sstevel@tonic-gate * the value of ASYNC_OUT_FLW_RESUME bit, but in fact, 1838*0Sstevel@tonic-gate * the TCSET* commands which call asy_program 1839*0Sstevel@tonic-gate * are put into the write queue, so there is no output needed to 1840*0Sstevel@tonic-gate * be resumed at this point. 1841*0Sstevel@tonic-gate */ 1842*0Sstevel@tonic-gate if (!(IXON & async->async_ttycommon.t_iflag)) 1843*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW; 1844*0Sstevel@tonic-gate 1845*0Sstevel@tonic-gate /* manually flush receive buffer or fifo (workaround for buggy fifos) */ 1846*0Sstevel@tonic-gate if (mode == ASY_INIT) 1847*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 1848*0Sstevel@tonic-gate for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) { 1849*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, 1850*0Sstevel@tonic-gate asy->asy_ioaddr + DAT); 1851*0Sstevel@tonic-gate } 1852*0Sstevel@tonic-gate } else { 1853*0Sstevel@tonic-gate flush_reg = ddi_io_get8(asy->asy_iohandle, 1854*0Sstevel@tonic-gate asy->asy_ioaddr + DAT); 1855*0Sstevel@tonic-gate } 1856*0Sstevel@tonic-gate 1857*0Sstevel@tonic-gate if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 1858*0Sstevel@tonic-gate /* Set line control */ 1859*0Sstevel@tonic-gate lcr = ddi_io_get8(asy->asy_iohandle, 1860*0Sstevel@tonic-gate asy->asy_ioaddr + LCR); 1861*0Sstevel@tonic-gate lcr &= ~(WLS0|WLS1|STB|PEN|EPS); 1862*0Sstevel@tonic-gate 1863*0Sstevel@tonic-gate if (c_flag & CSTOPB) 1864*0Sstevel@tonic-gate lcr |= STB; /* 2 stop bits */ 1865*0Sstevel@tonic-gate 1866*0Sstevel@tonic-gate if (c_flag & PARENB) 1867*0Sstevel@tonic-gate lcr |= PEN; 1868*0Sstevel@tonic-gate 1869*0Sstevel@tonic-gate if ((c_flag & PARODD) == 0) 1870*0Sstevel@tonic-gate lcr |= EPS; 1871*0Sstevel@tonic-gate 1872*0Sstevel@tonic-gate switch (c_flag & CSIZE) { 1873*0Sstevel@tonic-gate case CS5: 1874*0Sstevel@tonic-gate lcr |= BITS5; 1875*0Sstevel@tonic-gate break; 1876*0Sstevel@tonic-gate case CS6: 1877*0Sstevel@tonic-gate lcr |= BITS6; 1878*0Sstevel@tonic-gate break; 1879*0Sstevel@tonic-gate case CS7: 1880*0Sstevel@tonic-gate lcr |= BITS7; 1881*0Sstevel@tonic-gate break; 1882*0Sstevel@tonic-gate case CS8: 1883*0Sstevel@tonic-gate lcr |= BITS8; 1884*0Sstevel@tonic-gate break; 1885*0Sstevel@tonic-gate } 1886*0Sstevel@tonic-gate 1887*0Sstevel@tonic-gate /* set the baud rate, unless it is "0" */ 1888*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 1889*0Sstevel@tonic-gate asy->asy_ioaddr + LCR, DLAB); 1890*0Sstevel@tonic-gate if (baudrate != 0) { 1891*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 1892*0Sstevel@tonic-gate asyspdtab[baudrate] & 0xff); 1893*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 1894*0Sstevel@tonic-gate (asyspdtab[baudrate] >> 8) & 0xff); 1895*0Sstevel@tonic-gate } 1896*0Sstevel@tonic-gate /* set the line control modes */ 1897*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr); 1898*0Sstevel@tonic-gate 1899*0Sstevel@tonic-gate /* 1900*0Sstevel@tonic-gate * If we have a FIFO buffer, enable/flush 1901*0Sstevel@tonic-gate * at intialize time, flush if transitioning from 1902*0Sstevel@tonic-gate * CREAD off to CREAD on. 1903*0Sstevel@tonic-gate */ 1904*0Sstevel@tonic-gate if ((ocflags & CREAD) == 0 && (c_flag & CREAD) || 1905*0Sstevel@tonic-gate mode == ASY_INIT) 1906*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) 1907*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFORXFLSH); 1908*0Sstevel@tonic-gate 1909*0Sstevel@tonic-gate /* remember the new cflags */ 1910*0Sstevel@tonic-gate asy->asy_ocflag = c_flag & ~CLOCAL; 1911*0Sstevel@tonic-gate } 1912*0Sstevel@tonic-gate 1913*0Sstevel@tonic-gate if (baudrate == 0) 1914*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 1915*0Sstevel@tonic-gate (asy->asy_mcr & RTS) | OUT2); 1916*0Sstevel@tonic-gate else 1917*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 1918*0Sstevel@tonic-gate asy->asy_mcr | OUT2); 1919*0Sstevel@tonic-gate 1920*0Sstevel@tonic-gate /* 1921*0Sstevel@tonic-gate * Call the modem status interrupt handler to check for the carrier 1922*0Sstevel@tonic-gate * in case CLOCAL was turned off after the carrier came on. 1923*0Sstevel@tonic-gate * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 1924*0Sstevel@tonic-gate */ 1925*0Sstevel@tonic-gate async_msint(asy); 1926*0Sstevel@tonic-gate 1927*0Sstevel@tonic-gate /* Set interrupt control */ 1928*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2, 1929*0Sstevel@tonic-gate "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n", 1930*0Sstevel@tonic-gate instance, 1931*0Sstevel@tonic-gate c_flag & CLOCAL, 1932*0Sstevel@tonic-gate async->async_ttycommon.t_cflag & CRTSCTS); 1933*0Sstevel@tonic-gate if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) 1934*0Sstevel@tonic-gate /* 1935*0Sstevel@tonic-gate * direct-wired line ignores DCD, so we don't enable modem 1936*0Sstevel@tonic-gate * status interrupts. 1937*0Sstevel@tonic-gate */ 1938*0Sstevel@tonic-gate icr = (TIEN | SIEN); 1939*0Sstevel@tonic-gate else 1940*0Sstevel@tonic-gate icr = (TIEN | SIEN | MIEN); 1941*0Sstevel@tonic-gate 1942*0Sstevel@tonic-gate if (c_flag & CREAD) 1943*0Sstevel@tonic-gate icr |= RIEN; 1944*0Sstevel@tonic-gate 1945*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, icr); 1946*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "asy%d_program: done\n", instance); 1947*0Sstevel@tonic-gate } 1948*0Sstevel@tonic-gate 1949*0Sstevel@tonic-gate static boolean_t 1950*0Sstevel@tonic-gate asy_baudok(struct asycom *asy) 1951*0Sstevel@tonic-gate { 1952*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 1953*0Sstevel@tonic-gate int baudrate; 1954*0Sstevel@tonic-gate 1955*0Sstevel@tonic-gate 1956*0Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 1957*0Sstevel@tonic-gate 1958*0Sstevel@tonic-gate if (baudrate >= sizeof (asyspdtab)/sizeof (*asyspdtab)) 1959*0Sstevel@tonic-gate return (0); 1960*0Sstevel@tonic-gate 1961*0Sstevel@tonic-gate return (baudrate == 0 || asyspdtab[baudrate]); 1962*0Sstevel@tonic-gate } 1963*0Sstevel@tonic-gate 1964*0Sstevel@tonic-gate /* 1965*0Sstevel@tonic-gate * asyintr() is the High Level Interrupt Handler. 1966*0Sstevel@tonic-gate * 1967*0Sstevel@tonic-gate * There are four different interrupt types indexed by ISR register values: 1968*0Sstevel@tonic-gate * 0: modem 1969*0Sstevel@tonic-gate * 1: Tx holding register is empty, ready for next char 1970*0Sstevel@tonic-gate * 2: Rx register now holds a char to be picked up 1971*0Sstevel@tonic-gate * 3: error or break on line 1972*0Sstevel@tonic-gate * This routine checks the Bit 0 (interrupt-not-pending) to determine if 1973*0Sstevel@tonic-gate * the interrupt is from this port. 1974*0Sstevel@tonic-gate */ 1975*0Sstevel@tonic-gate uint_t 1976*0Sstevel@tonic-gate asyintr(caddr_t argasy) 1977*0Sstevel@tonic-gate { 1978*0Sstevel@tonic-gate struct asycom *asy = (struct asycom *)argasy; 1979*0Sstevel@tonic-gate struct asyncline *async; 1980*0Sstevel@tonic-gate int ret_status = DDI_INTR_UNCLAIMED; 1981*0Sstevel@tonic-gate uchar_t interrupt_id, lsr; 1982*0Sstevel@tonic-gate 1983*0Sstevel@tonic-gate interrupt_id = ddi_io_get8(asy->asy_iohandle, 1984*0Sstevel@tonic-gate asy->asy_ioaddr + ISR) & 0x0F; 1985*0Sstevel@tonic-gate async = asy->asy_priv; 1986*0Sstevel@tonic-gate if ((async == NULL) || asy_addedsoft == 0 || 1987*0Sstevel@tonic-gate !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 1988*0Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) 1989*0Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 1990*0Sstevel@tonic-gate else { 1991*0Sstevel@tonic-gate /* 1992*0Sstevel@tonic-gate * reset the device by: 1993*0Sstevel@tonic-gate * reading line status 1994*0Sstevel@tonic-gate * reading any data from data status register 1995*0Sstevel@tonic-gate * reading modem status 1996*0Sstevel@tonic-gate */ 1997*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, 1998*0Sstevel@tonic-gate asy->asy_ioaddr + LSR); 1999*0Sstevel@tonic-gate (void) ddi_io_get8(asy->asy_iohandle, 2000*0Sstevel@tonic-gate asy->asy_ioaddr + DAT); 2001*0Sstevel@tonic-gate asy->asy_msr = ddi_io_get8(asy->asy_iohandle, 2002*0Sstevel@tonic-gate asy->asy_ioaddr + MSR); 2003*0Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 2004*0Sstevel@tonic-gate } 2005*0Sstevel@tonic-gate } 2006*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2007*0Sstevel@tonic-gate /* 2008*0Sstevel@tonic-gate * We will loop until the interrupt line is pulled low. asy 2009*0Sstevel@tonic-gate * interrupt is edge triggered. 2010*0Sstevel@tonic-gate */ 2011*0Sstevel@tonic-gate /* CSTYLED */ 2012*0Sstevel@tonic-gate for (;; interrupt_id = (ddi_io_get8(asy->asy_iohandle, 2013*0Sstevel@tonic-gate asy->asy_ioaddr + ISR) & 0x0F)) { 2014*0Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) 2015*0Sstevel@tonic-gate break; 2016*0Sstevel@tonic-gate ret_status = DDI_INTR_CLAIMED; 2017*0Sstevel@tonic-gate 2018*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INTR, 2019*0Sstevel@tonic-gate "asyintr: interrupt_id = 0x%d\n", interrupt_id); 2020*0Sstevel@tonic-gate lsr = ddi_io_get8(asy->asy_iohandle, 2021*0Sstevel@tonic-gate asy->asy_ioaddr + LSR); 2022*0Sstevel@tonic-gate switch (interrupt_id) { 2023*0Sstevel@tonic-gate case RxRDY: 2024*0Sstevel@tonic-gate case RSTATUS: 2025*0Sstevel@tonic-gate case FFTMOUT: 2026*0Sstevel@tonic-gate /* receiver interrupt or receiver errors */ 2027*0Sstevel@tonic-gate async_rxint(asy, lsr); 2028*0Sstevel@tonic-gate break; 2029*0Sstevel@tonic-gate case TxRDY: 2030*0Sstevel@tonic-gate /* transmit interrupt */ 2031*0Sstevel@tonic-gate async_txint(asy); 2032*0Sstevel@tonic-gate continue; 2033*0Sstevel@tonic-gate case MSTATUS: 2034*0Sstevel@tonic-gate /* modem status interrupt */ 2035*0Sstevel@tonic-gate async_msint(asy); 2036*0Sstevel@tonic-gate break; 2037*0Sstevel@tonic-gate } 2038*0Sstevel@tonic-gate if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) && 2039*0Sstevel@tonic-gate (async->async_ocnt > 0)) 2040*0Sstevel@tonic-gate async_txint(asy); 2041*0Sstevel@tonic-gate } 2042*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2043*0Sstevel@tonic-gate return (ret_status); 2044*0Sstevel@tonic-gate } 2045*0Sstevel@tonic-gate 2046*0Sstevel@tonic-gate /* 2047*0Sstevel@tonic-gate * Transmitter interrupt service routine. 2048*0Sstevel@tonic-gate * If there is more data to transmit in the current pseudo-DMA block, 2049*0Sstevel@tonic-gate * send the next character if output is not stopped or draining. 2050*0Sstevel@tonic-gate * Otherwise, queue up a soft interrupt. 2051*0Sstevel@tonic-gate * 2052*0Sstevel@tonic-gate * XXX - Needs review for HW FIFOs. 2053*0Sstevel@tonic-gate */ 2054*0Sstevel@tonic-gate static void 2055*0Sstevel@tonic-gate async_txint(struct asycom *asy) 2056*0Sstevel@tonic-gate { 2057*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 2058*0Sstevel@tonic-gate int fifo_len; 2059*0Sstevel@tonic-gate 2060*0Sstevel@tonic-gate /* 2061*0Sstevel@tonic-gate * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to 2062*0Sstevel@tonic-gate * asyintr()'s context to claim the interrupt without performing 2063*0Sstevel@tonic-gate * any action. No character will be loaded into FIFO/THR until 2064*0Sstevel@tonic-gate * timed or untimed break is removed 2065*0Sstevel@tonic-gate */ 2066*0Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND)) 2067*0Sstevel@tonic-gate return; 2068*0Sstevel@tonic-gate 2069*0Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 2070*0Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 2071*0Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 2072*0Sstevel@tonic-gate 2073*0Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 2074*0Sstevel@tonic-gate fifo_len--; 2075*0Sstevel@tonic-gate 2076*0Sstevel@tonic-gate if (async->async_ocnt > 0 && fifo_len > 0 && 2077*0Sstevel@tonic-gate !(async->async_flags & 2078*0Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) { 2079*0Sstevel@tonic-gate while (fifo_len-- > 0 && async->async_ocnt-- > 0) { 2080*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 2081*0Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 2082*0Sstevel@tonic-gate } 2083*0Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 2084*0Sstevel@tonic-gate } 2085*0Sstevel@tonic-gate 2086*0Sstevel@tonic-gate if (fifo_len <= 0) 2087*0Sstevel@tonic-gate return; 2088*0Sstevel@tonic-gate 2089*0Sstevel@tonic-gate ASYSETSOFT(asy); 2090*0Sstevel@tonic-gate } 2091*0Sstevel@tonic-gate 2092*0Sstevel@tonic-gate /* 2093*0Sstevel@tonic-gate * Interrupt on port: handle PPS event. This function is only called 2094*0Sstevel@tonic-gate * for a port on which PPS event handling has been enabled. 2095*0Sstevel@tonic-gate */ 2096*0Sstevel@tonic-gate static void 2097*0Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr) 2098*0Sstevel@tonic-gate { 2099*0Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS_EDGE) { 2100*0Sstevel@tonic-gate /* Have seen leading edge, now look for and record drop */ 2101*0Sstevel@tonic-gate if ((msr & DCD) == 0) 2102*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 2103*0Sstevel@tonic-gate /* 2104*0Sstevel@tonic-gate * Waiting for leading edge, look for rise; stamp event and 2105*0Sstevel@tonic-gate * calibrate kernel clock. 2106*0Sstevel@tonic-gate */ 2107*0Sstevel@tonic-gate } else if (msr & DCD) { 2108*0Sstevel@tonic-gate /* 2109*0Sstevel@tonic-gate * This code captures a timestamp at the designated 2110*0Sstevel@tonic-gate * transition of the PPS signal (DCD asserted). The 2111*0Sstevel@tonic-gate * code provides a pointer to the timestamp, as well 2112*0Sstevel@tonic-gate * as the hardware counter value at the capture. 2113*0Sstevel@tonic-gate * 2114*0Sstevel@tonic-gate * Note: the kernel has nano based time values while 2115*0Sstevel@tonic-gate * NTP requires micro based, an in-line fast algorithm 2116*0Sstevel@tonic-gate * to convert nsec to usec is used here -- see hrt2ts() 2117*0Sstevel@tonic-gate * in common/os/timers.c for a full description. 2118*0Sstevel@tonic-gate */ 2119*0Sstevel@tonic-gate struct timeval *tvp = &asy_ppsev.tv; 2120*0Sstevel@tonic-gate timestruc_t ts; 2121*0Sstevel@tonic-gate long nsec, usec; 2122*0Sstevel@tonic-gate 2123*0Sstevel@tonic-gate asy->asy_flags |= ASY_PPS_EDGE; 2124*0Sstevel@tonic-gate LED_OFF; 2125*0Sstevel@tonic-gate gethrestime(&ts); 2126*0Sstevel@tonic-gate LED_ON; 2127*0Sstevel@tonic-gate nsec = ts.tv_nsec; 2128*0Sstevel@tonic-gate usec = nsec + (nsec >> 2); 2129*0Sstevel@tonic-gate usec = nsec + (usec >> 1); 2130*0Sstevel@tonic-gate usec = nsec + (usec >> 2); 2131*0Sstevel@tonic-gate usec = nsec + (usec >> 4); 2132*0Sstevel@tonic-gate usec = nsec - (usec >> 3); 2133*0Sstevel@tonic-gate usec = nsec + (usec >> 2); 2134*0Sstevel@tonic-gate usec = nsec + (usec >> 3); 2135*0Sstevel@tonic-gate usec = nsec + (usec >> 4); 2136*0Sstevel@tonic-gate usec = nsec + (usec >> 1); 2137*0Sstevel@tonic-gate usec = nsec + (usec >> 6); 2138*0Sstevel@tonic-gate tvp->tv_usec = usec >> 10; 2139*0Sstevel@tonic-gate tvp->tv_sec = ts.tv_sec; 2140*0Sstevel@tonic-gate 2141*0Sstevel@tonic-gate ++asy_ppsev.serial; 2142*0Sstevel@tonic-gate 2143*0Sstevel@tonic-gate /* 2144*0Sstevel@tonic-gate * Because the kernel keeps a high-resolution time, 2145*0Sstevel@tonic-gate * pass the current highres timestamp in tvp and zero 2146*0Sstevel@tonic-gate * in usec. 2147*0Sstevel@tonic-gate */ 2148*0Sstevel@tonic-gate ddi_hardpps(tvp, 0); 2149*0Sstevel@tonic-gate } 2150*0Sstevel@tonic-gate } 2151*0Sstevel@tonic-gate 2152*0Sstevel@tonic-gate /* 2153*0Sstevel@tonic-gate * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 2154*0Sstevel@tonic-gate * error interrupt. 2155*0Sstevel@tonic-gate * Try to put the character into the circular buffer for this line; if it 2156*0Sstevel@tonic-gate * overflows, indicate a circular buffer overrun. If this port is always 2157*0Sstevel@tonic-gate * to be serviced immediately, or the character is a STOP character, or 2158*0Sstevel@tonic-gate * more than 15 characters have arrived, queue up a soft interrupt to 2159*0Sstevel@tonic-gate * drain the circular buffer. 2160*0Sstevel@tonic-gate * XXX - needs review for hw FIFOs support. 2161*0Sstevel@tonic-gate */ 2162*0Sstevel@tonic-gate 2163*0Sstevel@tonic-gate static void 2164*0Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr) 2165*0Sstevel@tonic-gate { 2166*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 2167*0Sstevel@tonic-gate uchar_t c; 2168*0Sstevel@tonic-gate uint_t s, needsoft = 0; 2169*0Sstevel@tonic-gate tty_common_t *tp; 2170*0Sstevel@tonic-gate int looplim = asy->asy_fifo_buf * 2; 2171*0Sstevel@tonic-gate 2172*0Sstevel@tonic-gate tp = &async->async_ttycommon; 2173*0Sstevel@tonic-gate if (!(tp->t_cflag & CREAD)) { 2174*0Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 2175*0Sstevel@tonic-gate (void) (ddi_io_get8(asy->asy_iohandle, 2176*0Sstevel@tonic-gate asy->asy_ioaddr + DAT) & 0xff); 2177*0Sstevel@tonic-gate lsr = ddi_io_get8(asy->asy_iohandle, 2178*0Sstevel@tonic-gate asy->asy_ioaddr + LSR); 2179*0Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 2180*0Sstevel@tonic-gate break; 2181*0Sstevel@tonic-gate } 2182*0Sstevel@tonic-gate return; /* line is not open for read? */ 2183*0Sstevel@tonic-gate } 2184*0Sstevel@tonic-gate 2185*0Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 2186*0Sstevel@tonic-gate c = 0; 2187*0Sstevel@tonic-gate s = 0; /* reset error status */ 2188*0Sstevel@tonic-gate if (lsr & RCA) { 2189*0Sstevel@tonic-gate c = ddi_io_get8(asy->asy_iohandle, 2190*0Sstevel@tonic-gate asy->asy_ioaddr + DAT) & 0xff; 2191*0Sstevel@tonic-gate 2192*0Sstevel@tonic-gate /* 2193*0Sstevel@tonic-gate * We handle XON/XOFF char if IXON is set, 2194*0Sstevel@tonic-gate * but if received char is _POSIX_VDISABLE, 2195*0Sstevel@tonic-gate * we left it to the up level module. 2196*0Sstevel@tonic-gate */ 2197*0Sstevel@tonic-gate if (tp->t_iflag & IXON) { 2198*0Sstevel@tonic-gate if ((c == async->async_stopc) && 2199*0Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 2200*0Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 2201*0Sstevel@tonic-gate FLOW_STOP); 2202*0Sstevel@tonic-gate goto check_looplim; 2203*0Sstevel@tonic-gate } else if ((c == async->async_startc) && 2204*0Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 2205*0Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 2206*0Sstevel@tonic-gate FLOW_START); 2207*0Sstevel@tonic-gate needsoft = 1; 2208*0Sstevel@tonic-gate goto check_looplim; 2209*0Sstevel@tonic-gate } 2210*0Sstevel@tonic-gate if ((tp->t_iflag & IXANY) && 2211*0Sstevel@tonic-gate (async->async_flags & ASYNC_SW_OUT_FLW)) { 2212*0Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 2213*0Sstevel@tonic-gate FLOW_START); 2214*0Sstevel@tonic-gate needsoft = 1; 2215*0Sstevel@tonic-gate } 2216*0Sstevel@tonic-gate } 2217*0Sstevel@tonic-gate } 2218*0Sstevel@tonic-gate 2219*0Sstevel@tonic-gate /* 2220*0Sstevel@tonic-gate * Check for character break sequence 2221*0Sstevel@tonic-gate */ 2222*0Sstevel@tonic-gate if ((abort_enable == KIOCABORTALTERNATE) && 2223*0Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE)) { 2224*0Sstevel@tonic-gate if (abort_charseq_recognize(c)) 2225*0Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 2226*0Sstevel@tonic-gate } 2227*0Sstevel@tonic-gate 2228*0Sstevel@tonic-gate /* Handle framing errors */ 2229*0Sstevel@tonic-gate if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 2230*0Sstevel@tonic-gate if (lsr & PARERR) { 2231*0Sstevel@tonic-gate if (tp->t_iflag & INPCK) /* parity enabled */ 2232*0Sstevel@tonic-gate s |= PERROR; 2233*0Sstevel@tonic-gate } 2234*0Sstevel@tonic-gate 2235*0Sstevel@tonic-gate if (lsr & (FRMERR|BRKDET)) 2236*0Sstevel@tonic-gate s |= FRERROR; 2237*0Sstevel@tonic-gate if (lsr & OVRRUN) { 2238*0Sstevel@tonic-gate async->async_hw_overrun = 1; 2239*0Sstevel@tonic-gate s |= OVERRUN; 2240*0Sstevel@tonic-gate } 2241*0Sstevel@tonic-gate } 2242*0Sstevel@tonic-gate 2243*0Sstevel@tonic-gate if (s == 0) 2244*0Sstevel@tonic-gate if ((tp->t_iflag & PARMRK) && 2245*0Sstevel@tonic-gate !(tp->t_iflag & (IGNPAR|ISTRIP)) && 2246*0Sstevel@tonic-gate (c == 0377)) 2247*0Sstevel@tonic-gate if (RING_POK(async, 2)) { 2248*0Sstevel@tonic-gate RING_PUT(async, 0377); 2249*0Sstevel@tonic-gate RING_PUT(async, c); 2250*0Sstevel@tonic-gate } else 2251*0Sstevel@tonic-gate async->async_sw_overrun = 1; 2252*0Sstevel@tonic-gate else 2253*0Sstevel@tonic-gate if (RING_POK(async, 1)) 2254*0Sstevel@tonic-gate RING_PUT(async, c); 2255*0Sstevel@tonic-gate else 2256*0Sstevel@tonic-gate async->async_sw_overrun = 1; 2257*0Sstevel@tonic-gate else 2258*0Sstevel@tonic-gate if (s & FRERROR) /* Handle framing errors */ 2259*0Sstevel@tonic-gate if (c == 0) 2260*0Sstevel@tonic-gate if ((asy->asy_flags & ASY_CONSOLE) && 2261*0Sstevel@tonic-gate (abort_enable != 2262*0Sstevel@tonic-gate KIOCABORTALTERNATE)) 2263*0Sstevel@tonic-gate abort_sequence_enter((char *)0); 2264*0Sstevel@tonic-gate else 2265*0Sstevel@tonic-gate async->async_break++; 2266*0Sstevel@tonic-gate else 2267*0Sstevel@tonic-gate if (RING_POK(async, 1)) 2268*0Sstevel@tonic-gate RING_MARK(async, c, s); 2269*0Sstevel@tonic-gate else 2270*0Sstevel@tonic-gate async->async_sw_overrun = 1; 2271*0Sstevel@tonic-gate else /* Parity errors are handled by ldterm */ 2272*0Sstevel@tonic-gate if (RING_POK(async, 1)) 2273*0Sstevel@tonic-gate RING_MARK(async, c, s); 2274*0Sstevel@tonic-gate else 2275*0Sstevel@tonic-gate async->async_sw_overrun = 1; 2276*0Sstevel@tonic-gate check_looplim: 2277*0Sstevel@tonic-gate lsr = ddi_io_get8(asy->asy_iohandle, 2278*0Sstevel@tonic-gate asy->asy_ioaddr + LSR); 2279*0Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 2280*0Sstevel@tonic-gate break; 2281*0Sstevel@tonic-gate } 2282*0Sstevel@tonic-gate if ((RING_CNT(async) > (RINGSIZE * 3)/4) && 2283*0Sstevel@tonic-gate !(async->async_inflow_source & IN_FLOW_RINGBUFF)) { 2284*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF); 2285*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 2286*0Sstevel@tonic-gate IN_FLOW_RINGBUFF); 2287*0Sstevel@tonic-gate } 2288*0Sstevel@tonic-gate 2289*0Sstevel@tonic-gate if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 2290*0Sstevel@tonic-gate (RING_FRAC(async)) || (async->async_polltid == 0)) 2291*0Sstevel@tonic-gate ASYSETSOFT(asy); /* need a soft interrupt */ 2292*0Sstevel@tonic-gate } 2293*0Sstevel@tonic-gate 2294*0Sstevel@tonic-gate /* 2295*0Sstevel@tonic-gate * Modem status interrupt. 2296*0Sstevel@tonic-gate * 2297*0Sstevel@tonic-gate * (Note: It is assumed that the MSR hasn't been read by asyintr().) 2298*0Sstevel@tonic-gate */ 2299*0Sstevel@tonic-gate 2300*0Sstevel@tonic-gate static void 2301*0Sstevel@tonic-gate async_msint(struct asycom *asy) 2302*0Sstevel@tonic-gate { 2303*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 2304*0Sstevel@tonic-gate int msr, t_cflag = async->async_ttycommon.t_cflag; 2305*0Sstevel@tonic-gate #ifdef DEBUG 2306*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 2307*0Sstevel@tonic-gate #endif 2308*0Sstevel@tonic-gate 2309*0Sstevel@tonic-gate async_msint_retry: 2310*0Sstevel@tonic-gate /* this resets the interrupt */ 2311*0Sstevel@tonic-gate msr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 2312*0Sstevel@tonic-gate DEBUGCONT10(ASY_DEBUG_STATE, 2313*0Sstevel@tonic-gate "async%d_msint call #%d:\n" 2314*0Sstevel@tonic-gate " transition: %3s %3s %3s %3s\n" 2315*0Sstevel@tonic-gate "current state: %3s %3s %3s %3s\n", 2316*0Sstevel@tonic-gate instance, 2317*0Sstevel@tonic-gate ++(asy->asy_msint_cnt), 2318*0Sstevel@tonic-gate (msr & DCTS) ? "DCTS" : " ", 2319*0Sstevel@tonic-gate (msr & DDSR) ? "DDSR" : " ", 2320*0Sstevel@tonic-gate (msr & DRI) ? "DRI " : " ", 2321*0Sstevel@tonic-gate (msr & DDCD) ? "DDCD" : " ", 2322*0Sstevel@tonic-gate (msr & CTS) ? "CTS " : " ", 2323*0Sstevel@tonic-gate (msr & DSR) ? "DSR " : " ", 2324*0Sstevel@tonic-gate (msr & RI) ? "RI " : " ", 2325*0Sstevel@tonic-gate (msr & DCD) ? "DCD " : " "); 2326*0Sstevel@tonic-gate 2327*0Sstevel@tonic-gate /* If CTS status is changed, do H/W output flow control */ 2328*0Sstevel@tonic-gate if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0)) 2329*0Sstevel@tonic-gate async_flowcontrol_hw_output(asy, 2330*0Sstevel@tonic-gate msr & CTS ? FLOW_START : FLOW_STOP); 2331*0Sstevel@tonic-gate /* 2332*0Sstevel@tonic-gate * Reading MSR resets the interrupt, we save the 2333*0Sstevel@tonic-gate * value of msr so that other functions could examine MSR by 2334*0Sstevel@tonic-gate * looking at asy_msr. 2335*0Sstevel@tonic-gate */ 2336*0Sstevel@tonic-gate asy->asy_msr = (uchar_t)msr; 2337*0Sstevel@tonic-gate 2338*0Sstevel@tonic-gate /* Handle PPS event */ 2339*0Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 2340*0Sstevel@tonic-gate asy_ppsevent(asy, msr); 2341*0Sstevel@tonic-gate 2342*0Sstevel@tonic-gate async->async_ext++; 2343*0Sstevel@tonic-gate ASYSETSOFT(asy); 2344*0Sstevel@tonic-gate /* 2345*0Sstevel@tonic-gate * We will make sure that the modem status presented to us 2346*0Sstevel@tonic-gate * during the previous read has not changed. If the chip samples 2347*0Sstevel@tonic-gate * the modem status on the falling edge of the interrupt line, 2348*0Sstevel@tonic-gate * and uses this state as the base for detecting change of modem 2349*0Sstevel@tonic-gate * status, we would miss a change of modem status event that occured 2350*0Sstevel@tonic-gate * after we initiated a read MSR operation. 2351*0Sstevel@tonic-gate */ 2352*0Sstevel@tonic-gate msr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 2353*0Sstevel@tonic-gate if (STATES(msr) != STATES(asy->asy_msr)) 2354*0Sstevel@tonic-gate goto async_msint_retry; 2355*0Sstevel@tonic-gate } 2356*0Sstevel@tonic-gate 2357*0Sstevel@tonic-gate /* 2358*0Sstevel@tonic-gate * Handle a second-stage interrupt. 2359*0Sstevel@tonic-gate */ 2360*0Sstevel@tonic-gate /*ARGSUSED*/ 2361*0Sstevel@tonic-gate uint_t 2362*0Sstevel@tonic-gate asysoftintr(caddr_t intarg) 2363*0Sstevel@tonic-gate { 2364*0Sstevel@tonic-gate struct asycom *asy; 2365*0Sstevel@tonic-gate int rv; 2366*0Sstevel@tonic-gate int instance; 2367*0Sstevel@tonic-gate 2368*0Sstevel@tonic-gate /* 2369*0Sstevel@tonic-gate * Test and clear soft interrupt. 2370*0Sstevel@tonic-gate */ 2371*0Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 2372*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n"); 2373*0Sstevel@tonic-gate rv = asysoftpend; 2374*0Sstevel@tonic-gate if (rv != 0) 2375*0Sstevel@tonic-gate asysoftpend = 0; 2376*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2377*0Sstevel@tonic-gate 2378*0Sstevel@tonic-gate if (rv) { 2379*0Sstevel@tonic-gate /* 2380*0Sstevel@tonic-gate * Note - we can optimize the loop by remembering the last 2381*0Sstevel@tonic-gate * device that requested soft interrupt 2382*0Sstevel@tonic-gate */ 2383*0Sstevel@tonic-gate for (instance = 0; instance <= max_asy_instance; instance++) { 2384*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 2385*0Sstevel@tonic-gate if (asy == NULL || asy->asy_priv == NULL) 2386*0Sstevel@tonic-gate continue; 2387*0Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 2388*0Sstevel@tonic-gate if (asy->asy_flags & ASY_NEEDSOFT) { 2389*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_NEEDSOFT; 2390*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2391*0Sstevel@tonic-gate async_softint(asy); 2392*0Sstevel@tonic-gate } else 2393*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2394*0Sstevel@tonic-gate } 2395*0Sstevel@tonic-gate } 2396*0Sstevel@tonic-gate return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 2397*0Sstevel@tonic-gate } 2398*0Sstevel@tonic-gate 2399*0Sstevel@tonic-gate /* 2400*0Sstevel@tonic-gate * Handle a software interrupt. 2401*0Sstevel@tonic-gate */ 2402*0Sstevel@tonic-gate static void 2403*0Sstevel@tonic-gate async_softint(struct asycom *asy) 2404*0Sstevel@tonic-gate { 2405*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 2406*0Sstevel@tonic-gate short cc; 2407*0Sstevel@tonic-gate mblk_t *bp; 2408*0Sstevel@tonic-gate queue_t *q; 2409*0Sstevel@tonic-gate uchar_t val; 2410*0Sstevel@tonic-gate uchar_t c; 2411*0Sstevel@tonic-gate tty_common_t *tp; 2412*0Sstevel@tonic-gate int nb; 2413*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 2414*0Sstevel@tonic-gate 2415*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance); 2416*0Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 2417*0Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT) { 2418*0Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT_RETRY; 2419*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2420*0Sstevel@tonic-gate return; 2421*0Sstevel@tonic-gate } 2422*0Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT; 2423*0Sstevel@tonic-gate begin: 2424*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT_RETRY; 2425*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2426*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2427*0Sstevel@tonic-gate tp = &async->async_ttycommon; 2428*0Sstevel@tonic-gate q = tp->t_readq; 2429*0Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 2430*0Sstevel@tonic-gate if (async->async_ocnt > 0) { 2431*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2432*0Sstevel@tonic-gate async_resume(async); 2433*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2434*0Sstevel@tonic-gate } else { 2435*0Sstevel@tonic-gate if (async->async_xmitblk) 2436*0Sstevel@tonic-gate freeb(async->async_xmitblk); 2437*0Sstevel@tonic-gate async->async_xmitblk = NULL; 2438*0Sstevel@tonic-gate async_start(async); 2439*0Sstevel@tonic-gate } 2440*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 2441*0Sstevel@tonic-gate } 2442*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2443*0Sstevel@tonic-gate if (async->async_ext) { 2444*0Sstevel@tonic-gate async->async_ext = 0; 2445*0Sstevel@tonic-gate /* check for carrier up */ 2446*0Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2, 2447*0Sstevel@tonic-gate "async%d_softint: asy_msr & DCD = %x, " 2448*0Sstevel@tonic-gate "tp->t_flags & TS_SOFTCAR = %x\n", 2449*0Sstevel@tonic-gate instance, 2450*0Sstevel@tonic-gate asy->asy_msr & DCD, 2451*0Sstevel@tonic-gate tp->t_flags & TS_SOFTCAR); 2452*0Sstevel@tonic-gate if (asy->asy_msr & DCD) { 2453*0Sstevel@tonic-gate /* carrier present */ 2454*0Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) == 0) { 2455*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODM2, 2456*0Sstevel@tonic-gate "async%d_softint: set ASYNC_CARR_ON\n", 2457*0Sstevel@tonic-gate instance); 2458*0Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON; 2459*0Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 2460*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2461*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2462*0Sstevel@tonic-gate (void) putctl(q, M_UNHANGUP); 2463*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2464*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2465*0Sstevel@tonic-gate } 2466*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 2467*0Sstevel@tonic-gate } 2468*0Sstevel@tonic-gate } else { 2469*0Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) && 2470*0Sstevel@tonic-gate !(tp->t_cflag & CLOCAL) && 2471*0Sstevel@tonic-gate !(tp->t_flags & TS_SOFTCAR)) { 2472*0Sstevel@tonic-gate int flushflag; 2473*0Sstevel@tonic-gate 2474*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 2475*0Sstevel@tonic-gate "async%d_softint: carrier dropped, " 2476*0Sstevel@tonic-gate "so drop DTR\n", 2477*0Sstevel@tonic-gate instance); 2478*0Sstevel@tonic-gate /* 2479*0Sstevel@tonic-gate * Carrier went away. 2480*0Sstevel@tonic-gate * Drop DTR, abort any output in 2481*0Sstevel@tonic-gate * progress, indicate that output is 2482*0Sstevel@tonic-gate * not stopped, and send a hangup 2483*0Sstevel@tonic-gate * notification upstream. 2484*0Sstevel@tonic-gate */ 2485*0Sstevel@tonic-gate val = ddi_io_get8(asy->asy_iohandle, 2486*0Sstevel@tonic-gate asy->asy_ioaddr + MCR); 2487*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 2488*0Sstevel@tonic-gate asy->asy_ioaddr + MCR, (val & ~DTR)); 2489*0Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 2490*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_BUSY, 2491*0Sstevel@tonic-gate "async_softint: " 2492*0Sstevel@tonic-gate "Carrier dropped. " 2493*0Sstevel@tonic-gate "Clearing async_ocnt\n"); 2494*0Sstevel@tonic-gate async->async_ocnt = 0; 2495*0Sstevel@tonic-gate } /* if */ 2496*0Sstevel@tonic-gate 2497*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 2498*0Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 2499*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2500*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2501*0Sstevel@tonic-gate (void) putctl(q, M_HANGUP); 2502*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2503*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 2504*0Sstevel@tonic-gate "async%d_softint: " 2505*0Sstevel@tonic-gate "putctl(q, M_HANGUP)\n", 2506*0Sstevel@tonic-gate instance); 2507*0Sstevel@tonic-gate /* 2508*0Sstevel@tonic-gate * Flush FIFO buffers 2509*0Sstevel@tonic-gate * Any data left in there is invalid now 2510*0Sstevel@tonic-gate */ 2511*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) 2512*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH); 2513*0Sstevel@tonic-gate /* 2514*0Sstevel@tonic-gate * Flush our write queue if we have one. 2515*0Sstevel@tonic-gate * 2516*0Sstevel@tonic-gate * If we're in the midst of close, then flush 2517*0Sstevel@tonic-gate * everything. Don't leave stale ioctls lying 2518*0Sstevel@tonic-gate * about. 2519*0Sstevel@tonic-gate */ 2520*0Sstevel@tonic-gate flushflag = (async->async_flags & 2521*0Sstevel@tonic-gate ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; 2522*0Sstevel@tonic-gate flushq(tp->t_writeq, flushflag); 2523*0Sstevel@tonic-gate 2524*0Sstevel@tonic-gate bp = async->async_xmitblk; /* active msg */ 2525*0Sstevel@tonic-gate if (bp != NULL) { 2526*0Sstevel@tonic-gate freeb(bp); 2527*0Sstevel@tonic-gate async->async_xmitblk = NULL; 2528*0Sstevel@tonic-gate } 2529*0Sstevel@tonic-gate 2530*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2531*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 2532*0Sstevel@tonic-gate /* 2533*0Sstevel@tonic-gate * This message warns of Carrier loss 2534*0Sstevel@tonic-gate * with data left to transmit can hang the 2535*0Sstevel@tonic-gate * system. 2536*0Sstevel@tonic-gate */ 2537*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, 2538*0Sstevel@tonic-gate "async_softint: Flushing to " 2539*0Sstevel@tonic-gate "prevent HUPCL hanging\n"); 2540*0Sstevel@tonic-gate } /* if (ASYNC_ISOPEN) */ 2541*0Sstevel@tonic-gate } /* if (ASYNC_CARR_ON && CLOCAL) */ 2542*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON; 2543*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 2544*0Sstevel@tonic-gate } /* else */ 2545*0Sstevel@tonic-gate } /* if (async->async_ext) */ 2546*0Sstevel@tonic-gate 2547*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2548*0Sstevel@tonic-gate 2549*0Sstevel@tonic-gate /* 2550*0Sstevel@tonic-gate * If data has been added to the circular buffer, remove 2551*0Sstevel@tonic-gate * it from the buffer, and send it up the stream if there's 2552*0Sstevel@tonic-gate * somebody listening. Try to do it 16 bytes at a time. If we 2553*0Sstevel@tonic-gate * have more than 16 bytes to move, move 16 byte chunks and 2554*0Sstevel@tonic-gate * leave the rest for next time around (maybe it will grow). 2555*0Sstevel@tonic-gate */ 2556*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2557*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) { 2558*0Sstevel@tonic-gate RING_INIT(async); 2559*0Sstevel@tonic-gate goto rv; 2560*0Sstevel@tonic-gate } 2561*0Sstevel@tonic-gate if ((cc = RING_CNT(async)) <= 0) 2562*0Sstevel@tonic-gate goto rv; 2563*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2564*0Sstevel@tonic-gate 2565*0Sstevel@tonic-gate if (!canput(q)) { 2566*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2567*0Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 2568*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, 2569*0Sstevel@tonic-gate IN_FLOW_STREAMS); 2570*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 2571*0Sstevel@tonic-gate IN_FLOW_STREAMS); 2572*0Sstevel@tonic-gate } 2573*0Sstevel@tonic-gate goto rv; 2574*0Sstevel@tonic-gate } 2575*0Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_STREAMS) { 2576*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2577*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, 2578*0Sstevel@tonic-gate IN_FLOW_STREAMS); 2579*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 2580*0Sstevel@tonic-gate IN_FLOW_STREAMS); 2581*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2582*0Sstevel@tonic-gate } 2583*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INPUT, 2584*0Sstevel@tonic-gate "async%d_softint: %d char(s) in queue.\n", instance, cc); 2585*0Sstevel@tonic-gate if (!(bp = allocb(cc, BPRI_MED))) { 2586*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2587*0Sstevel@tonic-gate ttycommon_qfull(&async->async_ttycommon, q); 2588*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2589*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2590*0Sstevel@tonic-gate goto rv; 2591*0Sstevel@tonic-gate } 2592*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2593*0Sstevel@tonic-gate do { 2594*0Sstevel@tonic-gate if (RING_ERR(async, S_ERRORS)) { 2595*0Sstevel@tonic-gate RING_UNMARK(async); 2596*0Sstevel@tonic-gate c = RING_GET(async); 2597*0Sstevel@tonic-gate break; 2598*0Sstevel@tonic-gate } else 2599*0Sstevel@tonic-gate *bp->b_wptr++ = RING_GET(async); 2600*0Sstevel@tonic-gate } while (--cc); 2601*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2602*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2603*0Sstevel@tonic-gate if (bp->b_wptr > bp->b_rptr) { 2604*0Sstevel@tonic-gate if (!canput(q)) { 2605*0Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: local queue full", 2606*0Sstevel@tonic-gate instance); 2607*0Sstevel@tonic-gate freemsg(bp); 2608*0Sstevel@tonic-gate } else 2609*0Sstevel@tonic-gate (void) putq(q, bp); 2610*0Sstevel@tonic-gate } else 2611*0Sstevel@tonic-gate freemsg(bp); 2612*0Sstevel@tonic-gate /* 2613*0Sstevel@tonic-gate * If we have a parity error, then send 2614*0Sstevel@tonic-gate * up an M_BREAK with the "bad" 2615*0Sstevel@tonic-gate * character as an argument. Let ldterm 2616*0Sstevel@tonic-gate * figure out what to do with the error. 2617*0Sstevel@tonic-gate */ 2618*0Sstevel@tonic-gate if (cc) { 2619*0Sstevel@tonic-gate (void) putctl1(q, M_BREAK, c); 2620*0Sstevel@tonic-gate ASYSETSOFT(async->async_common); /* finish cc chars */ 2621*0Sstevel@tonic-gate } 2622*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2623*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2624*0Sstevel@tonic-gate rv: 2625*0Sstevel@tonic-gate if ((RING_CNT(async) < (RINGSIZE/4)) && 2626*0Sstevel@tonic-gate (async->async_inflow_source & IN_FLOW_RINGBUFF)) { 2627*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF); 2628*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 2629*0Sstevel@tonic-gate IN_FLOW_RINGBUFF); 2630*0Sstevel@tonic-gate } 2631*0Sstevel@tonic-gate 2632*0Sstevel@tonic-gate /* 2633*0Sstevel@tonic-gate * If a transmission has finished, indicate that it's finished, 2634*0Sstevel@tonic-gate * and start that line up again. 2635*0Sstevel@tonic-gate */ 2636*0Sstevel@tonic-gate if (async->async_break > 0) { 2637*0Sstevel@tonic-gate nb = async->async_break; 2638*0Sstevel@tonic-gate async->async_break = 0; 2639*0Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 2640*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2641*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2642*0Sstevel@tonic-gate for (; nb > 0; nb--) 2643*0Sstevel@tonic-gate (void) putctl(q, M_BREAK); 2644*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2645*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2646*0Sstevel@tonic-gate } 2647*0Sstevel@tonic-gate } 2648*0Sstevel@tonic-gate if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) { 2649*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 2650*0Sstevel@tonic-gate "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n", 2651*0Sstevel@tonic-gate instance, 2652*0Sstevel@tonic-gate async->async_ocnt); 2653*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 2654*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2655*0Sstevel@tonic-gate if (async->async_xmitblk) 2656*0Sstevel@tonic-gate freeb(async->async_xmitblk); 2657*0Sstevel@tonic-gate async->async_xmitblk = NULL; 2658*0Sstevel@tonic-gate async_start(async); 2659*0Sstevel@tonic-gate /* 2660*0Sstevel@tonic-gate * If the flag isn't set after doing the async_start above, we 2661*0Sstevel@tonic-gate * may have finished all the queued output. Signal any thread 2662*0Sstevel@tonic-gate * stuck in close. 2663*0Sstevel@tonic-gate */ 2664*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BUSY)) 2665*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 2666*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2667*0Sstevel@tonic-gate } 2668*0Sstevel@tonic-gate /* 2669*0Sstevel@tonic-gate * A note about these overrun bits: all they do is *tell* someone 2670*0Sstevel@tonic-gate * about an error- They do not track multiple errors. In fact, 2671*0Sstevel@tonic-gate * you could consider them latched register bits if you like. 2672*0Sstevel@tonic-gate * We are only interested in printing the error message once for 2673*0Sstevel@tonic-gate * any cluster of overrun errrors. 2674*0Sstevel@tonic-gate */ 2675*0Sstevel@tonic-gate if (async->async_hw_overrun) { 2676*0Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 2677*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2678*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2679*0Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: silo overflow", instance); 2680*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2681*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2682*0Sstevel@tonic-gate } 2683*0Sstevel@tonic-gate async->async_hw_overrun = 0; 2684*0Sstevel@tonic-gate } 2685*0Sstevel@tonic-gate if (async->async_sw_overrun) { 2686*0Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 2687*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2688*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2689*0Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: ring buffer overflow", 2690*0Sstevel@tonic-gate instance); 2691*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2692*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2693*0Sstevel@tonic-gate } 2694*0Sstevel@tonic-gate async->async_sw_overrun = 0; 2695*0Sstevel@tonic-gate } 2696*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2697*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2698*0Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 2699*0Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT_RETRY) { 2700*0Sstevel@tonic-gate goto begin; 2701*0Sstevel@tonic-gate } 2702*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT; 2703*0Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 2704*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance); 2705*0Sstevel@tonic-gate } 2706*0Sstevel@tonic-gate 2707*0Sstevel@tonic-gate /* 2708*0Sstevel@tonic-gate * Restart output on a line after a delay or break timer expired. 2709*0Sstevel@tonic-gate */ 2710*0Sstevel@tonic-gate static void 2711*0Sstevel@tonic-gate async_restart(void *arg) 2712*0Sstevel@tonic-gate { 2713*0Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)arg; 2714*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 2715*0Sstevel@tonic-gate uchar_t lcr; 2716*0Sstevel@tonic-gate 2717*0Sstevel@tonic-gate /* 2718*0Sstevel@tonic-gate * If break timer expired, turn off the break bit. 2719*0Sstevel@tonic-gate */ 2720*0Sstevel@tonic-gate #ifdef DEBUG 2721*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 2722*0Sstevel@tonic-gate 2723*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance); 2724*0Sstevel@tonic-gate #endif 2725*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2726*0Sstevel@tonic-gate /* 2727*0Sstevel@tonic-gate * If ASYNC_OUT_SUSPEND is also set, we don't really 2728*0Sstevel@tonic-gate * clean the HW break, TIOCCBRK is responsible for this. 2729*0Sstevel@tonic-gate */ 2730*0Sstevel@tonic-gate if ((async->async_flags & ASYNC_BREAK) && 2731*0Sstevel@tonic-gate !(async->async_flags & ASYNC_OUT_SUSPEND)) { 2732*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2733*0Sstevel@tonic-gate lcr = ddi_io_get8(asy->asy_iohandle, 2734*0Sstevel@tonic-gate asy->asy_ioaddr + LCR); 2735*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 2736*0Sstevel@tonic-gate (lcr & ~SETBREAK)); 2737*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2738*0Sstevel@tonic-gate } 2739*0Sstevel@tonic-gate async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); 2740*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 2741*0Sstevel@tonic-gate async_start(async); 2742*0Sstevel@tonic-gate 2743*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2744*0Sstevel@tonic-gate } 2745*0Sstevel@tonic-gate 2746*0Sstevel@tonic-gate static void 2747*0Sstevel@tonic-gate async_start(struct asyncline *async) 2748*0Sstevel@tonic-gate { 2749*0Sstevel@tonic-gate async_nstart(async, 0); 2750*0Sstevel@tonic-gate } 2751*0Sstevel@tonic-gate 2752*0Sstevel@tonic-gate /* 2753*0Sstevel@tonic-gate * Start output on a line, unless it's busy, frozen, or otherwise. 2754*0Sstevel@tonic-gate */ 2755*0Sstevel@tonic-gate /*ARGSUSED*/ 2756*0Sstevel@tonic-gate static void 2757*0Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode) 2758*0Sstevel@tonic-gate { 2759*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 2760*0Sstevel@tonic-gate int cc; 2761*0Sstevel@tonic-gate queue_t *q; 2762*0Sstevel@tonic-gate mblk_t *bp; 2763*0Sstevel@tonic-gate uchar_t *xmit_addr; 2764*0Sstevel@tonic-gate uchar_t val; 2765*0Sstevel@tonic-gate int fifo_len = 1; 2766*0Sstevel@tonic-gate boolean_t didsome; 2767*0Sstevel@tonic-gate mblk_t *nbp; 2768*0Sstevel@tonic-gate 2769*0Sstevel@tonic-gate #ifdef DEBUG 2770*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 2771*0Sstevel@tonic-gate 2772*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance); 2773*0Sstevel@tonic-gate #endif 2774*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 2775*0Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 2776*0Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 2777*0Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 2778*0Sstevel@tonic-gate } 2779*0Sstevel@tonic-gate 2780*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 2781*0Sstevel@tonic-gate 2782*0Sstevel@tonic-gate /* 2783*0Sstevel@tonic-gate * If the chip is busy (i.e., we're waiting for a break timeout 2784*0Sstevel@tonic-gate * to expire, or for the current transmission to finish, or for 2785*0Sstevel@tonic-gate * output to finish draining from chip), don't grab anything new. 2786*0Sstevel@tonic-gate */ 2787*0Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { 2788*0Sstevel@tonic-gate DEBUGCONT2((mode? ASY_DEBUG_OUT : 0), 2789*0Sstevel@tonic-gate "async%d_nstart: start %s.\n", 2790*0Sstevel@tonic-gate instance, 2791*0Sstevel@tonic-gate async->async_flags & ASYNC_BREAK ? "break" : "busy"); 2792*0Sstevel@tonic-gate return; 2793*0Sstevel@tonic-gate } 2794*0Sstevel@tonic-gate 2795*0Sstevel@tonic-gate /* 2796*0Sstevel@tonic-gate * Check only pended sw input flow control. 2797*0Sstevel@tonic-gate */ 2798*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2799*0Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 2800*0Sstevel@tonic-gate fifo_len--; 2801*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2802*0Sstevel@tonic-gate 2803*0Sstevel@tonic-gate /* 2804*0Sstevel@tonic-gate * If we're waiting for a delay timeout to expire, don't grab 2805*0Sstevel@tonic-gate * anything new. 2806*0Sstevel@tonic-gate */ 2807*0Sstevel@tonic-gate if (async->async_flags & ASYNC_DELAY) { 2808*0Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 2809*0Sstevel@tonic-gate "async%d_nstart: start ASYNC_DELAY.\n", instance); 2810*0Sstevel@tonic-gate return; 2811*0Sstevel@tonic-gate } 2812*0Sstevel@tonic-gate 2813*0Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 2814*0Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 2815*0Sstevel@tonic-gate "async%d_nstart: start writeq is null.\n", instance); 2816*0Sstevel@tonic-gate return; /* not attached to a stream */ 2817*0Sstevel@tonic-gate } 2818*0Sstevel@tonic-gate 2819*0Sstevel@tonic-gate for (;;) { 2820*0Sstevel@tonic-gate if ((bp = getq(q)) == NULL) 2821*0Sstevel@tonic-gate return; /* no data to transmit */ 2822*0Sstevel@tonic-gate 2823*0Sstevel@tonic-gate /* 2824*0Sstevel@tonic-gate * We have a message block to work on. 2825*0Sstevel@tonic-gate * Check whether it's a break, a delay, or an ioctl (the latter 2826*0Sstevel@tonic-gate * occurs if the ioctl in question was waiting for the output 2827*0Sstevel@tonic-gate * to drain). If it's one of those, process it immediately. 2828*0Sstevel@tonic-gate */ 2829*0Sstevel@tonic-gate switch (bp->b_datap->db_type) { 2830*0Sstevel@tonic-gate 2831*0Sstevel@tonic-gate case M_BREAK: 2832*0Sstevel@tonic-gate /* 2833*0Sstevel@tonic-gate * Set the break bit, and arrange for "async_restart" 2834*0Sstevel@tonic-gate * to be called in 1/4 second; it will turn the 2835*0Sstevel@tonic-gate * break bit off, and call "async_start" to grab 2836*0Sstevel@tonic-gate * the next message. 2837*0Sstevel@tonic-gate */ 2838*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2839*0Sstevel@tonic-gate val = ddi_io_get8(asy->asy_iohandle, 2840*0Sstevel@tonic-gate asy->asy_ioaddr + LCR); 2841*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 2842*0Sstevel@tonic-gate asy->asy_ioaddr + LCR, (val | SETBREAK)); 2843*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2844*0Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 2845*0Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 2846*0Sstevel@tonic-gate drv_usectohz(1000000)/4); 2847*0Sstevel@tonic-gate freemsg(bp); 2848*0Sstevel@tonic-gate return; /* wait for this to finish */ 2849*0Sstevel@tonic-gate 2850*0Sstevel@tonic-gate case M_DELAY: 2851*0Sstevel@tonic-gate /* 2852*0Sstevel@tonic-gate * Arrange for "async_restart" to be called when the 2853*0Sstevel@tonic-gate * delay expires; it will turn ASYNC_DELAY off, 2854*0Sstevel@tonic-gate * and call "async_start" to grab the next message. 2855*0Sstevel@tonic-gate */ 2856*0Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 2857*0Sstevel@tonic-gate (int)(*(unsigned char *)bp->b_rptr + 6)); 2858*0Sstevel@tonic-gate async->async_flags |= ASYNC_DELAY; 2859*0Sstevel@tonic-gate freemsg(bp); 2860*0Sstevel@tonic-gate return; /* wait for this to finish */ 2861*0Sstevel@tonic-gate 2862*0Sstevel@tonic-gate case M_IOCTL: 2863*0Sstevel@tonic-gate /* 2864*0Sstevel@tonic-gate * This ioctl was waiting for the output ahead of 2865*0Sstevel@tonic-gate * it to drain; obviously, it has. Do it, and 2866*0Sstevel@tonic-gate * then grab the next message after it. 2867*0Sstevel@tonic-gate */ 2868*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2869*0Sstevel@tonic-gate async_ioctl(async, q, bp); 2870*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2871*0Sstevel@tonic-gate continue; 2872*0Sstevel@tonic-gate } 2873*0Sstevel@tonic-gate 2874*0Sstevel@tonic-gate while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { 2875*0Sstevel@tonic-gate nbp = bp->b_cont; 2876*0Sstevel@tonic-gate freeb(bp); 2877*0Sstevel@tonic-gate bp = nbp; 2878*0Sstevel@tonic-gate } 2879*0Sstevel@tonic-gate if (bp != NULL) 2880*0Sstevel@tonic-gate break; 2881*0Sstevel@tonic-gate } 2882*0Sstevel@tonic-gate 2883*0Sstevel@tonic-gate /* 2884*0Sstevel@tonic-gate * We have data to transmit. If output is stopped, put 2885*0Sstevel@tonic-gate * it back and try again later. 2886*0Sstevel@tonic-gate */ 2887*0Sstevel@tonic-gate if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW | 2888*0Sstevel@tonic-gate ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) { 2889*0Sstevel@tonic-gate (void) putbq(q, bp); 2890*0Sstevel@tonic-gate return; 2891*0Sstevel@tonic-gate } 2892*0Sstevel@tonic-gate 2893*0Sstevel@tonic-gate async->async_xmitblk = bp; 2894*0Sstevel@tonic-gate xmit_addr = bp->b_rptr; 2895*0Sstevel@tonic-gate bp = bp->b_cont; 2896*0Sstevel@tonic-gate if (bp != NULL) 2897*0Sstevel@tonic-gate (void) putbq(q, bp); /* not done with this message yet */ 2898*0Sstevel@tonic-gate 2899*0Sstevel@tonic-gate /* 2900*0Sstevel@tonic-gate * In 5-bit mode, the high order bits are used 2901*0Sstevel@tonic-gate * to indicate character sizes less than five, 2902*0Sstevel@tonic-gate * so we need to explicitly mask before transmitting 2903*0Sstevel@tonic-gate */ 2904*0Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 2905*0Sstevel@tonic-gate unsigned char *p = xmit_addr; 2906*0Sstevel@tonic-gate int cnt = cc; 2907*0Sstevel@tonic-gate 2908*0Sstevel@tonic-gate while (cnt--) 2909*0Sstevel@tonic-gate *p++ &= (unsigned char) 0x1f; 2910*0Sstevel@tonic-gate } 2911*0Sstevel@tonic-gate 2912*0Sstevel@tonic-gate /* 2913*0Sstevel@tonic-gate * Set up this block for pseudo-DMA. 2914*0Sstevel@tonic-gate */ 2915*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2916*0Sstevel@tonic-gate /* 2917*0Sstevel@tonic-gate * If the transmitter is ready, shove the first 2918*0Sstevel@tonic-gate * character out. 2919*0Sstevel@tonic-gate */ 2920*0Sstevel@tonic-gate didsome = B_FALSE; 2921*0Sstevel@tonic-gate while (--fifo_len >= 0 && cc > 0) { 2922*0Sstevel@tonic-gate if (!(ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & 2923*0Sstevel@tonic-gate XHRE)) 2924*0Sstevel@tonic-gate break; 2925*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 2926*0Sstevel@tonic-gate *xmit_addr++); 2927*0Sstevel@tonic-gate cc--; 2928*0Sstevel@tonic-gate didsome = B_TRUE; 2929*0Sstevel@tonic-gate } 2930*0Sstevel@tonic-gate async->async_optr = xmit_addr; 2931*0Sstevel@tonic-gate async->async_ocnt = cc; 2932*0Sstevel@tonic-gate if (didsome) 2933*0Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 2934*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 2935*0Sstevel@tonic-gate "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", 2936*0Sstevel@tonic-gate instance, 2937*0Sstevel@tonic-gate async->async_ocnt); 2938*0Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY; 2939*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 2940*0Sstevel@tonic-gate } 2941*0Sstevel@tonic-gate 2942*0Sstevel@tonic-gate /* 2943*0Sstevel@tonic-gate * Resume output by poking the transmitter. 2944*0Sstevel@tonic-gate */ 2945*0Sstevel@tonic-gate static void 2946*0Sstevel@tonic-gate async_resume(struct asyncline *async) 2947*0Sstevel@tonic-gate { 2948*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 2949*0Sstevel@tonic-gate #ifdef DEBUG 2950*0Sstevel@tonic-gate int instance; 2951*0Sstevel@tonic-gate #endif 2952*0Sstevel@tonic-gate 2953*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 2954*0Sstevel@tonic-gate #ifdef DEBUG 2955*0Sstevel@tonic-gate instance = UNIT(async->async_dev); 2956*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance); 2957*0Sstevel@tonic-gate #endif 2958*0Sstevel@tonic-gate 2959*0Sstevel@tonic-gate if (ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) { 2960*0Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 2961*0Sstevel@tonic-gate return; 2962*0Sstevel@tonic-gate if (async->async_ocnt > 0 && 2963*0Sstevel@tonic-gate !(async->async_flags & 2964*0Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) { 2965*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 2966*0Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 2967*0Sstevel@tonic-gate async->async_ocnt--; 2968*0Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 2969*0Sstevel@tonic-gate } 2970*0Sstevel@tonic-gate } 2971*0Sstevel@tonic-gate } 2972*0Sstevel@tonic-gate 2973*0Sstevel@tonic-gate /* 2974*0Sstevel@tonic-gate * Hold the untimed break to last the minimum time. 2975*0Sstevel@tonic-gate */ 2976*0Sstevel@tonic-gate static void 2977*0Sstevel@tonic-gate async_hold_utbrk(void *arg) 2978*0Sstevel@tonic-gate { 2979*0Sstevel@tonic-gate struct asyncline *async = arg; 2980*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 2981*0Sstevel@tonic-gate 2982*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 2983*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_HOLD_UTBRK; 2984*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 2985*0Sstevel@tonic-gate async->async_utbrktid = 0; 2986*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 2987*0Sstevel@tonic-gate } 2988*0Sstevel@tonic-gate 2989*0Sstevel@tonic-gate /* 2990*0Sstevel@tonic-gate * Resume the untimed break. 2991*0Sstevel@tonic-gate */ 2992*0Sstevel@tonic-gate static void 2993*0Sstevel@tonic-gate async_resume_utbrk(struct asyncline *async) 2994*0Sstevel@tonic-gate { 2995*0Sstevel@tonic-gate uchar_t val; 2996*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 2997*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 2998*0Sstevel@tonic-gate 2999*0Sstevel@tonic-gate /* 3000*0Sstevel@tonic-gate * Because the wait time is very short, 3001*0Sstevel@tonic-gate * so we use uninterruptably wait. 3002*0Sstevel@tonic-gate */ 3003*0Sstevel@tonic-gate while (async->async_flags & ASYNC_HOLD_UTBRK) { 3004*0Sstevel@tonic-gate cv_wait(&async->async_flags_cv, &asy->asy_excl); 3005*0Sstevel@tonic-gate } 3006*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3007*0Sstevel@tonic-gate /* 3008*0Sstevel@tonic-gate * Timed break and untimed break can exist simultaneously, 3009*0Sstevel@tonic-gate * if ASYNC_BREAK is also set at here, we don't 3010*0Sstevel@tonic-gate * really clean the HW break. 3011*0Sstevel@tonic-gate */ 3012*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BREAK)) { 3013*0Sstevel@tonic-gate val = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 3014*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 3015*0Sstevel@tonic-gate (val & ~SETBREAK)); 3016*0Sstevel@tonic-gate } 3017*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND; 3018*0Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 3019*0Sstevel@tonic-gate if (async->async_ocnt > 0) { 3020*0Sstevel@tonic-gate async_resume(async); 3021*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3022*0Sstevel@tonic-gate } else { 3023*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 3024*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3025*0Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 3026*0Sstevel@tonic-gate freeb(async->async_xmitblk); 3027*0Sstevel@tonic-gate async->async_xmitblk = NULL; 3028*0Sstevel@tonic-gate } 3029*0Sstevel@tonic-gate async_start(async); 3030*0Sstevel@tonic-gate } 3031*0Sstevel@tonic-gate } 3032*0Sstevel@tonic-gate 3033*0Sstevel@tonic-gate /* 3034*0Sstevel@tonic-gate * Process an "ioctl" message sent down to us. 3035*0Sstevel@tonic-gate * Note that we don't need to get any locks until we are ready to access 3036*0Sstevel@tonic-gate * the hardware. Nothing we access until then is going to be altered 3037*0Sstevel@tonic-gate * outside of the STREAMS framework, so we should be safe. 3038*0Sstevel@tonic-gate */ 3039*0Sstevel@tonic-gate int asydelay = 10000; 3040*0Sstevel@tonic-gate static void 3041*0Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 3042*0Sstevel@tonic-gate { 3043*0Sstevel@tonic-gate struct asycom *asy = async->async_common; 3044*0Sstevel@tonic-gate tty_common_t *tp = &async->async_ttycommon; 3045*0Sstevel@tonic-gate struct iocblk *iocp; 3046*0Sstevel@tonic-gate unsigned datasize; 3047*0Sstevel@tonic-gate int error = 0; 3048*0Sstevel@tonic-gate uchar_t val; 3049*0Sstevel@tonic-gate mblk_t *datamp; 3050*0Sstevel@tonic-gate unsigned int index; 3051*0Sstevel@tonic-gate 3052*0Sstevel@tonic-gate #ifdef DEBUG 3053*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 3054*0Sstevel@tonic-gate 3055*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance); 3056*0Sstevel@tonic-gate #endif 3057*0Sstevel@tonic-gate 3058*0Sstevel@tonic-gate if (tp->t_iocpending != NULL) { 3059*0Sstevel@tonic-gate /* 3060*0Sstevel@tonic-gate * We were holding an "ioctl" response pending the 3061*0Sstevel@tonic-gate * availability of an "mblk" to hold data to be passed up; 3062*0Sstevel@tonic-gate * another "ioctl" came through, which means that "ioctl" 3063*0Sstevel@tonic-gate * must have timed out or been aborted. 3064*0Sstevel@tonic-gate */ 3065*0Sstevel@tonic-gate freemsg(async->async_ttycommon.t_iocpending); 3066*0Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 3067*0Sstevel@tonic-gate } 3068*0Sstevel@tonic-gate 3069*0Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 3070*0Sstevel@tonic-gate 3071*0Sstevel@tonic-gate /* 3072*0Sstevel@tonic-gate * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 3073*0Sstevel@tonic-gate * because this function frees up the message block (mp->b_cont) that 3074*0Sstevel@tonic-gate * contains the user location where we pass back the results. 3075*0Sstevel@tonic-gate * 3076*0Sstevel@tonic-gate * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 3077*0Sstevel@tonic-gate * zaps. We know that ttycommon_ioctl doesn't know any CONS* 3078*0Sstevel@tonic-gate * ioctls, so keep the others safe too. 3079*0Sstevel@tonic-gate */ 3080*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n", 3081*0Sstevel@tonic-gate instance, 3082*0Sstevel@tonic-gate iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 3083*0Sstevel@tonic-gate iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 3084*0Sstevel@tonic-gate iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 3085*0Sstevel@tonic-gate iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : 3086*0Sstevel@tonic-gate "other"); 3087*0Sstevel@tonic-gate switch (iocp->ioc_cmd) { 3088*0Sstevel@tonic-gate case TIOCMGET: 3089*0Sstevel@tonic-gate case TIOCGPPS: 3090*0Sstevel@tonic-gate case TIOCSPPS: 3091*0Sstevel@tonic-gate case TIOCGPPSEV: 3092*0Sstevel@tonic-gate case CONSOPENPOLLEDIO: 3093*0Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 3094*0Sstevel@tonic-gate case CONSSETABORTENABLE: 3095*0Sstevel@tonic-gate case CONSGETABORTENABLE: 3096*0Sstevel@tonic-gate error = -1; /* Do Nothing */ 3097*0Sstevel@tonic-gate break; 3098*0Sstevel@tonic-gate default: 3099*0Sstevel@tonic-gate 3100*0Sstevel@tonic-gate /* 3101*0Sstevel@tonic-gate * The only way in which "ttycommon_ioctl" can fail is if the 3102*0Sstevel@tonic-gate * "ioctl" requires a response containing data to be returned 3103*0Sstevel@tonic-gate * to the user, and no mblk could be allocated for the data. 3104*0Sstevel@tonic-gate * No such "ioctl" alters our state. Thus, we always go ahead 3105*0Sstevel@tonic-gate * and do any state-changes the "ioctl" calls for. If we 3106*0Sstevel@tonic-gate * couldn't allocate the data, "ttycommon_ioctl" has stashed 3107*0Sstevel@tonic-gate * the "ioctl" away safely, so we just call "bufcall" to 3108*0Sstevel@tonic-gate * request that we be called back when we stand a better 3109*0Sstevel@tonic-gate * chance of allocating the data. 3110*0Sstevel@tonic-gate */ 3111*0Sstevel@tonic-gate if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 3112*0Sstevel@tonic-gate if (async->async_wbufcid) 3113*0Sstevel@tonic-gate unbufcall(async->async_wbufcid); 3114*0Sstevel@tonic-gate async->async_wbufcid = bufcall(datasize, BPRI_HI, 3115*0Sstevel@tonic-gate (void (*)(void *)) async_reioctl, 3116*0Sstevel@tonic-gate (void *)(intptr_t)async->async_common->asy_unit); 3117*0Sstevel@tonic-gate return; 3118*0Sstevel@tonic-gate } 3119*0Sstevel@tonic-gate } 3120*0Sstevel@tonic-gate 3121*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3122*0Sstevel@tonic-gate 3123*0Sstevel@tonic-gate if (error == 0) { 3124*0Sstevel@tonic-gate /* 3125*0Sstevel@tonic-gate * "ttycommon_ioctl" did most of the work; we just use the 3126*0Sstevel@tonic-gate * data it set up. 3127*0Sstevel@tonic-gate */ 3128*0Sstevel@tonic-gate switch (iocp->ioc_cmd) { 3129*0Sstevel@tonic-gate 3130*0Sstevel@tonic-gate case TCSETS: 3131*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3132*0Sstevel@tonic-gate if (asy_baudok(asy)) 3133*0Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 3134*0Sstevel@tonic-gate else 3135*0Sstevel@tonic-gate error = EINVAL; 3136*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3137*0Sstevel@tonic-gate break; 3138*0Sstevel@tonic-gate case TCSETSF: 3139*0Sstevel@tonic-gate case TCSETSW: 3140*0Sstevel@tonic-gate case TCSETA: 3141*0Sstevel@tonic-gate case TCSETAW: 3142*0Sstevel@tonic-gate case TCSETAF: 3143*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3144*0Sstevel@tonic-gate if (!asy_baudok(asy)) 3145*0Sstevel@tonic-gate error = EINVAL; 3146*0Sstevel@tonic-gate else { 3147*0Sstevel@tonic-gate if (asy_isbusy(asy)) 3148*0Sstevel@tonic-gate asy_waiteot(asy); 3149*0Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 3150*0Sstevel@tonic-gate } 3151*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3152*0Sstevel@tonic-gate break; 3153*0Sstevel@tonic-gate } 3154*0Sstevel@tonic-gate } else if (error < 0) { 3155*0Sstevel@tonic-gate /* 3156*0Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here. 3157*0Sstevel@tonic-gate */ 3158*0Sstevel@tonic-gate error = 0; 3159*0Sstevel@tonic-gate switch (iocp->ioc_cmd) { 3160*0Sstevel@tonic-gate 3161*0Sstevel@tonic-gate case TIOCGPPS: 3162*0Sstevel@tonic-gate /* 3163*0Sstevel@tonic-gate * Get PPS on/off. 3164*0Sstevel@tonic-gate */ 3165*0Sstevel@tonic-gate if (mp->b_cont != NULL) 3166*0Sstevel@tonic-gate freemsg(mp->b_cont); 3167*0Sstevel@tonic-gate 3168*0Sstevel@tonic-gate mp->b_cont = allocb(sizeof (int), BPRI_HI); 3169*0Sstevel@tonic-gate if (mp->b_cont == NULL) { 3170*0Sstevel@tonic-gate error = ENOMEM; 3171*0Sstevel@tonic-gate break; 3172*0Sstevel@tonic-gate } 3173*0Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 3174*0Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 1; 3175*0Sstevel@tonic-gate else 3176*0Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 0; 3177*0Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int); 3178*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3179*0Sstevel@tonic-gate iocp->ioc_count = sizeof (int); 3180*0Sstevel@tonic-gate break; 3181*0Sstevel@tonic-gate 3182*0Sstevel@tonic-gate case TIOCSPPS: 3183*0Sstevel@tonic-gate /* 3184*0Sstevel@tonic-gate * Set PPS on/off. 3185*0Sstevel@tonic-gate */ 3186*0Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 3187*0Sstevel@tonic-gate if (error != 0) 3188*0Sstevel@tonic-gate break; 3189*0Sstevel@tonic-gate 3190*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3191*0Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr) 3192*0Sstevel@tonic-gate asy->asy_flags |= ASY_PPS; 3193*0Sstevel@tonic-gate else 3194*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS; 3195*0Sstevel@tonic-gate /* Reset edge sense */ 3196*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 3197*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3198*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3199*0Sstevel@tonic-gate break; 3200*0Sstevel@tonic-gate 3201*0Sstevel@tonic-gate case TIOCGPPSEV: 3202*0Sstevel@tonic-gate { 3203*0Sstevel@tonic-gate /* 3204*0Sstevel@tonic-gate * Get PPS event data. 3205*0Sstevel@tonic-gate */ 3206*0Sstevel@tonic-gate mblk_t *bp; 3207*0Sstevel@tonic-gate void *buf; 3208*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 3209*0Sstevel@tonic-gate struct ppsclockev32 p32; 3210*0Sstevel@tonic-gate #endif 3211*0Sstevel@tonic-gate struct ppsclockev ppsclockev; 3212*0Sstevel@tonic-gate 3213*0Sstevel@tonic-gate if (mp->b_cont != NULL) { 3214*0Sstevel@tonic-gate freemsg(mp->b_cont); 3215*0Sstevel@tonic-gate mp->b_cont = NULL; 3216*0Sstevel@tonic-gate } 3217*0Sstevel@tonic-gate 3218*0Sstevel@tonic-gate if ((asy->asy_flags & ASY_PPS) == 0) { 3219*0Sstevel@tonic-gate error = ENXIO; 3220*0Sstevel@tonic-gate break; 3221*0Sstevel@tonic-gate } 3222*0Sstevel@tonic-gate 3223*0Sstevel@tonic-gate /* Protect from incomplete asy_ppsev */ 3224*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3225*0Sstevel@tonic-gate ppsclockev = asy_ppsev; 3226*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3227*0Sstevel@tonic-gate 3228*0Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 3229*0Sstevel@tonic-gate if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 3230*0Sstevel@tonic-gate TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 3231*0Sstevel@tonic-gate p32.serial = ppsclockev.serial; 3232*0Sstevel@tonic-gate buf = &p32; 3233*0Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev32); 3234*0Sstevel@tonic-gate } else 3235*0Sstevel@tonic-gate #endif 3236*0Sstevel@tonic-gate { 3237*0Sstevel@tonic-gate buf = &ppsclockev; 3238*0Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev); 3239*0Sstevel@tonic-gate } 3240*0Sstevel@tonic-gate 3241*0Sstevel@tonic-gate if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 3242*0Sstevel@tonic-gate error = ENOMEM; 3243*0Sstevel@tonic-gate break; 3244*0Sstevel@tonic-gate } 3245*0Sstevel@tonic-gate mp->b_cont = bp; 3246*0Sstevel@tonic-gate 3247*0Sstevel@tonic-gate bcopy(buf, bp->b_wptr, iocp->ioc_count); 3248*0Sstevel@tonic-gate bp->b_wptr += iocp->ioc_count; 3249*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3250*0Sstevel@tonic-gate break; 3251*0Sstevel@tonic-gate } 3252*0Sstevel@tonic-gate 3253*0Sstevel@tonic-gate case TCSBRK: 3254*0Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 3255*0Sstevel@tonic-gate if (error != 0) 3256*0Sstevel@tonic-gate break; 3257*0Sstevel@tonic-gate 3258*0Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) { 3259*0Sstevel@tonic-gate 3260*0Sstevel@tonic-gate /* 3261*0Sstevel@tonic-gate * XXX Arrangements to ensure that a break 3262*0Sstevel@tonic-gate * isn't in progress should be sufficient. 3263*0Sstevel@tonic-gate * This ugly delay() is the only thing 3264*0Sstevel@tonic-gate * that seems to work on the NCR Worldmark. 3265*0Sstevel@tonic-gate * It should be replaced. Note that an 3266*0Sstevel@tonic-gate * asy_waiteot() also does not work. 3267*0Sstevel@tonic-gate */ 3268*0Sstevel@tonic-gate if (asydelay) 3269*0Sstevel@tonic-gate delay(drv_usectohz(asydelay)); 3270*0Sstevel@tonic-gate 3271*0Sstevel@tonic-gate while (async->async_flags & ASYNC_BREAK) { 3272*0Sstevel@tonic-gate cv_wait(&async->async_flags_cv, 3273*0Sstevel@tonic-gate &asy->asy_excl); 3274*0Sstevel@tonic-gate } 3275*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3276*0Sstevel@tonic-gate /* 3277*0Sstevel@tonic-gate * We loop until the TSR is empty and then 3278*0Sstevel@tonic-gate * set the break. ASYNC_BREAK has been set 3279*0Sstevel@tonic-gate * to ensure that no characters are 3280*0Sstevel@tonic-gate * transmitted while the TSR is being 3281*0Sstevel@tonic-gate * flushed and SOUT is being used for the 3282*0Sstevel@tonic-gate * break signal. 3283*0Sstevel@tonic-gate * 3284*0Sstevel@tonic-gate * The wait period is equal to 3285*0Sstevel@tonic-gate * clock / (baud * 16) * 16 * 2. 3286*0Sstevel@tonic-gate */ 3287*0Sstevel@tonic-gate index = BAUDINDEX( 3288*0Sstevel@tonic-gate async->async_ttycommon.t_cflag); 3289*0Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 3290*0Sstevel@tonic-gate while ((ddi_io_get8(asy->asy_iohandle, 3291*0Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 3292*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3293*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3294*0Sstevel@tonic-gate drv_usecwait( 3295*0Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff); 3296*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3297*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3298*0Sstevel@tonic-gate } 3299*0Sstevel@tonic-gate /* 3300*0Sstevel@tonic-gate * Arrange for "async_restart" 3301*0Sstevel@tonic-gate * to be called in 1/4 second; 3302*0Sstevel@tonic-gate * it will turn the break bit off, and call 3303*0Sstevel@tonic-gate * "async_start" to grab the next message. 3304*0Sstevel@tonic-gate */ 3305*0Sstevel@tonic-gate val = ddi_io_get8(asy->asy_iohandle, 3306*0Sstevel@tonic-gate asy->asy_ioaddr + LCR); 3307*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 3308*0Sstevel@tonic-gate asy->asy_ioaddr + LCR, 3309*0Sstevel@tonic-gate (val | SETBREAK)); 3310*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3311*0Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 3312*0Sstevel@tonic-gate drv_usectohz(1000000)/4); 3313*0Sstevel@tonic-gate } else { 3314*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 3315*0Sstevel@tonic-gate "async%d_ioctl: wait for flush.\n", 3316*0Sstevel@tonic-gate instance); 3317*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3318*0Sstevel@tonic-gate asy_waiteot(asy); 3319*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3320*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 3321*0Sstevel@tonic-gate "async%d_ioctl: ldterm satisfied.\n", 3322*0Sstevel@tonic-gate instance); 3323*0Sstevel@tonic-gate } 3324*0Sstevel@tonic-gate break; 3325*0Sstevel@tonic-gate 3326*0Sstevel@tonic-gate case TIOCSBRK: 3327*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_OUT_SUSPEND)) { 3328*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3329*0Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_SUSPEND; 3330*0Sstevel@tonic-gate async->async_flags |= ASYNC_HOLD_UTBRK; 3331*0Sstevel@tonic-gate index = BAUDINDEX( 3332*0Sstevel@tonic-gate async->async_ttycommon.t_cflag); 3333*0Sstevel@tonic-gate while ((ddi_io_get8(asy->asy_iohandle, 3334*0Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 3335*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3336*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3337*0Sstevel@tonic-gate drv_usecwait( 3338*0Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff); 3339*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3340*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3341*0Sstevel@tonic-gate } 3342*0Sstevel@tonic-gate val = ddi_io_get8(asy->asy_iohandle, 3343*0Sstevel@tonic-gate asy->asy_ioaddr + LCR); 3344*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 3345*0Sstevel@tonic-gate asy->asy_ioaddr + LCR, (val | SETBREAK)); 3346*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3347*0Sstevel@tonic-gate /* wait for 100ms to hold BREAK */ 3348*0Sstevel@tonic-gate async->async_utbrktid = 3349*0Sstevel@tonic-gate timeout((void (*)())async_hold_utbrk, 3350*0Sstevel@tonic-gate (caddr_t)async, 3351*0Sstevel@tonic-gate drv_usectohz(asy_min_utbrk)); 3352*0Sstevel@tonic-gate } 3353*0Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 3354*0Sstevel@tonic-gate break; 3355*0Sstevel@tonic-gate 3356*0Sstevel@tonic-gate case TIOCCBRK: 3357*0Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) 3358*0Sstevel@tonic-gate async_resume_utbrk(async); 3359*0Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 3360*0Sstevel@tonic-gate break; 3361*0Sstevel@tonic-gate 3362*0Sstevel@tonic-gate case TIOCMSET: 3363*0Sstevel@tonic-gate case TIOCMBIS: 3364*0Sstevel@tonic-gate case TIOCMBIC: 3365*0Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 3366*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3367*0Sstevel@tonic-gate "non-transparent\n", instance); 3368*0Sstevel@tonic-gate 3369*0Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 3370*0Sstevel@tonic-gate if (error != 0) 3371*0Sstevel@tonic-gate break; 3372*0Sstevel@tonic-gate 3373*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3374*0Sstevel@tonic-gate (void) asymctl(asy, 3375*0Sstevel@tonic-gate dmtoasy(*(int *)mp->b_cont->b_rptr), 3376*0Sstevel@tonic-gate iocp->ioc_cmd); 3377*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3378*0Sstevel@tonic-gate iocp->ioc_error = 0; 3379*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3380*0Sstevel@tonic-gate } else { 3381*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3382*0Sstevel@tonic-gate "transparent\n", instance); 3383*0Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL); 3384*0Sstevel@tonic-gate } 3385*0Sstevel@tonic-gate break; 3386*0Sstevel@tonic-gate 3387*0Sstevel@tonic-gate case TIOCMGET: 3388*0Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED); 3389*0Sstevel@tonic-gate if (datamp == NULL) { 3390*0Sstevel@tonic-gate error = EAGAIN; 3391*0Sstevel@tonic-gate break; 3392*0Sstevel@tonic-gate } 3393*0Sstevel@tonic-gate 3394*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3395*0Sstevel@tonic-gate *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 3396*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3397*0Sstevel@tonic-gate 3398*0Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) { 3399*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3400*0Sstevel@tonic-gate "transparent\n", instance); 3401*0Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (int), NULL, 3402*0Sstevel@tonic-gate datamp); 3403*0Sstevel@tonic-gate } else { 3404*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3405*0Sstevel@tonic-gate "non-transparent\n", instance); 3406*0Sstevel@tonic-gate mioc2ack(mp, datamp, sizeof (int), 0); 3407*0Sstevel@tonic-gate } 3408*0Sstevel@tonic-gate break; 3409*0Sstevel@tonic-gate 3410*0Sstevel@tonic-gate case CONSOPENPOLLEDIO: 3411*0Sstevel@tonic-gate error = miocpullup(mp, sizeof (struct cons_polledio *)); 3412*0Sstevel@tonic-gate if (error != 0) 3413*0Sstevel@tonic-gate break; 3414*0Sstevel@tonic-gate 3415*0Sstevel@tonic-gate *(struct cons_polledio **)mp->b_cont->b_rptr = 3416*0Sstevel@tonic-gate &asy->polledio; 3417*0Sstevel@tonic-gate 3418*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3419*0Sstevel@tonic-gate break; 3420*0Sstevel@tonic-gate 3421*0Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 3422*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3423*0Sstevel@tonic-gate iocp->ioc_error = 0; 3424*0Sstevel@tonic-gate iocp->ioc_rval = 0; 3425*0Sstevel@tonic-gate break; 3426*0Sstevel@tonic-gate 3427*0Sstevel@tonic-gate case CONSSETABORTENABLE: 3428*0Sstevel@tonic-gate error = secpolicy_console(iocp->ioc_cr); 3429*0Sstevel@tonic-gate if (error != 0) 3430*0Sstevel@tonic-gate break; 3431*0Sstevel@tonic-gate 3432*0Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 3433*0Sstevel@tonic-gate error = EINVAL; 3434*0Sstevel@tonic-gate break; 3435*0Sstevel@tonic-gate } 3436*0Sstevel@tonic-gate 3437*0Sstevel@tonic-gate if (*(intptr_t *)mp->b_cont->b_rptr) 3438*0Sstevel@tonic-gate asy->asy_flags |= ASY_CONSOLE; 3439*0Sstevel@tonic-gate else 3440*0Sstevel@tonic-gate asy->asy_flags &= ~ASY_CONSOLE; 3441*0Sstevel@tonic-gate 3442*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3443*0Sstevel@tonic-gate iocp->ioc_error = 0; 3444*0Sstevel@tonic-gate iocp->ioc_rval = 0; 3445*0Sstevel@tonic-gate break; 3446*0Sstevel@tonic-gate 3447*0Sstevel@tonic-gate case CONSGETABORTENABLE: 3448*0Sstevel@tonic-gate /*CONSTANTCONDITION*/ 3449*0Sstevel@tonic-gate ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 3450*0Sstevel@tonic-gate /* 3451*0Sstevel@tonic-gate * Store the return value right in the payload 3452*0Sstevel@tonic-gate * we were passed. Crude. 3453*0Sstevel@tonic-gate */ 3454*0Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 3455*0Sstevel@tonic-gate *(boolean_t *)mp->b_cont->b_rptr = 3456*0Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE) != 0; 3457*0Sstevel@tonic-gate break; 3458*0Sstevel@tonic-gate 3459*0Sstevel@tonic-gate default: 3460*0Sstevel@tonic-gate /* 3461*0Sstevel@tonic-gate * If we don't understand it, it's an error. NAK it. 3462*0Sstevel@tonic-gate */ 3463*0Sstevel@tonic-gate error = EINVAL; 3464*0Sstevel@tonic-gate break; 3465*0Sstevel@tonic-gate } 3466*0Sstevel@tonic-gate } 3467*0Sstevel@tonic-gate if (error != 0) { 3468*0Sstevel@tonic-gate iocp->ioc_error = error; 3469*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 3470*0Sstevel@tonic-gate } 3471*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3472*0Sstevel@tonic-gate qreply(wq, mp); 3473*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance); 3474*0Sstevel@tonic-gate } 3475*0Sstevel@tonic-gate 3476*0Sstevel@tonic-gate static int 3477*0Sstevel@tonic-gate asyrsrv(queue_t *q) 3478*0Sstevel@tonic-gate { 3479*0Sstevel@tonic-gate mblk_t *bp; 3480*0Sstevel@tonic-gate struct asyncline *async; 3481*0Sstevel@tonic-gate 3482*0Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 3483*0Sstevel@tonic-gate 3484*0Sstevel@tonic-gate while (canputnext(q) && (bp = getq(q))) 3485*0Sstevel@tonic-gate putnext(q, bp); 3486*0Sstevel@tonic-gate ASYSETSOFT(async->async_common); 3487*0Sstevel@tonic-gate async->async_polltid = 0; 3488*0Sstevel@tonic-gate return (0); 3489*0Sstevel@tonic-gate } 3490*0Sstevel@tonic-gate 3491*0Sstevel@tonic-gate /* 3492*0Sstevel@tonic-gate * Put procedure for write queue. 3493*0Sstevel@tonic-gate * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 3494*0Sstevel@tonic-gate * set the flow control character for M_STOPI and M_STARTI messages; 3495*0Sstevel@tonic-gate * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 3496*0Sstevel@tonic-gate * by the start routine, and then call the start routine; discard 3497*0Sstevel@tonic-gate * everything else. Note that this driver does not incorporate any 3498*0Sstevel@tonic-gate * mechanism to negotiate to handle the canonicalization process. 3499*0Sstevel@tonic-gate * It expects that these functions are handled in upper module(s), 3500*0Sstevel@tonic-gate * as we do in ldterm. 3501*0Sstevel@tonic-gate */ 3502*0Sstevel@tonic-gate static int 3503*0Sstevel@tonic-gate asywput(queue_t *q, mblk_t *mp) 3504*0Sstevel@tonic-gate { 3505*0Sstevel@tonic-gate struct asyncline *async; 3506*0Sstevel@tonic-gate struct asycom *asy; 3507*0Sstevel@tonic-gate #ifdef DEBUG 3508*0Sstevel@tonic-gate int instance; 3509*0Sstevel@tonic-gate #endif 3510*0Sstevel@tonic-gate int error; 3511*0Sstevel@tonic-gate 3512*0Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 3513*0Sstevel@tonic-gate #ifdef DEBUG 3514*0Sstevel@tonic-gate instance = UNIT(async->async_dev); 3515*0Sstevel@tonic-gate #endif 3516*0Sstevel@tonic-gate asy = async->async_common; 3517*0Sstevel@tonic-gate 3518*0Sstevel@tonic-gate switch (mp->b_datap->db_type) { 3519*0Sstevel@tonic-gate 3520*0Sstevel@tonic-gate case M_STOP: 3521*0Sstevel@tonic-gate /* 3522*0Sstevel@tonic-gate * Since we don't do real DMA, we can just let the 3523*0Sstevel@tonic-gate * chip coast to a stop after applying the brakes. 3524*0Sstevel@tonic-gate */ 3525*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3526*0Sstevel@tonic-gate async->async_flags |= ASYNC_STOPPED; 3527*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3528*0Sstevel@tonic-gate freemsg(mp); 3529*0Sstevel@tonic-gate break; 3530*0Sstevel@tonic-gate 3531*0Sstevel@tonic-gate case M_START: 3532*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3533*0Sstevel@tonic-gate if (async->async_flags & ASYNC_STOPPED) { 3534*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 3535*0Sstevel@tonic-gate /* 3536*0Sstevel@tonic-gate * If an output operation is in progress, 3537*0Sstevel@tonic-gate * resume it. Otherwise, prod the start 3538*0Sstevel@tonic-gate * routine. 3539*0Sstevel@tonic-gate */ 3540*0Sstevel@tonic-gate if (async->async_ocnt > 0) { 3541*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3542*0Sstevel@tonic-gate async_resume(async); 3543*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3544*0Sstevel@tonic-gate } else { 3545*0Sstevel@tonic-gate async_start(async); 3546*0Sstevel@tonic-gate } 3547*0Sstevel@tonic-gate } 3548*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3549*0Sstevel@tonic-gate freemsg(mp); 3550*0Sstevel@tonic-gate break; 3551*0Sstevel@tonic-gate 3552*0Sstevel@tonic-gate case M_IOCTL: 3553*0Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 3554*0Sstevel@tonic-gate 3555*0Sstevel@tonic-gate case TCSBRK: 3556*0Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 3557*0Sstevel@tonic-gate if (error != 0) { 3558*0Sstevel@tonic-gate miocnak(q, mp, 0, error); 3559*0Sstevel@tonic-gate return (0); 3560*0Sstevel@tonic-gate } 3561*0Sstevel@tonic-gate 3562*0Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr != 0) { 3563*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 3564*0Sstevel@tonic-gate "async%d_ioctl: flush request.\n", 3565*0Sstevel@tonic-gate instance); 3566*0Sstevel@tonic-gate (void) putq(q, mp); 3567*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3568*0Sstevel@tonic-gate 3569*0Sstevel@tonic-gate /* 3570*0Sstevel@tonic-gate * If an TIOCSBRK is in progress, 3571*0Sstevel@tonic-gate * clean it as TIOCCBRK does, 3572*0Sstevel@tonic-gate * then kick off output. 3573*0Sstevel@tonic-gate * If TIOCSBRK is not in progress, 3574*0Sstevel@tonic-gate * just kick off output. 3575*0Sstevel@tonic-gate */ 3576*0Sstevel@tonic-gate async_resume_utbrk(async); 3577*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3578*0Sstevel@tonic-gate break; 3579*0Sstevel@tonic-gate } 3580*0Sstevel@tonic-gate /*FALLTHROUGH*/ 3581*0Sstevel@tonic-gate case TCSETSW: 3582*0Sstevel@tonic-gate case TCSETSF: 3583*0Sstevel@tonic-gate case TCSETAW: 3584*0Sstevel@tonic-gate case TCSETAF: 3585*0Sstevel@tonic-gate /* 3586*0Sstevel@tonic-gate * The changes do not take effect until all 3587*0Sstevel@tonic-gate * output queued before them is drained. 3588*0Sstevel@tonic-gate * Put this message on the queue, so that 3589*0Sstevel@tonic-gate * "async_start" will see it when it's done 3590*0Sstevel@tonic-gate * with the output before it. Poke the 3591*0Sstevel@tonic-gate * start routine, just in case. 3592*0Sstevel@tonic-gate */ 3593*0Sstevel@tonic-gate (void) putq(q, mp); 3594*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3595*0Sstevel@tonic-gate 3596*0Sstevel@tonic-gate /* 3597*0Sstevel@tonic-gate * If an TIOCSBRK is in progress, 3598*0Sstevel@tonic-gate * clean it as TIOCCBRK does. 3599*0Sstevel@tonic-gate * then kick off output. 3600*0Sstevel@tonic-gate * If TIOCSBRK is not in progress, 3601*0Sstevel@tonic-gate * just kick off output. 3602*0Sstevel@tonic-gate */ 3603*0Sstevel@tonic-gate async_resume_utbrk(async); 3604*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3605*0Sstevel@tonic-gate break; 3606*0Sstevel@tonic-gate 3607*0Sstevel@tonic-gate default: 3608*0Sstevel@tonic-gate /* 3609*0Sstevel@tonic-gate * Do it now. 3610*0Sstevel@tonic-gate */ 3611*0Sstevel@tonic-gate async_ioctl(async, q, mp); 3612*0Sstevel@tonic-gate break; 3613*0Sstevel@tonic-gate } 3614*0Sstevel@tonic-gate break; 3615*0Sstevel@tonic-gate 3616*0Sstevel@tonic-gate case M_FLUSH: 3617*0Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) { 3618*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3619*0Sstevel@tonic-gate 3620*0Sstevel@tonic-gate /* 3621*0Sstevel@tonic-gate * Abort any output in progress. 3622*0Sstevel@tonic-gate */ 3623*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3624*0Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 3625*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " 3626*0Sstevel@tonic-gate "Clearing async_ocnt, " 3627*0Sstevel@tonic-gate "leaving ASYNC_BUSY set\n", 3628*0Sstevel@tonic-gate instance); 3629*0Sstevel@tonic-gate async->async_ocnt = 0; 3630*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 3631*0Sstevel@tonic-gate } /* if */ 3632*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3633*0Sstevel@tonic-gate 3634*0Sstevel@tonic-gate /* Flush FIFO buffers */ 3635*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 3636*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH); 3637*0Sstevel@tonic-gate } 3638*0Sstevel@tonic-gate 3639*0Sstevel@tonic-gate /* 3640*0Sstevel@tonic-gate * Flush our write queue. 3641*0Sstevel@tonic-gate */ 3642*0Sstevel@tonic-gate flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 3643*0Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 3644*0Sstevel@tonic-gate freeb(async->async_xmitblk); 3645*0Sstevel@tonic-gate async->async_xmitblk = NULL; 3646*0Sstevel@tonic-gate } 3647*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3648*0Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 3649*0Sstevel@tonic-gate } 3650*0Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) { 3651*0Sstevel@tonic-gate /* Flush FIFO buffers */ 3652*0Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 3653*0Sstevel@tonic-gate asy_reset_fifo(asy, FIFORXFLSH); 3654*0Sstevel@tonic-gate } 3655*0Sstevel@tonic-gate flushq(RD(q), FLUSHDATA); 3656*0Sstevel@tonic-gate qreply(q, mp); /* give the read queues a crack at it */ 3657*0Sstevel@tonic-gate } else { 3658*0Sstevel@tonic-gate freemsg(mp); 3659*0Sstevel@tonic-gate } 3660*0Sstevel@tonic-gate 3661*0Sstevel@tonic-gate /* 3662*0Sstevel@tonic-gate * We must make sure we process messages that survive the 3663*0Sstevel@tonic-gate * write-side flush. 3664*0Sstevel@tonic-gate */ 3665*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3666*0Sstevel@tonic-gate async_start(async); 3667*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3668*0Sstevel@tonic-gate break; 3669*0Sstevel@tonic-gate 3670*0Sstevel@tonic-gate case M_BREAK: 3671*0Sstevel@tonic-gate case M_DELAY: 3672*0Sstevel@tonic-gate case M_DATA: 3673*0Sstevel@tonic-gate /* 3674*0Sstevel@tonic-gate * Queue the message up to be transmitted, 3675*0Sstevel@tonic-gate * and poke the start routine. 3676*0Sstevel@tonic-gate */ 3677*0Sstevel@tonic-gate (void) putq(q, mp); 3678*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3679*0Sstevel@tonic-gate async_start(async); 3680*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3681*0Sstevel@tonic-gate break; 3682*0Sstevel@tonic-gate 3683*0Sstevel@tonic-gate case M_STOPI: 3684*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3685*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3686*0Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_USER)) { 3687*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, 3688*0Sstevel@tonic-gate IN_FLOW_USER); 3689*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 3690*0Sstevel@tonic-gate IN_FLOW_USER); 3691*0Sstevel@tonic-gate } 3692*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3693*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3694*0Sstevel@tonic-gate freemsg(mp); 3695*0Sstevel@tonic-gate break; 3696*0Sstevel@tonic-gate 3697*0Sstevel@tonic-gate case M_STARTI: 3698*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3699*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3700*0Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_USER) { 3701*0Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, 3702*0Sstevel@tonic-gate IN_FLOW_USER); 3703*0Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 3704*0Sstevel@tonic-gate IN_FLOW_USER); 3705*0Sstevel@tonic-gate } 3706*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3707*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3708*0Sstevel@tonic-gate freemsg(mp); 3709*0Sstevel@tonic-gate break; 3710*0Sstevel@tonic-gate 3711*0Sstevel@tonic-gate case M_CTL: 3712*0Sstevel@tonic-gate if (MBLKL(mp) >= sizeof (struct iocblk) && 3713*0Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 3714*0Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; 3715*0Sstevel@tonic-gate qreply(q, mp); 3716*0Sstevel@tonic-gate } else { 3717*0Sstevel@tonic-gate /* 3718*0Sstevel@tonic-gate * These MC_SERVICE type messages are used by upper 3719*0Sstevel@tonic-gate * modules to tell this driver to send input up 3720*0Sstevel@tonic-gate * immediately, or that it can wait for normal 3721*0Sstevel@tonic-gate * processing that may or may not be done. Sun 3722*0Sstevel@tonic-gate * requires these for the mouse module. 3723*0Sstevel@tonic-gate * (XXX - for x86?) 3724*0Sstevel@tonic-gate */ 3725*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3726*0Sstevel@tonic-gate switch (*mp->b_rptr) { 3727*0Sstevel@tonic-gate 3728*0Sstevel@tonic-gate case MC_SERVICEIMM: 3729*0Sstevel@tonic-gate async->async_flags |= ASYNC_SERVICEIMM; 3730*0Sstevel@tonic-gate break; 3731*0Sstevel@tonic-gate 3732*0Sstevel@tonic-gate case MC_SERVICEDEF: 3733*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_SERVICEIMM; 3734*0Sstevel@tonic-gate break; 3735*0Sstevel@tonic-gate } 3736*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3737*0Sstevel@tonic-gate freemsg(mp); 3738*0Sstevel@tonic-gate } 3739*0Sstevel@tonic-gate break; 3740*0Sstevel@tonic-gate 3741*0Sstevel@tonic-gate case M_IOCDATA: 3742*0Sstevel@tonic-gate async_iocdata(q, mp); 3743*0Sstevel@tonic-gate break; 3744*0Sstevel@tonic-gate 3745*0Sstevel@tonic-gate default: 3746*0Sstevel@tonic-gate freemsg(mp); 3747*0Sstevel@tonic-gate break; 3748*0Sstevel@tonic-gate } 3749*0Sstevel@tonic-gate return (0); 3750*0Sstevel@tonic-gate } 3751*0Sstevel@tonic-gate 3752*0Sstevel@tonic-gate /* 3753*0Sstevel@tonic-gate * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 3754*0Sstevel@tonic-gate * the buffer we need. 3755*0Sstevel@tonic-gate */ 3756*0Sstevel@tonic-gate static void 3757*0Sstevel@tonic-gate async_reioctl(void *unit) 3758*0Sstevel@tonic-gate { 3759*0Sstevel@tonic-gate int instance = (uintptr_t)unit; 3760*0Sstevel@tonic-gate struct asyncline *async; 3761*0Sstevel@tonic-gate struct asycom *asy; 3762*0Sstevel@tonic-gate queue_t *q; 3763*0Sstevel@tonic-gate mblk_t *mp; 3764*0Sstevel@tonic-gate 3765*0Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 3766*0Sstevel@tonic-gate ASSERT(asy != NULL); 3767*0Sstevel@tonic-gate async = asy->asy_priv; 3768*0Sstevel@tonic-gate 3769*0Sstevel@tonic-gate /* 3770*0Sstevel@tonic-gate * The bufcall is no longer pending. 3771*0Sstevel@tonic-gate */ 3772*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3773*0Sstevel@tonic-gate async->async_wbufcid = 0; 3774*0Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 3775*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3776*0Sstevel@tonic-gate return; 3777*0Sstevel@tonic-gate } 3778*0Sstevel@tonic-gate if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 3779*0Sstevel@tonic-gate /* not pending any more */ 3780*0Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 3781*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3782*0Sstevel@tonic-gate async_ioctl(async, q, mp); 3783*0Sstevel@tonic-gate } else 3784*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3785*0Sstevel@tonic-gate } 3786*0Sstevel@tonic-gate 3787*0Sstevel@tonic-gate static void 3788*0Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp) 3789*0Sstevel@tonic-gate { 3790*0Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)q->q_ptr; 3791*0Sstevel@tonic-gate struct asycom *asy; 3792*0Sstevel@tonic-gate struct iocblk *ip; 3793*0Sstevel@tonic-gate struct copyresp *csp; 3794*0Sstevel@tonic-gate #ifdef DEBUG 3795*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 3796*0Sstevel@tonic-gate #endif 3797*0Sstevel@tonic-gate 3798*0Sstevel@tonic-gate asy = async->async_common; 3799*0Sstevel@tonic-gate ip = (struct iocblk *)mp->b_rptr; 3800*0Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr; 3801*0Sstevel@tonic-gate 3802*0Sstevel@tonic-gate if (csp->cp_rval != 0) { 3803*0Sstevel@tonic-gate if (csp->cp_private) 3804*0Sstevel@tonic-gate freemsg(csp->cp_private); 3805*0Sstevel@tonic-gate freemsg(mp); 3806*0Sstevel@tonic-gate return; 3807*0Sstevel@tonic-gate } 3808*0Sstevel@tonic-gate 3809*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 3810*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n", 3811*0Sstevel@tonic-gate instance, 3812*0Sstevel@tonic-gate csp->cp_cmd == TIOCMGET ? "TIOCMGET" : 3813*0Sstevel@tonic-gate csp->cp_cmd == TIOCMSET ? "TIOCMSET" : 3814*0Sstevel@tonic-gate csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : 3815*0Sstevel@tonic-gate "TIOCMBIC"); 3816*0Sstevel@tonic-gate switch (csp->cp_cmd) { 3817*0Sstevel@tonic-gate 3818*0Sstevel@tonic-gate case TIOCMGET: 3819*0Sstevel@tonic-gate if (mp->b_cont) { 3820*0Sstevel@tonic-gate freemsg(mp->b_cont); 3821*0Sstevel@tonic-gate mp->b_cont = NULL; 3822*0Sstevel@tonic-gate } 3823*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 3824*0Sstevel@tonic-gate ip->ioc_error = 0; 3825*0Sstevel@tonic-gate ip->ioc_count = 0; 3826*0Sstevel@tonic-gate ip->ioc_rval = 0; 3827*0Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 3828*0Sstevel@tonic-gate break; 3829*0Sstevel@tonic-gate 3830*0Sstevel@tonic-gate case TIOCMSET: 3831*0Sstevel@tonic-gate case TIOCMBIS: 3832*0Sstevel@tonic-gate case TIOCMBIC: 3833*0Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 3834*0Sstevel@tonic-gate (void) asymctl(asy, 3835*0Sstevel@tonic-gate dmtoasy(*(int *)mp->b_cont->b_rptr), 3836*0Sstevel@tonic-gate csp->cp_cmd); 3837*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 3838*0Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 3839*0Sstevel@tonic-gate break; 3840*0Sstevel@tonic-gate 3841*0Sstevel@tonic-gate default: 3842*0Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 3843*0Sstevel@tonic-gate ip->ioc_error = EINVAL; 3844*0Sstevel@tonic-gate break; 3845*0Sstevel@tonic-gate } 3846*0Sstevel@tonic-gate qreply(q, mp); 3847*0Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 3848*0Sstevel@tonic-gate } 3849*0Sstevel@tonic-gate 3850*0Sstevel@tonic-gate /* 3851*0Sstevel@tonic-gate * debugger/console support routines. 3852*0Sstevel@tonic-gate */ 3853*0Sstevel@tonic-gate 3854*0Sstevel@tonic-gate /* 3855*0Sstevel@tonic-gate * put a character out 3856*0Sstevel@tonic-gate * Do not use interrupts. If char is LF, put out CR, LF. 3857*0Sstevel@tonic-gate */ 3858*0Sstevel@tonic-gate static void 3859*0Sstevel@tonic-gate asyputchar(struct cons_polledio_arg *arg, uchar_t c) 3860*0Sstevel@tonic-gate { 3861*0Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 3862*0Sstevel@tonic-gate 3863*0Sstevel@tonic-gate if (c == '\n') 3864*0Sstevel@tonic-gate asyputchar(arg, '\r'); 3865*0Sstevel@tonic-gate 3866*0Sstevel@tonic-gate while ((ddi_io_get8(asy->asy_iohandle, 3867*0Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XHRE) == 0) { 3868*0Sstevel@tonic-gate /* wait for xmit to finish */ 3869*0Sstevel@tonic-gate drv_usecwait(10); 3870*0Sstevel@tonic-gate } 3871*0Sstevel@tonic-gate 3872*0Sstevel@tonic-gate /* put the character out */ 3873*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c); 3874*0Sstevel@tonic-gate } 3875*0Sstevel@tonic-gate 3876*0Sstevel@tonic-gate /* 3877*0Sstevel@tonic-gate * See if there's a character available. If no character is 3878*0Sstevel@tonic-gate * available, return 0. Run in polled mode, no interrupts. 3879*0Sstevel@tonic-gate */ 3880*0Sstevel@tonic-gate static boolean_t 3881*0Sstevel@tonic-gate asyischar(struct cons_polledio_arg *arg) 3882*0Sstevel@tonic-gate { 3883*0Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 3884*0Sstevel@tonic-gate 3885*0Sstevel@tonic-gate return ((ddi_io_get8(asy->asy_iohandle, 3886*0Sstevel@tonic-gate asy->asy_ioaddr + LSR) & RCA) != 0); 3887*0Sstevel@tonic-gate } 3888*0Sstevel@tonic-gate 3889*0Sstevel@tonic-gate /* 3890*0Sstevel@tonic-gate * Get a character. Run in polled mode, no interrupts. 3891*0Sstevel@tonic-gate */ 3892*0Sstevel@tonic-gate static int 3893*0Sstevel@tonic-gate asygetchar(struct cons_polledio_arg *arg) 3894*0Sstevel@tonic-gate { 3895*0Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 3896*0Sstevel@tonic-gate 3897*0Sstevel@tonic-gate while (!asyischar(arg)) 3898*0Sstevel@tonic-gate drv_usecwait(10); 3899*0Sstevel@tonic-gate return (ddi_io_get8(asy->asy_iohandle, 3900*0Sstevel@tonic-gate asy->asy_ioaddr + DAT)); 3901*0Sstevel@tonic-gate } 3902*0Sstevel@tonic-gate 3903*0Sstevel@tonic-gate /* 3904*0Sstevel@tonic-gate * Set or get the modem control status. 3905*0Sstevel@tonic-gate */ 3906*0Sstevel@tonic-gate static int 3907*0Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how) 3908*0Sstevel@tonic-gate { 3909*0Sstevel@tonic-gate int mcr_r, msr_r; 3910*0Sstevel@tonic-gate int instance = asy->asy_unit; 3911*0Sstevel@tonic-gate 3912*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 3913*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 3914*0Sstevel@tonic-gate 3915*0Sstevel@tonic-gate /* Read Modem Control Registers */ 3916*0Sstevel@tonic-gate mcr_r = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 3917*0Sstevel@tonic-gate 3918*0Sstevel@tonic-gate switch (how) { 3919*0Sstevel@tonic-gate 3920*0Sstevel@tonic-gate case TIOCMSET: 3921*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 3922*0Sstevel@tonic-gate "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); 3923*0Sstevel@tonic-gate mcr_r = bits; /* Set bits */ 3924*0Sstevel@tonic-gate break; 3925*0Sstevel@tonic-gate 3926*0Sstevel@tonic-gate case TIOCMBIS: 3927*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n", 3928*0Sstevel@tonic-gate instance, bits); 3929*0Sstevel@tonic-gate mcr_r |= bits; /* Mask in bits */ 3930*0Sstevel@tonic-gate break; 3931*0Sstevel@tonic-gate 3932*0Sstevel@tonic-gate case TIOCMBIC: 3933*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n", 3934*0Sstevel@tonic-gate instance, bits); 3935*0Sstevel@tonic-gate mcr_r &= ~bits; /* Mask out bits */ 3936*0Sstevel@tonic-gate break; 3937*0Sstevel@tonic-gate 3938*0Sstevel@tonic-gate case TIOCMGET: 3939*0Sstevel@tonic-gate /* Read Modem Status Registers */ 3940*0Sstevel@tonic-gate /* 3941*0Sstevel@tonic-gate * If modem interrupts are enabled, we return the 3942*0Sstevel@tonic-gate * saved value of msr. We read MSR only in async_msint() 3943*0Sstevel@tonic-gate */ 3944*0Sstevel@tonic-gate if (ddi_io_get8(asy->asy_iohandle, 3945*0Sstevel@tonic-gate asy->asy_ioaddr + ICR) & MIEN) { 3946*0Sstevel@tonic-gate msr_r = asy->asy_msr; 3947*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 3948*0Sstevel@tonic-gate "asy%dmctl: TIOCMGET, read msr_r = %x\n", 3949*0Sstevel@tonic-gate instance, msr_r); 3950*0Sstevel@tonic-gate } else { 3951*0Sstevel@tonic-gate msr_r = ddi_io_get8(asy->asy_iohandle, 3952*0Sstevel@tonic-gate asy->asy_ioaddr + MSR); 3953*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 3954*0Sstevel@tonic-gate "asy%dmctl: TIOCMGET, read MSR = %x\n", 3955*0Sstevel@tonic-gate instance, msr_r); 3956*0Sstevel@tonic-gate } 3957*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n", 3958*0Sstevel@tonic-gate instance, asytodm(mcr_r, msr_r)); 3959*0Sstevel@tonic-gate return (asytodm(mcr_r, msr_r)); 3960*0Sstevel@tonic-gate } 3961*0Sstevel@tonic-gate 3962*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r); 3963*0Sstevel@tonic-gate 3964*0Sstevel@tonic-gate return (mcr_r); 3965*0Sstevel@tonic-gate } 3966*0Sstevel@tonic-gate 3967*0Sstevel@tonic-gate static int 3968*0Sstevel@tonic-gate asytodm(int mcr_r, int msr_r) 3969*0Sstevel@tonic-gate { 3970*0Sstevel@tonic-gate int b = 0; 3971*0Sstevel@tonic-gate 3972*0Sstevel@tonic-gate /* MCR registers */ 3973*0Sstevel@tonic-gate if (mcr_r & RTS) 3974*0Sstevel@tonic-gate b |= TIOCM_RTS; 3975*0Sstevel@tonic-gate 3976*0Sstevel@tonic-gate if (mcr_r & DTR) 3977*0Sstevel@tonic-gate b |= TIOCM_DTR; 3978*0Sstevel@tonic-gate 3979*0Sstevel@tonic-gate /* MSR registers */ 3980*0Sstevel@tonic-gate if (msr_r & DCD) 3981*0Sstevel@tonic-gate b |= TIOCM_CAR; 3982*0Sstevel@tonic-gate 3983*0Sstevel@tonic-gate if (msr_r & CTS) 3984*0Sstevel@tonic-gate b |= TIOCM_CTS; 3985*0Sstevel@tonic-gate 3986*0Sstevel@tonic-gate if (msr_r & DSR) 3987*0Sstevel@tonic-gate b |= TIOCM_DSR; 3988*0Sstevel@tonic-gate 3989*0Sstevel@tonic-gate if (msr_r & RI) 3990*0Sstevel@tonic-gate b |= TIOCM_RNG; 3991*0Sstevel@tonic-gate return (b); 3992*0Sstevel@tonic-gate } 3993*0Sstevel@tonic-gate 3994*0Sstevel@tonic-gate static int 3995*0Sstevel@tonic-gate dmtoasy(int bits) 3996*0Sstevel@tonic-gate { 3997*0Sstevel@tonic-gate int b = 0; 3998*0Sstevel@tonic-gate 3999*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits); 4000*0Sstevel@tonic-gate #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 4001*0Sstevel@tonic-gate if (bits & TIOCM_CAR) 4002*0Sstevel@tonic-gate b |= DCD; 4003*0Sstevel@tonic-gate if (bits & TIOCM_CTS) 4004*0Sstevel@tonic-gate b |= CTS; 4005*0Sstevel@tonic-gate if (bits & TIOCM_DSR) 4006*0Sstevel@tonic-gate b |= DSR; 4007*0Sstevel@tonic-gate if (bits & TIOCM_RNG) 4008*0Sstevel@tonic-gate b |= RI; 4009*0Sstevel@tonic-gate #endif 4010*0Sstevel@tonic-gate 4011*0Sstevel@tonic-gate if (bits & TIOCM_RTS) { 4012*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n"); 4013*0Sstevel@tonic-gate b |= RTS; 4014*0Sstevel@tonic-gate } 4015*0Sstevel@tonic-gate if (bits & TIOCM_DTR) { 4016*0Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n"); 4017*0Sstevel@tonic-gate b |= DTR; 4018*0Sstevel@tonic-gate } 4019*0Sstevel@tonic-gate 4020*0Sstevel@tonic-gate return (b); 4021*0Sstevel@tonic-gate } 4022*0Sstevel@tonic-gate 4023*0Sstevel@tonic-gate static void 4024*0Sstevel@tonic-gate asyerror(int level, const char *fmt, ...) 4025*0Sstevel@tonic-gate { 4026*0Sstevel@tonic-gate va_list adx; 4027*0Sstevel@tonic-gate static time_t last; 4028*0Sstevel@tonic-gate static const char *lastfmt; 4029*0Sstevel@tonic-gate time_t now; 4030*0Sstevel@tonic-gate 4031*0Sstevel@tonic-gate /* 4032*0Sstevel@tonic-gate * Don't print the same error message too often. 4033*0Sstevel@tonic-gate * Print the message only if we have not printed the 4034*0Sstevel@tonic-gate * message within the last second. 4035*0Sstevel@tonic-gate * Note: that fmt cannot be a pointer to a string 4036*0Sstevel@tonic-gate * stored on the stack. The fmt pointer 4037*0Sstevel@tonic-gate * must be in the data segment otherwise lastfmt would point 4038*0Sstevel@tonic-gate * to non-sense. 4039*0Sstevel@tonic-gate */ 4040*0Sstevel@tonic-gate now = gethrestime_sec(); 4041*0Sstevel@tonic-gate if (last == now && lastfmt == fmt) 4042*0Sstevel@tonic-gate return; 4043*0Sstevel@tonic-gate 4044*0Sstevel@tonic-gate last = now; 4045*0Sstevel@tonic-gate lastfmt = fmt; 4046*0Sstevel@tonic-gate 4047*0Sstevel@tonic-gate va_start(adx, fmt); 4048*0Sstevel@tonic-gate vcmn_err(level, fmt, adx); 4049*0Sstevel@tonic-gate va_end(adx); 4050*0Sstevel@tonic-gate } 4051*0Sstevel@tonic-gate 4052*0Sstevel@tonic-gate /* 4053*0Sstevel@tonic-gate * asy_parse_mode(dev_info_t *devi, struct asycom *asy) 4054*0Sstevel@tonic-gate * The value of this property is in the form of "9600,8,n,1,-" 4055*0Sstevel@tonic-gate * 1) speed: 9600, 4800, ... 4056*0Sstevel@tonic-gate * 2) data bits 4057*0Sstevel@tonic-gate * 3) parity: n(none), e(even), o(odd) 4058*0Sstevel@tonic-gate * 4) stop bits 4059*0Sstevel@tonic-gate * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off) 4060*0Sstevel@tonic-gate * 4061*0Sstevel@tonic-gate * This parsing came from a SPARCstation eeprom. 4062*0Sstevel@tonic-gate */ 4063*0Sstevel@tonic-gate static void 4064*0Sstevel@tonic-gate asy_parse_mode(dev_info_t *devi, struct asycom *asy) 4065*0Sstevel@tonic-gate { 4066*0Sstevel@tonic-gate char name[40]; 4067*0Sstevel@tonic-gate char val[40]; 4068*0Sstevel@tonic-gate int len; 4069*0Sstevel@tonic-gate int ret; 4070*0Sstevel@tonic-gate char *p; 4071*0Sstevel@tonic-gate char *p1; 4072*0Sstevel@tonic-gate 4073*0Sstevel@tonic-gate ASSERT(asy->asy_com_port != 0); 4074*0Sstevel@tonic-gate 4075*0Sstevel@tonic-gate /* 4076*0Sstevel@tonic-gate * Parse the ttyx-mode property 4077*0Sstevel@tonic-gate */ 4078*0Sstevel@tonic-gate (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1); 4079*0Sstevel@tonic-gate len = sizeof (val); 4080*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 4081*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 4082*0Sstevel@tonic-gate (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0'); 4083*0Sstevel@tonic-gate len = sizeof (val); 4084*0Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 4085*0Sstevel@tonic-gate } 4086*0Sstevel@tonic-gate 4087*0Sstevel@tonic-gate /* no property to parse */ 4088*0Sstevel@tonic-gate asy->asy_cflag = 0; 4089*0Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 4090*0Sstevel@tonic-gate return; 4091*0Sstevel@tonic-gate 4092*0Sstevel@tonic-gate p = val; 4093*0Sstevel@tonic-gate /* ---- baud rate ---- */ 4094*0Sstevel@tonic-gate asy->asy_cflag = CREAD|B9600; /* initial default */ 4095*0Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 4096*0Sstevel@tonic-gate *p1++ = '\0'; 4097*0Sstevel@tonic-gate } else { 4098*0Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 4099*0Sstevel@tonic-gate return; 4100*0Sstevel@tonic-gate } 4101*0Sstevel@tonic-gate 4102*0Sstevel@tonic-gate if (strcmp(p, "110") == 0) 4103*0Sstevel@tonic-gate asy->asy_bidx = B110; 4104*0Sstevel@tonic-gate else if (strcmp(p, "150") == 0) 4105*0Sstevel@tonic-gate asy->asy_bidx = B150; 4106*0Sstevel@tonic-gate else if (strcmp(p, "300") == 0) 4107*0Sstevel@tonic-gate asy->asy_bidx = B300; 4108*0Sstevel@tonic-gate else if (strcmp(p, "600") == 0) 4109*0Sstevel@tonic-gate asy->asy_bidx = B600; 4110*0Sstevel@tonic-gate else if (strcmp(p, "1200") == 0) 4111*0Sstevel@tonic-gate asy->asy_bidx = B1200; 4112*0Sstevel@tonic-gate else if (strcmp(p, "2400") == 0) 4113*0Sstevel@tonic-gate asy->asy_bidx = B2400; 4114*0Sstevel@tonic-gate else if (strcmp(p, "4800") == 0) 4115*0Sstevel@tonic-gate asy->asy_bidx = B4800; 4116*0Sstevel@tonic-gate else if (strcmp(p, "9600") == 0) 4117*0Sstevel@tonic-gate asy->asy_bidx = B9600; 4118*0Sstevel@tonic-gate else if (strcmp(p, "19200") == 0) 4119*0Sstevel@tonic-gate asy->asy_bidx = B19200; 4120*0Sstevel@tonic-gate else if (strcmp(p, "38400") == 0) 4121*0Sstevel@tonic-gate asy->asy_bidx = B38400; 4122*0Sstevel@tonic-gate else if (strcmp(p, "57600") == 0) 4123*0Sstevel@tonic-gate asy->asy_bidx = B57600; 4124*0Sstevel@tonic-gate else if (strcmp(p, "115200") == 0) 4125*0Sstevel@tonic-gate asy->asy_bidx = B115200; 4126*0Sstevel@tonic-gate else 4127*0Sstevel@tonic-gate asy->asy_bidx = B9600; 4128*0Sstevel@tonic-gate 4129*0Sstevel@tonic-gate asy->asy_cflag &= ~CBAUD; 4130*0Sstevel@tonic-gate if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */ 4131*0Sstevel@tonic-gate asy->asy_cflag |= CBAUDEXT; 4132*0Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx - CBAUD - 1; 4133*0Sstevel@tonic-gate } else { 4134*0Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx; 4135*0Sstevel@tonic-gate } 4136*0Sstevel@tonic-gate 4137*0Sstevel@tonic-gate ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag)); 4138*0Sstevel@tonic-gate 4139*0Sstevel@tonic-gate /* ---- Next item is data bits ---- */ 4140*0Sstevel@tonic-gate p = p1; 4141*0Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 4142*0Sstevel@tonic-gate *p1++ = '\0'; 4143*0Sstevel@tonic-gate } else { 4144*0Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 4145*0Sstevel@tonic-gate return; 4146*0Sstevel@tonic-gate } 4147*0Sstevel@tonic-gate switch (*p) { 4148*0Sstevel@tonic-gate default: 4149*0Sstevel@tonic-gate case '8': 4150*0Sstevel@tonic-gate asy->asy_cflag |= CS8; 4151*0Sstevel@tonic-gate asy->asy_lcr = BITS8; 4152*0Sstevel@tonic-gate break; 4153*0Sstevel@tonic-gate case '7': 4154*0Sstevel@tonic-gate asy->asy_cflag |= CS7; 4155*0Sstevel@tonic-gate asy->asy_lcr = BITS7; 4156*0Sstevel@tonic-gate break; 4157*0Sstevel@tonic-gate case '6': 4158*0Sstevel@tonic-gate asy->asy_cflag |= CS6; 4159*0Sstevel@tonic-gate asy->asy_lcr = BITS6; 4160*0Sstevel@tonic-gate break; 4161*0Sstevel@tonic-gate case '5': 4162*0Sstevel@tonic-gate /* LINTED: CS5 is currently zero (but might change) */ 4163*0Sstevel@tonic-gate asy->asy_cflag |= CS5; 4164*0Sstevel@tonic-gate asy->asy_lcr = BITS5; 4165*0Sstevel@tonic-gate break; 4166*0Sstevel@tonic-gate } 4167*0Sstevel@tonic-gate 4168*0Sstevel@tonic-gate /* ---- Parity info ---- */ 4169*0Sstevel@tonic-gate p = p1; 4170*0Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 4171*0Sstevel@tonic-gate *p1++ = '\0'; 4172*0Sstevel@tonic-gate } else { 4173*0Sstevel@tonic-gate return; 4174*0Sstevel@tonic-gate } 4175*0Sstevel@tonic-gate switch (*p) { 4176*0Sstevel@tonic-gate default: 4177*0Sstevel@tonic-gate case 'n': 4178*0Sstevel@tonic-gate break; 4179*0Sstevel@tonic-gate case 'e': 4180*0Sstevel@tonic-gate asy->asy_cflag |= PARENB; 4181*0Sstevel@tonic-gate asy->asy_lcr |= PEN; break; 4182*0Sstevel@tonic-gate case 'o': 4183*0Sstevel@tonic-gate asy->asy_cflag |= PARENB|PARODD; 4184*0Sstevel@tonic-gate asy->asy_lcr |= PEN|EPS; 4185*0Sstevel@tonic-gate break; 4186*0Sstevel@tonic-gate } 4187*0Sstevel@tonic-gate 4188*0Sstevel@tonic-gate /* ---- Find stop bits ---- */ 4189*0Sstevel@tonic-gate p = p1; 4190*0Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 4191*0Sstevel@tonic-gate *p1++ = '\0'; 4192*0Sstevel@tonic-gate } else { 4193*0Sstevel@tonic-gate return; 4194*0Sstevel@tonic-gate } 4195*0Sstevel@tonic-gate if (*p == '2') { 4196*0Sstevel@tonic-gate asy->asy_cflag |= CSTOPB; 4197*0Sstevel@tonic-gate asy->asy_lcr |= STB; 4198*0Sstevel@tonic-gate } 4199*0Sstevel@tonic-gate 4200*0Sstevel@tonic-gate /* ---- handshake is next ---- */ 4201*0Sstevel@tonic-gate p = p1; 4202*0Sstevel@tonic-gate if (p) { 4203*0Sstevel@tonic-gate if ((p1 = strchr(p, ',')) != 0) 4204*0Sstevel@tonic-gate *p1++ = '\0'; 4205*0Sstevel@tonic-gate 4206*0Sstevel@tonic-gate if (*p == 'h') 4207*0Sstevel@tonic-gate asy->asy_cflag |= CRTSCTS; 4208*0Sstevel@tonic-gate else if (*p == 's') 4209*0Sstevel@tonic-gate asy->asy_cflag |= CRTSXOFF; 4210*0Sstevel@tonic-gate } 4211*0Sstevel@tonic-gate } 4212*0Sstevel@tonic-gate 4213*0Sstevel@tonic-gate /* 4214*0Sstevel@tonic-gate * Check for abort character sequence 4215*0Sstevel@tonic-gate */ 4216*0Sstevel@tonic-gate static boolean_t 4217*0Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch) 4218*0Sstevel@tonic-gate { 4219*0Sstevel@tonic-gate static int state = 0; 4220*0Sstevel@tonic-gate #define CNTRL(c) ((c)&037) 4221*0Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') }; 4222*0Sstevel@tonic-gate 4223*0Sstevel@tonic-gate if (ch == sequence[state]) { 4224*0Sstevel@tonic-gate if (++state >= sizeof (sequence)) { 4225*0Sstevel@tonic-gate state = 0; 4226*0Sstevel@tonic-gate return (B_TRUE); 4227*0Sstevel@tonic-gate } 4228*0Sstevel@tonic-gate } else { 4229*0Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0; 4230*0Sstevel@tonic-gate } 4231*0Sstevel@tonic-gate return (B_FALSE); 4232*0Sstevel@tonic-gate } 4233*0Sstevel@tonic-gate 4234*0Sstevel@tonic-gate /* 4235*0Sstevel@tonic-gate * Flow control functions 4236*0Sstevel@tonic-gate */ 4237*0Sstevel@tonic-gate /* 4238*0Sstevel@tonic-gate * Software input flow control 4239*0Sstevel@tonic-gate * This function can execute software input flow control sucessfully 4240*0Sstevel@tonic-gate * at most of situations except that the line is in BREAK status 4241*0Sstevel@tonic-gate * (timed and untimed break). 4242*0Sstevel@tonic-gate * INPUT VALUE of onoff: 4243*0Sstevel@tonic-gate * FLOW_START means to send out a XON char 4244*0Sstevel@tonic-gate * and clear SW input flow control flag. 4245*0Sstevel@tonic-gate * FLOW_STOP means to send out a XOFF char 4246*0Sstevel@tonic-gate * and set SW input flow control flag. 4247*0Sstevel@tonic-gate * FLOW_CHECK means to check whether there is pending XON/XOFF 4248*0Sstevel@tonic-gate * if it is true, send it out. 4249*0Sstevel@tonic-gate * INPUT VALUE of type: 4250*0Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 4251*0Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 4252*0Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 4253*0Sstevel@tonic-gate * RETURN VALUE: B_FALSE means no flow control char is sent 4254*0Sstevel@tonic-gate * B_TRUE means one flow control char is sent 4255*0Sstevel@tonic-gate */ 4256*0Sstevel@tonic-gate static boolean_t 4257*0Sstevel@tonic-gate async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff, 4258*0Sstevel@tonic-gate int type) 4259*0Sstevel@tonic-gate { 4260*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 4261*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 4262*0Sstevel@tonic-gate int rval = B_FALSE; 4263*0Sstevel@tonic-gate 4264*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 4265*0Sstevel@tonic-gate 4266*0Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXOFF)) 4267*0Sstevel@tonic-gate return (rval); 4268*0Sstevel@tonic-gate 4269*0Sstevel@tonic-gate /* 4270*0Sstevel@tonic-gate * If we get this far, then we know IXOFF is set. 4271*0Sstevel@tonic-gate */ 4272*0Sstevel@tonic-gate switch (onoff) { 4273*0Sstevel@tonic-gate case FLOW_STOP: 4274*0Sstevel@tonic-gate async->async_inflow_source |= type; 4275*0Sstevel@tonic-gate 4276*0Sstevel@tonic-gate /* 4277*0Sstevel@tonic-gate * We'll send an XOFF character for each of up to 4278*0Sstevel@tonic-gate * three different input flow control attempts to stop input. 4279*0Sstevel@tonic-gate * If we already send out one XOFF, but FLOW_STOP comes again, 4280*0Sstevel@tonic-gate * it seems that input flow control becomes more serious, 4281*0Sstevel@tonic-gate * then send XOFF again. 4282*0Sstevel@tonic-gate */ 4283*0Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 4284*0Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 4285*0Sstevel@tonic-gate async->async_flags |= ASYNC_SW_IN_FLOW | 4286*0Sstevel@tonic-gate ASYNC_SW_IN_NEEDED; 4287*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, " 4288*0Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 4289*0Sstevel@tonic-gate break; 4290*0Sstevel@tonic-gate case FLOW_START: 4291*0Sstevel@tonic-gate async->async_inflow_source &= ~type; 4292*0Sstevel@tonic-gate if (async->async_inflow_source == 0) { 4293*0Sstevel@tonic-gate async->async_flags = (async->async_flags & 4294*0Sstevel@tonic-gate ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 4295*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: " 4296*0Sstevel@tonic-gate "input sflow start\n", instance); 4297*0Sstevel@tonic-gate } 4298*0Sstevel@tonic-gate break; 4299*0Sstevel@tonic-gate default: 4300*0Sstevel@tonic-gate break; 4301*0Sstevel@tonic-gate } 4302*0Sstevel@tonic-gate 4303*0Sstevel@tonic-gate if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK | 4304*0Sstevel@tonic-gate ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) && 4305*0Sstevel@tonic-gate (ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) { 4306*0Sstevel@tonic-gate /* 4307*0Sstevel@tonic-gate * If we get this far, then we know we need to send out 4308*0Sstevel@tonic-gate * XON or XOFF char. 4309*0Sstevel@tonic-gate */ 4310*0Sstevel@tonic-gate async->async_flags = (async->async_flags & 4311*0Sstevel@tonic-gate ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY; 4312*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 4313*0Sstevel@tonic-gate async->async_flags & ASYNC_SW_IN_FLOW ? 4314*0Sstevel@tonic-gate async->async_stopc : async->async_startc); 4315*0Sstevel@tonic-gate rval = B_TRUE; 4316*0Sstevel@tonic-gate } 4317*0Sstevel@tonic-gate return (rval); 4318*0Sstevel@tonic-gate } 4319*0Sstevel@tonic-gate 4320*0Sstevel@tonic-gate /* 4321*0Sstevel@tonic-gate * Software output flow control 4322*0Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 4323*0Sstevel@tonic-gate * It does not handle HW, and just change the SW output flow control flag. 4324*0Sstevel@tonic-gate * INPUT VALUE of onoff: 4325*0Sstevel@tonic-gate * FLOW_START means to clear SW output flow control flag, 4326*0Sstevel@tonic-gate * also combine with HW output flow control status to 4327*0Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 4328*0Sstevel@tonic-gate * FLOW_STOP means to set SW output flow control flag, 4329*0Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 4330*0Sstevel@tonic-gate */ 4331*0Sstevel@tonic-gate static void 4332*0Sstevel@tonic-gate async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff) 4333*0Sstevel@tonic-gate { 4334*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 4335*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 4336*0Sstevel@tonic-gate 4337*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 4338*0Sstevel@tonic-gate 4339*0Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXON)) 4340*0Sstevel@tonic-gate return; 4341*0Sstevel@tonic-gate 4342*0Sstevel@tonic-gate switch (onoff) { 4343*0Sstevel@tonic-gate case FLOW_STOP: 4344*0Sstevel@tonic-gate async->async_flags |= ASYNC_SW_OUT_FLW; 4345*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 4346*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n", 4347*0Sstevel@tonic-gate instance); 4348*0Sstevel@tonic-gate break; 4349*0Sstevel@tonic-gate case FLOW_START: 4350*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW; 4351*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_HW_OUT_FLW)) 4352*0Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 4353*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n", 4354*0Sstevel@tonic-gate instance); 4355*0Sstevel@tonic-gate break; 4356*0Sstevel@tonic-gate default: 4357*0Sstevel@tonic-gate break; 4358*0Sstevel@tonic-gate } 4359*0Sstevel@tonic-gate } 4360*0Sstevel@tonic-gate 4361*0Sstevel@tonic-gate /* 4362*0Sstevel@tonic-gate * Hardware input flow control 4363*0Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 4364*0Sstevel@tonic-gate * It directly changes RTS depending on input parameter onoff. 4365*0Sstevel@tonic-gate * INPUT VALUE of onoff: 4366*0Sstevel@tonic-gate * FLOW_START means to clear HW input flow control flag, 4367*0Sstevel@tonic-gate * and pull up RTS if it is low. 4368*0Sstevel@tonic-gate * FLOW_STOP means to set HW input flow control flag, 4369*0Sstevel@tonic-gate * and low RTS if it is high. 4370*0Sstevel@tonic-gate * INPUT VALUE of type: 4371*0Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 4372*0Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 4373*0Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 4374*0Sstevel@tonic-gate */ 4375*0Sstevel@tonic-gate static void 4376*0Sstevel@tonic-gate async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff, 4377*0Sstevel@tonic-gate int type) 4378*0Sstevel@tonic-gate { 4379*0Sstevel@tonic-gate uchar_t mcr; 4380*0Sstevel@tonic-gate uchar_t flag; 4381*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 4382*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 4383*0Sstevel@tonic-gate 4384*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 4385*0Sstevel@tonic-gate 4386*0Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSXOFF)) 4387*0Sstevel@tonic-gate return; 4388*0Sstevel@tonic-gate 4389*0Sstevel@tonic-gate switch (onoff) { 4390*0Sstevel@tonic-gate case FLOW_STOP: 4391*0Sstevel@tonic-gate async->async_inflow_source |= type; 4392*0Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 4393*0Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 4394*0Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW; 4395*0Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, " 4396*0Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 4397*0Sstevel@tonic-gate break; 4398*0Sstevel@tonic-gate case FLOW_START: 4399*0Sstevel@tonic-gate async->async_inflow_source &= ~type; 4400*0Sstevel@tonic-gate if (async->async_inflow_source == 0) { 4401*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW; 4402*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: " 4403*0Sstevel@tonic-gate "input hflow start\n", instance); 4404*0Sstevel@tonic-gate } 4405*0Sstevel@tonic-gate break; 4406*0Sstevel@tonic-gate default: 4407*0Sstevel@tonic-gate break; 4408*0Sstevel@tonic-gate } 4409*0Sstevel@tonic-gate mcr = ddi_io_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 4410*0Sstevel@tonic-gate flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 4411*0Sstevel@tonic-gate 4412*0Sstevel@tonic-gate if (((mcr ^ flag) & RTS) != 0) { 4413*0Sstevel@tonic-gate ddi_io_put8(asy->asy_iohandle, 4414*0Sstevel@tonic-gate asy->asy_ioaddr + MCR, (mcr ^ RTS)); 4415*0Sstevel@tonic-gate } 4416*0Sstevel@tonic-gate } 4417*0Sstevel@tonic-gate 4418*0Sstevel@tonic-gate /* 4419*0Sstevel@tonic-gate * Hardware output flow control 4420*0Sstevel@tonic-gate * This function can execute HW output flow control sucessfully 4421*0Sstevel@tonic-gate * at any situation. 4422*0Sstevel@tonic-gate * It doesn't really change RTS, and just change 4423*0Sstevel@tonic-gate * HW output flow control flag depending on CTS status. 4424*0Sstevel@tonic-gate * INPUT VALUE of onoff: 4425*0Sstevel@tonic-gate * FLOW_START means to clear HW output flow control flag. 4426*0Sstevel@tonic-gate * also combine with SW output flow control status to 4427*0Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 4428*0Sstevel@tonic-gate * FLOW_STOP means to set HW output flow control flag. 4429*0Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 4430*0Sstevel@tonic-gate */ 4431*0Sstevel@tonic-gate static void 4432*0Sstevel@tonic-gate async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff) 4433*0Sstevel@tonic-gate { 4434*0Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 4435*0Sstevel@tonic-gate int instance = UNIT(async->async_dev); 4436*0Sstevel@tonic-gate 4437*0Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 4438*0Sstevel@tonic-gate 4439*0Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSCTS)) 4440*0Sstevel@tonic-gate return; 4441*0Sstevel@tonic-gate 4442*0Sstevel@tonic-gate switch (onoff) { 4443*0Sstevel@tonic-gate case FLOW_STOP: 4444*0Sstevel@tonic-gate async->async_flags |= ASYNC_HW_OUT_FLW; 4445*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 4446*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n", 4447*0Sstevel@tonic-gate instance); 4448*0Sstevel@tonic-gate break; 4449*0Sstevel@tonic-gate case FLOW_START: 4450*0Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW; 4451*0Sstevel@tonic-gate if (!(async->async_flags & ASYNC_SW_OUT_FLW)) 4452*0Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 4453*0Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n", 4454*0Sstevel@tonic-gate instance); 4455*0Sstevel@tonic-gate break; 4456*0Sstevel@tonic-gate default: 4457*0Sstevel@tonic-gate break; 4458*0Sstevel@tonic-gate } 4459*0Sstevel@tonic-gate } 4460