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