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