xref: /illumos-gate/usr/src/uts/sun4/io/su_driver.c (revision e476cc14bc84d0f0ee2be4b969d558ef54fd2d8f)
1d3cf9c7dSdf157793 /*
2d3cf9c7dSdf157793  * CDDL HEADER START
3d3cf9c7dSdf157793  *
4d3cf9c7dSdf157793  * The contents of this file are subject to the terms of the
57cb42c7eSrameshc  * Common Development and Distribution License (the "License").
67cb42c7eSrameshc  * You may not use this file except in compliance with the License.
7d3cf9c7dSdf157793  *
8d3cf9c7dSdf157793  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d3cf9c7dSdf157793  * or http://www.opensolaris.org/os/licensing.
10d3cf9c7dSdf157793  * See the License for the specific language governing permissions
11d3cf9c7dSdf157793  * and limitations under the License.
12d3cf9c7dSdf157793  *
13d3cf9c7dSdf157793  * When distributing Covered Code, include this CDDL HEADER in each
14d3cf9c7dSdf157793  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d3cf9c7dSdf157793  * If applicable, add the following below this CDDL HEADER, with the
16d3cf9c7dSdf157793  * fields enclosed by brackets "[]" replaced with your own identifying
17d3cf9c7dSdf157793  * information: Portions Copyright [yyyy] [name of copyright owner]
18d3cf9c7dSdf157793  *
19d3cf9c7dSdf157793  * CDDL HEADER END
20d3cf9c7dSdf157793  */
21d3cf9c7dSdf157793 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
22d3cf9c7dSdf157793 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
23d3cf9c7dSdf157793 /*	  All Rights Reserved					*/
24d3cf9c7dSdf157793 
25d3cf9c7dSdf157793 /*
264d0b1b0dSAn Bui  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27d3cf9c7dSdf157793  * Use is subject to license terms.
2848bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
29730a650aSPeter Tribble  * Copyright (c) 2019 Peter Tribble.
30d3cf9c7dSdf157793  */
31d3cf9c7dSdf157793 
32d3cf9c7dSdf157793 
33d3cf9c7dSdf157793 /*
34f63f7506Sanovick  *	Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips.
35d3cf9c7dSdf157793  *	Modified as sparc keyboard/mouse driver.
36d3cf9c7dSdf157793  */
37d3cf9c7dSdf157793 #define	SU_REGISTER_FILE_NO 0
38d3cf9c7dSdf157793 #define	SU_REGOFFSET 0
39d3cf9c7dSdf157793 #define	SU_REGISTER_LEN 8
40d3cf9c7dSdf157793 
41d3cf9c7dSdf157793 #include <sys/param.h>
42d3cf9c7dSdf157793 #include <sys/types.h>
43d3cf9c7dSdf157793 #include <sys/signal.h>
44d3cf9c7dSdf157793 #include <sys/stream.h>
45d3cf9c7dSdf157793 #include <sys/termio.h>
46d3cf9c7dSdf157793 #include <sys/errno.h>
47d3cf9c7dSdf157793 #include <sys/file.h>
48d3cf9c7dSdf157793 #include <sys/cmn_err.h>
49d3cf9c7dSdf157793 #include <sys/stropts.h>
50d3cf9c7dSdf157793 #include <sys/strsubr.h>
51d3cf9c7dSdf157793 #include <sys/strsun.h>
52d3cf9c7dSdf157793 #include <sys/strtty.h>
53d3cf9c7dSdf157793 #include <sys/debug.h>
54d3cf9c7dSdf157793 #include <sys/kbio.h>
55d3cf9c7dSdf157793 #include <sys/cred.h>
56d3cf9c7dSdf157793 #include <sys/modctl.h>
57d3cf9c7dSdf157793 #include <sys/stat.h>
58d3cf9c7dSdf157793 #include <sys/consdev.h>
59d3cf9c7dSdf157793 #include <sys/mkdev.h>
60d3cf9c7dSdf157793 #include <sys/kmem.h>
61d3cf9c7dSdf157793 #include <sys/cred.h>
62d3cf9c7dSdf157793 #ifdef DEBUG
63d3cf9c7dSdf157793 #include <sys/promif.h>
64d3cf9c7dSdf157793 #endif
65d3cf9c7dSdf157793 #include <sys/ddi.h>
66d3cf9c7dSdf157793 #include <sys/sunddi.h>
67d3cf9c7dSdf157793 #include <sys/sudev.h>
68d3cf9c7dSdf157793 #include <sys/note.h>
69d3cf9c7dSdf157793 #include <sys/timex.h>
70d3cf9c7dSdf157793 #include <sys/policy.h>
71d3cf9c7dSdf157793 
72d3cf9c7dSdf157793 #define	async_stopc	async_ttycommon.t_stopc
73d3cf9c7dSdf157793 #define	async_startc	async_ttycommon.t_startc
74d3cf9c7dSdf157793 
75d3cf9c7dSdf157793 #define	ASY_INIT	1
76d3cf9c7dSdf157793 #define	ASY_NOINIT	0
77d3cf9c7dSdf157793 
78d3cf9c7dSdf157793 #ifdef DEBUG
79d3cf9c7dSdf157793 #define	ASY_DEBUG_INIT	0x001
80d3cf9c7dSdf157793 #define	ASY_DEBUG_INPUT	0x002
81d3cf9c7dSdf157793 #define	ASY_DEBUG_EOT	0x004
82d3cf9c7dSdf157793 #define	ASY_DEBUG_CLOSE	0x008
83d3cf9c7dSdf157793 #define	ASY_DEBUG_HFLOW	0x010
84d3cf9c7dSdf157793 #define	ASY_DEBUG_PROCS	0x020
85d3cf9c7dSdf157793 #define	ASY_DEBUG_STATE	0x040
86d3cf9c7dSdf157793 #define	ASY_DEBUG_INTR	0x080
87d3cf9c7dSdf157793 static	int asydebug = 0;
88d3cf9c7dSdf157793 #endif
89d3cf9c7dSdf157793 static	int su_log = 0;
90d3cf9c7dSdf157793 
91d3cf9c7dSdf157793 int su_drain_check = 15000000;		/* tunable: exit drain check time */
92d3cf9c7dSdf157793 
93d3cf9c7dSdf157793 static	struct ppsclockev asy_ppsev;
94d3cf9c7dSdf157793 
95d3cf9c7dSdf157793 static	int max_asy_instance = -1;
96d3cf9c7dSdf157793 static	void	*su_asycom;	/* soft state asycom pointer */
97d3cf9c7dSdf157793 static	void	*su_asyncline;	/* soft state asyncline pointer */
98d3cf9c7dSdf157793 static	boolean_t abort_charseq_recognize(uchar_t ch);
99d3cf9c7dSdf157793 
100d3cf9c7dSdf157793 static	uint_t	asysoftintr(caddr_t intarg);
101d3cf9c7dSdf157793 static	uint_t	asyintr(caddr_t argasy);
102d3cf9c7dSdf157793 
103d3cf9c7dSdf157793 /* The async interrupt entry points */
104d3cf9c7dSdf157793 static void	async_txint(struct asycom *asy, uchar_t lsr);
105d3cf9c7dSdf157793 static void	async_rxint(struct asycom *asy, uchar_t lsr);
106d3cf9c7dSdf157793 static void	async_msint(struct asycom *asy);
107d3cf9c7dSdf157793 static int	async_softint(struct asycom *asy);
108d3cf9c7dSdf157793 
109d3cf9c7dSdf157793 static void	async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp,
110d3cf9c7dSdf157793     boolean_t iswput);
111d3cf9c7dSdf157793 static void	async_reioctl(void *);
112d3cf9c7dSdf157793 static void	async_iocdata(queue_t *q, mblk_t *mp);
113d3cf9c7dSdf157793 static void	async_restart(void *);
114d3cf9c7dSdf157793 static void	async_start(struct asyncline *async);
115d3cf9c7dSdf157793 static void	async_nstart(struct asyncline *async, int mode);
116d3cf9c7dSdf157793 static void	async_resume(struct asyncline *async);
117d3cf9c7dSdf157793 static int	asy_program(struct asycom *asy, int mode);
118d3cf9c7dSdf157793 
1190280efdcSzk194757 /* Polled mode functions */
1200280efdcSzk194757 static void	asyputchar(cons_polledio_arg_t, uchar_t c);
1210280efdcSzk194757 static int	asygetchar(cons_polledio_arg_t);
1220280efdcSzk194757 static boolean_t	asyischar(cons_polledio_arg_t);
1230280efdcSzk194757 static void	asy_polled_enter(cons_polledio_arg_t);
1240280efdcSzk194757 static void	asy_polled_exit(cons_polledio_arg_t);
1250280efdcSzk194757 
126d3cf9c7dSdf157793 static int	asymctl(struct asycom *, int, int);
127d3cf9c7dSdf157793 static int	asytodm(int, int);
128d3cf9c7dSdf157793 static int	dmtoasy(int);
129d3cf9c7dSdf157793 static void	asycheckflowcontrol_hw(struct asycom *asy);
130d3cf9c7dSdf157793 static boolean_t asycheckflowcontrol_sw(struct asycom *asy);
131d3cf9c7dSdf157793 static void	asy_ppsevent(struct asycom *asy, int msr);
132d3cf9c7dSdf157793 
133d3cf9c7dSdf157793 extern kcondvar_t lbolt_cv;
134d3cf9c7dSdf157793 extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
135d3cf9c7dSdf157793 		int spec_type, minor_t minor_num);
136d3cf9c7dSdf157793 
137d3cf9c7dSdf157793 
138d3cf9c7dSdf157793 /*
139d3cf9c7dSdf157793  * Baud rate table. Indexed by #defines found in sys/termios.h
140d3cf9c7dSdf157793  */
141d3cf9c7dSdf157793 ushort_t asyspdtab[] = {
142d3cf9c7dSdf157793 	0,	/* 0 baud rate */
143d3cf9c7dSdf157793 	0x900,	/* 50 baud rate */
144d3cf9c7dSdf157793 	0x600,	/* 75 baud rate */
145d3cf9c7dSdf157793 	0x417,	/* 110 baud rate (%0.026) */
146d3cf9c7dSdf157793 	0x359,	/* 134 baud rate (%0.058) */
147d3cf9c7dSdf157793 	0x300,	/* 150 baud rate */
148d3cf9c7dSdf157793 	0x240,	/* 200 baud rate */
149d3cf9c7dSdf157793 	0x180,	/* 300 baud rate */
150d3cf9c7dSdf157793 	0x0c0,	/* 600 baud rate */
151d3cf9c7dSdf157793 	0x060,	/* 1200 baud rate */
152d3cf9c7dSdf157793 	0x040,	/* 1800 baud rate */
153d3cf9c7dSdf157793 	0x030,	/* 2400 baud rate */
154d3cf9c7dSdf157793 	0x018,	/* 4800 baud rate */
155d3cf9c7dSdf157793 	0x00c,	/* 9600 baud rate */
156d3cf9c7dSdf157793 	0x006,	/* 19200 baud rate */
157d3cf9c7dSdf157793 	0x003,	/* 38400 baud rate */
158d3cf9c7dSdf157793 	0x002,	/* 57600 baud rate */
159d3cf9c7dSdf157793 	0,	/* 76800 baud rate - not supported */
160d3cf9c7dSdf157793 	0x001,	/* 115200 baud rate */
161d3cf9c7dSdf157793 	0,	/* 153600 baud rate - not supported */
162d3cf9c7dSdf157793 	0x8002,	/* 230400 baud rate - supported on specific platforms */
163d3cf9c7dSdf157793 	0,	/* 307200 baud rate - not supported */
164d3cf9c7dSdf157793 	0x8001	/* 460800 baud rate - supported on specific platforms */
165d3cf9c7dSdf157793 };
166d3cf9c7dSdf157793 
167d3cf9c7dSdf157793 /*
168d3cf9c7dSdf157793  * Number of speeds supported is the number of entries in
169d3cf9c7dSdf157793  * the above table.
170d3cf9c7dSdf157793  */
171d3cf9c7dSdf157793 #define	N_SU_SPEEDS	(sizeof (asyspdtab)/sizeof (ushort_t))
172d3cf9c7dSdf157793 
173d3cf9c7dSdf157793 /*
174d3cf9c7dSdf157793  * Human-readable baud rate table.
175d3cf9c7dSdf157793  * Indexed by #defines found in sys/termios.h
176d3cf9c7dSdf157793  */
177d3cf9c7dSdf157793 int baudtable[] = {
178d3cf9c7dSdf157793 	0,	/* 0 baud rate */
179d3cf9c7dSdf157793 	50,	/* 50 baud rate */
180d3cf9c7dSdf157793 	75,	/* 75 baud rate */
181d3cf9c7dSdf157793 	110,	/* 110 baud rate */
182d3cf9c7dSdf157793 	134,	/* 134 baud rate */
183d3cf9c7dSdf157793 	150,	/* 150 baud rate */
184d3cf9c7dSdf157793 	200,	/* 200 baud rate */
185d3cf9c7dSdf157793 	300,	/* 300 baud rate */
186d3cf9c7dSdf157793 	600,	/* 600 baud rate */
187d3cf9c7dSdf157793 	1200,	/* 1200 baud rate */
188d3cf9c7dSdf157793 	1800,	/* 1800 baud rate */
189d3cf9c7dSdf157793 	2400,	/* 2400 baud rate */
190d3cf9c7dSdf157793 	4800,	/* 4800 baud rate */
191d3cf9c7dSdf157793 	9600,	/* 9600 baud rate */
192d3cf9c7dSdf157793 	19200,	/* 19200 baud rate */
193d3cf9c7dSdf157793 	38400,	/* 38400 baud rate */
194d3cf9c7dSdf157793 	57600,	/* 57600 baud rate */
195d3cf9c7dSdf157793 	76800,	/* 76800 baud rate */
196d3cf9c7dSdf157793 	115200,	/* 115200 baud rate */
197d3cf9c7dSdf157793 	153600,	/* 153600 baud rate */
198d3cf9c7dSdf157793 	230400,	/* 230400 baud rate */
199d3cf9c7dSdf157793 	307200,	/* 307200 baud rate */
200d3cf9c7dSdf157793 	460800	/* 460800 baud rate */
201d3cf9c7dSdf157793 };
202d3cf9c7dSdf157793 
203d3cf9c7dSdf157793 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
204730a650aSPeter Tribble static int asyclose(queue_t *q, int flag, cred_t *cr);
205*e476cc14SToomas Soome static int asywput(queue_t *q, mblk_t *mp);
206*e476cc14SToomas Soome static int asyrsrv(queue_t *q);
207d3cf9c7dSdf157793 
208d3cf9c7dSdf157793 struct module_info asy_info = {
209d3cf9c7dSdf157793 	0,
210d3cf9c7dSdf157793 	"su",
211d3cf9c7dSdf157793 	0,
212d3cf9c7dSdf157793 	INFPSZ,
213d3cf9c7dSdf157793 	32*4096,
214d3cf9c7dSdf157793 	4096
215d3cf9c7dSdf157793 };
216d3cf9c7dSdf157793 
217d3cf9c7dSdf157793 static struct qinit asy_rint = {
218d3cf9c7dSdf157793 	putq,
219*e476cc14SToomas Soome 	asyrsrv,
220d3cf9c7dSdf157793 	asyopen,
221d3cf9c7dSdf157793 	asyclose,
222d3cf9c7dSdf157793 	NULL,
223d3cf9c7dSdf157793 	&asy_info,
224d3cf9c7dSdf157793 	NULL
225d3cf9c7dSdf157793 };
226d3cf9c7dSdf157793 
227d3cf9c7dSdf157793 static struct qinit asy_wint = {
228*e476cc14SToomas Soome 	asywput,
229d3cf9c7dSdf157793 	NULL,
230d3cf9c7dSdf157793 	NULL,
231d3cf9c7dSdf157793 	NULL,
232d3cf9c7dSdf157793 	NULL,
233d3cf9c7dSdf157793 	&asy_info,
234d3cf9c7dSdf157793 	NULL
235d3cf9c7dSdf157793 };
236d3cf9c7dSdf157793 
237d3cf9c7dSdf157793 struct streamtab asy_str_info = {
238d3cf9c7dSdf157793 	&asy_rint,
239d3cf9c7dSdf157793 	&asy_wint,
240d3cf9c7dSdf157793 	NULL,
241d3cf9c7dSdf157793 	NULL
242d3cf9c7dSdf157793 };
243d3cf9c7dSdf157793 
244d3cf9c7dSdf157793 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
245d3cf9c7dSdf157793 		void **result);
246d3cf9c7dSdf157793 static int asyprobe(dev_info_t *);
247d3cf9c7dSdf157793 static int asyattach(dev_info_t *, ddi_attach_cmd_t);
248d3cf9c7dSdf157793 static int asydetach(dev_info_t *, ddi_detach_cmd_t);
249d3cf9c7dSdf157793 
250d3cf9c7dSdf157793 static struct cb_ops cb_asy_ops = {
251d3cf9c7dSdf157793 	nodev,			/* cb_open */
252d3cf9c7dSdf157793 	nodev,			/* cb_close */
253d3cf9c7dSdf157793 	nodev,			/* cb_strategy */
254d3cf9c7dSdf157793 	nodev,			/* cb_print */
255d3cf9c7dSdf157793 	nodev,			/* cb_dump */
256d3cf9c7dSdf157793 	nodev,			/* cb_read */
257d3cf9c7dSdf157793 	nodev,			/* cb_write */
258d3cf9c7dSdf157793 	nodev,			/* cb_ioctl */
259d3cf9c7dSdf157793 	nodev,			/* cb_devmap */
260d3cf9c7dSdf157793 	nodev,			/* cb_mmap */
261d3cf9c7dSdf157793 	nodev,			/* cb_segmap */
262d3cf9c7dSdf157793 	nochpoll,		/* cb_chpoll */
263d3cf9c7dSdf157793 	ddi_prop_op,		/* cb_prop_op */
264d3cf9c7dSdf157793 	&asy_str_info,		/* cb_stream */
265d3cf9c7dSdf157793 	D_MP			/* cb_flag */
266d3cf9c7dSdf157793 };
267d3cf9c7dSdf157793 
268d3cf9c7dSdf157793 struct dev_ops asy_ops = {
269d3cf9c7dSdf157793 	DEVO_REV,		/* devo_rev */
270d3cf9c7dSdf157793 	0,			/* devo_refcnt */
271d3cf9c7dSdf157793 	asyinfo,		/* devo_getinfo */
272d3cf9c7dSdf157793 	nulldev,		/* devo_identify */
273d3cf9c7dSdf157793 	asyprobe,		/* devo_probe */
274d3cf9c7dSdf157793 	asyattach,		/* devo_attach */
275d3cf9c7dSdf157793 	asydetach,		/* devo_detach */
276d3cf9c7dSdf157793 	nodev,			/* devo_reset */
277d3cf9c7dSdf157793 	&cb_asy_ops,		/* devo_cb_ops */
27819397407SSherry Moore 	NULL,			/* devo_bus_ops */
27919397407SSherry Moore 	NULL,			/* devo_power */
28019397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
281d3cf9c7dSdf157793 };
282d3cf9c7dSdf157793 
283d3cf9c7dSdf157793 /*
284d3cf9c7dSdf157793  * Module linkage information for the kernel.
285d3cf9c7dSdf157793  */
286d3cf9c7dSdf157793 
287d3cf9c7dSdf157793 static struct modldrv modldrv = {
288d3cf9c7dSdf157793 	&mod_driverops, /* Type of module.  This one is a driver */
28919397407SSherry Moore 	"su driver",
290d3cf9c7dSdf157793 	&asy_ops,	/* driver ops */
291d3cf9c7dSdf157793 };
292d3cf9c7dSdf157793 
293d3cf9c7dSdf157793 static struct modlinkage modlinkage = {
294d3cf9c7dSdf157793 	MODREV_1,
295d3cf9c7dSdf157793 	&modldrv,
296d3cf9c7dSdf157793 	NULL
297d3cf9c7dSdf157793 };
298d3cf9c7dSdf157793 
299d3cf9c7dSdf157793 int
_init(void)300d3cf9c7dSdf157793 _init(void)
301d3cf9c7dSdf157793 {
302d3cf9c7dSdf157793 	int status;
303d3cf9c7dSdf157793 
304d3cf9c7dSdf157793 	status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom),
305d3cf9c7dSdf157793 	    SU_INITIAL_SOFT_ITEMS);
306d3cf9c7dSdf157793 	if (status != 0)
307d3cf9c7dSdf157793 		return (status);
308d3cf9c7dSdf157793 	status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline),
309d3cf9c7dSdf157793 	    SU_INITIAL_SOFT_ITEMS);
310d3cf9c7dSdf157793 	if (status != 0) {
311d3cf9c7dSdf157793 		ddi_soft_state_fini(&su_asycom);
312d3cf9c7dSdf157793 		return (status);
313d3cf9c7dSdf157793 	}
314d3cf9c7dSdf157793 
315d3cf9c7dSdf157793 	if ((status = mod_install(&modlinkage)) != 0) {
316d3cf9c7dSdf157793 		ddi_soft_state_fini(&su_asycom);
317d3cf9c7dSdf157793 		ddi_soft_state_fini(&su_asyncline);
318d3cf9c7dSdf157793 	}
319d3cf9c7dSdf157793 
320d3cf9c7dSdf157793 	return (status);
321d3cf9c7dSdf157793 }
322d3cf9c7dSdf157793 
323d3cf9c7dSdf157793 int
_fini(void)324d3cf9c7dSdf157793 _fini(void)
325d3cf9c7dSdf157793 {
326d3cf9c7dSdf157793 	int i;
327d3cf9c7dSdf157793 
328d3cf9c7dSdf157793 	i = mod_remove(&modlinkage);
329d3cf9c7dSdf157793 	if (i == 0) {
330d3cf9c7dSdf157793 		ddi_soft_state_fini(&su_asycom);
331d3cf9c7dSdf157793 		ddi_soft_state_fini(&su_asyncline);
332d3cf9c7dSdf157793 	}
333d3cf9c7dSdf157793 
334d3cf9c7dSdf157793 	return (i);
335d3cf9c7dSdf157793 }
336d3cf9c7dSdf157793 
337d3cf9c7dSdf157793 int
_info(struct modinfo * modinfop)338d3cf9c7dSdf157793 _info(struct modinfo *modinfop)
339d3cf9c7dSdf157793 {
340d3cf9c7dSdf157793 	return (mod_info(&modlinkage, modinfop));
341d3cf9c7dSdf157793 }
342d3cf9c7dSdf157793 
343d3cf9c7dSdf157793 static int
asyprobe(dev_info_t * devi)344d3cf9c7dSdf157793 asyprobe(dev_info_t *devi)
345d3cf9c7dSdf157793 {
346d3cf9c7dSdf157793 	int		instance;
347d3cf9c7dSdf157793 	ddi_acc_handle_t handle;
348d3cf9c7dSdf157793 	uchar_t *addr;
349d3cf9c7dSdf157793 	ddi_device_acc_attr_t attr;
350d3cf9c7dSdf157793 
351d3cf9c7dSdf157793 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
352d3cf9c7dSdf157793 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
353d3cf9c7dSdf157793 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
354d3cf9c7dSdf157793 	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr,
355d3cf9c7dSdf157793 	    SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) {
356d3cf9c7dSdf157793 		cmn_err(CE_WARN, "asyprobe regs map setup failed");
357d3cf9c7dSdf157793 		return (DDI_PROBE_FAILURE);
358d3cf9c7dSdf157793 	}
359d3cf9c7dSdf157793 #ifdef DEBUG
360d3cf9c7dSdf157793 	if (asydebug)
361d3cf9c7dSdf157793 		printf("Probe address mapped %p\n", (void *)addr);
362d3cf9c7dSdf157793 #endif
363d3cf9c7dSdf157793 
364d3cf9c7dSdf157793 	/*
365d3cf9c7dSdf157793 	 * Probe for the device:
366d3cf9c7dSdf157793 	 *	Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low.
367d3cf9c7dSdf157793 	 *	If bit 4 or 5 appears on inb() ISR, board is not there.
368d3cf9c7dSdf157793 	 */
3692d526643Sjesusm 	if (ddi_get8(handle, addr+ISR) & 0x30) {
3702d526643Sjesusm 		ddi_regs_map_free(&handle);
371d3cf9c7dSdf157793 		return (DDI_PROBE_FAILURE);
3722d526643Sjesusm 	}
3732d526643Sjesusm 
374d3cf9c7dSdf157793 	instance = ddi_get_instance(devi);
375d3cf9c7dSdf157793 	if (max_asy_instance < instance)
376d3cf9c7dSdf157793 		max_asy_instance = instance;
377d3cf9c7dSdf157793 	ddi_regs_map_free(&handle);
378d3cf9c7dSdf157793 
379d3cf9c7dSdf157793 	return (DDI_PROBE_SUCCESS); /* hw is present */
380d3cf9c7dSdf157793 }
381d3cf9c7dSdf157793 
382d3cf9c7dSdf157793 static int
asydetach(dev_info_t * devi,ddi_detach_cmd_t cmd)383d3cf9c7dSdf157793 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
384d3cf9c7dSdf157793 {
385*e476cc14SToomas Soome 	int	instance;
386d3cf9c7dSdf157793 	struct asycom	*asy;
387d3cf9c7dSdf157793 	struct asyncline *async;
388d3cf9c7dSdf157793 	char		name[16];
389d3cf9c7dSdf157793 
390d3cf9c7dSdf157793 	instance = ddi_get_instance(devi);	/* find out which unit */
391d3cf9c7dSdf157793 
392d3cf9c7dSdf157793 	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
393d3cf9c7dSdf157793 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
394d3cf9c7dSdf157793 
395d3cf9c7dSdf157793 	switch (cmd) {
396d3cf9c7dSdf157793 		case DDI_DETACH:
397d3cf9c7dSdf157793 			break;
398d3cf9c7dSdf157793 		case DDI_SUSPEND:
399d3cf9c7dSdf157793 			/* grab both mutex locks */
400d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
401d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
402d3cf9c7dSdf157793 			if (asy->suspended) {
403d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
404d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
405d3cf9c7dSdf157793 				return (DDI_SUCCESS);
406d3cf9c7dSdf157793 			}
407d3cf9c7dSdf157793 			asy->suspended = B_TRUE;
40817ea09c7Sanovick 
40917ea09c7Sanovick 			/*
4100280efdcSzk194757 			 * The quad UART ST16C554D, version D2 (made by EXAR)
4110280efdcSzk194757 			 * has an anomaly of generating spurious interrupts
4120280efdcSzk194757 			 * when the ICR is loaded with zero. The workaround
4130280efdcSzk194757 			 * would be to read/write any register with DATA1 bit
4140280efdcSzk194757 			 * set to 0 before such write.
41517ea09c7Sanovick 			 */
416f63f7506Sanovick 			if (asy->asy_hwtype == ASY16C554D)
417f63f7506Sanovick 				OUTB(SPR, 0);
41817ea09c7Sanovick 
419d3cf9c7dSdf157793 			/* Disable further interrupts */
420d3cf9c7dSdf157793 			OUTB(ICR, 0);
421d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
422d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
423d3cf9c7dSdf157793 			return (DDI_SUCCESS);
424d3cf9c7dSdf157793 
425d3cf9c7dSdf157793 		default:
426d3cf9c7dSdf157793 			return (DDI_FAILURE);
427d3cf9c7dSdf157793 	}
428d3cf9c7dSdf157793 
429d3cf9c7dSdf157793 #ifdef DEBUG
430d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_INIT)
431d3cf9c7dSdf157793 		cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance,
432d3cf9c7dSdf157793 		    asy->asy_hwtype == ASY82510 ? "82510" :
433d3cf9c7dSdf157793 		    asy->asy_hwtype == ASY16550AF ? "16550AF" :
434f63f7506Sanovick 		    asy->asy_hwtype == ASY16C554D ? "16C554D" :
435d3cf9c7dSdf157793 		    "8250");
436d3cf9c7dSdf157793 #endif
437d3cf9c7dSdf157793 	/*
438d3cf9c7dSdf157793 	 * Before removing interrupts it is always better to disable
439d3cf9c7dSdf157793 	 * interrupts if the chip gives a provision to disable the
440d3cf9c7dSdf157793 	 * serial port interrupts.
441d3cf9c7dSdf157793 	 */
442d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
443d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
44417ea09c7Sanovick 	/* disable interrupts, see EXAR bug */
445f63f7506Sanovick 	if (asy->asy_hwtype == ASY16C554D)
446f63f7506Sanovick 		OUTB(SPR, 0);
44717ea09c7Sanovick 	OUTB(ICR, 0);
448d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
449d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
450d3cf9c7dSdf157793 
451d3cf9c7dSdf157793 	/* remove minor device node(s) for this device */
452d3cf9c7dSdf157793 	(void) sprintf(name, "%c", (instance+'a'));	/* serial-port */
453d3cf9c7dSdf157793 	ddi_remove_minor_node(devi, name);
454d3cf9c7dSdf157793 	(void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */
455d3cf9c7dSdf157793 	ddi_remove_minor_node(devi, name);
456d3cf9c7dSdf157793 
457d3cf9c7dSdf157793 	mutex_destroy(asy->asy_excl);
458d3cf9c7dSdf157793 	mutex_destroy(asy->asy_excl_hi);
459d3cf9c7dSdf157793 	kmem_free(asy->asy_excl, sizeof (kmutex_t));
460d3cf9c7dSdf157793 	kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
461d3cf9c7dSdf157793 	cv_destroy(&async->async_flags_cv);
462d3cf9c7dSdf157793 	kstat_delete(asy->sukstat);
463d3cf9c7dSdf157793 	ddi_remove_intr(devi, 0, asy->asy_iblock);
464d3cf9c7dSdf157793 	ddi_regs_map_free(&asy->asy_handle);
465d3cf9c7dSdf157793 	ddi_remove_softintr(asy->asy_softintr_id);
466d3cf9c7dSdf157793 	mutex_destroy(asy->asy_soft_lock);
467d3cf9c7dSdf157793 	kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
468d3cf9c7dSdf157793 	ddi_soft_state_free(su_asycom, instance);
469d3cf9c7dSdf157793 	ddi_soft_state_free(su_asyncline, instance);
470d3cf9c7dSdf157793 	return (DDI_SUCCESS);
471d3cf9c7dSdf157793 }
472d3cf9c7dSdf157793 
473d3cf9c7dSdf157793 static int
asyattach(dev_info_t * devi,ddi_attach_cmd_t cmd)474d3cf9c7dSdf157793 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
475d3cf9c7dSdf157793 {
476*e476cc14SToomas Soome 	int	instance;
477d3cf9c7dSdf157793 	struct asycom	*asy;
478d3cf9c7dSdf157793 	struct asyncline *async;
479d3cf9c7dSdf157793 	char		name[40];
480d3cf9c7dSdf157793 	ddi_device_acc_attr_t attr;
481d3cf9c7dSdf157793 	enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR,
482d3cf9c7dSdf157793 	    SOFTINTR, ASYINIT, KSTAT, MINORNODE };
483d3cf9c7dSdf157793 	enum states state = EMPTY;
484f63f7506Sanovick 	char *hwtype;
485d3cf9c7dSdf157793 
486d3cf9c7dSdf157793 	instance = ddi_get_instance(devi);	/* find out which unit */
487d3cf9c7dSdf157793 
488d3cf9c7dSdf157793 	/* cannot attach a device that has not been probed first */
489d3cf9c7dSdf157793 	if (instance > max_asy_instance)
490d3cf9c7dSdf157793 		return (DDI_FAILURE);
491d3cf9c7dSdf157793 
492d3cf9c7dSdf157793 	if (cmd != DDI_RESUME) {
493d3cf9c7dSdf157793 		/* Allocate soft state space */
494d3cf9c7dSdf157793 		if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) {
495d3cf9c7dSdf157793 			cmn_err(CE_WARN, "su%d: cannot allocate soft state",
496d3cf9c7dSdf157793 			    instance);
497d3cf9c7dSdf157793 			goto error;
498d3cf9c7dSdf157793 		}
499d3cf9c7dSdf157793 	}
500d3cf9c7dSdf157793 	state = SOFTSTATE;
501d3cf9c7dSdf157793 
502d3cf9c7dSdf157793 	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
503d3cf9c7dSdf157793 
504d3cf9c7dSdf157793 	if (asy == NULL) {
505d3cf9c7dSdf157793 		cmn_err(CE_WARN, "su%d: cannot get soft state", instance);
506d3cf9c7dSdf157793 		goto error;
507d3cf9c7dSdf157793 	}
508d3cf9c7dSdf157793 
509d3cf9c7dSdf157793 	switch (cmd) {
510d3cf9c7dSdf157793 		case DDI_ATTACH:
511d3cf9c7dSdf157793 			break;
512d3cf9c7dSdf157793 		case DDI_RESUME: {
513d3cf9c7dSdf157793 			struct asyncline *async;
514d3cf9c7dSdf157793 
515d3cf9c7dSdf157793 			/* grab both mutex locks */
516d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
517d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
518d3cf9c7dSdf157793 			if (!asy->suspended) {
519d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
520d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
521d3cf9c7dSdf157793 				return (DDI_SUCCESS);
522d3cf9c7dSdf157793 			}
5230280efdcSzk194757 			/*
5240280efdcSzk194757 			 * re-setup all the registers and enable interrupts if
5250280efdcSzk194757 			 * needed
5260280efdcSzk194757 			 */
527d3cf9c7dSdf157793 			async = (struct asyncline *)asy->asy_priv;
528d3cf9c7dSdf157793 			if ((async) && (async->async_flags & ASYNC_ISOPEN))
529d3cf9c7dSdf157793 				(void) asy_program(asy, ASY_INIT);
530d3cf9c7dSdf157793 			asy->suspended = B_FALSE;
531d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
532d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
533d3cf9c7dSdf157793 			return (DDI_SUCCESS);
534d3cf9c7dSdf157793 		}
535d3cf9c7dSdf157793 		default:
536d3cf9c7dSdf157793 			goto error;
537d3cf9c7dSdf157793 	}
538d3cf9c7dSdf157793 
539d3cf9c7dSdf157793 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
540d3cf9c7dSdf157793 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
541d3cf9c7dSdf157793 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
542d3cf9c7dSdf157793 
543d3cf9c7dSdf157793 	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO,
544d3cf9c7dSdf157793 	    (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN,
545d3cf9c7dSdf157793 	    &attr, &asy->asy_handle) != DDI_SUCCESS) {
546d3cf9c7dSdf157793 		cmn_err(CE_WARN, "asyprobe regs map setup failed");
547d3cf9c7dSdf157793 		goto error;
548d3cf9c7dSdf157793 	}
549d3cf9c7dSdf157793 	state = REGSMAP;
550d3cf9c7dSdf157793 
551d3cf9c7dSdf157793 #ifdef DEBUG
552d3cf9c7dSdf157793 	if (asydebug)
553d3cf9c7dSdf157793 		printf("su attach mapped %p\n", (void *)asy->asy_ioaddr);
554d3cf9c7dSdf157793 #endif
555d3cf9c7dSdf157793 
556d3cf9c7dSdf157793 	/*
557d3cf9c7dSdf157793 	 * Initialize the port with default settings.
558d3cf9c7dSdf157793 	 */
559d3cf9c7dSdf157793 	asy->asy_fifo_buf = 1;
560d3cf9c7dSdf157793 	asy->asy_use_fifo = FIFO_OFF;
561d3cf9c7dSdf157793 
562d3cf9c7dSdf157793 	/*
563d3cf9c7dSdf157793 	 * Check for baudrate generator's "baud-divisor-factor" property setup
564d3cf9c7dSdf157793 	 * by OBP, since different UART chips might have different baudrate
565d3cf9c7dSdf157793 	 * generator divisor. e.g., in case of NSPG's Sputnik platform, the
566d3cf9c7dSdf157793 	 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip
567d3cf9c7dSdf157793 	 * instead of SuperIO. Since the baud-divisor-factor must be a positive
568d3cf9c7dSdf157793 	 * integer, the divisors will always be at least as large as the values
569d3cf9c7dSdf157793 	 * in asyspdtab[].  Make the default factor 1.
570d3cf9c7dSdf157793 	 */
571d3cf9c7dSdf157793 	asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
572d3cf9c7dSdf157793 	    DDI_PROP_DONTPASS, "baud-divisor-factor", 1);
573d3cf9c7dSdf157793 
574d3cf9c7dSdf157793 	/* set speed cap */
575d3cf9c7dSdf157793 	asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
576d3cf9c7dSdf157793 	    DDI_PROP_DONTPASS, "serial-speed-cap", 115200);
577d3cf9c7dSdf157793 
578d3cf9c7dSdf157793 	/* check for ASY82510 chip */
579d3cf9c7dSdf157793 	OUTB(ISR, 0x20);
580d3cf9c7dSdf157793 	if (INB(ISR) & 0x20) { /* 82510 chip is present */
581d3cf9c7dSdf157793 		/*
582d3cf9c7dSdf157793 		 * Since most of the general operation of the 82510 chip
583d3cf9c7dSdf157793 		 * can be done from BANK 0 (8250A/16450 compatable mode)
584d3cf9c7dSdf157793 		 * we will default to BANK 0.
585d3cf9c7dSdf157793 		 */
586d3cf9c7dSdf157793 		asy->asy_hwtype = ASY82510;
587d3cf9c7dSdf157793 		OUTB(DAT+7, 0x04); /* clear status */
588d3cf9c7dSdf157793 		OUTB(ISR, 0x40); /* set to bank 2 */
589d3cf9c7dSdf157793 		OUTB(MCR, 0x08); /* IMD */
590d3cf9c7dSdf157793 		OUTB(DAT, 0x21); /* FMD */
591d3cf9c7dSdf157793 		OUTB(ISR, 0x00); /* set to bank 0 */
592d3cf9c7dSdf157793 		asy->asy_trig_level = 0;
593d3cf9c7dSdf157793 	} else { /* Set the UART in FIFO mode if it has FIFO buffers */
594d3cf9c7dSdf157793 		asy->asy_hwtype = ASY16550AF;
595d3cf9c7dSdf157793 		OUTB(FIFOR, 0x00); /* clear fifo register */
596d3cf9c7dSdf157793 		asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */
597d3cf9c7dSdf157793 
598d3cf9c7dSdf157793 		/* set/Enable FIFO */
599d3cf9c7dSdf157793 		OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH |
600d3cf9c7dSdf157793 		    (asy->asy_trig_level & 0xff));
601d3cf9c7dSdf157793 
602d3cf9c7dSdf157793 		if ((INB(ISR) & 0xc0) == 0xc0)
603d3cf9c7dSdf157793 			asy->asy_use_fifo = FIFO_ON;
604d3cf9c7dSdf157793 		else {
605d3cf9c7dSdf157793 			asy->asy_hwtype = ASY8250;
606d3cf9c7dSdf157793 			OUTB(FIFOR, 0x00); /* NO FIFOs */
607d3cf9c7dSdf157793 			asy->asy_trig_level = 0;
608d3cf9c7dSdf157793 		}
609d3cf9c7dSdf157793 	}
610d3cf9c7dSdf157793 
611f63f7506Sanovick 	/* check for ST16C554D chip */
612f63f7506Sanovick 	if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM |
613f63f7506Sanovick 	    DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) {
614f63f7506Sanovick 		if (strcmp(hwtype, "ST16C554D") == 0)
615f63f7506Sanovick 			asy->asy_hwtype = ASY16C554D;
616f63f7506Sanovick 		ddi_prop_free(hwtype);
617f63f7506Sanovick 	}
618f63f7506Sanovick 
61917ea09c7Sanovick 	/* disable interrupts, see EXAR bug */
620f63f7506Sanovick 	if (asy->asy_hwtype == ASY16C554D)
621f63f7506Sanovick 		OUTB(SPR, 0);
62217ea09c7Sanovick 	OUTB(ICR, 0);
623d3cf9c7dSdf157793 	OUTB(LCR, DLAB); /* select baud rate generator */
624d3cf9c7dSdf157793 	/* Set the baud rate to 9600 */
625d3cf9c7dSdf157793 	OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff);
626d3cf9c7dSdf157793 	OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff);
627d3cf9c7dSdf157793 	OUTB(LCR, STOP1|BITS8);
628d3cf9c7dSdf157793 	OUTB(MCR, (DTR | RTS| OUT2));
629d3cf9c7dSdf157793 
630d3cf9c7dSdf157793 	/*
631d3cf9c7dSdf157793 	 * Set up the other components of the asycom structure for this port.
632d3cf9c7dSdf157793 	 */
633d3cf9c7dSdf157793 	asy->asy_excl = (kmutex_t *)
634d3cf9c7dSdf157793 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
635d3cf9c7dSdf157793 	asy->asy_excl_hi = (kmutex_t *)
636d3cf9c7dSdf157793 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
637d3cf9c7dSdf157793 	asy->asy_soft_lock = (kmutex_t *)
638d3cf9c7dSdf157793 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
639d3cf9c7dSdf157793 	asy->asy_unit = instance;
640d3cf9c7dSdf157793 	asy->asy_dip = devi;
641d3cf9c7dSdf157793 
642d3cf9c7dSdf157793 	if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) {
643d3cf9c7dSdf157793 		cmn_err(CE_NOTE,
644d3cf9c7dSdf157793 		    "Get iblock_cookie failed-Device interrupt%x\n", instance);
645d3cf9c7dSdf157793 		goto error;
646d3cf9c7dSdf157793 	}
647d3cf9c7dSdf157793 
648d3cf9c7dSdf157793 	if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH,
649d3cf9c7dSdf157793 	    &asy->asy_soft_iblock) != DDI_SUCCESS) {
650d3cf9c7dSdf157793 		cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n",
651d3cf9c7dSdf157793 		    instance);
652d3cf9c7dSdf157793 		goto error;
653d3cf9c7dSdf157793 	}
654d3cf9c7dSdf157793 
655d3cf9c7dSdf157793 	mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER,
656d3cf9c7dSdf157793 	    (void *)asy->asy_soft_iblock);
657d3cf9c7dSdf157793 	mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
658d3cf9c7dSdf157793 	mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER,
659d3cf9c7dSdf157793 	    (void *)asy->asy_iblock);
660d3cf9c7dSdf157793 	state = MUTEXES;
661d3cf9c7dSdf157793 
662d3cf9c7dSdf157793 	/*
663d3cf9c7dSdf157793 	 * Install interrupt handlers for this device.
664d3cf9c7dSdf157793 	 */
665d3cf9c7dSdf157793 	if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr,
666d3cf9c7dSdf157793 	    (caddr_t)asy) != DDI_SUCCESS) {
667d3cf9c7dSdf157793 		cmn_err(CE_CONT,
668d3cf9c7dSdf157793 		    "Cannot set device interrupt for su driver\n");
669d3cf9c7dSdf157793 		goto error;
670d3cf9c7dSdf157793 	}
671d3cf9c7dSdf157793 	state = ADDINTR;
672d3cf9c7dSdf157793 
673d3cf9c7dSdf157793 	if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id),
674d3cf9c7dSdf157793 	    &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy)
675d3cf9c7dSdf157793 	    != DDI_SUCCESS) {
676d3cf9c7dSdf157793 		cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n");
677d3cf9c7dSdf157793 		goto error;
678d3cf9c7dSdf157793 	}
679d3cf9c7dSdf157793 	state = SOFTINTR;
680d3cf9c7dSdf157793 
681d3cf9c7dSdf157793 	/* initialize the asyncline structure */
682d3cf9c7dSdf157793 	if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) {
683d3cf9c7dSdf157793 		cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance);
684d3cf9c7dSdf157793 		goto error;
685d3cf9c7dSdf157793 	}
686d3cf9c7dSdf157793 	state = ASYINIT;
687d3cf9c7dSdf157793 
688d3cf9c7dSdf157793 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
689d3cf9c7dSdf157793 
690d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
691d3cf9c7dSdf157793 	async->async_common = asy;
692d3cf9c7dSdf157793 	cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL);
693d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
694d3cf9c7dSdf157793 
695d3cf9c7dSdf157793 	if ((asy->sukstat = kstat_create("su", instance, "serialstat",
696d3cf9c7dSdf157793 	    "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) {
697d3cf9c7dSdf157793 		asy->sukstat->ks_data = &asy->kstats;
698d3cf9c7dSdf157793 		kstat_named_init(&asy->kstats.ringover, "ring buffer overflow",
699d3cf9c7dSdf157793 		    KSTAT_DATA_UINT64);
700d3cf9c7dSdf157793 		kstat_named_init(&asy->kstats.siloover, "silo overflow",
701d3cf9c7dSdf157793 		    KSTAT_DATA_UINT64);
702d3cf9c7dSdf157793 		kstat_install(asy->sukstat);
703d3cf9c7dSdf157793 	}
704d3cf9c7dSdf157793 	state = KSTAT;
705d3cf9c7dSdf157793 
706d3cf9c7dSdf157793 	if (strcmp(ddi_node_name(devi), "rsc-console") == 0) {
707d3cf9c7dSdf157793 		/*
708d3cf9c7dSdf157793 		 * If the device is configured as the 'rsc-console'
709d3cf9c7dSdf157793 		 * create the minor device for this node.
710d3cf9c7dSdf157793 		 */
711d3cf9c7dSdf157793 		if (ddi_create_minor_node(devi, "ssp", S_IFCHR,
712574493ffSToomas Soome 		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
713d3cf9c7dSdf157793 			cmn_err(CE_WARN,
714d3cf9c7dSdf157793 			    "%s%d: Failed to create node rsc-console",
715d3cf9c7dSdf157793 			    ddi_get_name(devi), ddi_get_instance(devi));
716d3cf9c7dSdf157793 			goto error;
717d3cf9c7dSdf157793 		}
718d3cf9c7dSdf157793 
719d3cf9c7dSdf157793 		asy->asy_lom_console = 0;
720d3cf9c7dSdf157793 		asy->asy_rsc_console = 1;
721d3cf9c7dSdf157793 		asy->asy_rsc_control = 0;
722d3cf9c7dSdf157793 		asy->asy_device_type = ASY_SERIAL;
723d3cf9c7dSdf157793 		asy->asy_flags |= ASY_IGNORE_CD;
724d3cf9c7dSdf157793 
725d3cf9c7dSdf157793 	} else if (strcmp(ddi_node_name(devi), "lom-console") == 0) {
726d3cf9c7dSdf157793 		/*
727d3cf9c7dSdf157793 		 * If the device is configured as the 'lom-console'
728d3cf9c7dSdf157793 		 * create the minor device for this node.
729d3cf9c7dSdf157793 		 * Do not create a dialout device.
730d3cf9c7dSdf157793 		 * Use the same minor numbers as would be used for standard
731d3cf9c7dSdf157793 		 * serial instances.
732d3cf9c7dSdf157793 		 */
733d3cf9c7dSdf157793 		if (ddi_create_minor_node(devi, "lom-console", S_IFCHR,
734574493ffSToomas Soome 		    instance, DDI_NT_SERIAL_LOMCON, 0) == DDI_FAILURE) {
735d3cf9c7dSdf157793 			cmn_err(CE_WARN,
736d3cf9c7dSdf157793 			    "%s%d: Failed to create node lom-console",
737d3cf9c7dSdf157793 			    ddi_get_name(devi), ddi_get_instance(devi));
738d3cf9c7dSdf157793 			goto error;
739d3cf9c7dSdf157793 		}
740d3cf9c7dSdf157793 		asy->asy_lom_console = 1;
741d3cf9c7dSdf157793 		asy->asy_rsc_console = 0;
742d3cf9c7dSdf157793 		asy->asy_rsc_control = 0;
743d3cf9c7dSdf157793 		asy->asy_device_type = ASY_SERIAL;
744d3cf9c7dSdf157793 		asy->asy_flags |= ASY_IGNORE_CD;
745d3cf9c7dSdf157793 
746d3cf9c7dSdf157793 	} else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) {
747d3cf9c7dSdf157793 		/*
748d3cf9c7dSdf157793 		 * If the device is configured as the 'rsc-control'
749d3cf9c7dSdf157793 		 * create the minor device for this node.
750d3cf9c7dSdf157793 		 */
751d3cf9c7dSdf157793 		if (ddi_create_minor_node(devi, "sspctl", S_IFCHR,
752574493ffSToomas Soome 		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
753d3cf9c7dSdf157793 			cmn_err(CE_WARN, "%s%d: Failed to create rsc-control",
754d3cf9c7dSdf157793 			    ddi_get_name(devi), ddi_get_instance(devi));
755d3cf9c7dSdf157793 			goto error;
756d3cf9c7dSdf157793 		}
757d3cf9c7dSdf157793 
758d3cf9c7dSdf157793 		asy->asy_lom_console = 0;
759d3cf9c7dSdf157793 		asy->asy_rsc_console = 0;
760d3cf9c7dSdf157793 		asy->asy_rsc_control = 1;
761d3cf9c7dSdf157793 		asy->asy_device_type = ASY_SERIAL;
762d3cf9c7dSdf157793 		asy->asy_flags |= ASY_IGNORE_CD;
763d3cf9c7dSdf157793 
764d3cf9c7dSdf157793 	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
765d3cf9c7dSdf157793 	    "keyboard", 0)) {
766d3cf9c7dSdf157793 		/*
767d3cf9c7dSdf157793 		 * If the device is a keyboard, then create an internal
768d3cf9c7dSdf157793 		 * pathname so that the dacf code will link the node into
769d3cf9c7dSdf157793 		 * the keyboard console stream.  See dacf.conf.
770d3cf9c7dSdf157793 		 */
771d3cf9c7dSdf157793 		if (ddi_create_internal_pathname(devi, "keyboard",
772d3cf9c7dSdf157793 		    S_IFCHR, instance) == DDI_FAILURE) {
773d3cf9c7dSdf157793 			goto error;
774d3cf9c7dSdf157793 		}
775d3cf9c7dSdf157793 		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
776d3cf9c7dSdf157793 		asy->asy_device_type = ASY_KEYBOARD;	/* Device type */
777d3cf9c7dSdf157793 	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
778d3cf9c7dSdf157793 	    "mouse", 0)) {
779d3cf9c7dSdf157793 		/*
780d3cf9c7dSdf157793 		 * If the device is a mouse, then create an internal
781d3cf9c7dSdf157793 		 * pathname so that the dacf code will link the node into
782d3cf9c7dSdf157793 		 * the mouse stream.  See dacf.conf.
783d3cf9c7dSdf157793 		 */
784d3cf9c7dSdf157793 		if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR,
785d3cf9c7dSdf157793 		    instance) == DDI_FAILURE) {
786d3cf9c7dSdf157793 			goto error;
787d3cf9c7dSdf157793 		}
788d3cf9c7dSdf157793 		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
789d3cf9c7dSdf157793 		asy->asy_device_type = ASY_MOUSE;
790d3cf9c7dSdf157793 	} else {
791d3cf9c7dSdf157793 		/*
792d3cf9c7dSdf157793 		 * If not used for keyboard/mouse, create minor devices nodes
793d3cf9c7dSdf157793 		 * for this device
794d3cf9c7dSdf157793 		 */
795d3cf9c7dSdf157793 		/* serial-port */
796d3cf9c7dSdf157793 		(void) sprintf(name, "%c", (instance+'a'));
797d3cf9c7dSdf157793 		if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
798574493ffSToomas Soome 		    DDI_NT_SERIAL_MB, 0) == DDI_FAILURE) {
799d3cf9c7dSdf157793 			goto error;
800d3cf9c7dSdf157793 		}
801d3cf9c7dSdf157793 		state = MINORNODE;
802d3cf9c7dSdf157793 		/* serial-port:dailout */
803d3cf9c7dSdf157793 		(void) sprintf(name, "%c,cu", (instance+'a'));
804d3cf9c7dSdf157793 		if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE,
805574493ffSToomas Soome 		    DDI_NT_SERIAL_MB_DO, 0) == DDI_FAILURE) {
806d3cf9c7dSdf157793 			goto error;
807d3cf9c7dSdf157793 		}
808d3cf9c7dSdf157793 		/* Property for ignoring DCD */
809d3cf9c7dSdf157793 		if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
810d3cf9c7dSdf157793 		    "ignore-cd", 0)) {
811d3cf9c7dSdf157793 			asy->asy_flags |= ASY_IGNORE_CD;  /* ignore cd */
812d3cf9c7dSdf157793 		} else {
813d3cf9c7dSdf157793 			asy->asy_flags &= ~ASY_IGNORE_CD;
814d3cf9c7dSdf157793 			/*
815d3cf9c7dSdf157793 			 * if ignore-cd is not available it could be
816d3cf9c7dSdf157793 			 * some old legacy platform, try to see
817d3cf9c7dSdf157793 			 * whether the old legacy property exists
818d3cf9c7dSdf157793 			 */
819d3cf9c7dSdf157793 			(void) sprintf(name,
820d3cf9c7dSdf157793 			    "port-%c-ignore-cd", (instance+ 'a'));
821d3cf9c7dSdf157793 			if (ddi_getprop(DDI_DEV_T_ANY, devi,
822d3cf9c7dSdf157793 			    DDI_PROP_DONTPASS, name, 0))
823d3cf9c7dSdf157793 				asy->asy_flags |= ASY_IGNORE_CD;
824d3cf9c7dSdf157793 		}
825d3cf9c7dSdf157793 		asy->asy_device_type = ASY_SERIAL;
826d3cf9c7dSdf157793 	}
8270280efdcSzk194757 
8280280efdcSzk194757 	/*
8290280efdcSzk194757 	 * Fill in the polled I/O structure
8300280efdcSzk194757 	 */
8310280efdcSzk194757 	asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
8320280efdcSzk194757 	asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
8330280efdcSzk194757 	asy->polledio.cons_polledio_putchar =  asyputchar;
8340280efdcSzk194757 	asy->polledio.cons_polledio_getchar = asygetchar;
8350280efdcSzk194757 	asy->polledio.cons_polledio_ischar = asyischar;
8360280efdcSzk194757 	asy->polledio.cons_polledio_enter = asy_polled_enter;
8370280efdcSzk194757 	asy->polledio.cons_polledio_exit = asy_polled_exit;
8380280efdcSzk194757 
8390280efdcSzk194757 	/* Initialize saved ICR and polled_enter */
8400280efdcSzk194757 	asy->polled_icr = 0;
8410280efdcSzk194757 	asy->polled_enter = B_FALSE;
8420280efdcSzk194757 
843d3cf9c7dSdf157793 	ddi_report_dev(devi);
844d3cf9c7dSdf157793 	return (DDI_SUCCESS);
845d3cf9c7dSdf157793 
846d3cf9c7dSdf157793 error:
847d3cf9c7dSdf157793 	if (state == MINORNODE) {
848d3cf9c7dSdf157793 		(void) sprintf(name, "%c", (instance+'a'));
849d3cf9c7dSdf157793 		ddi_remove_minor_node(devi, name);
850d3cf9c7dSdf157793 	}
851d3cf9c7dSdf157793 	if (state >= KSTAT)
852d3cf9c7dSdf157793 		kstat_delete(asy->sukstat);
853d3cf9c7dSdf157793 	if (state >= ASYINIT) {
854d3cf9c7dSdf157793 		cv_destroy(&async->async_flags_cv);
855d3cf9c7dSdf157793 		ddi_soft_state_free(su_asyncline, instance);
856d3cf9c7dSdf157793 	}
857d3cf9c7dSdf157793 	if (state >= SOFTINTR)
858d3cf9c7dSdf157793 		ddi_remove_softintr(asy->asy_softintr_id);
859d3cf9c7dSdf157793 	if (state >= ADDINTR)
860d3cf9c7dSdf157793 		ddi_remove_intr(devi, 0, asy->asy_iblock);
861d3cf9c7dSdf157793 	if (state >= MUTEXES) {
862d3cf9c7dSdf157793 		mutex_destroy(asy->asy_excl_hi);
863d3cf9c7dSdf157793 		mutex_destroy(asy->asy_excl);
864d3cf9c7dSdf157793 		mutex_destroy(asy->asy_soft_lock);
865d3cf9c7dSdf157793 		kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
866d3cf9c7dSdf157793 		kmem_free(asy->asy_excl, sizeof (kmutex_t));
867d3cf9c7dSdf157793 		kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
868d3cf9c7dSdf157793 	}
869d3cf9c7dSdf157793 	if (state >= REGSMAP)
870d3cf9c7dSdf157793 		ddi_regs_map_free(&asy->asy_handle);
871d3cf9c7dSdf157793 	if (state >= SOFTSTATE)
872d3cf9c7dSdf157793 		ddi_soft_state_free(su_asycom, instance);
873d3cf9c7dSdf157793 	/* no action for EMPTY state */
874d3cf9c7dSdf157793 	return (DDI_FAILURE);
875d3cf9c7dSdf157793 }
876d3cf9c7dSdf157793 
877d3cf9c7dSdf157793 static int
asyinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)878d3cf9c7dSdf157793 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
879d3cf9c7dSdf157793     void **result)
880d3cf9c7dSdf157793 {
881d3cf9c7dSdf157793 	_NOTE(ARGUNUSED(dip))
882*e476cc14SToomas Soome 	dev_t dev = (dev_t)arg;
883*e476cc14SToomas Soome 	int instance, error;
884d3cf9c7dSdf157793 	struct asycom *asy;
885d3cf9c7dSdf157793 
886d3cf9c7dSdf157793 	if ((instance = UNIT(dev)) > max_asy_instance)
887d3cf9c7dSdf157793 		return (DDI_FAILURE);
888d3cf9c7dSdf157793 
889d3cf9c7dSdf157793 	switch (infocmd) {
890d3cf9c7dSdf157793 		case DDI_INFO_DEVT2DEVINFO:
8910280efdcSzk194757 			asy = (struct asycom *)ddi_get_soft_state(su_asycom,
8920280efdcSzk194757 			    instance);
893d3cf9c7dSdf157793 			if (asy->asy_dip == NULL)
894d3cf9c7dSdf157793 				error = DDI_FAILURE;
895d3cf9c7dSdf157793 			else {
896d3cf9c7dSdf157793 				*result = (void *) asy->asy_dip;
897d3cf9c7dSdf157793 				error = DDI_SUCCESS;
898d3cf9c7dSdf157793 			}
899d3cf9c7dSdf157793 			break;
900d3cf9c7dSdf157793 		case DDI_INFO_DEVT2INSTANCE:
901360e6f5eSmathue 			*result = (void *)(uintptr_t)instance;
902d3cf9c7dSdf157793 			error = DDI_SUCCESS;
903d3cf9c7dSdf157793 			break;
904d3cf9c7dSdf157793 		default:
905d3cf9c7dSdf157793 			error = DDI_FAILURE;
906d3cf9c7dSdf157793 	}
907d3cf9c7dSdf157793 	return (error);
908d3cf9c7dSdf157793 }
909d3cf9c7dSdf157793 
910d3cf9c7dSdf157793 static int
asyopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)911d3cf9c7dSdf157793 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
912d3cf9c7dSdf157793 {
913d3cf9c7dSdf157793 	_NOTE(ARGUNUSED(sflag))
914d3cf9c7dSdf157793 	struct asycom	*asy;
915d3cf9c7dSdf157793 	struct asyncline *async;
916d3cf9c7dSdf157793 	int		mcr;
917d3cf9c7dSdf157793 	int		unit;
918d3cf9c7dSdf157793 	int		len;
919d3cf9c7dSdf157793 	struct termios	*termiosp;
920d3cf9c7dSdf157793 
921d3cf9c7dSdf157793 #ifdef DEBUG
922d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_CLOSE)
923d3cf9c7dSdf157793 		printf("open\n");
924d3cf9c7dSdf157793 #endif
925d3cf9c7dSdf157793 	unit = UNIT(*dev);
926d3cf9c7dSdf157793 	if (unit > max_asy_instance)
927d3cf9c7dSdf157793 		return (ENXIO);		/* unit not configured */
928d3cf9c7dSdf157793 
929d3cf9c7dSdf157793 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit);
930d3cf9c7dSdf157793 	if (async == NULL)
931d3cf9c7dSdf157793 		return (ENXIO);
932d3cf9c7dSdf157793 
933d3cf9c7dSdf157793 	asy = async->async_common;
934d3cf9c7dSdf157793 	if (asy == NULL)
935d3cf9c7dSdf157793 		return (ENXIO);		/* device not found by autoconfig */
936d3cf9c7dSdf157793 
937d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
938d3cf9c7dSdf157793 	asy->asy_priv = (caddr_t)async;
939d3cf9c7dSdf157793 
940d3cf9c7dSdf157793 again:
941d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
942d3cf9c7dSdf157793 	/*
943d3cf9c7dSdf157793 	 * Block waiting for carrier to come up, unless this is a no-delay open.
944d3cf9c7dSdf157793 	 */
945d3cf9c7dSdf157793 	if (!(async->async_flags & ASYNC_ISOPEN)) {
946d3cf9c7dSdf157793 		/*
947d3cf9c7dSdf157793 		 * If this port is for a RSC console or control
948d3cf9c7dSdf157793 		 * use the following termio info
949d3cf9c7dSdf157793 		 */
950d3cf9c7dSdf157793 		if (asy->asy_rsc_console || asy->asy_rsc_control) {
951d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT |
952d3cf9c7dSdf157793 			    (B115200 & CBAUD);
953d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT)
954d3cf9c7dSdf157793 			    & CIBAUD);
955d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
956d3cf9c7dSdf157793 		} else if (asy->asy_lom_console) {
957d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag = B9600 & CBAUD;
958d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT)
959d3cf9c7dSdf157793 			    & CIBAUD);
960d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
961d3cf9c7dSdf157793 		} else {
962d3cf9c7dSdf157793 
963d3cf9c7dSdf157793 			/*
964d3cf9c7dSdf157793 			 * Set the default termios settings (cflag).
965d3cf9c7dSdf157793 			 * Others are set in ldterm.  Release the spin
966d3cf9c7dSdf157793 			 * mutex as we can block here, reaquire before
967d3cf9c7dSdf157793 			 * calling asy_program.
968d3cf9c7dSdf157793 			 */
969d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
970d3cf9c7dSdf157793 			if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
971d3cf9c7dSdf157793 			    0, "ttymodes", (caddr_t)&termiosp, &len)
972d3cf9c7dSdf157793 			    == DDI_PROP_SUCCESS &&
973d3cf9c7dSdf157793 			    len == sizeof (struct termios)) {
974d3cf9c7dSdf157793 				async->async_ttycommon.t_cflag =
975d3cf9c7dSdf157793 				    termiosp->c_cflag;
976d3cf9c7dSdf157793 				kmem_free(termiosp, len);
977d3cf9c7dSdf157793 			} else {
978d3cf9c7dSdf157793 				cmn_err(CE_WARN,
979d3cf9c7dSdf157793 					"su: couldn't get ttymodes property!");
980d3cf9c7dSdf157793 			}
981d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
982d3cf9c7dSdf157793 		}
983d3cf9c7dSdf157793 		async->async_ttycommon.t_iflag = 0;
984d3cf9c7dSdf157793 		async->async_ttycommon.t_iocpending = NULL;
985d3cf9c7dSdf157793 		async->async_ttycommon.t_size.ws_row = 0;
986d3cf9c7dSdf157793 		async->async_ttycommon.t_size.ws_col = 0;
987d3cf9c7dSdf157793 		async->async_ttycommon.t_size.ws_xpixel = 0;
988d3cf9c7dSdf157793 		async->async_ttycommon.t_size.ws_ypixel = 0;
989d3cf9c7dSdf157793 		async->async_dev = *dev;
990d3cf9c7dSdf157793 		async->async_wbufcid = 0;
991d3cf9c7dSdf157793 
992d3cf9c7dSdf157793 		async->async_startc = CSTART;
993d3cf9c7dSdf157793 		async->async_stopc = CSTOP;
994d3cf9c7dSdf157793 		(void) asy_program(asy, ASY_INIT);
995d3cf9c7dSdf157793 	} else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
996d3cf9c7dSdf157793 	    secpolicy_excl_open(cr) != 0) {
997d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
998d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
999d3cf9c7dSdf157793 		return (EBUSY);
1000d3cf9c7dSdf157793 	} else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
1001d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
1002d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
1003d3cf9c7dSdf157793 		return (EBUSY);
1004d3cf9c7dSdf157793 	}
1005d3cf9c7dSdf157793 
1006d3cf9c7dSdf157793 	if (*dev & OUTLINE)
1007d3cf9c7dSdf157793 		async->async_flags |= ASYNC_OUT;
1008d3cf9c7dSdf157793 
1009d3cf9c7dSdf157793 	/* Raise DTR on every open */
1010d3cf9c7dSdf157793 	mcr = INB(MCR);
1011d3cf9c7dSdf157793 	OUTB(MCR, mcr|DTR);
1012d3cf9c7dSdf157793 
1013d3cf9c7dSdf157793 	/*
1014d3cf9c7dSdf157793 	 * Check carrier.
1015d3cf9c7dSdf157793 	 */
1016d3cf9c7dSdf157793 	if (asy->asy_flags & ASY_IGNORE_CD)
1017d3cf9c7dSdf157793 		async->async_ttycommon.t_flags |= TS_SOFTCAR;
1018d3cf9c7dSdf157793 	if ((async->async_ttycommon.t_flags & TS_SOFTCAR) ||
1019d3cf9c7dSdf157793 	    (INB(MSR) & DCD))
1020d3cf9c7dSdf157793 		async->async_flags |= ASYNC_CARR_ON;
1021d3cf9c7dSdf157793 	else
1022d3cf9c7dSdf157793 		async->async_flags &= ~ASYNC_CARR_ON;
1023d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
1024d3cf9c7dSdf157793 
1025d3cf9c7dSdf157793 	/*
1026d3cf9c7dSdf157793 	 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1027d3cf9c7dSdf157793 	 * Quit on interrupt.
1028d3cf9c7dSdf157793 	 */
1029d3cf9c7dSdf157793 	if (!(flag & (FNDELAY|FNONBLOCK)) &&
1030d3cf9c7dSdf157793 	    !(async->async_ttycommon.t_cflag & CLOCAL)) {
1031d3cf9c7dSdf157793 		if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) ||
1032d3cf9c7dSdf157793 		    ((async->async_flags & ASYNC_OUT) &&
1033d3cf9c7dSdf157793 		    !(*dev & OUTLINE))) {
1034d3cf9c7dSdf157793 				async->async_flags |= ASYNC_WOPEN;
1035d3cf9c7dSdf157793 				if (cv_wait_sig(&async->async_flags_cv,
1036d3cf9c7dSdf157793 				    asy->asy_excl) == 0) {
1037d3cf9c7dSdf157793 					async->async_flags &= ~ASYNC_WOPEN;
1038d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl);
1039d3cf9c7dSdf157793 					return (EINTR);
1040d3cf9c7dSdf157793 				}
1041d3cf9c7dSdf157793 				async->async_flags &= ~ASYNC_WOPEN;
1042d3cf9c7dSdf157793 				goto again;
1043d3cf9c7dSdf157793 		}
1044d3cf9c7dSdf157793 	} else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
1045d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
1046d3cf9c7dSdf157793 		return (EBUSY);
1047d3cf9c7dSdf157793 	}
1048d3cf9c7dSdf157793 
1049d3cf9c7dSdf157793 	if (asy->suspended) {
1050d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
1051d3cf9c7dSdf157793 		(void) ddi_dev_is_needed(asy->asy_dip, 0, 1);
1052d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
1053d3cf9c7dSdf157793 	}
1054d3cf9c7dSdf157793 
1055d3cf9c7dSdf157793 	async->async_ttycommon.t_readq = rq;
1056d3cf9c7dSdf157793 	async->async_ttycommon.t_writeq = WR(rq);
1057d3cf9c7dSdf157793 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
1058d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
1059d3cf9c7dSdf157793 	qprocson(rq);
1060d3cf9c7dSdf157793 	async->async_flags |= ASYNC_ISOPEN;
1061d3cf9c7dSdf157793 	async->async_polltid = 0;
1062d3cf9c7dSdf157793 	return (0);
1063d3cf9c7dSdf157793 }
1064d3cf9c7dSdf157793 
1065d3cf9c7dSdf157793 static void
async_progress_check(void * arg)1066d3cf9c7dSdf157793 async_progress_check(void *arg)
1067d3cf9c7dSdf157793 {
1068d3cf9c7dSdf157793 	struct asyncline *async = arg;
1069d3cf9c7dSdf157793 	struct asycom	 *asy = async->async_common;
1070d3cf9c7dSdf157793 	mblk_t *bp;
1071d3cf9c7dSdf157793 
1072d3cf9c7dSdf157793 	/*
1073d3cf9c7dSdf157793 	 * We define "progress" as either waiting on a timed break or delay, or
1074d3cf9c7dSdf157793 	 * having had at least one transmitter interrupt.  If none of these are
1075d3cf9c7dSdf157793 	 * true, then just terminate the output and wake up that close thread.
1076d3cf9c7dSdf157793 	 */
1077d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
1078d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
1079d3cf9c7dSdf157793 	if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
1080d3cf9c7dSdf157793 		async->async_ocnt = 0;
1081d3cf9c7dSdf157793 		async->async_flags &= ~ASYNC_BUSY;
1082d3cf9c7dSdf157793 		async->async_timer = 0;
1083d3cf9c7dSdf157793 		bp = async->async_xmitblk;
1084d3cf9c7dSdf157793 		async->async_xmitblk = NULL;
1085d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
1086d3cf9c7dSdf157793 		if (bp != NULL)
1087d3cf9c7dSdf157793 			freeb(bp);
1088d3cf9c7dSdf157793 		/*
1089d3cf9c7dSdf157793 		 * Since this timer is running, we know that we're in exit(2).
1090d3cf9c7dSdf157793 		 * That means that the user can't possibly be waiting on any
1091d3cf9c7dSdf157793 		 * valid ioctl(2) completion anymore, and we should just flush
1092d3cf9c7dSdf157793 		 * everything.
1093d3cf9c7dSdf157793 		 */
1094d3cf9c7dSdf157793 		flushq(async->async_ttycommon.t_writeq, FLUSHALL);
1095d3cf9c7dSdf157793 		cv_broadcast(&async->async_flags_cv);
1096d3cf9c7dSdf157793 	} else {
1097d3cf9c7dSdf157793 		async->async_flags &= ~ASYNC_PROGRESS;
1098d3cf9c7dSdf157793 		async->async_timer = timeout(async_progress_check, async,
1099d3cf9c7dSdf157793 		    drv_usectohz(su_drain_check));
1100d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
1101d3cf9c7dSdf157793 	}
1102d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
1103d3cf9c7dSdf157793 }
1104d3cf9c7dSdf157793 
1105d3cf9c7dSdf157793 /*
1106d3cf9c7dSdf157793  * Close routine.
1107d3cf9c7dSdf157793  */
1108d3cf9c7dSdf157793 static int
asyclose(queue_t * q,int flag,cred_t * cr __unused)1109730a650aSPeter Tribble asyclose(queue_t *q, int flag, cred_t *cr __unused)
1110d3cf9c7dSdf157793 {
1111d3cf9c7dSdf157793 	struct asyncline *async;
1112d3cf9c7dSdf157793 	struct asycom	 *asy;
1113d3cf9c7dSdf157793 	int icr, lcr;
1114d3cf9c7dSdf157793 	int		nohupcl;
1115d3cf9c7dSdf157793 
1116d3cf9c7dSdf157793 
1117d3cf9c7dSdf157793 #ifdef DEBUG
1118d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_CLOSE)
1119d3cf9c7dSdf157793 		printf("close\n");
1120d3cf9c7dSdf157793 #endif
1121d3cf9c7dSdf157793 	async = q->q_ptr;
1122d3cf9c7dSdf157793 	ASSERT(async != NULL);
1123d3cf9c7dSdf157793 	asy = async->async_common;
1124d3cf9c7dSdf157793 
1125d3cf9c7dSdf157793 	/* get the nohupcl OBP property of this device */
1126d3cf9c7dSdf157793 	nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS,
1127d3cf9c7dSdf157793 	    "nohupcl", 0);
1128d3cf9c7dSdf157793 
1129d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
1130d3cf9c7dSdf157793 	async->async_flags |= ASYNC_CLOSING;
1131d3cf9c7dSdf157793 
1132d3cf9c7dSdf157793 	/*
1133d3cf9c7dSdf157793 	 * Turn off PPS handling early to avoid events occuring during
1134d3cf9c7dSdf157793 	 * close.  Also reset the DCD edge monitoring bit.
1135d3cf9c7dSdf157793 	 */
1136d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
1137d3cf9c7dSdf157793 	asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
1138d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
1139d3cf9c7dSdf157793 
1140d3cf9c7dSdf157793 	/*
1141d3cf9c7dSdf157793 	 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1142d3cf9c7dSdf157793 	 * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
1143d3cf9c7dSdf157793 	 * write queue and there's a timer running, so we don't have to worry
1144d3cf9c7dSdf157793 	 * about them.  For the untimed case, though, the user obviously made a
1145d3cf9c7dSdf157793 	 * mistake, because these are handled immediately.  We'll terminate the
114648bbca81SDaniel Hoffman 	 * break now and honor their implicit request by discarding the rest of
1147d3cf9c7dSdf157793 	 * the data.
1148d3cf9c7dSdf157793 	 */
1149d3cf9c7dSdf157793 	if (!(async->async_flags & ASYNC_BREAK)) {
1150d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
1151d3cf9c7dSdf157793 		lcr = INB(LCR);
1152d3cf9c7dSdf157793 		if (lcr & SETBREAK) {
1153d3cf9c7dSdf157793 			OUTB(LCR, (lcr & ~SETBREAK));
1154d3cf9c7dSdf157793 		}
1155d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
1156d3cf9c7dSdf157793 		if (lcr & SETBREAK)
1157d3cf9c7dSdf157793 			goto nodrain;
1158d3cf9c7dSdf157793 	}
1159d3cf9c7dSdf157793 
1160d3cf9c7dSdf157793 	/*
1161d3cf9c7dSdf157793 	 * If the user told us not to delay the close ("non-blocking"), then
1162d3cf9c7dSdf157793 	 * don't bother trying to drain.
1163d3cf9c7dSdf157793 	 *
1164d3cf9c7dSdf157793 	 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1165d3cf9c7dSdf157793 	 * getting an M_START (since these messages aren't enqueued), and the
1166d3cf9c7dSdf157793 	 * only other way to clear the stop condition is by loss of DCD, which
1167d3cf9c7dSdf157793 	 * would discard the queue data.  Thus, we drop the output data if
1168d3cf9c7dSdf157793 	 * ASYNC_STOPPED is set.
1169d3cf9c7dSdf157793 	 */
1170d3cf9c7dSdf157793 	if ((flag & (FNDELAY|FNONBLOCK)) ||
1171d3cf9c7dSdf157793 	    (async->async_flags & ASYNC_STOPPED)) {
1172d3cf9c7dSdf157793 		goto nodrain;
1173d3cf9c7dSdf157793 	}
1174d3cf9c7dSdf157793 
1175d3cf9c7dSdf157793 	/*
1176d3cf9c7dSdf157793 	 * If there's any pending output, then we have to try to drain it.
1177d3cf9c7dSdf157793 	 * There are two main cases to be handled:
1178d3cf9c7dSdf157793 	 *	- called by close(2): need to drain until done or until
1179d3cf9c7dSdf157793 	 *	  a signal is received.  No timeout.
1180d3cf9c7dSdf157793 	 *	- called by exit(2): need to drain while making progress
1181d3cf9c7dSdf157793 	 *	  or until a timeout occurs.  No signals.
1182d3cf9c7dSdf157793 	 *
1183d3cf9c7dSdf157793 	 * If we can't rely on receiving a signal to get us out of a hung
1184d3cf9c7dSdf157793 	 * session, then we have to use a timer.  In this case, we set a timer
1185d3cf9c7dSdf157793 	 * to check for progress in sending the output data -- all that we ask
1186d3cf9c7dSdf157793 	 * (at each interval) is that there's been some progress made.  Since
1187d3cf9c7dSdf157793 	 * the interrupt routine grabs buffers from the write queue, we can't
1188d3cf9c7dSdf157793 	 * trust async_ocnt.  Instead, we use a flag.
1189d3cf9c7dSdf157793 	 *
1190d3cf9c7dSdf157793 	 * Note that loss of carrier will cause the output queue to be flushed,
1191d3cf9c7dSdf157793 	 * and we'll wake up again and finish normally.
1192d3cf9c7dSdf157793 	 */
1193d3cf9c7dSdf157793 	if (!ddi_can_receive_sig() && su_drain_check != 0) {
1194d3cf9c7dSdf157793 		async->async_flags &= ~ASYNC_PROGRESS;
1195d3cf9c7dSdf157793 		async->async_timer = timeout(async_progress_check, async,
1196d3cf9c7dSdf157793 		    drv_usectohz(su_drain_check));
1197d3cf9c7dSdf157793 	}
1198d3cf9c7dSdf157793 
1199d3cf9c7dSdf157793 	while (async->async_ocnt > 0 ||
1200d3cf9c7dSdf157793 	    async->async_ttycommon.t_writeq->q_first != NULL ||
1201d3cf9c7dSdf157793 	    (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
1202d3cf9c7dSdf157793 		if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0)
1203d3cf9c7dSdf157793 			break;
1204d3cf9c7dSdf157793 	}
1205d3cf9c7dSdf157793 	if (async->async_timer != 0) {
1206d3cf9c7dSdf157793 		(void) untimeout(async->async_timer);
1207d3cf9c7dSdf157793 		async->async_timer = 0;
1208d3cf9c7dSdf157793 	}
1209d3cf9c7dSdf157793 
1210d3cf9c7dSdf157793 nodrain:
1211d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
1212d3cf9c7dSdf157793 
1213d3cf9c7dSdf157793 	/* turn off the loopback mode */
1214d3cf9c7dSdf157793 	if ((async->async_dev != rconsdev) &&
1215d3cf9c7dSdf157793 	    (async->async_dev != kbddev) &&
1216d3cf9c7dSdf157793 	    (async->async_dev != stdindev)) {
1217d3cf9c7dSdf157793 		OUTB(MCR, INB(MCR) & ~ ASY_LOOP);
1218d3cf9c7dSdf157793 	}
1219d3cf9c7dSdf157793 
1220d3cf9c7dSdf157793 	async->async_ocnt = 0;
1221d3cf9c7dSdf157793 	if (async->async_xmitblk != NULL)
1222d3cf9c7dSdf157793 		freeb(async->async_xmitblk);
1223d3cf9c7dSdf157793 	async->async_xmitblk = NULL;
1224d3cf9c7dSdf157793 
1225d3cf9c7dSdf157793 	/*
1226d3cf9c7dSdf157793 	 * If the "nohupcl" OBP property is set for this device, do
1227d3cf9c7dSdf157793 	 * not turn off DTR and RTS no matter what.  Otherwise, if the
1228d3cf9c7dSdf157793 	 * line has HUPCL set or is incompletely opened, turn off DTR
1229d3cf9c7dSdf157793 	 * and RTS to fix the modem line.
1230d3cf9c7dSdf157793 	 */
1231d3cf9c7dSdf157793 	if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) ||
1232d3cf9c7dSdf157793 	    (async->async_flags & ASYNC_WOPEN))) {
1233d3cf9c7dSdf157793 		/* turn off DTR, RTS but NOT interrupt to 386 */
1234d3cf9c7dSdf157793 		OUTB(MCR, OUT2);
1235d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
1236d3cf9c7dSdf157793 		/*
1237d3cf9c7dSdf157793 		 * Don't let an interrupt in the middle of close
1238d3cf9c7dSdf157793 		 * bounce us back to the top; just continue closing
1239d3cf9c7dSdf157793 		 * as if nothing had happened.
1240d3cf9c7dSdf157793 		 */
1241d3cf9c7dSdf157793 		if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0)
1242d3cf9c7dSdf157793 			goto out;
1243d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
1244d3cf9c7dSdf157793 	}
1245d3cf9c7dSdf157793 
1246d3cf9c7dSdf157793 	/*
1247d3cf9c7dSdf157793 	 * If nobody's using it now, turn off receiver interrupts.
1248d3cf9c7dSdf157793 	 */
1249d3cf9c7dSdf157793 	if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
1250d3cf9c7dSdf157793 		icr = INB(ICR);
1251d3cf9c7dSdf157793 		OUTB(ICR, (icr & ~RIEN));
1252d3cf9c7dSdf157793 	}
1253d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
1254d3cf9c7dSdf157793 out:
1255d3cf9c7dSdf157793 	/*
1256d3cf9c7dSdf157793 	 * Clear out device state.
1257d3cf9c7dSdf157793 	 */
1258d3cf9c7dSdf157793 	async->async_flags = 0;
1259d3cf9c7dSdf157793 	ttycommon_close(&async->async_ttycommon);
1260d3cf9c7dSdf157793 	cv_broadcast(&async->async_flags_cv);
1261d3cf9c7dSdf157793 
1262d3cf9c7dSdf157793 	/*
1263d3cf9c7dSdf157793 	 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in
1264d3cf9c7dSdf157793 	 * async_softint or an interrupt was pending when the process
1265d3cf9c7dSdf157793 	 * using the port exited.
1266d3cf9c7dSdf157793 	 */
1267d3cf9c7dSdf157793 	asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
1268d3cf9c7dSdf157793 
1269d3cf9c7dSdf157793 	/*
1270d3cf9c7dSdf157793 	 * Cancel outstanding "bufcall" request.
1271d3cf9c7dSdf157793 	 */
1272d3cf9c7dSdf157793 	if (async->async_wbufcid) {
1273d3cf9c7dSdf157793 		unbufcall(async->async_wbufcid);
1274d3cf9c7dSdf157793 		async->async_wbufcid = 0;
1275d3cf9c7dSdf157793 	}
1276d3cf9c7dSdf157793 
1277d3cf9c7dSdf157793 	/*
1278d3cf9c7dSdf157793 	 * If inperim is true, it means the port is closing while there's
1279d3cf9c7dSdf157793 	 * a pending software interrupt.  async_flags has been zeroed out,
1280d3cf9c7dSdf157793 	 * so this instance of leaveq() needs to be called before we call
1281d3cf9c7dSdf157793 	 * qprocsoff() to disable services on the q.  If inperim is false,
1282d3cf9c7dSdf157793 	 * leaveq() has already been called or we're not in a perimeter.
1283d3cf9c7dSdf157793 	 */
1284d3cf9c7dSdf157793 	if (asy->inperim == B_TRUE) {
1285d3cf9c7dSdf157793 		asy->inperim = B_FALSE;
1286d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
1287d3cf9c7dSdf157793 		leaveq(q);
1288d3cf9c7dSdf157793 	} else {
1289d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
1290d3cf9c7dSdf157793 	}
1291d3cf9c7dSdf157793 
1292d3cf9c7dSdf157793 	/* Note that qprocsoff can't be done until after interrupts are off */
1293d3cf9c7dSdf157793 	qprocsoff(q);
1294d3cf9c7dSdf157793 	q->q_ptr = WR(q)->q_ptr = NULL;
1295d3cf9c7dSdf157793 	async->async_ttycommon.t_readq = NULL;
1296d3cf9c7dSdf157793 	async->async_ttycommon.t_writeq = NULL;
1297d3cf9c7dSdf157793 
1298d3cf9c7dSdf157793 	return (0);
1299d3cf9c7dSdf157793 }
1300d3cf9c7dSdf157793 
1301d3cf9c7dSdf157793 /*
1302d3cf9c7dSdf157793  * Checks to see if the serial port is still transmitting
1303d3cf9c7dSdf157793  * characters.  It returns true when there are characters
1304d3cf9c7dSdf157793  * queued to transmit,  when the holding register contains
1305d3cf9c7dSdf157793  * a byte, or when the shifting register still contains
1306d3cf9c7dSdf157793  * data to send.
1307d3cf9c7dSdf157793  *
1308d3cf9c7dSdf157793  */
1309d3cf9c7dSdf157793 static boolean_t
asy_isbusy(struct asycom * asy)1310d3cf9c7dSdf157793 asy_isbusy(struct asycom *asy)
1311d3cf9c7dSdf157793 {
1312d3cf9c7dSdf157793 	struct asyncline *async;
1313d3cf9c7dSdf157793 
1314d3cf9c7dSdf157793 #ifdef DEBUG
1315d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_EOT)
1316d3cf9c7dSdf157793 		printf("isbusy\n");
1317d3cf9c7dSdf157793 #endif
1318d3cf9c7dSdf157793 	async = (struct asyncline *)asy->asy_priv;
1319d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl));
1320d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
1321d3cf9c7dSdf157793 	return ((async->async_ocnt > 0) ||
1322d3cf9c7dSdf157793 	    ((INB(LSR) & XSRE) == 0));
1323d3cf9c7dSdf157793 }
1324d3cf9c7dSdf157793 
1325d3cf9c7dSdf157793 /*
1326d3cf9c7dSdf157793  * Program the ASY port. Most of the async operation is based on the values
1327d3cf9c7dSdf157793  * of 'c_iflag' and 'c_cflag'.
1328d3cf9c7dSdf157793  */
1329d3cf9c7dSdf157793 static int
asy_program(struct asycom * asy,int mode)1330d3cf9c7dSdf157793 asy_program(struct asycom *asy, int mode)
1331d3cf9c7dSdf157793 {
1332d3cf9c7dSdf157793 	struct asyncline *async;
1333d3cf9c7dSdf157793 	int baudrate, c_flag;
1334d3cf9c7dSdf157793 	int icr, lcr;
1335d3cf9c7dSdf157793 	int ocflags;
1336d3cf9c7dSdf157793 	int error = 0;
1337d3cf9c7dSdf157793 
1338d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl));
1339d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
1340d3cf9c7dSdf157793 
1341d3cf9c7dSdf157793 #ifdef DEBUG
1342d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
1343d3cf9c7dSdf157793 		printf("program\n");
1344d3cf9c7dSdf157793 #endif
1345d3cf9c7dSdf157793 	async = (struct asyncline *)asy->asy_priv;
1346d3cf9c7dSdf157793 
1347d3cf9c7dSdf157793 	baudrate = async->async_ttycommon.t_cflag & CBAUD;
1348d3cf9c7dSdf157793 	if (async->async_ttycommon.t_cflag & CBAUDEXT)
1349d3cf9c7dSdf157793 		baudrate += 16;
1350d3cf9c7dSdf157793 
1351d3cf9c7dSdf157793 	/* Limit baudrate so it can't index out of baudtable */
1352d3cf9c7dSdf157793 	if (baudrate >= N_SU_SPEEDS) baudrate = B9600;
1353d3cf9c7dSdf157793 
1354d3cf9c7dSdf157793 	/*
1355d3cf9c7dSdf157793 	 * If baud rate requested is greater than the speed cap
1356d3cf9c7dSdf157793 	 * or is an unsupported baud rate then reset t_cflag baud
1357d3cf9c7dSdf157793 	 * to the last valid baud rate.  If this is the initial
1358d3cf9c7dSdf157793 	 * pass through asy_program then set it to 9600.
1359d3cf9c7dSdf157793 	 */
1360d3cf9c7dSdf157793 	if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) ||
1361d3cf9c7dSdf157793 	    (baudtable[baudrate] > asy->asy_speed_cap)) {
1362d3cf9c7dSdf157793 		async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1363d3cf9c7dSdf157793 		    ~CIBAUD & ~CIBAUDEXT;
1364d3cf9c7dSdf157793 		if (mode == ASY_INIT) {
1365d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= B9600;
1366b4a51ac1Skc28005 			async->async_ttycommon.t_cflag |= B9600 << IBSHIFT;
1367d3cf9c7dSdf157793 			baudrate = B9600;
1368d3cf9c7dSdf157793 		} else {
1369d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |=
1370d3cf9c7dSdf157793 			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1371d3cf9c7dSdf157793 			    CIBAUD | CIBAUDEXT));
1372d3cf9c7dSdf157793 			error = EINVAL;
1373d3cf9c7dSdf157793 			goto end;
1374d3cf9c7dSdf157793 		}
1375b4a51ac1Skc28005 	}
1376d3cf9c7dSdf157793 
1377b4a51ac1Skc28005 	/*
1378b4a51ac1Skc28005 	 * If CIBAUD and CIBAUDEXT are zero then we should set them to
1379b4a51ac1Skc28005 	 * the equivelant output baud bits.  Else, if CIBAUD and CIBAUDEXT
1380b4a51ac1Skc28005 	 * don't match CBAUD and CBAUDEXT respectively then we should
1381b4a51ac1Skc28005 	 * notify the requestor that we do not support split speeds.
1382b4a51ac1Skc28005 	 */
1383b4a51ac1Skc28005 	if ((async->async_ttycommon.t_cflag  & (CIBAUD|CIBAUDEXT)) == 0) {
1384b4a51ac1Skc28005 		async->async_ttycommon.t_cflag |=
1385b4a51ac1Skc28005 		    (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT;
1386b4a51ac1Skc28005 		if (async->async_ttycommon.t_cflag & CBAUDEXT)
1387d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |= CIBAUDEXT;
1388d3cf9c7dSdf157793 	} else {
1389b4a51ac1Skc28005 		if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) !=
1390b4a51ac1Skc28005 		    (async->async_ttycommon.t_cflag & CIBAUD)) ||
1391b4a51ac1Skc28005 		    !(((async->async_ttycommon.t_cflag & (CBAUDEXT |
1392b4a51ac1Skc28005 		    CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) ||
1393b4a51ac1Skc28005 		    ((async->async_ttycommon.t_cflag & (CBAUDEXT |
1394b4a51ac1Skc28005 		    CIBAUDEXT)) == 0))) {
1395b4a51ac1Skc28005 			async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1396b4a51ac1Skc28005 			    ~CIBAUD & ~CIBAUDEXT;
1397d3cf9c7dSdf157793 			async->async_ttycommon.t_cflag |=
1398b4a51ac1Skc28005 			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1399b4a51ac1Skc28005 			    CIBAUD | CIBAUDEXT));
1400b4a51ac1Skc28005 			error = EINVAL;
1401b4a51ac1Skc28005 			goto end;
1402d3cf9c7dSdf157793 		}
1403d3cf9c7dSdf157793 	}
1404d3cf9c7dSdf157793 
1405d3cf9c7dSdf157793 	c_flag = async->async_ttycommon.t_cflag &
1406d3cf9c7dSdf157793 	    (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD |
1407d3cf9c7dSdf157793 	    CBAUDEXT | CIBAUD | CIBAUDEXT);
140817ea09c7Sanovick 
140917ea09c7Sanovick 	/* disable interrupts, see EXAR bug */
1410f63f7506Sanovick 	if (asy->asy_hwtype == ASY16C554D)
1411f63f7506Sanovick 		OUTB(SPR, 0);
141217ea09c7Sanovick 	OUTB(ICR, 0);
1413d3cf9c7dSdf157793 
1414d3cf9c7dSdf157793 	ocflags = asy->asy_ocflags;
1415d3cf9c7dSdf157793 
1416d3cf9c7dSdf157793 	/* flush/reset the status registers */
1417d3cf9c7dSdf157793 	if (mode == ASY_INIT) {
1418d3cf9c7dSdf157793 		(void) INB(DAT);
1419d3cf9c7dSdf157793 		(void) INB(ISR);
1420d3cf9c7dSdf157793 		(void) INB(LSR);
1421d3cf9c7dSdf157793 		(void) INB(MSR);
1422d3cf9c7dSdf157793 	}
1423d3cf9c7dSdf157793 
1424d3cf9c7dSdf157793 	if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
1425d3cf9c7dSdf157793 		/* Set line control */
1426d3cf9c7dSdf157793 		lcr = INB(LCR);
1427d3cf9c7dSdf157793 		lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
1428d3cf9c7dSdf157793 
1429d3cf9c7dSdf157793 		if (c_flag & CSTOPB)
1430d3cf9c7dSdf157793 			lcr |= STB;	/* 2 stop bits */
1431d3cf9c7dSdf157793 
1432d3cf9c7dSdf157793 		if (c_flag & PARENB)
1433d3cf9c7dSdf157793 			lcr |= PEN;
1434d3cf9c7dSdf157793 
1435d3cf9c7dSdf157793 		if ((c_flag & PARODD) == 0)
1436d3cf9c7dSdf157793 			lcr |= EPS;
1437d3cf9c7dSdf157793 
1438d3cf9c7dSdf157793 		switch (c_flag & CSIZE) {
1439d3cf9c7dSdf157793 		case CS5:
1440d3cf9c7dSdf157793 			lcr |= BITS5;
1441d3cf9c7dSdf157793 			break;
1442d3cf9c7dSdf157793 		case CS6:
1443d3cf9c7dSdf157793 			lcr |= BITS6;
1444d3cf9c7dSdf157793 			break;
1445d3cf9c7dSdf157793 		case CS7:
1446d3cf9c7dSdf157793 			lcr |= BITS7;
1447d3cf9c7dSdf157793 			break;
1448d3cf9c7dSdf157793 		case CS8:
1449d3cf9c7dSdf157793 			lcr |= BITS8;
1450d3cf9c7dSdf157793 			break;
1451d3cf9c7dSdf157793 		}
1452d3cf9c7dSdf157793 
1453d3cf9c7dSdf157793 		/* set the baud rate when the rate is NOT B0 */
1454d3cf9c7dSdf157793 		if (baudrate != 0) {
1455d3cf9c7dSdf157793 			OUTB(LCR, DLAB);
1456d3cf9c7dSdf157793 			OUTB(DAT, (asyspdtab[baudrate] *
1457d3cf9c7dSdf157793 			    asy->asy_baud_divisor_factor) & 0xff);
1458d3cf9c7dSdf157793 			OUTB(ICR, ((asyspdtab[baudrate] *
1459d3cf9c7dSdf157793 			    asy->asy_baud_divisor_factor) >> 8) & 0xff);
1460d3cf9c7dSdf157793 		}
1461d3cf9c7dSdf157793 		/* set the line control modes */
1462d3cf9c7dSdf157793 		OUTB(LCR, lcr);
1463d3cf9c7dSdf157793 
1464d3cf9c7dSdf157793 		/*
1465d3cf9c7dSdf157793 		 * if transitioning from CREAD off to CREAD on,
1466d3cf9c7dSdf157793 		 * flush the FIFO buffer if we have one.
1467d3cf9c7dSdf157793 		 */
1468d3cf9c7dSdf157793 		if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) {
1469d3cf9c7dSdf157793 			if (asy->asy_use_fifo == FIFO_ON) {
1470d3cf9c7dSdf157793 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
1471d3cf9c7dSdf157793 				    (asy->asy_trig_level & 0xff));
1472d3cf9c7dSdf157793 			}
1473d3cf9c7dSdf157793 		}
1474d3cf9c7dSdf157793 
1475d3cf9c7dSdf157793 		/* remember the new cflags */
1476d3cf9c7dSdf157793 		asy->asy_ocflags = c_flag & ~CLOCAL;
1477d3cf9c7dSdf157793 	}
1478d3cf9c7dSdf157793 
1479d3cf9c7dSdf157793 	/* whether or not CLOCAL is set, modify the modem control lines */
1480d3cf9c7dSdf157793 	if (baudrate == 0)
1481d3cf9c7dSdf157793 		/* B0 has been issued, lower DTR */
1482d3cf9c7dSdf157793 		OUTB(MCR, RTS|OUT2);
1483d3cf9c7dSdf157793 	else
1484d3cf9c7dSdf157793 		/* raise DTR */
1485d3cf9c7dSdf157793 		OUTB(MCR, DTR|RTS|OUT2);
1486d3cf9c7dSdf157793 
1487d3cf9c7dSdf157793 	/*
1488d3cf9c7dSdf157793 	 * Call the modem status interrupt handler to check for the carrier
1489d3cf9c7dSdf157793 	 * in case CLOCAL was turned off after the carrier came on.
1490d3cf9c7dSdf157793 	 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
1491d3cf9c7dSdf157793 	 */
1492d3cf9c7dSdf157793 	async_msint(asy);
1493d3cf9c7dSdf157793 
1494d3cf9c7dSdf157793 	/* Set interrupt control */
1495d3cf9c7dSdf157793 	if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
1496d3cf9c7dSdf157793 		/*
1497d3cf9c7dSdf157793 		 * direct-wired line ignores DCD, so we don't enable modem
1498d3cf9c7dSdf157793 		 * status interrupts.
1499d3cf9c7dSdf157793 		 */
1500d3cf9c7dSdf157793 		icr = (TIEN | SIEN);
1501d3cf9c7dSdf157793 	else
1502d3cf9c7dSdf157793 		icr = (TIEN | SIEN | MIEN);
1503d3cf9c7dSdf157793 
1504d3cf9c7dSdf157793 	if (c_flag & CREAD)
1505d3cf9c7dSdf157793 		icr |= RIEN;
1506d3cf9c7dSdf157793 
1507d3cf9c7dSdf157793 	OUTB(ICR, icr);
1508d3cf9c7dSdf157793 end:
1509d3cf9c7dSdf157793 	return (error);
1510d3cf9c7dSdf157793 }
1511d3cf9c7dSdf157793 
1512d3cf9c7dSdf157793 /*
15130280efdcSzk194757  * Polled mode support -- all functions called with interrupts
15140280efdcSzk194757  * disabled.
15150280efdcSzk194757  */
15160280efdcSzk194757 
15170280efdcSzk194757 static void
asyputchar(cons_polledio_arg_t arg,uchar_t c)15180280efdcSzk194757 asyputchar(cons_polledio_arg_t arg, uchar_t c)
15190280efdcSzk194757 {
15200280efdcSzk194757 	struct asycom *asy = (struct asycom *)arg;
15210280efdcSzk194757 
15220280efdcSzk194757 	/*
15230280efdcSzk194757 	 * If we see a line feed make sure to also
15240280efdcSzk194757 	 * put out a carriage return.
15250280efdcSzk194757 	 */
15260280efdcSzk194757 	if (c == '\n')
15270280efdcSzk194757 		asyputchar(arg, '\r');
15280280efdcSzk194757 
15290280efdcSzk194757 	while ((INB(LSR) & XHRE) == 0) {
15300280efdcSzk194757 		/* wait for the transmission to complete */
15310280efdcSzk194757 		drv_usecwait(10);
15320280efdcSzk194757 	}
15330280efdcSzk194757 
15340280efdcSzk194757 	/* ouput the character */
15350280efdcSzk194757 	OUTB(DAT, c);
15360280efdcSzk194757 }
15370280efdcSzk194757 
15380280efdcSzk194757 /*
15390280efdcSzk194757  * Determines if there is a character avaialable for
15400280efdcSzk194757  * reading.
15410280efdcSzk194757  */
15420280efdcSzk194757 static boolean_t
asyischar(cons_polledio_arg_t arg)15430280efdcSzk194757 asyischar(cons_polledio_arg_t arg)
15440280efdcSzk194757 {
15450280efdcSzk194757 	struct asycom *asy = (struct asycom *)arg;
15460280efdcSzk194757 	return ((INB(LSR) & RCA) != 0);
15470280efdcSzk194757 }
15480280efdcSzk194757 
15490280efdcSzk194757 static int
asygetchar(cons_polledio_arg_t arg)15500280efdcSzk194757 asygetchar(cons_polledio_arg_t arg)
15510280efdcSzk194757 {
15520280efdcSzk194757 	struct asycom *asy = (struct asycom *)arg;
15530280efdcSzk194757 
15540280efdcSzk194757 	/*
15550280efdcSzk194757 	 * Spin waiting for a character to be
15560280efdcSzk194757 	 * available to read.
15570280efdcSzk194757 	 */
15580280efdcSzk194757 	while (!asyischar(arg))
15590280efdcSzk194757 		drv_usecwait(10);
15600280efdcSzk194757 
15610280efdcSzk194757 	return (INB(DAT));
15620280efdcSzk194757 }
15630280efdcSzk194757 
15640280efdcSzk194757 /*
15650280efdcSzk194757  * Called when machine is transitioning to polled mode
15660280efdcSzk194757  */
15670280efdcSzk194757 static void
asy_polled_enter(cons_polledio_arg_t arg)15680280efdcSzk194757 asy_polled_enter(cons_polledio_arg_t arg)
15690280efdcSzk194757 {
15700280efdcSzk194757 	struct asycom *asy = (struct asycom *)arg;
15710280efdcSzk194757 
15720280efdcSzk194757 	mutex_enter(asy->asy_excl);
15730280efdcSzk194757 	mutex_enter(asy->asy_excl_hi);
15740280efdcSzk194757 
15750280efdcSzk194757 	/*
15760280efdcSzk194757 	 * If this is the first time that asy_polled_enter()
15770280efdcSzk194757 	 * has been called, during this transition request,
15780280efdcSzk194757 	 * save the ICR. Clear the software interrupt
15790280efdcSzk194757 	 * flags since we won't be able to handle these when
15800280efdcSzk194757 	 * we are in polled mode.
15810280efdcSzk194757 	 */
15820280efdcSzk194757 	if (!asy->polled_enter) {
15830280efdcSzk194757 		asy->polled_enter = B_TRUE;
15840280efdcSzk194757 		asy->polled_icr = INB(ICR);
15850280efdcSzk194757 
15860280efdcSzk194757 		/* Disable HW interrupts */
15870280efdcSzk194757 		if (asy->asy_hwtype == ASY16C554D)
15880280efdcSzk194757 			OUTB(SPR, 0);
15890280efdcSzk194757 		OUTB(ICR, 0);
15900280efdcSzk194757 
15910280efdcSzk194757 		asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
15920280efdcSzk194757 	}
15930280efdcSzk194757 	mutex_exit(asy->asy_excl_hi);
15940280efdcSzk194757 	mutex_exit(asy->asy_excl);
15950280efdcSzk194757 }
15960280efdcSzk194757 
15970280efdcSzk194757 /*
15980280efdcSzk194757  * Called when machine is transitioning from polled mode.
15990280efdcSzk194757  */
16000280efdcSzk194757 static void
asy_polled_exit(cons_polledio_arg_t arg)16010280efdcSzk194757 asy_polled_exit(cons_polledio_arg_t arg)
16020280efdcSzk194757 {
16030280efdcSzk194757 	struct asycom *asy = (struct asycom *)arg;
16040280efdcSzk194757 
16050280efdcSzk194757 	mutex_enter(asy->asy_excl);
16060280efdcSzk194757 	mutex_enter(asy->asy_excl_hi);
16070280efdcSzk194757 
16080280efdcSzk194757 	/* Restore the ICR */
16090280efdcSzk194757 	OUTB(ICR, asy->polled_icr);
16100280efdcSzk194757 
16110280efdcSzk194757 	/*
16120280efdcSzk194757 	 * We have finished this polled IO transition.
16130280efdcSzk194757 	 * Set polled_enter to B_FALSE to note this.
16140280efdcSzk194757 	 */
16150280efdcSzk194757 	asy->polled_enter = B_FALSE;
16160280efdcSzk194757 	mutex_exit(asy->asy_excl_hi);
16170280efdcSzk194757 	mutex_exit(asy->asy_excl);
16180280efdcSzk194757 }
16190280efdcSzk194757 
16200280efdcSzk194757 /*
1621d3cf9c7dSdf157793  * asyintr() is the High Level Interrupt Handler.
1622d3cf9c7dSdf157793  *
1623d3cf9c7dSdf157793  * There are four different interrupt types indexed by ISR register values:
1624d3cf9c7dSdf157793  *		0: modem
1625d3cf9c7dSdf157793  *		1: Tx holding register is empty, ready for next char
1626d3cf9c7dSdf157793  *		2: Rx register now holds a char to be picked up
1627d3cf9c7dSdf157793  *		3: error or break on line
1628d3cf9c7dSdf157793  * This routine checks the Bit 0 (interrupt-not-pending) to determine if
1629d3cf9c7dSdf157793  * the interrupt is from this port.
1630d3cf9c7dSdf157793  */
1631d3cf9c7dSdf157793 uint_t
asyintr(caddr_t argasy)1632d3cf9c7dSdf157793 asyintr(caddr_t argasy)
1633d3cf9c7dSdf157793 {
1634d3cf9c7dSdf157793 	struct asycom		*asy = (struct asycom *)argasy;
1635d3cf9c7dSdf157793 	struct asyncline	*async;
1636d3cf9c7dSdf157793 	int			ret_status = DDI_INTR_UNCLAIMED;
1637d3cf9c7dSdf157793 	uchar_t			interrupt_id, lsr;
1638d3cf9c7dSdf157793 
1639d3cf9c7dSdf157793 	interrupt_id = INB(ISR) & 0x0F;
1640d3cf9c7dSdf157793 	async = (struct asyncline *)asy->asy_priv;
1641d3cf9c7dSdf157793 	if ((async == NULL) ||
1642d3cf9c7dSdf157793 	    !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
1643d3cf9c7dSdf157793 		if (interrupt_id & NOINTERRUPT)  {
1644d3cf9c7dSdf157793 			return (DDI_INTR_UNCLAIMED);
1645d3cf9c7dSdf157793 		} else {
1646d3cf9c7dSdf157793 			lsr = INB(LSR);
1647d3cf9c7dSdf157793 			if ((lsr & BRKDET) &&
1648d3cf9c7dSdf157793 			    ((abort_enable == KIOCABORTENABLE) &&
1649d3cf9c7dSdf157793 			    (async->async_dev == rconsdev)))
1650d3cf9c7dSdf157793 				abort_sequence_enter((char *)NULL);
1651d3cf9c7dSdf157793 			else {
1652d3cf9c7dSdf157793 				/* reset line status */
1653d3cf9c7dSdf157793 				(void) INB(LSR);
1654d3cf9c7dSdf157793 				/* discard any data */
1655d3cf9c7dSdf157793 				(void) INB(DAT);
1656d3cf9c7dSdf157793 				/* reset modem status */
1657d3cf9c7dSdf157793 				(void) INB(MSR);
1658d3cf9c7dSdf157793 				return (DDI_INTR_CLAIMED);
1659d3cf9c7dSdf157793 			}
1660d3cf9c7dSdf157793 		}
1661d3cf9c7dSdf157793 	}
1662d3cf9c7dSdf157793 	/*
1663d3cf9c7dSdf157793 	 * Spurious interrupts happen in this driver
1664d3cf9c7dSdf157793 	 * because of the transmission on serial port not handled
1665d3cf9c7dSdf157793 	 * properly.
1666d3cf9c7dSdf157793 	 *
1667d3cf9c7dSdf157793 	 * The reasons for Spurious interrupts are:
1668d3cf9c7dSdf157793 	 *    1. There is a path in async_nstart which transmits
1669d3cf9c7dSdf157793 	 *	 characters without going through interrupt services routine
1670d3cf9c7dSdf157793 	 *	 which causes spurious interrupts to happen.
1671d3cf9c7dSdf157793 	 *    2. In the async_txint more than one character is sent
1672d3cf9c7dSdf157793 	 *	 in one interrupt service.
1673d3cf9c7dSdf157793 	 *    3. In async_rxint more than one characters are received in
1674d3cf9c7dSdf157793 	 *	 in one interrupt service.
1675d3cf9c7dSdf157793 	 *
1676d3cf9c7dSdf157793 	 * Hence we have flags to indicate that such scenerio has happened.
1677d3cf9c7dSdf157793 	 * and claim only such interrupts and others we donot claim it
1678d3cf9c7dSdf157793 	 * as it could be a indicator of some hardware problem.
1679d3cf9c7dSdf157793 	 *
1680d3cf9c7dSdf157793 	 */
1681d3cf9c7dSdf157793 	if (interrupt_id & NOINTERRUPT) {
1682d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
1683d3cf9c7dSdf157793 		if ((asy->asy_xmit_count > 1) ||
1684d3cf9c7dSdf157793 		    (asy->asy_out_of_band_xmit > 0) ||
1685d3cf9c7dSdf157793 		    (asy->asy_rx_count > 1)) {
1686d3cf9c7dSdf157793 			asy->asy_xmit_count = 0;
1687d3cf9c7dSdf157793 			asy->asy_out_of_band_xmit = 0;
1688d3cf9c7dSdf157793 			asy->asy_rx_count = 0;
1689d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
1690d3cf9c7dSdf157793 			return (DDI_INTR_CLAIMED);
1691d3cf9c7dSdf157793 		} else {
1692d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
1693d3cf9c7dSdf157793 			return (DDI_INTR_UNCLAIMED);
1694d3cf9c7dSdf157793 		}
1695d3cf9c7dSdf157793 	}
1696d3cf9c7dSdf157793 	ret_status = DDI_INTR_CLAIMED;
1697d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
1698d3cf9c7dSdf157793 	if (asy->asy_hwtype == ASY82510)
1699d3cf9c7dSdf157793 		OUTB(ISR, 0x00); /* set bank 0 */
1700d3cf9c7dSdf157793 
1701d3cf9c7dSdf157793 #ifdef DEBUG
1702d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_INTR)
1703d3cf9c7dSdf157793 		prom_printf("l");
1704d3cf9c7dSdf157793 #endif
1705d3cf9c7dSdf157793 	lsr = INB(LSR);
1706d3cf9c7dSdf157793 	switch (interrupt_id) {
1707d3cf9c7dSdf157793 	case RxRDY:
1708d3cf9c7dSdf157793 	case RSTATUS:
1709d3cf9c7dSdf157793 	case FFTMOUT:
1710d3cf9c7dSdf157793 		/* receiver interrupt or receiver errors */
1711d3cf9c7dSdf157793 		async_rxint(asy, lsr);
1712d3cf9c7dSdf157793 		break;
1713d3cf9c7dSdf157793 	case TxRDY:
1714d3cf9c7dSdf157793 		/* transmit interrupt */
1715d3cf9c7dSdf157793 		async_txint(asy, lsr);
1716d3cf9c7dSdf157793 		break;
1717d3cf9c7dSdf157793 	case MSTATUS:
1718d3cf9c7dSdf157793 		/* modem status interrupt */
1719d3cf9c7dSdf157793 		async_msint(asy);
1720d3cf9c7dSdf157793 		break;
1721d3cf9c7dSdf157793 	}
1722d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
1723d3cf9c7dSdf157793 	return (ret_status);
1724d3cf9c7dSdf157793 }
1725d3cf9c7dSdf157793 
1726d3cf9c7dSdf157793 /*
1727d3cf9c7dSdf157793  * Transmitter interrupt service routine.
1728d3cf9c7dSdf157793  * If there is more data to transmit in the current pseudo-DMA block,
1729d3cf9c7dSdf157793  * send the next character if output is not stopped or draining.
1730d3cf9c7dSdf157793  * Otherwise, queue up a soft interrupt.
1731d3cf9c7dSdf157793  *
1732d3cf9c7dSdf157793  * XXX -  Needs review for HW FIFOs.
1733d3cf9c7dSdf157793  */
1734d3cf9c7dSdf157793 static void
async_txint(struct asycom * asy,uchar_t lsr)1735d3cf9c7dSdf157793 async_txint(struct asycom *asy, uchar_t lsr)
1736d3cf9c7dSdf157793 {
1737d3cf9c7dSdf157793 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1738d3cf9c7dSdf157793 	int		fifo_len;
1739d3cf9c7dSdf157793 	int		xmit_progress;
1740d3cf9c7dSdf157793 
1741d3cf9c7dSdf157793 	asycheckflowcontrol_hw(asy);
1742d3cf9c7dSdf157793 
1743d3cf9c7dSdf157793 	/*
1744d3cf9c7dSdf157793 	 * If ASYNC_BREAK has been set, return to asyintr()'s context to
1745d3cf9c7dSdf157793 	 * claim the interrupt without performing any action.
1746d3cf9c7dSdf157793 	 */
1747d3cf9c7dSdf157793 	if (async->async_flags & ASYNC_BREAK)
1748d3cf9c7dSdf157793 		return;
1749d3cf9c7dSdf157793 
1750d3cf9c7dSdf157793 	fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
1751d3cf9c7dSdf157793 
1752d3cf9c7dSdf157793 	/*
1753d3cf9c7dSdf157793 	 * Check for flow control and do the needed action.
1754d3cf9c7dSdf157793 	 */
1755d3cf9c7dSdf157793 	if (asycheckflowcontrol_sw(asy)) {
1756d3cf9c7dSdf157793 		return;
1757d3cf9c7dSdf157793 	}
1758d3cf9c7dSdf157793 
1759d3cf9c7dSdf157793 	if (async->async_ocnt > 0 &&
1760d3cf9c7dSdf157793 	    !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) {
1761d3cf9c7dSdf157793 		xmit_progress = 0;
1762d3cf9c7dSdf157793 		while (fifo_len > 0 && async->async_ocnt > 0) {
1763d3cf9c7dSdf157793 			if (lsr & XHRE) {
1764d3cf9c7dSdf157793 				OUTB(DAT, *async->async_optr++);
1765d3cf9c7dSdf157793 				fifo_len--;
1766d3cf9c7dSdf157793 				async->async_ocnt--;
1767d3cf9c7dSdf157793 				xmit_progress++;
1768d3cf9c7dSdf157793 			}
1769d3cf9c7dSdf157793 			/*
1770d3cf9c7dSdf157793 			 * Reading the lsr, (moved reading at the end of
1771d3cf9c7dSdf157793 			 * while loop) as already we have read once at
1772d3cf9c7dSdf157793 			 * the beginning of interrupt service
1773d3cf9c7dSdf157793 			 */
1774d3cf9c7dSdf157793 			lsr = INB(LSR);
1775d3cf9c7dSdf157793 		}
1776d3cf9c7dSdf157793 		asy->asy_xmit_count = xmit_progress;
1777d3cf9c7dSdf157793 		if (xmit_progress > 0)
1778d3cf9c7dSdf157793 			async->async_flags |= ASYNC_PROGRESS;
1779d3cf9c7dSdf157793 	}
1780d3cf9c7dSdf157793 
1781d3cf9c7dSdf157793 	if (fifo_len == 0) {
1782d3cf9c7dSdf157793 		return;
1783d3cf9c7dSdf157793 	}
1784d3cf9c7dSdf157793 
1785d3cf9c7dSdf157793 
1786d3cf9c7dSdf157793 	ASYSETSOFT(asy);
1787d3cf9c7dSdf157793 }
1788d3cf9c7dSdf157793 
1789d3cf9c7dSdf157793 /*
1790d3cf9c7dSdf157793  * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
1791d3cf9c7dSdf157793  * error interrupt.
1792d3cf9c7dSdf157793  * Try to put the character into the circular buffer for this line; if it
1793d3cf9c7dSdf157793  * overflows, indicate a circular buffer overrun. If this port is always
1794d3cf9c7dSdf157793  * to be serviced immediately, or the character is a STOP character, or
1795d3cf9c7dSdf157793  * more than 15 characters have arrived, queue up a soft interrupt to
1796d3cf9c7dSdf157793  * drain the circular buffer.
1797d3cf9c7dSdf157793  * XXX - needs review for hw FIFOs support.
1798d3cf9c7dSdf157793  */
1799d3cf9c7dSdf157793 
1800d3cf9c7dSdf157793 static void
async_rxint(struct asycom * asy,uchar_t lsr)1801d3cf9c7dSdf157793 async_rxint(struct asycom *asy, uchar_t lsr)
1802d3cf9c7dSdf157793 {
1803d3cf9c7dSdf157793 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1804d3cf9c7dSdf157793 	uchar_t c = 0;
1805d3cf9c7dSdf157793 	uint_t s = 0, needsoft = 0;
1806d3cf9c7dSdf157793 	register tty_common_t *tp;
1807d3cf9c7dSdf157793 
1808d3cf9c7dSdf157793 	tp = &async->async_ttycommon;
1809d3cf9c7dSdf157793 	if (!(tp->t_cflag & CREAD)) {
1810d3cf9c7dSdf157793 		if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1811d3cf9c7dSdf157793 			(void) (INB(DAT) & 0xff);
1812d3cf9c7dSdf157793 		}
1813d3cf9c7dSdf157793 		return; /* line is not open for read? */
1814d3cf9c7dSdf157793 	}
1815d3cf9c7dSdf157793 	asy->asy_rx_count = 0;
1816d3cf9c7dSdf157793 	while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
1817d3cf9c7dSdf157793 		c = 0;
1818d3cf9c7dSdf157793 		s = 0;
1819d3cf9c7dSdf157793 		asy->asy_rx_count++;
1820d3cf9c7dSdf157793 		if (lsr & RCA) {
1821d3cf9c7dSdf157793 			c = INB(DAT) & 0xff;
1822d3cf9c7dSdf157793 			/*
1823d3cf9c7dSdf157793 			 * Even a single character is received
1824d3cf9c7dSdf157793 			 * we need Soft interrupt to pass it to
1825d3cf9c7dSdf157793 			 * higher layers.
1826d3cf9c7dSdf157793 			 */
1827d3cf9c7dSdf157793 			needsoft = 1;
1828d3cf9c7dSdf157793 		}
1829d3cf9c7dSdf157793 
1830d3cf9c7dSdf157793 		/* Check for character break sequence */
1831d3cf9c7dSdf157793 		if ((abort_enable == KIOCABORTALTERNATE) &&
1832d3cf9c7dSdf157793 		    (async->async_dev == rconsdev)) {
1833d3cf9c7dSdf157793 			if (abort_charseq_recognize(c))
1834d3cf9c7dSdf157793 				abort_sequence_enter((char *)NULL);
1835d3cf9c7dSdf157793 			}
1836d3cf9c7dSdf157793 
1837d3cf9c7dSdf157793 		/* Handle framing errors */
1838d3cf9c7dSdf157793 		if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
1839d3cf9c7dSdf157793 			if (lsr & PARERR) {
1840d3cf9c7dSdf157793 				if (tp->t_iflag & INPCK) /* parity enabled */
1841d3cf9c7dSdf157793 					s |= PERROR;
1842d3cf9c7dSdf157793 			}
1843d3cf9c7dSdf157793 			if (lsr & (FRMERR|BRKDET))
1844d3cf9c7dSdf157793 				s |= FRERROR;
1845d3cf9c7dSdf157793 			if (lsr & OVRRUN) {
1846d3cf9c7dSdf157793 				async->async_hw_overrun = 1;
1847d3cf9c7dSdf157793 				s |= OVERRUN;
1848d3cf9c7dSdf157793 			}
1849d3cf9c7dSdf157793 		}
1850d3cf9c7dSdf157793 
1851d3cf9c7dSdf157793 		if (s == 0)
1852d3cf9c7dSdf157793 			if ((tp->t_iflag & PARMRK) &&
1853d3cf9c7dSdf157793 			    !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
1854d3cf9c7dSdf157793 			    (c == 0377))
1855d3cf9c7dSdf157793 				if (RING_POK(async, 2)) {
1856d3cf9c7dSdf157793 					RING_PUT(async, 0377);
1857d3cf9c7dSdf157793 					RING_PUT(async, c);
1858d3cf9c7dSdf157793 				} else
1859d3cf9c7dSdf157793 					async->async_sw_overrun = 1;
1860d3cf9c7dSdf157793 			else
1861d3cf9c7dSdf157793 				if (RING_POK(async, 1))
1862d3cf9c7dSdf157793 					RING_PUT(async, c);
1863d3cf9c7dSdf157793 				else
1864d3cf9c7dSdf157793 					async->async_sw_overrun = 1;
1865d3cf9c7dSdf157793 		else
1866d3cf9c7dSdf157793 			if (s & FRERROR) { /* Handle framing errors */
1867d3cf9c7dSdf157793 				if (c == 0)  {
1868d3cf9c7dSdf157793 		/* Look for break on kbd, stdin, or rconsdev */
1869d3cf9c7dSdf157793 					if ((async->async_dev == kbddev) ||
1870d3cf9c7dSdf157793 					    ((async->async_dev == rconsdev) ||
1871d3cf9c7dSdf157793 					    (async->async_dev == stdindev)) &&
1872d3cf9c7dSdf157793 					    (abort_enable !=
1873d3cf9c7dSdf157793 					    KIOCABORTALTERNATE))
1874d3cf9c7dSdf157793 						abort_sequence_enter((char *)0);
1875d3cf9c7dSdf157793 					else
1876d3cf9c7dSdf157793 						async->async_break++;
1877d3cf9c7dSdf157793 				} else {
1878d3cf9c7dSdf157793 					if (RING_POK(async, 1))
1879d3cf9c7dSdf157793 						RING_MARK(async, c, s);
1880d3cf9c7dSdf157793 					else
1881d3cf9c7dSdf157793 						async->async_sw_overrun = 1;
1882d3cf9c7dSdf157793 				}
1883d3cf9c7dSdf157793 			} else  { /* Parity errors  handled by ldterm */
1884d3cf9c7dSdf157793 				if (RING_POK(async, 1))
1885d3cf9c7dSdf157793 					RING_MARK(async, c, s);
1886d3cf9c7dSdf157793 				else
1887d3cf9c7dSdf157793 					async->async_sw_overrun = 1;
1888d3cf9c7dSdf157793 			}
1889d3cf9c7dSdf157793 		lsr = INB(LSR);
1890d3cf9c7dSdf157793 		if (asy->asy_rx_count > 16) break;
1891d3cf9c7dSdf157793 	}
1892d3cf9c7dSdf157793 	/* Check whether there is a request for hw/sw inbound/input flow ctrl */
1893d3cf9c7dSdf157793 	if ((async->async_ttycommon.t_cflag & CRTSXOFF) ||
1894d3cf9c7dSdf157793 	    (async->async_ttycommon.t_iflag & IXOFF))
1895d3cf9c7dSdf157793 		if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) {
1896d3cf9c7dSdf157793 #ifdef DEBUG
1897d3cf9c7dSdf157793 			if (asydebug & ASY_DEBUG_HFLOW)
1898d3cf9c7dSdf157793 				printf("asy%d: hardware flow stop input.\n",
1899d3cf9c7dSdf157793 				    UNIT(async->async_dev));
1900d3cf9c7dSdf157793 #endif
1901d3cf9c7dSdf157793 			async->async_flags |= ASYNC_HW_IN_FLOW;
1902d3cf9c7dSdf157793 			async->async_flowc = async->async_stopc;
1903d3cf9c7dSdf157793 			async->async_ringbuf_overflow = 1;
1904d3cf9c7dSdf157793 		}
1905d3cf9c7dSdf157793 
1906d3cf9c7dSdf157793 	if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
1907d3cf9c7dSdf157793 	    (RING_FRAC(async)) || (async->async_polltid == 0))
1908d3cf9c7dSdf157793 		ASYSETSOFT(asy);	/* need a soft interrupt */
1909d3cf9c7dSdf157793 }
1910d3cf9c7dSdf157793 
1911d3cf9c7dSdf157793 /*
1912d3cf9c7dSdf157793  * Interrupt on port: handle PPS event.  This function is only called
1913d3cf9c7dSdf157793  * for a port on which PPS event handling has been enabled.
1914d3cf9c7dSdf157793  */
1915d3cf9c7dSdf157793 static void
asy_ppsevent(struct asycom * asy,int msr)1916d3cf9c7dSdf157793 asy_ppsevent(struct asycom *asy, int msr)
1917d3cf9c7dSdf157793 {
1918d3cf9c7dSdf157793 	if (asy->asy_flags & ASY_PPS_EDGE) {
1919d3cf9c7dSdf157793 		/* Have seen leading edge, now look for and record drop */
1920d3cf9c7dSdf157793 		if ((msr & DCD) == 0)
1921d3cf9c7dSdf157793 			asy->asy_flags &= ~ASY_PPS_EDGE;
1922d3cf9c7dSdf157793 		/*
1923d3cf9c7dSdf157793 		 * Waiting for leading edge, look for rise; stamp event and
1924d3cf9c7dSdf157793 		 * calibrate kernel clock.
1925d3cf9c7dSdf157793 		 */
1926d3cf9c7dSdf157793 	} else if (msr & DCD) {
1927d3cf9c7dSdf157793 		/*
1928d3cf9c7dSdf157793 		 * This code captures a timestamp at the designated
1929d3cf9c7dSdf157793 		 * transition of the PPS signal (DCD asserted).  The
1930d3cf9c7dSdf157793 		 * code provides a pointer to the timestamp, as well
1931d3cf9c7dSdf157793 		 * as the hardware counter value at the capture.
1932d3cf9c7dSdf157793 		 *
1933d3cf9c7dSdf157793 		 * Note: the kernel has nano based time values while
1934d3cf9c7dSdf157793 		 * NTP requires micro based, an in-line fast algorithm
1935d3cf9c7dSdf157793 		 * to convert nsec to usec is used here -- see hrt2ts()
1936d3cf9c7dSdf157793 		 * in common/os/timers.c for a full description.
1937d3cf9c7dSdf157793 		 */
1938d3cf9c7dSdf157793 		struct timeval *tvp = &asy_ppsev.tv;
1939d3cf9c7dSdf157793 		timestruc_t ts;
1940d3cf9c7dSdf157793 		long nsec, usec;
1941d3cf9c7dSdf157793 
1942d3cf9c7dSdf157793 		asy->asy_flags |= ASY_PPS_EDGE;
1943d3cf9c7dSdf157793 		gethrestime(&ts);
1944d3cf9c7dSdf157793 		nsec = ts.tv_nsec;
1945d3cf9c7dSdf157793 		usec = nsec + (nsec >> 2);
1946d3cf9c7dSdf157793 		usec = nsec + (usec >> 1);
1947d3cf9c7dSdf157793 		usec = nsec + (usec >> 2);
1948d3cf9c7dSdf157793 		usec = nsec + (usec >> 4);
1949d3cf9c7dSdf157793 		usec = nsec - (usec >> 3);
1950d3cf9c7dSdf157793 		usec = nsec + (usec >> 2);
1951d3cf9c7dSdf157793 		usec = nsec + (usec >> 3);
1952d3cf9c7dSdf157793 		usec = nsec + (usec >> 4);
1953d3cf9c7dSdf157793 		usec = nsec + (usec >> 1);
1954d3cf9c7dSdf157793 		usec = nsec + (usec >> 6);
1955d3cf9c7dSdf157793 		tvp->tv_usec = usec >> 10;
1956d3cf9c7dSdf157793 		tvp->tv_sec = ts.tv_sec;
1957d3cf9c7dSdf157793 
1958d3cf9c7dSdf157793 		++asy_ppsev.serial;
1959d3cf9c7dSdf157793 
1960d3cf9c7dSdf157793 		/*
1961d3cf9c7dSdf157793 		 * Because the kernel keeps a high-resolution time,
1962d3cf9c7dSdf157793 		 * pass the current highres timestamp in tvp and zero
1963d3cf9c7dSdf157793 		 * in usec.
1964d3cf9c7dSdf157793 		 */
1965d3cf9c7dSdf157793 		ddi_hardpps(tvp, 0);
1966d3cf9c7dSdf157793 	}
1967d3cf9c7dSdf157793 }
1968d3cf9c7dSdf157793 
1969d3cf9c7dSdf157793 /*
1970d3cf9c7dSdf157793  * Modem status interrupt.
1971d3cf9c7dSdf157793  *
1972d3cf9c7dSdf157793  * (Note: It is assumed that the MSR hasn't been read by asyintr().)
1973d3cf9c7dSdf157793  */
1974d3cf9c7dSdf157793 
1975d3cf9c7dSdf157793 static void
async_msint(struct asycom * asy)1976d3cf9c7dSdf157793 async_msint(struct asycom *asy)
1977d3cf9c7dSdf157793 {
1978d3cf9c7dSdf157793 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
1979d3cf9c7dSdf157793 	int msr;
1980d3cf9c7dSdf157793 
1981d3cf9c7dSdf157793 	msr = INB(MSR);	/* this resets the interrupt */
1982d3cf9c7dSdf157793 	asy->asy_cached_msr = msr;
1983d3cf9c7dSdf157793 #ifdef DEBUG
1984d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_STATE) {
1985d3cf9c7dSdf157793 		printf("   transition: %3s %3s %3s %3s\n"
1986d3cf9c7dSdf157793 		    "current state: %3s %3s %3s %3s\n",
1987d3cf9c7dSdf157793 		    (msr & DCTS) ? "CTS" : "   ",
1988d3cf9c7dSdf157793 		    (msr & DDSR) ? "DSR" : "   ",
1989d3cf9c7dSdf157793 		    (msr & DRI) ?  "RI " : "   ",
1990d3cf9c7dSdf157793 		    (msr & DDCD) ? "DCD" : "   ",
1991d3cf9c7dSdf157793 		    (msr & CTS) ?  "CTS" : "   ",
1992d3cf9c7dSdf157793 		    (msr & DSR) ?  "DSR" : "   ",
1993d3cf9c7dSdf157793 		    (msr & RI) ?   "RI " : "   ",
1994d3cf9c7dSdf157793 		    (msr & DCD) ?  "DCD" : "   ");
1995d3cf9c7dSdf157793 	}
1996d3cf9c7dSdf157793 #endif
1997d3cf9c7dSdf157793 	if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) {
1998d3cf9c7dSdf157793 #ifdef DEBUG
1999d3cf9c7dSdf157793 		if (asydebug & ASY_DEBUG_HFLOW)
2000d3cf9c7dSdf157793 			printf("asy%d: hflow start\n",
2001d3cf9c7dSdf157793 			    UNIT(async->async_dev));
2002d3cf9c7dSdf157793 #endif
2003d3cf9c7dSdf157793 		async->async_flags |= ASYNC_HW_OUT_FLW;
2004d3cf9c7dSdf157793 	}
2005d3cf9c7dSdf157793 	if (asy->asy_hwtype == ASY82510)
2006d3cf9c7dSdf157793 		OUTB(MSR, (msr & 0xF0));
2007d3cf9c7dSdf157793 
2008d3cf9c7dSdf157793 	/* Handle PPS event */
2009d3cf9c7dSdf157793 	if (asy->asy_flags & ASY_PPS)
2010d3cf9c7dSdf157793 		asy_ppsevent(asy, msr);
2011d3cf9c7dSdf157793 
2012d3cf9c7dSdf157793 	async->async_ext++;
2013d3cf9c7dSdf157793 	ASYSETSOFT(asy);
2014d3cf9c7dSdf157793 }
2015d3cf9c7dSdf157793 
2016d3cf9c7dSdf157793 /*
2017d3cf9c7dSdf157793  * Handle a second-stage interrupt.
2018d3cf9c7dSdf157793  */
2019d3cf9c7dSdf157793 uint_t
asysoftintr(caddr_t intarg)2020d3cf9c7dSdf157793 asysoftintr(caddr_t intarg)
2021d3cf9c7dSdf157793 {
2022d3cf9c7dSdf157793 	struct asycom *asy = (struct asycom *)intarg;
2023d3cf9c7dSdf157793 	struct asyncline *async;
2024d3cf9c7dSdf157793 	int rv;
2025d3cf9c7dSdf157793 	int cc;
2026d3cf9c7dSdf157793 	/*
2027d3cf9c7dSdf157793 	 * Test and clear soft interrupt.
2028d3cf9c7dSdf157793 	 */
2029d3cf9c7dSdf157793 	mutex_enter(asy->asy_soft_lock);
2030d3cf9c7dSdf157793 #ifdef DEBUG
2031d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2032d3cf9c7dSdf157793 		printf("softintr\n");
2033d3cf9c7dSdf157793 #endif
2034d3cf9c7dSdf157793 	rv = asy->asysoftpend;
2035d3cf9c7dSdf157793 	if (rv != 0)
2036d3cf9c7dSdf157793 		asy->asysoftpend = 0;
2037d3cf9c7dSdf157793 	mutex_exit(asy->asy_soft_lock);
2038d3cf9c7dSdf157793 
2039d3cf9c7dSdf157793 	if (rv) {
2040d3cf9c7dSdf157793 		if (asy->asy_priv == NULL)
2041d3cf9c7dSdf157793 			return (rv);
2042d3cf9c7dSdf157793 		async = (struct asyncline *)asy->asy_priv;
2043d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
2044d3cf9c7dSdf157793 		if (asy->asy_flags & ASY_NEEDSOFT) {
2045d3cf9c7dSdf157793 			asy->asy_flags &= ~ASY_NEEDSOFT;
2046d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2047d3cf9c7dSdf157793 			(void) async_softint(asy);
2048d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2049d3cf9c7dSdf157793 		}
2050d3cf9c7dSdf157793 		/*
2051d3cf9c7dSdf157793 		 * There are some instances where the softintr is not
2052d3cf9c7dSdf157793 		 * scheduled and hence not called. It so happened that makes
2053d3cf9c7dSdf157793 		 * the last few characters to be stuck in ringbuffer.
2054d3cf9c7dSdf157793 		 * Hence, call once again the  handler so that the last few
2055d3cf9c7dSdf157793 		 * characters are cleared.
2056d3cf9c7dSdf157793 		 */
2057d3cf9c7dSdf157793 		cc = RING_CNT(async);
2058d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
2059d3cf9c7dSdf157793 		if (cc > 0) {
2060d3cf9c7dSdf157793 			(void) async_softint(asy);
2061d3cf9c7dSdf157793 		}
2062d3cf9c7dSdf157793 	}
2063d3cf9c7dSdf157793 	return (rv);
2064d3cf9c7dSdf157793 }
2065d3cf9c7dSdf157793 
2066d3cf9c7dSdf157793 /*
2067d3cf9c7dSdf157793  * Handle a software interrupt.
2068d3cf9c7dSdf157793  */
2069d3cf9c7dSdf157793 static int
async_softint(struct asycom * asy)2070d3cf9c7dSdf157793 async_softint(struct asycom *asy)
2071d3cf9c7dSdf157793 {
2072d3cf9c7dSdf157793 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
2073d3cf9c7dSdf157793 	uint_t	cc;
2074d3cf9c7dSdf157793 	mblk_t	*bp;
2075d3cf9c7dSdf157793 	queue_t	*q;
2076d3cf9c7dSdf157793 	uchar_t	val;
2077d3cf9c7dSdf157793 	uchar_t	c;
2078d3cf9c7dSdf157793 	tty_common_t	*tp;
2079d3cf9c7dSdf157793 
2080d3cf9c7dSdf157793 #ifdef DEBUG
2081d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2082d3cf9c7dSdf157793 		printf("process\n");
2083d3cf9c7dSdf157793 #endif
2084d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
2085d3cf9c7dSdf157793 	if (asy->asy_flags & ASY_DOINGSOFT) {
2086d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
2087d3cf9c7dSdf157793 		return (0);
2088d3cf9c7dSdf157793 	}
2089d3cf9c7dSdf157793 	tp = &async->async_ttycommon;
2090d3cf9c7dSdf157793 	q = tp->t_readq;
2091d3cf9c7dSdf157793 	if (q != NULL) {
2092d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
2093d3cf9c7dSdf157793 		enterq(q);
2094d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
2095d3cf9c7dSdf157793 	}
2096d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2097d3cf9c7dSdf157793 	asy->asy_flags |= ASY_DOINGSOFT;
2098d3cf9c7dSdf157793 
2099d3cf9c7dSdf157793 	if (INB(ICR) & MIEN)
2100d3cf9c7dSdf157793 		val = asy->asy_cached_msr & 0xFF;
2101d3cf9c7dSdf157793 	else
2102d3cf9c7dSdf157793 		val = INB(MSR) & 0xFF;
2103d3cf9c7dSdf157793 
2104d3cf9c7dSdf157793 	if (async->async_ttycommon.t_cflag & CRTSCTS) {
2105d3cf9c7dSdf157793 		if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) {
2106d3cf9c7dSdf157793 #ifdef DEBUG
2107d3cf9c7dSdf157793 			if (asydebug & ASY_DEBUG_HFLOW)
2108d3cf9c7dSdf157793 				printf("asy%d: hflow start\n",
2109d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2110d3cf9c7dSdf157793 #endif
2111d3cf9c7dSdf157793 			async->async_flags &= ~ASYNC_HW_OUT_FLW;
2112d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2113d3cf9c7dSdf157793 			if (async->async_ocnt > 0) {
2114d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2115d3cf9c7dSdf157793 				async_resume(async);
2116d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2117d3cf9c7dSdf157793 			} else {
2118d3cf9c7dSdf157793 				async_start(async);
2119d3cf9c7dSdf157793 			}
2120d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2121d3cf9c7dSdf157793 		}
2122d3cf9c7dSdf157793 	}
2123d3cf9c7dSdf157793 	if (async->async_ext) {
2124d3cf9c7dSdf157793 		async->async_ext = 0;
2125d3cf9c7dSdf157793 		/* check for carrier up */
2126d3cf9c7dSdf157793 		if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) {
2127d3cf9c7dSdf157793 			/* carrier present */
2128d3cf9c7dSdf157793 			if ((async->async_flags & ASYNC_CARR_ON) == 0) {
2129d3cf9c7dSdf157793 				async->async_flags |= ASYNC_CARR_ON;
2130d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2131d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
2132d3cf9c7dSdf157793 				if (async->async_flags & ASYNC_ISOPEN)
2133d3cf9c7dSdf157793 					(void) putctl(q, M_UNHANGUP);
2134d3cf9c7dSdf157793 				cv_broadcast(&async->async_flags_cv);
2135d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl);
2136d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2137d3cf9c7dSdf157793 			}
2138d3cf9c7dSdf157793 		} else {
2139d3cf9c7dSdf157793 			if ((async->async_flags & ASYNC_CARR_ON) &&
2140d3cf9c7dSdf157793 			    !(tp->t_cflag & CLOCAL)) {
2141d3cf9c7dSdf157793 				int flushflag;
2142d3cf9c7dSdf157793 
2143d3cf9c7dSdf157793 				/*
2144d3cf9c7dSdf157793 				 * Carrier went away.
2145d3cf9c7dSdf157793 				 * Drop DTR, abort any output in
2146d3cf9c7dSdf157793 				 * progress, indicate that output is
2147d3cf9c7dSdf157793 				 * not stopped, and send a hangup
2148d3cf9c7dSdf157793 				 * notification upstream.
2149d3cf9c7dSdf157793 				 *
2150d3cf9c7dSdf157793 				 * If we're in the midst of close, then flush
2151d3cf9c7dSdf157793 				 * everything.  Don't leave stale ioctls lying
2152d3cf9c7dSdf157793 				 * about.
2153d3cf9c7dSdf157793 				 */
2154d3cf9c7dSdf157793 				val = INB(MCR);
2155d3cf9c7dSdf157793 				OUTB(MCR, (val & ~DTR));
2156d3cf9c7dSdf157793 				flushflag = (async->async_flags &
2157d3cf9c7dSdf157793 				    ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
21584d0b1b0dSAn Bui 				if (tp->t_writeq != NULL) {
2159d3cf9c7dSdf157793 					flushq(tp->t_writeq, flushflag);
21604d0b1b0dSAn Bui 				}
2161d3cf9c7dSdf157793 				if (async->async_xmitblk != NULL) {
2162d3cf9c7dSdf157793 					freeb(async->async_xmitblk);
2163d3cf9c7dSdf157793 					async->async_xmitblk = NULL;
2164d3cf9c7dSdf157793 				}
2165d3cf9c7dSdf157793 				if (async->async_flags & ASYNC_BUSY) {
2166d3cf9c7dSdf157793 					async->async_ocnt = 0;
2167d3cf9c7dSdf157793 					async->async_flags &= ~ASYNC_BUSY;
2168d3cf9c7dSdf157793 				}
2169d3cf9c7dSdf157793 				async->async_flags &= ~ASYNC_STOPPED;
2170d3cf9c7dSdf157793 				if (async->async_flags & ASYNC_ISOPEN) {
2171d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl_hi);
2172d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl);
2173d3cf9c7dSdf157793 					(void) putctl(q, M_HANGUP);
2174d3cf9c7dSdf157793 					mutex_enter(asy->asy_excl);
2175d3cf9c7dSdf157793 					mutex_enter(asy->asy_excl_hi);
2176d3cf9c7dSdf157793 				}
2177d3cf9c7dSdf157793 				async->async_flags &= ~ASYNC_CARR_ON;
2178d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2179d3cf9c7dSdf157793 				cv_broadcast(&async->async_flags_cv);
2180d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2181d3cf9c7dSdf157793 			}
2182d3cf9c7dSdf157793 		}
21837cb42c7eSrameshc 	}
2184d3cf9c7dSdf157793 
2185d3cf9c7dSdf157793 	/*
2186d3cf9c7dSdf157793 	 * If data has been added to the circular buffer, remove
2187d3cf9c7dSdf157793 	 * it from the buffer, and send it up the stream if there's
2188d3cf9c7dSdf157793 	 * somebody listening. Try to do it 16 bytes at a time. If we
2189d3cf9c7dSdf157793 	 * have more than 16 bytes to move, move 16 byte chunks and
2190d3cf9c7dSdf157793 	 * leave the rest for next time around (maybe it will grow).
2191d3cf9c7dSdf157793 	 */
2192d3cf9c7dSdf157793 	if (!(async->async_flags & ASYNC_ISOPEN)) {
2193d3cf9c7dSdf157793 		RING_INIT(async);
2194d3cf9c7dSdf157793 		goto rv;
2195d3cf9c7dSdf157793 	}
2196d3cf9c7dSdf157793 	if ((cc = RING_CNT(async)) == 0) {
2197d3cf9c7dSdf157793 		goto rv;
2198d3cf9c7dSdf157793 	}
2199d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2200d3cf9c7dSdf157793 
2201d3cf9c7dSdf157793 	if (!canput(q)) {
2202d3cf9c7dSdf157793 		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2203d3cf9c7dSdf157793 #ifdef DEBUG
2204d3cf9c7dSdf157793 			if (!(asydebug & ASY_DEBUG_HFLOW)) {
2205d3cf9c7dSdf157793 				printf("asy%d: hflow stop input.\n",
2206d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2207d3cf9c7dSdf157793 				if (canputnext(q))
2208d3cf9c7dSdf157793 					printf("asy%d: next queue is "
2209d3cf9c7dSdf157793 					    "ready\n",
2210d3cf9c7dSdf157793 					    UNIT(async->async_dev));
2211d3cf9c7dSdf157793 			}
2212d3cf9c7dSdf157793 #endif
2213d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2214d3cf9c7dSdf157793 			async->async_flags |= ASYNC_HW_IN_FLOW;
2215d3cf9c7dSdf157793 			async->async_flowc = async->async_stopc;
2216d3cf9c7dSdf157793 		} else mutex_enter(asy->asy_excl_hi);
2217d3cf9c7dSdf157793 		goto rv;
2218d3cf9c7dSdf157793 	}
2219d3cf9c7dSdf157793 
2220d3cf9c7dSdf157793 	if (async->async_ringbuf_overflow) {
2221d3cf9c7dSdf157793 		if ((async->async_flags & ASYNC_HW_IN_FLOW) &&
2222d3cf9c7dSdf157793 		    ((int)(RING_CNT(async)) < (RINGSIZE/4))) {
2223d3cf9c7dSdf157793 #ifdef DEBUG
2224d3cf9c7dSdf157793 			if (asydebug & ASY_DEBUG_HFLOW)
2225d3cf9c7dSdf157793 				printf("asy%d: hflow start input.\n",
2226d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2227d3cf9c7dSdf157793 #endif
2228d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2229d3cf9c7dSdf157793 			async->async_flags &= ~ASYNC_HW_IN_FLOW;
2230d3cf9c7dSdf157793 			async->async_flowc = async->async_startc;
2231d3cf9c7dSdf157793 			async->async_ringbuf_overflow = 0;
2232d3cf9c7dSdf157793 			goto rv;
2233d3cf9c7dSdf157793 		}
2234d3cf9c7dSdf157793 	}
2235d3cf9c7dSdf157793 #ifdef DEBUG
2236d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_INPUT)
2237d3cf9c7dSdf157793 		printf("asy%d: %d char(s) in queue.\n",
2238d3cf9c7dSdf157793 		    UNIT(async->async_dev), cc);
2239d3cf9c7dSdf157793 #endif
2240d3cf9c7dSdf157793 	/*
2241d3cf9c7dSdf157793 	 * Before you pull the characters from the RING BUF
2242d3cf9c7dSdf157793 	 * Check whether you can put into the queue again
2243d3cf9c7dSdf157793 	 */
2244d3cf9c7dSdf157793 	if ((!canputnext(q)) || (!canput(q))) {
2245d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
2246d3cf9c7dSdf157793 		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
2247d3cf9c7dSdf157793 			async->async_flags |= ASYNC_HW_IN_FLOW;
2248d3cf9c7dSdf157793 			async->async_flowc = async->async_stopc;
2249d3cf9c7dSdf157793 			async->async_queue_full = 1;
2250d3cf9c7dSdf157793 		}
2251d3cf9c7dSdf157793 		goto rv;
2252d3cf9c7dSdf157793 	}
2253d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2254d3cf9c7dSdf157793 	if (async->async_queue_full) {
2255d3cf9c7dSdf157793 		/*
2256d3cf9c7dSdf157793 		 * Last time the Stream queue didnot allow
2257d3cf9c7dSdf157793 		 * now it allows so, relax, the flow control
2258d3cf9c7dSdf157793 		 */
2259d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_HW_IN_FLOW) {
2260d3cf9c7dSdf157793 			async->async_flags &= ~ASYNC_HW_IN_FLOW;
2261d3cf9c7dSdf157793 			async->async_queue_full = 0;
2262d3cf9c7dSdf157793 			async->async_flowc = async->async_startc;
2263d3cf9c7dSdf157793 			goto rv;
2264d3cf9c7dSdf157793 		} else
2265d3cf9c7dSdf157793 			async->async_queue_full = 0;
2266d3cf9c7dSdf157793 	}
2267d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2268d3cf9c7dSdf157793 	if (!(bp = allocb(cc, BPRI_MED))) {
2269d3cf9c7dSdf157793 		ttycommon_qfull(&async->async_ttycommon, q);
2270d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
2271d3cf9c7dSdf157793 		goto rv;
2272d3cf9c7dSdf157793 	}
2273d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2274d3cf9c7dSdf157793 	do {
2275d3cf9c7dSdf157793 		if (RING_ERR(async, S_ERRORS)) {
2276d3cf9c7dSdf157793 			RING_UNMARK(async);
2277d3cf9c7dSdf157793 			c = RING_GET(async);
2278d3cf9c7dSdf157793 			break;
2279d3cf9c7dSdf157793 		} else {
2280d3cf9c7dSdf157793 			*bp->b_wptr++ = RING_GET(async);
2281d3cf9c7dSdf157793 		}
2282d3cf9c7dSdf157793 	} while (--cc);
2283d3cf9c7dSdf157793 
2284d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2285d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
2286d3cf9c7dSdf157793 	if (bp->b_wptr > bp->b_rptr) {
2287d3cf9c7dSdf157793 		if (!canputnext(q)) {
2288d3cf9c7dSdf157793 			if (!canput(q)) {
2289d3cf9c7dSdf157793 				/*
2290d3cf9c7dSdf157793 				 * Even after taking all precautions that
2291d3cf9c7dSdf157793 				 * Still we are unable to queue, then we
2292d3cf9c7dSdf157793 				 * cannot do anything, just drop the block
2293d3cf9c7dSdf157793 				 */
2294d3cf9c7dSdf157793 				cmn_err(CE_NOTE,
2295d3cf9c7dSdf157793 				    "su%d: local queue full\n",
2296d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2297d3cf9c7dSdf157793 				freemsg(bp);
2298d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2299d3cf9c7dSdf157793 				if ((async->async_flags &
2300d3cf9c7dSdf157793 				    ASYNC_HW_IN_FLOW) == 0) {
2301d3cf9c7dSdf157793 					async->async_flags |=
2302d3cf9c7dSdf157793 					    ASYNC_HW_IN_FLOW;
2303d3cf9c7dSdf157793 					async->async_flowc =
2304d3cf9c7dSdf157793 					    async->async_stopc;
2305d3cf9c7dSdf157793 					async->async_queue_full = 1;
2306d3cf9c7dSdf157793 				}
2307d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2308d3cf9c7dSdf157793 			} else {
2309d3cf9c7dSdf157793 				(void) putq(q, bp);
2310d3cf9c7dSdf157793 			}
2311d3cf9c7dSdf157793 		} else {
2312d3cf9c7dSdf157793 			putnext(q, bp);
2313d3cf9c7dSdf157793 		}
2314d3cf9c7dSdf157793 	} else {
2315d3cf9c7dSdf157793 		freemsg(bp);
2316d3cf9c7dSdf157793 	}
2317d3cf9c7dSdf157793 	/*
2318d3cf9c7dSdf157793 	 * If we have a parity error, then send
2319d3cf9c7dSdf157793 	 * up an M_BREAK with the "bad"
2320d3cf9c7dSdf157793 	 * character as an argument. Let ldterm
2321d3cf9c7dSdf157793 	 * figure out what to do with the error.
2322d3cf9c7dSdf157793 	 */
2323d3cf9c7dSdf157793 	if (cc)
2324d3cf9c7dSdf157793 		(void) putctl1(q, M_BREAK, c);
2325d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
2326d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2327d3cf9c7dSdf157793 rv:
2328d3cf9c7dSdf157793 	/*
2329d3cf9c7dSdf157793 	 * If a transmission has finished, indicate that it's finished,
2330d3cf9c7dSdf157793 	 * and start that line up again.
2331d3cf9c7dSdf157793 	 */
2332d3cf9c7dSdf157793 	if (async->async_break) {
2333d3cf9c7dSdf157793 		async->async_break = 0;
2334d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_ISOPEN) {
2335d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2336d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
2337d3cf9c7dSdf157793 			(void) putctl(q, M_BREAK);
2338d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
2339d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2340d3cf9c7dSdf157793 		}
2341d3cf9c7dSdf157793 	}
2342d3cf9c7dSdf157793 	if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) ||
234319cdc281Skc28005 	    (async->async_flowc != '\0')) {
2344d3cf9c7dSdf157793 		async->async_flags &= ~ASYNC_BUSY;
2345d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
2346d3cf9c7dSdf157793 		if (async->async_xmitblk)
2347d3cf9c7dSdf157793 			freeb(async->async_xmitblk);
2348d3cf9c7dSdf157793 		async->async_xmitblk = NULL;
2349d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_ISOPEN) {
2350d3cf9c7dSdf157793 			asy->inperim = B_TRUE;
2351d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
2352d3cf9c7dSdf157793 			enterq(async->async_ttycommon.t_writeq);
2353d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
2354d3cf9c7dSdf157793 		}
2355d3cf9c7dSdf157793 		async_start(async);
2356d3cf9c7dSdf157793 		/*
2357d3cf9c7dSdf157793 		 * We need to check for inperim and ISOPEN due to
2358d3cf9c7dSdf157793 		 * multi-threading implications; it's possible to close the
2359d3cf9c7dSdf157793 		 * port and nullify async_flags while completing the software
2360d3cf9c7dSdf157793 		 * interrupt.  If the port is closed, leaveq() will have already
2361d3cf9c7dSdf157793 		 * been called.  We don't want to call it twice.
2362d3cf9c7dSdf157793 		 */
2363d3cf9c7dSdf157793 		if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) {
2364d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
2365d3cf9c7dSdf157793 			leaveq(async->async_ttycommon.t_writeq);
2366d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
2367d3cf9c7dSdf157793 			asy->inperim = B_FALSE;
2368d3cf9c7dSdf157793 		}
2369d3cf9c7dSdf157793 		if (!(async->async_flags & ASYNC_BUSY))
2370d3cf9c7dSdf157793 			cv_broadcast(&async->async_flags_cv);
2371d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
2372d3cf9c7dSdf157793 	}
2373d3cf9c7dSdf157793 	/*
2374d3cf9c7dSdf157793 	 * A note about these overrun bits: all they do is *tell* someone
2375d3cf9c7dSdf157793 	 * about an error- They do not track multiple errors. In fact,
2376d3cf9c7dSdf157793 	 * you could consider them latched register bits if you like.
2377d3cf9c7dSdf157793 	 * We are only interested in printing the error message once for
23784cad604cSMarcel Telka 	 * any cluster of overrun errors.
2379d3cf9c7dSdf157793 	 */
2380d3cf9c7dSdf157793 	if (async->async_hw_overrun) {
2381d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_ISOPEN) {
2382d3cf9c7dSdf157793 			if (su_log > 0) {
2383d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2384d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
2385d3cf9c7dSdf157793 				cmn_err(CE_NOTE, "su%d: silo overflow\n",
2386d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2387d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl);
2388d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2389d3cf9c7dSdf157793 			}
2390d3cf9c7dSdf157793 			INC64_KSTAT(asy, siloover);
2391d3cf9c7dSdf157793 		}
2392d3cf9c7dSdf157793 		async->async_hw_overrun = 0;
2393d3cf9c7dSdf157793 	}
2394d3cf9c7dSdf157793 	if (async->async_sw_overrun) {
2395d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_ISOPEN) {
2396d3cf9c7dSdf157793 			if (su_log > 0) {
2397d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2398d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
2399d3cf9c7dSdf157793 				cmn_err(CE_NOTE, "su%d: ring buffer overflow\n",
2400d3cf9c7dSdf157793 				    UNIT(async->async_dev));
2401d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl);
2402d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2403d3cf9c7dSdf157793 			}
2404d3cf9c7dSdf157793 			INC64_KSTAT(asy, ringover);
2405d3cf9c7dSdf157793 		}
2406d3cf9c7dSdf157793 		async->async_sw_overrun = 0;
2407d3cf9c7dSdf157793 	}
2408d3cf9c7dSdf157793 	asy->asy_flags &= ~ASY_DOINGSOFT;
2409d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2410d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
2411d3cf9c7dSdf157793 	if (q != NULL)
2412d3cf9c7dSdf157793 		leaveq(q);
2413d3cf9c7dSdf157793 	return (0);
2414d3cf9c7dSdf157793 }
2415d3cf9c7dSdf157793 
2416d3cf9c7dSdf157793 /*
2417d3cf9c7dSdf157793  * Restart output on a line after a delay or break timer expired.
2418d3cf9c7dSdf157793  */
2419d3cf9c7dSdf157793 static void
async_restart(void * arg)2420d3cf9c7dSdf157793 async_restart(void *arg)
2421d3cf9c7dSdf157793 {
2422d3cf9c7dSdf157793 	struct asyncline *async = arg;
2423d3cf9c7dSdf157793 	struct asycom *asy = async->async_common;
2424d3cf9c7dSdf157793 	queue_t *q;
2425d3cf9c7dSdf157793 	uchar_t lcr;
2426d3cf9c7dSdf157793 
2427d3cf9c7dSdf157793 	/*
2428d3cf9c7dSdf157793 	 * If break timer expired, turn off the break bit.
2429d3cf9c7dSdf157793 	 */
2430d3cf9c7dSdf157793 #ifdef DEBUG
2431d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2432d3cf9c7dSdf157793 		printf("restart\n");
2433d3cf9c7dSdf157793 #endif
2434d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
2435d3cf9c7dSdf157793 	if (async->async_flags & ASYNC_BREAK) {
2436587bcfd8Skc28005 		unsigned int rate;
2437587bcfd8Skc28005 
2438d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
2439d3cf9c7dSdf157793 		lcr = INB(LCR);
2440d3cf9c7dSdf157793 		OUTB(LCR, (lcr & ~SETBREAK));
2441587bcfd8Skc28005 
2442587bcfd8Skc28005 		/*
2443587bcfd8Skc28005 		 * Go to sleep for the time it takes for at least one
2444587bcfd8Skc28005 		 * stop bit to be received by the device at the other
2445587bcfd8Skc28005 		 * end of the line as stated in the RS-232 specification.
2446587bcfd8Skc28005 		 * The wait period is equal to:
2447587bcfd8Skc28005 		 * 2 clock cycles * (1 MICROSEC / baud rate)
2448587bcfd8Skc28005 		 */
2449587bcfd8Skc28005 		rate = async->async_ttycommon.t_cflag & CBAUD;
2450587bcfd8Skc28005 		if (async->async_ttycommon.t_cflag & CBAUDEXT)
2451587bcfd8Skc28005 			rate += 16;
2452587bcfd8Skc28005 		if (rate >= N_SU_SPEEDS || rate == B0) {
2453587bcfd8Skc28005 			rate = B9600;
2454587bcfd8Skc28005 		}
2455587bcfd8Skc28005 
2456d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
2457587bcfd8Skc28005 		mutex_exit(asy->asy_excl);
2458587bcfd8Skc28005 		drv_usecwait(2 * MICROSEC / baudtable[rate]);
2459587bcfd8Skc28005 		mutex_enter(asy->asy_excl);
2460d3cf9c7dSdf157793 	}
2461d3cf9c7dSdf157793 	async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING);
2462d3cf9c7dSdf157793 	if ((q = async->async_ttycommon.t_writeq) != NULL) {
2463d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
2464d3cf9c7dSdf157793 		enterq(q);
2465d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
2466d3cf9c7dSdf157793 	}
2467d3cf9c7dSdf157793 	async_start(async);
2468d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
2469d3cf9c7dSdf157793 	if (q != NULL)
2470d3cf9c7dSdf157793 		leaveq(q);
2471d3cf9c7dSdf157793 
2472d3cf9c7dSdf157793 	/* cleared break or delay flag; may have made some output progress */
2473d3cf9c7dSdf157793 	cv_broadcast(&async->async_flags_cv);
2474d3cf9c7dSdf157793 }
2475d3cf9c7dSdf157793 
2476d3cf9c7dSdf157793 static void
async_start(struct asyncline * async)2477d3cf9c7dSdf157793 async_start(struct asyncline *async)
2478d3cf9c7dSdf157793 {
2479d3cf9c7dSdf157793 	async_nstart(async, 0);
2480d3cf9c7dSdf157793 }
2481d3cf9c7dSdf157793 
2482d3cf9c7dSdf157793 /*
2483d3cf9c7dSdf157793  * Start output on a line, unless it's busy, frozen, or otherwise.
2484d3cf9c7dSdf157793  */
2485d3cf9c7dSdf157793 static void
async_nstart(struct asyncline * async,int mode)2486d3cf9c7dSdf157793 async_nstart(struct asyncline *async, int mode)
2487d3cf9c7dSdf157793 {
2488d3cf9c7dSdf157793 	register struct asycom *asy = async->async_common;
2489d3cf9c7dSdf157793 	register int cc;
2490d3cf9c7dSdf157793 	register queue_t *q;
2491d3cf9c7dSdf157793 	mblk_t *bp, *nbp;
2492d3cf9c7dSdf157793 	uchar_t *xmit_addr;
2493d3cf9c7dSdf157793 	uchar_t	val;
2494d3cf9c7dSdf157793 	int	fifo_len = 1;
2495d3cf9c7dSdf157793 	int	xmit_progress;
2496d3cf9c7dSdf157793 
2497d3cf9c7dSdf157793 #ifdef DEBUG
2498d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2499d3cf9c7dSdf157793 		printf("start\n");
2500d3cf9c7dSdf157793 #endif
2501d3cf9c7dSdf157793 	if (asy->asy_use_fifo == FIFO_ON)
2502d3cf9c7dSdf157793 		fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
2503d3cf9c7dSdf157793 
2504d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl));
2505d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2506d3cf9c7dSdf157793 	asycheckflowcontrol_hw(asy);
2507d3cf9c7dSdf157793 
2508d3cf9c7dSdf157793 	/*
2509d3cf9c7dSdf157793 	 * If the chip is busy (i.e., we're waiting for a break timeout
2510d3cf9c7dSdf157793 	 * to expire, or for the current transmission to finish, or for
2511d3cf9c7dSdf157793 	 * output to finish draining from chip), don't grab anything new.
2512d3cf9c7dSdf157793 	 */
2513d3cf9c7dSdf157793 	if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) {
2514d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
2515d3cf9c7dSdf157793 #ifdef DEBUG
2516d3cf9c7dSdf157793 		if (mode && asydebug & ASY_DEBUG_CLOSE)
2517d3cf9c7dSdf157793 			printf("asy%d: start %s.\n",
2518d3cf9c7dSdf157793 			    UNIT(async->async_dev),
2519d3cf9c7dSdf157793 			    async->async_flags & ASYNC_BREAK
2520d3cf9c7dSdf157793 			    ? "break" : "busy");
2521d3cf9c7dSdf157793 #endif
2522d3cf9c7dSdf157793 		return;
2523d3cf9c7dSdf157793 	}
2524d3cf9c7dSdf157793 
2525d3cf9c7dSdf157793 	/*
2526d3cf9c7dSdf157793 	 * If we have a flow-control character to transmit, do it now.
2527d3cf9c7dSdf157793 	 */
2528d3cf9c7dSdf157793 	if (asycheckflowcontrol_sw(asy)) {
2529d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
2530d3cf9c7dSdf157793 		return;
2531d3cf9c7dSdf157793 	}
2532d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2533d3cf9c7dSdf157793 	/*
2534d3cf9c7dSdf157793 	 * If we're waiting for a delay timeout to expire, don't grab
2535d3cf9c7dSdf157793 	 * anything new.
2536d3cf9c7dSdf157793 	 */
2537d3cf9c7dSdf157793 	if (async->async_flags & ASYNC_DELAY) {
2538d3cf9c7dSdf157793 #ifdef DEBUG
2539d3cf9c7dSdf157793 		if (mode && asydebug & ASY_DEBUG_CLOSE)
2540d3cf9c7dSdf157793 			printf("asy%d: start ASYNC_DELAY.\n",
2541d3cf9c7dSdf157793 			    UNIT(async->async_dev));
2542d3cf9c7dSdf157793 #endif
2543d3cf9c7dSdf157793 		return;
2544d3cf9c7dSdf157793 	}
2545d3cf9c7dSdf157793 
2546d3cf9c7dSdf157793 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
2547d3cf9c7dSdf157793 #ifdef DEBUG
2548d3cf9c7dSdf157793 		if (mode && asydebug & ASY_DEBUG_CLOSE)
2549d3cf9c7dSdf157793 			printf("asy%d: start writeq is null.\n",
2550d3cf9c7dSdf157793 			    UNIT(async->async_dev));
2551d3cf9c7dSdf157793 #endif
2552d3cf9c7dSdf157793 		return;	/* not attached to a stream */
2553d3cf9c7dSdf157793 	}
2554d3cf9c7dSdf157793 
2555d3cf9c7dSdf157793 	for (;;) {
2556d3cf9c7dSdf157793 		if ((bp = getq(q)) == NULL)
2557d3cf9c7dSdf157793 			return;	/* no data to transmit */
2558d3cf9c7dSdf157793 
2559d3cf9c7dSdf157793 		/*
2560d3cf9c7dSdf157793 		 * We have a message block to work on.
2561d3cf9c7dSdf157793 		 * Check whether it's a break, a delay, or an ioctl (the latter
2562d3cf9c7dSdf157793 		 * occurs if the ioctl in question was waiting for the output
2563d3cf9c7dSdf157793 		 * to drain).  If it's one of those, process it immediately.
2564d3cf9c7dSdf157793 		 */
2565d3cf9c7dSdf157793 		switch (bp->b_datap->db_type) {
2566d3cf9c7dSdf157793 
2567d3cf9c7dSdf157793 		case M_BREAK:
2568d3cf9c7dSdf157793 			/*
2569d3cf9c7dSdf157793 			 * Set the break bit, and arrange for "async_restart"
2570d3cf9c7dSdf157793 			 * to be called in 1/4 second; it will turn the
2571d3cf9c7dSdf157793 			 * break bit off, and call "async_start" to grab
2572d3cf9c7dSdf157793 			 * the next message.
2573d3cf9c7dSdf157793 			 */
2574d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2575d3cf9c7dSdf157793 			val = INB(LCR);
2576d3cf9c7dSdf157793 			OUTB(LCR, (val | SETBREAK));
2577d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2578d3cf9c7dSdf157793 			async->async_flags |= ASYNC_BREAK;
2579d3cf9c7dSdf157793 			(void) timeout(async_restart, async, hz / 4);
2580d3cf9c7dSdf157793 			freemsg(bp);
2581d3cf9c7dSdf157793 			return;	/* wait for this to finish */
2582d3cf9c7dSdf157793 
2583d3cf9c7dSdf157793 		case M_DELAY:
2584d3cf9c7dSdf157793 			/*
2585d3cf9c7dSdf157793 			 * Arrange for "async_restart" to be called when the
2586d3cf9c7dSdf157793 			 * delay expires; it will turn ASYNC_DELAY off,
2587d3cf9c7dSdf157793 			 * and call "async_start" to grab the next message.
2588d3cf9c7dSdf157793 			 */
2589d3cf9c7dSdf157793 			(void) timeout(async_restart, async,
2590d3cf9c7dSdf157793 			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
2591d3cf9c7dSdf157793 			async->async_flags |= ASYNC_DELAY;
2592d3cf9c7dSdf157793 			freemsg(bp);
2593d3cf9c7dSdf157793 			return;	/* wait for this to finish */
2594d3cf9c7dSdf157793 
2595d3cf9c7dSdf157793 		case M_IOCTL:
2596d3cf9c7dSdf157793 			/*
2597d3cf9c7dSdf157793 			 * This ioctl needs to wait for the output ahead of
2598d3cf9c7dSdf157793 			 * it to drain.  Try to do it, and then either
2599d3cf9c7dSdf157793 			 * redo the ioctl at a later time or grab the next
2600d3cf9c7dSdf157793 			 * message after it.
2601d3cf9c7dSdf157793 			 */
2602d3cf9c7dSdf157793 
2603d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2604d3cf9c7dSdf157793 			if (asy_isbusy(asy)) {
2605d3cf9c7dSdf157793 				/*
2606d3cf9c7dSdf157793 				 * Get the divisor by calculating the rate
2607d3cf9c7dSdf157793 				 */
2608d3cf9c7dSdf157793 				unsigned int rate;
2609d3cf9c7dSdf157793 
2610d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2611d3cf9c7dSdf157793 				rate = async->async_ttycommon.t_cflag & CBAUD;
2612d3cf9c7dSdf157793 				if (async->async_ttycommon.t_cflag & CBAUDEXT)
2613d3cf9c7dSdf157793 					rate += 16;
2614d3cf9c7dSdf157793 				if (rate >= N_SU_SPEEDS || rate == B0) {
2615d3cf9c7dSdf157793 					rate = B9600;
2616d3cf9c7dSdf157793 				}
2617d3cf9c7dSdf157793 
2618d3cf9c7dSdf157793 				/*
2619d3cf9c7dSdf157793 				 * We need to do a callback as the port will
2620d3cf9c7dSdf157793 				 * be set to drain
2621d3cf9c7dSdf157793 				 */
2622d3cf9c7dSdf157793 				async->async_flags |= ASYNC_DRAINING;
2623d3cf9c7dSdf157793 
2624d3cf9c7dSdf157793 				/*
2625d3cf9c7dSdf157793 				 * Put the message we just processed back onto
2626d3cf9c7dSdf157793 				 * the end of the queue
2627d3cf9c7dSdf157793 				 */
2628d3cf9c7dSdf157793 				if (putq(q, bp) == 0)
2629d3cf9c7dSdf157793 					freemsg(bp);
2630d3cf9c7dSdf157793 
2631d3cf9c7dSdf157793 				/*
2632d3cf9c7dSdf157793 				 * We need to delay until the TSR and THR
2633d3cf9c7dSdf157793 				 * have been exhausted.  We base the delay on
2634d3cf9c7dSdf157793 				 * the amount of time it takes to transmit
2635d3cf9c7dSdf157793 				 * 2 chars at the current baud rate in
2636d3cf9c7dSdf157793 				 * microseconds.
2637d3cf9c7dSdf157793 				 *
2638d3cf9c7dSdf157793 				 * Therefore, the wait period is:
2639d3cf9c7dSdf157793 				 *
2640d3cf9c7dSdf157793 				 * (#TSR bits + #THR bits) *
2641d3cf9c7dSdf157793 				 *	1 MICROSEC / baud rate
2642d3cf9c7dSdf157793 				 */
2643d3cf9c7dSdf157793 				(void) timeout(async_restart, async,
2644d3cf9c7dSdf157793 				    drv_usectohz(16 * MICROSEC /
2645d3cf9c7dSdf157793 				    baudtable[rate]));
2646d3cf9c7dSdf157793 				return;
2647d3cf9c7dSdf157793 			}
2648d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2649d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
2650d3cf9c7dSdf157793 			async_ioctl(async, q, bp, B_FALSE);
2651d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
2652d3cf9c7dSdf157793 			continue;
2653d3cf9c7dSdf157793 		}
2654d3cf9c7dSdf157793 
2655d3cf9c7dSdf157793 		while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
2656d3cf9c7dSdf157793 			nbp = bp->b_cont;
2657d3cf9c7dSdf157793 			freeb(bp);
2658d3cf9c7dSdf157793 			bp = nbp;
2659d3cf9c7dSdf157793 		}
2660d3cf9c7dSdf157793 		if (bp != NULL)
2661d3cf9c7dSdf157793 			break;
2662d3cf9c7dSdf157793 	}
2663d3cf9c7dSdf157793 
2664d3cf9c7dSdf157793 	/*
2665d3cf9c7dSdf157793 	 * We have data to transmit.  If output is stopped, put
2666d3cf9c7dSdf157793 	 * it back and try again later.
2667d3cf9c7dSdf157793 	 */
2668d3cf9c7dSdf157793 	if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) {
2669d3cf9c7dSdf157793 #ifdef DEBUG
2670d3cf9c7dSdf157793 		if (asydebug & ASY_DEBUG_HFLOW &&
2671d3cf9c7dSdf157793 		    async->async_flags & ASYNC_HW_OUT_FLW)
2672d3cf9c7dSdf157793 			printf("asy%d: output hflow in effect.\n",
2673d3cf9c7dSdf157793 			    UNIT(async->async_dev));
2674d3cf9c7dSdf157793 #endif
2675d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
2676d3cf9c7dSdf157793 		(void) putbq(q, bp);
2677d3cf9c7dSdf157793 		/*
2678d3cf9c7dSdf157793 		 * We entered the routine owning the lock, we need to
2679d3cf9c7dSdf157793 		 * exit the routine owning the lock.
2680d3cf9c7dSdf157793 		 */
2681d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
2682d3cf9c7dSdf157793 		return;
2683d3cf9c7dSdf157793 	}
2684d3cf9c7dSdf157793 
2685d3cf9c7dSdf157793 	async->async_xmitblk = bp;
2686d3cf9c7dSdf157793 	xmit_addr = bp->b_rptr;
2687d3cf9c7dSdf157793 	bp = bp->b_cont;
2688d3cf9c7dSdf157793 	if (bp != NULL) {
2689d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
2690d3cf9c7dSdf157793 		(void) putbq(q, bp);	/* not done with this message yet */
2691d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
2692d3cf9c7dSdf157793 	}
2693d3cf9c7dSdf157793 
2694d3cf9c7dSdf157793 	/*
2695d3cf9c7dSdf157793 	 * In 5-bit mode, the high order bits are used
2696d3cf9c7dSdf157793 	 * to indicate character sizes less than five,
2697d3cf9c7dSdf157793 	 * so we need to explicitly mask before transmitting
2698d3cf9c7dSdf157793 	 */
2699d3cf9c7dSdf157793 	if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
2700d3cf9c7dSdf157793 		register unsigned char *p = xmit_addr;
2701d3cf9c7dSdf157793 		register int cnt = cc;
2702d3cf9c7dSdf157793 
2703d3cf9c7dSdf157793 		while (cnt--)
2704d3cf9c7dSdf157793 			*p++ &= (unsigned char) 0x1f;
2705d3cf9c7dSdf157793 	}
2706d3cf9c7dSdf157793 
2707d3cf9c7dSdf157793 	/*
2708d3cf9c7dSdf157793 	 * Set up this block for pseudo-DMA.
2709d3cf9c7dSdf157793 	 */
2710d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl_hi);
2711d3cf9c7dSdf157793 	async->async_optr = xmit_addr;
2712d3cf9c7dSdf157793 	async->async_ocnt = cc;
2713d3cf9c7dSdf157793 	/*
2714d3cf9c7dSdf157793 	 * If the transmitter is ready, shove some
2715d3cf9c7dSdf157793 	 * characters out.
2716d3cf9c7dSdf157793 	 */
2717d3cf9c7dSdf157793 	xmit_progress = 0;
2718d3cf9c7dSdf157793 	while (fifo_len-- && async->async_ocnt) {
2719d3cf9c7dSdf157793 		if (INB(LSR) & XHRE) {
2720d3cf9c7dSdf157793 			OUTB(DAT, *async->async_optr++);
2721d3cf9c7dSdf157793 			async->async_ocnt--;
2722d3cf9c7dSdf157793 			xmit_progress++;
2723d3cf9c7dSdf157793 		}
2724d3cf9c7dSdf157793 	}
2725d3cf9c7dSdf157793 	asy->asy_out_of_band_xmit = xmit_progress;
2726d3cf9c7dSdf157793 	if (xmit_progress > 0)
2727d3cf9c7dSdf157793 		async->async_flags |= ASYNC_PROGRESS;
2728d3cf9c7dSdf157793 	async->async_flags |= ASYNC_BUSY;
2729d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl_hi);
2730d3cf9c7dSdf157793 }
2731d3cf9c7dSdf157793 
2732d3cf9c7dSdf157793 /*
2733d3cf9c7dSdf157793  * Resume output by poking the transmitter.
2734d3cf9c7dSdf157793  */
2735d3cf9c7dSdf157793 static void
async_resume(struct asyncline * async)2736d3cf9c7dSdf157793 async_resume(struct asyncline *async)
2737d3cf9c7dSdf157793 {
2738d3cf9c7dSdf157793 	register struct asycom *asy = async->async_common;
2739d3cf9c7dSdf157793 
2740d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
2741d3cf9c7dSdf157793 #ifdef DEBUG
2742d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2743d3cf9c7dSdf157793 		printf("resume\n");
2744d3cf9c7dSdf157793 #endif
2745d3cf9c7dSdf157793 
2746d3cf9c7dSdf157793 	asycheckflowcontrol_hw(asy);
2747d3cf9c7dSdf157793 
2748d3cf9c7dSdf157793 	if (INB(LSR) & XHRE) {
2749d3cf9c7dSdf157793 		if (asycheckflowcontrol_sw(asy)) {
2750d3cf9c7dSdf157793 			return;
2751d3cf9c7dSdf157793 		} else if (async->async_ocnt > 0) {
2752d3cf9c7dSdf157793 			OUTB(DAT, *async->async_optr++);
2753d3cf9c7dSdf157793 			async->async_ocnt--;
2754d3cf9c7dSdf157793 			async->async_flags |= ASYNC_PROGRESS;
2755d3cf9c7dSdf157793 		}
2756d3cf9c7dSdf157793 	}
2757d3cf9c7dSdf157793 }
2758d3cf9c7dSdf157793 
2759d3cf9c7dSdf157793 /*
2760d3cf9c7dSdf157793  * Process an "ioctl" message sent down to us.
2761d3cf9c7dSdf157793  * Note that we don't need to get any locks until we are ready to access
2762d3cf9c7dSdf157793  * the hardware.  Nothing we access until then is going to be altered
2763d3cf9c7dSdf157793  * outside of the STREAMS framework, so we should be safe.
2764d3cf9c7dSdf157793  */
2765d3cf9c7dSdf157793 static void
async_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp,boolean_t iswput)2766d3cf9c7dSdf157793 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput)
2767d3cf9c7dSdf157793 {
2768d3cf9c7dSdf157793 	register struct asycom *asy = async->async_common;
2769d3cf9c7dSdf157793 	register tty_common_t  *tp = &async->async_ttycommon;
2770d3cf9c7dSdf157793 	register struct iocblk *iocp;
2771d3cf9c7dSdf157793 	register unsigned datasize;
277290a71dbdSzk194757 	size_t ioc_count;
2773d3cf9c7dSdf157793 	mblk_t *datamp;
2774d3cf9c7dSdf157793 	int error = 0;
2775d3cf9c7dSdf157793 	uchar_t val, icr;
2776d3cf9c7dSdf157793 #ifdef DEBUG
2777d3cf9c7dSdf157793 	if (asydebug & ASY_DEBUG_PROCS)
2778d3cf9c7dSdf157793 		printf("ioctl\n");
2779d3cf9c7dSdf157793 #endif
2780d3cf9c7dSdf157793 
2781d3cf9c7dSdf157793 	if (tp->t_iocpending != NULL) {
2782d3cf9c7dSdf157793 		/*
2783d3cf9c7dSdf157793 		 * We were holding an "ioctl" response pending the
2784d3cf9c7dSdf157793 		 * availability of an "mblk" to hold data to be passed up;
2785d3cf9c7dSdf157793 		 * another "ioctl" came through, which means that "ioctl"
2786d3cf9c7dSdf157793 		 * must have timed out or been aborted.
2787d3cf9c7dSdf157793 		 */
2788d3cf9c7dSdf157793 		freemsg(async->async_ttycommon.t_iocpending);
2789d3cf9c7dSdf157793 		async->async_ttycommon.t_iocpending = NULL;
2790d3cf9c7dSdf157793 	}
2791d3cf9c7dSdf157793 
2792d3cf9c7dSdf157793 	iocp = (struct iocblk *)mp->b_rptr;
2793d3cf9c7dSdf157793 
2794d3cf9c7dSdf157793 	/*
279590a71dbdSzk194757 	 * Save off the ioc count in case we need to restore it
279690a71dbdSzk194757 	 * because we are queuing a message block.
279790a71dbdSzk194757 	 */
279890a71dbdSzk194757 	ioc_count = iocp->ioc_count;
279990a71dbdSzk194757 
280090a71dbdSzk194757 	/*
2801d3cf9c7dSdf157793 	 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call
2802d3cf9c7dSdf157793 	 * ttycommon_ioctl() because this function frees up the message block
2803d3cf9c7dSdf157793 	 * (mp->b_cont) that contains the address of the user variable where
2804d3cf9c7dSdf157793 	 * we need to pass back the bit array.
28050280efdcSzk194757 	 *
28060280efdcSzk194757 	 * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO
28070280efdcSzk194757 	 * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them.
2808d3cf9c7dSdf157793 	 */
2809d3cf9c7dSdf157793 	if (iocp->ioc_cmd == TIOCMGET ||
2810d3cf9c7dSdf157793 	    iocp->ioc_cmd == TIOCMBIC ||
2811d3cf9c7dSdf157793 	    iocp->ioc_cmd == TIOCMBIS ||
2812d3cf9c7dSdf157793 	    iocp->ioc_cmd == TIOCMSET ||
2813d3cf9c7dSdf157793 	    iocp->ioc_cmd == TIOCGPPS ||
2814d3cf9c7dSdf157793 	    iocp->ioc_cmd == TIOCSPPS ||
28150280efdcSzk194757 	    iocp->ioc_cmd == TIOCGPPSEV ||
28160280efdcSzk194757 	    iocp->ioc_cmd == CONSOPENPOLLEDIO ||
28170280efdcSzk194757 	    iocp->ioc_cmd == CONSCLOSEPOLLEDIO)
2818d3cf9c7dSdf157793 		error = -1; /* Do Nothing */
2819d3cf9c7dSdf157793 	else
2820d3cf9c7dSdf157793 
2821d3cf9c7dSdf157793 	/*
2822d3cf9c7dSdf157793 	 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
2823d3cf9c7dSdf157793 	 * requires a response containing data to be returned to the user,
2824d3cf9c7dSdf157793 	 * and no mblk could be allocated for the data.
2825d3cf9c7dSdf157793 	 * No such "ioctl" alters our state.  Thus, we always go ahead and
2826d3cf9c7dSdf157793 	 * do any state-changes the "ioctl" calls for.  If we couldn't allocate
2827d3cf9c7dSdf157793 	 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
2828d3cf9c7dSdf157793 	 * we just call "bufcall" to request that we be called back when we
2829d3cf9c7dSdf157793 	 * stand a better chance of allocating the data.
2830d3cf9c7dSdf157793 	 */
2831d3cf9c7dSdf157793 	if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
2832d3cf9c7dSdf157793 		if (async->async_wbufcid)
2833d3cf9c7dSdf157793 			unbufcall(async->async_wbufcid);
2834d3cf9c7dSdf157793 		async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl,
2835d3cf9c7dSdf157793 		    async);
2836d3cf9c7dSdf157793 		return;
2837d3cf9c7dSdf157793 	}
2838d3cf9c7dSdf157793 
2839d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
2840d3cf9c7dSdf157793 
2841d3cf9c7dSdf157793 	if (error == 0) {
2842d3cf9c7dSdf157793 		/*
2843d3cf9c7dSdf157793 		 * "ttycommon_ioctl" did most of the work; we just use the
2844d3cf9c7dSdf157793 		 * data it set up.
2845d3cf9c7dSdf157793 		 */
2846d3cf9c7dSdf157793 		switch (iocp->ioc_cmd) {
2847d3cf9c7dSdf157793 
2848d3cf9c7dSdf157793 		case TCSETS:
2849d3cf9c7dSdf157793 			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2850d3cf9c7dSdf157793 			    asy->asy_lom_console)) {
2851d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2852d3cf9c7dSdf157793 				error = asy_program(asy, ASY_NOINIT);
2853d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2854d3cf9c7dSdf157793 			}
2855d3cf9c7dSdf157793 			break;
2856d3cf9c7dSdf157793 		case TCSETSF:
2857d3cf9c7dSdf157793 		case TCSETSW:
2858d3cf9c7dSdf157793 		case TCSETA:
2859d3cf9c7dSdf157793 		case TCSETAW:
2860d3cf9c7dSdf157793 		case TCSETAF:
2861d3cf9c7dSdf157793 			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
2862d3cf9c7dSdf157793 			    asy->asy_lom_console)) {
2863d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
2864d3cf9c7dSdf157793 				if (iswput && asy_isbusy(asy)) {
286590a71dbdSzk194757 					/*
286690a71dbdSzk194757 					 * ttycommon_ioctl sets the db_type to
286790a71dbdSzk194757 					 * M_IOCACK and ioc_count to zero
286890a71dbdSzk194757 					 * we need to undo this when we
286990a71dbdSzk194757 					 * queue a control message. This will
287090a71dbdSzk194757 					 * allow the control messages to be
287190a71dbdSzk194757 					 * processed again when the chip
287290a71dbdSzk194757 					 * becomes available.
287390a71dbdSzk194757 					 */
287490a71dbdSzk194757 					mp->b_datap->db_type = M_IOCTL;
287590a71dbdSzk194757 					iocp->ioc_count = ioc_count;
287690a71dbdSzk194757 
2877d3cf9c7dSdf157793 					if (putq(wq, mp) == 0)
2878d3cf9c7dSdf157793 						freemsg(mp);
2879d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl_hi);
2880d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl);
2881d3cf9c7dSdf157793 					return;
2882d3cf9c7dSdf157793 				}
28830a450c21Szk194757 
28840a450c21Szk194757 				/*
28850a450c21Szk194757 				 * TCSETA, TCSETAW, and TCSETAF make use of
28860a450c21Szk194757 				 * the termio structure and therefore have
28870a450c21Szk194757 				 * no concept of any speed except what can
28880a450c21Szk194757 				 * be represented by CBAUD. This is because
28890a450c21Szk194757 				 * of legacy SVR4 code. Therefore, if we see
28900a450c21Szk194757 				 * one of the aforementioned IOCTL commands
28910a450c21Szk194757 				 * we should zero out CBAUDEXT, CIBAUD, and
28920a450c21Szk194757 				 * CIBAUDEXT as to not break legacy
28930a450c21Szk194757 				 * functionality. This is because CBAUDEXT,
28940a450c21Szk194757 				 * CIBAUD, and CIBAUDEXT can't be stored in
28950a450c21Szk194757 				 * an unsigned short. By zeroing out CBAUDEXT,
28960a450c21Szk194757 				 * CIBAUD, and CIBAUDEXT in the t_cflag of the
28970a450c21Szk194757 				 * termios structure asy_program() will set the
28980a450c21Szk194757 				 * input baud rate to the output baud rate.
28990a450c21Szk194757 				 */
29000a450c21Szk194757 				if (iocp->ioc_cmd == TCSETA ||
29010a450c21Szk194757 				    iocp->ioc_cmd == TCSETAW ||
29020a450c21Szk194757 				    iocp->ioc_cmd == TCSETAF)
29030a450c21Szk194757 					tp->t_cflag &= ~(CIBAUD |
29040a450c21Szk194757 					    CIBAUDEXT | CBAUDEXT);
29050a450c21Szk194757 
2906d3cf9c7dSdf157793 				error = asy_program(asy, ASY_NOINIT);
2907d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
2908d3cf9c7dSdf157793 			}
2909d3cf9c7dSdf157793 			break;
2910d3cf9c7dSdf157793 		case TIOCSSOFTCAR:
2911d3cf9c7dSdf157793 			/* Set the driver state appropriately */
2912d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2913d3cf9c7dSdf157793 			if (tp->t_flags & TS_SOFTCAR)
2914d3cf9c7dSdf157793 				asy->asy_flags |= ASY_IGNORE_CD;
2915d3cf9c7dSdf157793 			else
2916d3cf9c7dSdf157793 				asy->asy_flags &= ~ASY_IGNORE_CD;
2917d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2918d3cf9c7dSdf157793 			break;
2919d3cf9c7dSdf157793 		}
2920d3cf9c7dSdf157793 	} else if (error < 0) {
2921d3cf9c7dSdf157793 		/*
2922d3cf9c7dSdf157793 		 * "ttycommon_ioctl" didn't do anything; we process it here.
2923d3cf9c7dSdf157793 		 */
2924d3cf9c7dSdf157793 		error = 0;
2925d3cf9c7dSdf157793 		switch (iocp->ioc_cmd) {
2926d3cf9c7dSdf157793 
2927d3cf9c7dSdf157793 		case TIOCGPPS:
2928d3cf9c7dSdf157793 			/*
2929d3cf9c7dSdf157793 			 * Get PPS on/off.
2930d3cf9c7dSdf157793 			 */
2931d3cf9c7dSdf157793 			if (mp->b_cont != NULL)
2932d3cf9c7dSdf157793 				freemsg(mp->b_cont);
2933d3cf9c7dSdf157793 
2934d3cf9c7dSdf157793 			mp->b_cont = allocb(sizeof (int), BPRI_HI);
2935d3cf9c7dSdf157793 			if (mp->b_cont == NULL) {
2936d3cf9c7dSdf157793 				error = ENOMEM;
2937d3cf9c7dSdf157793 				break;
2938d3cf9c7dSdf157793 			}
2939d3cf9c7dSdf157793 			if (asy->asy_flags & ASY_PPS)
2940d3cf9c7dSdf157793 				*(int *)mp->b_cont->b_wptr = 1;
2941d3cf9c7dSdf157793 			else
2942d3cf9c7dSdf157793 				*(int *)mp->b_cont->b_wptr = 0;
2943d3cf9c7dSdf157793 			mp->b_cont->b_wptr += sizeof (int);
2944d3cf9c7dSdf157793 			mp->b_datap->db_type = M_IOCACK;
2945d3cf9c7dSdf157793 			iocp->ioc_count = sizeof (int);
2946d3cf9c7dSdf157793 			break;
2947d3cf9c7dSdf157793 
2948d3cf9c7dSdf157793 		case TIOCSPPS:
2949d3cf9c7dSdf157793 			/*
2950d3cf9c7dSdf157793 			 * Set PPS on/off.
2951d3cf9c7dSdf157793 			 */
2952d3cf9c7dSdf157793 			error = miocpullup(mp, sizeof (int));
2953d3cf9c7dSdf157793 			if (error != 0)
2954d3cf9c7dSdf157793 				break;
2955d3cf9c7dSdf157793 
2956d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2957d3cf9c7dSdf157793 			if (*(int *)mp->b_cont->b_rptr)
2958d3cf9c7dSdf157793 				asy->asy_flags |= ASY_PPS;
2959d3cf9c7dSdf157793 			else
2960d3cf9c7dSdf157793 				asy->asy_flags &= ~ASY_PPS;
2961d3cf9c7dSdf157793 			/* Reset edge sense */
2962d3cf9c7dSdf157793 			asy->asy_flags &= ~ASY_PPS_EDGE;
2963d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2964d3cf9c7dSdf157793 			mp->b_datap->db_type = M_IOCACK;
2965d3cf9c7dSdf157793 			break;
2966d3cf9c7dSdf157793 
2967d3cf9c7dSdf157793 		case TIOCGPPSEV: {
2968d3cf9c7dSdf157793 			/*
2969d3cf9c7dSdf157793 			 * Get PPS event data.
2970d3cf9c7dSdf157793 			 */
2971d3cf9c7dSdf157793 			mblk_t *bp;
2972d3cf9c7dSdf157793 			void *buf;
2973d3cf9c7dSdf157793 #ifdef _SYSCALL32_IMPL
2974d3cf9c7dSdf157793 			struct ppsclockev32 p32;
2975d3cf9c7dSdf157793 #endif
2976d3cf9c7dSdf157793 			struct ppsclockev ppsclockev;
2977d3cf9c7dSdf157793 
2978d3cf9c7dSdf157793 			if (mp->b_cont != NULL) {
2979d3cf9c7dSdf157793 				freemsg(mp->b_cont);
2980d3cf9c7dSdf157793 				mp->b_cont = NULL;
2981d3cf9c7dSdf157793 			}
2982d3cf9c7dSdf157793 
2983d3cf9c7dSdf157793 			if ((asy->asy_flags & ASY_PPS) == 0) {
2984d3cf9c7dSdf157793 				error = ENXIO;
2985d3cf9c7dSdf157793 				break;
2986d3cf9c7dSdf157793 			}
2987d3cf9c7dSdf157793 
2988d3cf9c7dSdf157793 			/* Protect from incomplete asy_ppsev */
2989d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
2990d3cf9c7dSdf157793 			ppsclockev = asy_ppsev;
2991d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
2992d3cf9c7dSdf157793 
2993d3cf9c7dSdf157793 #ifdef _SYSCALL32_IMPL
2994d3cf9c7dSdf157793 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
2995d3cf9c7dSdf157793 				TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
2996d3cf9c7dSdf157793 				p32.serial = ppsclockev.serial;
2997d3cf9c7dSdf157793 				buf = &p32;
2998d3cf9c7dSdf157793 				iocp->ioc_count = sizeof (struct ppsclockev32);
2999d3cf9c7dSdf157793 			} else
3000d3cf9c7dSdf157793 #endif
3001d3cf9c7dSdf157793 			{
3002d3cf9c7dSdf157793 				buf = &ppsclockev;
3003d3cf9c7dSdf157793 				iocp->ioc_count = sizeof (struct ppsclockev);
3004d3cf9c7dSdf157793 			}
3005d3cf9c7dSdf157793 
3006d3cf9c7dSdf157793 			if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
3007d3cf9c7dSdf157793 				error = ENOMEM;
3008d3cf9c7dSdf157793 				break;
3009d3cf9c7dSdf157793 			}
3010d3cf9c7dSdf157793 			mp->b_cont = bp;
3011d3cf9c7dSdf157793 
3012d3cf9c7dSdf157793 			bcopy(buf, bp->b_wptr, iocp->ioc_count);
3013d3cf9c7dSdf157793 			bp->b_wptr += iocp->ioc_count;
3014d3cf9c7dSdf157793 			mp->b_datap->db_type = M_IOCACK;
3015d3cf9c7dSdf157793 			break;
3016d3cf9c7dSdf157793 		}
3017d3cf9c7dSdf157793 
3018d3cf9c7dSdf157793 		case TCSBRK:
3019d3cf9c7dSdf157793 			error = miocpullup(mp, sizeof (int));
3020d3cf9c7dSdf157793 			if (error != 0)
3021d3cf9c7dSdf157793 				break;
3022d3cf9c7dSdf157793 
3023d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3024d3cf9c7dSdf157793 			if (*(int *)mp->b_cont->b_rptr == 0) {
3025d3cf9c7dSdf157793 				/*
3026d3cf9c7dSdf157793 				 * Get the divisor by calculating the rate
3027d3cf9c7dSdf157793 				 */
3028d3cf9c7dSdf157793 				unsigned int rate, divisor;
3029d3cf9c7dSdf157793 				rate = async->async_ttycommon.t_cflag & CBAUD;
3030d3cf9c7dSdf157793 				if (async->async_ttycommon.t_cflag & CBAUDEXT)
3031d3cf9c7dSdf157793 					rate += 16;
3032d3cf9c7dSdf157793 				if (rate >= N_SU_SPEEDS) rate = B9600;
3033d3cf9c7dSdf157793 				divisor = asyspdtab[rate] & 0xfff;
3034d3cf9c7dSdf157793 
3035d3cf9c7dSdf157793 				/*
3036d3cf9c7dSdf157793 				 * To ensure that erroneous characters are
3037d3cf9c7dSdf157793 				 * not sent out when the break is set, SB
3038d3cf9c7dSdf157793 				 * recommends three steps:
3039d3cf9c7dSdf157793 				 *
3040d3cf9c7dSdf157793 				 * 1) pad the TSR with 0 bits
3041d3cf9c7dSdf157793 				 * 2) When the TSR is full, set break
3042d3cf9c7dSdf157793 				 * 3) When the TSR has been flushed, unset
3043d3cf9c7dSdf157793 				 *    the break when transmission must be
3044d3cf9c7dSdf157793 				 *    restored.
3045d3cf9c7dSdf157793 				 *
3046d3cf9c7dSdf157793 				 * We loop until the TSR is empty and then
3047d3cf9c7dSdf157793 				 * set the break.  ASYNC_BREAK has been set
3048d3cf9c7dSdf157793 				 * to ensure that no characters are
3049d3cf9c7dSdf157793 				 * transmitted while the TSR is being
3050d3cf9c7dSdf157793 				 * flushed and SOUT is being used for the
3051d3cf9c7dSdf157793 				 * break signal.
3052d3cf9c7dSdf157793 				 *
3053d3cf9c7dSdf157793 				 * The wait period is equal to
3054d3cf9c7dSdf157793 				 * clock / (baud * 16) * 16 * 2.
3055d3cf9c7dSdf157793 				 */
3056d3cf9c7dSdf157793 				async->async_flags |= ASYNC_BREAK;
3057d3cf9c7dSdf157793 				while ((INB(LSR) & XSRE) == 0) {
3058d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl_hi);
3059d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl);
3060d3cf9c7dSdf157793 					drv_usecwait(32*divisor);
3061d3cf9c7dSdf157793 					mutex_enter(asy->asy_excl);
3062d3cf9c7dSdf157793 					mutex_enter(asy->asy_excl_hi);
3063d3cf9c7dSdf157793 				}
3064d3cf9c7dSdf157793 
3065d3cf9c7dSdf157793 				/*
3066d3cf9c7dSdf157793 				 * Set the break bit, and arrange for
3067d3cf9c7dSdf157793 				 * "async_restart" to be called in 1/4 second;
3068d3cf9c7dSdf157793 				 * it will turn the break bit off, and call
3069d3cf9c7dSdf157793 				 * "async_start" to grab the next message.
3070d3cf9c7dSdf157793 				 */
3071d3cf9c7dSdf157793 				val = INB(LCR);
3072d3cf9c7dSdf157793 				OUTB(LCR, (val | SETBREAK));
3073d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3074d3cf9c7dSdf157793 				(void) timeout(async_restart, async, hz / 4);
3075d3cf9c7dSdf157793 			} else {
3076d3cf9c7dSdf157793 #ifdef DEBUG
3077d3cf9c7dSdf157793 				if (asydebug & ASY_DEBUG_CLOSE)
3078d3cf9c7dSdf157793 					printf("asy%d: wait for flush.\n",
3079d3cf9c7dSdf157793 					    UNIT(async->async_dev));
3080d3cf9c7dSdf157793 #endif
3081d3cf9c7dSdf157793 				if (iswput && asy_isbusy(asy)) {
3082d3cf9c7dSdf157793 					if (putq(wq, mp) == 0)
3083d3cf9c7dSdf157793 						freemsg(mp);
3084d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl_hi);
3085d3cf9c7dSdf157793 					mutex_exit(asy->asy_excl);
3086d3cf9c7dSdf157793 					return;
3087d3cf9c7dSdf157793 				}
3088d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3089d3cf9c7dSdf157793 #ifdef DEBUG
3090d3cf9c7dSdf157793 				if (asydebug & ASY_DEBUG_CLOSE)
3091d3cf9c7dSdf157793 					printf("asy%d: ldterm satisfied.\n",
3092d3cf9c7dSdf157793 					    UNIT(async->async_dev));
3093d3cf9c7dSdf157793 #endif
3094d3cf9c7dSdf157793 			}
3095d3cf9c7dSdf157793 			break;
3096d3cf9c7dSdf157793 
3097d3cf9c7dSdf157793 		case TIOCSBRK:
3098d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3099d3cf9c7dSdf157793 			val = INB(LCR);
3100d3cf9c7dSdf157793 			OUTB(LCR, (val | SETBREAK));
3101d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
3102d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3103d3cf9c7dSdf157793 			miocack(wq, mp, 0, 0);
3104d3cf9c7dSdf157793 			return;
3105d3cf9c7dSdf157793 
3106d3cf9c7dSdf157793 		case TIOCCBRK:
3107d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3108d3cf9c7dSdf157793 			val = INB(LCR);
3109d3cf9c7dSdf157793 			OUTB(LCR, (val & ~SETBREAK));
3110d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
3111d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3112d3cf9c7dSdf157793 			miocack(wq, mp, 0, 0);
3113d3cf9c7dSdf157793 			return;
3114d3cf9c7dSdf157793 
3115d3cf9c7dSdf157793 		case TIOCMSET:
3116d3cf9c7dSdf157793 		case TIOCMBIS:
3117d3cf9c7dSdf157793 		case TIOCMBIC:
3118d3cf9c7dSdf157793 			if (iocp->ioc_count == TRANSPARENT)
3119d3cf9c7dSdf157793 				mcopyin(mp, NULL, sizeof (int), NULL);
3120d3cf9c7dSdf157793 			else {
3121d3cf9c7dSdf157793 				error = miocpullup(mp, sizeof (int));
3122d3cf9c7dSdf157793 				if (error != 0)
3123d3cf9c7dSdf157793 					break;
3124d3cf9c7dSdf157793 
3125d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
3126d3cf9c7dSdf157793 
3127d3cf9c7dSdf157793 				(void) asymctl(asy,
3128d3cf9c7dSdf157793 				    dmtoasy(*(int *)mp->b_cont->b_rptr),
3129d3cf9c7dSdf157793 				    iocp->ioc_cmd);
3130d3cf9c7dSdf157793 
3131d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3132d3cf9c7dSdf157793 				iocp->ioc_error = 0;
3133d3cf9c7dSdf157793 				mp->b_datap->db_type = M_IOCACK;
3134d3cf9c7dSdf157793 			}
3135d3cf9c7dSdf157793 			break;
3136d3cf9c7dSdf157793 
3137d3cf9c7dSdf157793 		case TIOCSILOOP:
3138d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3139d3cf9c7dSdf157793 			/*
3140d3cf9c7dSdf157793 			 * If somebody misues this Ioctl when used for
3141d3cf9c7dSdf157793 			 * driving keyboard and mouse indicate not supported
3142d3cf9c7dSdf157793 			 */
3143d3cf9c7dSdf157793 			if ((asy->asy_device_type == ASY_KEYBOARD) ||
3144d3cf9c7dSdf157793 			    (asy->asy_device_type == ASY_MOUSE)) {
3145d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3146d3cf9c7dSdf157793 				error = ENOTTY;
3147d3cf9c7dSdf157793 				break;
3148d3cf9c7dSdf157793 			}
3149d3cf9c7dSdf157793 
3150d3cf9c7dSdf157793 			/* should not use when we're the console */
3151d3cf9c7dSdf157793 			if ((async->async_dev == kbddev) ||
3152d3cf9c7dSdf157793 			    (async->async_dev == rconsdev) ||
3153d3cf9c7dSdf157793 			    (async->async_dev == stdindev)) {
3154d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3155d3cf9c7dSdf157793 				error = EINVAL;
3156d3cf9c7dSdf157793 				break;
3157d3cf9c7dSdf157793 			}
3158d3cf9c7dSdf157793 
3159d3cf9c7dSdf157793 			val = INB(MCR);
3160d3cf9c7dSdf157793 			icr = INB(ICR);
3161d3cf9c7dSdf157793 			/*
3162d3cf9c7dSdf157793 			 * Disable the Modem Status Interrupt
3163d3cf9c7dSdf157793 			 * The reason for disabling is  the status of
3164d3cf9c7dSdf157793 			 * modem signal are in the higher 4 bits instead of
3165d3cf9c7dSdf157793 			 * lower four bits when in loopback mode,
3166d3cf9c7dSdf157793 			 * so, donot worry about Modem interrupt when
3167d3cf9c7dSdf157793 			 * you are planning to set
3168d3cf9c7dSdf157793 			 * this in loopback mode until it is cleared by
3169d3cf9c7dSdf157793 			 * another ioctl to get out of the loopback mode
3170d3cf9c7dSdf157793 			 */
3171d3cf9c7dSdf157793 			OUTB(ICR, icr & ~ MIEN);
3172d3cf9c7dSdf157793 			OUTB(MCR, val | ASY_LOOP);
3173d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
3174d3cf9c7dSdf157793 			iocp->ioc_error = 0;
3175d3cf9c7dSdf157793 			mp->b_datap->db_type = M_IOCACK;
3176d3cf9c7dSdf157793 			break;
3177d3cf9c7dSdf157793 
3178d3cf9c7dSdf157793 		case TIOCMGET:
3179d3cf9c7dSdf157793 			datamp = allocb(sizeof (int), BPRI_MED);
3180d3cf9c7dSdf157793 			if (datamp == NULL) {
3181d3cf9c7dSdf157793 				error = EAGAIN;
3182d3cf9c7dSdf157793 				break;
3183d3cf9c7dSdf157793 			}
3184d3cf9c7dSdf157793 
3185d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3186d3cf9c7dSdf157793 			*(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
3187d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
3188d3cf9c7dSdf157793 
3189d3cf9c7dSdf157793 			if (iocp->ioc_count == TRANSPARENT) {
3190d3cf9c7dSdf157793 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
3191d3cf9c7dSdf157793 			} else {
3192d3cf9c7dSdf157793 				if (mp->b_cont != NULL)
3193d3cf9c7dSdf157793 					freemsg(mp->b_cont);
3194d3cf9c7dSdf157793 				mp->b_cont = datamp;
3195d3cf9c7dSdf157793 				mp->b_cont->b_wptr += sizeof (int);
3196d3cf9c7dSdf157793 				mp->b_datap->db_type = M_IOCACK;
3197d3cf9c7dSdf157793 				iocp->ioc_count = sizeof (int);
3198d3cf9c7dSdf157793 			}
3199d3cf9c7dSdf157793 			break;
3200d3cf9c7dSdf157793 
32010280efdcSzk194757 		case CONSOPENPOLLEDIO:
32020280efdcSzk194757 			/*
32030280efdcSzk194757 			 * If we are driving a keyboard there is nothing
32040280efdcSzk194757 			 * upstream to translate the scan codes. Therefore,
32050280efdcSzk194757 			 * set the error code to ENOTSUP and NAK the request
32060280efdcSzk194757 			 */
32070280efdcSzk194757 			if (asy->asy_device_type == ASY_KEYBOARD) {
32080280efdcSzk194757 				error = ENOTSUP;
32090280efdcSzk194757 				break;
32100280efdcSzk194757 			}
32110280efdcSzk194757 
32120280efdcSzk194757 			error = miocpullup(mp, sizeof (struct cons_polledio *));
32130280efdcSzk194757 			if (error != 0)
32140280efdcSzk194757 				break;
32150280efdcSzk194757 
32160280efdcSzk194757 			/*
32170280efdcSzk194757 			 * send up a message block containing the
32180280efdcSzk194757 			 * cons_polledio structure. This provides
32190280efdcSzk194757 			 * handles to the putchar, getchar, ischar,
32200280efdcSzk194757 			 * polledio_enter and polledio_exit functions.
32210280efdcSzk194757 			 */
32220280efdcSzk194757 			*(struct cons_polledio **)mp->b_cont->b_rptr =
32230280efdcSzk194757 			    &asy->polledio;
32240280efdcSzk194757 
32250280efdcSzk194757 			mp->b_datap->db_type = M_IOCACK;
32260280efdcSzk194757 			break;
32270280efdcSzk194757 
32280280efdcSzk194757 		case CONSCLOSEPOLLEDIO:
32290280efdcSzk194757 			/*
32300280efdcSzk194757 			 * If we are driving a keyboard we never successfully
32310280efdcSzk194757 			 * called CONSOPENPOLLEDIO so set the error to
32320280efdcSzk194757 			 * ENOTSUP and NAK the request.
32330280efdcSzk194757 			 */
32340280efdcSzk194757 			if (asy->asy_device_type == ASY_KEYBOARD) {
32350280efdcSzk194757 				error = ENOTSUP;
32360280efdcSzk194757 				break;
32370280efdcSzk194757 			}
32380280efdcSzk194757 
32390280efdcSzk194757 			mp->b_datap->db_type = M_IOCACK;
32400280efdcSzk194757 			iocp->ioc_error = 0;
32410280efdcSzk194757 			iocp->ioc_rval = 0;
32420280efdcSzk194757 			break;
32430280efdcSzk194757 
3244d3cf9c7dSdf157793 		default: /* unexpected ioctl type */
3245d3cf9c7dSdf157793 			/*
3246d3cf9c7dSdf157793 			 * If we don't understand it, it's an error.  NAK it.
3247d3cf9c7dSdf157793 			 */
3248d3cf9c7dSdf157793 			error = EINVAL;
3249d3cf9c7dSdf157793 			break;
3250d3cf9c7dSdf157793 		}
3251d3cf9c7dSdf157793 	}
3252d3cf9c7dSdf157793 	if (error != 0) {
3253d3cf9c7dSdf157793 		iocp->ioc_error = error;
3254d3cf9c7dSdf157793 		mp->b_datap->db_type = M_IOCNAK;
3255d3cf9c7dSdf157793 	}
3256d3cf9c7dSdf157793 	mutex_exit(asy->asy_excl);
3257d3cf9c7dSdf157793 	qreply(wq, mp);
3258d3cf9c7dSdf157793 }
3259d3cf9c7dSdf157793 
3260*e476cc14SToomas Soome static int
asyrsrv(queue_t * q)3261d3cf9c7dSdf157793 asyrsrv(queue_t *q)
3262d3cf9c7dSdf157793 {
3263d3cf9c7dSdf157793 	mblk_t *bp;
3264d3cf9c7dSdf157793 	struct asyncline *async;
3265d3cf9c7dSdf157793 
3266d3cf9c7dSdf157793 	async = (struct asyncline *)q->q_ptr;
3267d3cf9c7dSdf157793 
3268d3cf9c7dSdf157793 	while (canputnext(q) && (bp = getq(q)))
3269d3cf9c7dSdf157793 		putnext(q, bp);
3270d3cf9c7dSdf157793 	ASYSETSOFT(async->async_common);
3271d3cf9c7dSdf157793 	async->async_polltid = 0;
3272*e476cc14SToomas Soome 	return (0);
3273d3cf9c7dSdf157793 }
3274d3cf9c7dSdf157793 
3275d3cf9c7dSdf157793 /*
3276d3cf9c7dSdf157793  * Put procedure for write queue.
3277d3cf9c7dSdf157793  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3278d3cf9c7dSdf157793  * set the flow control character for M_STOPI and M_STARTI messages;
3279d3cf9c7dSdf157793  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3280d3cf9c7dSdf157793  * by the start routine, and then call the start routine; discard
3281d3cf9c7dSdf157793  * everything else.  Note that this driver does not incorporate any
3282d3cf9c7dSdf157793  * mechanism to negotiate to handle the canonicalization process.
3283d3cf9c7dSdf157793  * It expects that these functions are handled in upper module(s),
3284d3cf9c7dSdf157793  * as we do in ldterm.
3285d3cf9c7dSdf157793  */
3286*e476cc14SToomas Soome static int
asywput(queue_t * q,mblk_t * mp)3287d3cf9c7dSdf157793 asywput(queue_t *q, mblk_t *mp)
3288d3cf9c7dSdf157793 {
3289d3cf9c7dSdf157793 	register struct asyncline *async;
3290d3cf9c7dSdf157793 	register struct asycom *asy;
3291d3cf9c7dSdf157793 	int error;
3292d3cf9c7dSdf157793 
3293d3cf9c7dSdf157793 	async = (struct asyncline *)q->q_ptr;
3294d3cf9c7dSdf157793 	asy = async->async_common;
3295d3cf9c7dSdf157793 
3296d3cf9c7dSdf157793 	switch (mp->b_datap->db_type) {
3297d3cf9c7dSdf157793 
3298d3cf9c7dSdf157793 	case M_STOP:
3299d3cf9c7dSdf157793 		/*
3300d3cf9c7dSdf157793 		 * Since we don't do real DMA, we can just let the
3301d3cf9c7dSdf157793 		 * chip coast to a stop after applying the brakes.
3302d3cf9c7dSdf157793 		 */
3303d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3304d3cf9c7dSdf157793 		async->async_flags |= ASYNC_STOPPED;
3305d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3306d3cf9c7dSdf157793 		freemsg(mp);
3307d3cf9c7dSdf157793 		break;
3308d3cf9c7dSdf157793 
3309d3cf9c7dSdf157793 	case M_START:
3310d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3311d3cf9c7dSdf157793 		if (async->async_flags & ASYNC_STOPPED) {
3312d3cf9c7dSdf157793 			async->async_flags &= ~ASYNC_STOPPED;
3313d3cf9c7dSdf157793 			/*
3314d3cf9c7dSdf157793 			 * If an output operation is in progress,
3315d3cf9c7dSdf157793 			 * resume it.  Otherwise, prod the start
3316d3cf9c7dSdf157793 			 * routine.
3317d3cf9c7dSdf157793 			 */
3318d3cf9c7dSdf157793 			if (async->async_ocnt > 0) {
3319d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl_hi);
3320d3cf9c7dSdf157793 				async_resume(async);
3321d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl_hi);
3322d3cf9c7dSdf157793 			} else {
3323d3cf9c7dSdf157793 				async_start(async);
3324d3cf9c7dSdf157793 			}
3325d3cf9c7dSdf157793 		}
3326d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3327d3cf9c7dSdf157793 		freemsg(mp);
3328d3cf9c7dSdf157793 		break;
3329d3cf9c7dSdf157793 
3330d3cf9c7dSdf157793 	case M_IOCTL:
3331d3cf9c7dSdf157793 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
3332d3cf9c7dSdf157793 
3333d3cf9c7dSdf157793 		case TCSBRK:
3334d3cf9c7dSdf157793 			error = miocpullup(mp, sizeof (int));
3335d3cf9c7dSdf157793 			if (error != 0) {
3336d3cf9c7dSdf157793 				miocnak(q, mp, 0, error);
3337*e476cc14SToomas Soome 				return (0);
3338d3cf9c7dSdf157793 			}
3339d3cf9c7dSdf157793 
3340d3cf9c7dSdf157793 			if (*(int *)mp->b_cont->b_rptr != 0) {
3341d3cf9c7dSdf157793 #ifdef DEBUG
3342d3cf9c7dSdf157793 				if (asydebug & ASY_DEBUG_CLOSE)
3343d3cf9c7dSdf157793 					printf("asy%d: flush request.\n",
3344d3cf9c7dSdf157793 					    UNIT(async->async_dev));
3345d3cf9c7dSdf157793 #endif
3346d3cf9c7dSdf157793 				(void) putq(q, mp);
3347d3cf9c7dSdf157793 				mutex_enter(asy->asy_excl);
3348d3cf9c7dSdf157793 				async_nstart(async, 1);
3349d3cf9c7dSdf157793 				mutex_exit(asy->asy_excl);
3350d3cf9c7dSdf157793 				break;
3351d3cf9c7dSdf157793 			}
3352d3cf9c7dSdf157793 			/*FALLTHROUGH*/
3353d3cf9c7dSdf157793 		case TCSETSW:
3354d3cf9c7dSdf157793 		case TCSETSF:
3355d3cf9c7dSdf157793 		case TCSETAW:
3356d3cf9c7dSdf157793 		case TCSETAF:
3357d3cf9c7dSdf157793 			/*
3358d3cf9c7dSdf157793 			 * The changes do not take effect until all
3359d3cf9c7dSdf157793 			 * output queued before them is drained.
3360d3cf9c7dSdf157793 			 * Put this message on the queue, so that
3361d3cf9c7dSdf157793 			 * "async_start" will see it when it's done
3362d3cf9c7dSdf157793 			 * with the output before it.  Poke the
3363d3cf9c7dSdf157793 			 * start routine, just in case.
3364d3cf9c7dSdf157793 			 */
3365d3cf9c7dSdf157793 			(void) putq(q, mp);
3366d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
3367d3cf9c7dSdf157793 			async_start(async);
3368d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3369d3cf9c7dSdf157793 			break;
3370d3cf9c7dSdf157793 
3371d3cf9c7dSdf157793 		default:
3372d3cf9c7dSdf157793 			/*
3373d3cf9c7dSdf157793 			 * Do it now.
3374d3cf9c7dSdf157793 			 */
3375d3cf9c7dSdf157793 			async_ioctl(async, q, mp, B_TRUE);
3376d3cf9c7dSdf157793 			break;
3377d3cf9c7dSdf157793 		}
3378d3cf9c7dSdf157793 		break;
3379d3cf9c7dSdf157793 
3380d3cf9c7dSdf157793 	case M_FLUSH:
3381d3cf9c7dSdf157793 		if (*mp->b_rptr & FLUSHW) {
3382d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
3383d3cf9c7dSdf157793 
3384d3cf9c7dSdf157793 			/*
3385d3cf9c7dSdf157793 			 * Abort any output in progress.
3386d3cf9c7dSdf157793 			 */
3387d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl_hi);
3388d3cf9c7dSdf157793 			if (async->async_flags & ASYNC_BUSY) {
3389d3cf9c7dSdf157793 				async->async_ocnt = 0;
3390d3cf9c7dSdf157793 				async->async_flags &= ~ASYNC_BUSY;
3391d3cf9c7dSdf157793 			}
3392d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl_hi);
3393d3cf9c7dSdf157793 
3394d3cf9c7dSdf157793 			/* Flush FIFO buffers */
3395d3cf9c7dSdf157793 			if (asy->asy_use_fifo == FIFO_ON) {
3396d3cf9c7dSdf157793 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH |
3397d3cf9c7dSdf157793 				    (asy->asy_trig_level & 0xff));
3398d3cf9c7dSdf157793 			}
3399d3cf9c7dSdf157793 
3400d3cf9c7dSdf157793 			/*
3401d3cf9c7dSdf157793 			 * Flush our write queue.
3402d3cf9c7dSdf157793 			 */
3403d3cf9c7dSdf157793 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
3404d3cf9c7dSdf157793 			if (async->async_xmitblk != NULL) {
3405d3cf9c7dSdf157793 				freeb(async->async_xmitblk);
3406d3cf9c7dSdf157793 				async->async_xmitblk = NULL;
3407d3cf9c7dSdf157793 			}
3408d3cf9c7dSdf157793 
3409d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3410d3cf9c7dSdf157793 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
3411d3cf9c7dSdf157793 		}
3412d3cf9c7dSdf157793 		if (*mp->b_rptr & FLUSHR) {
3413d3cf9c7dSdf157793 			/* Flush FIFO buffers */
3414d3cf9c7dSdf157793 			if (asy->asy_use_fifo == FIFO_ON) {
3415d3cf9c7dSdf157793 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
3416d3cf9c7dSdf157793 				    (asy->asy_trig_level & 0xff));
3417d3cf9c7dSdf157793 			}
3418d3cf9c7dSdf157793 			flushq(RD(q), FLUSHDATA);
3419d3cf9c7dSdf157793 			qreply(q, mp);	/* give the read queues a crack at it */
3420d3cf9c7dSdf157793 		} else {
3421d3cf9c7dSdf157793 			freemsg(mp);
3422d3cf9c7dSdf157793 		}
3423d3cf9c7dSdf157793 
3424d3cf9c7dSdf157793 		/*
3425d3cf9c7dSdf157793 		 * We must make sure we process messages that survive the
3426d3cf9c7dSdf157793 		 * write-side flush.  Without this call, the close protocol
3427d3cf9c7dSdf157793 		 * with ldterm can hang forever.  (ldterm will have sent us a
3428d3cf9c7dSdf157793 		 * TCSBRK ioctl that it expects a response to.)
3429d3cf9c7dSdf157793 		 */
3430d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3431d3cf9c7dSdf157793 		async_start(async);
3432d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3433d3cf9c7dSdf157793 		break;
3434d3cf9c7dSdf157793 	case M_BREAK:
3435d3cf9c7dSdf157793 	case M_DELAY:
3436d3cf9c7dSdf157793 	case M_DATA:
3437d3cf9c7dSdf157793 		/*
3438d3cf9c7dSdf157793 		 * Queue the message up to be transmitted,
3439d3cf9c7dSdf157793 		 * and poke the start routine.
3440d3cf9c7dSdf157793 		 */
3441d3cf9c7dSdf157793 		(void) putq(q, mp);
3442d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3443d3cf9c7dSdf157793 		async_start(async);
3444d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3445d3cf9c7dSdf157793 		break;
3446d3cf9c7dSdf157793 
3447d3cf9c7dSdf157793 	case M_STOPI:
3448d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3449d3cf9c7dSdf157793 		async->async_flowc = async->async_stopc;
3450d3cf9c7dSdf157793 		async_start(async);		/* poke the start routine */
3451d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3452d3cf9c7dSdf157793 		freemsg(mp);
3453d3cf9c7dSdf157793 		break;
3454d3cf9c7dSdf157793 
3455d3cf9c7dSdf157793 	case M_STARTI:
3456d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl);
3457d3cf9c7dSdf157793 		async->async_flowc = async->async_startc;
3458d3cf9c7dSdf157793 		async_start(async);		/* poke the start routine */
3459d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3460d3cf9c7dSdf157793 		freemsg(mp);
3461d3cf9c7dSdf157793 		break;
3462d3cf9c7dSdf157793 
3463d3cf9c7dSdf157793 	case M_CTL:
3464d3cf9c7dSdf157793 		if (MBLKL(mp) >= sizeof (struct iocblk) &&
3465d3cf9c7dSdf157793 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
3466d3cf9c7dSdf157793 			((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
3467d3cf9c7dSdf157793 			qreply(q, mp);
3468d3cf9c7dSdf157793 		} else {
3469d3cf9c7dSdf157793 			/*
3470d3cf9c7dSdf157793 			 * These MC_SERVICE type messages are used by upper
3471d3cf9c7dSdf157793 			 * modules to tell this driver to send input up
3472d3cf9c7dSdf157793 			 * immediately, or that it can wait for normal
3473d3cf9c7dSdf157793 			 * processing that may or may not be done.  Sun
3474d3cf9c7dSdf157793 			 * requires these for the mouse module.
3475d3cf9c7dSdf157793 			 * (XXX - for x86?)
3476d3cf9c7dSdf157793 			 */
3477d3cf9c7dSdf157793 			mutex_enter(asy->asy_excl);
3478d3cf9c7dSdf157793 			switch (*mp->b_rptr) {
3479d3cf9c7dSdf157793 
3480d3cf9c7dSdf157793 			case MC_SERVICEIMM:
3481d3cf9c7dSdf157793 				async->async_flags |= ASYNC_SERVICEIMM;
3482d3cf9c7dSdf157793 				break;
3483d3cf9c7dSdf157793 
3484d3cf9c7dSdf157793 			case MC_SERVICEDEF:
3485d3cf9c7dSdf157793 				async->async_flags &= ~ASYNC_SERVICEIMM;
3486d3cf9c7dSdf157793 				break;
3487d3cf9c7dSdf157793 			}
3488d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3489d3cf9c7dSdf157793 			freemsg(mp);
3490d3cf9c7dSdf157793 		}
3491d3cf9c7dSdf157793 		break;
3492d3cf9c7dSdf157793 
3493d3cf9c7dSdf157793 	case M_IOCDATA:
3494d3cf9c7dSdf157793 		async_iocdata(q, mp);
3495d3cf9c7dSdf157793 		break;
3496d3cf9c7dSdf157793 
3497d3cf9c7dSdf157793 	default:
3498d3cf9c7dSdf157793 		freemsg(mp);
3499d3cf9c7dSdf157793 		break;
3500d3cf9c7dSdf157793 	}
3501*e476cc14SToomas Soome 	return (0);
3502d3cf9c7dSdf157793 }
3503d3cf9c7dSdf157793 
3504d3cf9c7dSdf157793 /*
3505d3cf9c7dSdf157793  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
3506d3cf9c7dSdf157793  * the buffer we need.
3507d3cf9c7dSdf157793  */
3508d3cf9c7dSdf157793 static void
async_reioctl(void * arg)3509d3cf9c7dSdf157793 async_reioctl(void *arg)
3510d3cf9c7dSdf157793 {
3511d3cf9c7dSdf157793 	struct asyncline *async = arg;
3512d3cf9c7dSdf157793 	struct asycom *asy = async->async_common;
3513d3cf9c7dSdf157793 	queue_t	*q;
3514d3cf9c7dSdf157793 	mblk_t		*mp;
3515d3cf9c7dSdf157793 
3516d3cf9c7dSdf157793 	/*
3517d3cf9c7dSdf157793 	 * The bufcall is no longer pending.
3518d3cf9c7dSdf157793 	 */
3519d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
3520d3cf9c7dSdf157793 	async->async_wbufcid = 0;
3521d3cf9c7dSdf157793 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
3522d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3523d3cf9c7dSdf157793 		return;
3524d3cf9c7dSdf157793 	}
3525d3cf9c7dSdf157793 	if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
3526d3cf9c7dSdf157793 		/* not pending any more */
3527d3cf9c7dSdf157793 		async->async_ttycommon.t_iocpending = NULL;
3528d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3529d3cf9c7dSdf157793 		/* not in STREAMS queue; we no longer know if we're in wput */
3530d3cf9c7dSdf157793 		async_ioctl(async, q, mp, B_TRUE);
3531d3cf9c7dSdf157793 	} else
3532d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3533d3cf9c7dSdf157793 }
3534d3cf9c7dSdf157793 
3535d3cf9c7dSdf157793 static void
async_iocdata(queue_t * q,mblk_t * mp)3536d3cf9c7dSdf157793 async_iocdata(queue_t *q, mblk_t *mp)
3537d3cf9c7dSdf157793 {
3538d3cf9c7dSdf157793 	struct asyncline	*async = (struct asyncline *)q->q_ptr;
3539d3cf9c7dSdf157793 	struct asycom		*asy;
3540d3cf9c7dSdf157793 	struct copyresp *csp;
3541d3cf9c7dSdf157793 
3542d3cf9c7dSdf157793 	asy = async->async_common;
3543d3cf9c7dSdf157793 	csp = (struct copyresp *)mp->b_rptr;
3544d3cf9c7dSdf157793 
3545d3cf9c7dSdf157793 	if (csp->cp_rval != 0) {
3546d3cf9c7dSdf157793 		freemsg(mp);
3547d3cf9c7dSdf157793 		return;
3548d3cf9c7dSdf157793 	}
3549d3cf9c7dSdf157793 
3550d3cf9c7dSdf157793 	mutex_enter(asy->asy_excl);
3551d3cf9c7dSdf157793 
3552d3cf9c7dSdf157793 	switch (csp->cp_cmd) {
3553d3cf9c7dSdf157793 	case TIOCMSET:
3554d3cf9c7dSdf157793 	case TIOCMBIS:
3555d3cf9c7dSdf157793 	case TIOCMBIC:
3556d3cf9c7dSdf157793 		if (mp->b_cont == NULL) {
3557d3cf9c7dSdf157793 			mutex_exit(asy->asy_excl);
3558d3cf9c7dSdf157793 			miocnak(q, mp, 0, EINVAL);
3559d3cf9c7dSdf157793 			break;
3560d3cf9c7dSdf157793 		}
3561d3cf9c7dSdf157793 
3562d3cf9c7dSdf157793 		mutex_enter(asy->asy_excl_hi);
3563d3cf9c7dSdf157793 		(void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
3564d3cf9c7dSdf157793 		    csp->cp_cmd);
3565d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl_hi);
3566d3cf9c7dSdf157793 
3567d3cf9c7dSdf157793 		freemsg(mp->b_cont);
3568d3cf9c7dSdf157793 		mp->b_cont = NULL;
3569d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3570d3cf9c7dSdf157793 		miocack(q, mp, 0, 0);
3571d3cf9c7dSdf157793 		break;
3572d3cf9c7dSdf157793 
3573d3cf9c7dSdf157793 	case TIOCMGET:
3574d3cf9c7dSdf157793 		if (mp->b_cont != NULL) {
3575d3cf9c7dSdf157793 			freemsg(mp->b_cont);
3576d3cf9c7dSdf157793 			mp->b_cont = NULL;
3577d3cf9c7dSdf157793 		}
3578d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3579d3cf9c7dSdf157793 		miocack(q, mp, 0, 0);
3580d3cf9c7dSdf157793 		break;
3581d3cf9c7dSdf157793 
3582d3cf9c7dSdf157793 	default:
3583d3cf9c7dSdf157793 		mutex_exit(asy->asy_excl);
3584d3cf9c7dSdf157793 		miocnak(q, mp, 0, EINVAL);
3585d3cf9c7dSdf157793 		break;
3586d3cf9c7dSdf157793 	}
3587d3cf9c7dSdf157793 }
3588d3cf9c7dSdf157793 
3589d3cf9c7dSdf157793 
3590d3cf9c7dSdf157793 /*
3591d3cf9c7dSdf157793  * Set or get the modem control status.
3592d3cf9c7dSdf157793  */
3593d3cf9c7dSdf157793 static int
asymctl(struct asycom * asy,int bits,int how)3594d3cf9c7dSdf157793 asymctl(struct asycom *asy, int bits, int how)
3595d3cf9c7dSdf157793 {
3596d3cf9c7dSdf157793 	register int mcr_r, msr_r;
3597d3cf9c7dSdf157793 
3598d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
3599d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl));
3600d3cf9c7dSdf157793 
3601d3cf9c7dSdf157793 	/* Read Modem Control Registers */
3602d3cf9c7dSdf157793 	mcr_r = INB(MCR);
3603d3cf9c7dSdf157793 
3604d3cf9c7dSdf157793 	switch (how) {
3605d3cf9c7dSdf157793 
3606d3cf9c7dSdf157793 	case TIOCMSET:
3607d3cf9c7dSdf157793 		mcr_r = bits;
3608d3cf9c7dSdf157793 		break;
3609d3cf9c7dSdf157793 
3610d3cf9c7dSdf157793 	case TIOCMBIS:
3611d3cf9c7dSdf157793 		mcr_r |= bits;			/* Set bits from input	*/
3612d3cf9c7dSdf157793 		break;
3613d3cf9c7dSdf157793 
3614d3cf9c7dSdf157793 	case TIOCMBIC:
3615d3cf9c7dSdf157793 		mcr_r &= ~bits;			/* Set ~bits from input	*/
3616d3cf9c7dSdf157793 		break;
3617d3cf9c7dSdf157793 
3618d3cf9c7dSdf157793 	case TIOCMGET:
3619d3cf9c7dSdf157793 		/* Read Modem Status Registers */
3620d3cf9c7dSdf157793 		if (INB(ICR) & MIEN)
3621d3cf9c7dSdf157793 			msr_r = asy->asy_cached_msr;
3622d3cf9c7dSdf157793 		else
3623d3cf9c7dSdf157793 			msr_r = INB(MSR);
3624d3cf9c7dSdf157793 		return (asytodm(mcr_r, msr_r));
3625d3cf9c7dSdf157793 	}
3626d3cf9c7dSdf157793 
3627d3cf9c7dSdf157793 	OUTB(MCR, mcr_r);
3628d3cf9c7dSdf157793 
3629d3cf9c7dSdf157793 	return (mcr_r);
3630d3cf9c7dSdf157793 }
3631d3cf9c7dSdf157793 
3632d3cf9c7dSdf157793 static int
asytodm(int mcr_r,int msr_r)3633d3cf9c7dSdf157793 asytodm(int mcr_r, int msr_r)
3634d3cf9c7dSdf157793 {
3635d3cf9c7dSdf157793 	register int b = 0;
3636d3cf9c7dSdf157793 
3637d3cf9c7dSdf157793 
3638d3cf9c7dSdf157793 	/* MCR registers */
3639d3cf9c7dSdf157793 	if (mcr_r & RTS)
3640d3cf9c7dSdf157793 		b |= TIOCM_RTS;
3641d3cf9c7dSdf157793 
3642d3cf9c7dSdf157793 	if (mcr_r & DTR)
3643d3cf9c7dSdf157793 		b |= TIOCM_DTR;
3644d3cf9c7dSdf157793 
3645d3cf9c7dSdf157793 	/* MSR registers */
3646d3cf9c7dSdf157793 	if (msr_r & DCD)
3647d3cf9c7dSdf157793 		b |= TIOCM_CAR;
3648d3cf9c7dSdf157793 
3649d3cf9c7dSdf157793 	if (msr_r & CTS)
3650d3cf9c7dSdf157793 		b |= TIOCM_CTS;
3651d3cf9c7dSdf157793 
3652d3cf9c7dSdf157793 	if (msr_r & DSR)
3653d3cf9c7dSdf157793 		b |= TIOCM_DSR;
3654d3cf9c7dSdf157793 
3655d3cf9c7dSdf157793 	if (msr_r & RI)
3656d3cf9c7dSdf157793 		b |= TIOCM_RNG;
3657d3cf9c7dSdf157793 
3658d3cf9c7dSdf157793 	return (b);
3659d3cf9c7dSdf157793 }
3660d3cf9c7dSdf157793 
3661d3cf9c7dSdf157793 static int
dmtoasy(int bits)3662d3cf9c7dSdf157793 dmtoasy(int bits)
3663d3cf9c7dSdf157793 {
3664d3cf9c7dSdf157793 	register int b = 0;
3665d3cf9c7dSdf157793 
3666d3cf9c7dSdf157793 #ifdef	CAN_NOT_SET	/* only DTR and RTS can be set */
3667d3cf9c7dSdf157793 	if (bits & TIOCM_CAR)
3668d3cf9c7dSdf157793 		b |= DCD;
3669d3cf9c7dSdf157793 	if (bits & TIOCM_CTS)
3670d3cf9c7dSdf157793 		b |= CTS;
3671d3cf9c7dSdf157793 	if (bits & TIOCM_DSR)
3672d3cf9c7dSdf157793 		b |= DSR;
3673d3cf9c7dSdf157793 	if (bits & TIOCM_RNG)
3674d3cf9c7dSdf157793 		b |= RI;
3675d3cf9c7dSdf157793 #endif
3676d3cf9c7dSdf157793 
3677d3cf9c7dSdf157793 	if (bits & TIOCM_RTS)
3678d3cf9c7dSdf157793 		b |= RTS;
3679d3cf9c7dSdf157793 	if (bits & TIOCM_DTR)
3680d3cf9c7dSdf157793 		b |= DTR;
3681d3cf9c7dSdf157793 
3682d3cf9c7dSdf157793 	return (b);
3683d3cf9c7dSdf157793 }
3684d3cf9c7dSdf157793 
3685d3cf9c7dSdf157793 static void
asycheckflowcontrol_hw(struct asycom * asy)3686d3cf9c7dSdf157793 asycheckflowcontrol_hw(struct asycom *asy)
3687d3cf9c7dSdf157793 {
3688d3cf9c7dSdf157793 	struct asyncline *async;
3689d3cf9c7dSdf157793 	uchar_t	mcr, flag;
3690d3cf9c7dSdf157793 
3691d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
3692d3cf9c7dSdf157793 
3693d3cf9c7dSdf157793 	async = (struct asyncline *)asy->asy_priv;
3694d3cf9c7dSdf157793 	ASSERT(async != NULL);
3695d3cf9c7dSdf157793 
3696d3cf9c7dSdf157793 	if (async->async_ttycommon.t_cflag & CRTSXOFF) {
3697d3cf9c7dSdf157793 		mcr = INB(MCR);
3698d3cf9c7dSdf157793 		flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
3699d3cf9c7dSdf157793 		if (((mcr ^ flag) & RTS) != 0) {
3700d3cf9c7dSdf157793 			OUTB(MCR, (mcr ^ RTS));
3701d3cf9c7dSdf157793 		}
3702d3cf9c7dSdf157793 	}
3703d3cf9c7dSdf157793 }
3704d3cf9c7dSdf157793 
3705d3cf9c7dSdf157793 static boolean_t
asycheckflowcontrol_sw(struct asycom * asy)3706d3cf9c7dSdf157793 asycheckflowcontrol_sw(struct asycom *asy)
3707d3cf9c7dSdf157793 {
3708d3cf9c7dSdf157793 	uchar_t		ss;
3709d3cf9c7dSdf157793 	struct asyncline *async;
3710d3cf9c7dSdf157793 	int rval = B_FALSE;
3711d3cf9c7dSdf157793 
3712d3cf9c7dSdf157793 	ASSERT(mutex_owned(asy->asy_excl_hi));
3713d3cf9c7dSdf157793 
3714d3cf9c7dSdf157793 	async = (struct asyncline *)asy->asy_priv;
3715d3cf9c7dSdf157793 	ASSERT(async != NULL);
3716d3cf9c7dSdf157793 
3717d3cf9c7dSdf157793 	if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) {
3718d3cf9c7dSdf157793 		/*
3719d3cf9c7dSdf157793 		 * If we get this far, then we know that flowc is non-zero and
3720d3cf9c7dSdf157793 		 * that there's transmit room available.  We've "handled" the
3721d3cf9c7dSdf157793 		 * request now, so clear it.  If the user didn't ask for IXOFF,
3722d3cf9c7dSdf157793 		 * then don't actually send anything, but wait for the next
3723d3cf9c7dSdf157793 		 * opportunity.
3724d3cf9c7dSdf157793 		 */
3725d3cf9c7dSdf157793 		async->async_flowc = '\0';
3726d3cf9c7dSdf157793 		if (async->async_ttycommon.t_iflag & IXOFF) {
3727d3cf9c7dSdf157793 			async->async_flags |= ASYNC_BUSY;
3728d3cf9c7dSdf157793 			OUTB(DAT, ss);
3729d3cf9c7dSdf157793 			rval = B_TRUE;
3730d3cf9c7dSdf157793 		}
3731d3cf9c7dSdf157793 	}
3732d3cf9c7dSdf157793 
3733d3cf9c7dSdf157793 	return (rval);
3734d3cf9c7dSdf157793 }
3735d3cf9c7dSdf157793 
3736d3cf9c7dSdf157793 /*
3737d3cf9c7dSdf157793  * Check for abort character sequence
3738d3cf9c7dSdf157793  */
3739d3cf9c7dSdf157793 static boolean_t
abort_charseq_recognize(uchar_t ch)3740d3cf9c7dSdf157793 abort_charseq_recognize(uchar_t ch)
3741d3cf9c7dSdf157793 {
3742d3cf9c7dSdf157793 	static int state = 0;
3743d3cf9c7dSdf157793 #define	CNTRL(c) ((c)&037)
3744d3cf9c7dSdf157793 	static char sequence[] = { '\r', '~', CNTRL('b') };
3745d3cf9c7dSdf157793 
3746d3cf9c7dSdf157793 	if (ch == sequence[state]) {
3747d3cf9c7dSdf157793 		if (++state >= sizeof (sequence)) {
3748d3cf9c7dSdf157793 			state = 0;
3749d3cf9c7dSdf157793 			return (B_TRUE);
3750d3cf9c7dSdf157793 		}
3751d3cf9c7dSdf157793 	} else {
3752d3cf9c7dSdf157793 		state = (ch == sequence[0]) ? 1 : 0;
3753d3cf9c7dSdf157793 	}
3754d3cf9c7dSdf157793 	return (B_FALSE);
3755d3cf9c7dSdf157793 }
3756