17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54ab75253Smrj * Common Development and Distribution License (the "License"). 64ab75253Smrj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 214ab75253Smrj 227c478bd9Sstevel@tonic-gate /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 237c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 247c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 27cd43c54aSZach Kissel * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. 28b97d6ca7SMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved. 29*395a1446SAndy Fiddaman * Copyright 2023 Oxide Computer Company 307c478bd9Sstevel@tonic-gate */ 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate /* 347c478bd9Sstevel@tonic-gate * Serial I/O driver for 8250/16450/16550A/16650/16750 chips. 357c478bd9Sstevel@tonic-gate */ 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate #include <sys/param.h> 387c478bd9Sstevel@tonic-gate #include <sys/types.h> 397c478bd9Sstevel@tonic-gate #include <sys/signal.h> 407c478bd9Sstevel@tonic-gate #include <sys/stream.h> 417c478bd9Sstevel@tonic-gate #include <sys/termio.h> 427c478bd9Sstevel@tonic-gate #include <sys/errno.h> 437c478bd9Sstevel@tonic-gate #include <sys/file.h> 447c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 457c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 467c478bd9Sstevel@tonic-gate #include <sys/strsubr.h> 477c478bd9Sstevel@tonic-gate #include <sys/strtty.h> 487c478bd9Sstevel@tonic-gate #include <sys/debug.h> 497c478bd9Sstevel@tonic-gate #include <sys/kbio.h> 507c478bd9Sstevel@tonic-gate #include <sys/cred.h> 517c478bd9Sstevel@tonic-gate #include <sys/stat.h> 527c478bd9Sstevel@tonic-gate #include <sys/consdev.h> 537c478bd9Sstevel@tonic-gate #include <sys/mkdev.h> 547c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 557c478bd9Sstevel@tonic-gate #include <sys/cred.h> 567c478bd9Sstevel@tonic-gate #include <sys/strsun.h> 577c478bd9Sstevel@tonic-gate #ifdef DEBUG 587c478bd9Sstevel@tonic-gate #include <sys/promif.h> 597c478bd9Sstevel@tonic-gate #endif 607c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 617c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 627c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 6356001103Smyers #include <sys/pci.h> 647c478bd9Sstevel@tonic-gate #include <sys/asy.h> 657c478bd9Sstevel@tonic-gate #include <sys/policy.h> 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate /* 687c478bd9Sstevel@tonic-gate * set the RX FIFO trigger_level to half the RX FIFO size for now 697c478bd9Sstevel@tonic-gate * we may want to make this configurable later. 707c478bd9Sstevel@tonic-gate */ 717c478bd9Sstevel@tonic-gate static int asy_trig_level = FIFO_TRIG_8; 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate int asy_drain_check = 15000000; /* tunable: exit drain check time */ 747c478bd9Sstevel@tonic-gate int asy_min_dtr_low = 500000; /* tunable: minimum DTR down time */ 757c478bd9Sstevel@tonic-gate int asy_min_utbrk = 100000; /* tunable: minumum untimed brk time */ 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate int asymaxchip = ASY16750; /* tunable: limit chip support we look for */ 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate /* 807c478bd9Sstevel@tonic-gate * Just in case someone has a chip with broken loopback mode, we provide a 817c478bd9Sstevel@tonic-gate * means to disable the loopback test. By default, we only loopback test 827c478bd9Sstevel@tonic-gate * UARTs which look like they have FIFOs bigger than 16 bytes. 837c478bd9Sstevel@tonic-gate * Set to 0 to suppress test, or to 2 to enable test on any size FIFO. 847c478bd9Sstevel@tonic-gate */ 857c478bd9Sstevel@tonic-gate int asy_fifo_test = 1; /* tunable: set to 0, 1, or 2 */ 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * Allow ability to switch off testing of the scratch register. 897c478bd9Sstevel@tonic-gate * Some UART emulators might not have it. This will also disable the test 907c478bd9Sstevel@tonic-gate * for Exar/Startech ST16C650, as that requires use of the SCR register. 917c478bd9Sstevel@tonic-gate */ 927c478bd9Sstevel@tonic-gate int asy_scr_test = 1; /* tunable: set to 0 to disable SCR reg test */ 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate /* 957c478bd9Sstevel@tonic-gate * As we don't yet support on-chip flow control, it's a bad idea to put a 967c478bd9Sstevel@tonic-gate * large number of characters in the TX FIFO, since if other end tells us 977c478bd9Sstevel@tonic-gate * to stop transmitting, we can only stop filling the TX FIFO, but it will 987c478bd9Sstevel@tonic-gate * still carry on draining by itself, so remote end still gets what's left 997c478bd9Sstevel@tonic-gate * in the FIFO. 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate int asy_max_tx_fifo = 16; /* tunable: max fill of TX FIFO */ 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate #define async_stopc async_ttycommon.t_stopc 1047c478bd9Sstevel@tonic-gate #define async_startc async_ttycommon.t_startc 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate #define ASY_INIT 1 1077c478bd9Sstevel@tonic-gate #define ASY_NOINIT 0 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate /* enum value for sw and hw flow control action */ 1107c478bd9Sstevel@tonic-gate typedef enum { 1117c478bd9Sstevel@tonic-gate FLOW_CHECK, 1127c478bd9Sstevel@tonic-gate FLOW_STOP, 1137c478bd9Sstevel@tonic-gate FLOW_START 1147c478bd9Sstevel@tonic-gate } async_flowc_action; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate #ifdef DEBUG 1177c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */ 1187c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */ 1197c478bd9Sstevel@tonic-gate #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */ 1207c478bd9Sstevel@tonic-gate #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */ 1217c478bd9Sstevel@tonic-gate #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */ 1227c478bd9Sstevel@tonic-gate #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */ 1237c478bd9Sstevel@tonic-gate #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */ 1247c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */ 1257c478bd9Sstevel@tonic-gate #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */ 1267c478bd9Sstevel@tonic-gate #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */ 1277c478bd9Sstevel@tonic-gate #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */ 1287c478bd9Sstevel@tonic-gate #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */ 1297c478bd9Sstevel@tonic-gate #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */ 1307c478bd9Sstevel@tonic-gate #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */ 1317c478bd9Sstevel@tonic-gate #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */ 1327c478bd9Sstevel@tonic-gate #define ASY_DEBUG(x) (debug & (x)) 1337c478bd9Sstevel@tonic-gate static int debug = 0; 1347c478bd9Sstevel@tonic-gate #else 1357c478bd9Sstevel@tonic-gate #define ASY_DEBUG(x) B_FALSE 1367c478bd9Sstevel@tonic-gate #endif 1377c478bd9Sstevel@tonic-gate 1387c478bd9Sstevel@tonic-gate /* pnpISA compressed device ids */ 1397c478bd9Sstevel@tonic-gate #define pnpMTS0219 0xb6930219 /* Multitech MT5634ZTX modem */ 1407c478bd9Sstevel@tonic-gate 1417c478bd9Sstevel@tonic-gate /* 1427c478bd9Sstevel@tonic-gate * PPS (Pulse Per Second) support. 1437c478bd9Sstevel@tonic-gate */ 144b97d6ca7SMilan Jurik void ddi_hardpps(struct timeval *, int); 1457c478bd9Sstevel@tonic-gate /* 1467c478bd9Sstevel@tonic-gate * This is protected by the asy_excl_hi of the port on which PPS event 1477c478bd9Sstevel@tonic-gate * handling is enabled. Note that only one port should have this enabled at 1487c478bd9Sstevel@tonic-gate * any one time. Enabling PPS handling on multiple ports will result in 1497c478bd9Sstevel@tonic-gate * unpredictable (but benign) results. 1507c478bd9Sstevel@tonic-gate */ 1517c478bd9Sstevel@tonic-gate static struct ppsclockev asy_ppsev; 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate #ifdef PPSCLOCKLED 1547c478bd9Sstevel@tonic-gate /* XXX Use these to observe PPS latencies and jitter on a scope */ 1557c478bd9Sstevel@tonic-gate #define LED_ON 1567c478bd9Sstevel@tonic-gate #define LED_OFF 1577c478bd9Sstevel@tonic-gate #else 1587c478bd9Sstevel@tonic-gate #define LED_ON 1597c478bd9Sstevel@tonic-gate #define LED_OFF 1607c478bd9Sstevel@tonic-gate #endif 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate static int max_asy_instance = -1; 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate static uint_t asysoftintr(caddr_t intarg); 1657c478bd9Sstevel@tonic-gate static uint_t asyintr(caddr_t argasy); 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch); 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate /* The async interrupt entry points */ 1707c478bd9Sstevel@tonic-gate static void async_txint(struct asycom *asy); 1717c478bd9Sstevel@tonic-gate static void async_rxint(struct asycom *asy, uchar_t lsr); 1727c478bd9Sstevel@tonic-gate static void async_msint(struct asycom *asy); 1737c478bd9Sstevel@tonic-gate static void async_softint(struct asycom *asy); 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp); 1767c478bd9Sstevel@tonic-gate static void async_reioctl(void *unit); 1777c478bd9Sstevel@tonic-gate static void async_iocdata(queue_t *q, mblk_t *mp); 1787c478bd9Sstevel@tonic-gate static void async_restart(void *arg); 1797c478bd9Sstevel@tonic-gate static void async_start(struct asyncline *async); 1807c478bd9Sstevel@tonic-gate static void async_nstart(struct asyncline *async, int mode); 1817c478bd9Sstevel@tonic-gate static void async_resume(struct asyncline *async); 1827c478bd9Sstevel@tonic-gate static void asy_program(struct asycom *asy, int mode); 1837c478bd9Sstevel@tonic-gate static void asyinit(struct asycom *asy); 1847c478bd9Sstevel@tonic-gate static void asy_waiteot(struct asycom *asy); 185281f0747Slt200341 static void asyputchar(cons_polledio_arg_t, uchar_t c); 186281f0747Slt200341 static int asygetchar(cons_polledio_arg_t); 187281f0747Slt200341 static boolean_t asyischar(cons_polledio_arg_t); 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gate static int asymctl(struct asycom *, int, int); 1907c478bd9Sstevel@tonic-gate static int asytodm(int, int); 1917c478bd9Sstevel@tonic-gate static int dmtoasy(int); 1927c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/ 1937c478bd9Sstevel@tonic-gate static void asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2); 1947c478bd9Sstevel@tonic-gate static void asy_parse_mode(dev_info_t *devi, struct asycom *asy); 1957c478bd9Sstevel@tonic-gate static void asy_soft_state_free(struct asycom *); 1967c478bd9Sstevel@tonic-gate static char *asy_hw_name(struct asycom *asy); 1977c478bd9Sstevel@tonic-gate static void async_hold_utbrk(void *arg); 1987c478bd9Sstevel@tonic-gate static void async_resume_utbrk(struct asyncline *async); 1997c478bd9Sstevel@tonic-gate static void async_dtr_free(struct asyncline *async); 2007c478bd9Sstevel@tonic-gate static int asy_identify_chip(dev_info_t *devi, struct asycom *asy); 2017c478bd9Sstevel@tonic-gate static void asy_reset_fifo(struct asycom *asy, uchar_t flags); 2027c478bd9Sstevel@tonic-gate static int asy_getproperty(dev_info_t *devi, struct asycom *asy, 2037c478bd9Sstevel@tonic-gate const char *property); 2047c478bd9Sstevel@tonic-gate static boolean_t async_flowcontrol_sw_input(struct asycom *asy, 2057c478bd9Sstevel@tonic-gate async_flowc_action onoff, int type); 2067c478bd9Sstevel@tonic-gate static void async_flowcontrol_sw_output(struct asycom *asy, 2077c478bd9Sstevel@tonic-gate async_flowc_action onoff); 2087c478bd9Sstevel@tonic-gate static void async_flowcontrol_hw_input(struct asycom *asy, 2097c478bd9Sstevel@tonic-gate async_flowc_action onoff, int type); 2107c478bd9Sstevel@tonic-gate static void async_flowcontrol_hw_output(struct asycom *asy, 2117c478bd9Sstevel@tonic-gate async_flowc_action onoff); 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate #define GET_PROP(devi, pname, pflag, pval, plen) \ 2147c478bd9Sstevel@tonic-gate (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \ 2157c478bd9Sstevel@tonic-gate (pflag), (pname), (caddr_t)(pval), (plen))) 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate kmutex_t asy_glob_lock; /* lock protecting global data manipulation */ 2187c478bd9Sstevel@tonic-gate void *asy_soft_state; 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate /* Standard COM port I/O addresses */ 2217c478bd9Sstevel@tonic-gate static const int standard_com_ports[] = { 2227c478bd9Sstevel@tonic-gate COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR 2237c478bd9Sstevel@tonic-gate }; 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate static int *com_ports; 2267c478bd9Sstevel@tonic-gate static uint_t num_com_ports; 2277c478bd9Sstevel@tonic-gate 2282df1fe9cSrandyf #ifdef DEBUG 2292df1fe9cSrandyf /* 2302df1fe9cSrandyf * Set this to true to make the driver pretend to do a suspend. Useful 2312df1fe9cSrandyf * for debugging suspend/resume code with a serial debugger. 2322df1fe9cSrandyf */ 2332df1fe9cSrandyf boolean_t asy_nosuspend = B_FALSE; 2342df1fe9cSrandyf #endif 2352df1fe9cSrandyf 2362df1fe9cSrandyf 2377c478bd9Sstevel@tonic-gate /* 2387c478bd9Sstevel@tonic-gate * Baud rate table. Indexed by #defines found in sys/termios.h 2397c478bd9Sstevel@tonic-gate */ 2407c478bd9Sstevel@tonic-gate ushort_t asyspdtab[] = { 2417c478bd9Sstevel@tonic-gate 0, /* 0 baud rate */ 2427c478bd9Sstevel@tonic-gate 0x900, /* 50 baud rate */ 2437c478bd9Sstevel@tonic-gate 0x600, /* 75 baud rate */ 2447c478bd9Sstevel@tonic-gate 0x417, /* 110 baud rate (%0.026) */ 2457c478bd9Sstevel@tonic-gate 0x359, /* 134 baud rate (%0.058) */ 2467c478bd9Sstevel@tonic-gate 0x300, /* 150 baud rate */ 2477c478bd9Sstevel@tonic-gate 0x240, /* 200 baud rate */ 2487c478bd9Sstevel@tonic-gate 0x180, /* 300 baud rate */ 2497c478bd9Sstevel@tonic-gate 0x0c0, /* 600 baud rate */ 2507c478bd9Sstevel@tonic-gate 0x060, /* 1200 baud rate */ 2517c478bd9Sstevel@tonic-gate 0x040, /* 1800 baud rate */ 2527c478bd9Sstevel@tonic-gate 0x030, /* 2400 baud rate */ 2537c478bd9Sstevel@tonic-gate 0x018, /* 4800 baud rate */ 2547c478bd9Sstevel@tonic-gate 0x00c, /* 9600 baud rate */ 2557c478bd9Sstevel@tonic-gate 0x006, /* 19200 baud rate */ 2567c478bd9Sstevel@tonic-gate 0x003, /* 38400 baud rate */ 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate 0x002, /* 57600 baud rate */ 2597c478bd9Sstevel@tonic-gate 0x0, /* 76800 baud rate not supported */ 2607c478bd9Sstevel@tonic-gate 0x001, /* 115200 baud rate */ 2617c478bd9Sstevel@tonic-gate 0x0, /* 153600 baud rate not supported */ 2627c478bd9Sstevel@tonic-gate 0x0, /* 0x8002 (SMC chip) 230400 baud rate not supported */ 2637c478bd9Sstevel@tonic-gate 0x0, /* 307200 baud rate not supported */ 2647c478bd9Sstevel@tonic-gate 0x0, /* 0x8001 (SMC chip) 460800 baud rate not supported */ 2657c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2667c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2677c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2687c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2697c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2707c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2717c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2727c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2737c478bd9Sstevel@tonic-gate 0x0, /* unused */ 2747c478bd9Sstevel@tonic-gate }; 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate static int asyrsrv(queue_t *q); 2777c478bd9Sstevel@tonic-gate static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 2787c478bd9Sstevel@tonic-gate static int asyclose(queue_t *q, int flag, cred_t *credp); 2792df1fe9cSrandyf static int asywputdo(queue_t *q, mblk_t *mp, boolean_t); 2807c478bd9Sstevel@tonic-gate static int asywput(queue_t *q, mblk_t *mp); 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate struct module_info asy_info = { 2837c478bd9Sstevel@tonic-gate 0, 2847c478bd9Sstevel@tonic-gate "asy", 2857c478bd9Sstevel@tonic-gate 0, 2867c478bd9Sstevel@tonic-gate INFPSZ, 2877c478bd9Sstevel@tonic-gate 4096, 2887c478bd9Sstevel@tonic-gate 128 2897c478bd9Sstevel@tonic-gate }; 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate static struct qinit asy_rint = { 2927c478bd9Sstevel@tonic-gate putq, 2937c478bd9Sstevel@tonic-gate asyrsrv, 2947c478bd9Sstevel@tonic-gate asyopen, 2957c478bd9Sstevel@tonic-gate asyclose, 2967c478bd9Sstevel@tonic-gate NULL, 2977c478bd9Sstevel@tonic-gate &asy_info, 2987c478bd9Sstevel@tonic-gate NULL 2997c478bd9Sstevel@tonic-gate }; 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate static struct qinit asy_wint = { 3027c478bd9Sstevel@tonic-gate asywput, 3037c478bd9Sstevel@tonic-gate NULL, 3047c478bd9Sstevel@tonic-gate NULL, 3057c478bd9Sstevel@tonic-gate NULL, 3067c478bd9Sstevel@tonic-gate NULL, 3077c478bd9Sstevel@tonic-gate &asy_info, 3087c478bd9Sstevel@tonic-gate NULL 3097c478bd9Sstevel@tonic-gate }; 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate struct streamtab asy_str_info = { 3127c478bd9Sstevel@tonic-gate &asy_rint, 3137c478bd9Sstevel@tonic-gate &asy_wint, 3147c478bd9Sstevel@tonic-gate NULL, 3157c478bd9Sstevel@tonic-gate NULL 3167c478bd9Sstevel@tonic-gate }; 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 3197c478bd9Sstevel@tonic-gate void **result); 3207c478bd9Sstevel@tonic-gate static int asyprobe(dev_info_t *); 3217c478bd9Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t); 3227c478bd9Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t); 32319397407SSherry Moore static int asyquiesce(dev_info_t *); 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate static struct cb_ops cb_asy_ops = { 3267c478bd9Sstevel@tonic-gate nodev, /* cb_open */ 3277c478bd9Sstevel@tonic-gate nodev, /* cb_close */ 3287c478bd9Sstevel@tonic-gate nodev, /* cb_strategy */ 3297c478bd9Sstevel@tonic-gate nodev, /* cb_print */ 3307c478bd9Sstevel@tonic-gate nodev, /* cb_dump */ 3317c478bd9Sstevel@tonic-gate nodev, /* cb_read */ 3327c478bd9Sstevel@tonic-gate nodev, /* cb_write */ 3337c478bd9Sstevel@tonic-gate nodev, /* cb_ioctl */ 3347c478bd9Sstevel@tonic-gate nodev, /* cb_devmap */ 3357c478bd9Sstevel@tonic-gate nodev, /* cb_mmap */ 3367c478bd9Sstevel@tonic-gate nodev, /* cb_segmap */ 3377c478bd9Sstevel@tonic-gate nochpoll, /* cb_chpoll */ 3387c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 3397c478bd9Sstevel@tonic-gate &asy_str_info, /* cb_stream */ 3407c478bd9Sstevel@tonic-gate D_MP /* cb_flag */ 3417c478bd9Sstevel@tonic-gate }; 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate struct dev_ops asy_ops = { 3447c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev */ 3457c478bd9Sstevel@tonic-gate 0, /* devo_refcnt */ 3467c478bd9Sstevel@tonic-gate asyinfo, /* devo_getinfo */ 3477c478bd9Sstevel@tonic-gate nulldev, /* devo_identify */ 3487c478bd9Sstevel@tonic-gate asyprobe, /* devo_probe */ 3497c478bd9Sstevel@tonic-gate asyattach, /* devo_attach */ 3507c478bd9Sstevel@tonic-gate asydetach, /* devo_detach */ 3517c478bd9Sstevel@tonic-gate nodev, /* devo_reset */ 3527c478bd9Sstevel@tonic-gate &cb_asy_ops, /* devo_cb_ops */ 35319397407SSherry Moore NULL, /* devo_bus_ops */ 35419397407SSherry Moore NULL, /* power */ 35519397407SSherry Moore asyquiesce, /* quiesce */ 3567c478bd9Sstevel@tonic-gate }; 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 3597c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 36019397407SSherry Moore "ASY driver", 3617c478bd9Sstevel@tonic-gate &asy_ops, /* driver ops */ 3627c478bd9Sstevel@tonic-gate }; 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 3657c478bd9Sstevel@tonic-gate MODREV_1, 3667c478bd9Sstevel@tonic-gate (void *)&modldrv, 3677c478bd9Sstevel@tonic-gate NULL 3687c478bd9Sstevel@tonic-gate }; 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate int 3717c478bd9Sstevel@tonic-gate _init(void) 3727c478bd9Sstevel@tonic-gate { 3737c478bd9Sstevel@tonic-gate int i; 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2); 3767c478bd9Sstevel@tonic-gate if (i == 0) { 3777c478bd9Sstevel@tonic-gate mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL); 3787c478bd9Sstevel@tonic-gate if ((i = mod_install(&modlinkage)) != 0) { 3797c478bd9Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 3807c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 3817c478bd9Sstevel@tonic-gate } else { 3827c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n", 3837c478bd9Sstevel@tonic-gate modldrv.drv_linkinfo, debug); 3847c478bd9Sstevel@tonic-gate } 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate return (i); 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate int 3907c478bd9Sstevel@tonic-gate _fini(void) 3917c478bd9Sstevel@tonic-gate { 3927c478bd9Sstevel@tonic-gate int i; 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate if ((i = mod_remove(&modlinkage)) == 0) { 3957c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n", 3967c478bd9Sstevel@tonic-gate modldrv.drv_linkinfo); 3977c478bd9Sstevel@tonic-gate ASSERT(max_asy_instance == -1); 3987c478bd9Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 3997c478bd9Sstevel@tonic-gate /* free "motherboard-serial-ports" property if allocated */ 4007c478bd9Sstevel@tonic-gate if (com_ports != NULL && com_ports != (int *)standard_com_ports) 4017c478bd9Sstevel@tonic-gate ddi_prop_free(com_ports); 4027c478bd9Sstevel@tonic-gate com_ports = NULL; 4037c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate return (i); 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate int 4097c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 4107c478bd9Sstevel@tonic-gate { 4117c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 4127c478bd9Sstevel@tonic-gate } 4137c478bd9Sstevel@tonic-gate 4142df1fe9cSrandyf void 4152df1fe9cSrandyf async_put_suspq(struct asycom *asy, mblk_t *mp) 4162df1fe9cSrandyf { 4172df1fe9cSrandyf struct asyncline *async = asy->asy_priv; 4182df1fe9cSrandyf 4192df1fe9cSrandyf ASSERT(mutex_owned(&asy->asy_excl)); 4202df1fe9cSrandyf 4212df1fe9cSrandyf if (async->async_suspqf == NULL) 4222df1fe9cSrandyf async->async_suspqf = mp; 4232df1fe9cSrandyf else 4242df1fe9cSrandyf async->async_suspqb->b_next = mp; 4252df1fe9cSrandyf 4262df1fe9cSrandyf async->async_suspqb = mp; 4272df1fe9cSrandyf } 4282df1fe9cSrandyf 4292df1fe9cSrandyf static mblk_t * 4302df1fe9cSrandyf async_get_suspq(struct asycom *asy) 4312df1fe9cSrandyf { 4322df1fe9cSrandyf struct asyncline *async = asy->asy_priv; 4332df1fe9cSrandyf mblk_t *mp; 4342df1fe9cSrandyf 4352df1fe9cSrandyf ASSERT(mutex_owned(&asy->asy_excl)); 4362df1fe9cSrandyf 4372df1fe9cSrandyf if ((mp = async->async_suspqf) != NULL) { 4382df1fe9cSrandyf async->async_suspqf = mp->b_next; 4392df1fe9cSrandyf mp->b_next = NULL; 4402df1fe9cSrandyf } else { 4412df1fe9cSrandyf async->async_suspqb = NULL; 4422df1fe9cSrandyf } 4432df1fe9cSrandyf return (mp); 4442df1fe9cSrandyf } 4452df1fe9cSrandyf 4462df1fe9cSrandyf static void 4472df1fe9cSrandyf async_process_suspq(struct asycom *asy) 4482df1fe9cSrandyf { 4492df1fe9cSrandyf struct asyncline *async = asy->asy_priv; 4502df1fe9cSrandyf mblk_t *mp; 4512df1fe9cSrandyf 4522df1fe9cSrandyf ASSERT(mutex_owned(&asy->asy_excl)); 4532df1fe9cSrandyf 4542df1fe9cSrandyf while ((mp = async_get_suspq(asy)) != NULL) { 4552df1fe9cSrandyf queue_t *q; 4562df1fe9cSrandyf 4572df1fe9cSrandyf q = async->async_ttycommon.t_writeq; 4582df1fe9cSrandyf ASSERT(q != NULL); 4592df1fe9cSrandyf mutex_exit(&asy->asy_excl); 4602df1fe9cSrandyf (void) asywputdo(q, mp, B_FALSE); 4612df1fe9cSrandyf mutex_enter(&asy->asy_excl); 4622df1fe9cSrandyf } 4632df1fe9cSrandyf async->async_flags &= ~ASYNC_DDI_SUSPENDED; 4642df1fe9cSrandyf cv_broadcast(&async->async_flags_cv); 4652df1fe9cSrandyf } 4662df1fe9cSrandyf 4677c478bd9Sstevel@tonic-gate static int 46856001103Smyers asy_get_bus_type(dev_info_t *devinfo) 46956001103Smyers { 47056001103Smyers char parent_type[16]; 47156001103Smyers int parentlen; 47256001103Smyers 47356001103Smyers parentlen = sizeof (parent_type); 47456001103Smyers 47556001103Smyers if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0, 47656001103Smyers "device_type", (caddr_t)parent_type, &parentlen) 47756001103Smyers != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo, 47856001103Smyers PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type, 47956001103Smyers &parentlen) != DDI_PROP_SUCCESS) { 48056001103Smyers cmn_err(CE_WARN, 48156001103Smyers "asy: can't figure out device type for" 48256001103Smyers " parent \"%s\"", 48356001103Smyers ddi_get_name(ddi_get_parent(devinfo))); 48456001103Smyers return (ASY_BUS_UNKNOWN); 48556001103Smyers } 48656001103Smyers if (strcmp(parent_type, "isa") == 0) 48756001103Smyers return (ASY_BUS_ISA); 48856001103Smyers else if (strcmp(parent_type, "pci") == 0) 48956001103Smyers return (ASY_BUS_PCI); 49056001103Smyers else 49156001103Smyers return (ASY_BUS_UNKNOWN); 49256001103Smyers } 49356001103Smyers 49456001103Smyers static int 49556001103Smyers asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy) 49656001103Smyers { 49756001103Smyers int reglen, nregs; 49856001103Smyers int regnum, i; 49956001103Smyers uint64_t size; 50056001103Smyers struct pci_phys_spec *reglist; 50156001103Smyers 50256001103Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 50356001103Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 50456001103Smyers cmn_err(CE_WARN, "asy_get_io_regnum_pci: reg property" 50556001103Smyers " not found in devices property list"); 50656001103Smyers return (-1); 50756001103Smyers } 50856001103Smyers 50956001103Smyers /* 51056001103Smyers * PCI devices are assumed to not have broken FIFOs; 51156001103Smyers * Agere/Lucent Venus PCI modem chipsets are an example 51256001103Smyers */ 51356001103Smyers if (asy) 51456001103Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK; 51556001103Smyers 51656001103Smyers regnum = -1; 51756001103Smyers nregs = reglen / sizeof (*reglist); 51856001103Smyers for (i = 0; i < nregs; i++) { 51956001103Smyers switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) { 52056001103Smyers case PCI_ADDR_IO: /* I/O bus reg property */ 52156001103Smyers if (regnum == -1) /* use only the first one */ 52256001103Smyers regnum = i; 52356001103Smyers break; 52456001103Smyers 52556001103Smyers default: 52656001103Smyers break; 52756001103Smyers } 52856001103Smyers } 52956001103Smyers 53056001103Smyers /* check for valid count of registers */ 53156001103Smyers if (regnum >= 0) { 53256001103Smyers size = ((uint64_t)reglist[regnum].pci_size_low) | 53356001103Smyers ((uint64_t)reglist[regnum].pci_size_hi) << 32; 53456001103Smyers if (size < 8) 53556001103Smyers regnum = -1; 53656001103Smyers } 53756001103Smyers kmem_free(reglist, reglen); 53856001103Smyers return (regnum); 53956001103Smyers } 54056001103Smyers 54156001103Smyers static int 54256001103Smyers asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy) 54356001103Smyers { 54456001103Smyers int reglen, nregs; 54556001103Smyers int regnum, i; 54656001103Smyers struct { 54756001103Smyers uint_t bustype; 54856001103Smyers int base; 54956001103Smyers int size; 55056001103Smyers } *reglist; 55156001103Smyers 55256001103Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 55356001103Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 55456001103Smyers cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found " 55556001103Smyers "in devices property list"); 55656001103Smyers return (-1); 55756001103Smyers } 55856001103Smyers 55956001103Smyers regnum = -1; 56056001103Smyers nregs = reglen / sizeof (*reglist); 56156001103Smyers for (i = 0; i < nregs; i++) { 56256001103Smyers switch (reglist[i].bustype) { 56356001103Smyers case 1: /* I/O bus reg property */ 56456001103Smyers if (regnum == -1) /* only use the first one */ 56556001103Smyers regnum = i; 56656001103Smyers break; 56756001103Smyers 56856001103Smyers case pnpMTS0219: /* Multitech MT5634ZTX modem */ 56956001103Smyers /* Venus chipset can't do loopback test */ 57056001103Smyers if (asy) 57156001103Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK; 57256001103Smyers break; 57356001103Smyers 57456001103Smyers default: 57556001103Smyers break; 57656001103Smyers } 57756001103Smyers } 57856001103Smyers 57956001103Smyers /* check for valid count of registers */ 58056001103Smyers if ((regnum < 0) || (reglist[regnum].size < 8)) 58156001103Smyers regnum = -1; 58256001103Smyers kmem_free(reglist, reglen); 58356001103Smyers return (regnum); 58456001103Smyers } 58556001103Smyers 58656001103Smyers static int 58756001103Smyers asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy) 58856001103Smyers { 58956001103Smyers switch (asy_get_bus_type(devinfo)) { 59056001103Smyers case ASY_BUS_ISA: 59156001103Smyers return (asy_get_io_regnum_isa(devinfo, asy)); 59256001103Smyers case ASY_BUS_PCI: 59356001103Smyers return (asy_get_io_regnum_pci(devinfo, asy)); 59456001103Smyers default: 59556001103Smyers return (-1); 59656001103Smyers } 59756001103Smyers } 59856001103Smyers 59956001103Smyers static int 6007c478bd9Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 6017c478bd9Sstevel@tonic-gate { 6027c478bd9Sstevel@tonic-gate int instance; 6037c478bd9Sstevel@tonic-gate struct asycom *asy; 6047c478bd9Sstevel@tonic-gate struct asyncline *async; 6057c478bd9Sstevel@tonic-gate 6067c478bd9Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */ 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 6097c478bd9Sstevel@tonic-gate if (asy == NULL) 6107c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 6117c478bd9Sstevel@tonic-gate async = asy->asy_priv; 6127c478bd9Sstevel@tonic-gate 6132df1fe9cSrandyf switch (cmd) { 6142df1fe9cSrandyf case DDI_DETACH: 6157c478bd9Sstevel@tonic-gate DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.", 6167c478bd9Sstevel@tonic-gate instance, asy_hw_name(asy)); 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate /* cancel DTR hold timeout */ 6197c478bd9Sstevel@tonic-gate if (async->async_dtrtid != 0) { 6207c478bd9Sstevel@tonic-gate (void) untimeout(async->async_dtrtid); 6217c478bd9Sstevel@tonic-gate async->async_dtrtid = 0; 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate /* remove all minor device node(s) for this device */ 6257c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 6287c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 6297c478bd9Sstevel@tonic-gate cv_destroy(&async->async_flags_cv); 6307c478bd9Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock); 6317c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 632cd43c54aSZach Kissel ddi_remove_softintr(asy->asy_softintr_id); 633cd43c54aSZach Kissel mutex_destroy(&asy->asy_soft_lock); 6347c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 6352df1fe9cSrandyf DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", 6362df1fe9cSrandyf instance); 6372df1fe9cSrandyf break; 6382df1fe9cSrandyf case DDI_SUSPEND: 6392df1fe9cSrandyf { 6402df1fe9cSrandyf unsigned i; 6412df1fe9cSrandyf uchar_t lsr; 6422df1fe9cSrandyf 6432df1fe9cSrandyf #ifdef DEBUG 6442df1fe9cSrandyf if (asy_nosuspend) 6452df1fe9cSrandyf return (DDI_SUCCESS); 6462df1fe9cSrandyf #endif 6472df1fe9cSrandyf mutex_enter(&asy->asy_excl); 6482df1fe9cSrandyf 6492df1fe9cSrandyf ASSERT(async->async_ops >= 0); 6502df1fe9cSrandyf while (async->async_ops > 0) 6512df1fe9cSrandyf cv_wait(&async->async_ops_cv, &asy->asy_excl); 6522df1fe9cSrandyf 6532df1fe9cSrandyf async->async_flags |= ASYNC_DDI_SUSPENDED; 6542df1fe9cSrandyf 6552df1fe9cSrandyf /* Wait for timed break and delay to complete */ 6562df1fe9cSrandyf while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) { 6572df1fe9cSrandyf if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) 6582df1fe9cSrandyf == 0) { 6592df1fe9cSrandyf async_process_suspq(asy); 6602df1fe9cSrandyf mutex_exit(&asy->asy_excl); 6612df1fe9cSrandyf return (DDI_FAILURE); 6622df1fe9cSrandyf } 6632df1fe9cSrandyf } 6642df1fe9cSrandyf 6652df1fe9cSrandyf /* Clear untimed break */ 6662df1fe9cSrandyf if (async->async_flags & ASYNC_OUT_SUSPEND) 6672df1fe9cSrandyf async_resume_utbrk(async); 6682df1fe9cSrandyf 6692df1fe9cSrandyf mutex_exit(&asy->asy_excl); 6702df1fe9cSrandyf 6712df1fe9cSrandyf mutex_enter(&asy->asy_soft_sr); 6722df1fe9cSrandyf mutex_enter(&asy->asy_excl); 6732df1fe9cSrandyf if (async->async_wbufcid != 0) { 6742df1fe9cSrandyf bufcall_id_t bcid = async->async_wbufcid; 6752df1fe9cSrandyf async->async_wbufcid = 0; 6762df1fe9cSrandyf async->async_flags |= ASYNC_RESUME_BUFCALL; 6772df1fe9cSrandyf mutex_exit(&asy->asy_excl); 6782df1fe9cSrandyf unbufcall(bcid); 6792df1fe9cSrandyf mutex_enter(&asy->asy_excl); 6802df1fe9cSrandyf } 6812df1fe9cSrandyf mutex_enter(&asy->asy_excl_hi); 6822df1fe9cSrandyf 6832df1fe9cSrandyf /* Disable interrupts from chip */ 6842df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 6852df1fe9cSrandyf asy->asy_flags |= ASY_DDI_SUSPENDED; 6862df1fe9cSrandyf 68757ec6c03SZach Kissel /* 68857ec6c03SZach Kissel * Hardware interrupts are disabled we can drop our high level 68957ec6c03SZach Kissel * lock and proceed. 69057ec6c03SZach Kissel */ 69157ec6c03SZach Kissel mutex_exit(&asy->asy_excl_hi); 69257ec6c03SZach Kissel 6932df1fe9cSrandyf /* Process remaining RX characters and RX errors, if any */ 6942df1fe9cSrandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 6952df1fe9cSrandyf async_rxint(asy, lsr); 6962df1fe9cSrandyf 6972df1fe9cSrandyf /* Wait for TX to drain */ 6982df1fe9cSrandyf for (i = 1000; i > 0; i--) { 6992df1fe9cSrandyf lsr = ddi_get8(asy->asy_iohandle, 7002df1fe9cSrandyf asy->asy_ioaddr + LSR); 7012df1fe9cSrandyf if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE)) 7022df1fe9cSrandyf break; 7032df1fe9cSrandyf delay(drv_usectohz(10000)); 7042df1fe9cSrandyf } 7052df1fe9cSrandyf if (i == 0) 7062df1fe9cSrandyf cmn_err(CE_WARN, 7072df1fe9cSrandyf "asy: transmitter wasn't drained before " 7082df1fe9cSrandyf "driver was suspended"); 7092df1fe9cSrandyf 7102df1fe9cSrandyf mutex_exit(&asy->asy_excl); 7112df1fe9cSrandyf mutex_exit(&asy->asy_soft_sr); 7122df1fe9cSrandyf break; 7132df1fe9cSrandyf } 7142df1fe9cSrandyf default: 7152df1fe9cSrandyf return (DDI_FAILURE); 7162df1fe9cSrandyf } 7172df1fe9cSrandyf 7187c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 7197c478bd9Sstevel@tonic-gate } 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate /* 7227c478bd9Sstevel@tonic-gate * asyprobe 7237c478bd9Sstevel@tonic-gate * We don't bother probing for the hardware, as since Solaris 2.6, device 7247c478bd9Sstevel@tonic-gate * nodes are only created for auto-detected hardware or nodes explicitly 7257c478bd9Sstevel@tonic-gate * created by the user, e.g. via the DCA. However, we should check the 7267c478bd9Sstevel@tonic-gate * device node is at least vaguely usable, i.e. we have a block of 8 i/o 7277c478bd9Sstevel@tonic-gate * ports. This prevents attempting to attach to bogus serial ports which 7287c478bd9Sstevel@tonic-gate * some BIOSs still partially report when they are disabled in the BIOS. 7297c478bd9Sstevel@tonic-gate */ 7307c478bd9Sstevel@tonic-gate static int 7317c478bd9Sstevel@tonic-gate asyprobe(dev_info_t *devi) 7327c478bd9Sstevel@tonic-gate { 73356001103Smyers return ((asy_get_io_regnum(devi, NULL) < 0) ? 73456001103Smyers DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE); 7357c478bd9Sstevel@tonic-gate } 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate static int 7387c478bd9Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 7397c478bd9Sstevel@tonic-gate { 7407c478bd9Sstevel@tonic-gate int instance; 7417c478bd9Sstevel@tonic-gate int mcr; 7427c478bd9Sstevel@tonic-gate int ret; 7437c478bd9Sstevel@tonic-gate int regnum = 0; 7447c478bd9Sstevel@tonic-gate int i; 7457c478bd9Sstevel@tonic-gate struct asycom *asy; 74656001103Smyers char name[ASY_MINOR_LEN]; 7477c478bd9Sstevel@tonic-gate int status; 7487c478bd9Sstevel@tonic-gate static ddi_device_acc_attr_t ioattr = { 7497c478bd9Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 7507c478bd9Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 7517c478bd9Sstevel@tonic-gate DDI_STRICTORDER_ACC, 7527c478bd9Sstevel@tonic-gate }; 7537c478bd9Sstevel@tonic-gate 7542df1fe9cSrandyf instance = ddi_get_instance(devi); /* find out which unit */ 7552df1fe9cSrandyf 7562df1fe9cSrandyf switch (cmd) { 7572df1fe9cSrandyf case DDI_ATTACH: 7582df1fe9cSrandyf break; 7592df1fe9cSrandyf case DDI_RESUME: 7602df1fe9cSrandyf { 7612df1fe9cSrandyf struct asyncline *async; 7622df1fe9cSrandyf 7632df1fe9cSrandyf #ifdef DEBUG 7642df1fe9cSrandyf if (asy_nosuspend) 7652df1fe9cSrandyf return (DDI_SUCCESS); 7662df1fe9cSrandyf #endif 7672df1fe9cSrandyf asy = ddi_get_soft_state(asy_soft_state, instance); 7682df1fe9cSrandyf if (asy == NULL) 7697c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 7707c478bd9Sstevel@tonic-gate 7712df1fe9cSrandyf mutex_enter(&asy->asy_soft_sr); 7722df1fe9cSrandyf mutex_enter(&asy->asy_excl); 7732df1fe9cSrandyf mutex_enter(&asy->asy_excl_hi); 7742df1fe9cSrandyf 7752df1fe9cSrandyf async = asy->asy_priv; 7762df1fe9cSrandyf /* Disable interrupts */ 7772df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 7782df1fe9cSrandyf if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 7792df1fe9cSrandyf mutex_exit(&asy->asy_excl_hi); 7802df1fe9cSrandyf mutex_exit(&asy->asy_excl); 7812df1fe9cSrandyf mutex_exit(&asy->asy_soft_sr); 78251cc66a5SKevin Crowe cmn_err(CE_WARN, "!Cannot identify UART chip at %p\n", 7832df1fe9cSrandyf (void *)asy->asy_ioaddr); 7842df1fe9cSrandyf return (DDI_FAILURE); 7852df1fe9cSrandyf } 7862df1fe9cSrandyf asy->asy_flags &= ~ASY_DDI_SUSPENDED; 7872df1fe9cSrandyf if (async->async_flags & ASYNC_ISOPEN) { 7882df1fe9cSrandyf asy_program(asy, ASY_INIT); 7892df1fe9cSrandyf /* Kick off output */ 7902df1fe9cSrandyf if (async->async_ocnt > 0) { 7912df1fe9cSrandyf async_resume(async); 7922df1fe9cSrandyf } else { 7932df1fe9cSrandyf mutex_exit(&asy->asy_excl_hi); 7942df1fe9cSrandyf if (async->async_xmitblk) 7952df1fe9cSrandyf freeb(async->async_xmitblk); 7962df1fe9cSrandyf async->async_xmitblk = NULL; 7972df1fe9cSrandyf async_start(async); 7982df1fe9cSrandyf mutex_enter(&asy->asy_excl_hi); 7992df1fe9cSrandyf } 8002df1fe9cSrandyf ASYSETSOFT(asy); 8012df1fe9cSrandyf } 8022df1fe9cSrandyf mutex_exit(&asy->asy_excl_hi); 8032df1fe9cSrandyf mutex_exit(&asy->asy_excl); 8042df1fe9cSrandyf mutex_exit(&asy->asy_soft_sr); 8052df1fe9cSrandyf 8062df1fe9cSrandyf mutex_enter(&asy->asy_excl); 8072df1fe9cSrandyf if (async->async_flags & ASYNC_RESUME_BUFCALL) { 8082df1fe9cSrandyf async->async_wbufcid = bufcall(async->async_wbufcds, 8092df1fe9cSrandyf BPRI_HI, (void (*)(void *)) async_reioctl, 8102df1fe9cSrandyf (void *)(intptr_t)async->async_common->asy_unit); 8112df1fe9cSrandyf async->async_flags &= ~ASYNC_RESUME_BUFCALL; 8122df1fe9cSrandyf } 8132df1fe9cSrandyf async_process_suspq(asy); 8142df1fe9cSrandyf mutex_exit(&asy->asy_excl); 8152df1fe9cSrandyf return (DDI_SUCCESS); 8162df1fe9cSrandyf } 8172df1fe9cSrandyf default: 8182df1fe9cSrandyf return (DDI_FAILURE); 8192df1fe9cSrandyf } 8202df1fe9cSrandyf 8217c478bd9Sstevel@tonic-gate ret = ddi_soft_state_zalloc(asy_soft_state, instance); 8227c478bd9Sstevel@tonic-gate if (ret != DDI_SUCCESS) 8237c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8247c478bd9Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 8257c478bd9Sstevel@tonic-gate ASSERT(asy != NULL); /* can't fail - we only just allocated it */ 8267c478bd9Sstevel@tonic-gate asy->asy_unit = instance; 8277c478bd9Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 8287c478bd9Sstevel@tonic-gate if (instance > max_asy_instance) 8297c478bd9Sstevel@tonic-gate max_asy_instance = instance; 8307c478bd9Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 8317c478bd9Sstevel@tonic-gate 83256001103Smyers regnum = asy_get_io_regnum(devi, asy); 8337c478bd9Sstevel@tonic-gate 8347c478bd9Sstevel@tonic-gate if (regnum < 0 || 8357c478bd9Sstevel@tonic-gate ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr, 8367c478bd9Sstevel@tonic-gate (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle) 8377c478bd9Sstevel@tonic-gate != DDI_SUCCESS) { 8387c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p", 8397c478bd9Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 8407c478bd9Sstevel@tonic-gate 8417c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 8427c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 8437c478bd9Sstevel@tonic-gate } 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n", 8467c478bd9Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 8477c478bd9Sstevel@tonic-gate 8487c478bd9Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 8497c478bd9Sstevel@tonic-gate if (com_ports == NULL) { /* need to initialize com_ports */ 8507c478bd9Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0, 8517c478bd9Sstevel@tonic-gate "motherboard-serial-ports", &com_ports, &num_com_ports) != 8527c478bd9Sstevel@tonic-gate DDI_PROP_SUCCESS) { 8537c478bd9Sstevel@tonic-gate /* Use our built-in COM[1234] values */ 8547c478bd9Sstevel@tonic-gate com_ports = (int *)standard_com_ports; 8557c478bd9Sstevel@tonic-gate num_com_ports = sizeof (standard_com_ports) / 8567c478bd9Sstevel@tonic-gate sizeof (standard_com_ports[0]); 8577c478bd9Sstevel@tonic-gate } 8587c478bd9Sstevel@tonic-gate if (num_com_ports > 10) { 8597c478bd9Sstevel@tonic-gate /* We run out of single digits for device properties */ 8607c478bd9Sstevel@tonic-gate num_com_ports = 10; 8617c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 8627c478bd9Sstevel@tonic-gate "More than %d motherboard-serial-ports", 8637c478bd9Sstevel@tonic-gate num_com_ports); 8647c478bd9Sstevel@tonic-gate } 8657c478bd9Sstevel@tonic-gate } 8667c478bd9Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* 8697c478bd9Sstevel@tonic-gate * Lookup the i/o address to see if this is a standard COM port 8707c478bd9Sstevel@tonic-gate * in which case we assign it the correct tty[a-d] to match the 8717c478bd9Sstevel@tonic-gate * COM port number, or some other i/o address in which case it 8727c478bd9Sstevel@tonic-gate * will be assigned /dev/term/[0123...] in some rather arbitrary 8737c478bd9Sstevel@tonic-gate * fashion. 8747c478bd9Sstevel@tonic-gate */ 8757c478bd9Sstevel@tonic-gate 8767c478bd9Sstevel@tonic-gate for (i = 0; i < num_com_ports; i++) { 8777c478bd9Sstevel@tonic-gate if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) { 8787c478bd9Sstevel@tonic-gate asy->asy_com_port = i + 1; 8797c478bd9Sstevel@tonic-gate break; 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate } 8827c478bd9Sstevel@tonic-gate 8837c478bd9Sstevel@tonic-gate /* 8847c478bd9Sstevel@tonic-gate * It appears that there was async hardware that on reset 8857c478bd9Sstevel@tonic-gate * did not clear ICR. Hence when we get to 8867c478bd9Sstevel@tonic-gate * ddi_get_iblock_cookie below, this hardware would cause 8877c478bd9Sstevel@tonic-gate * the system to hang if there was input available. 8887c478bd9Sstevel@tonic-gate */ 8897c478bd9Sstevel@tonic-gate 8904ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00); 8917c478bd9Sstevel@tonic-gate 8927c478bd9Sstevel@tonic-gate /* establish default usage */ 8937c478bd9Sstevel@tonic-gate asy->asy_mcr |= RTS|DTR; /* do use RTS/DTR after open */ 8947c478bd9Sstevel@tonic-gate asy->asy_lcr = STOP1|BITS8; /* default to 1 stop 8 bits */ 8957c478bd9Sstevel@tonic-gate asy->asy_bidx = B9600; /* default to 9600 */ 8967c478bd9Sstevel@tonic-gate #ifdef DEBUG 8977c478bd9Sstevel@tonic-gate asy->asy_msint_cnt = 0; /* # of times in async_msint */ 8987c478bd9Sstevel@tonic-gate #endif 8997c478bd9Sstevel@tonic-gate mcr = 0; /* don't enable until open */ 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate if (asy->asy_com_port != 0) { 9027c478bd9Sstevel@tonic-gate /* 9037c478bd9Sstevel@tonic-gate * For motherboard ports, emulate tty eeprom properties. 9047c478bd9Sstevel@tonic-gate * Actually, we can't tell if a port is motherboard or not, 9057c478bd9Sstevel@tonic-gate * so for "motherboard ports", read standard DOS COM ports. 9067c478bd9Sstevel@tonic-gate */ 9077c478bd9Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "ignore-cd")) { 9087c478bd9Sstevel@tonic-gate case 0: /* *-ignore-cd=False */ 9097c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9107c478bd9Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD\n", instance); 9117c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 9127c478bd9Sstevel@tonic-gate break; 9137c478bd9Sstevel@tonic-gate case 1: /* *-ignore-cd=True */ 9147c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 9157c478bd9Sstevel@tonic-gate default: /* *-ignore-cd not defined */ 9167c478bd9Sstevel@tonic-gate /* 9177c478bd9Sstevel@tonic-gate * We set rather silly defaults of soft carrier on 9187c478bd9Sstevel@tonic-gate * and DTR/RTS raised here because it might be that 9197c478bd9Sstevel@tonic-gate * one of the motherboard ports is the system console. 9207c478bd9Sstevel@tonic-gate */ 9217c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9227c478bd9Sstevel@tonic-gate "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n", 9237c478bd9Sstevel@tonic-gate instance); 9247c478bd9Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 9257c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 9267c478bd9Sstevel@tonic-gate break; 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate /* Property for not raising DTR/RTS */ 9307c478bd9Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "rts-dtr-off")) { 9317c478bd9Sstevel@tonic-gate case 0: /* *-rts-dtr-off=False */ 9327c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_RTS_DTR_OFF; /* OFF */ 9337c478bd9Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 9347c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: " 9357c478bd9Sstevel@tonic-gate "ASY_RTS_DTR_OFF set and DTR & RTS set\n", 9367c478bd9Sstevel@tonic-gate instance); 9377c478bd9Sstevel@tonic-gate break; 9387c478bd9Sstevel@tonic-gate case 1: /* *-rts-dtr-off=True */ 9397c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 9407c478bd9Sstevel@tonic-gate default: /* *-rts-dtr-off undefined */ 9417c478bd9Sstevel@tonic-gate break; 9427c478bd9Sstevel@tonic-gate } 9437c478bd9Sstevel@tonic-gate 9447c478bd9Sstevel@tonic-gate /* Parse property for tty modes */ 9457c478bd9Sstevel@tonic-gate asy_parse_mode(devi, asy); 9467c478bd9Sstevel@tonic-gate } else { 9477c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9487c478bd9Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n", 9497c478bd9Sstevel@tonic-gate instance); 9507c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 9517c478bd9Sstevel@tonic-gate } 9527c478bd9Sstevel@tonic-gate 9537c478bd9Sstevel@tonic-gate /* 9547c478bd9Sstevel@tonic-gate * Initialize the port with default settings. 9557c478bd9Sstevel@tonic-gate */ 9567c478bd9Sstevel@tonic-gate 9577c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 1; 9587c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF; 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate /* 9617c478bd9Sstevel@tonic-gate * Get icookie for mutexes initialization 9627c478bd9Sstevel@tonic-gate */ 9637c478bd9Sstevel@tonic-gate if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != 9647c478bd9Sstevel@tonic-gate DDI_SUCCESS) || 9657c478bd9Sstevel@tonic-gate (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED, 966cd43c54aSZach Kissel &asy->asy_soft_iblock) != DDI_SUCCESS)) { 9677c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 9687c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, 9697c478bd9Sstevel@tonic-gate "asy%d: could not hook interrupt for UART @ %p\n", 9707c478bd9Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 9717c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 9727c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 9737c478bd9Sstevel@tonic-gate } 9747c478bd9Sstevel@tonic-gate 9757c478bd9Sstevel@tonic-gate /* 9767c478bd9Sstevel@tonic-gate * Initialize mutexes before accessing the hardware 9777c478bd9Sstevel@tonic-gate */ 978cd43c54aSZach Kissel mutex_init(&asy->asy_soft_lock, NULL, MUTEX_DRIVER, 979cd43c54aSZach Kissel (void *)asy->asy_soft_iblock); 980cd43c54aSZach Kissel mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, NULL); 9817c478bd9Sstevel@tonic-gate mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER, 9827c478bd9Sstevel@tonic-gate (void *)asy->asy_iblock); 983cd43c54aSZach Kissel mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, 984cd43c54aSZach Kissel (void *)asy->asy_soft_iblock); 9857c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 9867c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 9897c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 9907c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 991cd43c54aSZach Kissel mutex_destroy(&asy->asy_soft_lock); 9927c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 9937c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 9942df1fe9cSrandyf mutex_destroy(&asy->asy_soft_sr); 9957c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 99651cc66a5SKevin Crowe cmn_err(CE_CONT, "!Cannot identify UART chip at %p\n", 9977c478bd9Sstevel@tonic-gate (void *)asy->asy_ioaddr); 9987c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 9997c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10007c478bd9Sstevel@tonic-gate } 10017c478bd9Sstevel@tonic-gate 10027c478bd9Sstevel@tonic-gate /* disable all interrupts */ 10034ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 10047c478bd9Sstevel@tonic-gate /* select baud rate generator */ 10054ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB); 10067c478bd9Sstevel@tonic-gate /* Set the baud rate to 9600 */ 10074ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL), 10087c478bd9Sstevel@tonic-gate asyspdtab[asy->asy_bidx] & 0xff); 10094ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH), 10107c478bd9Sstevel@tonic-gate (asyspdtab[asy->asy_bidx] >> 8) & 0xff); 10112df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr); 10124ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr); 10137c478bd9Sstevel@tonic-gate 10147c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 10157c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate /* 10187c478bd9Sstevel@tonic-gate * Set up the other components of the asycom structure for this port. 10197c478bd9Sstevel@tonic-gate */ 10207c478bd9Sstevel@tonic-gate asy->asy_dip = devi; 10217c478bd9Sstevel@tonic-gate 1022cd43c54aSZach Kissel /* 1023cd43c54aSZach Kissel * Install per instance software interrupt handler. 1024cd43c54aSZach Kissel */ 10257c478bd9Sstevel@tonic-gate if (ddi_add_softintr(devi, DDI_SOFTINT_MED, 1026cd43c54aSZach Kissel &(asy->asy_softintr_id), NULL, 0, asysoftintr, 1027cd43c54aSZach Kissel (caddr_t)asy) != DDI_SUCCESS) { 1028cd43c54aSZach Kissel mutex_destroy(&asy->asy_soft_lock); 10297c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 10307c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 10317c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 10327c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, 10337c478bd9Sstevel@tonic-gate "Can not set soft interrupt for ASY driver\n"); 10347c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 10357c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10367c478bd9Sstevel@tonic-gate } 10377c478bd9Sstevel@tonic-gate 10387c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 10397c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate /* 10427c478bd9Sstevel@tonic-gate * Install interrupt handler for this device. 10437c478bd9Sstevel@tonic-gate */ 10447c478bd9Sstevel@tonic-gate if (ddi_add_intr(devi, 0, NULL, 0, asyintr, 10457c478bd9Sstevel@tonic-gate (caddr_t)asy) != DDI_SUCCESS) { 10467c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 10477c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 1048cd43c54aSZach Kissel ddi_remove_softintr(asy->asy_softintr_id); 1049cd43c54aSZach Kissel mutex_destroy(&asy->asy_soft_lock); 10507c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 10517c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 10527c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 10537c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, 10547c478bd9Sstevel@tonic-gate "Can not set device interrupt for ASY driver\n"); 10557c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 10567c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 10577c478bd9Sstevel@tonic-gate } 10587c478bd9Sstevel@tonic-gate 10597c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 10607c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 10617c478bd9Sstevel@tonic-gate 10627c478bd9Sstevel@tonic-gate asyinit(asy); /* initialize the asyncline structure */ 10637c478bd9Sstevel@tonic-gate 10647c478bd9Sstevel@tonic-gate /* create minor device nodes for this device */ 10657c478bd9Sstevel@tonic-gate if (asy->asy_com_port != 0) { 10667c478bd9Sstevel@tonic-gate /* 10677c478bd9Sstevel@tonic-gate * For DOS COM ports, add letter suffix so 10687c478bd9Sstevel@tonic-gate * devfsadm can create correct link names. 10697c478bd9Sstevel@tonic-gate */ 10707c478bd9Sstevel@tonic-gate name[0] = asy->asy_com_port + 'a' - 1; 10717c478bd9Sstevel@tonic-gate name[1] = '\0'; 10727c478bd9Sstevel@tonic-gate } else { 10737c478bd9Sstevel@tonic-gate /* 107456001103Smyers * asy port which isn't a standard DOS COM 107556001103Smyers * port gets a numeric name based on instance 10767c478bd9Sstevel@tonic-gate */ 107756001103Smyers (void) snprintf(name, ASY_MINOR_LEN, "%d", instance); 10787c478bd9Sstevel@tonic-gate } 10797c478bd9Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, instance, 10807c478bd9Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, NULL); 10817c478bd9Sstevel@tonic-gate if (status == DDI_SUCCESS) { 10827c478bd9Sstevel@tonic-gate (void) strcat(name, ",cu"); 10837c478bd9Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, 10847c478bd9Sstevel@tonic-gate OUTLINE | instance, 10857c478bd9Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO : 10867c478bd9Sstevel@tonic-gate DDI_NT_SERIAL_DO, NULL); 10877c478bd9Sstevel@tonic-gate } 10887c478bd9Sstevel@tonic-gate 10897c478bd9Sstevel@tonic-gate if (status != DDI_SUCCESS) { 10907c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 10937c478bd9Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock); 1094cd43c54aSZach Kissel ddi_remove_softintr(asy->asy_softintr_id); 1095cd43c54aSZach Kissel mutex_destroy(&asy->asy_soft_lock); 10967c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 10977c478bd9Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 10987c478bd9Sstevel@tonic-gate cv_destroy(&async->async_flags_cv); 10997c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 11007c478bd9Sstevel@tonic-gate asy_soft_state_free(asy); 11017c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 11027c478bd9Sstevel@tonic-gate } 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate /* 11057c478bd9Sstevel@tonic-gate * Fill in the polled I/O structure. 11067c478bd9Sstevel@tonic-gate */ 11077c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0; 1108281f0747Slt200341 asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy; 11097c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_putchar = asyputchar; 11107c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_getchar = asygetchar; 11117c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_ischar = asyischar; 11127c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_enter = NULL; 11137c478bd9Sstevel@tonic-gate asy->polledio.cons_polledio_exit = NULL; 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate ddi_report_dev(devi); 11167c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dattach: done\n", instance); 11177c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 11187c478bd9Sstevel@tonic-gate } 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 11217c478bd9Sstevel@tonic-gate static int 11227c478bd9Sstevel@tonic-gate asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 11237c478bd9Sstevel@tonic-gate void **result) 11247c478bd9Sstevel@tonic-gate { 11257c478bd9Sstevel@tonic-gate dev_t dev = (dev_t)arg; 11267c478bd9Sstevel@tonic-gate int instance, error; 11277c478bd9Sstevel@tonic-gate struct asycom *asy; 11287c478bd9Sstevel@tonic-gate 11297c478bd9Sstevel@tonic-gate instance = UNIT(dev); 11307c478bd9Sstevel@tonic-gate 11317c478bd9Sstevel@tonic-gate switch (infocmd) { 11327c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 113370796907Scth asy = ddi_get_soft_state(asy_soft_state, instance); 113470796907Scth if ((asy == NULL) || (asy->asy_dip == NULL)) 11357c478bd9Sstevel@tonic-gate error = DDI_FAILURE; 11367c478bd9Sstevel@tonic-gate else { 11377c478bd9Sstevel@tonic-gate *result = (void *) asy->asy_dip; 11387c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate break; 11417c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 11427c478bd9Sstevel@tonic-gate *result = (void *)(intptr_t)instance; 11437c478bd9Sstevel@tonic-gate error = DDI_SUCCESS; 11447c478bd9Sstevel@tonic-gate break; 11457c478bd9Sstevel@tonic-gate default: 11467c478bd9Sstevel@tonic-gate error = DDI_FAILURE; 11477c478bd9Sstevel@tonic-gate } 11487c478bd9Sstevel@tonic-gate return (error); 11497c478bd9Sstevel@tonic-gate } 11507c478bd9Sstevel@tonic-gate 11517c478bd9Sstevel@tonic-gate /* asy_getproperty -- walk through all name variants until we find a match */ 11527c478bd9Sstevel@tonic-gate 11537c478bd9Sstevel@tonic-gate static int 11547c478bd9Sstevel@tonic-gate asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property) 11557c478bd9Sstevel@tonic-gate { 11567c478bd9Sstevel@tonic-gate int len; 11577c478bd9Sstevel@tonic-gate int ret; 11587c478bd9Sstevel@tonic-gate char letter = asy->asy_com_port + 'a' - 1; /* for ttya */ 11597c478bd9Sstevel@tonic-gate char number = asy->asy_com_port + '0'; /* for COM1 */ 11607c478bd9Sstevel@tonic-gate char val[40]; 11617c478bd9Sstevel@tonic-gate char name[40]; 11627c478bd9Sstevel@tonic-gate 11637c478bd9Sstevel@tonic-gate /* Property for ignoring DCD */ 11647c478bd9Sstevel@tonic-gate (void) sprintf(name, "tty%c-%s", letter, property); 11657c478bd9Sstevel@tonic-gate len = sizeof (val); 11667c478bd9Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 11677c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 11687c478bd9Sstevel@tonic-gate (void) sprintf(name, "com%c-%s", number, property); 11697c478bd9Sstevel@tonic-gate len = sizeof (val); 11702df1fe9cSrandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 11717c478bd9Sstevel@tonic-gate } 11727c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 11737c478bd9Sstevel@tonic-gate (void) sprintf(name, "tty0%c-%s", number, property); 11747c478bd9Sstevel@tonic-gate len = sizeof (val); 11752df1fe9cSrandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 11767c478bd9Sstevel@tonic-gate } 11777c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 11787c478bd9Sstevel@tonic-gate (void) sprintf(name, "port-%c-%s", letter, property); 11797c478bd9Sstevel@tonic-gate len = sizeof (val); 11802df1fe9cSrandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 11817c478bd9Sstevel@tonic-gate } 11827c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 11837c478bd9Sstevel@tonic-gate return (-1); /* property non-existant */ 11847c478bd9Sstevel@tonic-gate if (val[0] == 'f' || val[0] == 'F' || val[0] == '0') 11857c478bd9Sstevel@tonic-gate return (0); /* property false/0 */ 11867c478bd9Sstevel@tonic-gate return (1); /* property true/!0 */ 11877c478bd9Sstevel@tonic-gate } 11887c478bd9Sstevel@tonic-gate 11897c478bd9Sstevel@tonic-gate /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */ 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate static void 11927c478bd9Sstevel@tonic-gate asy_soft_state_free(struct asycom *asy) 11937c478bd9Sstevel@tonic-gate { 11947c478bd9Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 11957c478bd9Sstevel@tonic-gate /* If we were the max_asy_instance, work out new value */ 11967c478bd9Sstevel@tonic-gate if (asy->asy_unit == max_asy_instance) { 11977c478bd9Sstevel@tonic-gate while (--max_asy_instance >= 0) { 11987c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(asy_soft_state, 11997c478bd9Sstevel@tonic-gate max_asy_instance) != NULL) 12007c478bd9Sstevel@tonic-gate break; 12017c478bd9Sstevel@tonic-gate } 12027c478bd9Sstevel@tonic-gate } 12037c478bd9Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 12047c478bd9Sstevel@tonic-gate 12057c478bd9Sstevel@tonic-gate if (asy->asy_priv != NULL) { 12067c478bd9Sstevel@tonic-gate kmem_free(asy->asy_priv, sizeof (struct asyncline)); 12077c478bd9Sstevel@tonic-gate asy->asy_priv = NULL; 12087c478bd9Sstevel@tonic-gate } 12097c478bd9Sstevel@tonic-gate ddi_soft_state_free(asy_soft_state, asy->asy_unit); 12107c478bd9Sstevel@tonic-gate } 12117c478bd9Sstevel@tonic-gate 12127c478bd9Sstevel@tonic-gate static char * 12137c478bd9Sstevel@tonic-gate asy_hw_name(struct asycom *asy) 12147c478bd9Sstevel@tonic-gate { 12157c478bd9Sstevel@tonic-gate switch (asy->asy_hwtype) { 12167c478bd9Sstevel@tonic-gate case ASY8250A: 12177c478bd9Sstevel@tonic-gate return ("8250A/16450"); 12187c478bd9Sstevel@tonic-gate case ASY16550: 12197c478bd9Sstevel@tonic-gate return ("16550"); 12207c478bd9Sstevel@tonic-gate case ASY16550A: 12217c478bd9Sstevel@tonic-gate return ("16550A"); 12227c478bd9Sstevel@tonic-gate case ASY16650: 12237c478bd9Sstevel@tonic-gate return ("16650"); 12247c478bd9Sstevel@tonic-gate case ASY16750: 12257c478bd9Sstevel@tonic-gate return ("16750"); 12267c478bd9Sstevel@tonic-gate default: 12277c478bd9Sstevel@tonic-gate DEBUGNOTE2(ASY_DEBUG_INIT, 12287c478bd9Sstevel@tonic-gate "asy%d: asy_hw_name: unknown asy_hwtype: %d", 12297c478bd9Sstevel@tonic-gate asy->asy_unit, asy->asy_hwtype); 12307c478bd9Sstevel@tonic-gate return ("?"); 12317c478bd9Sstevel@tonic-gate } 12327c478bd9Sstevel@tonic-gate } 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate static int 12357c478bd9Sstevel@tonic-gate asy_identify_chip(dev_info_t *devi, struct asycom *asy) 12367c478bd9Sstevel@tonic-gate { 12377c478bd9Sstevel@tonic-gate int ret; 12387c478bd9Sstevel@tonic-gate int mcr; 12397c478bd9Sstevel@tonic-gate dev_t dev; 12407c478bd9Sstevel@tonic-gate uint_t hwtype; 12417c478bd9Sstevel@tonic-gate 12427c478bd9Sstevel@tonic-gate if (asy_scr_test) { 12437c478bd9Sstevel@tonic-gate /* Check scratch register works. */ 12447c478bd9Sstevel@tonic-gate 12457c478bd9Sstevel@tonic-gate /* write to scratch register */ 12464ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, SCRTEST); 12477c478bd9Sstevel@tonic-gate /* make sure that pattern doesn't just linger on the bus */ 12484ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 0x00); 12497c478bd9Sstevel@tonic-gate /* read data back from scratch register */ 12504ab75253Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR); 12517c478bd9Sstevel@tonic-gate if (ret != SCRTEST) { 12527c478bd9Sstevel@tonic-gate /* 12537c478bd9Sstevel@tonic-gate * Scratch register not working. 12547c478bd9Sstevel@tonic-gate * Probably not an async chip. 12557c478bd9Sstevel@tonic-gate * 8250 and 8250B don't have scratch registers, 12567c478bd9Sstevel@tonic-gate * but only worked in ancient PC XT's anyway. 12577c478bd9Sstevel@tonic-gate */ 125851cc66a5SKevin Crowe cmn_err(CE_CONT, "!asy%d: UART @ %p " 12597c478bd9Sstevel@tonic-gate "scratch register: expected 0x5a, got 0x%02x\n", 12607c478bd9Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr, ret); 12617c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 12627c478bd9Sstevel@tonic-gate } 12637c478bd9Sstevel@tonic-gate } 12647c478bd9Sstevel@tonic-gate /* 12657c478bd9Sstevel@tonic-gate * Use 16550 fifo reset sequence specified in NS application 12667c478bd9Sstevel@tonic-gate * note. Disable fifos until chip is initialized. 12677c478bd9Sstevel@tonic-gate */ 12684ab75253Smrj ddi_put8(asy->asy_iohandle, 12697c478bd9Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, 0x00); /* clear */ 12704ab75253Smrj ddi_put8(asy->asy_iohandle, 12717c478bd9Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON); /* enable */ 12724ab75253Smrj ddi_put8(asy->asy_iohandle, 12737c478bd9Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON | FIFORXFLSH); 12747c478bd9Sstevel@tonic-gate /* reset */ 12757c478bd9Sstevel@tonic-gate if (asymaxchip >= ASY16650 && asy_scr_test) { 12767c478bd9Sstevel@tonic-gate /* 12777c478bd9Sstevel@tonic-gate * Reset 16650 enhanced regs also, in case we have one of these 12787c478bd9Sstevel@tonic-gate */ 12794ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 12807c478bd9Sstevel@tonic-gate EFRACCESS); 12814ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 12827c478bd9Sstevel@tonic-gate 0); 12834ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 12847c478bd9Sstevel@tonic-gate STOP1|BITS8); 12857c478bd9Sstevel@tonic-gate } 12867c478bd9Sstevel@tonic-gate 12877c478bd9Sstevel@tonic-gate /* 12887c478bd9Sstevel@tonic-gate * See what sort of FIFO we have. 12897c478bd9Sstevel@tonic-gate * Try enabling it and see what chip makes of this. 12907c478bd9Sstevel@tonic-gate */ 12917c478bd9Sstevel@tonic-gate 12927c478bd9Sstevel@tonic-gate asy->asy_fifor = 0; 12937c478bd9Sstevel@tonic-gate asy->asy_hwtype = asymaxchip; /* just for asy_reset_fifo() */ 12947c478bd9Sstevel@tonic-gate if (asymaxchip >= ASY16550A) 12957c478bd9Sstevel@tonic-gate asy->asy_fifor |= 12967c478bd9Sstevel@tonic-gate FIFO_ON | FIFODMA | (asy_trig_level & 0xff); 12977c478bd9Sstevel@tonic-gate if (asymaxchip >= ASY16650) 12987c478bd9Sstevel@tonic-gate asy->asy_fifor |= FIFOEXTRA1 | FIFOEXTRA2; 12997c478bd9Sstevel@tonic-gate 13007c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 13017c478bd9Sstevel@tonic-gate 13024ab75253Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 13034ab75253Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR); 13047c478bd9Sstevel@tonic-gate DEBUGCONT4(ASY_DEBUG_CHIP, 13057c478bd9Sstevel@tonic-gate "asy%d: probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n", 13067c478bd9Sstevel@tonic-gate asy->asy_unit, asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, 13077c478bd9Sstevel@tonic-gate ret, mcr); 13087c478bd9Sstevel@tonic-gate switch (ret & 0xf0) { 13097c478bd9Sstevel@tonic-gate case 0x40: 13107c478bd9Sstevel@tonic-gate hwtype = ASY16550; /* 16550 with broken FIFO */ 13117c478bd9Sstevel@tonic-gate asy->asy_fifor = 0; 13127c478bd9Sstevel@tonic-gate break; 13137c478bd9Sstevel@tonic-gate case 0xc0: 13147c478bd9Sstevel@tonic-gate hwtype = ASY16550A; 13157c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 16; 13167c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 13177c478bd9Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2); 13187c478bd9Sstevel@tonic-gate break; 13197c478bd9Sstevel@tonic-gate case 0xe0: 13207c478bd9Sstevel@tonic-gate hwtype = ASY16650; 13217c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 32; 13227c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 13237c478bd9Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1); 13247c478bd9Sstevel@tonic-gate break; 13257c478bd9Sstevel@tonic-gate case 0xf0: 13267c478bd9Sstevel@tonic-gate /* 13277c478bd9Sstevel@tonic-gate * Note we get 0xff if chip didn't return us anything, 13287c478bd9Sstevel@tonic-gate * e.g. if there's no chip there. 13297c478bd9Sstevel@tonic-gate */ 13307c478bd9Sstevel@tonic-gate if (ret == 0xff) { 13317c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p " 13327c478bd9Sstevel@tonic-gate "interrupt register: got 0xff\n", 13337c478bd9Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr); 13347c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 13357c478bd9Sstevel@tonic-gate } 13367c478bd9Sstevel@tonic-gate /*FALLTHRU*/ 13377c478bd9Sstevel@tonic-gate case 0xd0: 13387c478bd9Sstevel@tonic-gate hwtype = ASY16750; 13397c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 64; 13407c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON; 13417c478bd9Sstevel@tonic-gate break; 13427c478bd9Sstevel@tonic-gate default: 13437c478bd9Sstevel@tonic-gate hwtype = ASY8250A; /* No FIFO */ 13447c478bd9Sstevel@tonic-gate asy->asy_fifor = 0; 13457c478bd9Sstevel@tonic-gate } 13467c478bd9Sstevel@tonic-gate 13477c478bd9Sstevel@tonic-gate if (hwtype > asymaxchip) { 13487c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p " 13497c478bd9Sstevel@tonic-gate "unexpected probe result: " 13507c478bd9Sstevel@tonic-gate "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n", 13517c478bd9Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr, 13527c478bd9Sstevel@tonic-gate asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr); 13537c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 13547c478bd9Sstevel@tonic-gate } 13557c478bd9Sstevel@tonic-gate 13567c478bd9Sstevel@tonic-gate /* 13577c478bd9Sstevel@tonic-gate * Now reset the FIFO operation appropriate for the chip type. 13587c478bd9Sstevel@tonic-gate * Note we must call asy_reset_fifo() before any possible 13597c478bd9Sstevel@tonic-gate * downgrade of the asy->asy_hwtype, or it may not disable 13607c478bd9Sstevel@tonic-gate * the more advanced features we specifically want downgraded. 13617c478bd9Sstevel@tonic-gate */ 13627c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, 0); 13637c478bd9Sstevel@tonic-gate asy->asy_hwtype = hwtype; 13647c478bd9Sstevel@tonic-gate 13657c478bd9Sstevel@tonic-gate /* 13667c478bd9Sstevel@tonic-gate * Check for Exar/Startech ST16C650, which will still look like a 13677c478bd9Sstevel@tonic-gate * 16550A until we enable its enhanced mode. 13687c478bd9Sstevel@tonic-gate */ 13697c478bd9Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550A && asymaxchip >= ASY16650 && 13707c478bd9Sstevel@tonic-gate asy_scr_test) { 13717c478bd9Sstevel@tonic-gate /* Enable enhanced mode register access */ 13724ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 13737c478bd9Sstevel@tonic-gate EFRACCESS); 13747c478bd9Sstevel@tonic-gate /* zero scratch register (not scratch register if enhanced) */ 13754ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, 0); 13767c478bd9Sstevel@tonic-gate /* Disable enhanced mode register access */ 13774ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 13787c478bd9Sstevel@tonic-gate STOP1|BITS8); 13797c478bd9Sstevel@tonic-gate /* read back scratch register */ 13804ab75253Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR); 13817c478bd9Sstevel@tonic-gate if (ret == SCRTEST) { 13827c478bd9Sstevel@tonic-gate /* looks like we have an ST16650 -- enable it */ 13834ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 13847c478bd9Sstevel@tonic-gate EFRACCESS); 13854ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 13867c478bd9Sstevel@tonic-gate ENHENABLE); 13874ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 13887c478bd9Sstevel@tonic-gate STOP1|BITS8); 13897c478bd9Sstevel@tonic-gate asy->asy_hwtype = ASY16650; 13907c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 32; 13917c478bd9Sstevel@tonic-gate asy->asy_fifor |= 0x10; /* 24 byte txfifo trigger */ 13927c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, 0); 13937c478bd9Sstevel@tonic-gate } 13947c478bd9Sstevel@tonic-gate } 13957c478bd9Sstevel@tonic-gate 13967c478bd9Sstevel@tonic-gate /* 13977c478bd9Sstevel@tonic-gate * If we think we might have a FIFO larger than 16 characters, 13987c478bd9Sstevel@tonic-gate * measure FIFO size and check it against expected. 13997c478bd9Sstevel@tonic-gate */ 14007c478bd9Sstevel@tonic-gate if (asy_fifo_test > 0 && 14017c478bd9Sstevel@tonic-gate !(asy->asy_flags2 & ASY2_NO_LOOPBACK) && 14027c478bd9Sstevel@tonic-gate (asy->asy_fifo_buf > 16 || 14037c478bd9Sstevel@tonic-gate (asy_fifo_test > 1 && asy->asy_use_fifo == FIFO_ON) || 14047c478bd9Sstevel@tonic-gate ASY_DEBUG(ASY_DEBUG_CHIP))) { 14057c478bd9Sstevel@tonic-gate int i; 14067c478bd9Sstevel@tonic-gate 14077c478bd9Sstevel@tonic-gate /* Set baud rate to 57600 (fairly arbitrary choice) */ 14084ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 14097c478bd9Sstevel@tonic-gate DLAB); 14104ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 14117c478bd9Sstevel@tonic-gate asyspdtab[B57600] & 0xff); 14124ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 14137c478bd9Sstevel@tonic-gate (asyspdtab[B57600] >> 8) & 0xff); 14147c478bd9Sstevel@tonic-gate /* Set 8 bits, 1 stop bit */ 14154ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 14167c478bd9Sstevel@tonic-gate STOP1|BITS8); 14177c478bd9Sstevel@tonic-gate /* Set loopback mode */ 14184ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 14197c478bd9Sstevel@tonic-gate DTR | RTS | ASY_LOOP | OUT1 | OUT2); 14207c478bd9Sstevel@tonic-gate 14217c478bd9Sstevel@tonic-gate /* Overfill fifo */ 14227c478bd9Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 2; i++) { 14234ab75253Smrj ddi_put8(asy->asy_iohandle, 14247c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT, i); 14257c478bd9Sstevel@tonic-gate } 14267c478bd9Sstevel@tonic-gate /* 14277c478bd9Sstevel@tonic-gate * Now there's an interesting question here about which 14287c478bd9Sstevel@tonic-gate * FIFO we're testing the size of, RX or TX. We just 14297c478bd9Sstevel@tonic-gate * filled the TX FIFO much faster than it can empty, 14307c478bd9Sstevel@tonic-gate * although it is possible one or two characters may 14317c478bd9Sstevel@tonic-gate * have gone from it to the TX shift register. 14327c478bd9Sstevel@tonic-gate * We wait for enough time for all the characters to 14337c478bd9Sstevel@tonic-gate * move into the RX FIFO and any excess characters to 14347c478bd9Sstevel@tonic-gate * have been lost, and then read all the RX FIFO. So 14357c478bd9Sstevel@tonic-gate * the answer we finally get will be the size which is 14367c478bd9Sstevel@tonic-gate * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical 14377c478bd9Sstevel@tonic-gate * one is actually the TX FIFO, because if we overfill 14387c478bd9Sstevel@tonic-gate * it in normal operation, the excess characters are 14397c478bd9Sstevel@tonic-gate * lost with no warning. 14407c478bd9Sstevel@tonic-gate */ 14417c478bd9Sstevel@tonic-gate /* 14427c478bd9Sstevel@tonic-gate * Wait for characters to move into RX FIFO. 14437c478bd9Sstevel@tonic-gate * In theory, 200 * asy->asy_fifo_buf * 2 should be 14447c478bd9Sstevel@tonic-gate * enough. However, in practice it isn't always, so we 14457c478bd9Sstevel@tonic-gate * increase to 400 so some slow 16550A's finish, and we 14467c478bd9Sstevel@tonic-gate * increase to 3 so we spot more characters coming back 14477c478bd9Sstevel@tonic-gate * than we sent, in case that should ever happen. 14487c478bd9Sstevel@tonic-gate */ 14497c478bd9Sstevel@tonic-gate delay(drv_usectohz(400 * asy->asy_fifo_buf * 3)); 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate /* Now see how many characters we can read back */ 14527c478bd9Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 3; i++) { 14534ab75253Smrj ret = ddi_get8(asy->asy_iohandle, 14547c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR); 14557c478bd9Sstevel@tonic-gate if (!(ret & RCA)) 14567c478bd9Sstevel@tonic-gate break; /* FIFO emptied */ 14574ab75253Smrj (void) ddi_get8(asy->asy_iohandle, 14587c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT); /* lose another */ 14597c478bd9Sstevel@tonic-gate } 14607c478bd9Sstevel@tonic-gate 14617c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_CHIP, 14627c478bd9Sstevel@tonic-gate "asy%d FIFO size: expected=%d, measured=%d\n", 14637c478bd9Sstevel@tonic-gate asy->asy_unit, asy->asy_fifo_buf, i); 14647c478bd9Sstevel@tonic-gate 14657c478bd9Sstevel@tonic-gate hwtype = asy->asy_hwtype; 14667c478bd9Sstevel@tonic-gate if (i < asy->asy_fifo_buf) { 14677c478bd9Sstevel@tonic-gate /* 14687c478bd9Sstevel@tonic-gate * FIFO is somewhat smaller than we anticipated. 14697c478bd9Sstevel@tonic-gate * If we have 16 characters usable, then this 14707c478bd9Sstevel@tonic-gate * UART will probably work well enough in 14717c478bd9Sstevel@tonic-gate * 16550A mode. If less than 16 characters, 14727c478bd9Sstevel@tonic-gate * then we'd better not use it at all. 14737c478bd9Sstevel@tonic-gate * UARTs with busted FIFOs do crop up. 14747c478bd9Sstevel@tonic-gate */ 14757c478bd9Sstevel@tonic-gate if (i >= 16 && asy->asy_fifo_buf >= 16) { 14767c478bd9Sstevel@tonic-gate /* fall back to a 16550A */ 14777c478bd9Sstevel@tonic-gate hwtype = ASY16550A; 14787c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 16; 14797c478bd9Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2); 14807c478bd9Sstevel@tonic-gate } else { 14817c478bd9Sstevel@tonic-gate /* fall back to no FIFO at all */ 14827c478bd9Sstevel@tonic-gate hwtype = ASY16550; 14837c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 1; 14847c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF; 14857c478bd9Sstevel@tonic-gate asy->asy_fifor &= 14867c478bd9Sstevel@tonic-gate ~(FIFO_ON | FIFOEXTRA1 | FIFOEXTRA2); 14877c478bd9Sstevel@tonic-gate } 14887c478bd9Sstevel@tonic-gate } 14897c478bd9Sstevel@tonic-gate /* 14907c478bd9Sstevel@tonic-gate * We will need to reprogram the FIFO if we changed 14917c478bd9Sstevel@tonic-gate * our mind about how to drive it above, and in any 14927c478bd9Sstevel@tonic-gate * case, it would be a good idea to flush any garbage 14937c478bd9Sstevel@tonic-gate * out incase the loopback test left anything behind. 14947c478bd9Sstevel@tonic-gate * Again as earlier above, we must call asy_reset_fifo() 14957c478bd9Sstevel@tonic-gate * before any possible downgrade of asy->asy_hwtype. 14967c478bd9Sstevel@tonic-gate */ 14977c478bd9Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16650 && hwtype < ASY16650) { 14987c478bd9Sstevel@tonic-gate /* Disable 16650 enhanced mode */ 14994ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 15007c478bd9Sstevel@tonic-gate EFRACCESS); 15014ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR, 15027c478bd9Sstevel@tonic-gate 0); 15034ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 15047c478bd9Sstevel@tonic-gate STOP1|BITS8); 15057c478bd9Sstevel@tonic-gate } 15067c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 15077c478bd9Sstevel@tonic-gate asy->asy_hwtype = hwtype; 15087c478bd9Sstevel@tonic-gate 15097c478bd9Sstevel@tonic-gate /* Clear loopback mode and restore DTR/RTS */ 15104ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr); 15117c478bd9Sstevel@tonic-gate } 15127c478bd9Sstevel@tonic-gate 15137c478bd9Sstevel@tonic-gate DEBUGNOTE3(ASY_DEBUG_CHIP, "asy%d %s @ %p", 15147c478bd9Sstevel@tonic-gate asy->asy_unit, asy_hw_name(asy), (void *)asy->asy_ioaddr); 15157c478bd9Sstevel@tonic-gate 15167c478bd9Sstevel@tonic-gate /* Make UART type visible in device tree for prtconf, etc */ 15177c478bd9Sstevel@tonic-gate dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit); 15187c478bd9Sstevel@tonic-gate (void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy)); 15197c478bd9Sstevel@tonic-gate 15207c478bd9Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550) /* for broken 16550's, */ 15217c478bd9Sstevel@tonic-gate asy->asy_hwtype = ASY8250A; /* drive them as 8250A */ 15227c478bd9Sstevel@tonic-gate 15237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 15247c478bd9Sstevel@tonic-gate } 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate /* 15277c478bd9Sstevel@tonic-gate * asyinit() initializes the TTY protocol-private data for this channel 15287c478bd9Sstevel@tonic-gate * before enabling the interrupts. 15297c478bd9Sstevel@tonic-gate */ 15307c478bd9Sstevel@tonic-gate static void 15317c478bd9Sstevel@tonic-gate asyinit(struct asycom *asy) 15327c478bd9Sstevel@tonic-gate { 15337c478bd9Sstevel@tonic-gate struct asyncline *async; 15347c478bd9Sstevel@tonic-gate 15357c478bd9Sstevel@tonic-gate asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP); 15367c478bd9Sstevel@tonic-gate async = asy->asy_priv; 15377c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 15387c478bd9Sstevel@tonic-gate async->async_common = asy; 15397c478bd9Sstevel@tonic-gate cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL); 15407c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 15417c478bd9Sstevel@tonic-gate } 15427c478bd9Sstevel@tonic-gate 15437c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 15447c478bd9Sstevel@tonic-gate static int 15457c478bd9Sstevel@tonic-gate asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 15467c478bd9Sstevel@tonic-gate { 15477c478bd9Sstevel@tonic-gate struct asycom *asy; 15487c478bd9Sstevel@tonic-gate struct asyncline *async; 15497c478bd9Sstevel@tonic-gate int mcr; 15507c478bd9Sstevel@tonic-gate int unit; 15517c478bd9Sstevel@tonic-gate int len; 15527c478bd9Sstevel@tonic-gate struct termios *termiosp; 15537c478bd9Sstevel@tonic-gate 15547c478bd9Sstevel@tonic-gate unit = UNIT(*dev); 15557c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dopen\n", unit); 15567c478bd9Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, unit); 15577c478bd9Sstevel@tonic-gate if (asy == NULL) 15587c478bd9Sstevel@tonic-gate return (ENXIO); /* unit not configured */ 15597c478bd9Sstevel@tonic-gate async = asy->asy_priv; 15607c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 15617c478bd9Sstevel@tonic-gate 15627c478bd9Sstevel@tonic-gate again: 15637c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 15647c478bd9Sstevel@tonic-gate 15657c478bd9Sstevel@tonic-gate /* 15667c478bd9Sstevel@tonic-gate * Block waiting for carrier to come up, unless this is a no-delay open. 15677c478bd9Sstevel@tonic-gate */ 15687c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) { 15697c478bd9Sstevel@tonic-gate /* 15707c478bd9Sstevel@tonic-gate * Set the default termios settings (cflag). 15717c478bd9Sstevel@tonic-gate * Others are set in ldterm. 15727c478bd9Sstevel@tonic-gate */ 15737c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 15747c478bd9Sstevel@tonic-gate 15757c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 15767c478bd9Sstevel@tonic-gate 0, "ttymodes", 15777c478bd9Sstevel@tonic-gate (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS && 15787c478bd9Sstevel@tonic-gate len == sizeof (struct termios)) { 15797c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag = termiosp->c_cflag; 15807c478bd9Sstevel@tonic-gate kmem_free(termiosp, len); 15817c478bd9Sstevel@tonic-gate } else 15827c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 15837c478bd9Sstevel@tonic-gate "asy: couldn't get ttymodes property!"); 15847c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 15857c478bd9Sstevel@tonic-gate 15867c478bd9Sstevel@tonic-gate /* eeprom mode support - respect properties */ 15877c478bd9Sstevel@tonic-gate if (asy->asy_cflag) 15887c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag = asy->asy_cflag; 15897c478bd9Sstevel@tonic-gate 15907c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iflag = 0; 15917c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 15927c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_row = 0; 15937c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_col = 0; 15947c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_xpixel = 0; 15957c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_ypixel = 0; 15967c478bd9Sstevel@tonic-gate async->async_dev = *dev; 15977c478bd9Sstevel@tonic-gate async->async_wbufcid = 0; 15987c478bd9Sstevel@tonic-gate 15997c478bd9Sstevel@tonic-gate async->async_startc = CSTART; 16007c478bd9Sstevel@tonic-gate async->async_stopc = CSTOP; 16017c478bd9Sstevel@tonic-gate asy_program(asy, ASY_INIT); 16022df1fe9cSrandyf } else 16032df1fe9cSrandyf if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 16047c478bd9Sstevel@tonic-gate secpolicy_excl_open(cr) != 0) { 16057c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 16067c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16077c478bd9Sstevel@tonic-gate return (EBUSY); 16087c478bd9Sstevel@tonic-gate } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 16097c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 16107c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16117c478bd9Sstevel@tonic-gate return (EBUSY); 16127c478bd9Sstevel@tonic-gate } 16137c478bd9Sstevel@tonic-gate 16147c478bd9Sstevel@tonic-gate if (*dev & OUTLINE) 16157c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_OUT; 16167c478bd9Sstevel@tonic-gate 16177c478bd9Sstevel@tonic-gate /* Raise DTR on every open, but delay if it was just lowered. */ 16187c478bd9Sstevel@tonic-gate while (async->async_flags & ASYNC_DTR_DELAY) { 16197c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 16207c478bd9Sstevel@tonic-gate "asy%dopen: waiting for the ASYNC_DTR_DELAY to be clear\n", 16217c478bd9Sstevel@tonic-gate unit); 16227c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 16237c478bd9Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, 16247c478bd9Sstevel@tonic-gate &asy->asy_excl) == 0) { 16257c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 16267c478bd9Sstevel@tonic-gate "asy%dopen: interrupted by signal, exiting\n", 16277c478bd9Sstevel@tonic-gate unit); 16287c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16297c478bd9Sstevel@tonic-gate return (EINTR); 16307c478bd9Sstevel@tonic-gate } 16317c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 16327c478bd9Sstevel@tonic-gate } 16337c478bd9Sstevel@tonic-gate 16344ab75253Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 16354ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 16367c478bd9Sstevel@tonic-gate mcr|(asy->asy_mcr&DTR)); 16377c478bd9Sstevel@tonic-gate 16387c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT, 16397c478bd9Sstevel@tonic-gate "asy%dopen: \"Raise DTR on every open\": make mcr = %x, " 16407c478bd9Sstevel@tonic-gate "make TS_SOFTCAR = %s\n", 16417c478bd9Sstevel@tonic-gate unit, mcr|(asy->asy_mcr&DTR), 16427c478bd9Sstevel@tonic-gate (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF"); 16432df1fe9cSrandyf 16447c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_IGNORE_CD) { 16457c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 16467c478bd9Sstevel@tonic-gate "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n", 16477c478bd9Sstevel@tonic-gate unit); 16487c478bd9Sstevel@tonic-gate async->async_ttycommon.t_flags |= TS_SOFTCAR; 16497c478bd9Sstevel@tonic-gate } 16507c478bd9Sstevel@tonic-gate else 16517c478bd9Sstevel@tonic-gate async->async_ttycommon.t_flags &= ~TS_SOFTCAR; 16527c478bd9Sstevel@tonic-gate 16537c478bd9Sstevel@tonic-gate /* 16547c478bd9Sstevel@tonic-gate * Check carrier. 16557c478bd9Sstevel@tonic-gate */ 16564ab75253Smrj asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 16577c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, " 16587c478bd9Sstevel@tonic-gate "MSR & DCD is %s\n", 16597c478bd9Sstevel@tonic-gate unit, 16607c478bd9Sstevel@tonic-gate (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear", 16617c478bd9Sstevel@tonic-gate (asy->asy_msr & DCD) ? "set" : "clear"); 16622df1fe9cSrandyf 16637c478bd9Sstevel@tonic-gate if (asy->asy_msr & DCD) 16647c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON; 16657c478bd9Sstevel@tonic-gate else 16667c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON; 16677c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate /* 16707c478bd9Sstevel@tonic-gate * If FNDELAY and FNONBLOCK are clear, block until carrier up. 16717c478bd9Sstevel@tonic-gate * Quit on interrupt. 16727c478bd9Sstevel@tonic-gate */ 16737c478bd9Sstevel@tonic-gate if (!(flag & (FNDELAY|FNONBLOCK)) && 16747c478bd9Sstevel@tonic-gate !(async->async_ttycommon.t_cflag & CLOCAL)) { 16757c478bd9Sstevel@tonic-gate if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) && 16767c478bd9Sstevel@tonic-gate !(async->async_ttycommon.t_flags & TS_SOFTCAR)) || 16777c478bd9Sstevel@tonic-gate ((async->async_flags & ASYNC_OUT) && 16787c478bd9Sstevel@tonic-gate !(*dev & OUTLINE))) { 16797c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_WOPEN; 16807c478bd9Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, 16817c478bd9Sstevel@tonic-gate &asy->asy_excl) == B_FALSE) { 16827c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN; 16837c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16847c478bd9Sstevel@tonic-gate return (EINTR); 16857c478bd9Sstevel@tonic-gate } 16867c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN; 16877c478bd9Sstevel@tonic-gate goto again; 16887c478bd9Sstevel@tonic-gate } 16897c478bd9Sstevel@tonic-gate } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 16907c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16917c478bd9Sstevel@tonic-gate return (EBUSY); 16927c478bd9Sstevel@tonic-gate } 16937c478bd9Sstevel@tonic-gate 16947c478bd9Sstevel@tonic-gate async->async_ttycommon.t_readq = rq; 16957c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq = WR(rq); 16967c478bd9Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 16977c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 16987c478bd9Sstevel@tonic-gate /* 16997c478bd9Sstevel@tonic-gate * Caution here -- qprocson sets the pointers that are used by canput 17007c478bd9Sstevel@tonic-gate * called by async_softint. ASYNC_ISOPEN must *not* be set until those 17017c478bd9Sstevel@tonic-gate * pointers are valid. 17027c478bd9Sstevel@tonic-gate */ 17037c478bd9Sstevel@tonic-gate qprocson(rq); 17047c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_ISOPEN; 17057c478bd9Sstevel@tonic-gate async->async_polltid = 0; 17067c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dopen: done\n", unit); 17077c478bd9Sstevel@tonic-gate return (0); 17087c478bd9Sstevel@tonic-gate } 17097c478bd9Sstevel@tonic-gate 17107c478bd9Sstevel@tonic-gate static void 17117c478bd9Sstevel@tonic-gate async_progress_check(void *arg) 17127c478bd9Sstevel@tonic-gate { 17137c478bd9Sstevel@tonic-gate struct asyncline *async = arg; 17147c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 17157c478bd9Sstevel@tonic-gate mblk_t *bp; 17167c478bd9Sstevel@tonic-gate 17177c478bd9Sstevel@tonic-gate /* 17187c478bd9Sstevel@tonic-gate * We define "progress" as either waiting on a timed break or delay, or 17197c478bd9Sstevel@tonic-gate * having had at least one transmitter interrupt. If none of these are 17207c478bd9Sstevel@tonic-gate * true, then just terminate the output and wake up that close thread. 17217c478bd9Sstevel@tonic-gate */ 17227c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 17237c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 17247c478bd9Sstevel@tonic-gate if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 17257c478bd9Sstevel@tonic-gate async->async_ocnt = 0; 17267c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 17277c478bd9Sstevel@tonic-gate async->async_timer = 0; 17287c478bd9Sstevel@tonic-gate bp = async->async_xmitblk; 17297c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 17307c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 17317c478bd9Sstevel@tonic-gate if (bp != NULL) 17327c478bd9Sstevel@tonic-gate freeb(bp); 17337c478bd9Sstevel@tonic-gate /* 17347c478bd9Sstevel@tonic-gate * Since this timer is running, we know that we're in exit(2). 17357c478bd9Sstevel@tonic-gate * That means that the user can't possibly be waiting on any 17367c478bd9Sstevel@tonic-gate * valid ioctl(2) completion anymore, and we should just flush 17377c478bd9Sstevel@tonic-gate * everything. 17387c478bd9Sstevel@tonic-gate */ 17397c478bd9Sstevel@tonic-gate flushq(async->async_ttycommon.t_writeq, FLUSHALL); 17407c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 17417c478bd9Sstevel@tonic-gate } else { 17427c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS; 17437c478bd9Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async, 17447c478bd9Sstevel@tonic-gate drv_usectohz(asy_drain_check)); 17457c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 17467c478bd9Sstevel@tonic-gate } 17477c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 17487c478bd9Sstevel@tonic-gate } 17497c478bd9Sstevel@tonic-gate 17507c478bd9Sstevel@tonic-gate /* 17517c478bd9Sstevel@tonic-gate * Release DTR so that asyopen() can raise it. 17527c478bd9Sstevel@tonic-gate */ 17537c478bd9Sstevel@tonic-gate static void 17547c478bd9Sstevel@tonic-gate async_dtr_free(struct asyncline *async) 17557c478bd9Sstevel@tonic-gate { 17567c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 17577c478bd9Sstevel@tonic-gate 17587c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, 17597c478bd9Sstevel@tonic-gate "async_dtr_free, clearing ASYNC_DTR_DELAY\n"); 17607c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 17617c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_DTR_DELAY; 17627c478bd9Sstevel@tonic-gate async->async_dtrtid = 0; 17637c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 17647c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 17657c478bd9Sstevel@tonic-gate } 17667c478bd9Sstevel@tonic-gate 17677c478bd9Sstevel@tonic-gate /* 17687c478bd9Sstevel@tonic-gate * Close routine. 17697c478bd9Sstevel@tonic-gate */ 17707c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 17717c478bd9Sstevel@tonic-gate static int 17727c478bd9Sstevel@tonic-gate asyclose(queue_t *q, int flag, cred_t *credp) 17737c478bd9Sstevel@tonic-gate { 17747c478bd9Sstevel@tonic-gate struct asyncline *async; 17757c478bd9Sstevel@tonic-gate struct asycom *asy; 17767c478bd9Sstevel@tonic-gate int icr, lcr; 17777c478bd9Sstevel@tonic-gate #ifdef DEBUG 17787c478bd9Sstevel@tonic-gate int instance; 17797c478bd9Sstevel@tonic-gate #endif 17807c478bd9Sstevel@tonic-gate 17817c478bd9Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 17827c478bd9Sstevel@tonic-gate ASSERT(async != NULL); 17837c478bd9Sstevel@tonic-gate #ifdef DEBUG 17847c478bd9Sstevel@tonic-gate instance = UNIT(async->async_dev); 17857c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose\n", instance); 17867c478bd9Sstevel@tonic-gate #endif 17877c478bd9Sstevel@tonic-gate asy = async->async_common; 17887c478bd9Sstevel@tonic-gate 17897c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 17907c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CLOSING; 17917c478bd9Sstevel@tonic-gate 17927c478bd9Sstevel@tonic-gate /* 17937c478bd9Sstevel@tonic-gate * Turn off PPS handling early to avoid events occuring during 17947c478bd9Sstevel@tonic-gate * close. Also reset the DCD edge monitoring bit. 17957c478bd9Sstevel@tonic-gate */ 17967c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 17977c478bd9Sstevel@tonic-gate asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 17987c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 17997c478bd9Sstevel@tonic-gate 18007c478bd9Sstevel@tonic-gate /* 18017c478bd9Sstevel@tonic-gate * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 18027c478bd9Sstevel@tonic-gate * untimed (TIOCSBRK). For the timed case, these are enqueued on our 18037c478bd9Sstevel@tonic-gate * write queue and there's a timer running, so we don't have to worry 18047c478bd9Sstevel@tonic-gate * about them. For the untimed case, though, the user obviously made a 18057c478bd9Sstevel@tonic-gate * mistake, because these are handled immediately. We'll terminate the 18067c478bd9Sstevel@tonic-gate * break now and honor his implicit request by discarding the rest of 18077c478bd9Sstevel@tonic-gate * the data. 18087c478bd9Sstevel@tonic-gate */ 18097c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) { 18107c478bd9Sstevel@tonic-gate if (async->async_utbrktid != 0) { 18117c478bd9Sstevel@tonic-gate (void) untimeout(async->async_utbrktid); 18127c478bd9Sstevel@tonic-gate async->async_utbrktid = 0; 18137c478bd9Sstevel@tonic-gate } 18147c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 18154ab75253Smrj lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 18164ab75253Smrj ddi_put8(asy->asy_iohandle, 18177c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR, (lcr & ~SETBREAK)); 18187c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 18197c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND; 18207c478bd9Sstevel@tonic-gate goto nodrain; 18217c478bd9Sstevel@tonic-gate } 18227c478bd9Sstevel@tonic-gate 18237c478bd9Sstevel@tonic-gate /* 18247c478bd9Sstevel@tonic-gate * If the user told us not to delay the close ("non-blocking"), then 18257c478bd9Sstevel@tonic-gate * don't bother trying to drain. 18267c478bd9Sstevel@tonic-gate * 18277c478bd9Sstevel@tonic-gate * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 18287c478bd9Sstevel@tonic-gate * getting an M_START (since these messages aren't enqueued), and the 18297c478bd9Sstevel@tonic-gate * only other way to clear the stop condition is by loss of DCD, which 18307c478bd9Sstevel@tonic-gate * would discard the queue data. Thus, we drop the output data if 18317c478bd9Sstevel@tonic-gate * ASYNC_STOPPED is set. 18327c478bd9Sstevel@tonic-gate */ 18337c478bd9Sstevel@tonic-gate if ((flag & (FNDELAY|FNONBLOCK)) || 18347c478bd9Sstevel@tonic-gate (async->async_flags & ASYNC_STOPPED)) { 18357c478bd9Sstevel@tonic-gate goto nodrain; 18367c478bd9Sstevel@tonic-gate } 18377c478bd9Sstevel@tonic-gate 18387c478bd9Sstevel@tonic-gate /* 18397c478bd9Sstevel@tonic-gate * If there's any pending output, then we have to try to drain it. 18407c478bd9Sstevel@tonic-gate * There are two main cases to be handled: 18417c478bd9Sstevel@tonic-gate * - called by close(2): need to drain until done or until 18427c478bd9Sstevel@tonic-gate * a signal is received. No timeout. 18437c478bd9Sstevel@tonic-gate * - called by exit(2): need to drain while making progress 18447c478bd9Sstevel@tonic-gate * or until a timeout occurs. No signals. 18457c478bd9Sstevel@tonic-gate * 18467c478bd9Sstevel@tonic-gate * If we can't rely on receiving a signal to get us out of a hung 18477c478bd9Sstevel@tonic-gate * session, then we have to use a timer. In this case, we set a timer 18487c478bd9Sstevel@tonic-gate * to check for progress in sending the output data -- all that we ask 18497c478bd9Sstevel@tonic-gate * (at each interval) is that there's been some progress made. Since 18507c478bd9Sstevel@tonic-gate * the interrupt routine grabs buffers from the write queue, we can't 18517c478bd9Sstevel@tonic-gate * trust changes in async_ocnt. Instead, we use a progress flag. 18527c478bd9Sstevel@tonic-gate * 18537c478bd9Sstevel@tonic-gate * Note that loss of carrier will cause the output queue to be flushed, 18547c478bd9Sstevel@tonic-gate * and we'll wake up again and finish normally. 18557c478bd9Sstevel@tonic-gate */ 18567c478bd9Sstevel@tonic-gate if (!ddi_can_receive_sig() && asy_drain_check != 0) { 18577c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS; 18587c478bd9Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async, 18597c478bd9Sstevel@tonic-gate drv_usectohz(asy_drain_check)); 18607c478bd9Sstevel@tonic-gate } 18617c478bd9Sstevel@tonic-gate while (async->async_ocnt > 0 || 18627c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq->q_first != NULL || 18637c478bd9Sstevel@tonic-gate (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 18647c478bd9Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0) 18657c478bd9Sstevel@tonic-gate break; 18667c478bd9Sstevel@tonic-gate } 18677c478bd9Sstevel@tonic-gate if (async->async_timer != 0) { 18687c478bd9Sstevel@tonic-gate (void) untimeout(async->async_timer); 18697c478bd9Sstevel@tonic-gate async->async_timer = 0; 18707c478bd9Sstevel@tonic-gate } 18717c478bd9Sstevel@tonic-gate 18727c478bd9Sstevel@tonic-gate nodrain: 18737c478bd9Sstevel@tonic-gate async->async_ocnt = 0; 18747c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL) 18757c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk); 18767c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 18777c478bd9Sstevel@tonic-gate 18787c478bd9Sstevel@tonic-gate /* 18797c478bd9Sstevel@tonic-gate * If line has HUPCL set or is incompletely opened fix up the modem 18807c478bd9Sstevel@tonic-gate * lines. 18817c478bd9Sstevel@tonic-gate */ 18822df1fe9cSrandyf DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: next check HUPCL flag\n", 18832df1fe9cSrandyf instance); 18847c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 18857c478bd9Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & HUPCL) || 18867c478bd9Sstevel@tonic-gate (async->async_flags & ASYNC_WOPEN)) { 18877c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM, 18887c478bd9Sstevel@tonic-gate "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n", 18897c478bd9Sstevel@tonic-gate instance, 18907c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag & HUPCL, 18917c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag & ASYNC_WOPEN); 18927c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_DTR_DELAY; 18937c478bd9Sstevel@tonic-gate 18947c478bd9Sstevel@tonic-gate /* turn off DTR, RTS but NOT interrupt to 386 */ 18957c478bd9Sstevel@tonic-gate if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) { 18967c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM, 18977c478bd9Sstevel@tonic-gate "asy%dclose: ASY_IGNORE_CD flag = %x, " 18987c478bd9Sstevel@tonic-gate "ASY_RTS_DTR_OFF flag = %x\n", 18997c478bd9Sstevel@tonic-gate instance, 19007c478bd9Sstevel@tonic-gate asy->asy_flags & ASY_IGNORE_CD, 19017c478bd9Sstevel@tonic-gate asy->asy_flags & ASY_RTS_DTR_OFF); 19022df1fe9cSrandyf 19032df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 19042df1fe9cSrandyf asy->asy_mcr|OUT2); 19057c478bd9Sstevel@tonic-gate } else { 19067c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 19077c478bd9Sstevel@tonic-gate "asy%dclose: Dropping DTR and RTS\n", instance); 19082df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 19092df1fe9cSrandyf OUT2); 19107c478bd9Sstevel@tonic-gate } 19117c478bd9Sstevel@tonic-gate async->async_dtrtid = 19127c478bd9Sstevel@tonic-gate timeout((void (*)())async_dtr_free, 19137c478bd9Sstevel@tonic-gate (caddr_t)async, drv_usectohz(asy_min_dtr_low)); 19147c478bd9Sstevel@tonic-gate } 19157c478bd9Sstevel@tonic-gate /* 19167c478bd9Sstevel@tonic-gate * If nobody's using it now, turn off receiver interrupts. 19177c478bd9Sstevel@tonic-gate */ 19187c478bd9Sstevel@tonic-gate if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { 19192df1fe9cSrandyf icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR); 19204ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 19217c478bd9Sstevel@tonic-gate (icr & ~RIEN)); 19227c478bd9Sstevel@tonic-gate } 19237c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 19247c478bd9Sstevel@tonic-gate out: 19257c478bd9Sstevel@tonic-gate ttycommon_close(&async->async_ttycommon); 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate /* 19287c478bd9Sstevel@tonic-gate * Cancel outstanding "bufcall" request. 19297c478bd9Sstevel@tonic-gate */ 19307c478bd9Sstevel@tonic-gate if (async->async_wbufcid != 0) { 19317c478bd9Sstevel@tonic-gate unbufcall(async->async_wbufcid); 19327c478bd9Sstevel@tonic-gate async->async_wbufcid = 0; 19337c478bd9Sstevel@tonic-gate } 19347c478bd9Sstevel@tonic-gate 19357c478bd9Sstevel@tonic-gate /* Note that qprocsoff can't be done until after interrupts are off */ 19367c478bd9Sstevel@tonic-gate qprocsoff(q); 19377c478bd9Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = NULL; 19387c478bd9Sstevel@tonic-gate async->async_ttycommon.t_readq = NULL; 19397c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq = NULL; 19407c478bd9Sstevel@tonic-gate 19417c478bd9Sstevel@tonic-gate /* 19427c478bd9Sstevel@tonic-gate * Clear out device state, except persistant device property flags. 19437c478bd9Sstevel@tonic-gate */ 19447c478bd9Sstevel@tonic-gate async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF); 19457c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 19467c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 19477c478bd9Sstevel@tonic-gate 19487c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose: done\n", instance); 19497c478bd9Sstevel@tonic-gate return (0); 19507c478bd9Sstevel@tonic-gate } 19517c478bd9Sstevel@tonic-gate 19527c478bd9Sstevel@tonic-gate static boolean_t 19537c478bd9Sstevel@tonic-gate asy_isbusy(struct asycom *asy) 19547c478bd9Sstevel@tonic-gate { 19557c478bd9Sstevel@tonic-gate struct asyncline *async; 19567c478bd9Sstevel@tonic-gate 19577c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_isbusy\n"); 19587c478bd9Sstevel@tonic-gate async = asy->asy_priv; 19597c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 19607c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 19612df1fe9cSrandyf /* 19622df1fe9cSrandyf * XXXX this should be recoded 19632df1fe9cSrandyf */ 19647c478bd9Sstevel@tonic-gate return ((async->async_ocnt > 0) || 19654ab75253Smrj ((ddi_get8(asy->asy_iohandle, 19667c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0)); 19677c478bd9Sstevel@tonic-gate } 19687c478bd9Sstevel@tonic-gate 19697c478bd9Sstevel@tonic-gate static void 19707c478bd9Sstevel@tonic-gate asy_waiteot(struct asycom *asy) 19717c478bd9Sstevel@tonic-gate { 19727c478bd9Sstevel@tonic-gate /* 19737c478bd9Sstevel@tonic-gate * Wait for the current transmission block and the 19747c478bd9Sstevel@tonic-gate * current fifo data to transmit. Once this is done 19757c478bd9Sstevel@tonic-gate * we may go on. 19767c478bd9Sstevel@tonic-gate */ 19777c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_waiteot\n"); 19787c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 19797c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 19807c478bd9Sstevel@tonic-gate while (asy_isbusy(asy)) { 19817c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 19827c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 19837c478bd9Sstevel@tonic-gate drv_usecwait(10000); /* wait .01 */ 19847c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 19857c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 19867c478bd9Sstevel@tonic-gate } 19877c478bd9Sstevel@tonic-gate } 19887c478bd9Sstevel@tonic-gate 19897c478bd9Sstevel@tonic-gate /* asy_reset_fifo -- flush fifos and [re]program fifo control register */ 19907c478bd9Sstevel@tonic-gate static void 19917c478bd9Sstevel@tonic-gate asy_reset_fifo(struct asycom *asy, uchar_t flush) 19927c478bd9Sstevel@tonic-gate { 19937c478bd9Sstevel@tonic-gate uchar_t lcr; 19947c478bd9Sstevel@tonic-gate 19957c478bd9Sstevel@tonic-gate /* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */ 19967c478bd9Sstevel@tonic-gate 19977c478bd9Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) { 19984ab75253Smrj lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 19994ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 20007c478bd9Sstevel@tonic-gate lcr | DLAB); 20017c478bd9Sstevel@tonic-gate } 20027c478bd9Sstevel@tonic-gate 20034ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 20047c478bd9Sstevel@tonic-gate asy->asy_fifor | flush); 20057c478bd9Sstevel@tonic-gate 20067c478bd9Sstevel@tonic-gate /* Clear DLAB */ 20077c478bd9Sstevel@tonic-gate 20087c478bd9Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) { 20094ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr); 20107c478bd9Sstevel@tonic-gate } 20117c478bd9Sstevel@tonic-gate } 20127c478bd9Sstevel@tonic-gate 20137c478bd9Sstevel@tonic-gate /* 20147c478bd9Sstevel@tonic-gate * Program the ASY port. Most of the async operation is based on the values 20157c478bd9Sstevel@tonic-gate * of 'c_iflag' and 'c_cflag'. 20167c478bd9Sstevel@tonic-gate */ 20177c478bd9Sstevel@tonic-gate 20187c478bd9Sstevel@tonic-gate #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \ 20197c478bd9Sstevel@tonic-gate (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD)) 20207c478bd9Sstevel@tonic-gate 20217c478bd9Sstevel@tonic-gate static void 20227c478bd9Sstevel@tonic-gate asy_program(struct asycom *asy, int mode) 20237c478bd9Sstevel@tonic-gate { 20247c478bd9Sstevel@tonic-gate struct asyncline *async; 20257c478bd9Sstevel@tonic-gate int baudrate, c_flag; 20267c478bd9Sstevel@tonic-gate int icr, lcr; 20277c478bd9Sstevel@tonic-gate int flush_reg; 20287c478bd9Sstevel@tonic-gate int ocflags; 20297c478bd9Sstevel@tonic-gate #ifdef DEBUG 20307c478bd9Sstevel@tonic-gate int instance; 20317c478bd9Sstevel@tonic-gate #endif 20327c478bd9Sstevel@tonic-gate 20337c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 20347c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 20357c478bd9Sstevel@tonic-gate 20367c478bd9Sstevel@tonic-gate async = asy->asy_priv; 20377c478bd9Sstevel@tonic-gate #ifdef DEBUG 20387c478bd9Sstevel@tonic-gate instance = UNIT(async->async_dev); 20397c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_PROCS, 20407c478bd9Sstevel@tonic-gate "asy%d_program: mode = 0x%08X, enter\n", instance, mode); 20417c478bd9Sstevel@tonic-gate #endif 20427c478bd9Sstevel@tonic-gate 20437c478bd9Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 20447c478bd9Sstevel@tonic-gate 20457c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~(CIBAUD); 20467c478bd9Sstevel@tonic-gate 20477c478bd9Sstevel@tonic-gate if (baudrate > CBAUD) { 20487c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= CIBAUDEXT; 20497c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= 20507c478bd9Sstevel@tonic-gate (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); 20517c478bd9Sstevel@tonic-gate } else { 20527c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~CIBAUDEXT; 20537c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= 20547c478bd9Sstevel@tonic-gate ((baudrate << IBSHIFT) & CIBAUD); 20557c478bd9Sstevel@tonic-gate } 20567c478bd9Sstevel@tonic-gate 20577c478bd9Sstevel@tonic-gate c_flag = async->async_ttycommon.t_cflag & 20587c478bd9Sstevel@tonic-gate (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT); 20597c478bd9Sstevel@tonic-gate 20607c478bd9Sstevel@tonic-gate /* disable interrupts */ 20614ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 20627c478bd9Sstevel@tonic-gate 20637c478bd9Sstevel@tonic-gate ocflags = asy->asy_ocflag; 20647c478bd9Sstevel@tonic-gate 20657c478bd9Sstevel@tonic-gate /* flush/reset the status registers */ 20664ab75253Smrj (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR); 20674ab75253Smrj (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 20684ab75253Smrj asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle, 20697c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MSR); 20707c478bd9Sstevel@tonic-gate /* 20717c478bd9Sstevel@tonic-gate * The device is programmed in the open sequence, if we 20727c478bd9Sstevel@tonic-gate * have to hardware handshake, then this is a good time 20737c478bd9Sstevel@tonic-gate * to check if the device can receive any data. 20747c478bd9Sstevel@tonic-gate */ 20757c478bd9Sstevel@tonic-gate 20767c478bd9Sstevel@tonic-gate if ((CRTSCTS & async->async_ttycommon.t_cflag) && !(flush_reg & CTS)) { 20777c478bd9Sstevel@tonic-gate async_flowcontrol_hw_output(asy, FLOW_STOP); 20787c478bd9Sstevel@tonic-gate } else { 20797c478bd9Sstevel@tonic-gate /* 20807c478bd9Sstevel@tonic-gate * We can not use async_flowcontrol_hw_output(asy, FLOW_START) 20817c478bd9Sstevel@tonic-gate * here, because if CRTSCTS is clear, we need clear 20827c478bd9Sstevel@tonic-gate * ASYNC_HW_OUT_FLW bit. 20837c478bd9Sstevel@tonic-gate */ 20847c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW; 20857c478bd9Sstevel@tonic-gate } 20867c478bd9Sstevel@tonic-gate 20877c478bd9Sstevel@tonic-gate /* 20887c478bd9Sstevel@tonic-gate * If IXON is not set, clear ASYNC_SW_OUT_FLW; 20897c478bd9Sstevel@tonic-gate * If IXON is set, no matter what IXON flag is before this 20907c478bd9Sstevel@tonic-gate * function call to asy_program, 20917c478bd9Sstevel@tonic-gate * we will use the old ASYNC_SW_OUT_FLW status. 20927c478bd9Sstevel@tonic-gate * Because of handling IXON in the driver, we also should re-calculate 20937c478bd9Sstevel@tonic-gate * the value of ASYNC_OUT_FLW_RESUME bit, but in fact, 20947c478bd9Sstevel@tonic-gate * the TCSET* commands which call asy_program 20957c478bd9Sstevel@tonic-gate * are put into the write queue, so there is no output needed to 20967c478bd9Sstevel@tonic-gate * be resumed at this point. 20977c478bd9Sstevel@tonic-gate */ 20987c478bd9Sstevel@tonic-gate if (!(IXON & async->async_ttycommon.t_iflag)) 20997c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW; 21007c478bd9Sstevel@tonic-gate 21017c478bd9Sstevel@tonic-gate /* manually flush receive buffer or fifo (workaround for buggy fifos) */ 21027c478bd9Sstevel@tonic-gate if (mode == ASY_INIT) 21037c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 21047c478bd9Sstevel@tonic-gate for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) { 21054ab75253Smrj (void) ddi_get8(asy->asy_iohandle, 21067c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT); 21077c478bd9Sstevel@tonic-gate } 21087c478bd9Sstevel@tonic-gate } else { 21094ab75253Smrj flush_reg = ddi_get8(asy->asy_iohandle, 21107c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT); 21117c478bd9Sstevel@tonic-gate } 21127c478bd9Sstevel@tonic-gate 21137c478bd9Sstevel@tonic-gate if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 21147c478bd9Sstevel@tonic-gate /* Set line control */ 21152df1fe9cSrandyf lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 21167c478bd9Sstevel@tonic-gate lcr &= ~(WLS0|WLS1|STB|PEN|EPS); 21177c478bd9Sstevel@tonic-gate 21187c478bd9Sstevel@tonic-gate if (c_flag & CSTOPB) 21197c478bd9Sstevel@tonic-gate lcr |= STB; /* 2 stop bits */ 21207c478bd9Sstevel@tonic-gate 21217c478bd9Sstevel@tonic-gate if (c_flag & PARENB) 21227c478bd9Sstevel@tonic-gate lcr |= PEN; 21237c478bd9Sstevel@tonic-gate 21247c478bd9Sstevel@tonic-gate if ((c_flag & PARODD) == 0) 21257c478bd9Sstevel@tonic-gate lcr |= EPS; 21267c478bd9Sstevel@tonic-gate 21277c478bd9Sstevel@tonic-gate switch (c_flag & CSIZE) { 21287c478bd9Sstevel@tonic-gate case CS5: 21297c478bd9Sstevel@tonic-gate lcr |= BITS5; 21307c478bd9Sstevel@tonic-gate break; 21317c478bd9Sstevel@tonic-gate case CS6: 21327c478bd9Sstevel@tonic-gate lcr |= BITS6; 21337c478bd9Sstevel@tonic-gate break; 21347c478bd9Sstevel@tonic-gate case CS7: 21357c478bd9Sstevel@tonic-gate lcr |= BITS7; 21367c478bd9Sstevel@tonic-gate break; 21377c478bd9Sstevel@tonic-gate case CS8: 21387c478bd9Sstevel@tonic-gate lcr |= BITS8; 21397c478bd9Sstevel@tonic-gate break; 21407c478bd9Sstevel@tonic-gate } 21417c478bd9Sstevel@tonic-gate 21427c478bd9Sstevel@tonic-gate /* set the baud rate, unless it is "0" */ 21432df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB); 21442df1fe9cSrandyf 21457c478bd9Sstevel@tonic-gate if (baudrate != 0) { 21464ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 21477c478bd9Sstevel@tonic-gate asyspdtab[baudrate] & 0xff); 21484ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 21497c478bd9Sstevel@tonic-gate (asyspdtab[baudrate] >> 8) & 0xff); 21507c478bd9Sstevel@tonic-gate } 21517c478bd9Sstevel@tonic-gate /* set the line control modes */ 21524ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr); 21537c478bd9Sstevel@tonic-gate 21547c478bd9Sstevel@tonic-gate /* 21557c478bd9Sstevel@tonic-gate * If we have a FIFO buffer, enable/flush 21567c478bd9Sstevel@tonic-gate * at intialize time, flush if transitioning from 21577c478bd9Sstevel@tonic-gate * CREAD off to CREAD on. 21587c478bd9Sstevel@tonic-gate */ 21597c478bd9Sstevel@tonic-gate if ((ocflags & CREAD) == 0 && (c_flag & CREAD) || 21607c478bd9Sstevel@tonic-gate mode == ASY_INIT) 21617c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) 21627c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFORXFLSH); 21637c478bd9Sstevel@tonic-gate 21647c478bd9Sstevel@tonic-gate /* remember the new cflags */ 21657c478bd9Sstevel@tonic-gate asy->asy_ocflag = c_flag & ~CLOCAL; 21667c478bd9Sstevel@tonic-gate } 21677c478bd9Sstevel@tonic-gate 21687c478bd9Sstevel@tonic-gate if (baudrate == 0) 21694ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 21707c478bd9Sstevel@tonic-gate (asy->asy_mcr & RTS) | OUT2); 21717c478bd9Sstevel@tonic-gate else 21724ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, 21737c478bd9Sstevel@tonic-gate asy->asy_mcr | OUT2); 21747c478bd9Sstevel@tonic-gate 21757c478bd9Sstevel@tonic-gate /* 21767c478bd9Sstevel@tonic-gate * Call the modem status interrupt handler to check for the carrier 21777c478bd9Sstevel@tonic-gate * in case CLOCAL was turned off after the carrier came on. 21787c478bd9Sstevel@tonic-gate * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 21797c478bd9Sstevel@tonic-gate */ 21807c478bd9Sstevel@tonic-gate async_msint(asy); 21817c478bd9Sstevel@tonic-gate 21827c478bd9Sstevel@tonic-gate /* Set interrupt control */ 21837c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2, 21847c478bd9Sstevel@tonic-gate "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n", 21852df1fe9cSrandyf instance, c_flag & CLOCAL, 21867c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag & CRTSCTS); 21872df1fe9cSrandyf 21887c478bd9Sstevel@tonic-gate if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) 21897c478bd9Sstevel@tonic-gate /* 21907c478bd9Sstevel@tonic-gate * direct-wired line ignores DCD, so we don't enable modem 21917c478bd9Sstevel@tonic-gate * status interrupts. 21927c478bd9Sstevel@tonic-gate */ 21937c478bd9Sstevel@tonic-gate icr = (TIEN | SIEN); 21947c478bd9Sstevel@tonic-gate else 21957c478bd9Sstevel@tonic-gate icr = (TIEN | SIEN | MIEN); 21967c478bd9Sstevel@tonic-gate 21977c478bd9Sstevel@tonic-gate if (c_flag & CREAD) 21987c478bd9Sstevel@tonic-gate icr |= RIEN; 21997c478bd9Sstevel@tonic-gate 22004ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, icr); 22017c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "asy%d_program: done\n", instance); 22027c478bd9Sstevel@tonic-gate } 22037c478bd9Sstevel@tonic-gate 22047c478bd9Sstevel@tonic-gate static boolean_t 22057c478bd9Sstevel@tonic-gate asy_baudok(struct asycom *asy) 22067c478bd9Sstevel@tonic-gate { 22077c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 22087c478bd9Sstevel@tonic-gate int baudrate; 22097c478bd9Sstevel@tonic-gate 22107c478bd9Sstevel@tonic-gate 22117c478bd9Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 22127c478bd9Sstevel@tonic-gate 22137c478bd9Sstevel@tonic-gate if (baudrate >= sizeof (asyspdtab)/sizeof (*asyspdtab)) 22147c478bd9Sstevel@tonic-gate return (0); 22157c478bd9Sstevel@tonic-gate 22167c478bd9Sstevel@tonic-gate return (baudrate == 0 || asyspdtab[baudrate]); 22177c478bd9Sstevel@tonic-gate } 22187c478bd9Sstevel@tonic-gate 22197c478bd9Sstevel@tonic-gate /* 22207c478bd9Sstevel@tonic-gate * asyintr() is the High Level Interrupt Handler. 22217c478bd9Sstevel@tonic-gate * 22227c478bd9Sstevel@tonic-gate * There are four different interrupt types indexed by ISR register values: 22237c478bd9Sstevel@tonic-gate * 0: modem 22247c478bd9Sstevel@tonic-gate * 1: Tx holding register is empty, ready for next char 22257c478bd9Sstevel@tonic-gate * 2: Rx register now holds a char to be picked up 22267c478bd9Sstevel@tonic-gate * 3: error or break on line 22277c478bd9Sstevel@tonic-gate * This routine checks the Bit 0 (interrupt-not-pending) to determine if 22287c478bd9Sstevel@tonic-gate * the interrupt is from this port. 22297c478bd9Sstevel@tonic-gate */ 22307c478bd9Sstevel@tonic-gate uint_t 22317c478bd9Sstevel@tonic-gate asyintr(caddr_t argasy) 22327c478bd9Sstevel@tonic-gate { 22337c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)argasy; 22347c478bd9Sstevel@tonic-gate struct asyncline *async; 22357c478bd9Sstevel@tonic-gate int ret_status = DDI_INTR_UNCLAIMED; 22367c478bd9Sstevel@tonic-gate uchar_t interrupt_id, lsr; 22377c478bd9Sstevel@tonic-gate 22384ab75253Smrj interrupt_id = ddi_get8(asy->asy_iohandle, 22397c478bd9Sstevel@tonic-gate asy->asy_ioaddr + ISR) & 0x0F; 22407c478bd9Sstevel@tonic-gate async = asy->asy_priv; 22412df1fe9cSrandyf 2242cd43c54aSZach Kissel if ((async == NULL) || 22437c478bd9Sstevel@tonic-gate !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 22447c478bd9Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) 22457c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED); 22467c478bd9Sstevel@tonic-gate else { 22477c478bd9Sstevel@tonic-gate /* 22487c478bd9Sstevel@tonic-gate * reset the device by: 22497c478bd9Sstevel@tonic-gate * reading line status 22507c478bd9Sstevel@tonic-gate * reading any data from data status register 22517c478bd9Sstevel@tonic-gate * reading modem status 22527c478bd9Sstevel@tonic-gate */ 22534ab75253Smrj (void) ddi_get8(asy->asy_iohandle, 22547c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR); 22554ab75253Smrj (void) ddi_get8(asy->asy_iohandle, 22567c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT); 22574ab75253Smrj asy->asy_msr = ddi_get8(asy->asy_iohandle, 22587c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MSR); 22597c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 22607c478bd9Sstevel@tonic-gate } 22617c478bd9Sstevel@tonic-gate } 22622df1fe9cSrandyf 22637c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 226457ec6c03SZach Kissel 226557ec6c03SZach Kissel if (asy->asy_flags & ASY_DDI_SUSPENDED) { 226657ec6c03SZach Kissel mutex_exit(&asy->asy_excl_hi); 226757ec6c03SZach Kissel return (DDI_INTR_CLAIMED); 226857ec6c03SZach Kissel } 226957ec6c03SZach Kissel 22707c478bd9Sstevel@tonic-gate /* 22717c478bd9Sstevel@tonic-gate * We will loop until the interrupt line is pulled low. asy 22727c478bd9Sstevel@tonic-gate * interrupt is edge triggered. 22737c478bd9Sstevel@tonic-gate */ 22747c478bd9Sstevel@tonic-gate /* CSTYLED */ 22752df1fe9cSrandyf for (;; interrupt_id = 22762df1fe9cSrandyf (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) { 22772df1fe9cSrandyf 22787c478bd9Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) 22797c478bd9Sstevel@tonic-gate break; 22807c478bd9Sstevel@tonic-gate ret_status = DDI_INTR_CLAIMED; 22817c478bd9Sstevel@tonic-gate 22822df1fe9cSrandyf DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n", 22832df1fe9cSrandyf interrupt_id); 22842df1fe9cSrandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 22857c478bd9Sstevel@tonic-gate switch (interrupt_id) { 22867c478bd9Sstevel@tonic-gate case RxRDY: 22877c478bd9Sstevel@tonic-gate case RSTATUS: 22887c478bd9Sstevel@tonic-gate case FFTMOUT: 22897c478bd9Sstevel@tonic-gate /* receiver interrupt or receiver errors */ 22907c478bd9Sstevel@tonic-gate async_rxint(asy, lsr); 22917c478bd9Sstevel@tonic-gate break; 22927c478bd9Sstevel@tonic-gate case TxRDY: 22937c478bd9Sstevel@tonic-gate /* transmit interrupt */ 22947c478bd9Sstevel@tonic-gate async_txint(asy); 22957c478bd9Sstevel@tonic-gate continue; 22967c478bd9Sstevel@tonic-gate case MSTATUS: 22977c478bd9Sstevel@tonic-gate /* modem status interrupt */ 22987c478bd9Sstevel@tonic-gate async_msint(asy); 22997c478bd9Sstevel@tonic-gate break; 23007c478bd9Sstevel@tonic-gate } 23017c478bd9Sstevel@tonic-gate if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) && 23027c478bd9Sstevel@tonic-gate (async->async_ocnt > 0)) 23037c478bd9Sstevel@tonic-gate async_txint(asy); 23047c478bd9Sstevel@tonic-gate } 23057c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 23067c478bd9Sstevel@tonic-gate return (ret_status); 23077c478bd9Sstevel@tonic-gate } 23087c478bd9Sstevel@tonic-gate 23097c478bd9Sstevel@tonic-gate /* 23107c478bd9Sstevel@tonic-gate * Transmitter interrupt service routine. 23117c478bd9Sstevel@tonic-gate * If there is more data to transmit in the current pseudo-DMA block, 23127c478bd9Sstevel@tonic-gate * send the next character if output is not stopped or draining. 23137c478bd9Sstevel@tonic-gate * Otherwise, queue up a soft interrupt. 23147c478bd9Sstevel@tonic-gate * 23157c478bd9Sstevel@tonic-gate * XXX - Needs review for HW FIFOs. 23167c478bd9Sstevel@tonic-gate */ 23177c478bd9Sstevel@tonic-gate static void 23187c478bd9Sstevel@tonic-gate async_txint(struct asycom *asy) 23197c478bd9Sstevel@tonic-gate { 23207c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 23217c478bd9Sstevel@tonic-gate int fifo_len; 23227c478bd9Sstevel@tonic-gate 2323*395a1446SAndy Fiddaman ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2324*395a1446SAndy Fiddaman 23257c478bd9Sstevel@tonic-gate /* 23267c478bd9Sstevel@tonic-gate * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to 23277c478bd9Sstevel@tonic-gate * asyintr()'s context to claim the interrupt without performing 23287c478bd9Sstevel@tonic-gate * any action. No character will be loaded into FIFO/THR until 23297c478bd9Sstevel@tonic-gate * timed or untimed break is removed 23307c478bd9Sstevel@tonic-gate */ 23317c478bd9Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND)) 23327c478bd9Sstevel@tonic-gate return; 23337c478bd9Sstevel@tonic-gate 23347c478bd9Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 23357c478bd9Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 23367c478bd9Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 23377c478bd9Sstevel@tonic-gate 23387c478bd9Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 23397c478bd9Sstevel@tonic-gate fifo_len--; 23407c478bd9Sstevel@tonic-gate 23417c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0 && fifo_len > 0 && 23427c478bd9Sstevel@tonic-gate !(async->async_flags & 23437c478bd9Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) { 23447c478bd9Sstevel@tonic-gate while (fifo_len-- > 0 && async->async_ocnt-- > 0) { 23454ab75253Smrj ddi_put8(asy->asy_iohandle, 23467c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 23477c478bd9Sstevel@tonic-gate } 23487c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 23497c478bd9Sstevel@tonic-gate } 23507c478bd9Sstevel@tonic-gate 23517c478bd9Sstevel@tonic-gate if (fifo_len <= 0) 23527c478bd9Sstevel@tonic-gate return; 23537c478bd9Sstevel@tonic-gate 23547c478bd9Sstevel@tonic-gate ASYSETSOFT(asy); 23557c478bd9Sstevel@tonic-gate } 23567c478bd9Sstevel@tonic-gate 23577c478bd9Sstevel@tonic-gate /* 23587c478bd9Sstevel@tonic-gate * Interrupt on port: handle PPS event. This function is only called 23597c478bd9Sstevel@tonic-gate * for a port on which PPS event handling has been enabled. 23607c478bd9Sstevel@tonic-gate */ 23617c478bd9Sstevel@tonic-gate static void 23627c478bd9Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr) 23637c478bd9Sstevel@tonic-gate { 2364*395a1446SAndy Fiddaman ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2365*395a1446SAndy Fiddaman 23667c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS_EDGE) { 23677c478bd9Sstevel@tonic-gate /* Have seen leading edge, now look for and record drop */ 23687c478bd9Sstevel@tonic-gate if ((msr & DCD) == 0) 23697c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 23707c478bd9Sstevel@tonic-gate /* 23717c478bd9Sstevel@tonic-gate * Waiting for leading edge, look for rise; stamp event and 23727c478bd9Sstevel@tonic-gate * calibrate kernel clock. 23737c478bd9Sstevel@tonic-gate */ 23747c478bd9Sstevel@tonic-gate } else if (msr & DCD) { 23757c478bd9Sstevel@tonic-gate /* 23767c478bd9Sstevel@tonic-gate * This code captures a timestamp at the designated 23777c478bd9Sstevel@tonic-gate * transition of the PPS signal (DCD asserted). The 23787c478bd9Sstevel@tonic-gate * code provides a pointer to the timestamp, as well 23797c478bd9Sstevel@tonic-gate * as the hardware counter value at the capture. 23807c478bd9Sstevel@tonic-gate * 23817c478bd9Sstevel@tonic-gate * Note: the kernel has nano based time values while 23827c478bd9Sstevel@tonic-gate * NTP requires micro based, an in-line fast algorithm 23837c478bd9Sstevel@tonic-gate * to convert nsec to usec is used here -- see hrt2ts() 23847c478bd9Sstevel@tonic-gate * in common/os/timers.c for a full description. 23857c478bd9Sstevel@tonic-gate */ 23867c478bd9Sstevel@tonic-gate struct timeval *tvp = &asy_ppsev.tv; 23877c478bd9Sstevel@tonic-gate timestruc_t ts; 23887c478bd9Sstevel@tonic-gate long nsec, usec; 23897c478bd9Sstevel@tonic-gate 23907c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_PPS_EDGE; 23917c478bd9Sstevel@tonic-gate LED_OFF; 23927c478bd9Sstevel@tonic-gate gethrestime(&ts); 23937c478bd9Sstevel@tonic-gate LED_ON; 23947c478bd9Sstevel@tonic-gate nsec = ts.tv_nsec; 23957c478bd9Sstevel@tonic-gate usec = nsec + (nsec >> 2); 23967c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 1); 23977c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 2); 23987c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 4); 23997c478bd9Sstevel@tonic-gate usec = nsec - (usec >> 3); 24007c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 2); 24017c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 3); 24027c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 4); 24037c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 1); 24047c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 6); 24057c478bd9Sstevel@tonic-gate tvp->tv_usec = usec >> 10; 24067c478bd9Sstevel@tonic-gate tvp->tv_sec = ts.tv_sec; 24077c478bd9Sstevel@tonic-gate 24087c478bd9Sstevel@tonic-gate ++asy_ppsev.serial; 24097c478bd9Sstevel@tonic-gate 24107c478bd9Sstevel@tonic-gate /* 24117c478bd9Sstevel@tonic-gate * Because the kernel keeps a high-resolution time, 24127c478bd9Sstevel@tonic-gate * pass the current highres timestamp in tvp and zero 24137c478bd9Sstevel@tonic-gate * in usec. 24147c478bd9Sstevel@tonic-gate */ 24157c478bd9Sstevel@tonic-gate ddi_hardpps(tvp, 0); 24167c478bd9Sstevel@tonic-gate } 24177c478bd9Sstevel@tonic-gate } 24187c478bd9Sstevel@tonic-gate 24197c478bd9Sstevel@tonic-gate /* 24207c478bd9Sstevel@tonic-gate * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 24217c478bd9Sstevel@tonic-gate * error interrupt. 24227c478bd9Sstevel@tonic-gate * Try to put the character into the circular buffer for this line; if it 24237c478bd9Sstevel@tonic-gate * overflows, indicate a circular buffer overrun. If this port is always 24247c478bd9Sstevel@tonic-gate * to be serviced immediately, or the character is a STOP character, or 24257c478bd9Sstevel@tonic-gate * more than 15 characters have arrived, queue up a soft interrupt to 24267c478bd9Sstevel@tonic-gate * drain the circular buffer. 24277c478bd9Sstevel@tonic-gate * XXX - needs review for hw FIFOs support. 24287c478bd9Sstevel@tonic-gate */ 24297c478bd9Sstevel@tonic-gate 24307c478bd9Sstevel@tonic-gate static void 24317c478bd9Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr) 24327c478bd9Sstevel@tonic-gate { 24337c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 24347c478bd9Sstevel@tonic-gate uchar_t c; 24357c478bd9Sstevel@tonic-gate uint_t s, needsoft = 0; 24367c478bd9Sstevel@tonic-gate tty_common_t *tp; 24377c478bd9Sstevel@tonic-gate int looplim = asy->asy_fifo_buf * 2; 24387c478bd9Sstevel@tonic-gate 2439*395a1446SAndy Fiddaman ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2440*395a1446SAndy Fiddaman 24417c478bd9Sstevel@tonic-gate tp = &async->async_ttycommon; 24427c478bd9Sstevel@tonic-gate if (!(tp->t_cflag & CREAD)) { 24437c478bd9Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 24444ab75253Smrj (void) (ddi_get8(asy->asy_iohandle, 24457c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT) & 0xff); 24464ab75253Smrj lsr = ddi_get8(asy->asy_iohandle, 24477c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR); 24487c478bd9Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 24497c478bd9Sstevel@tonic-gate break; 24507c478bd9Sstevel@tonic-gate } 24517c478bd9Sstevel@tonic-gate return; /* line is not open for read? */ 24527c478bd9Sstevel@tonic-gate } 24537c478bd9Sstevel@tonic-gate 24547c478bd9Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 24557c478bd9Sstevel@tonic-gate c = 0; 24567c478bd9Sstevel@tonic-gate s = 0; /* reset error status */ 24577c478bd9Sstevel@tonic-gate if (lsr & RCA) { 24584ab75253Smrj c = ddi_get8(asy->asy_iohandle, 24597c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT) & 0xff; 24607c478bd9Sstevel@tonic-gate 24617c478bd9Sstevel@tonic-gate /* 24627c478bd9Sstevel@tonic-gate * We handle XON/XOFF char if IXON is set, 24637c478bd9Sstevel@tonic-gate * but if received char is _POSIX_VDISABLE, 24647c478bd9Sstevel@tonic-gate * we left it to the up level module. 24657c478bd9Sstevel@tonic-gate */ 24667c478bd9Sstevel@tonic-gate if (tp->t_iflag & IXON) { 24677c478bd9Sstevel@tonic-gate if ((c == async->async_stopc) && 24687c478bd9Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 24697c478bd9Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24707c478bd9Sstevel@tonic-gate FLOW_STOP); 24717c478bd9Sstevel@tonic-gate goto check_looplim; 24727c478bd9Sstevel@tonic-gate } else if ((c == async->async_startc) && 24737c478bd9Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 24747c478bd9Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24757c478bd9Sstevel@tonic-gate FLOW_START); 24767c478bd9Sstevel@tonic-gate needsoft = 1; 24777c478bd9Sstevel@tonic-gate goto check_looplim; 24787c478bd9Sstevel@tonic-gate } 24797c478bd9Sstevel@tonic-gate if ((tp->t_iflag & IXANY) && 24807c478bd9Sstevel@tonic-gate (async->async_flags & ASYNC_SW_OUT_FLW)) { 24817c478bd9Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24827c478bd9Sstevel@tonic-gate FLOW_START); 24837c478bd9Sstevel@tonic-gate needsoft = 1; 24847c478bd9Sstevel@tonic-gate } 24857c478bd9Sstevel@tonic-gate } 24867c478bd9Sstevel@tonic-gate } 24877c478bd9Sstevel@tonic-gate 24887c478bd9Sstevel@tonic-gate /* 24897c478bd9Sstevel@tonic-gate * Check for character break sequence 24907c478bd9Sstevel@tonic-gate */ 24917c478bd9Sstevel@tonic-gate if ((abort_enable == KIOCABORTALTERNATE) && 24927c478bd9Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE)) { 24937c478bd9Sstevel@tonic-gate if (abort_charseq_recognize(c)) 24947c478bd9Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 24957c478bd9Sstevel@tonic-gate } 24967c478bd9Sstevel@tonic-gate 24977c478bd9Sstevel@tonic-gate /* Handle framing errors */ 24987c478bd9Sstevel@tonic-gate if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 24997c478bd9Sstevel@tonic-gate if (lsr & PARERR) { 25007c478bd9Sstevel@tonic-gate if (tp->t_iflag & INPCK) /* parity enabled */ 25017c478bd9Sstevel@tonic-gate s |= PERROR; 25027c478bd9Sstevel@tonic-gate } 25037c478bd9Sstevel@tonic-gate 25047c478bd9Sstevel@tonic-gate if (lsr & (FRMERR|BRKDET)) 25057c478bd9Sstevel@tonic-gate s |= FRERROR; 25067c478bd9Sstevel@tonic-gate if (lsr & OVRRUN) { 25077c478bd9Sstevel@tonic-gate async->async_hw_overrun = 1; 25087c478bd9Sstevel@tonic-gate s |= OVERRUN; 25097c478bd9Sstevel@tonic-gate } 25107c478bd9Sstevel@tonic-gate } 25117c478bd9Sstevel@tonic-gate 25127c478bd9Sstevel@tonic-gate if (s == 0) 25137c478bd9Sstevel@tonic-gate if ((tp->t_iflag & PARMRK) && 25147c478bd9Sstevel@tonic-gate !(tp->t_iflag & (IGNPAR|ISTRIP)) && 25157c478bd9Sstevel@tonic-gate (c == 0377)) 25167c478bd9Sstevel@tonic-gate if (RING_POK(async, 2)) { 25177c478bd9Sstevel@tonic-gate RING_PUT(async, 0377); 25187c478bd9Sstevel@tonic-gate RING_PUT(async, c); 25197c478bd9Sstevel@tonic-gate } else 25207c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1; 25217c478bd9Sstevel@tonic-gate else 25227c478bd9Sstevel@tonic-gate if (RING_POK(async, 1)) 25237c478bd9Sstevel@tonic-gate RING_PUT(async, c); 25247c478bd9Sstevel@tonic-gate else 25257c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1; 25267c478bd9Sstevel@tonic-gate else 25277c478bd9Sstevel@tonic-gate if (s & FRERROR) /* Handle framing errors */ 25287c478bd9Sstevel@tonic-gate if (c == 0) 25297c478bd9Sstevel@tonic-gate if ((asy->asy_flags & ASY_CONSOLE) && 25307c478bd9Sstevel@tonic-gate (abort_enable != 25317c478bd9Sstevel@tonic-gate KIOCABORTALTERNATE)) 25327c478bd9Sstevel@tonic-gate abort_sequence_enter((char *)0); 25337c478bd9Sstevel@tonic-gate else 25347c478bd9Sstevel@tonic-gate async->async_break++; 25357c478bd9Sstevel@tonic-gate else 25367c478bd9Sstevel@tonic-gate if (RING_POK(async, 1)) 25377c478bd9Sstevel@tonic-gate RING_MARK(async, c, s); 25387c478bd9Sstevel@tonic-gate else 25397c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1; 25407c478bd9Sstevel@tonic-gate else /* Parity errors are handled by ldterm */ 25417c478bd9Sstevel@tonic-gate if (RING_POK(async, 1)) 25427c478bd9Sstevel@tonic-gate RING_MARK(async, c, s); 25437c478bd9Sstevel@tonic-gate else 25447c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1; 25457c478bd9Sstevel@tonic-gate check_looplim: 25462df1fe9cSrandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 25477c478bd9Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 25487c478bd9Sstevel@tonic-gate break; 25497c478bd9Sstevel@tonic-gate } 25507c478bd9Sstevel@tonic-gate if ((RING_CNT(async) > (RINGSIZE * 3)/4) && 25517c478bd9Sstevel@tonic-gate !(async->async_inflow_source & IN_FLOW_RINGBUFF)) { 25527c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF); 25537c478bd9Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 25547c478bd9Sstevel@tonic-gate IN_FLOW_RINGBUFF); 25557c478bd9Sstevel@tonic-gate } 25567c478bd9Sstevel@tonic-gate 25577c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 2558*395a1446SAndy Fiddaman (RING_FRAC(async)) || (async->async_polltid == 0)) { 25597c478bd9Sstevel@tonic-gate ASYSETSOFT(asy); /* need a soft interrupt */ 25607c478bd9Sstevel@tonic-gate } 2561*395a1446SAndy Fiddaman } 25627c478bd9Sstevel@tonic-gate 25637c478bd9Sstevel@tonic-gate /* 25647c478bd9Sstevel@tonic-gate * Modem status interrupt. 25657c478bd9Sstevel@tonic-gate * 25667c478bd9Sstevel@tonic-gate * (Note: It is assumed that the MSR hasn't been read by asyintr().) 25677c478bd9Sstevel@tonic-gate */ 25687c478bd9Sstevel@tonic-gate 25697c478bd9Sstevel@tonic-gate static void 25707c478bd9Sstevel@tonic-gate async_msint(struct asycom *asy) 25717c478bd9Sstevel@tonic-gate { 25727c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 25737c478bd9Sstevel@tonic-gate int msr, t_cflag = async->async_ttycommon.t_cflag; 25747c478bd9Sstevel@tonic-gate #ifdef DEBUG 25757c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 25767c478bd9Sstevel@tonic-gate #endif 25777c478bd9Sstevel@tonic-gate 2578*395a1446SAndy Fiddaman ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2579*395a1446SAndy Fiddaman 25807c478bd9Sstevel@tonic-gate async_msint_retry: 25817c478bd9Sstevel@tonic-gate /* this resets the interrupt */ 25824ab75253Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 25837c478bd9Sstevel@tonic-gate DEBUGCONT10(ASY_DEBUG_STATE, 25847c478bd9Sstevel@tonic-gate "async%d_msint call #%d:\n" 25857c478bd9Sstevel@tonic-gate " transition: %3s %3s %3s %3s\n" 25867c478bd9Sstevel@tonic-gate "current state: %3s %3s %3s %3s\n", 25877c478bd9Sstevel@tonic-gate instance, 25887c478bd9Sstevel@tonic-gate ++(asy->asy_msint_cnt), 25897c478bd9Sstevel@tonic-gate (msr & DCTS) ? "DCTS" : " ", 25907c478bd9Sstevel@tonic-gate (msr & DDSR) ? "DDSR" : " ", 25917c478bd9Sstevel@tonic-gate (msr & DRI) ? "DRI " : " ", 25927c478bd9Sstevel@tonic-gate (msr & DDCD) ? "DDCD" : " ", 25937c478bd9Sstevel@tonic-gate (msr & CTS) ? "CTS " : " ", 25947c478bd9Sstevel@tonic-gate (msr & DSR) ? "DSR " : " ", 25957c478bd9Sstevel@tonic-gate (msr & RI) ? "RI " : " ", 25967c478bd9Sstevel@tonic-gate (msr & DCD) ? "DCD " : " "); 25977c478bd9Sstevel@tonic-gate 25987c478bd9Sstevel@tonic-gate /* If CTS status is changed, do H/W output flow control */ 25997c478bd9Sstevel@tonic-gate if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0)) 26007c478bd9Sstevel@tonic-gate async_flowcontrol_hw_output(asy, 26017c478bd9Sstevel@tonic-gate msr & CTS ? FLOW_START : FLOW_STOP); 26027c478bd9Sstevel@tonic-gate /* 26037c478bd9Sstevel@tonic-gate * Reading MSR resets the interrupt, we save the 26047c478bd9Sstevel@tonic-gate * value of msr so that other functions could examine MSR by 26057c478bd9Sstevel@tonic-gate * looking at asy_msr. 26067c478bd9Sstevel@tonic-gate */ 26077c478bd9Sstevel@tonic-gate asy->asy_msr = (uchar_t)msr; 26087c478bd9Sstevel@tonic-gate 26097c478bd9Sstevel@tonic-gate /* Handle PPS event */ 26107c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 26117c478bd9Sstevel@tonic-gate asy_ppsevent(asy, msr); 26127c478bd9Sstevel@tonic-gate 26137c478bd9Sstevel@tonic-gate async->async_ext++; 26147c478bd9Sstevel@tonic-gate ASYSETSOFT(asy); 26157c478bd9Sstevel@tonic-gate /* 26167c478bd9Sstevel@tonic-gate * We will make sure that the modem status presented to us 26177c478bd9Sstevel@tonic-gate * during the previous read has not changed. If the chip samples 26187c478bd9Sstevel@tonic-gate * the modem status on the falling edge of the interrupt line, 26197c478bd9Sstevel@tonic-gate * and uses this state as the base for detecting change of modem 26207c478bd9Sstevel@tonic-gate * status, we would miss a change of modem status event that occured 26217c478bd9Sstevel@tonic-gate * after we initiated a read MSR operation. 26227c478bd9Sstevel@tonic-gate */ 26234ab75253Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 26247c478bd9Sstevel@tonic-gate if (STATES(msr) != STATES(asy->asy_msr)) 26257c478bd9Sstevel@tonic-gate goto async_msint_retry; 26267c478bd9Sstevel@tonic-gate } 26277c478bd9Sstevel@tonic-gate 26287c478bd9Sstevel@tonic-gate /* 26297c478bd9Sstevel@tonic-gate * Handle a second-stage interrupt. 26307c478bd9Sstevel@tonic-gate */ 26317c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 26327c478bd9Sstevel@tonic-gate uint_t 26337c478bd9Sstevel@tonic-gate asysoftintr(caddr_t intarg) 26347c478bd9Sstevel@tonic-gate { 2635cd43c54aSZach Kissel struct asycom *asy = (struct asycom *)intarg; 2636cd43c54aSZach Kissel struct asyncline *async; 26377c478bd9Sstevel@tonic-gate int rv; 2638cd43c54aSZach Kissel uint_t cc; 26397c478bd9Sstevel@tonic-gate 26407c478bd9Sstevel@tonic-gate /* 26417c478bd9Sstevel@tonic-gate * Test and clear soft interrupt. 26427c478bd9Sstevel@tonic-gate */ 2643cd43c54aSZach Kissel mutex_enter(&asy->asy_soft_lock); 26447c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n"); 2645cd43c54aSZach Kissel rv = asy->asysoftpend; 26467c478bd9Sstevel@tonic-gate if (rv != 0) 2647cd43c54aSZach Kissel asy->asysoftpend = 0; 2648cd43c54aSZach Kissel mutex_exit(&asy->asy_soft_lock); 26497c478bd9Sstevel@tonic-gate 26507c478bd9Sstevel@tonic-gate if (rv) { 2651cd43c54aSZach Kissel if (asy->asy_priv == NULL) 2652cd43c54aSZach Kissel return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 2653cd43c54aSZach Kissel async = (struct asyncline *)asy->asy_priv; 2654cd43c54aSZach Kissel mutex_enter(&asy->asy_excl_hi); 26557c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_NEEDSOFT) { 26567c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_NEEDSOFT; 2657cd43c54aSZach Kissel mutex_exit(&asy->asy_excl_hi); 26587c478bd9Sstevel@tonic-gate async_softint(asy); 2659cd43c54aSZach Kissel mutex_enter(&asy->asy_excl_hi); 26607c478bd9Sstevel@tonic-gate } 2661cd43c54aSZach Kissel 2662cd43c54aSZach Kissel /* 2663cd43c54aSZach Kissel * There are some instances where the softintr is not 2664cd43c54aSZach Kissel * scheduled and hence not called. It so happens that 2665cd43c54aSZach Kissel * causes the last few characters to be stuck in the 2666cd43c54aSZach Kissel * ringbuffer. Hence, call the handler once again so 2667cd43c54aSZach Kissel * the last few characters are cleared. 2668cd43c54aSZach Kissel */ 2669cd43c54aSZach Kissel cc = RING_CNT(async); 2670cd43c54aSZach Kissel mutex_exit(&asy->asy_excl_hi); 2671cd43c54aSZach Kissel if (cc > 0) 2672cd43c54aSZach Kissel (void) async_softint(asy); 26737c478bd9Sstevel@tonic-gate } 26747c478bd9Sstevel@tonic-gate return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 26757c478bd9Sstevel@tonic-gate } 26767c478bd9Sstevel@tonic-gate 26777c478bd9Sstevel@tonic-gate /* 26787c478bd9Sstevel@tonic-gate * Handle a software interrupt. 26797c478bd9Sstevel@tonic-gate */ 26807c478bd9Sstevel@tonic-gate static void 26817c478bd9Sstevel@tonic-gate async_softint(struct asycom *asy) 26827c478bd9Sstevel@tonic-gate { 26837c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 2684cd43c54aSZach Kissel uint_t cc; 26857c478bd9Sstevel@tonic-gate mblk_t *bp; 26867c478bd9Sstevel@tonic-gate queue_t *q; 26877c478bd9Sstevel@tonic-gate uchar_t val; 26887c478bd9Sstevel@tonic-gate uchar_t c; 26897c478bd9Sstevel@tonic-gate tty_common_t *tp; 26907c478bd9Sstevel@tonic-gate int nb; 26917c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 26927c478bd9Sstevel@tonic-gate 26937c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance); 2694cd43c54aSZach Kissel mutex_enter(&asy->asy_excl_hi); 26957c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT) { 26967c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT_RETRY; 2697cd43c54aSZach Kissel mutex_exit(&asy->asy_excl_hi); 26987c478bd9Sstevel@tonic-gate return; 26997c478bd9Sstevel@tonic-gate } 27007c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT; 27017c478bd9Sstevel@tonic-gate begin: 27027c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT_RETRY; 2703cd43c54aSZach Kissel mutex_exit(&asy->asy_excl_hi); 27047c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 27057c478bd9Sstevel@tonic-gate tp = &async->async_ttycommon; 27067c478bd9Sstevel@tonic-gate q = tp->t_readq; 27077c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 27087c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0) { 27097c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 27107c478bd9Sstevel@tonic-gate async_resume(async); 27117c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 27127c478bd9Sstevel@tonic-gate } else { 27137c478bd9Sstevel@tonic-gate if (async->async_xmitblk) 27147c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk); 27157c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 27167c478bd9Sstevel@tonic-gate async_start(async); 27177c478bd9Sstevel@tonic-gate } 27187c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 27197c478bd9Sstevel@tonic-gate } 27207c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 27217c478bd9Sstevel@tonic-gate if (async->async_ext) { 27227c478bd9Sstevel@tonic-gate async->async_ext = 0; 27237c478bd9Sstevel@tonic-gate /* check for carrier up */ 27247c478bd9Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2, 27257c478bd9Sstevel@tonic-gate "async%d_softint: asy_msr & DCD = %x, " 27267c478bd9Sstevel@tonic-gate "tp->t_flags & TS_SOFTCAR = %x\n", 27272df1fe9cSrandyf instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR); 27282df1fe9cSrandyf 27297c478bd9Sstevel@tonic-gate if (asy->asy_msr & DCD) { 27307c478bd9Sstevel@tonic-gate /* carrier present */ 27317c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) == 0) { 27327c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODM2, 27337c478bd9Sstevel@tonic-gate "async%d_softint: set ASYNC_CARR_ON\n", 27347c478bd9Sstevel@tonic-gate instance); 27357c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON; 27367c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 27377c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 27387c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 27397c478bd9Sstevel@tonic-gate (void) putctl(q, M_UNHANGUP); 27407c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 27417c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 27427c478bd9Sstevel@tonic-gate } 27437c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 27447c478bd9Sstevel@tonic-gate } 27457c478bd9Sstevel@tonic-gate } else { 27467c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) && 27477c478bd9Sstevel@tonic-gate !(tp->t_cflag & CLOCAL) && 27487c478bd9Sstevel@tonic-gate !(tp->t_flags & TS_SOFTCAR)) { 27497c478bd9Sstevel@tonic-gate int flushflag; 27507c478bd9Sstevel@tonic-gate 27517c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 27527c478bd9Sstevel@tonic-gate "async%d_softint: carrier dropped, " 27537c478bd9Sstevel@tonic-gate "so drop DTR\n", 27547c478bd9Sstevel@tonic-gate instance); 27557c478bd9Sstevel@tonic-gate /* 27567c478bd9Sstevel@tonic-gate * Carrier went away. 27577c478bd9Sstevel@tonic-gate * Drop DTR, abort any output in 27587c478bd9Sstevel@tonic-gate * progress, indicate that output is 27597c478bd9Sstevel@tonic-gate * not stopped, and send a hangup 27607c478bd9Sstevel@tonic-gate * notification upstream. 27617c478bd9Sstevel@tonic-gate */ 27624ab75253Smrj val = ddi_get8(asy->asy_iohandle, 27637c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MCR); 27644ab75253Smrj ddi_put8(asy->asy_iohandle, 27657c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MCR, (val & ~DTR)); 27662df1fe9cSrandyf 27677c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 27687c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_BUSY, 27697c478bd9Sstevel@tonic-gate "async_softint: " 27707c478bd9Sstevel@tonic-gate "Carrier dropped. " 27717c478bd9Sstevel@tonic-gate "Clearing async_ocnt\n"); 27727c478bd9Sstevel@tonic-gate async->async_ocnt = 0; 27737c478bd9Sstevel@tonic-gate } /* if */ 27747c478bd9Sstevel@tonic-gate 27757c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 27767c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 27777c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 27787c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 27797c478bd9Sstevel@tonic-gate (void) putctl(q, M_HANGUP); 27807c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 27817c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 27827c478bd9Sstevel@tonic-gate "async%d_softint: " 27837c478bd9Sstevel@tonic-gate "putctl(q, M_HANGUP)\n", 27847c478bd9Sstevel@tonic-gate instance); 27857c478bd9Sstevel@tonic-gate /* 27867c478bd9Sstevel@tonic-gate * Flush FIFO buffers 27877c478bd9Sstevel@tonic-gate * Any data left in there is invalid now 27887c478bd9Sstevel@tonic-gate */ 27897c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) 27907c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH); 27917c478bd9Sstevel@tonic-gate /* 27927c478bd9Sstevel@tonic-gate * Flush our write queue if we have one. 27932df1fe9cSrandyf * If we're in the midst of close, then 27942df1fe9cSrandyf * flush everything. Don't leave stale 27952df1fe9cSrandyf * ioctls lying about. 27967c478bd9Sstevel@tonic-gate */ 27977c478bd9Sstevel@tonic-gate flushflag = (async->async_flags & 27982df1fe9cSrandyf ASYNC_CLOSING) ? FLUSHALL : 27992df1fe9cSrandyf FLUSHDATA; 28007c478bd9Sstevel@tonic-gate flushq(tp->t_writeq, flushflag); 28017c478bd9Sstevel@tonic-gate 28022df1fe9cSrandyf /* active msg */ 28032df1fe9cSrandyf bp = async->async_xmitblk; 28047c478bd9Sstevel@tonic-gate if (bp != NULL) { 28057c478bd9Sstevel@tonic-gate freeb(bp); 28067c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 28077c478bd9Sstevel@tonic-gate } 28087c478bd9Sstevel@tonic-gate 28097c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28107c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 28117c478bd9Sstevel@tonic-gate /* 28127c478bd9Sstevel@tonic-gate * This message warns of Carrier loss 28132df1fe9cSrandyf * with data left to transmit can hang 28142df1fe9cSrandyf * the system. 28157c478bd9Sstevel@tonic-gate */ 28167c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, 28177c478bd9Sstevel@tonic-gate "async_softint: Flushing to " 28187c478bd9Sstevel@tonic-gate "prevent HUPCL hanging\n"); 28197c478bd9Sstevel@tonic-gate } /* if (ASYNC_ISOPEN) */ 28207c478bd9Sstevel@tonic-gate } /* if (ASYNC_CARR_ON && CLOCAL) */ 28217c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON; 28227c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 28237c478bd9Sstevel@tonic-gate } /* else */ 28247c478bd9Sstevel@tonic-gate } /* if (async->async_ext) */ 28257c478bd9Sstevel@tonic-gate 28267c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28277c478bd9Sstevel@tonic-gate 28287c478bd9Sstevel@tonic-gate /* 28297c478bd9Sstevel@tonic-gate * If data has been added to the circular buffer, remove 28307c478bd9Sstevel@tonic-gate * it from the buffer, and send it up the stream if there's 28317c478bd9Sstevel@tonic-gate * somebody listening. Try to do it 16 bytes at a time. If we 28327c478bd9Sstevel@tonic-gate * have more than 16 bytes to move, move 16 byte chunks and 28337c478bd9Sstevel@tonic-gate * leave the rest for next time around (maybe it will grow). 28347c478bd9Sstevel@tonic-gate */ 28357c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28367c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) { 28377c478bd9Sstevel@tonic-gate RING_INIT(async); 28387c478bd9Sstevel@tonic-gate goto rv; 28397c478bd9Sstevel@tonic-gate } 2840cd43c54aSZach Kissel if ((cc = RING_CNT(async)) == 0) 28417c478bd9Sstevel@tonic-gate goto rv; 28427c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28437c478bd9Sstevel@tonic-gate 28447c478bd9Sstevel@tonic-gate if (!canput(q)) { 28457c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28467c478bd9Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 28477c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, 28487c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS); 28497c478bd9Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 28507c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS); 28517c478bd9Sstevel@tonic-gate } 28527c478bd9Sstevel@tonic-gate goto rv; 28537c478bd9Sstevel@tonic-gate } 28547c478bd9Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_STREAMS) { 28557c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28567c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, 28577c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS); 28587c478bd9Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 28597c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS); 28607c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28617c478bd9Sstevel@tonic-gate } 28622df1fe9cSrandyf 28632df1fe9cSrandyf DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n", 28642df1fe9cSrandyf instance, cc); 28652df1fe9cSrandyf 28667c478bd9Sstevel@tonic-gate if (!(bp = allocb(cc, BPRI_MED))) { 28677c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 28687c478bd9Sstevel@tonic-gate ttycommon_qfull(&async->async_ttycommon, q); 28697c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 28707c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28717c478bd9Sstevel@tonic-gate goto rv; 28727c478bd9Sstevel@tonic-gate } 28737c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28747c478bd9Sstevel@tonic-gate do { 28757c478bd9Sstevel@tonic-gate if (RING_ERR(async, S_ERRORS)) { 28767c478bd9Sstevel@tonic-gate RING_UNMARK(async); 28777c478bd9Sstevel@tonic-gate c = RING_GET(async); 28787c478bd9Sstevel@tonic-gate break; 28797c478bd9Sstevel@tonic-gate } else 28807c478bd9Sstevel@tonic-gate *bp->b_wptr++ = RING_GET(async); 28817c478bd9Sstevel@tonic-gate } while (--cc); 28827c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28837c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 28847c478bd9Sstevel@tonic-gate if (bp->b_wptr > bp->b_rptr) { 28857c478bd9Sstevel@tonic-gate if (!canput(q)) { 28867c478bd9Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: local queue full", 28877c478bd9Sstevel@tonic-gate instance); 28887c478bd9Sstevel@tonic-gate freemsg(bp); 28897c478bd9Sstevel@tonic-gate } else 28907c478bd9Sstevel@tonic-gate (void) putq(q, bp); 28917c478bd9Sstevel@tonic-gate } else 28927c478bd9Sstevel@tonic-gate freemsg(bp); 28937c478bd9Sstevel@tonic-gate /* 28947c478bd9Sstevel@tonic-gate * If we have a parity error, then send 28957c478bd9Sstevel@tonic-gate * up an M_BREAK with the "bad" 28967c478bd9Sstevel@tonic-gate * character as an argument. Let ldterm 28977c478bd9Sstevel@tonic-gate * figure out what to do with the error. 28987c478bd9Sstevel@tonic-gate */ 2899*395a1446SAndy Fiddaman if (cc) 29007c478bd9Sstevel@tonic-gate (void) putctl1(q, M_BREAK, c); 29017c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29027c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 2903*395a1446SAndy Fiddaman if (cc) { 2904*395a1446SAndy Fiddaman ASYSETSOFT(asy); /* finish cc chars */ 2905*395a1446SAndy Fiddaman } 29067c478bd9Sstevel@tonic-gate rv: 29077c478bd9Sstevel@tonic-gate if ((RING_CNT(async) < (RINGSIZE/4)) && 29087c478bd9Sstevel@tonic-gate (async->async_inflow_source & IN_FLOW_RINGBUFF)) { 29097c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF); 29107c478bd9Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 29117c478bd9Sstevel@tonic-gate IN_FLOW_RINGBUFF); 29127c478bd9Sstevel@tonic-gate } 29137c478bd9Sstevel@tonic-gate 29147c478bd9Sstevel@tonic-gate /* 29157c478bd9Sstevel@tonic-gate * If a transmission has finished, indicate that it's finished, 29167c478bd9Sstevel@tonic-gate * and start that line up again. 29177c478bd9Sstevel@tonic-gate */ 29187c478bd9Sstevel@tonic-gate if (async->async_break > 0) { 29197c478bd9Sstevel@tonic-gate nb = async->async_break; 29207c478bd9Sstevel@tonic-gate async->async_break = 0; 29217c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 29227c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29237c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29247c478bd9Sstevel@tonic-gate for (; nb > 0; nb--) 29257c478bd9Sstevel@tonic-gate (void) putctl(q, M_BREAK); 29267c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29277c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29287c478bd9Sstevel@tonic-gate } 29297c478bd9Sstevel@tonic-gate } 29307c478bd9Sstevel@tonic-gate if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) { 29317c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 29327c478bd9Sstevel@tonic-gate "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n", 29337c478bd9Sstevel@tonic-gate instance, 29347c478bd9Sstevel@tonic-gate async->async_ocnt); 29357c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 29367c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29377c478bd9Sstevel@tonic-gate if (async->async_xmitblk) 29387c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk); 29397c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 29407c478bd9Sstevel@tonic-gate async_start(async); 29417c478bd9Sstevel@tonic-gate /* 29427c478bd9Sstevel@tonic-gate * If the flag isn't set after doing the async_start above, we 29437c478bd9Sstevel@tonic-gate * may have finished all the queued output. Signal any thread 29447c478bd9Sstevel@tonic-gate * stuck in close. 29457c478bd9Sstevel@tonic-gate */ 29467c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BUSY)) 29477c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 29487c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29497c478bd9Sstevel@tonic-gate } 29507c478bd9Sstevel@tonic-gate /* 29517c478bd9Sstevel@tonic-gate * A note about these overrun bits: all they do is *tell* someone 29527c478bd9Sstevel@tonic-gate * about an error- They do not track multiple errors. In fact, 29537c478bd9Sstevel@tonic-gate * you could consider them latched register bits if you like. 29547c478bd9Sstevel@tonic-gate * We are only interested in printing the error message once for 29557c478bd9Sstevel@tonic-gate * any cluster of overrun errrors. 29567c478bd9Sstevel@tonic-gate */ 29577c478bd9Sstevel@tonic-gate if (async->async_hw_overrun) { 29587c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 29597c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29607c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29617c478bd9Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: silo overflow", instance); 29627c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29637c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29647c478bd9Sstevel@tonic-gate } 29657c478bd9Sstevel@tonic-gate async->async_hw_overrun = 0; 29667c478bd9Sstevel@tonic-gate } 29677c478bd9Sstevel@tonic-gate if (async->async_sw_overrun) { 29687c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 29697c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29707c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29717c478bd9Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: ring buffer overflow", 29727c478bd9Sstevel@tonic-gate instance); 29737c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29747c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29757c478bd9Sstevel@tonic-gate } 29767c478bd9Sstevel@tonic-gate async->async_sw_overrun = 0; 29777c478bd9Sstevel@tonic-gate } 2978cd43c54aSZach Kissel if (asy->asy_flags & ASY_DOINGSOFT_RETRY) { 29797c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29807c478bd9Sstevel@tonic-gate goto begin; 29817c478bd9Sstevel@tonic-gate } 29827c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT; 2983cd43c54aSZach Kissel mutex_exit(&asy->asy_excl_hi); 2984cd43c54aSZach Kissel mutex_exit(&asy->asy_excl); 29857c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance); 29867c478bd9Sstevel@tonic-gate } 29877c478bd9Sstevel@tonic-gate 29887c478bd9Sstevel@tonic-gate /* 29897c478bd9Sstevel@tonic-gate * Restart output on a line after a delay or break timer expired. 29907c478bd9Sstevel@tonic-gate */ 29917c478bd9Sstevel@tonic-gate static void 29927c478bd9Sstevel@tonic-gate async_restart(void *arg) 29937c478bd9Sstevel@tonic-gate { 29947c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)arg; 29957c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 29967c478bd9Sstevel@tonic-gate uchar_t lcr; 29977c478bd9Sstevel@tonic-gate 29987c478bd9Sstevel@tonic-gate /* 29997c478bd9Sstevel@tonic-gate * If break timer expired, turn off the break bit. 30007c478bd9Sstevel@tonic-gate */ 30017c478bd9Sstevel@tonic-gate #ifdef DEBUG 30027c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 30037c478bd9Sstevel@tonic-gate 30047c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance); 30057c478bd9Sstevel@tonic-gate #endif 30067c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 30077c478bd9Sstevel@tonic-gate /* 30087c478bd9Sstevel@tonic-gate * If ASYNC_OUT_SUSPEND is also set, we don't really 30097c478bd9Sstevel@tonic-gate * clean the HW break, TIOCCBRK is responsible for this. 30107c478bd9Sstevel@tonic-gate */ 30117c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_BREAK) && 30127c478bd9Sstevel@tonic-gate !(async->async_flags & ASYNC_OUT_SUSPEND)) { 30137c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 30142df1fe9cSrandyf lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 30154ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 30167c478bd9Sstevel@tonic-gate (lcr & ~SETBREAK)); 30177c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 30187c478bd9Sstevel@tonic-gate } 30197c478bd9Sstevel@tonic-gate async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); 30207c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 30217c478bd9Sstevel@tonic-gate async_start(async); 30227c478bd9Sstevel@tonic-gate 30237c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 30247c478bd9Sstevel@tonic-gate } 30257c478bd9Sstevel@tonic-gate 30267c478bd9Sstevel@tonic-gate static void 30277c478bd9Sstevel@tonic-gate async_start(struct asyncline *async) 30287c478bd9Sstevel@tonic-gate { 30297c478bd9Sstevel@tonic-gate async_nstart(async, 0); 30307c478bd9Sstevel@tonic-gate } 30317c478bd9Sstevel@tonic-gate 30327c478bd9Sstevel@tonic-gate /* 30337c478bd9Sstevel@tonic-gate * Start output on a line, unless it's busy, frozen, or otherwise. 30347c478bd9Sstevel@tonic-gate */ 30357c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 30367c478bd9Sstevel@tonic-gate static void 30377c478bd9Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode) 30387c478bd9Sstevel@tonic-gate { 30397c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 30407c478bd9Sstevel@tonic-gate int cc; 30417c478bd9Sstevel@tonic-gate queue_t *q; 30427c478bd9Sstevel@tonic-gate mblk_t *bp; 30437c478bd9Sstevel@tonic-gate uchar_t *xmit_addr; 30447c478bd9Sstevel@tonic-gate uchar_t val; 30457c478bd9Sstevel@tonic-gate int fifo_len = 1; 30467c478bd9Sstevel@tonic-gate boolean_t didsome; 30477c478bd9Sstevel@tonic-gate mblk_t *nbp; 30487c478bd9Sstevel@tonic-gate 30497c478bd9Sstevel@tonic-gate #ifdef DEBUG 30507c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 30517c478bd9Sstevel@tonic-gate 30527c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance); 30537c478bd9Sstevel@tonic-gate #endif 30547c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 30557c478bd9Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 30567c478bd9Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 30577c478bd9Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 30587c478bd9Sstevel@tonic-gate } 30597c478bd9Sstevel@tonic-gate 30607c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 30617c478bd9Sstevel@tonic-gate 30627c478bd9Sstevel@tonic-gate /* 30637c478bd9Sstevel@tonic-gate * If the chip is busy (i.e., we're waiting for a break timeout 30647c478bd9Sstevel@tonic-gate * to expire, or for the current transmission to finish, or for 30657c478bd9Sstevel@tonic-gate * output to finish draining from chip), don't grab anything new. 30667c478bd9Sstevel@tonic-gate */ 30677c478bd9Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { 30687c478bd9Sstevel@tonic-gate DEBUGCONT2((mode? ASY_DEBUG_OUT : 0), 30697c478bd9Sstevel@tonic-gate "async%d_nstart: start %s.\n", 30707c478bd9Sstevel@tonic-gate instance, 30717c478bd9Sstevel@tonic-gate async->async_flags & ASYNC_BREAK ? "break" : "busy"); 30727c478bd9Sstevel@tonic-gate return; 30737c478bd9Sstevel@tonic-gate } 30747c478bd9Sstevel@tonic-gate 30757c478bd9Sstevel@tonic-gate /* 30767c478bd9Sstevel@tonic-gate * Check only pended sw input flow control. 30777c478bd9Sstevel@tonic-gate */ 30787c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 30797c478bd9Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 30807c478bd9Sstevel@tonic-gate fifo_len--; 30817c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 30827c478bd9Sstevel@tonic-gate 30837c478bd9Sstevel@tonic-gate /* 30847c478bd9Sstevel@tonic-gate * If we're waiting for a delay timeout to expire, don't grab 30857c478bd9Sstevel@tonic-gate * anything new. 30867c478bd9Sstevel@tonic-gate */ 30877c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_DELAY) { 30887c478bd9Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 30897c478bd9Sstevel@tonic-gate "async%d_nstart: start ASYNC_DELAY.\n", instance); 30907c478bd9Sstevel@tonic-gate return; 30917c478bd9Sstevel@tonic-gate } 30927c478bd9Sstevel@tonic-gate 30937c478bd9Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 30947c478bd9Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 30957c478bd9Sstevel@tonic-gate "async%d_nstart: start writeq is null.\n", instance); 30967c478bd9Sstevel@tonic-gate return; /* not attached to a stream */ 30977c478bd9Sstevel@tonic-gate } 30987c478bd9Sstevel@tonic-gate 30997c478bd9Sstevel@tonic-gate for (;;) { 31007c478bd9Sstevel@tonic-gate if ((bp = getq(q)) == NULL) 31017c478bd9Sstevel@tonic-gate return; /* no data to transmit */ 31027c478bd9Sstevel@tonic-gate 31037c478bd9Sstevel@tonic-gate /* 31047c478bd9Sstevel@tonic-gate * We have a message block to work on. 31057c478bd9Sstevel@tonic-gate * Check whether it's a break, a delay, or an ioctl (the latter 31067c478bd9Sstevel@tonic-gate * occurs if the ioctl in question was waiting for the output 31077c478bd9Sstevel@tonic-gate * to drain). If it's one of those, process it immediately. 31087c478bd9Sstevel@tonic-gate */ 31097c478bd9Sstevel@tonic-gate switch (bp->b_datap->db_type) { 31107c478bd9Sstevel@tonic-gate 31117c478bd9Sstevel@tonic-gate case M_BREAK: 31127c478bd9Sstevel@tonic-gate /* 31137c478bd9Sstevel@tonic-gate * Set the break bit, and arrange for "async_restart" 31147c478bd9Sstevel@tonic-gate * to be called in 1/4 second; it will turn the 31157c478bd9Sstevel@tonic-gate * break bit off, and call "async_start" to grab 31167c478bd9Sstevel@tonic-gate * the next message. 31177c478bd9Sstevel@tonic-gate */ 31187c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 31194ab75253Smrj val = ddi_get8(asy->asy_iohandle, 31207c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR); 31212df1fe9cSrandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 31222df1fe9cSrandyf (val | SETBREAK)); 31237c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 31247c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 31257c478bd9Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 31267c478bd9Sstevel@tonic-gate drv_usectohz(1000000)/4); 31277c478bd9Sstevel@tonic-gate freemsg(bp); 31287c478bd9Sstevel@tonic-gate return; /* wait for this to finish */ 31297c478bd9Sstevel@tonic-gate 31307c478bd9Sstevel@tonic-gate case M_DELAY: 31317c478bd9Sstevel@tonic-gate /* 31327c478bd9Sstevel@tonic-gate * Arrange for "async_restart" to be called when the 31337c478bd9Sstevel@tonic-gate * delay expires; it will turn ASYNC_DELAY off, 31347c478bd9Sstevel@tonic-gate * and call "async_start" to grab the next message. 31357c478bd9Sstevel@tonic-gate */ 31367c478bd9Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 31377c478bd9Sstevel@tonic-gate (int)(*(unsigned char *)bp->b_rptr + 6)); 31387c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_DELAY; 31397c478bd9Sstevel@tonic-gate freemsg(bp); 31407c478bd9Sstevel@tonic-gate return; /* wait for this to finish */ 31417c478bd9Sstevel@tonic-gate 31427c478bd9Sstevel@tonic-gate case M_IOCTL: 31437c478bd9Sstevel@tonic-gate /* 31447c478bd9Sstevel@tonic-gate * This ioctl was waiting for the output ahead of 31457c478bd9Sstevel@tonic-gate * it to drain; obviously, it has. Do it, and 31467c478bd9Sstevel@tonic-gate * then grab the next message after it. 31477c478bd9Sstevel@tonic-gate */ 31487c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 31497c478bd9Sstevel@tonic-gate async_ioctl(async, q, bp); 31507c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 31517c478bd9Sstevel@tonic-gate continue; 31527c478bd9Sstevel@tonic-gate } 31537c478bd9Sstevel@tonic-gate 315422eb7cb5Sgd78059 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) { 31557c478bd9Sstevel@tonic-gate nbp = bp->b_cont; 31567c478bd9Sstevel@tonic-gate freeb(bp); 31577c478bd9Sstevel@tonic-gate bp = nbp; 31587c478bd9Sstevel@tonic-gate } 31597c478bd9Sstevel@tonic-gate if (bp != NULL) 31607c478bd9Sstevel@tonic-gate break; 31617c478bd9Sstevel@tonic-gate } 31627c478bd9Sstevel@tonic-gate 31637c478bd9Sstevel@tonic-gate /* 31647c478bd9Sstevel@tonic-gate * We have data to transmit. If output is stopped, put 31657c478bd9Sstevel@tonic-gate * it back and try again later. 31667c478bd9Sstevel@tonic-gate */ 31677c478bd9Sstevel@tonic-gate if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW | 31687c478bd9Sstevel@tonic-gate ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) { 31697c478bd9Sstevel@tonic-gate (void) putbq(q, bp); 31707c478bd9Sstevel@tonic-gate return; 31717c478bd9Sstevel@tonic-gate } 31727c478bd9Sstevel@tonic-gate 31737c478bd9Sstevel@tonic-gate async->async_xmitblk = bp; 31747c478bd9Sstevel@tonic-gate xmit_addr = bp->b_rptr; 31757c478bd9Sstevel@tonic-gate bp = bp->b_cont; 31767c478bd9Sstevel@tonic-gate if (bp != NULL) 31777c478bd9Sstevel@tonic-gate (void) putbq(q, bp); /* not done with this message yet */ 31787c478bd9Sstevel@tonic-gate 31797c478bd9Sstevel@tonic-gate /* 31807c478bd9Sstevel@tonic-gate * In 5-bit mode, the high order bits are used 31817c478bd9Sstevel@tonic-gate * to indicate character sizes less than five, 31827c478bd9Sstevel@tonic-gate * so we need to explicitly mask before transmitting 31837c478bd9Sstevel@tonic-gate */ 31847c478bd9Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 31857c478bd9Sstevel@tonic-gate unsigned char *p = xmit_addr; 31867c478bd9Sstevel@tonic-gate int cnt = cc; 31877c478bd9Sstevel@tonic-gate 31887c478bd9Sstevel@tonic-gate while (cnt--) 31897c478bd9Sstevel@tonic-gate *p++ &= (unsigned char) 0x1f; 31907c478bd9Sstevel@tonic-gate } 31917c478bd9Sstevel@tonic-gate 31927c478bd9Sstevel@tonic-gate /* 31937c478bd9Sstevel@tonic-gate * Set up this block for pseudo-DMA. 31947c478bd9Sstevel@tonic-gate */ 31957c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 31967c478bd9Sstevel@tonic-gate /* 31977c478bd9Sstevel@tonic-gate * If the transmitter is ready, shove the first 31987c478bd9Sstevel@tonic-gate * character out. 31997c478bd9Sstevel@tonic-gate */ 32007c478bd9Sstevel@tonic-gate didsome = B_FALSE; 32017c478bd9Sstevel@tonic-gate while (--fifo_len >= 0 && cc > 0) { 32024ab75253Smrj if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & 32037c478bd9Sstevel@tonic-gate XHRE)) 32047c478bd9Sstevel@tonic-gate break; 32054ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 32067c478bd9Sstevel@tonic-gate *xmit_addr++); 32077c478bd9Sstevel@tonic-gate cc--; 32087c478bd9Sstevel@tonic-gate didsome = B_TRUE; 32097c478bd9Sstevel@tonic-gate } 32107c478bd9Sstevel@tonic-gate async->async_optr = xmit_addr; 32117c478bd9Sstevel@tonic-gate async->async_ocnt = cc; 32127c478bd9Sstevel@tonic-gate if (didsome) 32137c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 32147c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 32157c478bd9Sstevel@tonic-gate "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", 32162df1fe9cSrandyf instance, async->async_ocnt); 32177c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY; 32187c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 32197c478bd9Sstevel@tonic-gate } 32207c478bd9Sstevel@tonic-gate 32217c478bd9Sstevel@tonic-gate /* 32227c478bd9Sstevel@tonic-gate * Resume output by poking the transmitter. 32237c478bd9Sstevel@tonic-gate */ 32247c478bd9Sstevel@tonic-gate static void 32257c478bd9Sstevel@tonic-gate async_resume(struct asyncline *async) 32267c478bd9Sstevel@tonic-gate { 32277c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 32287c478bd9Sstevel@tonic-gate #ifdef DEBUG 32297c478bd9Sstevel@tonic-gate int instance; 32307c478bd9Sstevel@tonic-gate #endif 32317c478bd9Sstevel@tonic-gate 32327c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 32337c478bd9Sstevel@tonic-gate #ifdef DEBUG 32347c478bd9Sstevel@tonic-gate instance = UNIT(async->async_dev); 32357c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance); 32367c478bd9Sstevel@tonic-gate #endif 32377c478bd9Sstevel@tonic-gate 32384ab75253Smrj if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) { 32397c478bd9Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 32407c478bd9Sstevel@tonic-gate return; 32417c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0 && 32427c478bd9Sstevel@tonic-gate !(async->async_flags & 32437c478bd9Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) { 32444ab75253Smrj ddi_put8(asy->asy_iohandle, 32457c478bd9Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 32467c478bd9Sstevel@tonic-gate async->async_ocnt--; 32477c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 32487c478bd9Sstevel@tonic-gate } 32497c478bd9Sstevel@tonic-gate } 32507c478bd9Sstevel@tonic-gate } 32517c478bd9Sstevel@tonic-gate 32527c478bd9Sstevel@tonic-gate /* 32537c478bd9Sstevel@tonic-gate * Hold the untimed break to last the minimum time. 32547c478bd9Sstevel@tonic-gate */ 32557c478bd9Sstevel@tonic-gate static void 32567c478bd9Sstevel@tonic-gate async_hold_utbrk(void *arg) 32577c478bd9Sstevel@tonic-gate { 32587c478bd9Sstevel@tonic-gate struct asyncline *async = arg; 32597c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 32607c478bd9Sstevel@tonic-gate 32617c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 32627c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HOLD_UTBRK; 32637c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 32647c478bd9Sstevel@tonic-gate async->async_utbrktid = 0; 32657c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 32667c478bd9Sstevel@tonic-gate } 32677c478bd9Sstevel@tonic-gate 32687c478bd9Sstevel@tonic-gate /* 32697c478bd9Sstevel@tonic-gate * Resume the untimed break. 32707c478bd9Sstevel@tonic-gate */ 32717c478bd9Sstevel@tonic-gate static void 32727c478bd9Sstevel@tonic-gate async_resume_utbrk(struct asyncline *async) 32737c478bd9Sstevel@tonic-gate { 32747c478bd9Sstevel@tonic-gate uchar_t val; 32757c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 32767c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 32777c478bd9Sstevel@tonic-gate 32787c478bd9Sstevel@tonic-gate /* 32797c478bd9Sstevel@tonic-gate * Because the wait time is very short, 32807c478bd9Sstevel@tonic-gate * so we use uninterruptably wait. 32817c478bd9Sstevel@tonic-gate */ 32827c478bd9Sstevel@tonic-gate while (async->async_flags & ASYNC_HOLD_UTBRK) { 32837c478bd9Sstevel@tonic-gate cv_wait(&async->async_flags_cv, &asy->asy_excl); 32847c478bd9Sstevel@tonic-gate } 32857c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 32867c478bd9Sstevel@tonic-gate /* 32877c478bd9Sstevel@tonic-gate * Timed break and untimed break can exist simultaneously, 32887c478bd9Sstevel@tonic-gate * if ASYNC_BREAK is also set at here, we don't 32897c478bd9Sstevel@tonic-gate * really clean the HW break. 32907c478bd9Sstevel@tonic-gate */ 32917c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BREAK)) { 32924ab75253Smrj val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 32934ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 32947c478bd9Sstevel@tonic-gate (val & ~SETBREAK)); 32957c478bd9Sstevel@tonic-gate } 32967c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND; 32977c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 32987c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0) { 32997c478bd9Sstevel@tonic-gate async_resume(async); 33007c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 33017c478bd9Sstevel@tonic-gate } else { 33027c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 33037c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 33047c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 33057c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk); 33067c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 33077c478bd9Sstevel@tonic-gate } 33087c478bd9Sstevel@tonic-gate async_start(async); 33097c478bd9Sstevel@tonic-gate } 33107c478bd9Sstevel@tonic-gate } 33117c478bd9Sstevel@tonic-gate 33127c478bd9Sstevel@tonic-gate /* 33137c478bd9Sstevel@tonic-gate * Process an "ioctl" message sent down to us. 33147c478bd9Sstevel@tonic-gate * Note that we don't need to get any locks until we are ready to access 33157c478bd9Sstevel@tonic-gate * the hardware. Nothing we access until then is going to be altered 33167c478bd9Sstevel@tonic-gate * outside of the STREAMS framework, so we should be safe. 33177c478bd9Sstevel@tonic-gate */ 33187c478bd9Sstevel@tonic-gate int asydelay = 10000; 33197c478bd9Sstevel@tonic-gate static void 33207c478bd9Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 33217c478bd9Sstevel@tonic-gate { 33227c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common; 33237c478bd9Sstevel@tonic-gate tty_common_t *tp = &async->async_ttycommon; 33247c478bd9Sstevel@tonic-gate struct iocblk *iocp; 33257c478bd9Sstevel@tonic-gate unsigned datasize; 33267c478bd9Sstevel@tonic-gate int error = 0; 33277c478bd9Sstevel@tonic-gate uchar_t val; 33287c478bd9Sstevel@tonic-gate mblk_t *datamp; 33297c478bd9Sstevel@tonic-gate unsigned int index; 33307c478bd9Sstevel@tonic-gate 33317c478bd9Sstevel@tonic-gate #ifdef DEBUG 33327c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 33337c478bd9Sstevel@tonic-gate 33347c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance); 33357c478bd9Sstevel@tonic-gate #endif 33367c478bd9Sstevel@tonic-gate 33377c478bd9Sstevel@tonic-gate if (tp->t_iocpending != NULL) { 33387c478bd9Sstevel@tonic-gate /* 33397c478bd9Sstevel@tonic-gate * We were holding an "ioctl" response pending the 33407c478bd9Sstevel@tonic-gate * availability of an "mblk" to hold data to be passed up; 33417c478bd9Sstevel@tonic-gate * another "ioctl" came through, which means that "ioctl" 33427c478bd9Sstevel@tonic-gate * must have timed out or been aborted. 33437c478bd9Sstevel@tonic-gate */ 33447c478bd9Sstevel@tonic-gate freemsg(async->async_ttycommon.t_iocpending); 33457c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 33467c478bd9Sstevel@tonic-gate } 33477c478bd9Sstevel@tonic-gate 33487c478bd9Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 33497c478bd9Sstevel@tonic-gate 33507c478bd9Sstevel@tonic-gate /* 33517c478bd9Sstevel@tonic-gate * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 33527c478bd9Sstevel@tonic-gate * because this function frees up the message block (mp->b_cont) that 33537c478bd9Sstevel@tonic-gate * contains the user location where we pass back the results. 33547c478bd9Sstevel@tonic-gate * 33557c478bd9Sstevel@tonic-gate * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 33567c478bd9Sstevel@tonic-gate * zaps. We know that ttycommon_ioctl doesn't know any CONS* 33577c478bd9Sstevel@tonic-gate * ioctls, so keep the others safe too. 33587c478bd9Sstevel@tonic-gate */ 33597c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n", 33607c478bd9Sstevel@tonic-gate instance, 33617c478bd9Sstevel@tonic-gate iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 33627c478bd9Sstevel@tonic-gate iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 33637c478bd9Sstevel@tonic-gate iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 33647c478bd9Sstevel@tonic-gate iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : 33657c478bd9Sstevel@tonic-gate "other"); 33662df1fe9cSrandyf 33677c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) { 33687c478bd9Sstevel@tonic-gate case TIOCMGET: 33697c478bd9Sstevel@tonic-gate case TIOCGPPS: 33707c478bd9Sstevel@tonic-gate case TIOCSPPS: 33717c478bd9Sstevel@tonic-gate case TIOCGPPSEV: 33727c478bd9Sstevel@tonic-gate case CONSOPENPOLLEDIO: 33737c478bd9Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 33747c478bd9Sstevel@tonic-gate case CONSSETABORTENABLE: 33757c478bd9Sstevel@tonic-gate case CONSGETABORTENABLE: 33767c478bd9Sstevel@tonic-gate error = -1; /* Do Nothing */ 33777c478bd9Sstevel@tonic-gate break; 33787c478bd9Sstevel@tonic-gate default: 33797c478bd9Sstevel@tonic-gate 33807c478bd9Sstevel@tonic-gate /* 33817c478bd9Sstevel@tonic-gate * The only way in which "ttycommon_ioctl" can fail is if the 33827c478bd9Sstevel@tonic-gate * "ioctl" requires a response containing data to be returned 33837c478bd9Sstevel@tonic-gate * to the user, and no mblk could be allocated for the data. 33847c478bd9Sstevel@tonic-gate * No such "ioctl" alters our state. Thus, we always go ahead 33857c478bd9Sstevel@tonic-gate * and do any state-changes the "ioctl" calls for. If we 33867c478bd9Sstevel@tonic-gate * couldn't allocate the data, "ttycommon_ioctl" has stashed 33877c478bd9Sstevel@tonic-gate * the "ioctl" away safely, so we just call "bufcall" to 33887c478bd9Sstevel@tonic-gate * request that we be called back when we stand a better 33897c478bd9Sstevel@tonic-gate * chance of allocating the data. 33907c478bd9Sstevel@tonic-gate */ 33917c478bd9Sstevel@tonic-gate if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 33927c478bd9Sstevel@tonic-gate if (async->async_wbufcid) 33937c478bd9Sstevel@tonic-gate unbufcall(async->async_wbufcid); 33947c478bd9Sstevel@tonic-gate async->async_wbufcid = bufcall(datasize, BPRI_HI, 33957c478bd9Sstevel@tonic-gate (void (*)(void *)) async_reioctl, 33967c478bd9Sstevel@tonic-gate (void *)(intptr_t)async->async_common->asy_unit); 33977c478bd9Sstevel@tonic-gate return; 33987c478bd9Sstevel@tonic-gate } 33997c478bd9Sstevel@tonic-gate } 34007c478bd9Sstevel@tonic-gate 34017c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 34027c478bd9Sstevel@tonic-gate 34037c478bd9Sstevel@tonic-gate if (error == 0) { 34047c478bd9Sstevel@tonic-gate /* 34057c478bd9Sstevel@tonic-gate * "ttycommon_ioctl" did most of the work; we just use the 34067c478bd9Sstevel@tonic-gate * data it set up. 34077c478bd9Sstevel@tonic-gate */ 34087c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) { 34097c478bd9Sstevel@tonic-gate 34107c478bd9Sstevel@tonic-gate case TCSETS: 34117c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34127c478bd9Sstevel@tonic-gate if (asy_baudok(asy)) 34137c478bd9Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 34147c478bd9Sstevel@tonic-gate else 34157c478bd9Sstevel@tonic-gate error = EINVAL; 34167c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34177c478bd9Sstevel@tonic-gate break; 34187c478bd9Sstevel@tonic-gate case TCSETSF: 34197c478bd9Sstevel@tonic-gate case TCSETSW: 34207c478bd9Sstevel@tonic-gate case TCSETA: 34217c478bd9Sstevel@tonic-gate case TCSETAW: 34227c478bd9Sstevel@tonic-gate case TCSETAF: 34237c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34247c478bd9Sstevel@tonic-gate if (!asy_baudok(asy)) 34257c478bd9Sstevel@tonic-gate error = EINVAL; 34267c478bd9Sstevel@tonic-gate else { 34277c478bd9Sstevel@tonic-gate if (asy_isbusy(asy)) 34287c478bd9Sstevel@tonic-gate asy_waiteot(asy); 34297c478bd9Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 34307c478bd9Sstevel@tonic-gate } 34317c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34327c478bd9Sstevel@tonic-gate break; 34337c478bd9Sstevel@tonic-gate } 34347c478bd9Sstevel@tonic-gate } else if (error < 0) { 34357c478bd9Sstevel@tonic-gate /* 34367c478bd9Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here. 34377c478bd9Sstevel@tonic-gate */ 34387c478bd9Sstevel@tonic-gate error = 0; 34397c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) { 34407c478bd9Sstevel@tonic-gate 34417c478bd9Sstevel@tonic-gate case TIOCGPPS: 34427c478bd9Sstevel@tonic-gate /* 34437c478bd9Sstevel@tonic-gate * Get PPS on/off. 34447c478bd9Sstevel@tonic-gate */ 34457c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL) 34467c478bd9Sstevel@tonic-gate freemsg(mp->b_cont); 34477c478bd9Sstevel@tonic-gate 34487c478bd9Sstevel@tonic-gate mp->b_cont = allocb(sizeof (int), BPRI_HI); 34497c478bd9Sstevel@tonic-gate if (mp->b_cont == NULL) { 34507c478bd9Sstevel@tonic-gate error = ENOMEM; 34517c478bd9Sstevel@tonic-gate break; 34527c478bd9Sstevel@tonic-gate } 34537c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 34547c478bd9Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 1; 34557c478bd9Sstevel@tonic-gate else 34567c478bd9Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 0; 34577c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int); 34587c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 34597c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (int); 34607c478bd9Sstevel@tonic-gate break; 34617c478bd9Sstevel@tonic-gate 34627c478bd9Sstevel@tonic-gate case TIOCSPPS: 34637c478bd9Sstevel@tonic-gate /* 34647c478bd9Sstevel@tonic-gate * Set PPS on/off. 34657c478bd9Sstevel@tonic-gate */ 34667c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 34677c478bd9Sstevel@tonic-gate if (error != 0) 34687c478bd9Sstevel@tonic-gate break; 34697c478bd9Sstevel@tonic-gate 34707c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34717c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr) 34727c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_PPS; 34737c478bd9Sstevel@tonic-gate else 34747c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS; 34757c478bd9Sstevel@tonic-gate /* Reset edge sense */ 34767c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 34777c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34787c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 34797c478bd9Sstevel@tonic-gate break; 34807c478bd9Sstevel@tonic-gate 34817c478bd9Sstevel@tonic-gate case TIOCGPPSEV: 34827c478bd9Sstevel@tonic-gate { 34837c478bd9Sstevel@tonic-gate /* 34847c478bd9Sstevel@tonic-gate * Get PPS event data. 34857c478bd9Sstevel@tonic-gate */ 34867c478bd9Sstevel@tonic-gate mblk_t *bp; 34877c478bd9Sstevel@tonic-gate void *buf; 34887c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 34897c478bd9Sstevel@tonic-gate struct ppsclockev32 p32; 34907c478bd9Sstevel@tonic-gate #endif 34917c478bd9Sstevel@tonic-gate struct ppsclockev ppsclockev; 34927c478bd9Sstevel@tonic-gate 34937c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL) { 34947c478bd9Sstevel@tonic-gate freemsg(mp->b_cont); 34957c478bd9Sstevel@tonic-gate mp->b_cont = NULL; 34967c478bd9Sstevel@tonic-gate } 34977c478bd9Sstevel@tonic-gate 34987c478bd9Sstevel@tonic-gate if ((asy->asy_flags & ASY_PPS) == 0) { 34997c478bd9Sstevel@tonic-gate error = ENXIO; 35007c478bd9Sstevel@tonic-gate break; 35017c478bd9Sstevel@tonic-gate } 35027c478bd9Sstevel@tonic-gate 35037c478bd9Sstevel@tonic-gate /* Protect from incomplete asy_ppsev */ 35047c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35057c478bd9Sstevel@tonic-gate ppsclockev = asy_ppsev; 35067c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35077c478bd9Sstevel@tonic-gate 35087c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 35097c478bd9Sstevel@tonic-gate if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 35107c478bd9Sstevel@tonic-gate TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 35117c478bd9Sstevel@tonic-gate p32.serial = ppsclockev.serial; 35127c478bd9Sstevel@tonic-gate buf = &p32; 35137c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev32); 35147c478bd9Sstevel@tonic-gate } else 35157c478bd9Sstevel@tonic-gate #endif 35167c478bd9Sstevel@tonic-gate { 35177c478bd9Sstevel@tonic-gate buf = &ppsclockev; 35187c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev); 35197c478bd9Sstevel@tonic-gate } 35207c478bd9Sstevel@tonic-gate 35217c478bd9Sstevel@tonic-gate if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 35227c478bd9Sstevel@tonic-gate error = ENOMEM; 35237c478bd9Sstevel@tonic-gate break; 35247c478bd9Sstevel@tonic-gate } 35257c478bd9Sstevel@tonic-gate mp->b_cont = bp; 35267c478bd9Sstevel@tonic-gate 35277c478bd9Sstevel@tonic-gate bcopy(buf, bp->b_wptr, iocp->ioc_count); 35287c478bd9Sstevel@tonic-gate bp->b_wptr += iocp->ioc_count; 35297c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 35307c478bd9Sstevel@tonic-gate break; 35317c478bd9Sstevel@tonic-gate } 35327c478bd9Sstevel@tonic-gate 35337c478bd9Sstevel@tonic-gate case TCSBRK: 35347c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 35357c478bd9Sstevel@tonic-gate if (error != 0) 35367c478bd9Sstevel@tonic-gate break; 35377c478bd9Sstevel@tonic-gate 35387c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) { 35397c478bd9Sstevel@tonic-gate 35407c478bd9Sstevel@tonic-gate /* 35417c478bd9Sstevel@tonic-gate * XXX Arrangements to ensure that a break 35427c478bd9Sstevel@tonic-gate * isn't in progress should be sufficient. 35437c478bd9Sstevel@tonic-gate * This ugly delay() is the only thing 35447c478bd9Sstevel@tonic-gate * that seems to work on the NCR Worldmark. 35457c478bd9Sstevel@tonic-gate * It should be replaced. Note that an 35467c478bd9Sstevel@tonic-gate * asy_waiteot() also does not work. 35477c478bd9Sstevel@tonic-gate */ 35487c478bd9Sstevel@tonic-gate if (asydelay) 35497c478bd9Sstevel@tonic-gate delay(drv_usectohz(asydelay)); 35507c478bd9Sstevel@tonic-gate 35517c478bd9Sstevel@tonic-gate while (async->async_flags & ASYNC_BREAK) { 35527c478bd9Sstevel@tonic-gate cv_wait(&async->async_flags_cv, 35537c478bd9Sstevel@tonic-gate &asy->asy_excl); 35547c478bd9Sstevel@tonic-gate } 35557c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35567c478bd9Sstevel@tonic-gate /* 35577c478bd9Sstevel@tonic-gate * We loop until the TSR is empty and then 35587c478bd9Sstevel@tonic-gate * set the break. ASYNC_BREAK has been set 35597c478bd9Sstevel@tonic-gate * to ensure that no characters are 35607c478bd9Sstevel@tonic-gate * transmitted while the TSR is being 35617c478bd9Sstevel@tonic-gate * flushed and SOUT is being used for the 35627c478bd9Sstevel@tonic-gate * break signal. 35637c478bd9Sstevel@tonic-gate * 35647c478bd9Sstevel@tonic-gate * The wait period is equal to 35657c478bd9Sstevel@tonic-gate * clock / (baud * 16) * 16 * 2. 35667c478bd9Sstevel@tonic-gate */ 35677c478bd9Sstevel@tonic-gate index = BAUDINDEX( 35687c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag); 35697c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 35702df1fe9cSrandyf 35714ab75253Smrj while ((ddi_get8(asy->asy_iohandle, 35727c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 35737c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35747c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 35757c478bd9Sstevel@tonic-gate drv_usecwait( 35767c478bd9Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff); 35777c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 35787c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35797c478bd9Sstevel@tonic-gate } 35807c478bd9Sstevel@tonic-gate /* 35817c478bd9Sstevel@tonic-gate * Arrange for "async_restart" 35827c478bd9Sstevel@tonic-gate * to be called in 1/4 second; 35837c478bd9Sstevel@tonic-gate * it will turn the break bit off, and call 35847c478bd9Sstevel@tonic-gate * "async_start" to grab the next message. 35857c478bd9Sstevel@tonic-gate */ 35864ab75253Smrj val = ddi_get8(asy->asy_iohandle, 35877c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR); 35884ab75253Smrj ddi_put8(asy->asy_iohandle, 35897c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR, 35907c478bd9Sstevel@tonic-gate (val | SETBREAK)); 35917c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35927c478bd9Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 35937c478bd9Sstevel@tonic-gate drv_usectohz(1000000)/4); 35947c478bd9Sstevel@tonic-gate } else { 35957c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 35967c478bd9Sstevel@tonic-gate "async%d_ioctl: wait for flush.\n", 35977c478bd9Sstevel@tonic-gate instance); 35987c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35997c478bd9Sstevel@tonic-gate asy_waiteot(asy); 36007c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36017c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 36027c478bd9Sstevel@tonic-gate "async%d_ioctl: ldterm satisfied.\n", 36037c478bd9Sstevel@tonic-gate instance); 36047c478bd9Sstevel@tonic-gate } 36057c478bd9Sstevel@tonic-gate break; 36067c478bd9Sstevel@tonic-gate 36077c478bd9Sstevel@tonic-gate case TIOCSBRK: 36087c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_OUT_SUSPEND)) { 36097c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36107c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_SUSPEND; 36117c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HOLD_UTBRK; 36127c478bd9Sstevel@tonic-gate index = BAUDINDEX( 36137c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag); 36144ab75253Smrj while ((ddi_get8(asy->asy_iohandle, 36157c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 36167c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36177c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 36187c478bd9Sstevel@tonic-gate drv_usecwait( 36197c478bd9Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff); 36207c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 36217c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36227c478bd9Sstevel@tonic-gate } 36234ab75253Smrj val = ddi_get8(asy->asy_iohandle, 36247c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR); 36254ab75253Smrj ddi_put8(asy->asy_iohandle, 36267c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LCR, (val | SETBREAK)); 36277c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36287c478bd9Sstevel@tonic-gate /* wait for 100ms to hold BREAK */ 36297c478bd9Sstevel@tonic-gate async->async_utbrktid = 36307c478bd9Sstevel@tonic-gate timeout((void (*)())async_hold_utbrk, 36317c478bd9Sstevel@tonic-gate (caddr_t)async, 36327c478bd9Sstevel@tonic-gate drv_usectohz(asy_min_utbrk)); 36337c478bd9Sstevel@tonic-gate } 36347c478bd9Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 36357c478bd9Sstevel@tonic-gate break; 36367c478bd9Sstevel@tonic-gate 36377c478bd9Sstevel@tonic-gate case TIOCCBRK: 36387c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) 36397c478bd9Sstevel@tonic-gate async_resume_utbrk(async); 36407c478bd9Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 36417c478bd9Sstevel@tonic-gate break; 36427c478bd9Sstevel@tonic-gate 36437c478bd9Sstevel@tonic-gate case TIOCMSET: 36447c478bd9Sstevel@tonic-gate case TIOCMBIS: 36457c478bd9Sstevel@tonic-gate case TIOCMBIC: 36467c478bd9Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 36477c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36487c478bd9Sstevel@tonic-gate "non-transparent\n", instance); 36497c478bd9Sstevel@tonic-gate 36507c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 36517c478bd9Sstevel@tonic-gate if (error != 0) 36527c478bd9Sstevel@tonic-gate break; 36537c478bd9Sstevel@tonic-gate 36547c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36557c478bd9Sstevel@tonic-gate (void) asymctl(asy, 36567c478bd9Sstevel@tonic-gate dmtoasy(*(int *)mp->b_cont->b_rptr), 36577c478bd9Sstevel@tonic-gate iocp->ioc_cmd); 36587c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36597c478bd9Sstevel@tonic-gate iocp->ioc_error = 0; 36607c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36617c478bd9Sstevel@tonic-gate } else { 36627c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36637c478bd9Sstevel@tonic-gate "transparent\n", instance); 36647c478bd9Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL); 36657c478bd9Sstevel@tonic-gate } 36667c478bd9Sstevel@tonic-gate break; 36677c478bd9Sstevel@tonic-gate 36687c478bd9Sstevel@tonic-gate case TIOCMGET: 36697c478bd9Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED); 36707c478bd9Sstevel@tonic-gate if (datamp == NULL) { 36717c478bd9Sstevel@tonic-gate error = EAGAIN; 36727c478bd9Sstevel@tonic-gate break; 36737c478bd9Sstevel@tonic-gate } 36747c478bd9Sstevel@tonic-gate 36757c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36767c478bd9Sstevel@tonic-gate *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 36777c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36787c478bd9Sstevel@tonic-gate 36797c478bd9Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) { 36807c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36817c478bd9Sstevel@tonic-gate "transparent\n", instance); 36822df1fe9cSrandyf mcopyout(mp, NULL, sizeof (int), NULL, datamp); 36837c478bd9Sstevel@tonic-gate } else { 36847c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36857c478bd9Sstevel@tonic-gate "non-transparent\n", instance); 36867c478bd9Sstevel@tonic-gate mioc2ack(mp, datamp, sizeof (int), 0); 36877c478bd9Sstevel@tonic-gate } 36887c478bd9Sstevel@tonic-gate break; 36897c478bd9Sstevel@tonic-gate 36907c478bd9Sstevel@tonic-gate case CONSOPENPOLLEDIO: 36917c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (struct cons_polledio *)); 36927c478bd9Sstevel@tonic-gate if (error != 0) 36937c478bd9Sstevel@tonic-gate break; 36947c478bd9Sstevel@tonic-gate 36957c478bd9Sstevel@tonic-gate *(struct cons_polledio **)mp->b_cont->b_rptr = 36967c478bd9Sstevel@tonic-gate &asy->polledio; 36977c478bd9Sstevel@tonic-gate 36987c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36997c478bd9Sstevel@tonic-gate break; 37007c478bd9Sstevel@tonic-gate 37017c478bd9Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 37027c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 37037c478bd9Sstevel@tonic-gate iocp->ioc_error = 0; 37047c478bd9Sstevel@tonic-gate iocp->ioc_rval = 0; 37057c478bd9Sstevel@tonic-gate break; 37067c478bd9Sstevel@tonic-gate 37077c478bd9Sstevel@tonic-gate case CONSSETABORTENABLE: 37087c478bd9Sstevel@tonic-gate error = secpolicy_console(iocp->ioc_cr); 37097c478bd9Sstevel@tonic-gate if (error != 0) 37107c478bd9Sstevel@tonic-gate break; 37117c478bd9Sstevel@tonic-gate 37127c478bd9Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 37137c478bd9Sstevel@tonic-gate error = EINVAL; 37147c478bd9Sstevel@tonic-gate break; 37157c478bd9Sstevel@tonic-gate } 37167c478bd9Sstevel@tonic-gate 3717*395a1446SAndy Fiddaman mutex_enter(&asy->asy_excl_hi); 37187c478bd9Sstevel@tonic-gate if (*(intptr_t *)mp->b_cont->b_rptr) 37197c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_CONSOLE; 37207c478bd9Sstevel@tonic-gate else 37217c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_CONSOLE; 3722*395a1446SAndy Fiddaman mutex_exit(&asy->asy_excl_hi); 37237c478bd9Sstevel@tonic-gate 37247c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 37257c478bd9Sstevel@tonic-gate iocp->ioc_error = 0; 37267c478bd9Sstevel@tonic-gate iocp->ioc_rval = 0; 37277c478bd9Sstevel@tonic-gate break; 37287c478bd9Sstevel@tonic-gate 37297c478bd9Sstevel@tonic-gate case CONSGETABORTENABLE: 37307c478bd9Sstevel@tonic-gate /*CONSTANTCONDITION*/ 37317c478bd9Sstevel@tonic-gate ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 37327c478bd9Sstevel@tonic-gate /* 37337c478bd9Sstevel@tonic-gate * Store the return value right in the payload 37347c478bd9Sstevel@tonic-gate * we were passed. Crude. 37357c478bd9Sstevel@tonic-gate */ 37367c478bd9Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 37377c478bd9Sstevel@tonic-gate *(boolean_t *)mp->b_cont->b_rptr = 37387c478bd9Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE) != 0; 37397c478bd9Sstevel@tonic-gate break; 37407c478bd9Sstevel@tonic-gate 37417c478bd9Sstevel@tonic-gate default: 37427c478bd9Sstevel@tonic-gate /* 37437c478bd9Sstevel@tonic-gate * If we don't understand it, it's an error. NAK it. 37447c478bd9Sstevel@tonic-gate */ 37457c478bd9Sstevel@tonic-gate error = EINVAL; 37467c478bd9Sstevel@tonic-gate break; 37477c478bd9Sstevel@tonic-gate } 37487c478bd9Sstevel@tonic-gate } 37497c478bd9Sstevel@tonic-gate if (error != 0) { 37507c478bd9Sstevel@tonic-gate iocp->ioc_error = error; 37517c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 37527c478bd9Sstevel@tonic-gate } 37537c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 37547c478bd9Sstevel@tonic-gate qreply(wq, mp); 37557c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance); 37567c478bd9Sstevel@tonic-gate } 37577c478bd9Sstevel@tonic-gate 37587c478bd9Sstevel@tonic-gate static int 37597c478bd9Sstevel@tonic-gate asyrsrv(queue_t *q) 37607c478bd9Sstevel@tonic-gate { 37617c478bd9Sstevel@tonic-gate mblk_t *bp; 37627c478bd9Sstevel@tonic-gate struct asyncline *async; 3763*395a1446SAndy Fiddaman struct asycom *asy; 37647c478bd9Sstevel@tonic-gate 37657c478bd9Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 3766*395a1446SAndy Fiddaman asy = (struct asycom *)async->async_common; 37677c478bd9Sstevel@tonic-gate 37687c478bd9Sstevel@tonic-gate while (canputnext(q) && (bp = getq(q))) 37697c478bd9Sstevel@tonic-gate putnext(q, bp); 3770*395a1446SAndy Fiddaman mutex_enter(&asy->asy_excl_hi); 3771*395a1446SAndy Fiddaman ASYSETSOFT(asy); 3772*395a1446SAndy Fiddaman mutex_exit(&asy->asy_excl_hi); 37737c478bd9Sstevel@tonic-gate async->async_polltid = 0; 37747c478bd9Sstevel@tonic-gate return (0); 37757c478bd9Sstevel@tonic-gate } 37767c478bd9Sstevel@tonic-gate 37777c478bd9Sstevel@tonic-gate /* 37782df1fe9cSrandyf * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should 37792df1fe9cSrandyf * handle messages as though the driver is operating normally or is 37802df1fe9cSrandyf * suspended. In the suspended case, some or all of the processing may have 37812df1fe9cSrandyf * to be delayed until the driver is resumed. 37822df1fe9cSrandyf */ 37832df1fe9cSrandyf #define ASYWPUTDO_NOT_SUSP(async, wput) \ 37842df1fe9cSrandyf !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED)) 37852df1fe9cSrandyf 37862df1fe9cSrandyf /* 37872df1fe9cSrandyf * Processing for write queue put procedure. 37887c478bd9Sstevel@tonic-gate * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 37897c478bd9Sstevel@tonic-gate * set the flow control character for M_STOPI and M_STARTI messages; 37907c478bd9Sstevel@tonic-gate * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 37917c478bd9Sstevel@tonic-gate * by the start routine, and then call the start routine; discard 37927c478bd9Sstevel@tonic-gate * everything else. Note that this driver does not incorporate any 37937c478bd9Sstevel@tonic-gate * mechanism to negotiate to handle the canonicalization process. 37947c478bd9Sstevel@tonic-gate * It expects that these functions are handled in upper module(s), 37957c478bd9Sstevel@tonic-gate * as we do in ldterm. 37967c478bd9Sstevel@tonic-gate */ 37977c478bd9Sstevel@tonic-gate static int 37982df1fe9cSrandyf asywputdo(queue_t *q, mblk_t *mp, boolean_t wput) 37997c478bd9Sstevel@tonic-gate { 38007c478bd9Sstevel@tonic-gate struct asyncline *async; 38017c478bd9Sstevel@tonic-gate struct asycom *asy; 38027c478bd9Sstevel@tonic-gate #ifdef DEBUG 38037c478bd9Sstevel@tonic-gate int instance; 38047c478bd9Sstevel@tonic-gate #endif 38057c478bd9Sstevel@tonic-gate int error; 38067c478bd9Sstevel@tonic-gate 38077c478bd9Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 38082df1fe9cSrandyf 38097c478bd9Sstevel@tonic-gate #ifdef DEBUG 38107c478bd9Sstevel@tonic-gate instance = UNIT(async->async_dev); 38117c478bd9Sstevel@tonic-gate #endif 38127c478bd9Sstevel@tonic-gate asy = async->async_common; 38137c478bd9Sstevel@tonic-gate 38147c478bd9Sstevel@tonic-gate switch (mp->b_datap->db_type) { 38157c478bd9Sstevel@tonic-gate 38167c478bd9Sstevel@tonic-gate case M_STOP: 38177c478bd9Sstevel@tonic-gate /* 38187c478bd9Sstevel@tonic-gate * Since we don't do real DMA, we can just let the 38197c478bd9Sstevel@tonic-gate * chip coast to a stop after applying the brakes. 38207c478bd9Sstevel@tonic-gate */ 38217c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 38227c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_STOPPED; 38237c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38247c478bd9Sstevel@tonic-gate freemsg(mp); 38257c478bd9Sstevel@tonic-gate break; 38267c478bd9Sstevel@tonic-gate 38277c478bd9Sstevel@tonic-gate case M_START: 38287c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 38297c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_STOPPED) { 38307c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 38312df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38327c478bd9Sstevel@tonic-gate /* 38337c478bd9Sstevel@tonic-gate * If an output operation is in progress, 38347c478bd9Sstevel@tonic-gate * resume it. Otherwise, prod the start 38357c478bd9Sstevel@tonic-gate * routine. 38367c478bd9Sstevel@tonic-gate */ 38377c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0) { 38387c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 38397c478bd9Sstevel@tonic-gate async_resume(async); 38407c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 38417c478bd9Sstevel@tonic-gate } else { 38427c478bd9Sstevel@tonic-gate async_start(async); 38437c478bd9Sstevel@tonic-gate } 38447c478bd9Sstevel@tonic-gate } 38452df1fe9cSrandyf } 38467c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38477c478bd9Sstevel@tonic-gate freemsg(mp); 38487c478bd9Sstevel@tonic-gate break; 38497c478bd9Sstevel@tonic-gate 38507c478bd9Sstevel@tonic-gate case M_IOCTL: 38517c478bd9Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 38527c478bd9Sstevel@tonic-gate 38537c478bd9Sstevel@tonic-gate case TCSBRK: 38547c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 38557c478bd9Sstevel@tonic-gate if (error != 0) { 38567c478bd9Sstevel@tonic-gate miocnak(q, mp, 0, error); 38577c478bd9Sstevel@tonic-gate return (0); 38587c478bd9Sstevel@tonic-gate } 38597c478bd9Sstevel@tonic-gate 38607c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr != 0) { 38617c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 38627c478bd9Sstevel@tonic-gate "async%d_ioctl: flush request.\n", 38637c478bd9Sstevel@tonic-gate instance); 38647c478bd9Sstevel@tonic-gate (void) putq(q, mp); 38657c478bd9Sstevel@tonic-gate 38662df1fe9cSrandyf mutex_enter(&asy->asy_excl); 38672df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38687c478bd9Sstevel@tonic-gate /* 38697c478bd9Sstevel@tonic-gate * If an TIOCSBRK is in progress, 38707c478bd9Sstevel@tonic-gate * clean it as TIOCCBRK does, 38717c478bd9Sstevel@tonic-gate * then kick off output. 38727c478bd9Sstevel@tonic-gate * If TIOCSBRK is not in progress, 38737c478bd9Sstevel@tonic-gate * just kick off output. 38747c478bd9Sstevel@tonic-gate */ 38757c478bd9Sstevel@tonic-gate async_resume_utbrk(async); 38762df1fe9cSrandyf } 38777c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38787c478bd9Sstevel@tonic-gate break; 38797c478bd9Sstevel@tonic-gate } 38807c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/ 38817c478bd9Sstevel@tonic-gate case TCSETSW: 38827c478bd9Sstevel@tonic-gate case TCSETSF: 38837c478bd9Sstevel@tonic-gate case TCSETAW: 38847c478bd9Sstevel@tonic-gate case TCSETAF: 38857c478bd9Sstevel@tonic-gate /* 38867c478bd9Sstevel@tonic-gate * The changes do not take effect until all 38877c478bd9Sstevel@tonic-gate * output queued before them is drained. 38887c478bd9Sstevel@tonic-gate * Put this message on the queue, so that 38897c478bd9Sstevel@tonic-gate * "async_start" will see it when it's done 38907c478bd9Sstevel@tonic-gate * with the output before it. Poke the 38917c478bd9Sstevel@tonic-gate * start routine, just in case. 38927c478bd9Sstevel@tonic-gate */ 38937c478bd9Sstevel@tonic-gate (void) putq(q, mp); 38947c478bd9Sstevel@tonic-gate 38952df1fe9cSrandyf mutex_enter(&asy->asy_excl); 38962df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38977c478bd9Sstevel@tonic-gate /* 38987c478bd9Sstevel@tonic-gate * If an TIOCSBRK is in progress, 38997c478bd9Sstevel@tonic-gate * clean it as TIOCCBRK does. 39007c478bd9Sstevel@tonic-gate * then kick off output. 39017c478bd9Sstevel@tonic-gate * If TIOCSBRK is not in progress, 39027c478bd9Sstevel@tonic-gate * just kick off output. 39037c478bd9Sstevel@tonic-gate */ 39047c478bd9Sstevel@tonic-gate async_resume_utbrk(async); 39052df1fe9cSrandyf } 39067c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 39077c478bd9Sstevel@tonic-gate break; 39087c478bd9Sstevel@tonic-gate 39097c478bd9Sstevel@tonic-gate default: 39107c478bd9Sstevel@tonic-gate /* 39117c478bd9Sstevel@tonic-gate * Do it now. 39127c478bd9Sstevel@tonic-gate */ 39132df1fe9cSrandyf mutex_enter(&asy->asy_excl); 39142df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39152df1fe9cSrandyf mutex_exit(&asy->asy_excl); 39167c478bd9Sstevel@tonic-gate async_ioctl(async, q, mp); 39177c478bd9Sstevel@tonic-gate break; 39187c478bd9Sstevel@tonic-gate } 39192df1fe9cSrandyf async_put_suspq(asy, mp); 39202df1fe9cSrandyf mutex_exit(&asy->asy_excl); 39212df1fe9cSrandyf break; 39222df1fe9cSrandyf } 39237c478bd9Sstevel@tonic-gate break; 39247c478bd9Sstevel@tonic-gate 39257c478bd9Sstevel@tonic-gate case M_FLUSH: 39267c478bd9Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) { 39277c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 39287c478bd9Sstevel@tonic-gate 39297c478bd9Sstevel@tonic-gate /* 39307c478bd9Sstevel@tonic-gate * Abort any output in progress. 39317c478bd9Sstevel@tonic-gate */ 39327c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 39337c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 39347c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " 39357c478bd9Sstevel@tonic-gate "Clearing async_ocnt, " 39367c478bd9Sstevel@tonic-gate "leaving ASYNC_BUSY set\n", 39377c478bd9Sstevel@tonic-gate instance); 39387c478bd9Sstevel@tonic-gate async->async_ocnt = 0; 39397c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 39407c478bd9Sstevel@tonic-gate } /* if */ 39412df1fe9cSrandyf 39422df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39432df1fe9cSrandyf /* Flush FIFO buffers */ 39442df1fe9cSrandyf if (asy->asy_use_fifo == FIFO_ON) { 39452df1fe9cSrandyf asy_reset_fifo(asy, FIFOTXFLSH); 39462df1fe9cSrandyf } 39472df1fe9cSrandyf } 39487c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 39497c478bd9Sstevel@tonic-gate 39507c478bd9Sstevel@tonic-gate /* Flush FIFO buffers */ 39517c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 39527c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH); 39537c478bd9Sstevel@tonic-gate } 39547c478bd9Sstevel@tonic-gate 39557c478bd9Sstevel@tonic-gate /* 39567c478bd9Sstevel@tonic-gate * Flush our write queue. 39577c478bd9Sstevel@tonic-gate */ 39587c478bd9Sstevel@tonic-gate flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 39597c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 39607c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk); 39617c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL; 39627c478bd9Sstevel@tonic-gate } 39637c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 39647c478bd9Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 39657c478bd9Sstevel@tonic-gate } 39667c478bd9Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) { 39672df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39687c478bd9Sstevel@tonic-gate /* Flush FIFO buffers */ 39697c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 39707c478bd9Sstevel@tonic-gate asy_reset_fifo(asy, FIFORXFLSH); 39717c478bd9Sstevel@tonic-gate } 39722df1fe9cSrandyf } 39737c478bd9Sstevel@tonic-gate flushq(RD(q), FLUSHDATA); 39747c478bd9Sstevel@tonic-gate qreply(q, mp); /* give the read queues a crack at it */ 39757c478bd9Sstevel@tonic-gate } else { 39767c478bd9Sstevel@tonic-gate freemsg(mp); 39777c478bd9Sstevel@tonic-gate } 39787c478bd9Sstevel@tonic-gate 39797c478bd9Sstevel@tonic-gate /* 39807c478bd9Sstevel@tonic-gate * We must make sure we process messages that survive the 39817c478bd9Sstevel@tonic-gate * write-side flush. 39827c478bd9Sstevel@tonic-gate */ 39832df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39847c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 39857c478bd9Sstevel@tonic-gate async_start(async); 39867c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 39872df1fe9cSrandyf } 39887c478bd9Sstevel@tonic-gate break; 39897c478bd9Sstevel@tonic-gate 39907c478bd9Sstevel@tonic-gate case M_BREAK: 39917c478bd9Sstevel@tonic-gate case M_DELAY: 39927c478bd9Sstevel@tonic-gate case M_DATA: 39937c478bd9Sstevel@tonic-gate /* 39947c478bd9Sstevel@tonic-gate * Queue the message up to be transmitted, 39957c478bd9Sstevel@tonic-gate * and poke the start routine. 39967c478bd9Sstevel@tonic-gate */ 39977c478bd9Sstevel@tonic-gate (void) putq(q, mp); 39982df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39997c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40007c478bd9Sstevel@tonic-gate async_start(async); 40017c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40022df1fe9cSrandyf } 40037c478bd9Sstevel@tonic-gate break; 40047c478bd9Sstevel@tonic-gate 40057c478bd9Sstevel@tonic-gate case M_STOPI: 40067c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40072df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40087c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 40097c478bd9Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_USER)) { 40107c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, 40117c478bd9Sstevel@tonic-gate IN_FLOW_USER); 40122df1fe9cSrandyf (void) async_flowcontrol_sw_input(asy, 40132df1fe9cSrandyf FLOW_STOP, IN_FLOW_USER); 40147c478bd9Sstevel@tonic-gate } 40157c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 40167c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40177c478bd9Sstevel@tonic-gate freemsg(mp); 40187c478bd9Sstevel@tonic-gate break; 40192df1fe9cSrandyf } 40202df1fe9cSrandyf async_put_suspq(asy, mp); 40212df1fe9cSrandyf mutex_exit(&asy->asy_excl); 40222df1fe9cSrandyf break; 40237c478bd9Sstevel@tonic-gate 40247c478bd9Sstevel@tonic-gate case M_STARTI: 40257c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40262df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40277c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 40287c478bd9Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_USER) { 40297c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, 40307c478bd9Sstevel@tonic-gate IN_FLOW_USER); 40312df1fe9cSrandyf (void) async_flowcontrol_sw_input(asy, 40322df1fe9cSrandyf FLOW_START, IN_FLOW_USER); 40337c478bd9Sstevel@tonic-gate } 40347c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 40357c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40367c478bd9Sstevel@tonic-gate freemsg(mp); 40377c478bd9Sstevel@tonic-gate break; 40382df1fe9cSrandyf } 40392df1fe9cSrandyf async_put_suspq(asy, mp); 40402df1fe9cSrandyf mutex_exit(&asy->asy_excl); 40412df1fe9cSrandyf break; 40427c478bd9Sstevel@tonic-gate 40437c478bd9Sstevel@tonic-gate case M_CTL: 40447c478bd9Sstevel@tonic-gate if (MBLKL(mp) >= sizeof (struct iocblk) && 40457c478bd9Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 40462df1fe9cSrandyf mutex_enter(&asy->asy_excl); 40472df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40482df1fe9cSrandyf ((struct iocblk *)mp->b_rptr)->ioc_cmd = 40492df1fe9cSrandyf MC_HAS_POSIX; 40502df1fe9cSrandyf mutex_exit(&asy->asy_excl); 40517c478bd9Sstevel@tonic-gate qreply(q, mp); 40522df1fe9cSrandyf break; 40532df1fe9cSrandyf } else { 40542df1fe9cSrandyf async_put_suspq(asy, mp); 40552df1fe9cSrandyf } 40567c478bd9Sstevel@tonic-gate } else { 40577c478bd9Sstevel@tonic-gate /* 40587c478bd9Sstevel@tonic-gate * These MC_SERVICE type messages are used by upper 40597c478bd9Sstevel@tonic-gate * modules to tell this driver to send input up 40607c478bd9Sstevel@tonic-gate * immediately, or that it can wait for normal 40617c478bd9Sstevel@tonic-gate * processing that may or may not be done. Sun 40627c478bd9Sstevel@tonic-gate * requires these for the mouse module. 40637c478bd9Sstevel@tonic-gate * (XXX - for x86?) 40647c478bd9Sstevel@tonic-gate */ 40657c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40667c478bd9Sstevel@tonic-gate switch (*mp->b_rptr) { 40677c478bd9Sstevel@tonic-gate 40687c478bd9Sstevel@tonic-gate case MC_SERVICEIMM: 40697c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_SERVICEIMM; 40707c478bd9Sstevel@tonic-gate break; 40717c478bd9Sstevel@tonic-gate 40727c478bd9Sstevel@tonic-gate case MC_SERVICEDEF: 40737c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_SERVICEIMM; 40747c478bd9Sstevel@tonic-gate break; 40757c478bd9Sstevel@tonic-gate } 40767c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40777c478bd9Sstevel@tonic-gate freemsg(mp); 40787c478bd9Sstevel@tonic-gate } 40797c478bd9Sstevel@tonic-gate break; 40807c478bd9Sstevel@tonic-gate 40817c478bd9Sstevel@tonic-gate case M_IOCDATA: 40822df1fe9cSrandyf mutex_enter(&asy->asy_excl); 40832df1fe9cSrandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40842df1fe9cSrandyf mutex_exit(&asy->asy_excl); 40857c478bd9Sstevel@tonic-gate async_iocdata(q, mp); 40867c478bd9Sstevel@tonic-gate break; 40872df1fe9cSrandyf } 40882df1fe9cSrandyf async_put_suspq(asy, mp); 40892df1fe9cSrandyf mutex_exit(&asy->asy_excl); 40902df1fe9cSrandyf break; 40917c478bd9Sstevel@tonic-gate 40927c478bd9Sstevel@tonic-gate default: 40937c478bd9Sstevel@tonic-gate freemsg(mp); 40947c478bd9Sstevel@tonic-gate break; 40957c478bd9Sstevel@tonic-gate } 40967c478bd9Sstevel@tonic-gate return (0); 40977c478bd9Sstevel@tonic-gate } 40987c478bd9Sstevel@tonic-gate 40992df1fe9cSrandyf static int 41002df1fe9cSrandyf asywput(queue_t *q, mblk_t *mp) 41012df1fe9cSrandyf { 41022df1fe9cSrandyf return (asywputdo(q, mp, B_TRUE)); 41032df1fe9cSrandyf } 41042df1fe9cSrandyf 41057c478bd9Sstevel@tonic-gate /* 41067c478bd9Sstevel@tonic-gate * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 41077c478bd9Sstevel@tonic-gate * the buffer we need. 41087c478bd9Sstevel@tonic-gate */ 41097c478bd9Sstevel@tonic-gate static void 41107c478bd9Sstevel@tonic-gate async_reioctl(void *unit) 41117c478bd9Sstevel@tonic-gate { 41127c478bd9Sstevel@tonic-gate int instance = (uintptr_t)unit; 41137c478bd9Sstevel@tonic-gate struct asyncline *async; 41147c478bd9Sstevel@tonic-gate struct asycom *asy; 41157c478bd9Sstevel@tonic-gate queue_t *q; 41167c478bd9Sstevel@tonic-gate mblk_t *mp; 41177c478bd9Sstevel@tonic-gate 41187c478bd9Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 41197c478bd9Sstevel@tonic-gate ASSERT(asy != NULL); 41207c478bd9Sstevel@tonic-gate async = asy->asy_priv; 41217c478bd9Sstevel@tonic-gate 41227c478bd9Sstevel@tonic-gate /* 41237c478bd9Sstevel@tonic-gate * The bufcall is no longer pending. 41247c478bd9Sstevel@tonic-gate */ 41257c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 41267c478bd9Sstevel@tonic-gate async->async_wbufcid = 0; 41277c478bd9Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 41287c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41297c478bd9Sstevel@tonic-gate return; 41307c478bd9Sstevel@tonic-gate } 41317c478bd9Sstevel@tonic-gate if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 41327c478bd9Sstevel@tonic-gate /* not pending any more */ 41337c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 41347c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41357c478bd9Sstevel@tonic-gate async_ioctl(async, q, mp); 41367c478bd9Sstevel@tonic-gate } else 41377c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41387c478bd9Sstevel@tonic-gate } 41397c478bd9Sstevel@tonic-gate 41407c478bd9Sstevel@tonic-gate static void 41417c478bd9Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp) 41427c478bd9Sstevel@tonic-gate { 41437c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)q->q_ptr; 41447c478bd9Sstevel@tonic-gate struct asycom *asy; 41457c478bd9Sstevel@tonic-gate struct iocblk *ip; 41467c478bd9Sstevel@tonic-gate struct copyresp *csp; 41477c478bd9Sstevel@tonic-gate #ifdef DEBUG 41487c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 41497c478bd9Sstevel@tonic-gate #endif 41507c478bd9Sstevel@tonic-gate 41517c478bd9Sstevel@tonic-gate asy = async->async_common; 41527c478bd9Sstevel@tonic-gate ip = (struct iocblk *)mp->b_rptr; 41537c478bd9Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr; 41547c478bd9Sstevel@tonic-gate 41557c478bd9Sstevel@tonic-gate if (csp->cp_rval != 0) { 41567c478bd9Sstevel@tonic-gate if (csp->cp_private) 41577c478bd9Sstevel@tonic-gate freemsg(csp->cp_private); 41587c478bd9Sstevel@tonic-gate freemsg(mp); 41597c478bd9Sstevel@tonic-gate return; 41607c478bd9Sstevel@tonic-gate } 41617c478bd9Sstevel@tonic-gate 41627c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 41637c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n", 41647c478bd9Sstevel@tonic-gate instance, 41657c478bd9Sstevel@tonic-gate csp->cp_cmd == TIOCMGET ? "TIOCMGET" : 41667c478bd9Sstevel@tonic-gate csp->cp_cmd == TIOCMSET ? "TIOCMSET" : 41677c478bd9Sstevel@tonic-gate csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : 41687c478bd9Sstevel@tonic-gate "TIOCMBIC"); 41697c478bd9Sstevel@tonic-gate switch (csp->cp_cmd) { 41707c478bd9Sstevel@tonic-gate 41717c478bd9Sstevel@tonic-gate case TIOCMGET: 41727c478bd9Sstevel@tonic-gate if (mp->b_cont) { 41737c478bd9Sstevel@tonic-gate freemsg(mp->b_cont); 41747c478bd9Sstevel@tonic-gate mp->b_cont = NULL; 41757c478bd9Sstevel@tonic-gate } 41767c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 41777c478bd9Sstevel@tonic-gate ip->ioc_error = 0; 41787c478bd9Sstevel@tonic-gate ip->ioc_count = 0; 41797c478bd9Sstevel@tonic-gate ip->ioc_rval = 0; 41807c478bd9Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 41817c478bd9Sstevel@tonic-gate break; 41827c478bd9Sstevel@tonic-gate 41837c478bd9Sstevel@tonic-gate case TIOCMSET: 41847c478bd9Sstevel@tonic-gate case TIOCMBIS: 41857c478bd9Sstevel@tonic-gate case TIOCMBIC: 41867c478bd9Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 41872df1fe9cSrandyf (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 41887c478bd9Sstevel@tonic-gate csp->cp_cmd); 41897c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 41907c478bd9Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 41917c478bd9Sstevel@tonic-gate break; 41927c478bd9Sstevel@tonic-gate 41937c478bd9Sstevel@tonic-gate default: 41947c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 41957c478bd9Sstevel@tonic-gate ip->ioc_error = EINVAL; 41967c478bd9Sstevel@tonic-gate break; 41977c478bd9Sstevel@tonic-gate } 41987c478bd9Sstevel@tonic-gate qreply(q, mp); 41997c478bd9Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 42007c478bd9Sstevel@tonic-gate } 42017c478bd9Sstevel@tonic-gate 42027c478bd9Sstevel@tonic-gate /* 42037c478bd9Sstevel@tonic-gate * debugger/console support routines. 42047c478bd9Sstevel@tonic-gate */ 42057c478bd9Sstevel@tonic-gate 42067c478bd9Sstevel@tonic-gate /* 42077c478bd9Sstevel@tonic-gate * put a character out 42087c478bd9Sstevel@tonic-gate * Do not use interrupts. If char is LF, put out CR, LF. 42097c478bd9Sstevel@tonic-gate */ 42107c478bd9Sstevel@tonic-gate static void 4211281f0747Slt200341 asyputchar(cons_polledio_arg_t arg, uchar_t c) 42127c478bd9Sstevel@tonic-gate { 42137c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 42147c478bd9Sstevel@tonic-gate 42157c478bd9Sstevel@tonic-gate if (c == '\n') 42167c478bd9Sstevel@tonic-gate asyputchar(arg, '\r'); 42177c478bd9Sstevel@tonic-gate 42184ab75253Smrj while ((ddi_get8(asy->asy_iohandle, 42197c478bd9Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XHRE) == 0) { 42207c478bd9Sstevel@tonic-gate /* wait for xmit to finish */ 42217c478bd9Sstevel@tonic-gate drv_usecwait(10); 42227c478bd9Sstevel@tonic-gate } 42237c478bd9Sstevel@tonic-gate 42247c478bd9Sstevel@tonic-gate /* put the character out */ 42254ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c); 42267c478bd9Sstevel@tonic-gate } 42277c478bd9Sstevel@tonic-gate 42287c478bd9Sstevel@tonic-gate /* 42297c478bd9Sstevel@tonic-gate * See if there's a character available. If no character is 42307c478bd9Sstevel@tonic-gate * available, return 0. Run in polled mode, no interrupts. 42317c478bd9Sstevel@tonic-gate */ 42327c478bd9Sstevel@tonic-gate static boolean_t 4233281f0747Slt200341 asyischar(cons_polledio_arg_t arg) 42347c478bd9Sstevel@tonic-gate { 42357c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 42367c478bd9Sstevel@tonic-gate 42372df1fe9cSrandyf return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA) 42382df1fe9cSrandyf != 0); 42397c478bd9Sstevel@tonic-gate } 42407c478bd9Sstevel@tonic-gate 42417c478bd9Sstevel@tonic-gate /* 42427c478bd9Sstevel@tonic-gate * Get a character. Run in polled mode, no interrupts. 42437c478bd9Sstevel@tonic-gate */ 42447c478bd9Sstevel@tonic-gate static int 4245281f0747Slt200341 asygetchar(cons_polledio_arg_t arg) 42467c478bd9Sstevel@tonic-gate { 42477c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 42487c478bd9Sstevel@tonic-gate 42497c478bd9Sstevel@tonic-gate while (!asyischar(arg)) 42507c478bd9Sstevel@tonic-gate drv_usecwait(10); 42512df1fe9cSrandyf return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT)); 42527c478bd9Sstevel@tonic-gate } 42537c478bd9Sstevel@tonic-gate 42547c478bd9Sstevel@tonic-gate /* 42557c478bd9Sstevel@tonic-gate * Set or get the modem control status. 42567c478bd9Sstevel@tonic-gate */ 42577c478bd9Sstevel@tonic-gate static int 42587c478bd9Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how) 42597c478bd9Sstevel@tonic-gate { 42607c478bd9Sstevel@tonic-gate int mcr_r, msr_r; 42617c478bd9Sstevel@tonic-gate int instance = asy->asy_unit; 42627c478bd9Sstevel@tonic-gate 42637c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 42647c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 42657c478bd9Sstevel@tonic-gate 42667c478bd9Sstevel@tonic-gate /* Read Modem Control Registers */ 42674ab75253Smrj mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 42687c478bd9Sstevel@tonic-gate 42697c478bd9Sstevel@tonic-gate switch (how) { 42707c478bd9Sstevel@tonic-gate 42717c478bd9Sstevel@tonic-gate case TIOCMSET: 42727c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 42737c478bd9Sstevel@tonic-gate "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); 42747c478bd9Sstevel@tonic-gate mcr_r = bits; /* Set bits */ 42757c478bd9Sstevel@tonic-gate break; 42767c478bd9Sstevel@tonic-gate 42777c478bd9Sstevel@tonic-gate case TIOCMBIS: 42787c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n", 42797c478bd9Sstevel@tonic-gate instance, bits); 42807c478bd9Sstevel@tonic-gate mcr_r |= bits; /* Mask in bits */ 42817c478bd9Sstevel@tonic-gate break; 42827c478bd9Sstevel@tonic-gate 42837c478bd9Sstevel@tonic-gate case TIOCMBIC: 42847c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n", 42857c478bd9Sstevel@tonic-gate instance, bits); 42867c478bd9Sstevel@tonic-gate mcr_r &= ~bits; /* Mask out bits */ 42877c478bd9Sstevel@tonic-gate break; 42887c478bd9Sstevel@tonic-gate 42897c478bd9Sstevel@tonic-gate case TIOCMGET: 42907c478bd9Sstevel@tonic-gate /* Read Modem Status Registers */ 42917c478bd9Sstevel@tonic-gate /* 42927c478bd9Sstevel@tonic-gate * If modem interrupts are enabled, we return the 42937c478bd9Sstevel@tonic-gate * saved value of msr. We read MSR only in async_msint() 42947c478bd9Sstevel@tonic-gate */ 42954ab75253Smrj if (ddi_get8(asy->asy_iohandle, 42967c478bd9Sstevel@tonic-gate asy->asy_ioaddr + ICR) & MIEN) { 42977c478bd9Sstevel@tonic-gate msr_r = asy->asy_msr; 42987c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 42997c478bd9Sstevel@tonic-gate "asy%dmctl: TIOCMGET, read msr_r = %x\n", 43007c478bd9Sstevel@tonic-gate instance, msr_r); 43017c478bd9Sstevel@tonic-gate } else { 43024ab75253Smrj msr_r = ddi_get8(asy->asy_iohandle, 43037c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MSR); 43047c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 43057c478bd9Sstevel@tonic-gate "asy%dmctl: TIOCMGET, read MSR = %x\n", 43067c478bd9Sstevel@tonic-gate instance, msr_r); 43077c478bd9Sstevel@tonic-gate } 43087c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n", 43097c478bd9Sstevel@tonic-gate instance, asytodm(mcr_r, msr_r)); 43107c478bd9Sstevel@tonic-gate return (asytodm(mcr_r, msr_r)); 43117c478bd9Sstevel@tonic-gate } 43127c478bd9Sstevel@tonic-gate 43134ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r); 43147c478bd9Sstevel@tonic-gate 43157c478bd9Sstevel@tonic-gate return (mcr_r); 43167c478bd9Sstevel@tonic-gate } 43177c478bd9Sstevel@tonic-gate 43187c478bd9Sstevel@tonic-gate static int 43197c478bd9Sstevel@tonic-gate asytodm(int mcr_r, int msr_r) 43207c478bd9Sstevel@tonic-gate { 43217c478bd9Sstevel@tonic-gate int b = 0; 43227c478bd9Sstevel@tonic-gate 43237c478bd9Sstevel@tonic-gate /* MCR registers */ 43247c478bd9Sstevel@tonic-gate if (mcr_r & RTS) 43257c478bd9Sstevel@tonic-gate b |= TIOCM_RTS; 43267c478bd9Sstevel@tonic-gate 43277c478bd9Sstevel@tonic-gate if (mcr_r & DTR) 43287c478bd9Sstevel@tonic-gate b |= TIOCM_DTR; 43297c478bd9Sstevel@tonic-gate 43307c478bd9Sstevel@tonic-gate /* MSR registers */ 43317c478bd9Sstevel@tonic-gate if (msr_r & DCD) 43327c478bd9Sstevel@tonic-gate b |= TIOCM_CAR; 43337c478bd9Sstevel@tonic-gate 43347c478bd9Sstevel@tonic-gate if (msr_r & CTS) 43357c478bd9Sstevel@tonic-gate b |= TIOCM_CTS; 43367c478bd9Sstevel@tonic-gate 43377c478bd9Sstevel@tonic-gate if (msr_r & DSR) 43387c478bd9Sstevel@tonic-gate b |= TIOCM_DSR; 43397c478bd9Sstevel@tonic-gate 43407c478bd9Sstevel@tonic-gate if (msr_r & RI) 43417c478bd9Sstevel@tonic-gate b |= TIOCM_RNG; 43427c478bd9Sstevel@tonic-gate return (b); 43437c478bd9Sstevel@tonic-gate } 43447c478bd9Sstevel@tonic-gate 43457c478bd9Sstevel@tonic-gate static int 43467c478bd9Sstevel@tonic-gate dmtoasy(int bits) 43477c478bd9Sstevel@tonic-gate { 43487c478bd9Sstevel@tonic-gate int b = 0; 43497c478bd9Sstevel@tonic-gate 43507c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits); 43517c478bd9Sstevel@tonic-gate #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 43527c478bd9Sstevel@tonic-gate if (bits & TIOCM_CAR) 43537c478bd9Sstevel@tonic-gate b |= DCD; 43547c478bd9Sstevel@tonic-gate if (bits & TIOCM_CTS) 43557c478bd9Sstevel@tonic-gate b |= CTS; 43567c478bd9Sstevel@tonic-gate if (bits & TIOCM_DSR) 43577c478bd9Sstevel@tonic-gate b |= DSR; 43587c478bd9Sstevel@tonic-gate if (bits & TIOCM_RNG) 43597c478bd9Sstevel@tonic-gate b |= RI; 43607c478bd9Sstevel@tonic-gate #endif 43617c478bd9Sstevel@tonic-gate 43627c478bd9Sstevel@tonic-gate if (bits & TIOCM_RTS) { 43637c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n"); 43647c478bd9Sstevel@tonic-gate b |= RTS; 43657c478bd9Sstevel@tonic-gate } 43667c478bd9Sstevel@tonic-gate if (bits & TIOCM_DTR) { 43677c478bd9Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n"); 43687c478bd9Sstevel@tonic-gate b |= DTR; 43697c478bd9Sstevel@tonic-gate } 43707c478bd9Sstevel@tonic-gate 43717c478bd9Sstevel@tonic-gate return (b); 43727c478bd9Sstevel@tonic-gate } 43737c478bd9Sstevel@tonic-gate 43747c478bd9Sstevel@tonic-gate static void 43757c478bd9Sstevel@tonic-gate asyerror(int level, const char *fmt, ...) 43767c478bd9Sstevel@tonic-gate { 43777c478bd9Sstevel@tonic-gate va_list adx; 43787c478bd9Sstevel@tonic-gate static time_t last; 43797c478bd9Sstevel@tonic-gate static const char *lastfmt; 43807c478bd9Sstevel@tonic-gate time_t now; 43817c478bd9Sstevel@tonic-gate 43827c478bd9Sstevel@tonic-gate /* 43837c478bd9Sstevel@tonic-gate * Don't print the same error message too often. 43847c478bd9Sstevel@tonic-gate * Print the message only if we have not printed the 43857c478bd9Sstevel@tonic-gate * message within the last second. 43867c478bd9Sstevel@tonic-gate * Note: that fmt cannot be a pointer to a string 43877c478bd9Sstevel@tonic-gate * stored on the stack. The fmt pointer 43887c478bd9Sstevel@tonic-gate * must be in the data segment otherwise lastfmt would point 43897c478bd9Sstevel@tonic-gate * to non-sense. 43907c478bd9Sstevel@tonic-gate */ 43917c478bd9Sstevel@tonic-gate now = gethrestime_sec(); 43927c478bd9Sstevel@tonic-gate if (last == now && lastfmt == fmt) 43937c478bd9Sstevel@tonic-gate return; 43947c478bd9Sstevel@tonic-gate 43957c478bd9Sstevel@tonic-gate last = now; 43967c478bd9Sstevel@tonic-gate lastfmt = fmt; 43977c478bd9Sstevel@tonic-gate 43987c478bd9Sstevel@tonic-gate va_start(adx, fmt); 43997c478bd9Sstevel@tonic-gate vcmn_err(level, fmt, adx); 44007c478bd9Sstevel@tonic-gate va_end(adx); 44017c478bd9Sstevel@tonic-gate } 44027c478bd9Sstevel@tonic-gate 44037c478bd9Sstevel@tonic-gate /* 44047c478bd9Sstevel@tonic-gate * asy_parse_mode(dev_info_t *devi, struct asycom *asy) 44057c478bd9Sstevel@tonic-gate * The value of this property is in the form of "9600,8,n,1,-" 44067c478bd9Sstevel@tonic-gate * 1) speed: 9600, 4800, ... 44077c478bd9Sstevel@tonic-gate * 2) data bits 44087c478bd9Sstevel@tonic-gate * 3) parity: n(none), e(even), o(odd) 44097c478bd9Sstevel@tonic-gate * 4) stop bits 44107c478bd9Sstevel@tonic-gate * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off) 44117c478bd9Sstevel@tonic-gate * 44127c478bd9Sstevel@tonic-gate * This parsing came from a SPARCstation eeprom. 44137c478bd9Sstevel@tonic-gate */ 44147c478bd9Sstevel@tonic-gate static void 44157c478bd9Sstevel@tonic-gate asy_parse_mode(dev_info_t *devi, struct asycom *asy) 44167c478bd9Sstevel@tonic-gate { 44177c478bd9Sstevel@tonic-gate char name[40]; 44187c478bd9Sstevel@tonic-gate char val[40]; 44197c478bd9Sstevel@tonic-gate int len; 44207c478bd9Sstevel@tonic-gate int ret; 44217c478bd9Sstevel@tonic-gate char *p; 44227c478bd9Sstevel@tonic-gate char *p1; 44237c478bd9Sstevel@tonic-gate 44247c478bd9Sstevel@tonic-gate ASSERT(asy->asy_com_port != 0); 44257c478bd9Sstevel@tonic-gate 44267c478bd9Sstevel@tonic-gate /* 44277c478bd9Sstevel@tonic-gate * Parse the ttyx-mode property 44287c478bd9Sstevel@tonic-gate */ 44297c478bd9Sstevel@tonic-gate (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1); 44307c478bd9Sstevel@tonic-gate len = sizeof (val); 44317c478bd9Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 44327c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 44337c478bd9Sstevel@tonic-gate (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0'); 44347c478bd9Sstevel@tonic-gate len = sizeof (val); 44357c478bd9Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 44367c478bd9Sstevel@tonic-gate } 44377c478bd9Sstevel@tonic-gate 44387c478bd9Sstevel@tonic-gate /* no property to parse */ 44397c478bd9Sstevel@tonic-gate asy->asy_cflag = 0; 44407c478bd9Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 44417c478bd9Sstevel@tonic-gate return; 44427c478bd9Sstevel@tonic-gate 44437c478bd9Sstevel@tonic-gate p = val; 44447c478bd9Sstevel@tonic-gate /* ---- baud rate ---- */ 44457c478bd9Sstevel@tonic-gate asy->asy_cflag = CREAD|B9600; /* initial default */ 44467c478bd9Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 44477c478bd9Sstevel@tonic-gate *p1++ = '\0'; 44487c478bd9Sstevel@tonic-gate } else { 44497c478bd9Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 44507c478bd9Sstevel@tonic-gate return; 44517c478bd9Sstevel@tonic-gate } 44527c478bd9Sstevel@tonic-gate 44537c478bd9Sstevel@tonic-gate if (strcmp(p, "110") == 0) 44547c478bd9Sstevel@tonic-gate asy->asy_bidx = B110; 44557c478bd9Sstevel@tonic-gate else if (strcmp(p, "150") == 0) 44567c478bd9Sstevel@tonic-gate asy->asy_bidx = B150; 44577c478bd9Sstevel@tonic-gate else if (strcmp(p, "300") == 0) 44587c478bd9Sstevel@tonic-gate asy->asy_bidx = B300; 44597c478bd9Sstevel@tonic-gate else if (strcmp(p, "600") == 0) 44607c478bd9Sstevel@tonic-gate asy->asy_bidx = B600; 44617c478bd9Sstevel@tonic-gate else if (strcmp(p, "1200") == 0) 44627c478bd9Sstevel@tonic-gate asy->asy_bidx = B1200; 44637c478bd9Sstevel@tonic-gate else if (strcmp(p, "2400") == 0) 44647c478bd9Sstevel@tonic-gate asy->asy_bidx = B2400; 44657c478bd9Sstevel@tonic-gate else if (strcmp(p, "4800") == 0) 44667c478bd9Sstevel@tonic-gate asy->asy_bidx = B4800; 44677c478bd9Sstevel@tonic-gate else if (strcmp(p, "9600") == 0) 44687c478bd9Sstevel@tonic-gate asy->asy_bidx = B9600; 44697c478bd9Sstevel@tonic-gate else if (strcmp(p, "19200") == 0) 44707c478bd9Sstevel@tonic-gate asy->asy_bidx = B19200; 44717c478bd9Sstevel@tonic-gate else if (strcmp(p, "38400") == 0) 44727c478bd9Sstevel@tonic-gate asy->asy_bidx = B38400; 44737c478bd9Sstevel@tonic-gate else if (strcmp(p, "57600") == 0) 44747c478bd9Sstevel@tonic-gate asy->asy_bidx = B57600; 44757c478bd9Sstevel@tonic-gate else if (strcmp(p, "115200") == 0) 44767c478bd9Sstevel@tonic-gate asy->asy_bidx = B115200; 44777c478bd9Sstevel@tonic-gate else 44787c478bd9Sstevel@tonic-gate asy->asy_bidx = B9600; 44797c478bd9Sstevel@tonic-gate 44807c478bd9Sstevel@tonic-gate asy->asy_cflag &= ~CBAUD; 44817c478bd9Sstevel@tonic-gate if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */ 44827c478bd9Sstevel@tonic-gate asy->asy_cflag |= CBAUDEXT; 44837c478bd9Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx - CBAUD - 1; 44847c478bd9Sstevel@tonic-gate } else { 44857c478bd9Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx; 44867c478bd9Sstevel@tonic-gate } 44877c478bd9Sstevel@tonic-gate 44887c478bd9Sstevel@tonic-gate ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag)); 44897c478bd9Sstevel@tonic-gate 44907c478bd9Sstevel@tonic-gate /* ---- Next item is data bits ---- */ 44917c478bd9Sstevel@tonic-gate p = p1; 44927c478bd9Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 44937c478bd9Sstevel@tonic-gate *p1++ = '\0'; 44947c478bd9Sstevel@tonic-gate } else { 44957c478bd9Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 44967c478bd9Sstevel@tonic-gate return; 44977c478bd9Sstevel@tonic-gate } 44987c478bd9Sstevel@tonic-gate switch (*p) { 44997c478bd9Sstevel@tonic-gate default: 45007c478bd9Sstevel@tonic-gate case '8': 45017c478bd9Sstevel@tonic-gate asy->asy_cflag |= CS8; 45027c478bd9Sstevel@tonic-gate asy->asy_lcr = BITS8; 45037c478bd9Sstevel@tonic-gate break; 45047c478bd9Sstevel@tonic-gate case '7': 45057c478bd9Sstevel@tonic-gate asy->asy_cflag |= CS7; 45067c478bd9Sstevel@tonic-gate asy->asy_lcr = BITS7; 45077c478bd9Sstevel@tonic-gate break; 45087c478bd9Sstevel@tonic-gate case '6': 45097c478bd9Sstevel@tonic-gate asy->asy_cflag |= CS6; 45107c478bd9Sstevel@tonic-gate asy->asy_lcr = BITS6; 45117c478bd9Sstevel@tonic-gate break; 45127c478bd9Sstevel@tonic-gate case '5': 45137c478bd9Sstevel@tonic-gate /* LINTED: CS5 is currently zero (but might change) */ 45147c478bd9Sstevel@tonic-gate asy->asy_cflag |= CS5; 45157c478bd9Sstevel@tonic-gate asy->asy_lcr = BITS5; 45167c478bd9Sstevel@tonic-gate break; 45177c478bd9Sstevel@tonic-gate } 45187c478bd9Sstevel@tonic-gate 45197c478bd9Sstevel@tonic-gate /* ---- Parity info ---- */ 45207c478bd9Sstevel@tonic-gate p = p1; 45217c478bd9Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 45227c478bd9Sstevel@tonic-gate *p1++ = '\0'; 45237c478bd9Sstevel@tonic-gate } else { 45247c478bd9Sstevel@tonic-gate return; 45257c478bd9Sstevel@tonic-gate } 45267c478bd9Sstevel@tonic-gate switch (*p) { 45277c478bd9Sstevel@tonic-gate default: 45287c478bd9Sstevel@tonic-gate case 'n': 45297c478bd9Sstevel@tonic-gate break; 45307c478bd9Sstevel@tonic-gate case 'e': 45317c478bd9Sstevel@tonic-gate asy->asy_cflag |= PARENB; 45327c478bd9Sstevel@tonic-gate asy->asy_lcr |= PEN; break; 45337c478bd9Sstevel@tonic-gate case 'o': 45347c478bd9Sstevel@tonic-gate asy->asy_cflag |= PARENB|PARODD; 45357c478bd9Sstevel@tonic-gate asy->asy_lcr |= PEN|EPS; 45367c478bd9Sstevel@tonic-gate break; 45377c478bd9Sstevel@tonic-gate } 45387c478bd9Sstevel@tonic-gate 45397c478bd9Sstevel@tonic-gate /* ---- Find stop bits ---- */ 45407c478bd9Sstevel@tonic-gate p = p1; 45417c478bd9Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 45427c478bd9Sstevel@tonic-gate *p1++ = '\0'; 45437c478bd9Sstevel@tonic-gate } else { 45447c478bd9Sstevel@tonic-gate return; 45457c478bd9Sstevel@tonic-gate } 45467c478bd9Sstevel@tonic-gate if (*p == '2') { 45477c478bd9Sstevel@tonic-gate asy->asy_cflag |= CSTOPB; 45487c478bd9Sstevel@tonic-gate asy->asy_lcr |= STB; 45497c478bd9Sstevel@tonic-gate } 45507c478bd9Sstevel@tonic-gate 45517c478bd9Sstevel@tonic-gate /* ---- handshake is next ---- */ 45527c478bd9Sstevel@tonic-gate p = p1; 45537c478bd9Sstevel@tonic-gate if (p) { 45547c478bd9Sstevel@tonic-gate if ((p1 = strchr(p, ',')) != 0) 45557c478bd9Sstevel@tonic-gate *p1++ = '\0'; 45567c478bd9Sstevel@tonic-gate 45577c478bd9Sstevel@tonic-gate if (*p == 'h') 45587c478bd9Sstevel@tonic-gate asy->asy_cflag |= CRTSCTS; 45597c478bd9Sstevel@tonic-gate else if (*p == 's') 45607c478bd9Sstevel@tonic-gate asy->asy_cflag |= CRTSXOFF; 45617c478bd9Sstevel@tonic-gate } 45627c478bd9Sstevel@tonic-gate } 45637c478bd9Sstevel@tonic-gate 45647c478bd9Sstevel@tonic-gate /* 45657c478bd9Sstevel@tonic-gate * Check for abort character sequence 45667c478bd9Sstevel@tonic-gate */ 45677c478bd9Sstevel@tonic-gate static boolean_t 45687c478bd9Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch) 45697c478bd9Sstevel@tonic-gate { 45707c478bd9Sstevel@tonic-gate static int state = 0; 45717c478bd9Sstevel@tonic-gate #define CNTRL(c) ((c)&037) 45727c478bd9Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') }; 45737c478bd9Sstevel@tonic-gate 45747c478bd9Sstevel@tonic-gate if (ch == sequence[state]) { 45757c478bd9Sstevel@tonic-gate if (++state >= sizeof (sequence)) { 45767c478bd9Sstevel@tonic-gate state = 0; 45777c478bd9Sstevel@tonic-gate return (B_TRUE); 45787c478bd9Sstevel@tonic-gate } 45797c478bd9Sstevel@tonic-gate } else { 45807c478bd9Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0; 45817c478bd9Sstevel@tonic-gate } 45827c478bd9Sstevel@tonic-gate return (B_FALSE); 45837c478bd9Sstevel@tonic-gate } 45847c478bd9Sstevel@tonic-gate 45857c478bd9Sstevel@tonic-gate /* 45867c478bd9Sstevel@tonic-gate * Flow control functions 45877c478bd9Sstevel@tonic-gate */ 45887c478bd9Sstevel@tonic-gate /* 45897c478bd9Sstevel@tonic-gate * Software input flow control 45907c478bd9Sstevel@tonic-gate * This function can execute software input flow control sucessfully 45917c478bd9Sstevel@tonic-gate * at most of situations except that the line is in BREAK status 45927c478bd9Sstevel@tonic-gate * (timed and untimed break). 45937c478bd9Sstevel@tonic-gate * INPUT VALUE of onoff: 45947c478bd9Sstevel@tonic-gate * FLOW_START means to send out a XON char 45957c478bd9Sstevel@tonic-gate * and clear SW input flow control flag. 45967c478bd9Sstevel@tonic-gate * FLOW_STOP means to send out a XOFF char 45977c478bd9Sstevel@tonic-gate * and set SW input flow control flag. 45987c478bd9Sstevel@tonic-gate * FLOW_CHECK means to check whether there is pending XON/XOFF 45997c478bd9Sstevel@tonic-gate * if it is true, send it out. 46007c478bd9Sstevel@tonic-gate * INPUT VALUE of type: 46017c478bd9Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 46027c478bd9Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 46037c478bd9Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 46047c478bd9Sstevel@tonic-gate * RETURN VALUE: B_FALSE means no flow control char is sent 46057c478bd9Sstevel@tonic-gate * B_TRUE means one flow control char is sent 46067c478bd9Sstevel@tonic-gate */ 46077c478bd9Sstevel@tonic-gate static boolean_t 46087c478bd9Sstevel@tonic-gate async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff, 46097c478bd9Sstevel@tonic-gate int type) 46107c478bd9Sstevel@tonic-gate { 46117c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 46127c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 46137c478bd9Sstevel@tonic-gate int rval = B_FALSE; 46147c478bd9Sstevel@tonic-gate 46157c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 46167c478bd9Sstevel@tonic-gate 46177c478bd9Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXOFF)) 46187c478bd9Sstevel@tonic-gate return (rval); 46197c478bd9Sstevel@tonic-gate 46207c478bd9Sstevel@tonic-gate /* 46217c478bd9Sstevel@tonic-gate * If we get this far, then we know IXOFF is set. 46227c478bd9Sstevel@tonic-gate */ 46237c478bd9Sstevel@tonic-gate switch (onoff) { 46247c478bd9Sstevel@tonic-gate case FLOW_STOP: 46257c478bd9Sstevel@tonic-gate async->async_inflow_source |= type; 46267c478bd9Sstevel@tonic-gate 46277c478bd9Sstevel@tonic-gate /* 46287c478bd9Sstevel@tonic-gate * We'll send an XOFF character for each of up to 46297c478bd9Sstevel@tonic-gate * three different input flow control attempts to stop input. 46307c478bd9Sstevel@tonic-gate * If we already send out one XOFF, but FLOW_STOP comes again, 46317c478bd9Sstevel@tonic-gate * it seems that input flow control becomes more serious, 46327c478bd9Sstevel@tonic-gate * then send XOFF again. 46337c478bd9Sstevel@tonic-gate */ 46347c478bd9Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 46357c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 46367c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_SW_IN_FLOW | 46377c478bd9Sstevel@tonic-gate ASYNC_SW_IN_NEEDED; 46387c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, " 46397c478bd9Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 46407c478bd9Sstevel@tonic-gate break; 46417c478bd9Sstevel@tonic-gate case FLOW_START: 46427c478bd9Sstevel@tonic-gate async->async_inflow_source &= ~type; 46437c478bd9Sstevel@tonic-gate if (async->async_inflow_source == 0) { 46447c478bd9Sstevel@tonic-gate async->async_flags = (async->async_flags & 46457c478bd9Sstevel@tonic-gate ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 46467c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: " 46477c478bd9Sstevel@tonic-gate "input sflow start\n", instance); 46487c478bd9Sstevel@tonic-gate } 46497c478bd9Sstevel@tonic-gate break; 46507c478bd9Sstevel@tonic-gate default: 46517c478bd9Sstevel@tonic-gate break; 46527c478bd9Sstevel@tonic-gate } 46537c478bd9Sstevel@tonic-gate 46547c478bd9Sstevel@tonic-gate if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK | 46557c478bd9Sstevel@tonic-gate ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) && 46564ab75253Smrj (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) { 46577c478bd9Sstevel@tonic-gate /* 46587c478bd9Sstevel@tonic-gate * If we get this far, then we know we need to send out 46597c478bd9Sstevel@tonic-gate * XON or XOFF char. 46607c478bd9Sstevel@tonic-gate */ 46617c478bd9Sstevel@tonic-gate async->async_flags = (async->async_flags & 46627c478bd9Sstevel@tonic-gate ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY; 46634ab75253Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 46647c478bd9Sstevel@tonic-gate async->async_flags & ASYNC_SW_IN_FLOW ? 46657c478bd9Sstevel@tonic-gate async->async_stopc : async->async_startc); 46667c478bd9Sstevel@tonic-gate rval = B_TRUE; 46677c478bd9Sstevel@tonic-gate } 46687c478bd9Sstevel@tonic-gate return (rval); 46697c478bd9Sstevel@tonic-gate } 46707c478bd9Sstevel@tonic-gate 46717c478bd9Sstevel@tonic-gate /* 46727c478bd9Sstevel@tonic-gate * Software output flow control 46737c478bd9Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 46747c478bd9Sstevel@tonic-gate * It does not handle HW, and just change the SW output flow control flag. 46757c478bd9Sstevel@tonic-gate * INPUT VALUE of onoff: 46767c478bd9Sstevel@tonic-gate * FLOW_START means to clear SW output flow control flag, 46777c478bd9Sstevel@tonic-gate * also combine with HW output flow control status to 46787c478bd9Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 46797c478bd9Sstevel@tonic-gate * FLOW_STOP means to set SW output flow control flag, 46807c478bd9Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 46817c478bd9Sstevel@tonic-gate */ 46827c478bd9Sstevel@tonic-gate static void 46837c478bd9Sstevel@tonic-gate async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff) 46847c478bd9Sstevel@tonic-gate { 46857c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 46867c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 46877c478bd9Sstevel@tonic-gate 46887c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 46897c478bd9Sstevel@tonic-gate 46907c478bd9Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXON)) 46917c478bd9Sstevel@tonic-gate return; 46927c478bd9Sstevel@tonic-gate 46937c478bd9Sstevel@tonic-gate switch (onoff) { 46947c478bd9Sstevel@tonic-gate case FLOW_STOP: 46957c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_SW_OUT_FLW; 46967c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 46977c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n", 46987c478bd9Sstevel@tonic-gate instance); 46997c478bd9Sstevel@tonic-gate break; 47007c478bd9Sstevel@tonic-gate case FLOW_START: 47017c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW; 47027c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_HW_OUT_FLW)) 47037c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 47047c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n", 47057c478bd9Sstevel@tonic-gate instance); 47067c478bd9Sstevel@tonic-gate break; 47077c478bd9Sstevel@tonic-gate default: 47087c478bd9Sstevel@tonic-gate break; 47097c478bd9Sstevel@tonic-gate } 47107c478bd9Sstevel@tonic-gate } 47117c478bd9Sstevel@tonic-gate 47127c478bd9Sstevel@tonic-gate /* 47137c478bd9Sstevel@tonic-gate * Hardware input flow control 47147c478bd9Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 47157c478bd9Sstevel@tonic-gate * It directly changes RTS depending on input parameter onoff. 47167c478bd9Sstevel@tonic-gate * INPUT VALUE of onoff: 47177c478bd9Sstevel@tonic-gate * FLOW_START means to clear HW input flow control flag, 47187c478bd9Sstevel@tonic-gate * and pull up RTS if it is low. 47197c478bd9Sstevel@tonic-gate * FLOW_STOP means to set HW input flow control flag, 47207c478bd9Sstevel@tonic-gate * and low RTS if it is high. 47217c478bd9Sstevel@tonic-gate * INPUT VALUE of type: 47227c478bd9Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 47237c478bd9Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 47247c478bd9Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 47257c478bd9Sstevel@tonic-gate */ 47267c478bd9Sstevel@tonic-gate static void 47277c478bd9Sstevel@tonic-gate async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff, 47287c478bd9Sstevel@tonic-gate int type) 47297c478bd9Sstevel@tonic-gate { 47307c478bd9Sstevel@tonic-gate uchar_t mcr; 47317c478bd9Sstevel@tonic-gate uchar_t flag; 47327c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 47337c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 47347c478bd9Sstevel@tonic-gate 47357c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 47367c478bd9Sstevel@tonic-gate 47377c478bd9Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSXOFF)) 47387c478bd9Sstevel@tonic-gate return; 47397c478bd9Sstevel@tonic-gate 47407c478bd9Sstevel@tonic-gate switch (onoff) { 47417c478bd9Sstevel@tonic-gate case FLOW_STOP: 47427c478bd9Sstevel@tonic-gate async->async_inflow_source |= type; 47437c478bd9Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 47447c478bd9Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 47457c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW; 47467c478bd9Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, " 47477c478bd9Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 47487c478bd9Sstevel@tonic-gate break; 47497c478bd9Sstevel@tonic-gate case FLOW_START: 47507c478bd9Sstevel@tonic-gate async->async_inflow_source &= ~type; 47517c478bd9Sstevel@tonic-gate if (async->async_inflow_source == 0) { 47527c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW; 47537c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: " 47547c478bd9Sstevel@tonic-gate "input hflow start\n", instance); 47557c478bd9Sstevel@tonic-gate } 47567c478bd9Sstevel@tonic-gate break; 47577c478bd9Sstevel@tonic-gate default: 47587c478bd9Sstevel@tonic-gate break; 47597c478bd9Sstevel@tonic-gate } 47604ab75253Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 47617c478bd9Sstevel@tonic-gate flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 47627c478bd9Sstevel@tonic-gate 47637c478bd9Sstevel@tonic-gate if (((mcr ^ flag) & RTS) != 0) { 47644ab75253Smrj ddi_put8(asy->asy_iohandle, 47657c478bd9Sstevel@tonic-gate asy->asy_ioaddr + MCR, (mcr ^ RTS)); 47667c478bd9Sstevel@tonic-gate } 47677c478bd9Sstevel@tonic-gate } 47687c478bd9Sstevel@tonic-gate 47697c478bd9Sstevel@tonic-gate /* 47707c478bd9Sstevel@tonic-gate * Hardware output flow control 47717c478bd9Sstevel@tonic-gate * This function can execute HW output flow control sucessfully 47727c478bd9Sstevel@tonic-gate * at any situation. 47737c478bd9Sstevel@tonic-gate * It doesn't really change RTS, and just change 47747c478bd9Sstevel@tonic-gate * HW output flow control flag depending on CTS status. 47757c478bd9Sstevel@tonic-gate * INPUT VALUE of onoff: 47767c478bd9Sstevel@tonic-gate * FLOW_START means to clear HW output flow control flag. 47777c478bd9Sstevel@tonic-gate * also combine with SW output flow control status to 47787c478bd9Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 47797c478bd9Sstevel@tonic-gate * FLOW_STOP means to set HW output flow control flag. 47807c478bd9Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 47817c478bd9Sstevel@tonic-gate */ 47827c478bd9Sstevel@tonic-gate static void 47837c478bd9Sstevel@tonic-gate async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff) 47847c478bd9Sstevel@tonic-gate { 47857c478bd9Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 47867c478bd9Sstevel@tonic-gate int instance = UNIT(async->async_dev); 47877c478bd9Sstevel@tonic-gate 47887c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 47897c478bd9Sstevel@tonic-gate 47907c478bd9Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSCTS)) 47917c478bd9Sstevel@tonic-gate return; 47927c478bd9Sstevel@tonic-gate 47937c478bd9Sstevel@tonic-gate switch (onoff) { 47947c478bd9Sstevel@tonic-gate case FLOW_STOP: 47957c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_OUT_FLW; 47967c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 47977c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n", 47987c478bd9Sstevel@tonic-gate instance); 47997c478bd9Sstevel@tonic-gate break; 48007c478bd9Sstevel@tonic-gate case FLOW_START: 48017c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW; 48027c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_SW_OUT_FLW)) 48037c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 48047c478bd9Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n", 48057c478bd9Sstevel@tonic-gate instance); 48067c478bd9Sstevel@tonic-gate break; 48077c478bd9Sstevel@tonic-gate default: 48087c478bd9Sstevel@tonic-gate break; 48097c478bd9Sstevel@tonic-gate } 48107c478bd9Sstevel@tonic-gate } 481119397407SSherry Moore 481219397407SSherry Moore 481319397407SSherry Moore /* 481419397407SSherry Moore * quiesce(9E) entry point. 481519397407SSherry Moore * 481619397407SSherry Moore * This function is called when the system is single-threaded at high 481719397407SSherry Moore * PIL with preemption disabled. Therefore, this function must not be 481819397407SSherry Moore * blocked. 481919397407SSherry Moore * 482019397407SSherry Moore * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 482119397407SSherry Moore * DDI_FAILURE indicates an error condition and should almost never happen. 482219397407SSherry Moore */ 482319397407SSherry Moore static int 482419397407SSherry Moore asyquiesce(dev_info_t *devi) 482519397407SSherry Moore { 482619397407SSherry Moore int instance; 482719397407SSherry Moore struct asycom *asy; 482819397407SSherry Moore 482919397407SSherry Moore instance = ddi_get_instance(devi); /* find out which unit */ 483019397407SSherry Moore 483119397407SSherry Moore asy = ddi_get_soft_state(asy_soft_state, instance); 483219397407SSherry Moore if (asy == NULL) 483319397407SSherry Moore return (DDI_FAILURE); 483419397407SSherry Moore 483519397407SSherry Moore /* disable all interrupts */ 483619397407SSherry Moore ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 483719397407SSherry Moore 483819397407SSherry Moore /* reset the FIFO */ 483919397407SSherry Moore asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 484019397407SSherry Moore 484119397407SSherry Moore return (DDI_SUCCESS); 484219397407SSherry Moore } 4843