xref: /illumos-gate/usr/src/uts/sun4/io/su_driver.c (revision e476cc14)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57cb42c7eSrameshc  * Common Development and Distribution License (the "License").
67cb42c7eSrameshc  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
227c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
237c478bd9Sstevel@tonic-gate /*	  All Rights Reserved					*/
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate /*
264d0b1b0dSAn Bui  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
277c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
2848bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
29730a650aSPeter Tribble  * Copyright (c) 2019 Peter Tribble.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate /*
34f63f7506Sanovick  *	Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips.
357c478bd9Sstevel@tonic-gate  *	Modified as sparc keyboard/mouse driver.
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate #define	SU_REGISTER_FILE_NO 0
387c478bd9Sstevel@tonic-gate #define	SU_REGOFFSET 0
397c478bd9Sstevel@tonic-gate #define	SU_REGISTER_LEN 8
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #include <sys/param.h>
427c478bd9Sstevel@tonic-gate #include <sys/types.h>
437c478bd9Sstevel@tonic-gate #include <sys/signal.h>
447c478bd9Sstevel@tonic-gate #include <sys/stream.h>
457c478bd9Sstevel@tonic-gate #include <sys/termio.h>
467c478bd9Sstevel@tonic-gate #include <sys/errno.h>
477c478bd9Sstevel@tonic-gate #include <sys/file.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
507c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
517c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
527c478bd9Sstevel@tonic-gate #include <sys/strtty.h>
537c478bd9Sstevel@tonic-gate #include <sys/debug.h>
547c478bd9Sstevel@tonic-gate #include <sys/kbio.h>
557c478bd9Sstevel@tonic-gate #include <sys/cred.h>
567c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
577c478bd9Sstevel@tonic-gate #include <sys/stat.h>
587c478bd9Sstevel@tonic-gate #include <sys/consdev.h>
597c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
607c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
617c478bd9Sstevel@tonic-gate #include <sys/cred.h>
627c478bd9Sstevel@tonic-gate #ifdef DEBUG
637c478bd9Sstevel@tonic-gate #include <sys/promif.h>
647c478bd9Sstevel@tonic-gate #endif
657c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
667c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
677c478bd9Sstevel@tonic-gate #include <sys/sudev.h>
687c478bd9Sstevel@tonic-gate #include <sys/note.h>
697c478bd9Sstevel@tonic-gate #include <sys/timex.h>
707c478bd9Sstevel@tonic-gate #include <sys/policy.h>
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #define	async_stopc	async_ttycommon.t_stopc
737c478bd9Sstevel@tonic-gate #define	async_startc	async_ttycommon.t_startc
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate #define	ASY_INIT	1
767c478bd9Sstevel@tonic-gate #define	ASY_NOINIT	0
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate #ifdef DEBUG
797c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_INIT	0x001
807c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_INPUT	0x002
817c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_EOT	0x004
827c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_CLOSE	0x008
837c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_HFLOW	0x010
847c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_PROCS	0x020
857c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_STATE	0x040
867c478bd9Sstevel@tonic-gate #define	ASY_DEBUG_INTR	0x080
877c478bd9Sstevel@tonic-gate static	int asydebug = 0;
887c478bd9Sstevel@tonic-gate #endif
897c478bd9Sstevel@tonic-gate static	int su_log = 0;
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate int su_drain_check = 15000000;		/* tunable: exit drain check time */
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate static	struct ppsclockev asy_ppsev;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate static	int max_asy_instance = -1;
967c478bd9Sstevel@tonic-gate static	void	*su_asycom;	/* soft state asycom pointer */
977c478bd9Sstevel@tonic-gate static	void	*su_asyncline;	/* soft state asyncline pointer */
987c478bd9Sstevel@tonic-gate static	boolean_t abort_charseq_recognize(uchar_t ch);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate static	uint_t	asysoftintr(caddr_t intarg);
1017c478bd9Sstevel@tonic-gate static	uint_t	asyintr(caddr_t argasy);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate /* The async interrupt entry points */
1047c478bd9Sstevel@tonic-gate static void	async_txint(struct asycom *asy, uchar_t lsr);
1057c478bd9Sstevel@tonic-gate static void	async_rxint(struct asycom *asy, uchar_t lsr);
1067c478bd9Sstevel@tonic-gate static void	async_msint(struct asycom *asy);
1077c478bd9Sstevel@tonic-gate static int	async_softint(struct asycom *asy);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate static void	async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp,
1107c478bd9Sstevel@tonic-gate     boolean_t iswput);
1117c478bd9Sstevel@tonic-gate static void	async_reioctl(void *);
1127c478bd9Sstevel@tonic-gate static void	async_iocdata(queue_t *q, mblk_t *mp);
1137c478bd9Sstevel@tonic-gate static void	async_restart(void *);
1147c478bd9Sstevel@tonic-gate static void	async_start(struct asyncline *async);
1157c478bd9Sstevel@tonic-gate static void	async_nstart(struct asyncline *async, int mode);
1167c478bd9Sstevel@tonic-gate static void	async_resume(struct asyncline *async);
1177c478bd9Sstevel@tonic-gate static int	asy_program(struct asycom *asy, int mode);
1187c478bd9Sstevel@tonic-gate 
1190280efdcSzk /* Polled mode functions */
1200280efdcSzk static void	asyputchar(cons_polledio_arg_t, uchar_t c);
1210280efdcSzk static int	asygetchar(cons_polledio_arg_t);
1220280efdcSzk static boolean_t	asyischar(cons_polledio_arg_t);
1230280efdcSzk static void	asy_polled_enter(cons_polledio_arg_t);
1240280efdcSzk static void	asy_polled_exit(cons_polledio_arg_t);
1250280efdcSzk 
1267c478bd9Sstevel@tonic-gate static int	asymctl(struct asycom *, int, int);
1277c478bd9Sstevel@tonic-gate static int	asytodm(int, int);
1287c478bd9Sstevel@tonic-gate static int	dmtoasy(int);
1297c478bd9Sstevel@tonic-gate static void	asycheckflowcontrol_hw(struct asycom *asy);
1307c478bd9Sstevel@tonic-gate static boolean_t asycheckflowcontrol_sw(struct asycom *asy);
1317c478bd9Sstevel@tonic-gate static void	asy_ppsevent(struct asycom *asy, int msr);
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate extern kcondvar_t lbolt_cv;
1347c478bd9Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
1357c478bd9Sstevel@tonic-gate 		int spec_type, minor_t minor_num);
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate /*
1397c478bd9Sstevel@tonic-gate  * Baud rate table. Indexed by #defines found in sys/termios.h
1407c478bd9Sstevel@tonic-gate  */
1417c478bd9Sstevel@tonic-gate ushort_t asyspdtab[] = {
1427c478bd9Sstevel@tonic-gate 	0,	/* 0 baud rate */
1437c478bd9Sstevel@tonic-gate 	0x900,	/* 50 baud rate */
1447c478bd9Sstevel@tonic-gate 	0x600,	/* 75 baud rate */
1457c478bd9Sstevel@tonic-gate 	0x417,	/* 110 baud rate (%0.026) */
1467c478bd9Sstevel@tonic-gate 	0x359,	/* 134 baud rate (%0.058) */
1477c478bd9Sstevel@tonic-gate 	0x300,	/* 150 baud rate */
1487c478bd9Sstevel@tonic-gate 	0x240,	/* 200 baud rate */
1497c478bd9Sstevel@tonic-gate 	0x180,	/* 300 baud rate */
1507c478bd9Sstevel@tonic-gate 	0x0c0,	/* 600 baud rate */
1517c478bd9Sstevel@tonic-gate 	0x060,	/* 1200 baud rate */
1527c478bd9Sstevel@tonic-gate 	0x040,	/* 1800 baud rate */
1537c478bd9Sstevel@tonic-gate 	0x030,	/* 2400 baud rate */
1547c478bd9Sstevel@tonic-gate 	0x018,	/* 4800 baud rate */
1557c478bd9Sstevel@tonic-gate 	0x00c,	/* 9600 baud rate */
1567c478bd9Sstevel@tonic-gate 	0x006,	/* 19200 baud rate */
1577c478bd9Sstevel@tonic-gate 	0x003,	/* 38400 baud rate */
1587c478bd9Sstevel@tonic-gate 	0x002,	/* 57600 baud rate */
1597c478bd9Sstevel@tonic-gate 	0,	/* 76800 baud rate - not supported */
1607c478bd9Sstevel@tonic-gate 	0x001,	/* 115200 baud rate */
1617c478bd9Sstevel@tonic-gate 	0,	/* 153600 baud rate - not supported */
1627c478bd9Sstevel@tonic-gate 	0x8002,	/* 230400 baud rate - supported on specific platforms */
1637c478bd9Sstevel@tonic-gate 	0,	/* 307200 baud rate - not supported */
1647c478bd9Sstevel@tonic-gate 	0x8001	/* 460800 baud rate - supported on specific platforms */
1657c478bd9Sstevel@tonic-gate };
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate  * Number of speeds supported is the number of entries in
1697c478bd9Sstevel@tonic-gate  * the above table.
1707c478bd9Sstevel@tonic-gate  */
1717c478bd9Sstevel@tonic-gate #define	N_SU_SPEEDS	(sizeof (asyspdtab)/sizeof (ushort_t))
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * Human-readable baud rate table.
1757c478bd9Sstevel@tonic-gate  * Indexed by #defines found in sys/termios.h
1767c478bd9Sstevel@tonic-gate  */
1777c478bd9Sstevel@tonic-gate int baudtable[] = {
1787c478bd9Sstevel@tonic-gate 	0,	/* 0 baud rate */
1797c478bd9Sstevel@tonic-gate 	50,	/* 50 baud rate */
1807c478bd9Sstevel@tonic-gate 	75,	/* 75 baud rate */
1817c478bd9Sstevel@tonic-gate 	110,	/* 110 baud rate */
1827c478bd9Sstevel@tonic-gate 	134,	/* 134 baud rate */
1837c478bd9Sstevel@tonic-gate 	150,	/* 150 baud rate */
1847c478bd9Sstevel@tonic-gate 	200,	/* 200 baud rate */
1857c478bd9Sstevel@tonic-gate 	300,	/* 300 baud rate */
1867c478bd9Sstevel@tonic-gate 	600,	/* 600 baud rate */
1877c478bd9Sstevel@tonic-gate 	1200,	/* 1200 baud rate */
1887c478bd9Sstevel@tonic-gate 	1800,	/* 1800 baud rate */
1897c478bd9Sstevel@tonic-gate 	2400,	/* 2400 baud rate */
1907c478bd9Sstevel@tonic-gate 	4800,	/* 4800 baud rate */
1917c478bd9Sstevel@tonic-gate 	9600,	/* 9600 baud rate */
1927c478bd9Sstevel@tonic-gate 	19200,	/* 19200 baud rate */
1937c478bd9Sstevel@tonic-gate 	38400,	/* 38400 baud rate */
1947c478bd9Sstevel@tonic-gate 	57600,	/* 57600 baud rate */
1957c478bd9Sstevel@tonic-gate 	76800,	/* 76800 baud rate */
1967c478bd9Sstevel@tonic-gate 	115200,	/* 115200 baud rate */
1977c478bd9Sstevel@tonic-gate 	153600,	/* 153600 baud rate */
1987c478bd9Sstevel@tonic-gate 	230400,	/* 230400 baud rate */
1997c478bd9Sstevel@tonic-gate 	307200,	/* 307200 baud rate */
2007c478bd9Sstevel@tonic-gate 	460800	/* 460800 baud rate */
2017c478bd9Sstevel@tonic-gate };
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 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);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate struct module_info asy_info = {
2097c478bd9Sstevel@tonic-gate 	0,
2107c478bd9Sstevel@tonic-gate 	"su",
2117c478bd9Sstevel@tonic-gate 	0,
2127c478bd9Sstevel@tonic-gate 	INFPSZ,
2137c478bd9Sstevel@tonic-gate 	32*4096,
2147c478bd9Sstevel@tonic-gate 	4096
2157c478bd9Sstevel@tonic-gate };
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static struct qinit asy_rint = {
2187c478bd9Sstevel@tonic-gate 	putq,
219*e476cc14SToomas Soome 	asyrsrv,
2207c478bd9Sstevel@tonic-gate 	asyopen,
2217c478bd9Sstevel@tonic-gate 	asyclose,
2227c478bd9Sstevel@tonic-gate 	NULL,
2237c478bd9Sstevel@tonic-gate 	&asy_info,
2247c478bd9Sstevel@tonic-gate 	NULL
2257c478bd9Sstevel@tonic-gate };
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate static struct qinit asy_wint = {
228*e476cc14SToomas Soome 	asywput,
2297c478bd9Sstevel@tonic-gate 	NULL,
2307c478bd9Sstevel@tonic-gate 	NULL,
2317c478bd9Sstevel@tonic-gate 	NULL,
2327c478bd9Sstevel@tonic-gate 	NULL,
2337c478bd9Sstevel@tonic-gate 	&asy_info,
2347c478bd9Sstevel@tonic-gate 	NULL
2357c478bd9Sstevel@tonic-gate };
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate struct streamtab asy_str_info = {
2387c478bd9Sstevel@tonic-gate 	&asy_rint,
2397c478bd9Sstevel@tonic-gate 	&asy_wint,
2407c478bd9Sstevel@tonic-gate 	NULL,
2417c478bd9Sstevel@tonic-gate 	NULL
2427c478bd9Sstevel@tonic-gate };
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2457c478bd9Sstevel@tonic-gate 		void **result);
2467c478bd9Sstevel@tonic-gate static int asyprobe(dev_info_t *);
2477c478bd9Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t);
2487c478bd9Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t);
2497c478bd9Sstevel@tonic-gate 
250574493ffSToomas Soome static struct cb_ops cb_asy_ops = {
2517c478bd9Sstevel@tonic-gate 	nodev,			/* cb_open */
2527c478bd9Sstevel@tonic-gate 	nodev,			/* cb_close */
2537c478bd9Sstevel@tonic-gate 	nodev,			/* cb_strategy */
2547c478bd9Sstevel@tonic-gate 	nodev,			/* cb_print */
2557c478bd9Sstevel@tonic-gate 	nodev,			/* cb_dump */
2567c478bd9Sstevel@tonic-gate 	nodev,			/* cb_read */
2577c478bd9Sstevel@tonic-gate 	nodev,			/* cb_write */
2587c478bd9Sstevel@tonic-gate 	nodev,			/* cb_ioctl */
2597c478bd9Sstevel@tonic-gate 	nodev,			/* cb_devmap */
2607c478bd9Sstevel@tonic-gate 	nodev,			/* cb_mmap */
2617c478bd9Sstevel@tonic-gate 	nodev,			/* cb_segmap */
2627c478bd9Sstevel@tonic-gate 	nochpoll,		/* cb_chpoll */
2637c478bd9Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
2647c478bd9Sstevel@tonic-gate 	&asy_str_info,		/* cb_stream */
2657c478bd9Sstevel@tonic-gate 	D_MP			/* cb_flag */
2667c478bd9Sstevel@tonic-gate };
2677c478bd9Sstevel@tonic-gate 
2687c478bd9Sstevel@tonic-gate struct dev_ops asy_ops = {
2697c478bd9Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev */
2707c478bd9Sstevel@tonic-gate 	0,			/* devo_refcnt */
2717c478bd9Sstevel@tonic-gate 	asyinfo,		/* devo_getinfo */
2727c478bd9Sstevel@tonic-gate 	nulldev,		/* devo_identify */
2737c478bd9Sstevel@tonic-gate 	asyprobe,		/* devo_probe */
2747c478bd9Sstevel@tonic-gate 	asyattach,		/* devo_attach */
2757c478bd9Sstevel@tonic-gate 	asydetach,		/* devo_detach */
2767c478bd9Sstevel@tonic-gate 	nodev,			/* devo_reset */
2777c478bd9Sstevel@tonic-gate 	&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 */
2817c478bd9Sstevel@tonic-gate };
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate /*
2847c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
2857c478bd9Sstevel@tonic-gate  */
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2887c478bd9Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
28919397407SSherry Moore 	"su driver",
2907c478bd9Sstevel@tonic-gate 	&asy_ops,	/* driver ops */
2917c478bd9Sstevel@tonic-gate };
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2947c478bd9Sstevel@tonic-gate 	MODREV_1,
2957c478bd9Sstevel@tonic-gate 	&modldrv,
2967c478bd9Sstevel@tonic-gate 	NULL
2977c478bd9Sstevel@tonic-gate };
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate int
_init(void)3007c478bd9Sstevel@tonic-gate _init(void)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate 	int status;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom),
3057c478bd9Sstevel@tonic-gate 	    SU_INITIAL_SOFT_ITEMS);
3067c478bd9Sstevel@tonic-gate 	if (status != 0)
3070280efdcSzk 		return (status);
3087c478bd9Sstevel@tonic-gate 	status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline),
3097c478bd9Sstevel@tonic-gate 	    SU_INITIAL_SOFT_ITEMS);
3107c478bd9Sstevel@tonic-gate 	if (status != 0) {
3117c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&su_asycom);
3127c478bd9Sstevel@tonic-gate 		return (status);
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	if ((status = mod_install(&modlinkage)) != 0) {
3167c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&su_asycom);
3177c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&su_asyncline);
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	return (status);
3217c478bd9Sstevel@tonic-gate }
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate int
_fini(void)3247c478bd9Sstevel@tonic-gate _fini(void)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate 	int i;
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	i = mod_remove(&modlinkage);
3297c478bd9Sstevel@tonic-gate 	if (i == 0) {
3307c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&su_asycom);
3317c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&su_asyncline);
3327c478bd9Sstevel@tonic-gate 	}
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	return (i);
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)3387c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
3397c478bd9Sstevel@tonic-gate {
3407c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate static int
asyprobe(dev_info_t * devi)3447c478bd9Sstevel@tonic-gate asyprobe(dev_info_t *devi)
3457c478bd9Sstevel@tonic-gate {
3467c478bd9Sstevel@tonic-gate 	int		instance;
3477c478bd9Sstevel@tonic-gate 	ddi_acc_handle_t handle;
3487c478bd9Sstevel@tonic-gate 	uchar_t *addr;
3497c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
3527c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
3537c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
3547c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr,
3557c478bd9Sstevel@tonic-gate 	    SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) {
3567c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "asyprobe regs map setup failed");
3577c478bd9Sstevel@tonic-gate 		return (DDI_PROBE_FAILURE);
3587c478bd9Sstevel@tonic-gate 	}
3597c478bd9Sstevel@tonic-gate #ifdef DEBUG
3607c478bd9Sstevel@tonic-gate 	if (asydebug)
3610280efdcSzk 		printf("Probe address mapped %p\n", (void *)addr);
3627c478bd9Sstevel@tonic-gate #endif
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	/*
3657c478bd9Sstevel@tonic-gate 	 * Probe for the device:
366574493ffSToomas Soome 	 *	Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low.
367574493ffSToomas Soome 	 *	If bit 4 or 5 appears on inb() ISR, board is not there.
3687c478bd9Sstevel@tonic-gate 	 */
3692d526643Sjesusm 	if (ddi_get8(handle, addr+ISR) & 0x30) {
3700280efdcSzk 		ddi_regs_map_free(&handle);
3710280efdcSzk 		return (DDI_PROBE_FAILURE);
3722d526643Sjesusm 	}
3732d526643Sjesusm 
3747c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
3757c478bd9Sstevel@tonic-gate 	if (max_asy_instance < instance)
3760280efdcSzk 		max_asy_instance = instance;
3777c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&handle);
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	return (DDI_PROBE_SUCCESS); /* hw is present */
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate static int
asydetach(dev_info_t * devi,ddi_detach_cmd_t cmd)3837c478bd9Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3847c478bd9Sstevel@tonic-gate {
385*e476cc14SToomas Soome 	int	instance;
3867c478bd9Sstevel@tonic-gate 	struct asycom	*asy;
3877c478bd9Sstevel@tonic-gate 	struct asyncline *async;
3887c478bd9Sstevel@tonic-gate 	char		name[16];
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(devi);	/* find out which unit */
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
3937c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 	switch (cmd) {
3960280efdcSzk 		case DDI_DETACH:
3970280efdcSzk 			break;
3980280efdcSzk 		case DDI_SUSPEND:
3990280efdcSzk 			/* grab both mutex locks */
4000280efdcSzk 			mutex_enter(asy->asy_excl);
4010280efdcSzk 			mutex_enter(asy->asy_excl_hi);
4020280efdcSzk 			if (asy->suspended) {
4030280efdcSzk 				mutex_exit(asy->asy_excl_hi);
4040280efdcSzk 				mutex_exit(asy->asy_excl);
4050280efdcSzk 				return (DDI_SUCCESS);
4060280efdcSzk 			}
4070280efdcSzk 			asy->suspended = B_TRUE;
4080280efdcSzk 
4090280efdcSzk 			/*
4100280efdcSzk 			 * The quad UART ST16C554D, version D2 (made by EXAR)
4110280efdcSzk 			 * has an anomaly of generating spurious interrupts
4120280efdcSzk 			 * when the ICR is loaded with zero. The workaround
4130280efdcSzk 			 * would be to read/write any register with DATA1 bit
4140280efdcSzk 			 * set to 0 before such write.
4150280efdcSzk 			 */
4160280efdcSzk 			if (asy->asy_hwtype == ASY16C554D)
4170280efdcSzk 				OUTB(SPR, 0);
4180280efdcSzk 
4190280efdcSzk 			/* Disable further interrupts */
4200280efdcSzk 			OUTB(ICR, 0);
4217c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
4227c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
4237c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
42417ea09c7Sanovick 
4250280efdcSzk 		default:
4260280efdcSzk 			return (DDI_FAILURE);
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate #ifdef DEBUG
4307c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_INIT)
4310280efdcSzk 		cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance,
4320280efdcSzk 		    asy->asy_hwtype == ASY82510 ? "82510" :
4330280efdcSzk 		    asy->asy_hwtype == ASY16550AF ? "16550AF" :
4340280efdcSzk 		    asy->asy_hwtype == ASY16C554D ? "16C554D" :
4350280efdcSzk 		    "8250");
4367c478bd9Sstevel@tonic-gate #endif
4377c478bd9Sstevel@tonic-gate 	/*
4387c478bd9Sstevel@tonic-gate 	 * Before removing interrupts it is always better to disable
4397c478bd9Sstevel@tonic-gate 	 * interrupts if the chip gives a provision to disable the
4407c478bd9Sstevel@tonic-gate 	 * serial port interrupts.
4417c478bd9Sstevel@tonic-gate 	 */
4427c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
4437c478bd9Sstevel@tonic-gate 	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);
4487c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
4497c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	/* remove minor device node(s) for this device */
4527c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "%c", (instance+'a'));	/* serial-port */
4537c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, name);
4547c478bd9Sstevel@tonic-gate 	(void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */
4557c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, name);
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	mutex_destroy(asy->asy_excl);
4587c478bd9Sstevel@tonic-gate 	mutex_destroy(asy->asy_excl_hi);
4597c478bd9Sstevel@tonic-gate 	kmem_free(asy->asy_excl, sizeof (kmutex_t));
4607c478bd9Sstevel@tonic-gate 	kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
4617c478bd9Sstevel@tonic-gate 	cv_destroy(&async->async_flags_cv);
4627c478bd9Sstevel@tonic-gate 	kstat_delete(asy->sukstat);
4637c478bd9Sstevel@tonic-gate 	ddi_remove_intr(devi, 0, asy->asy_iblock);
4647c478bd9Sstevel@tonic-gate 	ddi_regs_map_free(&asy->asy_handle);
4657c478bd9Sstevel@tonic-gate 	ddi_remove_softintr(asy->asy_softintr_id);
4667c478bd9Sstevel@tonic-gate 	mutex_destroy(asy->asy_soft_lock);
4677c478bd9Sstevel@tonic-gate 	kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
4687c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(su_asycom, instance);
4697c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(su_asyncline, instance);
4707c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate static int
asyattach(dev_info_t * devi,ddi_attach_cmd_t cmd)4747c478bd9Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
4757c478bd9Sstevel@tonic-gate {
476*e476cc14SToomas Soome 	int	instance;
4777c478bd9Sstevel@tonic-gate 	struct asycom	*asy;
4787c478bd9Sstevel@tonic-gate 	struct asyncline *async;
4797c478bd9Sstevel@tonic-gate 	char		name[40];
4807c478bd9Sstevel@tonic-gate 	ddi_device_acc_attr_t attr;
4817c478bd9Sstevel@tonic-gate 	enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR,
4827c478bd9Sstevel@tonic-gate 	    SOFTINTR, ASYINIT, KSTAT, MINORNODE };
4837c478bd9Sstevel@tonic-gate 	enum states state = EMPTY;
484f63f7506Sanovick 	char *hwtype;
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(devi);	/* find out which unit */
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	/* cannot attach a device that has not been probed first */
4897c478bd9Sstevel@tonic-gate 	if (instance > max_asy_instance)
4900280efdcSzk 		return (DDI_FAILURE);
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 	if (cmd != DDI_RESUME) {
4937c478bd9Sstevel@tonic-gate 		/* Allocate soft state space */
4947c478bd9Sstevel@tonic-gate 		if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) {
4957c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "su%d: cannot allocate soft state",
4967c478bd9Sstevel@tonic-gate 			    instance);
4977c478bd9Sstevel@tonic-gate 			goto error;
4987c478bd9Sstevel@tonic-gate 		}
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 	state = SOFTSTATE;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	if (asy == NULL) {
5057c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "su%d: cannot get soft state", instance);
5067c478bd9Sstevel@tonic-gate 		goto error;
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	switch (cmd) {
5100280efdcSzk 		case DDI_ATTACH:
5110280efdcSzk 			break;
5120280efdcSzk 		case DDI_RESUME: {
5130280efdcSzk 			struct asyncline *async;
5147c478bd9Sstevel@tonic-gate 
5150280efdcSzk 			/* grab both mutex locks */
5160280efdcSzk 			mutex_enter(asy->asy_excl);
5170280efdcSzk 			mutex_enter(asy->asy_excl_hi);
5180280efdcSzk 			if (!asy->suspended) {
5190280efdcSzk 				mutex_exit(asy->asy_excl_hi);
5200280efdcSzk 				mutex_exit(asy->asy_excl);
5210280efdcSzk 				return (DDI_SUCCESS);
5220280efdcSzk 			}
5230280efdcSzk 			/*
5240280efdcSzk 			 * re-setup all the registers and enable interrupts if
5250280efdcSzk 			 * needed
5260280efdcSzk 			 */
5270280efdcSzk 			async = (struct asyncline *)asy->asy_priv;
5280280efdcSzk 			if ((async) && (async->async_flags & ASYNC_ISOPEN))
5290280efdcSzk 				(void) asy_program(asy, ASY_INIT);
5300280efdcSzk 			asy->suspended = B_FALSE;
5317c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
5327c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
5337c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
5347c478bd9Sstevel@tonic-gate 		}
5350280efdcSzk 		default:
5360280efdcSzk 			goto error;
5377c478bd9Sstevel@tonic-gate 	}
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
5407c478bd9Sstevel@tonic-gate 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
5417c478bd9Sstevel@tonic-gate 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO,
5447c478bd9Sstevel@tonic-gate 	    (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN,
5457c478bd9Sstevel@tonic-gate 	    &attr, &asy->asy_handle) != DDI_SUCCESS) {
5467c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "asyprobe regs map setup failed");
5477c478bd9Sstevel@tonic-gate 		goto error;
5487c478bd9Sstevel@tonic-gate 	}
5497c478bd9Sstevel@tonic-gate 	state = REGSMAP;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate #ifdef DEBUG
5527c478bd9Sstevel@tonic-gate 	if (asydebug)
5530280efdcSzk 		printf("su attach mapped %p\n", (void *)asy->asy_ioaddr);
5547c478bd9Sstevel@tonic-gate #endif
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	/*
5577c478bd9Sstevel@tonic-gate 	 * Initialize the port with default settings.
5587c478bd9Sstevel@tonic-gate 	 */
5597c478bd9Sstevel@tonic-gate 	asy->asy_fifo_buf = 1;
5607c478bd9Sstevel@tonic-gate 	asy->asy_use_fifo = FIFO_OFF;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	/*
5637c478bd9Sstevel@tonic-gate 	 * Check for baudrate generator's "baud-divisor-factor" property setup
5647c478bd9Sstevel@tonic-gate 	 * by OBP, since different UART chips might have different baudrate
5657c478bd9Sstevel@tonic-gate 	 * generator divisor. e.g., in case of NSPG's Sputnik platform, the
5667c478bd9Sstevel@tonic-gate 	 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip
5677c478bd9Sstevel@tonic-gate 	 * instead of SuperIO. Since the baud-divisor-factor must be a positive
5687c478bd9Sstevel@tonic-gate 	 * integer, the divisors will always be at least as large as the values
5697c478bd9Sstevel@tonic-gate 	 * in asyspdtab[].  Make the default factor 1.
5707c478bd9Sstevel@tonic-gate 	 */
5717c478bd9Sstevel@tonic-gate 	asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
5727c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "baud-divisor-factor", 1);
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	/* set speed cap */
5757c478bd9Sstevel@tonic-gate 	asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
5767c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "serial-speed-cap", 115200);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/* check for ASY82510 chip */
5797c478bd9Sstevel@tonic-gate 	OUTB(ISR, 0x20);
5807c478bd9Sstevel@tonic-gate 	if (INB(ISR) & 0x20) { /* 82510 chip is present */
5817c478bd9Sstevel@tonic-gate 		/*
5827c478bd9Sstevel@tonic-gate 		 * Since most of the general operation of the 82510 chip
5837c478bd9Sstevel@tonic-gate 		 * can be done from BANK 0 (8250A/16450 compatable mode)
5847c478bd9Sstevel@tonic-gate 		 * we will default to BANK 0.
5857c478bd9Sstevel@tonic-gate 		 */
5867c478bd9Sstevel@tonic-gate 		asy->asy_hwtype = ASY82510;
5877c478bd9Sstevel@tonic-gate 		OUTB(DAT+7, 0x04); /* clear status */
5887c478bd9Sstevel@tonic-gate 		OUTB(ISR, 0x40); /* set to bank 2 */
5897c478bd9Sstevel@tonic-gate 		OUTB(MCR, 0x08); /* IMD */
5907c478bd9Sstevel@tonic-gate 		OUTB(DAT, 0x21); /* FMD */
5917c478bd9Sstevel@tonic-gate 		OUTB(ISR, 0x00); /* set to bank 0 */
5927c478bd9Sstevel@tonic-gate 		asy->asy_trig_level = 0;
5937c478bd9Sstevel@tonic-gate 	} else { /* Set the UART in FIFO mode if it has FIFO buffers */
5947c478bd9Sstevel@tonic-gate 		asy->asy_hwtype = ASY16550AF;
5957c478bd9Sstevel@tonic-gate 		OUTB(FIFOR, 0x00); /* clear fifo register */
5967c478bd9Sstevel@tonic-gate 		asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 		/* set/Enable FIFO */
5997c478bd9Sstevel@tonic-gate 		OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH |
6007c478bd9Sstevel@tonic-gate 		    (asy->asy_trig_level & 0xff));
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 		if ((INB(ISR) & 0xc0) == 0xc0)
6030280efdcSzk 			asy->asy_use_fifo = FIFO_ON;
6047c478bd9Sstevel@tonic-gate 		else {
6057c478bd9Sstevel@tonic-gate 			asy->asy_hwtype = ASY8250;
6067c478bd9Sstevel@tonic-gate 			OUTB(FIFOR, 0x00); /* NO FIFOs */
6077c478bd9Sstevel@tonic-gate 			asy->asy_trig_level = 0;
6087c478bd9Sstevel@tonic-gate 		}
6097c478bd9Sstevel@tonic-gate 	}
6107c478bd9Sstevel@tonic-gate 
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);
6237c478bd9Sstevel@tonic-gate 	OUTB(LCR, DLAB); /* select baud rate generator */
6247c478bd9Sstevel@tonic-gate 	/* Set the baud rate to 9600 */
6257c478bd9Sstevel@tonic-gate 	OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff);
6267c478bd9Sstevel@tonic-gate 	OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff);
6277c478bd9Sstevel@tonic-gate 	OUTB(LCR, STOP1|BITS8);
6287c478bd9Sstevel@tonic-gate 	OUTB(MCR, (DTR | RTS| OUT2));
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	/*
6317c478bd9Sstevel@tonic-gate 	 * Set up the other components of the asycom structure for this port.
6327c478bd9Sstevel@tonic-gate 	 */
6337c478bd9Sstevel@tonic-gate 	asy->asy_excl = (kmutex_t *)
6347c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6357c478bd9Sstevel@tonic-gate 	asy->asy_excl_hi = (kmutex_t *)
6367c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6377c478bd9Sstevel@tonic-gate 	asy->asy_soft_lock = (kmutex_t *)
6387c478bd9Sstevel@tonic-gate 	    kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6397c478bd9Sstevel@tonic-gate 	asy->asy_unit = instance;
6407c478bd9Sstevel@tonic-gate 	asy->asy_dip = devi;
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) {
6437c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE,
6447c478bd9Sstevel@tonic-gate 		    "Get iblock_cookie failed-Device interrupt%x\n", instance);
6457c478bd9Sstevel@tonic-gate 		goto error;
6467c478bd9Sstevel@tonic-gate 	}
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH,
6497c478bd9Sstevel@tonic-gate 	    &asy->asy_soft_iblock) != DDI_SUCCESS) {
6507c478bd9Sstevel@tonic-gate 		cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n",
6517c478bd9Sstevel@tonic-gate 		    instance);
6527c478bd9Sstevel@tonic-gate 		goto error;
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER,
6567c478bd9Sstevel@tonic-gate 	    (void *)asy->asy_soft_iblock);
6577c478bd9Sstevel@tonic-gate 	mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
6587c478bd9Sstevel@tonic-gate 	mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER,
6597c478bd9Sstevel@tonic-gate 	    (void *)asy->asy_iblock);
6607c478bd9Sstevel@tonic-gate 	state = MUTEXES;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	/*
6637c478bd9Sstevel@tonic-gate 	 * Install interrupt handlers for this device.
6647c478bd9Sstevel@tonic-gate 	 */
6657c478bd9Sstevel@tonic-gate 	if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr,
6667c478bd9Sstevel@tonic-gate 	    (caddr_t)asy) != DDI_SUCCESS) {
6677c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT,
6687c478bd9Sstevel@tonic-gate 		    "Cannot set device interrupt for su driver\n");
6697c478bd9Sstevel@tonic-gate 		goto error;
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 	state = ADDINTR;
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate 	if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id),
6747c478bd9Sstevel@tonic-gate 	    &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy)
6757c478bd9Sstevel@tonic-gate 	    != DDI_SUCCESS) {
6767c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n");
6777c478bd9Sstevel@tonic-gate 		goto error;
6787c478bd9Sstevel@tonic-gate 	}
6797c478bd9Sstevel@tonic-gate 	state = SOFTINTR;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 	/* initialize the asyncline structure */
6827c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) {
6837c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance);
6847c478bd9Sstevel@tonic-gate 		goto error;
6857c478bd9Sstevel@tonic-gate 	}
6867c478bd9Sstevel@tonic-gate 	state = ASYINIT;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
6917c478bd9Sstevel@tonic-gate 	async->async_common = asy;
6927c478bd9Sstevel@tonic-gate 	cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL);
6937c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	if ((asy->sukstat = kstat_create("su", instance, "serialstat",
6967c478bd9Sstevel@tonic-gate 	    "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) {
6977c478bd9Sstevel@tonic-gate 		asy->sukstat->ks_data = &asy->kstats;
6987c478bd9Sstevel@tonic-gate 		kstat_named_init(&asy->kstats.ringover, "ring buffer overflow",
6997c478bd9Sstevel@tonic-gate 		    KSTAT_DATA_UINT64);
7007c478bd9Sstevel@tonic-gate 		kstat_named_init(&asy->kstats.siloover, "silo overflow",
7017c478bd9Sstevel@tonic-gate 		    KSTAT_DATA_UINT64);
7027c478bd9Sstevel@tonic-gate 		kstat_install(asy->sukstat);
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 	state = KSTAT;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	if (strcmp(ddi_node_name(devi), "rsc-console") == 0) {
7077c478bd9Sstevel@tonic-gate 		/*
7087c478bd9Sstevel@tonic-gate 		 * If the device is configured as the 'rsc-console'
7097c478bd9Sstevel@tonic-gate 		 * create the minor device for this node.
7107c478bd9Sstevel@tonic-gate 		 */
7117c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, "ssp", S_IFCHR,
712574493ffSToomas Soome 		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
7137c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
7147c478bd9Sstevel@tonic-gate 			    "%s%d: Failed to create node rsc-console",
7157c478bd9Sstevel@tonic-gate 			    ddi_get_name(devi), ddi_get_instance(devi));
7167c478bd9Sstevel@tonic-gate 			goto error;
7177c478bd9Sstevel@tonic-gate 		}
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		asy->asy_lom_console = 0;
7207c478bd9Sstevel@tonic-gate 		asy->asy_rsc_console = 1;
7217c478bd9Sstevel@tonic-gate 		asy->asy_rsc_control = 0;
7227c478bd9Sstevel@tonic-gate 		asy->asy_device_type = ASY_SERIAL;
7237c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_IGNORE_CD;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	} else if (strcmp(ddi_node_name(devi), "lom-console") == 0) {
7267c478bd9Sstevel@tonic-gate 		/*
7277c478bd9Sstevel@tonic-gate 		 * If the device is configured as the 'lom-console'
7287c478bd9Sstevel@tonic-gate 		 * create the minor device for this node.
7297c478bd9Sstevel@tonic-gate 		 * Do not create a dialout device.
7307c478bd9Sstevel@tonic-gate 		 * Use the same minor numbers as would be used for standard
7317c478bd9Sstevel@tonic-gate 		 * serial instances.
7327c478bd9Sstevel@tonic-gate 		 */
7337c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, "lom-console", S_IFCHR,
734574493ffSToomas Soome 		    instance, DDI_NT_SERIAL_LOMCON, 0) == DDI_FAILURE) {
7357c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
7367c478bd9Sstevel@tonic-gate 			    "%s%d: Failed to create node lom-console",
7377c478bd9Sstevel@tonic-gate 			    ddi_get_name(devi), ddi_get_instance(devi));
7387c478bd9Sstevel@tonic-gate 			goto error;
7397c478bd9Sstevel@tonic-gate 		}
7407c478bd9Sstevel@tonic-gate 		asy->asy_lom_console = 1;
7417c478bd9Sstevel@tonic-gate 		asy->asy_rsc_console = 0;
7427c478bd9Sstevel@tonic-gate 		asy->asy_rsc_control = 0;
7437c478bd9Sstevel@tonic-gate 		asy->asy_device_type = ASY_SERIAL;
7447c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_IGNORE_CD;
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate 	} else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) {
7477c478bd9Sstevel@tonic-gate 		/*
7487c478bd9Sstevel@tonic-gate 		 * If the device is configured as the 'rsc-control'
7497c478bd9Sstevel@tonic-gate 		 * create the minor device for this node.
7507c478bd9Sstevel@tonic-gate 		 */
7517c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, "sspctl", S_IFCHR,
752574493ffSToomas Soome 		    asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
7537c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s%d: Failed to create rsc-control",
7547c478bd9Sstevel@tonic-gate 			    ddi_get_name(devi), ddi_get_instance(devi));
7557c478bd9Sstevel@tonic-gate 			goto error;
7567c478bd9Sstevel@tonic-gate 		}
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		asy->asy_lom_console = 0;
7597c478bd9Sstevel@tonic-gate 		asy->asy_rsc_console = 0;
7607c478bd9Sstevel@tonic-gate 		asy->asy_rsc_control = 1;
7617c478bd9Sstevel@tonic-gate 		asy->asy_device_type = ASY_SERIAL;
7627c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_IGNORE_CD;
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
7650280efdcSzk 	    "keyboard", 0)) {
7667c478bd9Sstevel@tonic-gate 		/*
7677c478bd9Sstevel@tonic-gate 		 * If the device is a keyboard, then create an internal
7687c478bd9Sstevel@tonic-gate 		 * pathname so that the dacf code will link the node into
7697c478bd9Sstevel@tonic-gate 		 * the keyboard console stream.  See dacf.conf.
7707c478bd9Sstevel@tonic-gate 		 */
7717c478bd9Sstevel@tonic-gate 		if (ddi_create_internal_pathname(devi, "keyboard",
7727c478bd9Sstevel@tonic-gate 		    S_IFCHR, instance) == DDI_FAILURE) {
7737c478bd9Sstevel@tonic-gate 			goto error;
7747c478bd9Sstevel@tonic-gate 		}
7757c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
776574493ffSToomas Soome 		asy->asy_device_type = ASY_KEYBOARD;	/* Device type */
7777c478bd9Sstevel@tonic-gate 	} else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
7780280efdcSzk 	    "mouse", 0)) {
7797c478bd9Sstevel@tonic-gate 		/*
7807c478bd9Sstevel@tonic-gate 		 * If the device is a mouse, then create an internal
7817c478bd9Sstevel@tonic-gate 		 * pathname so that the dacf code will link the node into
7827c478bd9Sstevel@tonic-gate 		 * the mouse stream.  See dacf.conf.
7837c478bd9Sstevel@tonic-gate 		 */
7847c478bd9Sstevel@tonic-gate 		if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR,
7857c478bd9Sstevel@tonic-gate 		    instance) == DDI_FAILURE) {
7867c478bd9Sstevel@tonic-gate 			goto error;
7877c478bd9Sstevel@tonic-gate 		}
7887c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_IGNORE_CD;	/* ignore cd */
7897c478bd9Sstevel@tonic-gate 		asy->asy_device_type = ASY_MOUSE;
7907c478bd9Sstevel@tonic-gate 	} else {
7917c478bd9Sstevel@tonic-gate 		/*
7927c478bd9Sstevel@tonic-gate 		 * If not used for keyboard/mouse, create minor devices nodes
7937c478bd9Sstevel@tonic-gate 		 * for this device
7947c478bd9Sstevel@tonic-gate 		 */
7957c478bd9Sstevel@tonic-gate 		/* serial-port */
7967c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%c", (instance+'a'));
7977c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
798574493ffSToomas Soome 		    DDI_NT_SERIAL_MB, 0) == DDI_FAILURE) {
7997c478bd9Sstevel@tonic-gate 			goto error;
8007c478bd9Sstevel@tonic-gate 		}
8017c478bd9Sstevel@tonic-gate 		state = MINORNODE;
8027c478bd9Sstevel@tonic-gate 		/* serial-port:dailout */
8037c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%c,cu", (instance+'a'));
8047c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE,
805574493ffSToomas Soome 		    DDI_NT_SERIAL_MB_DO, 0) == DDI_FAILURE) {
8067c478bd9Sstevel@tonic-gate 			goto error;
8077c478bd9Sstevel@tonic-gate 		}
8087c478bd9Sstevel@tonic-gate 		/* Property for ignoring DCD */
8097c478bd9Sstevel@tonic-gate 		if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
8107c478bd9Sstevel@tonic-gate 		    "ignore-cd", 0)) {
8117c478bd9Sstevel@tonic-gate 			asy->asy_flags |= ASY_IGNORE_CD;  /* ignore cd */
8127c478bd9Sstevel@tonic-gate 		} else {
8137c478bd9Sstevel@tonic-gate 			asy->asy_flags &= ~ASY_IGNORE_CD;
8147c478bd9Sstevel@tonic-gate 			/*
8157c478bd9Sstevel@tonic-gate 			 * if ignore-cd is not available it could be
8167c478bd9Sstevel@tonic-gate 			 * some old legacy platform, try to see
8177c478bd9Sstevel@tonic-gate 			 * whether the old legacy property exists
8187c478bd9Sstevel@tonic-gate 			 */
8197c478bd9Sstevel@tonic-gate 			(void) sprintf(name,
8207c478bd9Sstevel@tonic-gate 			    "port-%c-ignore-cd", (instance+ 'a'));
8217c478bd9Sstevel@tonic-gate 			if (ddi_getprop(DDI_DEV_T_ANY, devi,
8227c478bd9Sstevel@tonic-gate 			    DDI_PROP_DONTPASS, name, 0))
8237c478bd9Sstevel@tonic-gate 				asy->asy_flags |= ASY_IGNORE_CD;
8247c478bd9Sstevel@tonic-gate 		}
8257c478bd9Sstevel@tonic-gate 		asy->asy_device_type = ASY_SERIAL;
8267c478bd9Sstevel@tonic-gate 	}
8270280efdcSzk 
8280280efdcSzk 	/*
8290280efdcSzk 	 * Fill in the polled I/O structure
8300280efdcSzk 	 */
8310280efdcSzk 	asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
8320280efdcSzk 	asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
8330280efdcSzk 	asy->polledio.cons_polledio_putchar =  asyputchar;
8340280efdcSzk 	asy->polledio.cons_polledio_getchar = asygetchar;
8350280efdcSzk 	asy->polledio.cons_polledio_ischar = asyischar;
8360280efdcSzk 	asy->polledio.cons_polledio_enter = asy_polled_enter;
8370280efdcSzk 	asy->polledio.cons_polledio_exit = asy_polled_exit;
8380280efdcSzk 
8390280efdcSzk 	/* Initialize saved ICR and polled_enter */
8400280efdcSzk 	asy->polled_icr = 0;
8410280efdcSzk 	asy->polled_enter = B_FALSE;
8420280efdcSzk 
8437c478bd9Sstevel@tonic-gate 	ddi_report_dev(devi);
8447c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate error:
8477c478bd9Sstevel@tonic-gate 	if (state == MINORNODE) {
8487c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%c", (instance+'a'));
8497c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(devi, name);
8507c478bd9Sstevel@tonic-gate 	}
8517c478bd9Sstevel@tonic-gate 	if (state >= KSTAT)
8527c478bd9Sstevel@tonic-gate 		kstat_delete(asy->sukstat);
8537c478bd9Sstevel@tonic-gate 	if (state >= ASYINIT) {
8547c478bd9Sstevel@tonic-gate 		cv_destroy(&async->async_flags_cv);
8557c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(su_asyncline, instance);
8567c478bd9Sstevel@tonic-gate 	}
8577c478bd9Sstevel@tonic-gate 	if (state >= SOFTINTR)
8587c478bd9Sstevel@tonic-gate 		ddi_remove_softintr(asy->asy_softintr_id);
8597c478bd9Sstevel@tonic-gate 	if (state >= ADDINTR)
8607c478bd9Sstevel@tonic-gate 		ddi_remove_intr(devi, 0, asy->asy_iblock);
8617c478bd9Sstevel@tonic-gate 	if (state >= MUTEXES) {
8627c478bd9Sstevel@tonic-gate 		mutex_destroy(asy->asy_excl_hi);
8637c478bd9Sstevel@tonic-gate 		mutex_destroy(asy->asy_excl);
8647c478bd9Sstevel@tonic-gate 		mutex_destroy(asy->asy_soft_lock);
8657c478bd9Sstevel@tonic-gate 		kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
8667c478bd9Sstevel@tonic-gate 		kmem_free(asy->asy_excl, sizeof (kmutex_t));
8677c478bd9Sstevel@tonic-gate 		kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
8687c478bd9Sstevel@tonic-gate 	}
8697c478bd9Sstevel@tonic-gate 	if (state >= REGSMAP)
8707c478bd9Sstevel@tonic-gate 		ddi_regs_map_free(&asy->asy_handle);
8717c478bd9Sstevel@tonic-gate 	if (state >= SOFTSTATE)
8727c478bd9Sstevel@tonic-gate 		ddi_soft_state_free(su_asycom, instance);
8737c478bd9Sstevel@tonic-gate 	/* no action for EMPTY state */
8747c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
8757c478bd9Sstevel@tonic-gate }
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate static int
asyinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8787c478bd9Sstevel@tonic-gate asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
8794cad604cSMarcel Telka     void **result)
8807c478bd9Sstevel@tonic-gate {
8817c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(dip))
882*e476cc14SToomas Soome 	dev_t dev = (dev_t)arg;
883*e476cc14SToomas Soome 	int instance, error;
8847c478bd9Sstevel@tonic-gate 	struct asycom *asy;
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	if ((instance = UNIT(dev)) > max_asy_instance)
8877c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 	switch (infocmd) {
8900280efdcSzk 		case DDI_INFO_DEVT2DEVINFO:
8910280efdcSzk 			asy = (struct asycom *)ddi_get_soft_state(su_asycom,
8920280efdcSzk 			    instance);
8930280efdcSzk 			if (asy->asy_dip == NULL)
8940280efdcSzk 				error = DDI_FAILURE;
8950280efdcSzk 			else {
8960280efdcSzk 				*result = (void *) asy->asy_dip;
8970280efdcSzk 				error = DDI_SUCCESS;
8980280efdcSzk 			}
8990280efdcSzk 			break;
9000280efdcSzk 		case DDI_INFO_DEVT2INSTANCE:
9010280efdcSzk 			*result = (void *)(uintptr_t)instance;
9027c478bd9Sstevel@tonic-gate 			error = DDI_SUCCESS;
9030280efdcSzk 			break;
9040280efdcSzk 		default:
9050280efdcSzk 			error = DDI_FAILURE;
9067c478bd9Sstevel@tonic-gate 	}
9077c478bd9Sstevel@tonic-gate 	return (error);
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate 
9107c478bd9Sstevel@tonic-gate static int
asyopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)9117c478bd9Sstevel@tonic-gate asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
9127c478bd9Sstevel@tonic-gate {
9137c478bd9Sstevel@tonic-gate 	_NOTE(ARGUNUSED(sflag))
9147c478bd9Sstevel@tonic-gate 	struct asycom	*asy;
9157c478bd9Sstevel@tonic-gate 	struct asyncline *async;
9167c478bd9Sstevel@tonic-gate 	int		mcr;
9177c478bd9Sstevel@tonic-gate 	int		unit;
918574493ffSToomas Soome 	int		len;
919574493ffSToomas Soome 	struct termios	*termiosp;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate #ifdef DEBUG
9227c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_CLOSE)
9237c478bd9Sstevel@tonic-gate 		printf("open\n");
9247c478bd9Sstevel@tonic-gate #endif
9257c478bd9Sstevel@tonic-gate 	unit = UNIT(*dev);
9267c478bd9Sstevel@tonic-gate 	if (unit > max_asy_instance)
9277c478bd9Sstevel@tonic-gate 		return (ENXIO);		/* unit not configured */
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit);
9307c478bd9Sstevel@tonic-gate 	if (async == NULL)
9317c478bd9Sstevel@tonic-gate 		return (ENXIO);
9327c478bd9Sstevel@tonic-gate 
9337c478bd9Sstevel@tonic-gate 	asy = async->async_common;
9347c478bd9Sstevel@tonic-gate 	if (asy == NULL)
9357c478bd9Sstevel@tonic-gate 		return (ENXIO);		/* device not found by autoconfig */
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
9387c478bd9Sstevel@tonic-gate 	asy->asy_priv = (caddr_t)async;
9397c478bd9Sstevel@tonic-gate 
9407c478bd9Sstevel@tonic-gate again:
9417c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
9427c478bd9Sstevel@tonic-gate 	/*
9437c478bd9Sstevel@tonic-gate 	 * Block waiting for carrier to come up, unless this is a no-delay open.
9447c478bd9Sstevel@tonic-gate 	 */
9457c478bd9Sstevel@tonic-gate 	if (!(async->async_flags & ASYNC_ISOPEN)) {
9467c478bd9Sstevel@tonic-gate 		/*
9477c478bd9Sstevel@tonic-gate 		 * If this port is for a RSC console or control
9487c478bd9Sstevel@tonic-gate 		 * use the following termio info
9497c478bd9Sstevel@tonic-gate 		 */
9507c478bd9Sstevel@tonic-gate 		if (asy->asy_rsc_console || asy->asy_rsc_control) {
9517c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT |
9527c478bd9Sstevel@tonic-gate 			    (B115200 & CBAUD);
9537c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT)
9547c478bd9Sstevel@tonic-gate 			    & CIBAUD);
9557c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
9567c478bd9Sstevel@tonic-gate 		} else if (asy->asy_lom_console) {
9577c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag = B9600 & CBAUD;
9587c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT)
9597c478bd9Sstevel@tonic-gate 			    & CIBAUD);
9607c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
9617c478bd9Sstevel@tonic-gate 		} else {
9627c478bd9Sstevel@tonic-gate 
9637c478bd9Sstevel@tonic-gate 			/*
9647c478bd9Sstevel@tonic-gate 			 * Set the default termios settings (cflag).
9657c478bd9Sstevel@tonic-gate 			 * Others are set in ldterm.  Release the spin
9667c478bd9Sstevel@tonic-gate 			 * mutex as we can block here, reaquire before
9677c478bd9Sstevel@tonic-gate 			 * calling asy_program.
9687c478bd9Sstevel@tonic-gate 			 */
9697c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
9707c478bd9Sstevel@tonic-gate 			if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
9717c478bd9Sstevel@tonic-gate 			    0, "ttymodes", (caddr_t)&termiosp, &len)
9727c478bd9Sstevel@tonic-gate 			    == DDI_PROP_SUCCESS &&
9737c478bd9Sstevel@tonic-gate 			    len == sizeof (struct termios)) {
9747c478bd9Sstevel@tonic-gate 				async->async_ttycommon.t_cflag =
9757c478bd9Sstevel@tonic-gate 				    termiosp->c_cflag;
9767c478bd9Sstevel@tonic-gate 				kmem_free(termiosp, len);
9777c478bd9Sstevel@tonic-gate 			} else {
9787c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN,
9797c478bd9Sstevel@tonic-gate 					"su: couldn't get ttymodes property!");
9807c478bd9Sstevel@tonic-gate 			}
9817c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
9827c478bd9Sstevel@tonic-gate 		}
9837c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_iflag = 0;
9847c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_iocpending = NULL;
9857c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_size.ws_row = 0;
9867c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_size.ws_col = 0;
9877c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_size.ws_xpixel = 0;
9887c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_size.ws_ypixel = 0;
9897c478bd9Sstevel@tonic-gate 		async->async_dev = *dev;
9907c478bd9Sstevel@tonic-gate 		async->async_wbufcid = 0;
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		async->async_startc = CSTART;
9937c478bd9Sstevel@tonic-gate 		async->async_stopc = CSTOP;
9947c478bd9Sstevel@tonic-gate 		(void) asy_program(asy, ASY_INIT);
9957c478bd9Sstevel@tonic-gate 	} else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
9960280efdcSzk 	    secpolicy_excl_open(cr) != 0) {
9977c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
9987c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
9997c478bd9Sstevel@tonic-gate 		return (EBUSY);
10007c478bd9Sstevel@tonic-gate 	} else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
10017c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
10027c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
10037c478bd9Sstevel@tonic-gate 		return (EBUSY);
10047c478bd9Sstevel@tonic-gate 	}
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	if (*dev & OUTLINE)
10077c478bd9Sstevel@tonic-gate 		async->async_flags |= ASYNC_OUT;
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	/* Raise DTR on every open */
10107c478bd9Sstevel@tonic-gate 	mcr = INB(MCR);
10117c478bd9Sstevel@tonic-gate 	OUTB(MCR, mcr|DTR);
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	/*
10147c478bd9Sstevel@tonic-gate 	 * Check carrier.
10157c478bd9Sstevel@tonic-gate 	 */
10167c478bd9Sstevel@tonic-gate 	if (asy->asy_flags & ASY_IGNORE_CD)
10177c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_flags |= TS_SOFTCAR;
10187c478bd9Sstevel@tonic-gate 	if ((async->async_ttycommon.t_flags & TS_SOFTCAR) ||
10190280efdcSzk 	    (INB(MSR) & DCD))
10207c478bd9Sstevel@tonic-gate 		async->async_flags |= ASYNC_CARR_ON;
10217c478bd9Sstevel@tonic-gate 	else
10227c478bd9Sstevel@tonic-gate 		async->async_flags &= ~ASYNC_CARR_ON;
10237c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate 	/*
10267c478bd9Sstevel@tonic-gate 	 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
10277c478bd9Sstevel@tonic-gate 	 * Quit on interrupt.
10287c478bd9Sstevel@tonic-gate 	 */
10297c478bd9Sstevel@tonic-gate 	if (!(flag & (FNDELAY|FNONBLOCK)) &&
10300280efdcSzk 	    !(async->async_ttycommon.t_cflag & CLOCAL)) {
10317c478bd9Sstevel@tonic-gate 		if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) ||
10320280efdcSzk 		    ((async->async_flags & ASYNC_OUT) &&
10330280efdcSzk 		    !(*dev & OUTLINE))) {
10340280efdcSzk 				async->async_flags |= ASYNC_WOPEN;
10350280efdcSzk 				if (cv_wait_sig(&async->async_flags_cv,
10360280efdcSzk 				    asy->asy_excl) == 0) {
10370280efdcSzk 					async->async_flags &= ~ASYNC_WOPEN;
10380280efdcSzk 					mutex_exit(asy->asy_excl);
10390280efdcSzk 					return (EINTR);
10400280efdcSzk 				}
10417c478bd9Sstevel@tonic-gate 				async->async_flags &= ~ASYNC_WOPEN;
10420280efdcSzk 				goto again;
10437c478bd9Sstevel@tonic-gate 		}
10447c478bd9Sstevel@tonic-gate 	} else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
10450280efdcSzk 		mutex_exit(asy->asy_excl);
10460280efdcSzk 		return (EBUSY);
10477c478bd9Sstevel@tonic-gate 	}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 	if (asy->suspended) {
10507c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
10517c478bd9Sstevel@tonic-gate 		(void) ddi_dev_is_needed(asy->asy_dip, 0, 1);
10527c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
10537c478bd9Sstevel@tonic-gate 	}
10547c478bd9Sstevel@tonic-gate 
10557c478bd9Sstevel@tonic-gate 	async->async_ttycommon.t_readq = rq;
10567c478bd9Sstevel@tonic-gate 	async->async_ttycommon.t_writeq = WR(rq);
10577c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
10587c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
10597c478bd9Sstevel@tonic-gate 	qprocson(rq);
10607c478bd9Sstevel@tonic-gate 	async->async_flags |= ASYNC_ISOPEN;
10617c478bd9Sstevel@tonic-gate 	async->async_polltid = 0;
10627c478bd9Sstevel@tonic-gate 	return (0);
10637c478bd9Sstevel@tonic-gate }
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate static void
async_progress_check(void * arg)10667c478bd9Sstevel@tonic-gate async_progress_check(void *arg)
10677c478bd9Sstevel@tonic-gate {
10687c478bd9Sstevel@tonic-gate 	struct asyncline *async = arg;
10697c478bd9Sstevel@tonic-gate 	struct asycom	 *asy = async->async_common;
10707c478bd9Sstevel@tonic-gate 	mblk_t *bp;
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/*
10737c478bd9Sstevel@tonic-gate 	 * We define "progress" as either waiting on a timed break or delay, or
10747c478bd9Sstevel@tonic-gate 	 * having had at least one transmitter interrupt.  If none of these are
10757c478bd9Sstevel@tonic-gate 	 * true, then just terminate the output and wake up that close thread.
10767c478bd9Sstevel@tonic-gate 	 */
10777c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
10787c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
10797c478bd9Sstevel@tonic-gate 	if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
10807c478bd9Sstevel@tonic-gate 		async->async_ocnt = 0;
10817c478bd9Sstevel@tonic-gate 		async->async_flags &= ~ASYNC_BUSY;
10827c478bd9Sstevel@tonic-gate 		async->async_timer = 0;
10837c478bd9Sstevel@tonic-gate 		bp = async->async_xmitblk;
10847c478bd9Sstevel@tonic-gate 		async->async_xmitblk = NULL;
10857c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
10867c478bd9Sstevel@tonic-gate 		if (bp != NULL)
10877c478bd9Sstevel@tonic-gate 			freeb(bp);
10887c478bd9Sstevel@tonic-gate 		/*
10897c478bd9Sstevel@tonic-gate 		 * Since this timer is running, we know that we're in exit(2).
10907c478bd9Sstevel@tonic-gate 		 * That means that the user can't possibly be waiting on any
10917c478bd9Sstevel@tonic-gate 		 * valid ioctl(2) completion anymore, and we should just flush
10927c478bd9Sstevel@tonic-gate 		 * everything.
10937c478bd9Sstevel@tonic-gate 		 */
10947c478bd9Sstevel@tonic-gate 		flushq(async->async_ttycommon.t_writeq, FLUSHALL);
10957c478bd9Sstevel@tonic-gate 		cv_broadcast(&async->async_flags_cv);
10967c478bd9Sstevel@tonic-gate 	} else {
10977c478bd9Sstevel@tonic-gate 		async->async_flags &= ~ASYNC_PROGRESS;
10987c478bd9Sstevel@tonic-gate 		async->async_timer = timeout(async_progress_check, async,
10997c478bd9Sstevel@tonic-gate 		    drv_usectohz(su_drain_check));
11007c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
11017c478bd9Sstevel@tonic-gate 	}
11027c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
11037c478bd9Sstevel@tonic-gate }
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate /*
11067c478bd9Sstevel@tonic-gate  * Close routine.
11077c478bd9Sstevel@tonic-gate  */
11087c478bd9Sstevel@tonic-gate static int
asyclose(queue_t * q,int flag,cred_t * cr __unused)1109730a650aSPeter Tribble asyclose(queue_t *q, int flag, cred_t *cr __unused)
11107c478bd9Sstevel@tonic-gate {
11117c478bd9Sstevel@tonic-gate 	struct asyncline *async;
11127c478bd9Sstevel@tonic-gate 	struct asycom	 *asy;
11137c478bd9Sstevel@tonic-gate 	int icr, lcr;
11147c478bd9Sstevel@tonic-gate 	int		nohupcl;
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate #ifdef DEBUG
11187c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_CLOSE)
11197c478bd9Sstevel@tonic-gate 		printf("close\n");
11207c478bd9Sstevel@tonic-gate #endif
11217c478bd9Sstevel@tonic-gate 	async = q->q_ptr;
11227c478bd9Sstevel@tonic-gate 	ASSERT(async != NULL);
11237c478bd9Sstevel@tonic-gate 	asy = async->async_common;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	/* get the nohupcl OBP property of this device */
11267c478bd9Sstevel@tonic-gate 	nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS,
11277c478bd9Sstevel@tonic-gate 	    "nohupcl", 0);
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
11307c478bd9Sstevel@tonic-gate 	async->async_flags |= ASYNC_CLOSING;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	/*
11337c478bd9Sstevel@tonic-gate 	 * Turn off PPS handling early to avoid events occuring during
11347c478bd9Sstevel@tonic-gate 	 * close.  Also reset the DCD edge monitoring bit.
11357c478bd9Sstevel@tonic-gate 	 */
11367c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
11377c478bd9Sstevel@tonic-gate 	asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
11387c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
11397c478bd9Sstevel@tonic-gate 
11407c478bd9Sstevel@tonic-gate 	/*
11417c478bd9Sstevel@tonic-gate 	 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
11427c478bd9Sstevel@tonic-gate 	 * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
11437c478bd9Sstevel@tonic-gate 	 * write queue and there's a timer running, so we don't have to worry
11447c478bd9Sstevel@tonic-gate 	 * about them.  For the untimed case, though, the user obviously made a
11457c478bd9Sstevel@tonic-gate 	 * mistake, because these are handled immediately.  We'll terminate the
114648bbca81SDaniel Hoffman 	 * break now and honor their implicit request by discarding the rest of
11477c478bd9Sstevel@tonic-gate 	 * the data.
11487c478bd9Sstevel@tonic-gate 	 */
11497c478bd9Sstevel@tonic-gate 	if (!(async->async_flags & ASYNC_BREAK)) {
11507c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
11517c478bd9Sstevel@tonic-gate 		lcr = INB(LCR);
11527c478bd9Sstevel@tonic-gate 		if (lcr & SETBREAK) {
11537c478bd9Sstevel@tonic-gate 			OUTB(LCR, (lcr & ~SETBREAK));
11547c478bd9Sstevel@tonic-gate 		}
11557c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
11567c478bd9Sstevel@tonic-gate 		if (lcr & SETBREAK)
11577c478bd9Sstevel@tonic-gate 			goto nodrain;
11587c478bd9Sstevel@tonic-gate 	}
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	/*
11617c478bd9Sstevel@tonic-gate 	 * If the user told us not to delay the close ("non-blocking"), then
11627c478bd9Sstevel@tonic-gate 	 * don't bother trying to drain.
11637c478bd9Sstevel@tonic-gate 	 *
11647c478bd9Sstevel@tonic-gate 	 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
11657c478bd9Sstevel@tonic-gate 	 * getting an M_START (since these messages aren't enqueued), and the
11667c478bd9Sstevel@tonic-gate 	 * only other way to clear the stop condition is by loss of DCD, which
11677c478bd9Sstevel@tonic-gate 	 * would discard the queue data.  Thus, we drop the output data if
11687c478bd9Sstevel@tonic-gate 	 * ASYNC_STOPPED is set.
11697c478bd9Sstevel@tonic-gate 	 */
11707c478bd9Sstevel@tonic-gate 	if ((flag & (FNDELAY|FNONBLOCK)) ||
11717c478bd9Sstevel@tonic-gate 	    (async->async_flags & ASYNC_STOPPED)) {
11727c478bd9Sstevel@tonic-gate 		goto nodrain;
11737c478bd9Sstevel@tonic-gate 	}
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 	/*
11767c478bd9Sstevel@tonic-gate 	 * If there's any pending output, then we have to try to drain it.
11777c478bd9Sstevel@tonic-gate 	 * There are two main cases to be handled:
11787c478bd9Sstevel@tonic-gate 	 *	- called by close(2): need to drain until done or until
11797c478bd9Sstevel@tonic-gate 	 *	  a signal is received.  No timeout.
11807c478bd9Sstevel@tonic-gate 	 *	- called by exit(2): need to drain while making progress
11817c478bd9Sstevel@tonic-gate 	 *	  or until a timeout occurs.  No signals.
11827c478bd9Sstevel@tonic-gate 	 *
11837c478bd9Sstevel@tonic-gate 	 * If we can't rely on receiving a signal to get us out of a hung
11847c478bd9Sstevel@tonic-gate 	 * session, then we have to use a timer.  In this case, we set a timer
11857c478bd9Sstevel@tonic-gate 	 * to check for progress in sending the output data -- all that we ask
11867c478bd9Sstevel@tonic-gate 	 * (at each interval) is that there's been some progress made.  Since
11877c478bd9Sstevel@tonic-gate 	 * the interrupt routine grabs buffers from the write queue, we can't
11887c478bd9Sstevel@tonic-gate 	 * trust async_ocnt.  Instead, we use a flag.
11897c478bd9Sstevel@tonic-gate 	 *
11907c478bd9Sstevel@tonic-gate 	 * Note that loss of carrier will cause the output queue to be flushed,
11917c478bd9Sstevel@tonic-gate 	 * and we'll wake up again and finish normally.
11927c478bd9Sstevel@tonic-gate 	 */
11937c478bd9Sstevel@tonic-gate 	if (!ddi_can_receive_sig() && su_drain_check != 0) {
11947c478bd9Sstevel@tonic-gate 		async->async_flags &= ~ASYNC_PROGRESS;
11957c478bd9Sstevel@tonic-gate 		async->async_timer = timeout(async_progress_check, async,
11967c478bd9Sstevel@tonic-gate 		    drv_usectohz(su_drain_check));
11977c478bd9Sstevel@tonic-gate 	}
11987c478bd9Sstevel@tonic-gate 
11997c478bd9Sstevel@tonic-gate 	while (async->async_ocnt > 0 ||
12007c478bd9Sstevel@tonic-gate 	    async->async_ttycommon.t_writeq->q_first != NULL ||
12017c478bd9Sstevel@tonic-gate 	    (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
12027c478bd9Sstevel@tonic-gate 		if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0)
12037c478bd9Sstevel@tonic-gate 			break;
12047c478bd9Sstevel@tonic-gate 	}
12057c478bd9Sstevel@tonic-gate 	if (async->async_timer != 0) {
12067c478bd9Sstevel@tonic-gate 		(void) untimeout(async->async_timer);
12077c478bd9Sstevel@tonic-gate 		async->async_timer = 0;
12087c478bd9Sstevel@tonic-gate 	}
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate nodrain:
12117c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
12127c478bd9Sstevel@tonic-gate 
12137c478bd9Sstevel@tonic-gate 	/* turn off the loopback mode */
12147c478bd9Sstevel@tonic-gate 	if ((async->async_dev != rconsdev) &&
12157c478bd9Sstevel@tonic-gate 	    (async->async_dev != kbddev) &&
12167c478bd9Sstevel@tonic-gate 	    (async->async_dev != stdindev)) {
12177c478bd9Sstevel@tonic-gate 		OUTB(MCR, INB(MCR) & ~ ASY_LOOP);
12187c478bd9Sstevel@tonic-gate 	}
12197c478bd9Sstevel@tonic-gate 
12207c478bd9Sstevel@tonic-gate 	async->async_ocnt = 0;
12217c478bd9Sstevel@tonic-gate 	if (async->async_xmitblk != NULL)
12227c478bd9Sstevel@tonic-gate 		freeb(async->async_xmitblk);
12237c478bd9Sstevel@tonic-gate 	async->async_xmitblk = NULL;
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	/*
12267c478bd9Sstevel@tonic-gate 	 * If the "nohupcl" OBP property is set for this device, do
12277c478bd9Sstevel@tonic-gate 	 * not turn off DTR and RTS no matter what.  Otherwise, if the
12287c478bd9Sstevel@tonic-gate 	 * line has HUPCL set or is incompletely opened, turn off DTR
12297c478bd9Sstevel@tonic-gate 	 * and RTS to fix the modem line.
12307c478bd9Sstevel@tonic-gate 	 */
12317c478bd9Sstevel@tonic-gate 	if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) ||
12327c478bd9Sstevel@tonic-gate 	    (async->async_flags & ASYNC_WOPEN))) {
12337c478bd9Sstevel@tonic-gate 		/* turn off DTR, RTS but NOT interrupt to 386 */
12347c478bd9Sstevel@tonic-gate 		OUTB(MCR, OUT2);
12357c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
12367c478bd9Sstevel@tonic-gate 		/*
12377c478bd9Sstevel@tonic-gate 		 * Don't let an interrupt in the middle of close
12387c478bd9Sstevel@tonic-gate 		 * bounce us back to the top; just continue closing
12397c478bd9Sstevel@tonic-gate 		 * as if nothing had happened.
12407c478bd9Sstevel@tonic-gate 		 */
12417c478bd9Sstevel@tonic-gate 		if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0)
12427c478bd9Sstevel@tonic-gate 			goto out;
12437c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
12447c478bd9Sstevel@tonic-gate 	}
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	/*
12477c478bd9Sstevel@tonic-gate 	 * If nobody's using it now, turn off receiver interrupts.
12487c478bd9Sstevel@tonic-gate 	 */
12497c478bd9Sstevel@tonic-gate 	if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
12507c478bd9Sstevel@tonic-gate 		icr = INB(ICR);
12517c478bd9Sstevel@tonic-gate 		OUTB(ICR, (icr & ~RIEN));
12527c478bd9Sstevel@tonic-gate 	}
12537c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
12547c478bd9Sstevel@tonic-gate out:
12557c478bd9Sstevel@tonic-gate 	/*
12567c478bd9Sstevel@tonic-gate 	 * Clear out device state.
12577c478bd9Sstevel@tonic-gate 	 */
12587c478bd9Sstevel@tonic-gate 	async->async_flags = 0;
12597c478bd9Sstevel@tonic-gate 	ttycommon_close(&async->async_ttycommon);
12607c478bd9Sstevel@tonic-gate 	cv_broadcast(&async->async_flags_cv);
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate 	/*
12637c478bd9Sstevel@tonic-gate 	 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in
12647c478bd9Sstevel@tonic-gate 	 * async_softint or an interrupt was pending when the process
12657c478bd9Sstevel@tonic-gate 	 * using the port exited.
12667c478bd9Sstevel@tonic-gate 	 */
12677c478bd9Sstevel@tonic-gate 	asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	/*
12707c478bd9Sstevel@tonic-gate 	 * Cancel outstanding "bufcall" request.
12717c478bd9Sstevel@tonic-gate 	 */
12727c478bd9Sstevel@tonic-gate 	if (async->async_wbufcid) {
12737c478bd9Sstevel@tonic-gate 		unbufcall(async->async_wbufcid);
12747c478bd9Sstevel@tonic-gate 		async->async_wbufcid = 0;
12757c478bd9Sstevel@tonic-gate 	}
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	/*
12787c478bd9Sstevel@tonic-gate 	 * If inperim is true, it means the port is closing while there's
12797c478bd9Sstevel@tonic-gate 	 * a pending software interrupt.  async_flags has been zeroed out,
12807c478bd9Sstevel@tonic-gate 	 * so this instance of leaveq() needs to be called before we call
12817c478bd9Sstevel@tonic-gate 	 * qprocsoff() to disable services on the q.  If inperim is false,
12827c478bd9Sstevel@tonic-gate 	 * leaveq() has already been called or we're not in a perimeter.
12837c478bd9Sstevel@tonic-gate 	 */
12847c478bd9Sstevel@tonic-gate 	if (asy->inperim == B_TRUE) {
12857c478bd9Sstevel@tonic-gate 		asy->inperim = B_FALSE;
12867c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
12877c478bd9Sstevel@tonic-gate 		leaveq(q);
12887c478bd9Sstevel@tonic-gate 	} else {
12897c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
12907c478bd9Sstevel@tonic-gate 	}
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate 	/* Note that qprocsoff can't be done until after interrupts are off */
12937c478bd9Sstevel@tonic-gate 	qprocsoff(q);
12947c478bd9Sstevel@tonic-gate 	q->q_ptr = WR(q)->q_ptr = NULL;
12957c478bd9Sstevel@tonic-gate 	async->async_ttycommon.t_readq = NULL;
12967c478bd9Sstevel@tonic-gate 	async->async_ttycommon.t_writeq = NULL;
12977c478bd9Sstevel@tonic-gate 
12987c478bd9Sstevel@tonic-gate 	return (0);
12997c478bd9Sstevel@tonic-gate }
13007c478bd9Sstevel@tonic-gate 
13017c478bd9Sstevel@tonic-gate /*
13027c478bd9Sstevel@tonic-gate  * Checks to see if the serial port is still transmitting
13037c478bd9Sstevel@tonic-gate  * characters.  It returns true when there are characters
13047c478bd9Sstevel@tonic-gate  * queued to transmit,  when the holding register contains
13057c478bd9Sstevel@tonic-gate  * a byte, or when the shifting register still contains
13067c478bd9Sstevel@tonic-gate  * data to send.
13077c478bd9Sstevel@tonic-gate  *
13087c478bd9Sstevel@tonic-gate  */
13097c478bd9Sstevel@tonic-gate static boolean_t
asy_isbusy(struct asycom * asy)13107c478bd9Sstevel@tonic-gate asy_isbusy(struct asycom *asy)
13117c478bd9Sstevel@tonic-gate {
13127c478bd9Sstevel@tonic-gate 	struct asyncline *async;
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate #ifdef DEBUG
13157c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_EOT)
13167c478bd9Sstevel@tonic-gate 		printf("isbusy\n");
13177c478bd9Sstevel@tonic-gate #endif
13187c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)asy->asy_priv;
13197c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl));
13207c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
13217c478bd9Sstevel@tonic-gate 	return ((async->async_ocnt > 0) ||
13220280efdcSzk 	    ((INB(LSR) & XSRE) == 0));
13237c478bd9Sstevel@tonic-gate }
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate /*
13267c478bd9Sstevel@tonic-gate  * Program the ASY port. Most of the async operation is based on the values
13277c478bd9Sstevel@tonic-gate  * of 'c_iflag' and 'c_cflag'.
13287c478bd9Sstevel@tonic-gate  */
13297c478bd9Sstevel@tonic-gate static int
asy_program(struct asycom * asy,int mode)13307c478bd9Sstevel@tonic-gate asy_program(struct asycom *asy, int mode)
13317c478bd9Sstevel@tonic-gate {
13327c478bd9Sstevel@tonic-gate 	struct asyncline *async;
13337c478bd9Sstevel@tonic-gate 	int baudrate, c_flag;
13347c478bd9Sstevel@tonic-gate 	int icr, lcr;
13357c478bd9Sstevel@tonic-gate 	int ocflags;
13367c478bd9Sstevel@tonic-gate 	int error = 0;
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl));
13397c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate #ifdef DEBUG
13427c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
13437c478bd9Sstevel@tonic-gate 		printf("program\n");
13447c478bd9Sstevel@tonic-gate #endif
13457c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)asy->asy_priv;
13467c478bd9Sstevel@tonic-gate 
13477c478bd9Sstevel@tonic-gate 	baudrate = async->async_ttycommon.t_cflag & CBAUD;
13487c478bd9Sstevel@tonic-gate 	if (async->async_ttycommon.t_cflag & CBAUDEXT)
13497c478bd9Sstevel@tonic-gate 		baudrate += 16;
13507c478bd9Sstevel@tonic-gate 
13517c478bd9Sstevel@tonic-gate 	/* Limit baudrate so it can't index out of baudtable */
13527c478bd9Sstevel@tonic-gate 	if (baudrate >= N_SU_SPEEDS) baudrate = B9600;
13537c478bd9Sstevel@tonic-gate 
13547c478bd9Sstevel@tonic-gate 	/*
13557c478bd9Sstevel@tonic-gate 	 * If baud rate requested is greater than the speed cap
13567c478bd9Sstevel@tonic-gate 	 * or is an unsupported baud rate then reset t_cflag baud
13577c478bd9Sstevel@tonic-gate 	 * to the last valid baud rate.  If this is the initial
13587c478bd9Sstevel@tonic-gate 	 * pass through asy_program then set it to 9600.
13597c478bd9Sstevel@tonic-gate 	 */
13607c478bd9Sstevel@tonic-gate 	if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) ||
13617c478bd9Sstevel@tonic-gate 	    (baudtable[baudrate] > asy->asy_speed_cap)) {
13627c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
13637c478bd9Sstevel@tonic-gate 		    ~CIBAUD & ~CIBAUDEXT;
13647c478bd9Sstevel@tonic-gate 		if (mode == ASY_INIT) {
13657c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |= B9600;
1366b4a51ac1Skc 			async->async_ttycommon.t_cflag |= B9600 << IBSHIFT;
13677c478bd9Sstevel@tonic-gate 			baudrate = B9600;
13687c478bd9Sstevel@tonic-gate 		} else {
13697c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |=
13707c478bd9Sstevel@tonic-gate 			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
13717c478bd9Sstevel@tonic-gate 			    CIBAUD | CIBAUDEXT));
1372b4a51ac1Skc 			error = EINVAL;
1373b4a51ac1Skc 			goto end;
13747c478bd9Sstevel@tonic-gate 		}
13757c478bd9Sstevel@tonic-gate 	}
13767c478bd9Sstevel@tonic-gate 
1377b4a51ac1Skc 	/*
1378b4a51ac1Skc 	 * If CIBAUD and CIBAUDEXT are zero then we should set them to
1379b4a51ac1Skc 	 * the equivelant output baud bits.  Else, if CIBAUD and CIBAUDEXT
1380b4a51ac1Skc 	 * don't match CBAUD and CBAUDEXT respectively then we should
1381b4a51ac1Skc 	 * notify the requestor that we do not support split speeds.
1382b4a51ac1Skc 	 */
1383b4a51ac1Skc 	if ((async->async_ttycommon.t_cflag  & (CIBAUD|CIBAUDEXT)) == 0) {
1384b4a51ac1Skc 		async->async_ttycommon.t_cflag |=
1385b4a51ac1Skc 		    (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT;
1386b4a51ac1Skc 		if (async->async_ttycommon.t_cflag & CBAUDEXT)
13870280efdcSzk 			async->async_ttycommon.t_cflag |= CIBAUDEXT;
1388b4a51ac1Skc 	} else {
1389b4a51ac1Skc 		if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) !=
1390b4a51ac1Skc 		    (async->async_ttycommon.t_cflag & CIBAUD)) ||
1391b4a51ac1Skc 		    !(((async->async_ttycommon.t_cflag & (CBAUDEXT |
1392b4a51ac1Skc 		    CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) ||
1393b4a51ac1Skc 		    ((async->async_ttycommon.t_cflag & (CBAUDEXT |
1394b4a51ac1Skc 		    CIBAUDEXT)) == 0))) {
1395b4a51ac1Skc 			async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1396b4a51ac1Skc 			    ~CIBAUD & ~CIBAUDEXT;
13977c478bd9Sstevel@tonic-gate 			async->async_ttycommon.t_cflag |=
1398b4a51ac1Skc 			    (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1399b4a51ac1Skc 			    CIBAUD | CIBAUDEXT));
1400b4a51ac1Skc 			error = EINVAL;
1401b4a51ac1Skc 			goto end;
14027c478bd9Sstevel@tonic-gate 		}
14037c478bd9Sstevel@tonic-gate 	}
14047c478bd9Sstevel@tonic-gate 
14057c478bd9Sstevel@tonic-gate 	c_flag = async->async_ttycommon.t_cflag &
14067c478bd9Sstevel@tonic-gate 	    (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD |
14077c478bd9Sstevel@tonic-gate 	    CBAUDEXT | CIBAUD | CIBAUDEXT);
140817ea09c7Sanovick 
140917ea09c7Sanovick 	/* disable interrupts, see EXAR bug */
1410f63f7506Sanovick 	if (asy->asy_hwtype == ASY16C554D)
1411f63f7506Sanovick 		OUTB(SPR, 0);
141217ea09c7Sanovick 	OUTB(ICR, 0);
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	ocflags = asy->asy_ocflags;
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	/* flush/reset the status registers */
14177c478bd9Sstevel@tonic-gate 	if (mode == ASY_INIT) {
14187c478bd9Sstevel@tonic-gate 		(void) INB(DAT);
14197c478bd9Sstevel@tonic-gate 		(void) INB(ISR);
14207c478bd9Sstevel@tonic-gate 		(void) INB(LSR);
14217c478bd9Sstevel@tonic-gate 		(void) INB(MSR);
14227c478bd9Sstevel@tonic-gate 	}
14237c478bd9Sstevel@tonic-gate 
14247c478bd9Sstevel@tonic-gate 	if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
14257c478bd9Sstevel@tonic-gate 		/* Set line control */
14267c478bd9Sstevel@tonic-gate 		lcr = INB(LCR);
14277c478bd9Sstevel@tonic-gate 		lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 		if (c_flag & CSTOPB)
14307c478bd9Sstevel@tonic-gate 			lcr |= STB;	/* 2 stop bits */
14317c478bd9Sstevel@tonic-gate 
14327c478bd9Sstevel@tonic-gate 		if (c_flag & PARENB)
14337c478bd9Sstevel@tonic-gate 			lcr |= PEN;
14347c478bd9Sstevel@tonic-gate 
14357c478bd9Sstevel@tonic-gate 		if ((c_flag & PARODD) == 0)
14367c478bd9Sstevel@tonic-gate 			lcr |= EPS;
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate 		switch (c_flag & CSIZE) {
14397c478bd9Sstevel@tonic-gate 		case CS5:
14407c478bd9Sstevel@tonic-gate 			lcr |= BITS5;
14417c478bd9Sstevel@tonic-gate 			break;
14427c478bd9Sstevel@tonic-gate 		case CS6:
14437c478bd9Sstevel@tonic-gate 			lcr |= BITS6;
14447c478bd9Sstevel@tonic-gate 			break;
14457c478bd9Sstevel@tonic-gate 		case CS7:
14467c478bd9Sstevel@tonic-gate 			lcr |= BITS7;
14477c478bd9Sstevel@tonic-gate 			break;
14487c478bd9Sstevel@tonic-gate 		case CS8:
14497c478bd9Sstevel@tonic-gate 			lcr |= BITS8;
14507c478bd9Sstevel@tonic-gate 			break;
14517c478bd9Sstevel@tonic-gate 		}
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 		/* set the baud rate when the rate is NOT B0 */
14547c478bd9Sstevel@tonic-gate 		if (baudrate != 0) {
14557c478bd9Sstevel@tonic-gate 			OUTB(LCR, DLAB);
14567c478bd9Sstevel@tonic-gate 			OUTB(DAT, (asyspdtab[baudrate] *
14570280efdcSzk 			    asy->asy_baud_divisor_factor) & 0xff);
14587c478bd9Sstevel@tonic-gate 			OUTB(ICR, ((asyspdtab[baudrate] *
14590280efdcSzk 			    asy->asy_baud_divisor_factor) >> 8) & 0xff);
14607c478bd9Sstevel@tonic-gate 		}
14617c478bd9Sstevel@tonic-gate 		/* set the line control modes */
14627c478bd9Sstevel@tonic-gate 		OUTB(LCR, lcr);
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 		/*
14657c478bd9Sstevel@tonic-gate 		 * if transitioning from CREAD off to CREAD on,
14667c478bd9Sstevel@tonic-gate 		 * flush the FIFO buffer if we have one.
14677c478bd9Sstevel@tonic-gate 		 */
14687c478bd9Sstevel@tonic-gate 		if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) {
14697c478bd9Sstevel@tonic-gate 			if (asy->asy_use_fifo == FIFO_ON) {
14707c478bd9Sstevel@tonic-gate 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
14717c478bd9Sstevel@tonic-gate 				    (asy->asy_trig_level & 0xff));
14727c478bd9Sstevel@tonic-gate 			}
14737c478bd9Sstevel@tonic-gate 		}
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 		/* remember the new cflags */
14767c478bd9Sstevel@tonic-gate 		asy->asy_ocflags = c_flag & ~CLOCAL;
14777c478bd9Sstevel@tonic-gate 	}
14787c478bd9Sstevel@tonic-gate 
14797c478bd9Sstevel@tonic-gate 	/* whether or not CLOCAL is set, modify the modem control lines */
14807c478bd9Sstevel@tonic-gate 	if (baudrate == 0)
14817c478bd9Sstevel@tonic-gate 		/* B0 has been issued, lower DTR */
14827c478bd9Sstevel@tonic-gate 		OUTB(MCR, RTS|OUT2);
14837c478bd9Sstevel@tonic-gate 	else
14847c478bd9Sstevel@tonic-gate 		/* raise DTR */
14857c478bd9Sstevel@tonic-gate 		OUTB(MCR, DTR|RTS|OUT2);
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	/*
14887c478bd9Sstevel@tonic-gate 	 * Call the modem status interrupt handler to check for the carrier
14897c478bd9Sstevel@tonic-gate 	 * in case CLOCAL was turned off after the carrier came on.
14907c478bd9Sstevel@tonic-gate 	 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
14917c478bd9Sstevel@tonic-gate 	 */
14927c478bd9Sstevel@tonic-gate 	async_msint(asy);
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 	/* Set interrupt control */
14957c478bd9Sstevel@tonic-gate 	if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
14967c478bd9Sstevel@tonic-gate 		/*
14977c478bd9Sstevel@tonic-gate 		 * direct-wired line ignores DCD, so we don't enable modem
14987c478bd9Sstevel@tonic-gate 		 * status interrupts.
14997c478bd9Sstevel@tonic-gate 		 */
15007c478bd9Sstevel@tonic-gate 		icr = (TIEN | SIEN);
15017c478bd9Sstevel@tonic-gate 	else
15027c478bd9Sstevel@tonic-gate 		icr = (TIEN | SIEN | MIEN);
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 	if (c_flag & CREAD)
15057c478bd9Sstevel@tonic-gate 		icr |= RIEN;
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	OUTB(ICR, icr);
15087c478bd9Sstevel@tonic-gate end:
15097c478bd9Sstevel@tonic-gate 	return (error);
15107c478bd9Sstevel@tonic-gate }
15117c478bd9Sstevel@tonic-gate 
15120280efdcSzk /*
15130280efdcSzk  * Polled mode support -- all functions called with interrupts
15140280efdcSzk  * disabled.
15150280efdcSzk  */
15160280efdcSzk 
15170280efdcSzk static void
asyputchar(cons_polledio_arg_t arg,uchar_t c)15180280efdcSzk asyputchar(cons_polledio_arg_t arg, uchar_t c)
15190280efdcSzk {
15200280efdcSzk 	struct asycom *asy = (struct asycom *)arg;
15210280efdcSzk 
15220280efdcSzk 	/*
15230280efdcSzk 	 * If we see a line feed make sure to also
15240280efdcSzk 	 * put out a carriage return.
15250280efdcSzk 	 */
15260280efdcSzk 	if (c == '\n')
15270280efdcSzk 		asyputchar(arg, '\r');
15280280efdcSzk 
15290280efdcSzk 	while ((INB(LSR) & XHRE) == 0) {
15300280efdcSzk 		/* wait for the transmission to complete */
15310280efdcSzk 		drv_usecwait(10);
15320280efdcSzk 	}
15330280efdcSzk 
15340280efdcSzk 	/* ouput the character */
15350280efdcSzk 	OUTB(DAT, c);
15360280efdcSzk }
15370280efdcSzk 
15380280efdcSzk /*
15390280efdcSzk  * Determines if there is a character avaialable for
15400280efdcSzk  * reading.
15410280efdcSzk  */
15420280efdcSzk static boolean_t
asyischar(cons_polledio_arg_t arg)15430280efdcSzk asyischar(cons_polledio_arg_t arg)
15440280efdcSzk {
15450280efdcSzk 	struct asycom *asy = (struct asycom *)arg;
15460280efdcSzk 	return ((INB(LSR) & RCA) != 0);
15470280efdcSzk }
15480280efdcSzk 
15490280efdcSzk static int
asygetchar(cons_polledio_arg_t arg)15500280efdcSzk asygetchar(cons_polledio_arg_t arg)
15510280efdcSzk {
15520280efdcSzk 	struct asycom *asy = (struct asycom *)arg;
15530280efdcSzk 
15540280efdcSzk 	/*
15550280efdcSzk 	 * Spin waiting for a character to be
15560280efdcSzk 	 * available to read.
15570280efdcSzk 	 */
15580280efdcSzk 	while (!asyischar(arg))
15590280efdcSzk 		drv_usecwait(10);
15600280efdcSzk 
15610280efdcSzk 	return (INB(DAT));
15620280efdcSzk }
15630280efdcSzk 
15640280efdcSzk /*
15650280efdcSzk  * Called when machine is transitioning to polled mode
15660280efdcSzk  */
15670280efdcSzk static void
asy_polled_enter(cons_polledio_arg_t arg)15680280efdcSzk asy_polled_enter(cons_polledio_arg_t arg)
15690280efdcSzk {
15700280efdcSzk 	struct asycom *asy = (struct asycom *)arg;
15710280efdcSzk 
15720280efdcSzk 	mutex_enter(asy->asy_excl);
15730280efdcSzk 	mutex_enter(asy->asy_excl_hi);
15740280efdcSzk 
15750280efdcSzk 	/*
15760280efdcSzk 	 * If this is the first time that asy_polled_enter()
15770280efdcSzk 	 * has been called, during this transition request,
15780280efdcSzk 	 * save the ICR. Clear the software interrupt
15790280efdcSzk 	 * flags since we won't be able to handle these when
15800280efdcSzk 	 * we are in polled mode.
15810280efdcSzk 	 */
15820280efdcSzk 	if (!asy->polled_enter) {
15830280efdcSzk 		asy->polled_enter = B_TRUE;
15840280efdcSzk 		asy->polled_icr = INB(ICR);
15850280efdcSzk 
15860280efdcSzk 		/* Disable HW interrupts */
15870280efdcSzk 		if (asy->asy_hwtype == ASY16C554D)
15880280efdcSzk 			OUTB(SPR, 0);
15890280efdcSzk 		OUTB(ICR, 0);
15900280efdcSzk 
15910280efdcSzk 		asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
15920280efdcSzk 	}
15930280efdcSzk 	mutex_exit(asy->asy_excl_hi);
15940280efdcSzk 	mutex_exit(asy->asy_excl);
15950280efdcSzk }
15960280efdcSzk 
15970280efdcSzk /*
15980280efdcSzk  * Called when machine is transitioning from polled mode.
15990280efdcSzk  */
16000280efdcSzk static void
asy_polled_exit(cons_polledio_arg_t arg)16010280efdcSzk asy_polled_exit(cons_polledio_arg_t arg)
16020280efdcSzk {
16030280efdcSzk 	struct asycom *asy = (struct asycom *)arg;
16040280efdcSzk 
16050280efdcSzk 	mutex_enter(asy->asy_excl);
16060280efdcSzk 	mutex_enter(asy->asy_excl_hi);
16070280efdcSzk 
16080280efdcSzk 	/* Restore the ICR */
16090280efdcSzk 	OUTB(ICR, asy->polled_icr);
16100280efdcSzk 
16110280efdcSzk 	/*
16120280efdcSzk 	 * We have finished this polled IO transition.
16130280efdcSzk 	 * Set polled_enter to B_FALSE to note this.
16140280efdcSzk 	 */
16150280efdcSzk 	asy->polled_enter = B_FALSE;
16160280efdcSzk 	mutex_exit(asy->asy_excl_hi);
16170280efdcSzk 	mutex_exit(asy->asy_excl);
16180280efdcSzk }
16190280efdcSzk 
16207c478bd9Sstevel@tonic-gate /*
16217c478bd9Sstevel@tonic-gate  * asyintr() is the High Level Interrupt Handler.
16227c478bd9Sstevel@tonic-gate  *
16237c478bd9Sstevel@tonic-gate  * There are four different interrupt types indexed by ISR register values:
16247c478bd9Sstevel@tonic-gate  *		0: modem
16257c478bd9Sstevel@tonic-gate  *		1: Tx holding register is empty, ready for next char
16267c478bd9Sstevel@tonic-gate  *		2: Rx register now holds a char to be picked up
16277c478bd9Sstevel@tonic-gate  *		3: error or break on line
16287c478bd9Sstevel@tonic-gate  * This routine checks the Bit 0 (interrupt-not-pending) to determine if
16297c478bd9Sstevel@tonic-gate  * the interrupt is from this port.
16307c478bd9Sstevel@tonic-gate  */
16317c478bd9Sstevel@tonic-gate uint_t
asyintr(caddr_t argasy)16327c478bd9Sstevel@tonic-gate asyintr(caddr_t argasy)
16337c478bd9Sstevel@tonic-gate {
16347c478bd9Sstevel@tonic-gate 	struct asycom		*asy = (struct asycom *)argasy;
16357c478bd9Sstevel@tonic-gate 	struct asyncline	*async;
16367c478bd9Sstevel@tonic-gate 	int			ret_status = DDI_INTR_UNCLAIMED;
16377c478bd9Sstevel@tonic-gate 	uchar_t			interrupt_id, lsr;
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 	interrupt_id = INB(ISR) & 0x0F;
16407c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)asy->asy_priv;
16417c478bd9Sstevel@tonic-gate 	if ((async == NULL) ||
16420280efdcSzk 	    !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
16437c478bd9Sstevel@tonic-gate 		if (interrupt_id & NOINTERRUPT)  {
16447c478bd9Sstevel@tonic-gate 			return (DDI_INTR_UNCLAIMED);
16457c478bd9Sstevel@tonic-gate 		} else {
16467c478bd9Sstevel@tonic-gate 			lsr = INB(LSR);
16477c478bd9Sstevel@tonic-gate 			if ((lsr & BRKDET) &&
16487c478bd9Sstevel@tonic-gate 			    ((abort_enable == KIOCABORTENABLE) &&
16497c478bd9Sstevel@tonic-gate 			    (async->async_dev == rconsdev)))
16507c478bd9Sstevel@tonic-gate 				abort_sequence_enter((char *)NULL);
16517c478bd9Sstevel@tonic-gate 			else {
16527c478bd9Sstevel@tonic-gate 				/* reset line status */
16537c478bd9Sstevel@tonic-gate 				(void) INB(LSR);
16547c478bd9Sstevel@tonic-gate 				/* discard any data */
16557c478bd9Sstevel@tonic-gate 				(void) INB(DAT);
16567c478bd9Sstevel@tonic-gate 				/* reset modem status */
16577c478bd9Sstevel@tonic-gate 				(void) INB(MSR);
16587c478bd9Sstevel@tonic-gate 				return (DDI_INTR_CLAIMED);
16597c478bd9Sstevel@tonic-gate 			}
16607c478bd9Sstevel@tonic-gate 		}
16617c478bd9Sstevel@tonic-gate 	}
16627c478bd9Sstevel@tonic-gate 	/*
16637c478bd9Sstevel@tonic-gate 	 * Spurious interrupts happen in this driver
16647c478bd9Sstevel@tonic-gate 	 * because of the transmission on serial port not handled
16657c478bd9Sstevel@tonic-gate 	 * properly.
16667c478bd9Sstevel@tonic-gate 	 *
16677c478bd9Sstevel@tonic-gate 	 * The reasons for Spurious interrupts are:
16687c478bd9Sstevel@tonic-gate 	 *    1. There is a path in async_nstart which transmits
16697c478bd9Sstevel@tonic-gate 	 *	 characters without going through interrupt services routine
16707c478bd9Sstevel@tonic-gate 	 *	 which causes spurious interrupts to happen.
16717c478bd9Sstevel@tonic-gate 	 *    2. In the async_txint more than one character is sent
16727c478bd9Sstevel@tonic-gate 	 *	 in one interrupt service.
16737c478bd9Sstevel@tonic-gate 	 *    3. In async_rxint more than one characters are received in
16747c478bd9Sstevel@tonic-gate 	 *	 in one interrupt service.
16757c478bd9Sstevel@tonic-gate 	 *
16767c478bd9Sstevel@tonic-gate 	 * Hence we have flags to indicate that such scenerio has happened.
16777c478bd9Sstevel@tonic-gate 	 * and claim only such interrupts and others we donot claim it
16787c478bd9Sstevel@tonic-gate 	 * as it could be a indicator of some hardware problem.
16797c478bd9Sstevel@tonic-gate 	 *
16807c478bd9Sstevel@tonic-gate 	 */
16817c478bd9Sstevel@tonic-gate 	if (interrupt_id & NOINTERRUPT) {
16827c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
16837c478bd9Sstevel@tonic-gate 		if ((asy->asy_xmit_count > 1) ||
16840280efdcSzk 		    (asy->asy_out_of_band_xmit > 0) ||
16850280efdcSzk 		    (asy->asy_rx_count > 1)) {
16867c478bd9Sstevel@tonic-gate 			asy->asy_xmit_count = 0;
16877c478bd9Sstevel@tonic-gate 			asy->asy_out_of_band_xmit = 0;
16887c478bd9Sstevel@tonic-gate 			asy->asy_rx_count = 0;
16897c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
16907c478bd9Sstevel@tonic-gate 			return (DDI_INTR_CLAIMED);
16917c478bd9Sstevel@tonic-gate 		} else {
16927c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
16937c478bd9Sstevel@tonic-gate 			return (DDI_INTR_UNCLAIMED);
16947c478bd9Sstevel@tonic-gate 		}
16957c478bd9Sstevel@tonic-gate 	}
16967c478bd9Sstevel@tonic-gate 	ret_status = DDI_INTR_CLAIMED;
16977c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
16987c478bd9Sstevel@tonic-gate 	if (asy->asy_hwtype == ASY82510)
16997c478bd9Sstevel@tonic-gate 		OUTB(ISR, 0x00); /* set bank 0 */
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate #ifdef DEBUG
17027c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_INTR)
17037c478bd9Sstevel@tonic-gate 		prom_printf("l");
17047c478bd9Sstevel@tonic-gate #endif
17057c478bd9Sstevel@tonic-gate 	lsr = INB(LSR);
17067c478bd9Sstevel@tonic-gate 	switch (interrupt_id) {
17077c478bd9Sstevel@tonic-gate 	case RxRDY:
17087c478bd9Sstevel@tonic-gate 	case RSTATUS:
17097c478bd9Sstevel@tonic-gate 	case FFTMOUT:
17107c478bd9Sstevel@tonic-gate 		/* receiver interrupt or receiver errors */
17117c478bd9Sstevel@tonic-gate 		async_rxint(asy, lsr);
17127c478bd9Sstevel@tonic-gate 		break;
17137c478bd9Sstevel@tonic-gate 	case TxRDY:
17147c478bd9Sstevel@tonic-gate 		/* transmit interrupt */
17157c478bd9Sstevel@tonic-gate 		async_txint(asy, lsr);
17167c478bd9Sstevel@tonic-gate 		break;
17177c478bd9Sstevel@tonic-gate 	case MSTATUS:
17187c478bd9Sstevel@tonic-gate 		/* modem status interrupt */
17197c478bd9Sstevel@tonic-gate 		async_msint(asy);
17207c478bd9Sstevel@tonic-gate 		break;
17217c478bd9Sstevel@tonic-gate 	}
17227c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
17237c478bd9Sstevel@tonic-gate 	return (ret_status);
17247c478bd9Sstevel@tonic-gate }
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate /*
17277c478bd9Sstevel@tonic-gate  * Transmitter interrupt service routine.
17287c478bd9Sstevel@tonic-gate  * If there is more data to transmit in the current pseudo-DMA block,
17297c478bd9Sstevel@tonic-gate  * send the next character if output is not stopped or draining.
17307c478bd9Sstevel@tonic-gate  * Otherwise, queue up a soft interrupt.
17317c478bd9Sstevel@tonic-gate  *
17327c478bd9Sstevel@tonic-gate  * XXX -  Needs review for HW FIFOs.
17337c478bd9Sstevel@tonic-gate  */
17347c478bd9Sstevel@tonic-gate static void
async_txint(struct asycom * asy,uchar_t lsr)17357c478bd9Sstevel@tonic-gate async_txint(struct asycom *asy, uchar_t lsr)
17367c478bd9Sstevel@tonic-gate {
17377c478bd9Sstevel@tonic-gate 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
17387c478bd9Sstevel@tonic-gate 	int		fifo_len;
17397c478bd9Sstevel@tonic-gate 	int		xmit_progress;
17407c478bd9Sstevel@tonic-gate 
17417c478bd9Sstevel@tonic-gate 	asycheckflowcontrol_hw(asy);
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 	/*
17447c478bd9Sstevel@tonic-gate 	 * If ASYNC_BREAK has been set, return to asyintr()'s context to
17457c478bd9Sstevel@tonic-gate 	 * claim the interrupt without performing any action.
17467c478bd9Sstevel@tonic-gate 	 */
17477c478bd9Sstevel@tonic-gate 	if (async->async_flags & ASYNC_BREAK)
17487c478bd9Sstevel@tonic-gate 		return;
17497c478bd9Sstevel@tonic-gate 
17507c478bd9Sstevel@tonic-gate 	fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 	/*
17537c478bd9Sstevel@tonic-gate 	 * Check for flow control and do the needed action.
17547c478bd9Sstevel@tonic-gate 	 */
17557c478bd9Sstevel@tonic-gate 	if (asycheckflowcontrol_sw(asy)) {
17567c478bd9Sstevel@tonic-gate 		return;
17577c478bd9Sstevel@tonic-gate 	}
17587c478bd9Sstevel@tonic-gate 
17597c478bd9Sstevel@tonic-gate 	if (async->async_ocnt > 0 &&
17607c478bd9Sstevel@tonic-gate 	    !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) {
17617c478bd9Sstevel@tonic-gate 		xmit_progress = 0;
17627c478bd9Sstevel@tonic-gate 		while (fifo_len > 0 && async->async_ocnt > 0) {
17637c478bd9Sstevel@tonic-gate 			if (lsr & XHRE) {
17647c478bd9Sstevel@tonic-gate 				OUTB(DAT, *async->async_optr++);
17657c478bd9Sstevel@tonic-gate 				fifo_len--;
17667c478bd9Sstevel@tonic-gate 				async->async_ocnt--;
17677c478bd9Sstevel@tonic-gate 				xmit_progress++;
17687c478bd9Sstevel@tonic-gate 			}
17697c478bd9Sstevel@tonic-gate 			/*
17707c478bd9Sstevel@tonic-gate 			 * Reading the lsr, (moved reading at the end of
17717c478bd9Sstevel@tonic-gate 			 * while loop) as already we have read once at
17727c478bd9Sstevel@tonic-gate 			 * the beginning of interrupt service
17737c478bd9Sstevel@tonic-gate 			 */
17747c478bd9Sstevel@tonic-gate 			lsr = INB(LSR);
17757c478bd9Sstevel@tonic-gate 		}
17767c478bd9Sstevel@tonic-gate 		asy->asy_xmit_count = xmit_progress;
17777c478bd9Sstevel@tonic-gate 		if (xmit_progress > 0)
17787c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_PROGRESS;
17797c478bd9Sstevel@tonic-gate 	}
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate 	if (fifo_len == 0) {
17827c478bd9Sstevel@tonic-gate 		return;
17837c478bd9Sstevel@tonic-gate 	}
17847c478bd9Sstevel@tonic-gate 
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate 	ASYSETSOFT(asy);
17877c478bd9Sstevel@tonic-gate }
17887c478bd9Sstevel@tonic-gate 
17897c478bd9Sstevel@tonic-gate /*
17907c478bd9Sstevel@tonic-gate  * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
17917c478bd9Sstevel@tonic-gate  * error interrupt.
17927c478bd9Sstevel@tonic-gate  * Try to put the character into the circular buffer for this line; if it
17937c478bd9Sstevel@tonic-gate  * overflows, indicate a circular buffer overrun. If this port is always
17947c478bd9Sstevel@tonic-gate  * to be serviced immediately, or the character is a STOP character, or
17957c478bd9Sstevel@tonic-gate  * more than 15 characters have arrived, queue up a soft interrupt to
17967c478bd9Sstevel@tonic-gate  * drain the circular buffer.
17977c478bd9Sstevel@tonic-gate  * XXX - needs review for hw FIFOs support.
17987c478bd9Sstevel@tonic-gate  */
17997c478bd9Sstevel@tonic-gate 
18007c478bd9Sstevel@tonic-gate static void
async_rxint(struct asycom * asy,uchar_t lsr)18017c478bd9Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr)
18027c478bd9Sstevel@tonic-gate {
18037c478bd9Sstevel@tonic-gate 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
18047c478bd9Sstevel@tonic-gate 	uchar_t c = 0;
18057c478bd9Sstevel@tonic-gate 	uint_t s = 0, needsoft = 0;
18067c478bd9Sstevel@tonic-gate 	register tty_common_t *tp;
18077c478bd9Sstevel@tonic-gate 
18087c478bd9Sstevel@tonic-gate 	tp = &async->async_ttycommon;
18097c478bd9Sstevel@tonic-gate 	if (!(tp->t_cflag & CREAD)) {
18107c478bd9Sstevel@tonic-gate 		if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
18117c478bd9Sstevel@tonic-gate 			(void) (INB(DAT) & 0xff);
18127c478bd9Sstevel@tonic-gate 		}
18137c478bd9Sstevel@tonic-gate 		return; /* line is not open for read? */
18147c478bd9Sstevel@tonic-gate 	}
18157c478bd9Sstevel@tonic-gate 	asy->asy_rx_count = 0;
18167c478bd9Sstevel@tonic-gate 	while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
18177c478bd9Sstevel@tonic-gate 		c = 0;
18187c478bd9Sstevel@tonic-gate 		s = 0;
18197c478bd9Sstevel@tonic-gate 		asy->asy_rx_count++;
18207c478bd9Sstevel@tonic-gate 		if (lsr & RCA) {
18217c478bd9Sstevel@tonic-gate 			c = INB(DAT) & 0xff;
18227c478bd9Sstevel@tonic-gate 			/*
18237c478bd9Sstevel@tonic-gate 			 * Even a single character is received
18247c478bd9Sstevel@tonic-gate 			 * we need Soft interrupt to pass it to
18257c478bd9Sstevel@tonic-gate 			 * higher layers.
18267c478bd9Sstevel@tonic-gate 			 */
18277c478bd9Sstevel@tonic-gate 			needsoft = 1;
18287c478bd9Sstevel@tonic-gate 		}
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 		/* Check for character break sequence */
18317c478bd9Sstevel@tonic-gate 		if ((abort_enable == KIOCABORTALTERNATE) &&
18327c478bd9Sstevel@tonic-gate 		    (async->async_dev == rconsdev)) {
18337c478bd9Sstevel@tonic-gate 			if (abort_charseq_recognize(c))
18347c478bd9Sstevel@tonic-gate 				abort_sequence_enter((char *)NULL);
18357c478bd9Sstevel@tonic-gate 			}
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 		/* Handle framing errors */
18387c478bd9Sstevel@tonic-gate 		if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
18397c478bd9Sstevel@tonic-gate 			if (lsr & PARERR) {
18407c478bd9Sstevel@tonic-gate 				if (tp->t_iflag & INPCK) /* parity enabled */
18417c478bd9Sstevel@tonic-gate 					s |= PERROR;
18427c478bd9Sstevel@tonic-gate 			}
18437c478bd9Sstevel@tonic-gate 			if (lsr & (FRMERR|BRKDET))
18447c478bd9Sstevel@tonic-gate 				s |= FRERROR;
18457c478bd9Sstevel@tonic-gate 			if (lsr & OVRRUN) {
18467c478bd9Sstevel@tonic-gate 				async->async_hw_overrun = 1;
18477c478bd9Sstevel@tonic-gate 				s |= OVERRUN;
18487c478bd9Sstevel@tonic-gate 			}
18497c478bd9Sstevel@tonic-gate 		}
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 		if (s == 0)
18527c478bd9Sstevel@tonic-gate 			if ((tp->t_iflag & PARMRK) &&
18530280efdcSzk 			    !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
18540280efdcSzk 			    (c == 0377))
18557c478bd9Sstevel@tonic-gate 				if (RING_POK(async, 2)) {
18567c478bd9Sstevel@tonic-gate 					RING_PUT(async, 0377);
18577c478bd9Sstevel@tonic-gate 					RING_PUT(async, c);
18587c478bd9Sstevel@tonic-gate 				} else
18597c478bd9Sstevel@tonic-gate 					async->async_sw_overrun = 1;
18607c478bd9Sstevel@tonic-gate 			else
18617c478bd9Sstevel@tonic-gate 				if (RING_POK(async, 1))
18627c478bd9Sstevel@tonic-gate 					RING_PUT(async, c);
18637c478bd9Sstevel@tonic-gate 				else
18647c478bd9Sstevel@tonic-gate 					async->async_sw_overrun = 1;
18657c478bd9Sstevel@tonic-gate 		else
18667c478bd9Sstevel@tonic-gate 			if (s & FRERROR) { /* Handle framing errors */
18677c478bd9Sstevel@tonic-gate 				if (c == 0)  {
18687c478bd9Sstevel@tonic-gate 		/* Look for break on kbd, stdin, or rconsdev */
18697c478bd9Sstevel@tonic-gate 					if ((async->async_dev == kbddev) ||
18707c478bd9Sstevel@tonic-gate 					    ((async->async_dev == rconsdev) ||
18717c478bd9Sstevel@tonic-gate 					    (async->async_dev == stdindev)) &&
18727c478bd9Sstevel@tonic-gate 					    (abort_enable !=
18737c478bd9Sstevel@tonic-gate 					    KIOCABORTALTERNATE))
18747c478bd9Sstevel@tonic-gate 						abort_sequence_enter((char *)0);
18757c478bd9Sstevel@tonic-gate 					else
18767c478bd9Sstevel@tonic-gate 						async->async_break++;
18777c478bd9Sstevel@tonic-gate 				} else {
18787c478bd9Sstevel@tonic-gate 					if (RING_POK(async, 1))
18797c478bd9Sstevel@tonic-gate 						RING_MARK(async, c, s);
18807c478bd9Sstevel@tonic-gate 					else
18817c478bd9Sstevel@tonic-gate 						async->async_sw_overrun = 1;
18827c478bd9Sstevel@tonic-gate 				}
18837c478bd9Sstevel@tonic-gate 			} else  { /* Parity errors  handled by ldterm */
18847c478bd9Sstevel@tonic-gate 				if (RING_POK(async, 1))
18857c478bd9Sstevel@tonic-gate 					RING_MARK(async, c, s);
18867c478bd9Sstevel@tonic-gate 				else
18877c478bd9Sstevel@tonic-gate 					async->async_sw_overrun = 1;
18887c478bd9Sstevel@tonic-gate 			}
18897c478bd9Sstevel@tonic-gate 		lsr = INB(LSR);
18907c478bd9Sstevel@tonic-gate 		if (asy->asy_rx_count > 16) break;
18917c478bd9Sstevel@tonic-gate 	}
18927c478bd9Sstevel@tonic-gate 	/* Check whether there is a request for hw/sw inbound/input flow ctrl */
18937c478bd9Sstevel@tonic-gate 	if ((async->async_ttycommon.t_cflag & CRTSXOFF) ||
18940280efdcSzk 	    (async->async_ttycommon.t_iflag & IXOFF))
18957c478bd9Sstevel@tonic-gate 		if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) {
18967c478bd9Sstevel@tonic-gate #ifdef DEBUG
18977c478bd9Sstevel@tonic-gate 			if (asydebug & ASY_DEBUG_HFLOW)
18987c478bd9Sstevel@tonic-gate 				printf("asy%d: hardware flow stop input.\n",
18990280efdcSzk 				    UNIT(async->async_dev));
19007c478bd9Sstevel@tonic-gate #endif
19017c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_HW_IN_FLOW;
19027c478bd9Sstevel@tonic-gate 			async->async_flowc = async->async_stopc;
19037c478bd9Sstevel@tonic-gate 			async->async_ringbuf_overflow = 1;
19047c478bd9Sstevel@tonic-gate 		}
19057c478bd9Sstevel@tonic-gate 
19067c478bd9Sstevel@tonic-gate 	if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
19070280efdcSzk 	    (RING_FRAC(async)) || (async->async_polltid == 0))
19087c478bd9Sstevel@tonic-gate 		ASYSETSOFT(asy);	/* need a soft interrupt */
19097c478bd9Sstevel@tonic-gate }
19107c478bd9Sstevel@tonic-gate 
19117c478bd9Sstevel@tonic-gate /*
19127c478bd9Sstevel@tonic-gate  * Interrupt on port: handle PPS event.  This function is only called
19137c478bd9Sstevel@tonic-gate  * for a port on which PPS event handling has been enabled.
19147c478bd9Sstevel@tonic-gate  */
19157c478bd9Sstevel@tonic-gate static void
asy_ppsevent(struct asycom * asy,int msr)19167c478bd9Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr)
19177c478bd9Sstevel@tonic-gate {
19187c478bd9Sstevel@tonic-gate 	if (asy->asy_flags & ASY_PPS_EDGE) {
19197c478bd9Sstevel@tonic-gate 		/* Have seen leading edge, now look for and record drop */
19207c478bd9Sstevel@tonic-gate 		if ((msr & DCD) == 0)
19217c478bd9Sstevel@tonic-gate 			asy->asy_flags &= ~ASY_PPS_EDGE;
19227c478bd9Sstevel@tonic-gate 		/*
19237c478bd9Sstevel@tonic-gate 		 * Waiting for leading edge, look for rise; stamp event and
19247c478bd9Sstevel@tonic-gate 		 * calibrate kernel clock.
19257c478bd9Sstevel@tonic-gate 		 */
19267c478bd9Sstevel@tonic-gate 	} else if (msr & DCD) {
19277c478bd9Sstevel@tonic-gate 		/*
19287c478bd9Sstevel@tonic-gate 		 * This code captures a timestamp at the designated
19297c478bd9Sstevel@tonic-gate 		 * transition of the PPS signal (DCD asserted).  The
19307c478bd9Sstevel@tonic-gate 		 * code provides a pointer to the timestamp, as well
19317c478bd9Sstevel@tonic-gate 		 * as the hardware counter value at the capture.
19327c478bd9Sstevel@tonic-gate 		 *
19337c478bd9Sstevel@tonic-gate 		 * Note: the kernel has nano based time values while
19347c478bd9Sstevel@tonic-gate 		 * NTP requires micro based, an in-line fast algorithm
19357c478bd9Sstevel@tonic-gate 		 * to convert nsec to usec is used here -- see hrt2ts()
19367c478bd9Sstevel@tonic-gate 		 * in common/os/timers.c for a full description.
19377c478bd9Sstevel@tonic-gate 		 */
19387c478bd9Sstevel@tonic-gate 		struct timeval *tvp = &asy_ppsev.tv;
19397c478bd9Sstevel@tonic-gate 		timestruc_t ts;
19407c478bd9Sstevel@tonic-gate 		long nsec, usec;
19417c478bd9Sstevel@tonic-gate 
19427c478bd9Sstevel@tonic-gate 		asy->asy_flags |= ASY_PPS_EDGE;
19437c478bd9Sstevel@tonic-gate 		gethrestime(&ts);
19447c478bd9Sstevel@tonic-gate 		nsec = ts.tv_nsec;
19457c478bd9Sstevel@tonic-gate 		usec = nsec + (nsec >> 2);
19467c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 1);
19477c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 2);
19487c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 4);
19497c478bd9Sstevel@tonic-gate 		usec = nsec - (usec >> 3);
19507c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 2);
19517c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 3);
19527c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 4);
19537c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 1);
19547c478bd9Sstevel@tonic-gate 		usec = nsec + (usec >> 6);
19557c478bd9Sstevel@tonic-gate 		tvp->tv_usec = usec >> 10;
19567c478bd9Sstevel@tonic-gate 		tvp->tv_sec = ts.tv_sec;
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 		++asy_ppsev.serial;
19597c478bd9Sstevel@tonic-gate 
19607c478bd9Sstevel@tonic-gate 		/*
19617c478bd9Sstevel@tonic-gate 		 * Because the kernel keeps a high-resolution time,
19627c478bd9Sstevel@tonic-gate 		 * pass the current highres timestamp in tvp and zero
19637c478bd9Sstevel@tonic-gate 		 * in usec.
19647c478bd9Sstevel@tonic-gate 		 */
19657c478bd9Sstevel@tonic-gate 		ddi_hardpps(tvp, 0);
19667c478bd9Sstevel@tonic-gate 	}
19677c478bd9Sstevel@tonic-gate }
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate /*
19707c478bd9Sstevel@tonic-gate  * Modem status interrupt.
19717c478bd9Sstevel@tonic-gate  *
19727c478bd9Sstevel@tonic-gate  * (Note: It is assumed that the MSR hasn't been read by asyintr().)
19737c478bd9Sstevel@tonic-gate  */
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate static void
async_msint(struct asycom * asy)19767c478bd9Sstevel@tonic-gate async_msint(struct asycom *asy)
19777c478bd9Sstevel@tonic-gate {
19787c478bd9Sstevel@tonic-gate 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
19797c478bd9Sstevel@tonic-gate 	int msr;
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 	msr = INB(MSR);	/* this resets the interrupt */
19827c478bd9Sstevel@tonic-gate 	asy->asy_cached_msr = msr;
19837c478bd9Sstevel@tonic-gate #ifdef DEBUG
19847c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_STATE) {
19857c478bd9Sstevel@tonic-gate 		printf("   transition: %3s %3s %3s %3s\n"
19860280efdcSzk 		    "current state: %3s %3s %3s %3s\n",
19870280efdcSzk 		    (msr & DCTS) ? "CTS" : "   ",
19880280efdcSzk 		    (msr & DDSR) ? "DSR" : "   ",
19890280efdcSzk 		    (msr & DRI) ?  "RI " : "   ",
19900280efdcSzk 		    (msr & DDCD) ? "DCD" : "   ",
19910280efdcSzk 		    (msr & CTS) ?  "CTS" : "   ",
19920280efdcSzk 		    (msr & DSR) ?  "DSR" : "   ",
19930280efdcSzk 		    (msr & RI) ?   "RI " : "   ",
19940280efdcSzk 		    (msr & DCD) ?  "DCD" : "   ");
19957c478bd9Sstevel@tonic-gate 	}
19967c478bd9Sstevel@tonic-gate #endif
19977c478bd9Sstevel@tonic-gate 	if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) {
19987c478bd9Sstevel@tonic-gate #ifdef DEBUG
19997c478bd9Sstevel@tonic-gate 		if (asydebug & ASY_DEBUG_HFLOW)
20007c478bd9Sstevel@tonic-gate 			printf("asy%d: hflow start\n",
20010280efdcSzk 			    UNIT(async->async_dev));
20027c478bd9Sstevel@tonic-gate #endif
20037c478bd9Sstevel@tonic-gate 		async->async_flags |= ASYNC_HW_OUT_FLW;
20047c478bd9Sstevel@tonic-gate 	}
20057c478bd9Sstevel@tonic-gate 	if (asy->asy_hwtype == ASY82510)
20067c478bd9Sstevel@tonic-gate 		OUTB(MSR, (msr & 0xF0));
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 	/* Handle PPS event */
20097c478bd9Sstevel@tonic-gate 	if (asy->asy_flags & ASY_PPS)
20107c478bd9Sstevel@tonic-gate 		asy_ppsevent(asy, msr);
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	async->async_ext++;
20137c478bd9Sstevel@tonic-gate 	ASYSETSOFT(asy);
20147c478bd9Sstevel@tonic-gate }
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate /*
20177c478bd9Sstevel@tonic-gate  * Handle a second-stage interrupt.
20187c478bd9Sstevel@tonic-gate  */
20197c478bd9Sstevel@tonic-gate uint_t
asysoftintr(caddr_t intarg)20207c478bd9Sstevel@tonic-gate asysoftintr(caddr_t intarg)
20217c478bd9Sstevel@tonic-gate {
20227c478bd9Sstevel@tonic-gate 	struct asycom *asy = (struct asycom *)intarg;
20237c478bd9Sstevel@tonic-gate 	struct asyncline *async;
20247c478bd9Sstevel@tonic-gate 	int rv;
20257c478bd9Sstevel@tonic-gate 	int cc;
20267c478bd9Sstevel@tonic-gate 	/*
20277c478bd9Sstevel@tonic-gate 	 * Test and clear soft interrupt.
20287c478bd9Sstevel@tonic-gate 	 */
20297c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_soft_lock);
20307c478bd9Sstevel@tonic-gate #ifdef DEBUG
20317c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
20327c478bd9Sstevel@tonic-gate 		printf("softintr\n");
20337c478bd9Sstevel@tonic-gate #endif
20347c478bd9Sstevel@tonic-gate 	rv = asy->asysoftpend;
20357c478bd9Sstevel@tonic-gate 	if (rv != 0)
20367c478bd9Sstevel@tonic-gate 		asy->asysoftpend = 0;
20377c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_soft_lock);
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate 	if (rv) {
20407c478bd9Sstevel@tonic-gate 		if (asy->asy_priv == NULL)
20417c478bd9Sstevel@tonic-gate 			return (rv);
20427c478bd9Sstevel@tonic-gate 		async = (struct asyncline *)asy->asy_priv;
20437c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
20447c478bd9Sstevel@tonic-gate 		if (asy->asy_flags & ASY_NEEDSOFT) {
20457c478bd9Sstevel@tonic-gate 			asy->asy_flags &= ~ASY_NEEDSOFT;
20467c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
20477c478bd9Sstevel@tonic-gate 			(void) async_softint(asy);
20487c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
20497c478bd9Sstevel@tonic-gate 		}
20507c478bd9Sstevel@tonic-gate 		/*
20517c478bd9Sstevel@tonic-gate 		 * There are some instances where the softintr is not
20527c478bd9Sstevel@tonic-gate 		 * scheduled and hence not called. It so happened that makes
20537c478bd9Sstevel@tonic-gate 		 * the last few characters to be stuck in ringbuffer.
20547c478bd9Sstevel@tonic-gate 		 * Hence, call once again the  handler so that the last few
20557c478bd9Sstevel@tonic-gate 		 * characters are cleared.
20567c478bd9Sstevel@tonic-gate 		 */
20577c478bd9Sstevel@tonic-gate 		cc = RING_CNT(async);
20587c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
20597c478bd9Sstevel@tonic-gate 		if (cc > 0) {
20607c478bd9Sstevel@tonic-gate 			(void) async_softint(asy);
20617c478bd9Sstevel@tonic-gate 		}
20627c478bd9Sstevel@tonic-gate 	}
20637c478bd9Sstevel@tonic-gate 	return (rv);
20647c478bd9Sstevel@tonic-gate }
20657c478bd9Sstevel@tonic-gate 
20667c478bd9Sstevel@tonic-gate /*
20677c478bd9Sstevel@tonic-gate  * Handle a software interrupt.
20687c478bd9Sstevel@tonic-gate  */
20697c478bd9Sstevel@tonic-gate static int
async_softint(struct asycom * asy)20707c478bd9Sstevel@tonic-gate async_softint(struct asycom *asy)
20717c478bd9Sstevel@tonic-gate {
20727c478bd9Sstevel@tonic-gate 	struct asyncline *async = (struct asyncline *)asy->asy_priv;
20737c478bd9Sstevel@tonic-gate 	uint_t	cc;
20747c478bd9Sstevel@tonic-gate 	mblk_t	*bp;
20757c478bd9Sstevel@tonic-gate 	queue_t	*q;
20767c478bd9Sstevel@tonic-gate 	uchar_t	val;
20777c478bd9Sstevel@tonic-gate 	uchar_t	c;
20787c478bd9Sstevel@tonic-gate 	tty_common_t	*tp;
20797c478bd9Sstevel@tonic-gate 
20807c478bd9Sstevel@tonic-gate #ifdef DEBUG
20817c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
20827c478bd9Sstevel@tonic-gate 		printf("process\n");
20837c478bd9Sstevel@tonic-gate #endif
20847c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
20857c478bd9Sstevel@tonic-gate 	if (asy->asy_flags & ASY_DOINGSOFT) {
20867c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
20877c478bd9Sstevel@tonic-gate 		return (0);
20887c478bd9Sstevel@tonic-gate 	}
20897c478bd9Sstevel@tonic-gate 	tp = &async->async_ttycommon;
20907c478bd9Sstevel@tonic-gate 	q = tp->t_readq;
20917c478bd9Sstevel@tonic-gate 	if (q != NULL) {
20927c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
20937c478bd9Sstevel@tonic-gate 		enterq(q);
20947c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
20957c478bd9Sstevel@tonic-gate 	}
20967c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
20977c478bd9Sstevel@tonic-gate 	asy->asy_flags |= ASY_DOINGSOFT;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	if (INB(ICR) & MIEN)
21007c478bd9Sstevel@tonic-gate 		val = asy->asy_cached_msr & 0xFF;
21017c478bd9Sstevel@tonic-gate 	else
21027c478bd9Sstevel@tonic-gate 		val = INB(MSR) & 0xFF;
21037c478bd9Sstevel@tonic-gate 
21047c478bd9Sstevel@tonic-gate 	if (async->async_ttycommon.t_cflag & CRTSCTS) {
21057c478bd9Sstevel@tonic-gate 		if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) {
21067c478bd9Sstevel@tonic-gate #ifdef DEBUG
21077c478bd9Sstevel@tonic-gate 			if (asydebug & ASY_DEBUG_HFLOW)
21087c478bd9Sstevel@tonic-gate 				printf("asy%d: hflow start\n",
21090280efdcSzk 				    UNIT(async->async_dev));
21107c478bd9Sstevel@tonic-gate #endif
21117c478bd9Sstevel@tonic-gate 			async->async_flags &= ~ASYNC_HW_OUT_FLW;
21127c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
21137c478bd9Sstevel@tonic-gate 			if (async->async_ocnt > 0) {
21147c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
21157c478bd9Sstevel@tonic-gate 				async_resume(async);
21167c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
21177c478bd9Sstevel@tonic-gate 			} else {
21187c478bd9Sstevel@tonic-gate 				async_start(async);
21197c478bd9Sstevel@tonic-gate 			}
21207c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
21217c478bd9Sstevel@tonic-gate 		}
21227c478bd9Sstevel@tonic-gate 	}
21237c478bd9Sstevel@tonic-gate 	if (async->async_ext) {
21247c478bd9Sstevel@tonic-gate 		async->async_ext = 0;
21257c478bd9Sstevel@tonic-gate 		/* check for carrier up */
21267c478bd9Sstevel@tonic-gate 		if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) {
21277c478bd9Sstevel@tonic-gate 			/* carrier present */
21287c478bd9Sstevel@tonic-gate 			if ((async->async_flags & ASYNC_CARR_ON) == 0) {
21297c478bd9Sstevel@tonic-gate 				async->async_flags |= ASYNC_CARR_ON;
21307c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
21317c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl);
21327c478bd9Sstevel@tonic-gate 				if (async->async_flags & ASYNC_ISOPEN)
21337c478bd9Sstevel@tonic-gate 					(void) putctl(q, M_UNHANGUP);
21347c478bd9Sstevel@tonic-gate 				cv_broadcast(&async->async_flags_cv);
21357c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl);
21367c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
21377c478bd9Sstevel@tonic-gate 			}
21387c478bd9Sstevel@tonic-gate 		} else {
21397c478bd9Sstevel@tonic-gate 			if ((async->async_flags & ASYNC_CARR_ON) &&
21407c478bd9Sstevel@tonic-gate 			    !(tp->t_cflag & CLOCAL)) {
21417c478bd9Sstevel@tonic-gate 				int flushflag;
21427c478bd9Sstevel@tonic-gate 
21437c478bd9Sstevel@tonic-gate 				/*
21447c478bd9Sstevel@tonic-gate 				 * Carrier went away.
21457c478bd9Sstevel@tonic-gate 				 * Drop DTR, abort any output in
21467c478bd9Sstevel@tonic-gate 				 * progress, indicate that output is
21477c478bd9Sstevel@tonic-gate 				 * not stopped, and send a hangup
21487c478bd9Sstevel@tonic-gate 				 * notification upstream.
21497c478bd9Sstevel@tonic-gate 				 *
21507c478bd9Sstevel@tonic-gate 				 * If we're in the midst of close, then flush
21517c478bd9Sstevel@tonic-gate 				 * everything.  Don't leave stale ioctls lying
21527c478bd9Sstevel@tonic-gate 				 * about.
21537c478bd9Sstevel@tonic-gate 				 */
21547c478bd9Sstevel@tonic-gate 				val = INB(MCR);
21557c478bd9Sstevel@tonic-gate 				OUTB(MCR, (val & ~DTR));
21567c478bd9Sstevel@tonic-gate 				flushflag = (async->async_flags &
21577c478bd9Sstevel@tonic-gate 				    ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
21584d0b1b0dSAn Bui 				if (tp->t_writeq != NULL) {
21594d0b1b0dSAn Bui 					flushq(tp->t_writeq, flushflag);
21604d0b1b0dSAn Bui 				}
21617c478bd9Sstevel@tonic-gate 				if (async->async_xmitblk != NULL) {
21627c478bd9Sstevel@tonic-gate 					freeb(async->async_xmitblk);
21637c478bd9Sstevel@tonic-gate 					async->async_xmitblk = NULL;
21647c478bd9Sstevel@tonic-gate 				}
21657c478bd9Sstevel@tonic-gate 				if (async->async_flags & ASYNC_BUSY) {
21667c478bd9Sstevel@tonic-gate 					async->async_ocnt = 0;
21677c478bd9Sstevel@tonic-gate 					async->async_flags &= ~ASYNC_BUSY;
21687c478bd9Sstevel@tonic-gate 				}
21697c478bd9Sstevel@tonic-gate 				async->async_flags &= ~ASYNC_STOPPED;
21707c478bd9Sstevel@tonic-gate 				if (async->async_flags & ASYNC_ISOPEN) {
21717c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl_hi);
21727c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl);
21737c478bd9Sstevel@tonic-gate 					(void) putctl(q, M_HANGUP);
21747c478bd9Sstevel@tonic-gate 					mutex_enter(asy->asy_excl);
21757c478bd9Sstevel@tonic-gate 					mutex_enter(asy->asy_excl_hi);
21767c478bd9Sstevel@tonic-gate 				}
21777cb42c7eSrameshc 				async->async_flags &= ~ASYNC_CARR_ON;
21787cb42c7eSrameshc 				mutex_exit(asy->asy_excl_hi);
21797cb42c7eSrameshc 				cv_broadcast(&async->async_flags_cv);
21807cb42c7eSrameshc 				mutex_enter(asy->asy_excl_hi);
21817c478bd9Sstevel@tonic-gate 			}
21827c478bd9Sstevel@tonic-gate 		}
21837c478bd9Sstevel@tonic-gate 	}
21847c478bd9Sstevel@tonic-gate 
21857c478bd9Sstevel@tonic-gate 	/*
21867c478bd9Sstevel@tonic-gate 	 * If data has been added to the circular buffer, remove
21877c478bd9Sstevel@tonic-gate 	 * it from the buffer, and send it up the stream if there's
21887c478bd9Sstevel@tonic-gate 	 * somebody listening. Try to do it 16 bytes at a time. If we
21897c478bd9Sstevel@tonic-gate 	 * have more than 16 bytes to move, move 16 byte chunks and
21907c478bd9Sstevel@tonic-gate 	 * leave the rest for next time around (maybe it will grow).
21917c478bd9Sstevel@tonic-gate 	 */
21927c478bd9Sstevel@tonic-gate 	if (!(async->async_flags & ASYNC_ISOPEN)) {
21937c478bd9Sstevel@tonic-gate 		RING_INIT(async);
21947c478bd9Sstevel@tonic-gate 		goto rv;
21957c478bd9Sstevel@tonic-gate 	}
21967c478bd9Sstevel@tonic-gate 	if ((cc = RING_CNT(async)) == 0) {
21977c478bd9Sstevel@tonic-gate 		goto rv;
21987c478bd9Sstevel@tonic-gate 	}
21997c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
22007c478bd9Sstevel@tonic-gate 
22017c478bd9Sstevel@tonic-gate 	if (!canput(q)) {
22027c478bd9Sstevel@tonic-gate 		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
22037c478bd9Sstevel@tonic-gate #ifdef DEBUG
22047c478bd9Sstevel@tonic-gate 			if (!(asydebug & ASY_DEBUG_HFLOW)) {
22057c478bd9Sstevel@tonic-gate 				printf("asy%d: hflow stop input.\n",
22060280efdcSzk 				    UNIT(async->async_dev));
22077c478bd9Sstevel@tonic-gate 				if (canputnext(q))
22087c478bd9Sstevel@tonic-gate 					printf("asy%d: next queue is "
22090280efdcSzk 					    "ready\n",
22100280efdcSzk 					    UNIT(async->async_dev));
22117c478bd9Sstevel@tonic-gate 			}
22127c478bd9Sstevel@tonic-gate #endif
22137c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
22147c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_HW_IN_FLOW;
22157c478bd9Sstevel@tonic-gate 			async->async_flowc = async->async_stopc;
22167c478bd9Sstevel@tonic-gate 		} else mutex_enter(asy->asy_excl_hi);
22177c478bd9Sstevel@tonic-gate 		goto rv;
22187c478bd9Sstevel@tonic-gate 	}
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 	if (async->async_ringbuf_overflow) {
22217c478bd9Sstevel@tonic-gate 		if ((async->async_flags & ASYNC_HW_IN_FLOW) &&
22220280efdcSzk 		    ((int)(RING_CNT(async)) < (RINGSIZE/4))) {
22237c478bd9Sstevel@tonic-gate #ifdef DEBUG
22247c478bd9Sstevel@tonic-gate 			if (asydebug & ASY_DEBUG_HFLOW)
22257c478bd9Sstevel@tonic-gate 				printf("asy%d: hflow start input.\n",
22260280efdcSzk 				    UNIT(async->async_dev));
22277c478bd9Sstevel@tonic-gate #endif
22287c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
22297c478bd9Sstevel@tonic-gate 			async->async_flags &= ~ASYNC_HW_IN_FLOW;
22307c478bd9Sstevel@tonic-gate 			async->async_flowc = async->async_startc;
22317c478bd9Sstevel@tonic-gate 			async->async_ringbuf_overflow = 0;
22327c478bd9Sstevel@tonic-gate 			goto rv;
22337c478bd9Sstevel@tonic-gate 		}
22347c478bd9Sstevel@tonic-gate 	}
22357c478bd9Sstevel@tonic-gate #ifdef DEBUG
22367c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_INPUT)
22377c478bd9Sstevel@tonic-gate 		printf("asy%d: %d char(s) in queue.\n",
22380280efdcSzk 		    UNIT(async->async_dev), cc);
22397c478bd9Sstevel@tonic-gate #endif
22407c478bd9Sstevel@tonic-gate 	/*
22417c478bd9Sstevel@tonic-gate 	 * Before you pull the characters from the RING BUF
22427c478bd9Sstevel@tonic-gate 	 * Check whether you can put into the queue again
22437c478bd9Sstevel@tonic-gate 	 */
22447c478bd9Sstevel@tonic-gate 	if ((!canputnext(q)) || (!canput(q))) {
22457c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
22467c478bd9Sstevel@tonic-gate 		if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
22477c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_HW_IN_FLOW;
22487c478bd9Sstevel@tonic-gate 			async->async_flowc = async->async_stopc;
22497c478bd9Sstevel@tonic-gate 			async->async_queue_full = 1;
22507c478bd9Sstevel@tonic-gate 		}
22517c478bd9Sstevel@tonic-gate 		goto rv;
22527c478bd9Sstevel@tonic-gate 	}
22537c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
22547c478bd9Sstevel@tonic-gate 	if (async->async_queue_full) {
22557c478bd9Sstevel@tonic-gate 		/*
22567c478bd9Sstevel@tonic-gate 		 * Last time the Stream queue didnot allow
22577c478bd9Sstevel@tonic-gate 		 * now it allows so, relax, the flow control
22587c478bd9Sstevel@tonic-gate 		 */
22597c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_HW_IN_FLOW) {
22607c478bd9Sstevel@tonic-gate 			async->async_flags &= ~ASYNC_HW_IN_FLOW;
22617c478bd9Sstevel@tonic-gate 			async->async_queue_full = 0;
22627c478bd9Sstevel@tonic-gate 			async->async_flowc = async->async_startc;
22637c478bd9Sstevel@tonic-gate 			goto rv;
22647c478bd9Sstevel@tonic-gate 		} else
22657c478bd9Sstevel@tonic-gate 			async->async_queue_full = 0;
22667c478bd9Sstevel@tonic-gate 	}
22677c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
22687c478bd9Sstevel@tonic-gate 	if (!(bp = allocb(cc, BPRI_MED))) {
22697c478bd9Sstevel@tonic-gate 		ttycommon_qfull(&async->async_ttycommon, q);
22707c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
22717c478bd9Sstevel@tonic-gate 		goto rv;
22727c478bd9Sstevel@tonic-gate 	}
22737c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
22747c478bd9Sstevel@tonic-gate 	do {
22757c478bd9Sstevel@tonic-gate 		if (RING_ERR(async, S_ERRORS)) {
22767c478bd9Sstevel@tonic-gate 			RING_UNMARK(async);
22777c478bd9Sstevel@tonic-gate 			c = RING_GET(async);
22787c478bd9Sstevel@tonic-gate 			break;
22797c478bd9Sstevel@tonic-gate 		} else {
22807c478bd9Sstevel@tonic-gate 			*bp->b_wptr++ = RING_GET(async);
22817c478bd9Sstevel@tonic-gate 		}
22827c478bd9Sstevel@tonic-gate 	} while (--cc);
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
22857c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
22867c478bd9Sstevel@tonic-gate 	if (bp->b_wptr > bp->b_rptr) {
22877c478bd9Sstevel@tonic-gate 		if (!canputnext(q)) {
22887c478bd9Sstevel@tonic-gate 			if (!canput(q)) {
22897c478bd9Sstevel@tonic-gate 				/*
22907c478bd9Sstevel@tonic-gate 				 * Even after taking all precautions that
22917c478bd9Sstevel@tonic-gate 				 * Still we are unable to queue, then we
22927c478bd9Sstevel@tonic-gate 				 * cannot do anything, just drop the block
22937c478bd9Sstevel@tonic-gate 				 */
22947c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE,
22950280efdcSzk 				    "su%d: local queue full\n",
22960280efdcSzk 				    UNIT(async->async_dev));
22977c478bd9Sstevel@tonic-gate 				freemsg(bp);
22987c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
22997c478bd9Sstevel@tonic-gate 				if ((async->async_flags &
23000280efdcSzk 				    ASYNC_HW_IN_FLOW) == 0) {
23017c478bd9Sstevel@tonic-gate 					async->async_flags |=
23020280efdcSzk 					    ASYNC_HW_IN_FLOW;
23037c478bd9Sstevel@tonic-gate 					async->async_flowc =
23040280efdcSzk 					    async->async_stopc;
23057c478bd9Sstevel@tonic-gate 					async->async_queue_full = 1;
23067c478bd9Sstevel@tonic-gate 				}
23077c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
23087c478bd9Sstevel@tonic-gate 			} else {
23097c478bd9Sstevel@tonic-gate 				(void) putq(q, bp);
23107c478bd9Sstevel@tonic-gate 			}
23117c478bd9Sstevel@tonic-gate 		} else {
23127c478bd9Sstevel@tonic-gate 			putnext(q, bp);
23137c478bd9Sstevel@tonic-gate 		}
23147c478bd9Sstevel@tonic-gate 	} else {
23157c478bd9Sstevel@tonic-gate 		freemsg(bp);
23167c478bd9Sstevel@tonic-gate 	}
23177c478bd9Sstevel@tonic-gate 	/*
23187c478bd9Sstevel@tonic-gate 	 * If we have a parity error, then send
23197c478bd9Sstevel@tonic-gate 	 * up an M_BREAK with the "bad"
23207c478bd9Sstevel@tonic-gate 	 * character as an argument. Let ldterm
23217c478bd9Sstevel@tonic-gate 	 * figure out what to do with the error.
23227c478bd9Sstevel@tonic-gate 	 */
23237c478bd9Sstevel@tonic-gate 	if (cc)
23247c478bd9Sstevel@tonic-gate 		(void) putctl1(q, M_BREAK, c);
23257c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
23267c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
23277c478bd9Sstevel@tonic-gate rv:
23287c478bd9Sstevel@tonic-gate 	/*
23297c478bd9Sstevel@tonic-gate 	 * If a transmission has finished, indicate that it's finished,
23307c478bd9Sstevel@tonic-gate 	 * and start that line up again.
23317c478bd9Sstevel@tonic-gate 	 */
23327c478bd9Sstevel@tonic-gate 	if (async->async_break) {
23337c478bd9Sstevel@tonic-gate 		async->async_break = 0;
23347c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_ISOPEN) {
23357c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
23367c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
23377c478bd9Sstevel@tonic-gate 			(void) putctl(q, M_BREAK);
23387c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
23397c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
23407c478bd9Sstevel@tonic-gate 		}
23417c478bd9Sstevel@tonic-gate 	}
23427c478bd9Sstevel@tonic-gate 	if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) ||
234319cdc281Skc 	    (async->async_flowc != '\0')) {
23447c478bd9Sstevel@tonic-gate 		async->async_flags &= ~ASYNC_BUSY;
23457c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
23467c478bd9Sstevel@tonic-gate 		if (async->async_xmitblk)
23477c478bd9Sstevel@tonic-gate 			freeb(async->async_xmitblk);
23487c478bd9Sstevel@tonic-gate 		async->async_xmitblk = NULL;
23497c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_ISOPEN) {
23507c478bd9Sstevel@tonic-gate 			asy->inperim = B_TRUE;
23517c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
23527c478bd9Sstevel@tonic-gate 			enterq(async->async_ttycommon.t_writeq);
23537c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
23547c478bd9Sstevel@tonic-gate 		}
23557c478bd9Sstevel@tonic-gate 		async_start(async);
23567c478bd9Sstevel@tonic-gate 		/*
23577c478bd9Sstevel@tonic-gate 		 * We need to check for inperim and ISOPEN due to
23587c478bd9Sstevel@tonic-gate 		 * multi-threading implications; it's possible to close the
23597c478bd9Sstevel@tonic-gate 		 * port and nullify async_flags while completing the software
23607c478bd9Sstevel@tonic-gate 		 * interrupt.  If the port is closed, leaveq() will have already
23617c478bd9Sstevel@tonic-gate 		 * been called.  We don't want to call it twice.
23627c478bd9Sstevel@tonic-gate 		 */
23637c478bd9Sstevel@tonic-gate 		if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) {
23647c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
23657c478bd9Sstevel@tonic-gate 			leaveq(async->async_ttycommon.t_writeq);
23667c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
23677c478bd9Sstevel@tonic-gate 			asy->inperim = B_FALSE;
23687c478bd9Sstevel@tonic-gate 		}
23697c478bd9Sstevel@tonic-gate 		if (!(async->async_flags & ASYNC_BUSY))
23707c478bd9Sstevel@tonic-gate 			cv_broadcast(&async->async_flags_cv);
23717c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
23727c478bd9Sstevel@tonic-gate 	}
23737c478bd9Sstevel@tonic-gate 	/*
23747c478bd9Sstevel@tonic-gate 	 * A note about these overrun bits: all they do is *tell* someone
23757c478bd9Sstevel@tonic-gate 	 * about an error- They do not track multiple errors. In fact,
23767c478bd9Sstevel@tonic-gate 	 * you could consider them latched register bits if you like.
23777c478bd9Sstevel@tonic-gate 	 * We are only interested in printing the error message once for
23784cad604cSMarcel Telka 	 * any cluster of overrun errors.
23797c478bd9Sstevel@tonic-gate 	 */
23807c478bd9Sstevel@tonic-gate 	if (async->async_hw_overrun) {
23817c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_ISOPEN) {
23827c478bd9Sstevel@tonic-gate 			if (su_log > 0) {
23837c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
23847c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl);
23857c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "su%d: silo overflow\n",
23867c478bd9Sstevel@tonic-gate 				    UNIT(async->async_dev));
23877c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl);
23887c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
23897c478bd9Sstevel@tonic-gate 			}
23907c478bd9Sstevel@tonic-gate 			INC64_KSTAT(asy, siloover);
23917c478bd9Sstevel@tonic-gate 		}
23927c478bd9Sstevel@tonic-gate 		async->async_hw_overrun = 0;
23937c478bd9Sstevel@tonic-gate 	}
23947c478bd9Sstevel@tonic-gate 	if (async->async_sw_overrun) {
23957c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_ISOPEN) {
23967c478bd9Sstevel@tonic-gate 			if (su_log > 0) {
23977c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
23987c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl);
23997c478bd9Sstevel@tonic-gate 				cmn_err(CE_NOTE, "su%d: ring buffer overflow\n",
24007c478bd9Sstevel@tonic-gate 				    UNIT(async->async_dev));
24017c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl);
24027c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
24037c478bd9Sstevel@tonic-gate 			}
24047c478bd9Sstevel@tonic-gate 			INC64_KSTAT(asy, ringover);
24057c478bd9Sstevel@tonic-gate 		}
24067c478bd9Sstevel@tonic-gate 		async->async_sw_overrun = 0;
24077c478bd9Sstevel@tonic-gate 	}
24087c478bd9Sstevel@tonic-gate 	asy->asy_flags &= ~ASY_DOINGSOFT;
24097c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
24107c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
24117c478bd9Sstevel@tonic-gate 	if (q != NULL)
24127c478bd9Sstevel@tonic-gate 		leaveq(q);
24137c478bd9Sstevel@tonic-gate 	return (0);
24147c478bd9Sstevel@tonic-gate }
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate /*
24177c478bd9Sstevel@tonic-gate  * Restart output on a line after a delay or break timer expired.
24187c478bd9Sstevel@tonic-gate  */
24197c478bd9Sstevel@tonic-gate static void
async_restart(void * arg)24207c478bd9Sstevel@tonic-gate async_restart(void *arg)
24217c478bd9Sstevel@tonic-gate {
24227c478bd9Sstevel@tonic-gate 	struct asyncline *async = arg;
24237c478bd9Sstevel@tonic-gate 	struct asycom *asy = async->async_common;
24247c478bd9Sstevel@tonic-gate 	queue_t *q;
24257c478bd9Sstevel@tonic-gate 	uchar_t lcr;
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	/*
24287c478bd9Sstevel@tonic-gate 	 * If break timer expired, turn off the break bit.
24297c478bd9Sstevel@tonic-gate 	 */
24307c478bd9Sstevel@tonic-gate #ifdef DEBUG
24317c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
24327c478bd9Sstevel@tonic-gate 		printf("restart\n");
24337c478bd9Sstevel@tonic-gate #endif
24347c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
24357c478bd9Sstevel@tonic-gate 	if (async->async_flags & ASYNC_BREAK) {
2436587bcfd8Skc 		unsigned int rate;
2437587bcfd8Skc 
24387c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
24397c478bd9Sstevel@tonic-gate 		lcr = INB(LCR);
24407c478bd9Sstevel@tonic-gate 		OUTB(LCR, (lcr & ~SETBREAK));
2441587bcfd8Skc 
2442587bcfd8Skc 		/*
2443587bcfd8Skc 		 * Go to sleep for the time it takes for at least one
2444587bcfd8Skc 		 * stop bit to be received by the device at the other
2445587bcfd8Skc 		 * end of the line as stated in the RS-232 specification.
2446587bcfd8Skc 		 * The wait period is equal to:
2447587bcfd8Skc 		 * 2 clock cycles * (1 MICROSEC / baud rate)
2448587bcfd8Skc 		 */
2449587bcfd8Skc 		rate = async->async_ttycommon.t_cflag & CBAUD;
2450587bcfd8Skc 		if (async->async_ttycommon.t_cflag & CBAUDEXT)
2451587bcfd8Skc 			rate += 16;
2452587bcfd8Skc 		if (rate >= N_SU_SPEEDS || rate == B0) {
2453587bcfd8Skc 			rate = B9600;
2454587bcfd8Skc 		}
2455587bcfd8Skc 
24567c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
2457587bcfd8Skc 		mutex_exit(asy->asy_excl);
2458587bcfd8Skc 		drv_usecwait(2 * MICROSEC / baudtable[rate]);
2459587bcfd8Skc 		mutex_enter(asy->asy_excl);
24607c478bd9Sstevel@tonic-gate 	}
24617c478bd9Sstevel@tonic-gate 	async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING);
24627c478bd9Sstevel@tonic-gate 	if ((q = async->async_ttycommon.t_writeq) != NULL) {
24637c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
24647c478bd9Sstevel@tonic-gate 		enterq(q);
24657c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
24667c478bd9Sstevel@tonic-gate 	}
24677c478bd9Sstevel@tonic-gate 	async_start(async);
24687c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
24697c478bd9Sstevel@tonic-gate 	if (q != NULL)
24707c478bd9Sstevel@tonic-gate 		leaveq(q);
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate 	/* cleared break or delay flag; may have made some output progress */
24737c478bd9Sstevel@tonic-gate 	cv_broadcast(&async->async_flags_cv);
24747c478bd9Sstevel@tonic-gate }
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate static void
async_start(struct asyncline * async)24777c478bd9Sstevel@tonic-gate async_start(struct asyncline *async)
24787c478bd9Sstevel@tonic-gate {
24797c478bd9Sstevel@tonic-gate 	async_nstart(async, 0);
24807c478bd9Sstevel@tonic-gate }
24817c478bd9Sstevel@tonic-gate 
24827c478bd9Sstevel@tonic-gate /*
24837c478bd9Sstevel@tonic-gate  * Start output on a line, unless it's busy, frozen, or otherwise.
24847c478bd9Sstevel@tonic-gate  */
24857c478bd9Sstevel@tonic-gate static void
async_nstart(struct asyncline * async,int mode)24867c478bd9Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode)
24877c478bd9Sstevel@tonic-gate {
24887c478bd9Sstevel@tonic-gate 	register struct asycom *asy = async->async_common;
24897c478bd9Sstevel@tonic-gate 	register int cc;
24907c478bd9Sstevel@tonic-gate 	register queue_t *q;
24917c478bd9Sstevel@tonic-gate 	mblk_t *bp, *nbp;
24927c478bd9Sstevel@tonic-gate 	uchar_t *xmit_addr;
24937c478bd9Sstevel@tonic-gate 	uchar_t	val;
24947c478bd9Sstevel@tonic-gate 	int	fifo_len = 1;
24957c478bd9Sstevel@tonic-gate 	int	xmit_progress;
24967c478bd9Sstevel@tonic-gate 
24977c478bd9Sstevel@tonic-gate #ifdef DEBUG
24987c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
24997c478bd9Sstevel@tonic-gate 		printf("start\n");
25007c478bd9Sstevel@tonic-gate #endif
25017c478bd9Sstevel@tonic-gate 	if (asy->asy_use_fifo == FIFO_ON)
25027c478bd9Sstevel@tonic-gate 		fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
25037c478bd9Sstevel@tonic-gate 
25047c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl));
25057c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
25067c478bd9Sstevel@tonic-gate 	asycheckflowcontrol_hw(asy);
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 	/*
25097c478bd9Sstevel@tonic-gate 	 * If the chip is busy (i.e., we're waiting for a break timeout
25107c478bd9Sstevel@tonic-gate 	 * to expire, or for the current transmission to finish, or for
25117c478bd9Sstevel@tonic-gate 	 * output to finish draining from chip), don't grab anything new.
25127c478bd9Sstevel@tonic-gate 	 */
25137c478bd9Sstevel@tonic-gate 	if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) {
25147c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
25157c478bd9Sstevel@tonic-gate #ifdef DEBUG
25167c478bd9Sstevel@tonic-gate 		if (mode && asydebug & ASY_DEBUG_CLOSE)
25177c478bd9Sstevel@tonic-gate 			printf("asy%d: start %s.\n",
25180280efdcSzk 			    UNIT(async->async_dev),
25190280efdcSzk 			    async->async_flags & ASYNC_BREAK
25200280efdcSzk 			    ? "break" : "busy");
25217c478bd9Sstevel@tonic-gate #endif
25227c478bd9Sstevel@tonic-gate 		return;
25237c478bd9Sstevel@tonic-gate 	}
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 	/*
25267c478bd9Sstevel@tonic-gate 	 * If we have a flow-control character to transmit, do it now.
25277c478bd9Sstevel@tonic-gate 	 */
25287c478bd9Sstevel@tonic-gate 	if (asycheckflowcontrol_sw(asy)) {
25297c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
25307c478bd9Sstevel@tonic-gate 		return;
25317c478bd9Sstevel@tonic-gate 	}
25327c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
25337c478bd9Sstevel@tonic-gate 	/*
25347c478bd9Sstevel@tonic-gate 	 * If we're waiting for a delay timeout to expire, don't grab
25357c478bd9Sstevel@tonic-gate 	 * anything new.
25367c478bd9Sstevel@tonic-gate 	 */
25377c478bd9Sstevel@tonic-gate 	if (async->async_flags & ASYNC_DELAY) {
25387c478bd9Sstevel@tonic-gate #ifdef DEBUG
25397c478bd9Sstevel@tonic-gate 		if (mode && asydebug & ASY_DEBUG_CLOSE)
25407c478bd9Sstevel@tonic-gate 			printf("asy%d: start ASYNC_DELAY.\n",
25410280efdcSzk 			    UNIT(async->async_dev));
25427c478bd9Sstevel@tonic-gate #endif
25437c478bd9Sstevel@tonic-gate 		return;
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
25477c478bd9Sstevel@tonic-gate #ifdef DEBUG
25487c478bd9Sstevel@tonic-gate 		if (mode && asydebug & ASY_DEBUG_CLOSE)
25497c478bd9Sstevel@tonic-gate 			printf("asy%d: start writeq is null.\n",
25500280efdcSzk 			    UNIT(async->async_dev));
25517c478bd9Sstevel@tonic-gate #endif
25527c478bd9Sstevel@tonic-gate 		return;	/* not attached to a stream */
25537c478bd9Sstevel@tonic-gate 	}
25547c478bd9Sstevel@tonic-gate 
25557c478bd9Sstevel@tonic-gate 	for (;;) {
25567c478bd9Sstevel@tonic-gate 		if ((bp = getq(q)) == NULL)
25577c478bd9Sstevel@tonic-gate 			return;	/* no data to transmit */
25587c478bd9Sstevel@tonic-gate 
25597c478bd9Sstevel@tonic-gate 		/*
25607c478bd9Sstevel@tonic-gate 		 * We have a message block to work on.
25617c478bd9Sstevel@tonic-gate 		 * Check whether it's a break, a delay, or an ioctl (the latter
25627c478bd9Sstevel@tonic-gate 		 * occurs if the ioctl in question was waiting for the output
25637c478bd9Sstevel@tonic-gate 		 * to drain).  If it's one of those, process it immediately.
25647c478bd9Sstevel@tonic-gate 		 */
25657c478bd9Sstevel@tonic-gate 		switch (bp->b_datap->db_type) {
25667c478bd9Sstevel@tonic-gate 
25677c478bd9Sstevel@tonic-gate 		case M_BREAK:
25687c478bd9Sstevel@tonic-gate 			/*
25697c478bd9Sstevel@tonic-gate 			 * Set the break bit, and arrange for "async_restart"
25707c478bd9Sstevel@tonic-gate 			 * to be called in 1/4 second; it will turn the
25717c478bd9Sstevel@tonic-gate 			 * break bit off, and call "async_start" to grab
25727c478bd9Sstevel@tonic-gate 			 * the next message.
25737c478bd9Sstevel@tonic-gate 			 */
25747c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
25757c478bd9Sstevel@tonic-gate 			val = INB(LCR);
25767c478bd9Sstevel@tonic-gate 			OUTB(LCR, (val | SETBREAK));
25777c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
25787c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_BREAK;
25797c478bd9Sstevel@tonic-gate 			(void) timeout(async_restart, async, hz / 4);
25807c478bd9Sstevel@tonic-gate 			freemsg(bp);
25817c478bd9Sstevel@tonic-gate 			return;	/* wait for this to finish */
25827c478bd9Sstevel@tonic-gate 
25837c478bd9Sstevel@tonic-gate 		case M_DELAY:
25847c478bd9Sstevel@tonic-gate 			/*
25857c478bd9Sstevel@tonic-gate 			 * Arrange for "async_restart" to be called when the
25867c478bd9Sstevel@tonic-gate 			 * delay expires; it will turn ASYNC_DELAY off,
25877c478bd9Sstevel@tonic-gate 			 * and call "async_start" to grab the next message.
25887c478bd9Sstevel@tonic-gate 			 */
25897c478bd9Sstevel@tonic-gate 			(void) timeout(async_restart, async,
25900280efdcSzk 			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
25917c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_DELAY;
25927c478bd9Sstevel@tonic-gate 			freemsg(bp);
25937c478bd9Sstevel@tonic-gate 			return;	/* wait for this to finish */
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 		case M_IOCTL:
25967c478bd9Sstevel@tonic-gate 			/*
25977c478bd9Sstevel@tonic-gate 			 * This ioctl needs to wait for the output ahead of
25987c478bd9Sstevel@tonic-gate 			 * it to drain.  Try to do it, and then either
25997c478bd9Sstevel@tonic-gate 			 * redo the ioctl at a later time or grab the next
26007c478bd9Sstevel@tonic-gate 			 * message after it.
26017c478bd9Sstevel@tonic-gate 			 */
26027c478bd9Sstevel@tonic-gate 
26037c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
26047c478bd9Sstevel@tonic-gate 			if (asy_isbusy(asy)) {
26057c478bd9Sstevel@tonic-gate 				/*
26067c478bd9Sstevel@tonic-gate 				 * Get the divisor by calculating the rate
26077c478bd9Sstevel@tonic-gate 				 */
26087c478bd9Sstevel@tonic-gate 				unsigned int rate;
26097c478bd9Sstevel@tonic-gate 
26107c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
26117c478bd9Sstevel@tonic-gate 				rate = async->async_ttycommon.t_cflag & CBAUD;
26127c478bd9Sstevel@tonic-gate 				if (async->async_ttycommon.t_cflag & CBAUDEXT)
26137c478bd9Sstevel@tonic-gate 					rate += 16;
26147c478bd9Sstevel@tonic-gate 				if (rate >= N_SU_SPEEDS || rate == B0) {
26157c478bd9Sstevel@tonic-gate 					rate = B9600;
26167c478bd9Sstevel@tonic-gate 				}
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate 				/*
26197c478bd9Sstevel@tonic-gate 				 * We need to do a callback as the port will
26207c478bd9Sstevel@tonic-gate 				 * be set to drain
26217c478bd9Sstevel@tonic-gate 				 */
26227c478bd9Sstevel@tonic-gate 				async->async_flags |= ASYNC_DRAINING;
26237c478bd9Sstevel@tonic-gate 
26247c478bd9Sstevel@tonic-gate 				/*
26257c478bd9Sstevel@tonic-gate 				 * Put the message we just processed back onto
26267c478bd9Sstevel@tonic-gate 				 * the end of the queue
26277c478bd9Sstevel@tonic-gate 				 */
26287c478bd9Sstevel@tonic-gate 				if (putq(q, bp) == 0)
26297c478bd9Sstevel@tonic-gate 					freemsg(bp);
26307c478bd9Sstevel@tonic-gate 
26317c478bd9Sstevel@tonic-gate 				/*
26327c478bd9Sstevel@tonic-gate 				 * We need to delay until the TSR and THR
26337c478bd9Sstevel@tonic-gate 				 * have been exhausted.  We base the delay on
26347c478bd9Sstevel@tonic-gate 				 * the amount of time it takes to transmit
26357c478bd9Sstevel@tonic-gate 				 * 2 chars at the current baud rate in
26367c478bd9Sstevel@tonic-gate 				 * microseconds.
26377c478bd9Sstevel@tonic-gate 				 *
26387c478bd9Sstevel@tonic-gate 				 * Therefore, the wait period is:
26397c478bd9Sstevel@tonic-gate 				 *
26407c478bd9Sstevel@tonic-gate 				 * (#TSR bits + #THR bits) *
2641574493ffSToomas Soome 				 *	1 MICROSEC / baud rate
26427c478bd9Sstevel@tonic-gate 				 */
26437c478bd9Sstevel@tonic-gate 				(void) timeout(async_restart, async,
26440280efdcSzk 				    drv_usectohz(16 * MICROSEC /
26450280efdcSzk 				    baudtable[rate]));
26467c478bd9Sstevel@tonic-gate 				return;
26477c478bd9Sstevel@tonic-gate 			}
26487c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
26497c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
26507c478bd9Sstevel@tonic-gate 			async_ioctl(async, q, bp, B_FALSE);
26517c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
26527c478bd9Sstevel@tonic-gate 			continue;
26537c478bd9Sstevel@tonic-gate 		}
26547c478bd9Sstevel@tonic-gate 
26557c478bd9Sstevel@tonic-gate 		while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
26567c478bd9Sstevel@tonic-gate 			nbp = bp->b_cont;
26577c478bd9Sstevel@tonic-gate 			freeb(bp);
26587c478bd9Sstevel@tonic-gate 			bp = nbp;
26597c478bd9Sstevel@tonic-gate 		}
26607c478bd9Sstevel@tonic-gate 		if (bp != NULL)
26617c478bd9Sstevel@tonic-gate 			break;
26627c478bd9Sstevel@tonic-gate 	}
26637c478bd9Sstevel@tonic-gate 
26647c478bd9Sstevel@tonic-gate 	/*
26657c478bd9Sstevel@tonic-gate 	 * We have data to transmit.  If output is stopped, put
26667c478bd9Sstevel@tonic-gate 	 * it back and try again later.
26677c478bd9Sstevel@tonic-gate 	 */
26687c478bd9Sstevel@tonic-gate 	if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) {
26697c478bd9Sstevel@tonic-gate #ifdef DEBUG
26707c478bd9Sstevel@tonic-gate 		if (asydebug & ASY_DEBUG_HFLOW &&
26710280efdcSzk 		    async->async_flags & ASYNC_HW_OUT_FLW)
26727c478bd9Sstevel@tonic-gate 			printf("asy%d: output hflow in effect.\n",
26730280efdcSzk 			    UNIT(async->async_dev));
26747c478bd9Sstevel@tonic-gate #endif
26757c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
26767c478bd9Sstevel@tonic-gate 		(void) putbq(q, bp);
26777c478bd9Sstevel@tonic-gate 		/*
26787c478bd9Sstevel@tonic-gate 		 * We entered the routine owning the lock, we need to
26797c478bd9Sstevel@tonic-gate 		 * exit the routine owning the lock.
26807c478bd9Sstevel@tonic-gate 		 */
26817c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
26827c478bd9Sstevel@tonic-gate 		return;
26837c478bd9Sstevel@tonic-gate 	}
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate 	async->async_xmitblk = bp;
26867c478bd9Sstevel@tonic-gate 	xmit_addr = bp->b_rptr;
26877c478bd9Sstevel@tonic-gate 	bp = bp->b_cont;
26887c478bd9Sstevel@tonic-gate 	if (bp != NULL) {
26897c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
26907c478bd9Sstevel@tonic-gate 		(void) putbq(q, bp);	/* not done with this message yet */
26917c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
26927c478bd9Sstevel@tonic-gate 	}
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 	/*
26957c478bd9Sstevel@tonic-gate 	 * In 5-bit mode, the high order bits are used
26967c478bd9Sstevel@tonic-gate 	 * to indicate character sizes less than five,
26977c478bd9Sstevel@tonic-gate 	 * so we need to explicitly mask before transmitting
26987c478bd9Sstevel@tonic-gate 	 */
26997c478bd9Sstevel@tonic-gate 	if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
27007c478bd9Sstevel@tonic-gate 		register unsigned char *p = xmit_addr;
27017c478bd9Sstevel@tonic-gate 		register int cnt = cc;
27027c478bd9Sstevel@tonic-gate 
27037c478bd9Sstevel@tonic-gate 		while (cnt--)
27047c478bd9Sstevel@tonic-gate 			*p++ &= (unsigned char) 0x1f;
27057c478bd9Sstevel@tonic-gate 	}
27067c478bd9Sstevel@tonic-gate 
27077c478bd9Sstevel@tonic-gate 	/*
27087c478bd9Sstevel@tonic-gate 	 * Set up this block for pseudo-DMA.
27097c478bd9Sstevel@tonic-gate 	 */
27107c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl_hi);
27117c478bd9Sstevel@tonic-gate 	async->async_optr = xmit_addr;
27127c478bd9Sstevel@tonic-gate 	async->async_ocnt = cc;
27137c478bd9Sstevel@tonic-gate 	/*
27147c478bd9Sstevel@tonic-gate 	 * If the transmitter is ready, shove some
27157c478bd9Sstevel@tonic-gate 	 * characters out.
27167c478bd9Sstevel@tonic-gate 	 */
27177c478bd9Sstevel@tonic-gate 	xmit_progress = 0;
27187c478bd9Sstevel@tonic-gate 	while (fifo_len-- && async->async_ocnt) {
27197c478bd9Sstevel@tonic-gate 		if (INB(LSR) & XHRE) {
27207c478bd9Sstevel@tonic-gate 			OUTB(DAT, *async->async_optr++);
27217c478bd9Sstevel@tonic-gate 			async->async_ocnt--;
27227c478bd9Sstevel@tonic-gate 			xmit_progress++;
27237c478bd9Sstevel@tonic-gate 		}
27247c478bd9Sstevel@tonic-gate 	}
27257c478bd9Sstevel@tonic-gate 	asy->asy_out_of_band_xmit = xmit_progress;
27267c478bd9Sstevel@tonic-gate 	if (xmit_progress > 0)
27277c478bd9Sstevel@tonic-gate 		async->async_flags |= ASYNC_PROGRESS;
27287c478bd9Sstevel@tonic-gate 	async->async_flags |= ASYNC_BUSY;
27297c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl_hi);
27307c478bd9Sstevel@tonic-gate }
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate /*
27337c478bd9Sstevel@tonic-gate  * Resume output by poking the transmitter.
27347c478bd9Sstevel@tonic-gate  */
27357c478bd9Sstevel@tonic-gate static void
async_resume(struct asyncline * async)27367c478bd9Sstevel@tonic-gate async_resume(struct asyncline *async)
27377c478bd9Sstevel@tonic-gate {
27387c478bd9Sstevel@tonic-gate 	register struct asycom *asy = async->async_common;
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
27417c478bd9Sstevel@tonic-gate #ifdef DEBUG
27427c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
27437c478bd9Sstevel@tonic-gate 		printf("resume\n");
27447c478bd9Sstevel@tonic-gate #endif
27457c478bd9Sstevel@tonic-gate 
27467c478bd9Sstevel@tonic-gate 	asycheckflowcontrol_hw(asy);
27477c478bd9Sstevel@tonic-gate 
27487c478bd9Sstevel@tonic-gate 	if (INB(LSR) & XHRE) {
27497c478bd9Sstevel@tonic-gate 		if (asycheckflowcontrol_sw(asy)) {
27507c478bd9Sstevel@tonic-gate 			return;
27517c478bd9Sstevel@tonic-gate 		} else if (async->async_ocnt > 0) {
27527c478bd9Sstevel@tonic-gate 			OUTB(DAT, *async->async_optr++);
27537c478bd9Sstevel@tonic-gate 			async->async_ocnt--;
27547c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_PROGRESS;
27557c478bd9Sstevel@tonic-gate 		}
27567c478bd9Sstevel@tonic-gate 	}
27577c478bd9Sstevel@tonic-gate }
27587c478bd9Sstevel@tonic-gate 
27597c478bd9Sstevel@tonic-gate /*
27607c478bd9Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
27617c478bd9Sstevel@tonic-gate  * Note that we don't need to get any locks until we are ready to access
27627c478bd9Sstevel@tonic-gate  * the hardware.  Nothing we access until then is going to be altered
27637c478bd9Sstevel@tonic-gate  * outside of the STREAMS framework, so we should be safe.
27647c478bd9Sstevel@tonic-gate  */
27657c478bd9Sstevel@tonic-gate static void
async_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp,boolean_t iswput)27667c478bd9Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput)
27677c478bd9Sstevel@tonic-gate {
27687c478bd9Sstevel@tonic-gate 	register struct asycom *asy = async->async_common;
27697c478bd9Sstevel@tonic-gate 	register tty_common_t  *tp = &async->async_ttycommon;
27707c478bd9Sstevel@tonic-gate 	register struct iocblk *iocp;
27717c478bd9Sstevel@tonic-gate 	register unsigned datasize;
277290a71dbdSzk 	size_t ioc_count;
27737c478bd9Sstevel@tonic-gate 	mblk_t *datamp;
27747c478bd9Sstevel@tonic-gate 	int error = 0;
27757c478bd9Sstevel@tonic-gate 	uchar_t val, icr;
27767c478bd9Sstevel@tonic-gate #ifdef DEBUG
27777c478bd9Sstevel@tonic-gate 	if (asydebug & ASY_DEBUG_PROCS)
27787c478bd9Sstevel@tonic-gate 		printf("ioctl\n");
27797c478bd9Sstevel@tonic-gate #endif
27807c478bd9Sstevel@tonic-gate 
27817c478bd9Sstevel@tonic-gate 	if (tp->t_iocpending != NULL) {
27827c478bd9Sstevel@tonic-gate 		/*
27837c478bd9Sstevel@tonic-gate 		 * We were holding an "ioctl" response pending the
27847c478bd9Sstevel@tonic-gate 		 * availability of an "mblk" to hold data to be passed up;
27857c478bd9Sstevel@tonic-gate 		 * another "ioctl" came through, which means that "ioctl"
27867c478bd9Sstevel@tonic-gate 		 * must have timed out or been aborted.
27877c478bd9Sstevel@tonic-gate 		 */
27887c478bd9Sstevel@tonic-gate 		freemsg(async->async_ttycommon.t_iocpending);
27897c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_iocpending = NULL;
27907c478bd9Sstevel@tonic-gate 	}
27917c478bd9Sstevel@tonic-gate 
27927c478bd9Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
27937c478bd9Sstevel@tonic-gate 
279490a71dbdSzk 	/*
279590a71dbdSzk 	 * Save off the ioc count in case we need to restore it
279690a71dbdSzk 	 * because we are queuing a message block.
279790a71dbdSzk 	 */
279890a71dbdSzk 	ioc_count = iocp->ioc_count;
279990a71dbdSzk 
28007c478bd9Sstevel@tonic-gate 	/*
28017c478bd9Sstevel@tonic-gate 	 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call
28027c478bd9Sstevel@tonic-gate 	 * ttycommon_ioctl() because this function frees up the message block
28037c478bd9Sstevel@tonic-gate 	 * (mp->b_cont) that contains the address of the user variable where
28047c478bd9Sstevel@tonic-gate 	 * we need to pass back the bit array.
28050280efdcSzk 	 *
28060280efdcSzk 	 * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO
28070280efdcSzk 	 * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them.
28087c478bd9Sstevel@tonic-gate 	 */
28097c478bd9Sstevel@tonic-gate 	if (iocp->ioc_cmd == TIOCMGET ||
28100280efdcSzk 	    iocp->ioc_cmd == TIOCMBIC ||
28110280efdcSzk 	    iocp->ioc_cmd == TIOCMBIS ||
28120280efdcSzk 	    iocp->ioc_cmd == TIOCMSET ||
28130280efdcSzk 	    iocp->ioc_cmd == TIOCGPPS ||
28140280efdcSzk 	    iocp->ioc_cmd == TIOCSPPS ||
28150280efdcSzk 	    iocp->ioc_cmd == TIOCGPPSEV ||
28160280efdcSzk 	    iocp->ioc_cmd == CONSOPENPOLLEDIO ||
28170280efdcSzk 	    iocp->ioc_cmd == CONSCLOSEPOLLEDIO)
28187c478bd9Sstevel@tonic-gate 		error = -1; /* Do Nothing */
28197c478bd9Sstevel@tonic-gate 	else
28207c478bd9Sstevel@tonic-gate 
28217c478bd9Sstevel@tonic-gate 	/*
28227c478bd9Sstevel@tonic-gate 	 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
28237c478bd9Sstevel@tonic-gate 	 * requires a response containing data to be returned to the user,
28247c478bd9Sstevel@tonic-gate 	 * and no mblk could be allocated for the data.
28257c478bd9Sstevel@tonic-gate 	 * No such "ioctl" alters our state.  Thus, we always go ahead and
28267c478bd9Sstevel@tonic-gate 	 * do any state-changes the "ioctl" calls for.  If we couldn't allocate
28277c478bd9Sstevel@tonic-gate 	 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
28287c478bd9Sstevel@tonic-gate 	 * we just call "bufcall" to request that we be called back when we
28297c478bd9Sstevel@tonic-gate 	 * stand a better chance of allocating the data.
28307c478bd9Sstevel@tonic-gate 	 */
28317c478bd9Sstevel@tonic-gate 	if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
28327c478bd9Sstevel@tonic-gate 		if (async->async_wbufcid)
28337c478bd9Sstevel@tonic-gate 			unbufcall(async->async_wbufcid);
28347c478bd9Sstevel@tonic-gate 		async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl,
28357c478bd9Sstevel@tonic-gate 		    async);
28367c478bd9Sstevel@tonic-gate 		return;
28377c478bd9Sstevel@tonic-gate 	}
28387c478bd9Sstevel@tonic-gate 
28397c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
28407c478bd9Sstevel@tonic-gate 
28417c478bd9Sstevel@tonic-gate 	if (error == 0) {
28427c478bd9Sstevel@tonic-gate 		/*
28437c478bd9Sstevel@tonic-gate 		 * "ttycommon_ioctl" did most of the work; we just use the
28447c478bd9Sstevel@tonic-gate 		 * data it set up.
28457c478bd9Sstevel@tonic-gate 		 */
28467c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
28477c478bd9Sstevel@tonic-gate 
28487c478bd9Sstevel@tonic-gate 		case TCSETS:
28497c478bd9Sstevel@tonic-gate 			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
28507c478bd9Sstevel@tonic-gate 			    asy->asy_lom_console)) {
28517c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
28527c478bd9Sstevel@tonic-gate 				error = asy_program(asy, ASY_NOINIT);
28537c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
28547c478bd9Sstevel@tonic-gate 			}
28557c478bd9Sstevel@tonic-gate 			break;
28567c478bd9Sstevel@tonic-gate 		case TCSETSF:
28577c478bd9Sstevel@tonic-gate 		case TCSETSW:
28587c478bd9Sstevel@tonic-gate 		case TCSETA:
28597c478bd9Sstevel@tonic-gate 		case TCSETAW:
28607c478bd9Sstevel@tonic-gate 		case TCSETAF:
28617c478bd9Sstevel@tonic-gate 			if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
28627c478bd9Sstevel@tonic-gate 			    asy->asy_lom_console)) {
28637c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
28647c478bd9Sstevel@tonic-gate 				if (iswput && asy_isbusy(asy)) {
286590a71dbdSzk 					/*
286690a71dbdSzk 					 * ttycommon_ioctl sets the db_type to
286790a71dbdSzk 					 * M_IOCACK and ioc_count to zero
286890a71dbdSzk 					 * we need to undo this when we
286990a71dbdSzk 					 * queue a control message. This will
287090a71dbdSzk 					 * allow the control messages to be
287190a71dbdSzk 					 * processed again when the chip
287290a71dbdSzk 					 * becomes available.
287390a71dbdSzk 					 */
287490a71dbdSzk 					mp->b_datap->db_type = M_IOCTL;
287590a71dbdSzk 					iocp->ioc_count = ioc_count;
287690a71dbdSzk 
28777c478bd9Sstevel@tonic-gate 					if (putq(wq, mp) == 0)
28787c478bd9Sstevel@tonic-gate 						freemsg(mp);
28797c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl_hi);
28807c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl);
28817c478bd9Sstevel@tonic-gate 					return;
28827c478bd9Sstevel@tonic-gate 				}
28830a450c21Szk 
28840a450c21Szk 				/*
28850a450c21Szk 				 * TCSETA, TCSETAW, and TCSETAF make use of
28860a450c21Szk 				 * the termio structure and therefore have
28870a450c21Szk 				 * no concept of any speed except what can
28880a450c21Szk 				 * be represented by CBAUD. This is because
28890a450c21Szk 				 * of legacy SVR4 code. Therefore, if we see
28900a450c21Szk 				 * one of the aforementioned IOCTL commands
28910a450c21Szk 				 * we should zero out CBAUDEXT, CIBAUD, and
28920a450c21Szk 				 * CIBAUDEXT as to not break legacy
28930a450c21Szk 				 * functionality. This is because CBAUDEXT,
28940a450c21Szk 				 * CIBAUD, and CIBAUDEXT can't be stored in
28950a450c21Szk 				 * an unsigned short. By zeroing out CBAUDEXT,
28960a450c21Szk 				 * CIBAUD, and CIBAUDEXT in the t_cflag of the
28970a450c21Szk 				 * termios structure asy_program() will set the
28980a450c21Szk 				 * input baud rate to the output baud rate.
28990a450c21Szk 				 */
29000a450c21Szk 				if (iocp->ioc_cmd == TCSETA ||
29010a450c21Szk 				    iocp->ioc_cmd == TCSETAW ||
29020a450c21Szk 				    iocp->ioc_cmd == TCSETAF)
29030a450c21Szk 					tp->t_cflag &= ~(CIBAUD |
29040a450c21Szk 					    CIBAUDEXT | CBAUDEXT);
29050a450c21Szk 
29067c478bd9Sstevel@tonic-gate 				error = asy_program(asy, ASY_NOINIT);
29077c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
29087c478bd9Sstevel@tonic-gate 			}
29097c478bd9Sstevel@tonic-gate 			break;
29107c478bd9Sstevel@tonic-gate 		case TIOCSSOFTCAR:
29117c478bd9Sstevel@tonic-gate 			/* Set the driver state appropriately */
29127c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
29137c478bd9Sstevel@tonic-gate 			if (tp->t_flags & TS_SOFTCAR)
29147c478bd9Sstevel@tonic-gate 				asy->asy_flags |= ASY_IGNORE_CD;
29157c478bd9Sstevel@tonic-gate 			else
29167c478bd9Sstevel@tonic-gate 				asy->asy_flags &= ~ASY_IGNORE_CD;
29177c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
29187c478bd9Sstevel@tonic-gate 			break;
29197c478bd9Sstevel@tonic-gate 		}
29207c478bd9Sstevel@tonic-gate 	} else if (error < 0) {
29217c478bd9Sstevel@tonic-gate 		/*
29227c478bd9Sstevel@tonic-gate 		 * "ttycommon_ioctl" didn't do anything; we process it here.
29237c478bd9Sstevel@tonic-gate 		 */
29247c478bd9Sstevel@tonic-gate 		error = 0;
29257c478bd9Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
29267c478bd9Sstevel@tonic-gate 
29277c478bd9Sstevel@tonic-gate 		case TIOCGPPS:
29287c478bd9Sstevel@tonic-gate 			/*
29297c478bd9Sstevel@tonic-gate 			 * Get PPS on/off.
29307c478bd9Sstevel@tonic-gate 			 */
29317c478bd9Sstevel@tonic-gate 			if (mp->b_cont != NULL)
29327c478bd9Sstevel@tonic-gate 				freemsg(mp->b_cont);
29337c478bd9Sstevel@tonic-gate 
29347c478bd9Sstevel@tonic-gate 			mp->b_cont = allocb(sizeof (int), BPRI_HI);
29357c478bd9Sstevel@tonic-gate 			if (mp->b_cont == NULL) {
29367c478bd9Sstevel@tonic-gate 				error = ENOMEM;
29377c478bd9Sstevel@tonic-gate 				break;
29387c478bd9Sstevel@tonic-gate 			}
29397c478bd9Sstevel@tonic-gate 			if (asy->asy_flags & ASY_PPS)
29407c478bd9Sstevel@tonic-gate 				*(int *)mp->b_cont->b_wptr = 1;
29417c478bd9Sstevel@tonic-gate 			else
29427c478bd9Sstevel@tonic-gate 				*(int *)mp->b_cont->b_wptr = 0;
29437c478bd9Sstevel@tonic-gate 			mp->b_cont->b_wptr += sizeof (int);
29447c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
29457c478bd9Sstevel@tonic-gate 			iocp->ioc_count = sizeof (int);
29467c478bd9Sstevel@tonic-gate 			break;
29477c478bd9Sstevel@tonic-gate 
29487c478bd9Sstevel@tonic-gate 		case TIOCSPPS:
29497c478bd9Sstevel@tonic-gate 			/*
29507c478bd9Sstevel@tonic-gate 			 * Set PPS on/off.
29517c478bd9Sstevel@tonic-gate 			 */
29527c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
29537c478bd9Sstevel@tonic-gate 			if (error != 0)
29547c478bd9Sstevel@tonic-gate 				break;
29557c478bd9Sstevel@tonic-gate 
29567c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
29577c478bd9Sstevel@tonic-gate 			if (*(int *)mp->b_cont->b_rptr)
29587c478bd9Sstevel@tonic-gate 				asy->asy_flags |= ASY_PPS;
29597c478bd9Sstevel@tonic-gate 			else
29607c478bd9Sstevel@tonic-gate 				asy->asy_flags &= ~ASY_PPS;
29617c478bd9Sstevel@tonic-gate 			/* Reset edge sense */
29627c478bd9Sstevel@tonic-gate 			asy->asy_flags &= ~ASY_PPS_EDGE;
29637c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
29647c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
29657c478bd9Sstevel@tonic-gate 			break;
29667c478bd9Sstevel@tonic-gate 
29677c478bd9Sstevel@tonic-gate 		case TIOCGPPSEV: {
29687c478bd9Sstevel@tonic-gate 			/*
29697c478bd9Sstevel@tonic-gate 			 * Get PPS event data.
29707c478bd9Sstevel@tonic-gate 			 */
29717c478bd9Sstevel@tonic-gate 			mblk_t *bp;
29727c478bd9Sstevel@tonic-gate 			void *buf;
29737c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
29747c478bd9Sstevel@tonic-gate 			struct ppsclockev32 p32;
29757c478bd9Sstevel@tonic-gate #endif
29767c478bd9Sstevel@tonic-gate 			struct ppsclockev ppsclockev;
29777c478bd9Sstevel@tonic-gate 
29787c478bd9Sstevel@tonic-gate 			if (mp->b_cont != NULL) {
29797c478bd9Sstevel@tonic-gate 				freemsg(mp->b_cont);
29807c478bd9Sstevel@tonic-gate 				mp->b_cont = NULL;
29817c478bd9Sstevel@tonic-gate 			}
29827c478bd9Sstevel@tonic-gate 
29837c478bd9Sstevel@tonic-gate 			if ((asy->asy_flags & ASY_PPS) == 0) {
29847c478bd9Sstevel@tonic-gate 				error = ENXIO;
29857c478bd9Sstevel@tonic-gate 				break;
29867c478bd9Sstevel@tonic-gate 			}
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 			/* Protect from incomplete asy_ppsev */
29897c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
29907c478bd9Sstevel@tonic-gate 			ppsclockev = asy_ppsev;
29917c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
29927c478bd9Sstevel@tonic-gate 
29937c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
29947c478bd9Sstevel@tonic-gate 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
29957c478bd9Sstevel@tonic-gate 				TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
29967c478bd9Sstevel@tonic-gate 				p32.serial = ppsclockev.serial;
29977c478bd9Sstevel@tonic-gate 				buf = &p32;
29987c478bd9Sstevel@tonic-gate 				iocp->ioc_count = sizeof (struct ppsclockev32);
29997c478bd9Sstevel@tonic-gate 			} else
30007c478bd9Sstevel@tonic-gate #endif
30017c478bd9Sstevel@tonic-gate 			{
30027c478bd9Sstevel@tonic-gate 				buf = &ppsclockev;
30037c478bd9Sstevel@tonic-gate 				iocp->ioc_count = sizeof (struct ppsclockev);
30047c478bd9Sstevel@tonic-gate 			}
30057c478bd9Sstevel@tonic-gate 
30067c478bd9Sstevel@tonic-gate 			if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
30077c478bd9Sstevel@tonic-gate 				error = ENOMEM;
30087c478bd9Sstevel@tonic-gate 				break;
30097c478bd9Sstevel@tonic-gate 			}
30107c478bd9Sstevel@tonic-gate 			mp->b_cont = bp;
30117c478bd9Sstevel@tonic-gate 
30127c478bd9Sstevel@tonic-gate 			bcopy(buf, bp->b_wptr, iocp->ioc_count);
30137c478bd9Sstevel@tonic-gate 			bp->b_wptr += iocp->ioc_count;
30147c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
30157c478bd9Sstevel@tonic-gate 			break;
30167c478bd9Sstevel@tonic-gate 		}
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate 		case TCSBRK:
30197c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
30207c478bd9Sstevel@tonic-gate 			if (error != 0)
30217c478bd9Sstevel@tonic-gate 				break;
30227c478bd9Sstevel@tonic-gate 
30237c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
30247c478bd9Sstevel@tonic-gate 			if (*(int *)mp->b_cont->b_rptr == 0) {
30257c478bd9Sstevel@tonic-gate 				/*
30267c478bd9Sstevel@tonic-gate 				 * Get the divisor by calculating the rate
30277c478bd9Sstevel@tonic-gate 				 */
30287c478bd9Sstevel@tonic-gate 				unsigned int rate, divisor;
30297c478bd9Sstevel@tonic-gate 				rate = async->async_ttycommon.t_cflag & CBAUD;
30307c478bd9Sstevel@tonic-gate 				if (async->async_ttycommon.t_cflag & CBAUDEXT)
30317c478bd9Sstevel@tonic-gate 					rate += 16;
30327c478bd9Sstevel@tonic-gate 				if (rate >= N_SU_SPEEDS) rate = B9600;
30337c478bd9Sstevel@tonic-gate 				divisor = asyspdtab[rate] & 0xfff;
30347c478bd9Sstevel@tonic-gate 
30357c478bd9Sstevel@tonic-gate 				/*
30367c478bd9Sstevel@tonic-gate 				 * To ensure that erroneous characters are
30377c478bd9Sstevel@tonic-gate 				 * not sent out when the break is set, SB
30387c478bd9Sstevel@tonic-gate 				 * recommends three steps:
30397c478bd9Sstevel@tonic-gate 				 *
30407c478bd9Sstevel@tonic-gate 				 * 1) pad the TSR with 0 bits
30417c478bd9Sstevel@tonic-gate 				 * 2) When the TSR is full, set break
30427c478bd9Sstevel@tonic-gate 				 * 3) When the TSR has been flushed, unset
30437c478bd9Sstevel@tonic-gate 				 *    the break when transmission must be
30447c478bd9Sstevel@tonic-gate 				 *    restored.
30457c478bd9Sstevel@tonic-gate 				 *
30467c478bd9Sstevel@tonic-gate 				 * We loop until the TSR is empty and then
30477c478bd9Sstevel@tonic-gate 				 * set the break.  ASYNC_BREAK has been set
30487c478bd9Sstevel@tonic-gate 				 * to ensure that no characters are
30497c478bd9Sstevel@tonic-gate 				 * transmitted while the TSR is being
30507c478bd9Sstevel@tonic-gate 				 * flushed and SOUT is being used for the
30517c478bd9Sstevel@tonic-gate 				 * break signal.
30527c478bd9Sstevel@tonic-gate 				 *
30537c478bd9Sstevel@tonic-gate 				 * The wait period is equal to
30547c478bd9Sstevel@tonic-gate 				 * clock / (baud * 16) * 16 * 2.
30557c478bd9Sstevel@tonic-gate 				 */
30567c478bd9Sstevel@tonic-gate 				async->async_flags |= ASYNC_BREAK;
30577c478bd9Sstevel@tonic-gate 				while ((INB(LSR) & XSRE) == 0) {
30587c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl_hi);
30597c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl);
30607c478bd9Sstevel@tonic-gate 					drv_usecwait(32*divisor);
30617c478bd9Sstevel@tonic-gate 					mutex_enter(asy->asy_excl);
30627c478bd9Sstevel@tonic-gate 					mutex_enter(asy->asy_excl_hi);
30637c478bd9Sstevel@tonic-gate 				}
30647c478bd9Sstevel@tonic-gate 
30657c478bd9Sstevel@tonic-gate 				/*
30667c478bd9Sstevel@tonic-gate 				 * Set the break bit, and arrange for
30677c478bd9Sstevel@tonic-gate 				 * "async_restart" to be called in 1/4 second;
30687c478bd9Sstevel@tonic-gate 				 * it will turn the break bit off, and call
30697c478bd9Sstevel@tonic-gate 				 * "async_start" to grab the next message.
30707c478bd9Sstevel@tonic-gate 				 */
30717c478bd9Sstevel@tonic-gate 				val = INB(LCR);
30727c478bd9Sstevel@tonic-gate 				OUTB(LCR, (val | SETBREAK));
30737c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
30747c478bd9Sstevel@tonic-gate 				(void) timeout(async_restart, async, hz / 4);
30757c478bd9Sstevel@tonic-gate 			} else {
30767c478bd9Sstevel@tonic-gate #ifdef DEBUG
30777c478bd9Sstevel@tonic-gate 				if (asydebug & ASY_DEBUG_CLOSE)
30787c478bd9Sstevel@tonic-gate 					printf("asy%d: wait for flush.\n",
30790280efdcSzk 					    UNIT(async->async_dev));
30807c478bd9Sstevel@tonic-gate #endif
30817c478bd9Sstevel@tonic-gate 				if (iswput && asy_isbusy(asy)) {
30827c478bd9Sstevel@tonic-gate 					if (putq(wq, mp) == 0)
30837c478bd9Sstevel@tonic-gate 						freemsg(mp);
30847c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl_hi);
30857c478bd9Sstevel@tonic-gate 					mutex_exit(asy->asy_excl);
30867c478bd9Sstevel@tonic-gate 					return;
30877c478bd9Sstevel@tonic-gate 				}
30887c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
30897c478bd9Sstevel@tonic-gate #ifdef DEBUG
30907c478bd9Sstevel@tonic-gate 				if (asydebug & ASY_DEBUG_CLOSE)
30917c478bd9Sstevel@tonic-gate 					printf("asy%d: ldterm satisfied.\n",
30920280efdcSzk 					    UNIT(async->async_dev));
30937c478bd9Sstevel@tonic-gate #endif
30947c478bd9Sstevel@tonic-gate 			}
30957c478bd9Sstevel@tonic-gate 			break;
30967c478bd9Sstevel@tonic-gate 
30977c478bd9Sstevel@tonic-gate 		case TIOCSBRK:
30987c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
30997c478bd9Sstevel@tonic-gate 			val = INB(LCR);
31007c478bd9Sstevel@tonic-gate 			OUTB(LCR, (val | SETBREAK));
31017c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
31027c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
31037c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
31047c478bd9Sstevel@tonic-gate 			return;
31057c478bd9Sstevel@tonic-gate 
31067c478bd9Sstevel@tonic-gate 		case TIOCCBRK:
31077c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
31087c478bd9Sstevel@tonic-gate 			val = INB(LCR);
31097c478bd9Sstevel@tonic-gate 			OUTB(LCR, (val & ~SETBREAK));
31107c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
31117c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
31127c478bd9Sstevel@tonic-gate 			miocack(wq, mp, 0, 0);
31137c478bd9Sstevel@tonic-gate 			return;
31147c478bd9Sstevel@tonic-gate 
31157c478bd9Sstevel@tonic-gate 		case TIOCMSET:
31167c478bd9Sstevel@tonic-gate 		case TIOCMBIS:
31177c478bd9Sstevel@tonic-gate 		case TIOCMBIC:
31187c478bd9Sstevel@tonic-gate 			if (iocp->ioc_count == TRANSPARENT)
31197c478bd9Sstevel@tonic-gate 				mcopyin(mp, NULL, sizeof (int), NULL);
31207c478bd9Sstevel@tonic-gate 			else {
31217c478bd9Sstevel@tonic-gate 				error = miocpullup(mp, sizeof (int));
31227c478bd9Sstevel@tonic-gate 				if (error != 0)
31237c478bd9Sstevel@tonic-gate 					break;
31247c478bd9Sstevel@tonic-gate 
31257c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
31267c478bd9Sstevel@tonic-gate 
31277c478bd9Sstevel@tonic-gate 				(void) asymctl(asy,
31280280efdcSzk 				    dmtoasy(*(int *)mp->b_cont->b_rptr),
31290280efdcSzk 				    iocp->ioc_cmd);
31307c478bd9Sstevel@tonic-gate 
31317c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
31327c478bd9Sstevel@tonic-gate 				iocp->ioc_error = 0;
31337c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
31347c478bd9Sstevel@tonic-gate 			}
31357c478bd9Sstevel@tonic-gate 			break;
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate 		case TIOCSILOOP:
31387c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
31397c478bd9Sstevel@tonic-gate 			/*
31407c478bd9Sstevel@tonic-gate 			 * If somebody misues this Ioctl when used for
31417c478bd9Sstevel@tonic-gate 			 * driving keyboard and mouse indicate not supported
31427c478bd9Sstevel@tonic-gate 			 */
31437c478bd9Sstevel@tonic-gate 			if ((asy->asy_device_type == ASY_KEYBOARD) ||
31440280efdcSzk 			    (asy->asy_device_type == ASY_MOUSE)) {
31457c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
31467c478bd9Sstevel@tonic-gate 				error = ENOTTY;
31477c478bd9Sstevel@tonic-gate 				break;
31487c478bd9Sstevel@tonic-gate 			}
31497c478bd9Sstevel@tonic-gate 
31507c478bd9Sstevel@tonic-gate 			/* should not use when we're the console */
31517c478bd9Sstevel@tonic-gate 			if ((async->async_dev == kbddev) ||
31527c478bd9Sstevel@tonic-gate 			    (async->async_dev == rconsdev) ||
31537c478bd9Sstevel@tonic-gate 			    (async->async_dev == stdindev)) {
31547c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
31557c478bd9Sstevel@tonic-gate 				error = EINVAL;
31567c478bd9Sstevel@tonic-gate 				break;
31577c478bd9Sstevel@tonic-gate 			}
31587c478bd9Sstevel@tonic-gate 
31597c478bd9Sstevel@tonic-gate 			val = INB(MCR);
31607c478bd9Sstevel@tonic-gate 			icr = INB(ICR);
31617c478bd9Sstevel@tonic-gate 			/*
31627c478bd9Sstevel@tonic-gate 			 * Disable the Modem Status Interrupt
31637c478bd9Sstevel@tonic-gate 			 * The reason for disabling is  the status of
31647c478bd9Sstevel@tonic-gate 			 * modem signal are in the higher 4 bits instead of
31657c478bd9Sstevel@tonic-gate 			 * lower four bits when in loopback mode,
31667c478bd9Sstevel@tonic-gate 			 * so, donot worry about Modem interrupt when
31677c478bd9Sstevel@tonic-gate 			 * you are planning to set
31687c478bd9Sstevel@tonic-gate 			 * this in loopback mode until it is cleared by
31697c478bd9Sstevel@tonic-gate 			 * another ioctl to get out of the loopback mode
31707c478bd9Sstevel@tonic-gate 			 */
31717c478bd9Sstevel@tonic-gate 			OUTB(ICR, icr & ~ MIEN);
31727c478bd9Sstevel@tonic-gate 			OUTB(MCR, val | ASY_LOOP);
31737c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
31747c478bd9Sstevel@tonic-gate 			iocp->ioc_error = 0;
31757c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
31767c478bd9Sstevel@tonic-gate 			break;
31777c478bd9Sstevel@tonic-gate 
31787c478bd9Sstevel@tonic-gate 		case TIOCMGET:
31797c478bd9Sstevel@tonic-gate 			datamp = allocb(sizeof (int), BPRI_MED);
31807c478bd9Sstevel@tonic-gate 			if (datamp == NULL) {
31817c478bd9Sstevel@tonic-gate 				error = EAGAIN;
31827c478bd9Sstevel@tonic-gate 				break;
31837c478bd9Sstevel@tonic-gate 			}
31847c478bd9Sstevel@tonic-gate 
31857c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
31867c478bd9Sstevel@tonic-gate 			*(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
31877c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
31887c478bd9Sstevel@tonic-gate 
31897c478bd9Sstevel@tonic-gate 			if (iocp->ioc_count == TRANSPARENT) {
31907c478bd9Sstevel@tonic-gate 				mcopyout(mp, NULL, sizeof (int), NULL, datamp);
31917c478bd9Sstevel@tonic-gate 			} else {
31927c478bd9Sstevel@tonic-gate 				if (mp->b_cont != NULL)
31937c478bd9Sstevel@tonic-gate 					freemsg(mp->b_cont);
31947c478bd9Sstevel@tonic-gate 				mp->b_cont = datamp;
31957c478bd9Sstevel@tonic-gate 				mp->b_cont->b_wptr += sizeof (int);
31967c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
31977c478bd9Sstevel@tonic-gate 				iocp->ioc_count = sizeof (int);
31987c478bd9Sstevel@tonic-gate 			}
31997c478bd9Sstevel@tonic-gate 			break;
32007c478bd9Sstevel@tonic-gate 
32010280efdcSzk 		case CONSOPENPOLLEDIO:
32020280efdcSzk 			/*
32030280efdcSzk 			 * If we are driving a keyboard there is nothing
32040280efdcSzk 			 * upstream to translate the scan codes. Therefore,
32050280efdcSzk 			 * set the error code to ENOTSUP and NAK the request
32060280efdcSzk 			 */
32070280efdcSzk 			if (asy->asy_device_type == ASY_KEYBOARD) {
32080280efdcSzk 				error = ENOTSUP;
32090280efdcSzk 				break;
32100280efdcSzk 			}
32110280efdcSzk 
32120280efdcSzk 			error = miocpullup(mp, sizeof (struct cons_polledio *));
32130280efdcSzk 			if (error != 0)
32140280efdcSzk 				break;
32150280efdcSzk 
32160280efdcSzk 			/*
32170280efdcSzk 			 * send up a message block containing the
32180280efdcSzk 			 * cons_polledio structure. This provides
32190280efdcSzk 			 * handles to the putchar, getchar, ischar,
32200280efdcSzk 			 * polledio_enter and polledio_exit functions.
32210280efdcSzk 			 */
32220280efdcSzk 			*(struct cons_polledio **)mp->b_cont->b_rptr =
32230280efdcSzk 			    &asy->polledio;
32240280efdcSzk 
32250280efdcSzk 			mp->b_datap->db_type = M_IOCACK;
32260280efdcSzk 			break;
32270280efdcSzk 
32280280efdcSzk 		case CONSCLOSEPOLLEDIO:
32290280efdcSzk 			/*
32300280efdcSzk 			 * If we are driving a keyboard we never successfully
32310280efdcSzk 			 * called CONSOPENPOLLEDIO so set the error to
32320280efdcSzk 			 * ENOTSUP and NAK the request.
32330280efdcSzk 			 */
32340280efdcSzk 			if (asy->asy_device_type == ASY_KEYBOARD) {
32350280efdcSzk 				error = ENOTSUP;
32360280efdcSzk 				break;
32370280efdcSzk 			}
32380280efdcSzk 
32390280efdcSzk 			mp->b_datap->db_type = M_IOCACK;
32400280efdcSzk 			iocp->ioc_error = 0;
32410280efdcSzk 			iocp->ioc_rval = 0;
32420280efdcSzk 			break;
32430280efdcSzk 
32447c478bd9Sstevel@tonic-gate 		default: /* unexpected ioctl type */
32457c478bd9Sstevel@tonic-gate 			/*
32467c478bd9Sstevel@tonic-gate 			 * If we don't understand it, it's an error.  NAK it.
32477c478bd9Sstevel@tonic-gate 			 */
32487c478bd9Sstevel@tonic-gate 			error = EINVAL;
32497c478bd9Sstevel@tonic-gate 			break;
32507c478bd9Sstevel@tonic-gate 		}
32517c478bd9Sstevel@tonic-gate 	}
32527c478bd9Sstevel@tonic-gate 	if (error != 0) {
32537c478bd9Sstevel@tonic-gate 		iocp->ioc_error = error;
32547c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCNAK;
32557c478bd9Sstevel@tonic-gate 	}
32567c478bd9Sstevel@tonic-gate 	mutex_exit(asy->asy_excl);
32577c478bd9Sstevel@tonic-gate 	qreply(wq, mp);
32587c478bd9Sstevel@tonic-gate }
32597c478bd9Sstevel@tonic-gate 
3260*e476cc14SToomas Soome static int
asyrsrv(queue_t * q)32617c478bd9Sstevel@tonic-gate asyrsrv(queue_t *q)
32627c478bd9Sstevel@tonic-gate {
32637c478bd9Sstevel@tonic-gate 	mblk_t *bp;
32647c478bd9Sstevel@tonic-gate 	struct asyncline *async;
32657c478bd9Sstevel@tonic-gate 
32667c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)q->q_ptr;
32677c478bd9Sstevel@tonic-gate 
32687c478bd9Sstevel@tonic-gate 	while (canputnext(q) && (bp = getq(q)))
32697c478bd9Sstevel@tonic-gate 		putnext(q, bp);
32707c478bd9Sstevel@tonic-gate 	ASYSETSOFT(async->async_common);
32717c478bd9Sstevel@tonic-gate 	async->async_polltid = 0;
3272*e476cc14SToomas Soome 	return (0);
32737c478bd9Sstevel@tonic-gate }
32747c478bd9Sstevel@tonic-gate 
32757c478bd9Sstevel@tonic-gate /*
32767c478bd9Sstevel@tonic-gate  * Put procedure for write queue.
32777c478bd9Sstevel@tonic-gate  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
32787c478bd9Sstevel@tonic-gate  * set the flow control character for M_STOPI and M_STARTI messages;
32797c478bd9Sstevel@tonic-gate  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
32807c478bd9Sstevel@tonic-gate  * by the start routine, and then call the start routine; discard
32817c478bd9Sstevel@tonic-gate  * everything else.  Note that this driver does not incorporate any
32827c478bd9Sstevel@tonic-gate  * mechanism to negotiate to handle the canonicalization process.
32837c478bd9Sstevel@tonic-gate  * It expects that these functions are handled in upper module(s),
32847c478bd9Sstevel@tonic-gate  * as we do in ldterm.
32857c478bd9Sstevel@tonic-gate  */
3286*e476cc14SToomas Soome static int
asywput(queue_t * q,mblk_t * mp)32877c478bd9Sstevel@tonic-gate asywput(queue_t *q, mblk_t *mp)
32887c478bd9Sstevel@tonic-gate {
32897c478bd9Sstevel@tonic-gate 	register struct asyncline *async;
32907c478bd9Sstevel@tonic-gate 	register struct asycom *asy;
32917c478bd9Sstevel@tonic-gate 	int error;
32927c478bd9Sstevel@tonic-gate 
32937c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)q->q_ptr;
32947c478bd9Sstevel@tonic-gate 	asy = async->async_common;
32957c478bd9Sstevel@tonic-gate 
32967c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
32977c478bd9Sstevel@tonic-gate 
32987c478bd9Sstevel@tonic-gate 	case M_STOP:
32997c478bd9Sstevel@tonic-gate 		/*
33007c478bd9Sstevel@tonic-gate 		 * Since we don't do real DMA, we can just let the
33017c478bd9Sstevel@tonic-gate 		 * chip coast to a stop after applying the brakes.
33027c478bd9Sstevel@tonic-gate 		 */
33037c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
33047c478bd9Sstevel@tonic-gate 		async->async_flags |= ASYNC_STOPPED;
33057c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
33067c478bd9Sstevel@tonic-gate 		freemsg(mp);
33077c478bd9Sstevel@tonic-gate 		break;
33087c478bd9Sstevel@tonic-gate 
33097c478bd9Sstevel@tonic-gate 	case M_START:
33107c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
33117c478bd9Sstevel@tonic-gate 		if (async->async_flags & ASYNC_STOPPED) {
33127c478bd9Sstevel@tonic-gate 			async->async_flags &= ~ASYNC_STOPPED;
33137c478bd9Sstevel@tonic-gate 			/*
33147c478bd9Sstevel@tonic-gate 			 * If an output operation is in progress,
33157c478bd9Sstevel@tonic-gate 			 * resume it.  Otherwise, prod the start
33167c478bd9Sstevel@tonic-gate 			 * routine.
33177c478bd9Sstevel@tonic-gate 			 */
33187c478bd9Sstevel@tonic-gate 			if (async->async_ocnt > 0) {
33197c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl_hi);
33207c478bd9Sstevel@tonic-gate 				async_resume(async);
33217c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl_hi);
33227c478bd9Sstevel@tonic-gate 			} else {
33237c478bd9Sstevel@tonic-gate 				async_start(async);
33247c478bd9Sstevel@tonic-gate 			}
33257c478bd9Sstevel@tonic-gate 		}
33267c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
33277c478bd9Sstevel@tonic-gate 		freemsg(mp);
33287c478bd9Sstevel@tonic-gate 		break;
33297c478bd9Sstevel@tonic-gate 
33307c478bd9Sstevel@tonic-gate 	case M_IOCTL:
33317c478bd9Sstevel@tonic-gate 		switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
33327c478bd9Sstevel@tonic-gate 
33337c478bd9Sstevel@tonic-gate 		case TCSBRK:
33347c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
33357c478bd9Sstevel@tonic-gate 			if (error != 0) {
33367c478bd9Sstevel@tonic-gate 				miocnak(q, mp, 0, error);
3337*e476cc14SToomas Soome 				return (0);
33387c478bd9Sstevel@tonic-gate 			}
33397c478bd9Sstevel@tonic-gate 
33407c478bd9Sstevel@tonic-gate 			if (*(int *)mp->b_cont->b_rptr != 0) {
33417c478bd9Sstevel@tonic-gate #ifdef DEBUG
33427c478bd9Sstevel@tonic-gate 				if (asydebug & ASY_DEBUG_CLOSE)
33437c478bd9Sstevel@tonic-gate 					printf("asy%d: flush request.\n",
33447c478bd9Sstevel@tonic-gate 					    UNIT(async->async_dev));
33457c478bd9Sstevel@tonic-gate #endif
33467c478bd9Sstevel@tonic-gate 				(void) putq(q, mp);
33477c478bd9Sstevel@tonic-gate 				mutex_enter(asy->asy_excl);
33487c478bd9Sstevel@tonic-gate 				async_nstart(async, 1);
33497c478bd9Sstevel@tonic-gate 				mutex_exit(asy->asy_excl);
33507c478bd9Sstevel@tonic-gate 				break;
33517c478bd9Sstevel@tonic-gate 			}
33527c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
33537c478bd9Sstevel@tonic-gate 		case TCSETSW:
33547c478bd9Sstevel@tonic-gate 		case TCSETSF:
33557c478bd9Sstevel@tonic-gate 		case TCSETAW:
33567c478bd9Sstevel@tonic-gate 		case TCSETAF:
33577c478bd9Sstevel@tonic-gate 			/*
33587c478bd9Sstevel@tonic-gate 			 * The changes do not take effect until all
33597c478bd9Sstevel@tonic-gate 			 * output queued before them is drained.
33607c478bd9Sstevel@tonic-gate 			 * Put this message on the queue, so that
33617c478bd9Sstevel@tonic-gate 			 * "async_start" will see it when it's done
33627c478bd9Sstevel@tonic-gate 			 * with the output before it.  Poke the
33637c478bd9Sstevel@tonic-gate 			 * start routine, just in case.
33647c478bd9Sstevel@tonic-gate 			 */
33657c478bd9Sstevel@tonic-gate 			(void) putq(q, mp);
33667c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
33677c478bd9Sstevel@tonic-gate 			async_start(async);
33687c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
33697c478bd9Sstevel@tonic-gate 			break;
33707c478bd9Sstevel@tonic-gate 
33717c478bd9Sstevel@tonic-gate 		default:
33727c478bd9Sstevel@tonic-gate 			/*
33737c478bd9Sstevel@tonic-gate 			 * Do it now.
33747c478bd9Sstevel@tonic-gate 			 */
33757c478bd9Sstevel@tonic-gate 			async_ioctl(async, q, mp, B_TRUE);
33767c478bd9Sstevel@tonic-gate 			break;
33777c478bd9Sstevel@tonic-gate 		}
33787c478bd9Sstevel@tonic-gate 		break;
33797c478bd9Sstevel@tonic-gate 
33807c478bd9Sstevel@tonic-gate 	case M_FLUSH:
33817c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
33827c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
33837c478bd9Sstevel@tonic-gate 
33847c478bd9Sstevel@tonic-gate 			/*
33857c478bd9Sstevel@tonic-gate 			 * Abort any output in progress.
33867c478bd9Sstevel@tonic-gate 			 */
33877c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl_hi);
33887c478bd9Sstevel@tonic-gate 			if (async->async_flags & ASYNC_BUSY) {
33897c478bd9Sstevel@tonic-gate 				async->async_ocnt = 0;
33907c478bd9Sstevel@tonic-gate 				async->async_flags &= ~ASYNC_BUSY;
33917c478bd9Sstevel@tonic-gate 			}
33927c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl_hi);
33937c478bd9Sstevel@tonic-gate 
33947c478bd9Sstevel@tonic-gate 			/* Flush FIFO buffers */
33957c478bd9Sstevel@tonic-gate 			if (asy->asy_use_fifo == FIFO_ON) {
33967c478bd9Sstevel@tonic-gate 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH |
33977c478bd9Sstevel@tonic-gate 				    (asy->asy_trig_level & 0xff));
33987c478bd9Sstevel@tonic-gate 			}
33997c478bd9Sstevel@tonic-gate 
34007c478bd9Sstevel@tonic-gate 			/*
34017c478bd9Sstevel@tonic-gate 			 * Flush our write queue.
34027c478bd9Sstevel@tonic-gate 			 */
34037c478bd9Sstevel@tonic-gate 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
34047c478bd9Sstevel@tonic-gate 			if (async->async_xmitblk != NULL) {
34057c478bd9Sstevel@tonic-gate 				freeb(async->async_xmitblk);
34067c478bd9Sstevel@tonic-gate 				async->async_xmitblk = NULL;
34077c478bd9Sstevel@tonic-gate 			}
34087c478bd9Sstevel@tonic-gate 
34097c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
34107c478bd9Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
34117c478bd9Sstevel@tonic-gate 		}
34127c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
34137c478bd9Sstevel@tonic-gate 			/* Flush FIFO buffers */
34147c478bd9Sstevel@tonic-gate 			if (asy->asy_use_fifo == FIFO_ON) {
34157c478bd9Sstevel@tonic-gate 				OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
34167c478bd9Sstevel@tonic-gate 				    (asy->asy_trig_level & 0xff));
34177c478bd9Sstevel@tonic-gate 			}
34187c478bd9Sstevel@tonic-gate 			flushq(RD(q), FLUSHDATA);
34197c478bd9Sstevel@tonic-gate 			qreply(q, mp);	/* give the read queues a crack at it */
34207c478bd9Sstevel@tonic-gate 		} else {
34217c478bd9Sstevel@tonic-gate 			freemsg(mp);
34227c478bd9Sstevel@tonic-gate 		}
34237c478bd9Sstevel@tonic-gate 
34247c478bd9Sstevel@tonic-gate 		/*
34257c478bd9Sstevel@tonic-gate 		 * We must make sure we process messages that survive the
34267c478bd9Sstevel@tonic-gate 		 * write-side flush.  Without this call, the close protocol
34277c478bd9Sstevel@tonic-gate 		 * with ldterm can hang forever.  (ldterm will have sent us a
34287c478bd9Sstevel@tonic-gate 		 * TCSBRK ioctl that it expects a response to.)
34297c478bd9Sstevel@tonic-gate 		 */
34307c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
34317c478bd9Sstevel@tonic-gate 		async_start(async);
34327c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
34337c478bd9Sstevel@tonic-gate 		break;
34347c478bd9Sstevel@tonic-gate 	case M_BREAK:
34357c478bd9Sstevel@tonic-gate 	case M_DELAY:
34367c478bd9Sstevel@tonic-gate 	case M_DATA:
34377c478bd9Sstevel@tonic-gate 		/*
34387c478bd9Sstevel@tonic-gate 		 * Queue the message up to be transmitted,
34397c478bd9Sstevel@tonic-gate 		 * and poke the start routine.
34407c478bd9Sstevel@tonic-gate 		 */
34417c478bd9Sstevel@tonic-gate 		(void) putq(q, mp);
34427c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
34437c478bd9Sstevel@tonic-gate 		async_start(async);
34447c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
34457c478bd9Sstevel@tonic-gate 		break;
34467c478bd9Sstevel@tonic-gate 
34477c478bd9Sstevel@tonic-gate 	case M_STOPI:
34487c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
34497c478bd9Sstevel@tonic-gate 		async->async_flowc = async->async_stopc;
34507c478bd9Sstevel@tonic-gate 		async_start(async);		/* poke the start routine */
34517c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
34527c478bd9Sstevel@tonic-gate 		freemsg(mp);
34537c478bd9Sstevel@tonic-gate 		break;
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	case M_STARTI:
34567c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl);
34577c478bd9Sstevel@tonic-gate 		async->async_flowc = async->async_startc;
34587c478bd9Sstevel@tonic-gate 		async_start(async);		/* poke the start routine */
34597c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
34607c478bd9Sstevel@tonic-gate 		freemsg(mp);
34617c478bd9Sstevel@tonic-gate 		break;
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate 	case M_CTL:
34647c478bd9Sstevel@tonic-gate 		if (MBLKL(mp) >= sizeof (struct iocblk) &&
34657c478bd9Sstevel@tonic-gate 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
34667c478bd9Sstevel@tonic-gate 			((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
34677c478bd9Sstevel@tonic-gate 			qreply(q, mp);
34687c478bd9Sstevel@tonic-gate 		} else {
34697c478bd9Sstevel@tonic-gate 			/*
34707c478bd9Sstevel@tonic-gate 			 * These MC_SERVICE type messages are used by upper
34717c478bd9Sstevel@tonic-gate 			 * modules to tell this driver to send input up
34727c478bd9Sstevel@tonic-gate 			 * immediately, or that it can wait for normal
34737c478bd9Sstevel@tonic-gate 			 * processing that may or may not be done.  Sun
34747c478bd9Sstevel@tonic-gate 			 * requires these for the mouse module.
34757c478bd9Sstevel@tonic-gate 			 * (XXX - for x86?)
34767c478bd9Sstevel@tonic-gate 			 */
34777c478bd9Sstevel@tonic-gate 			mutex_enter(asy->asy_excl);
34787c478bd9Sstevel@tonic-gate 			switch (*mp->b_rptr) {
34797c478bd9Sstevel@tonic-gate 
34807c478bd9Sstevel@tonic-gate 			case MC_SERVICEIMM:
34817c478bd9Sstevel@tonic-gate 				async->async_flags |= ASYNC_SERVICEIMM;
34827c478bd9Sstevel@tonic-gate 				break;
34837c478bd9Sstevel@tonic-gate 
34847c478bd9Sstevel@tonic-gate 			case MC_SERVICEDEF:
34857c478bd9Sstevel@tonic-gate 				async->async_flags &= ~ASYNC_SERVICEIMM;
34867c478bd9Sstevel@tonic-gate 				break;
34877c478bd9Sstevel@tonic-gate 			}
34887c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
34897c478bd9Sstevel@tonic-gate 			freemsg(mp);
34907c478bd9Sstevel@tonic-gate 		}
34917c478bd9Sstevel@tonic-gate 		break;
34927c478bd9Sstevel@tonic-gate 
34937c478bd9Sstevel@tonic-gate 	case M_IOCDATA:
34947c478bd9Sstevel@tonic-gate 		async_iocdata(q, mp);
34957c478bd9Sstevel@tonic-gate 		break;
34967c478bd9Sstevel@tonic-gate 
34977c478bd9Sstevel@tonic-gate 	default:
34987c478bd9Sstevel@tonic-gate 		freemsg(mp);
34997c478bd9Sstevel@tonic-gate 		break;
35007c478bd9Sstevel@tonic-gate 	}
3501*e476cc14SToomas Soome 	return (0);
35027c478bd9Sstevel@tonic-gate }
35037c478bd9Sstevel@tonic-gate 
35047c478bd9Sstevel@tonic-gate /*
35057c478bd9Sstevel@tonic-gate  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
35067c478bd9Sstevel@tonic-gate  * the buffer we need.
35077c478bd9Sstevel@tonic-gate  */
35087c478bd9Sstevel@tonic-gate static void
async_reioctl(void * arg)35097c478bd9Sstevel@tonic-gate async_reioctl(void *arg)
35107c478bd9Sstevel@tonic-gate {
35117c478bd9Sstevel@tonic-gate 	struct asyncline *async = arg;
35127c478bd9Sstevel@tonic-gate 	struct asycom *asy = async->async_common;
35137c478bd9Sstevel@tonic-gate 	queue_t	*q;
35147c478bd9Sstevel@tonic-gate 	mblk_t		*mp;
35157c478bd9Sstevel@tonic-gate 
35167c478bd9Sstevel@tonic-gate 	/*
35177c478bd9Sstevel@tonic-gate 	 * The bufcall is no longer pending.
35187c478bd9Sstevel@tonic-gate 	 */
35197c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
35207c478bd9Sstevel@tonic-gate 	async->async_wbufcid = 0;
35217c478bd9Sstevel@tonic-gate 	if ((q = async->async_ttycommon.t_writeq) == NULL) {
35227c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35237c478bd9Sstevel@tonic-gate 		return;
35247c478bd9Sstevel@tonic-gate 	}
35257c478bd9Sstevel@tonic-gate 	if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
35267c478bd9Sstevel@tonic-gate 		/* not pending any more */
35277c478bd9Sstevel@tonic-gate 		async->async_ttycommon.t_iocpending = NULL;
35287c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35297c478bd9Sstevel@tonic-gate 		/* not in STREAMS queue; we no longer know if we're in wput */
35307c478bd9Sstevel@tonic-gate 		async_ioctl(async, q, mp, B_TRUE);
35317c478bd9Sstevel@tonic-gate 	} else
35327c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35337c478bd9Sstevel@tonic-gate }
35347c478bd9Sstevel@tonic-gate 
35357c478bd9Sstevel@tonic-gate static void
async_iocdata(queue_t * q,mblk_t * mp)35367c478bd9Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp)
35377c478bd9Sstevel@tonic-gate {
35387c478bd9Sstevel@tonic-gate 	struct asyncline	*async = (struct asyncline *)q->q_ptr;
35397c478bd9Sstevel@tonic-gate 	struct asycom		*asy;
35407c478bd9Sstevel@tonic-gate 	struct copyresp *csp;
35417c478bd9Sstevel@tonic-gate 
35427c478bd9Sstevel@tonic-gate 	asy = async->async_common;
35437c478bd9Sstevel@tonic-gate 	csp = (struct copyresp *)mp->b_rptr;
35447c478bd9Sstevel@tonic-gate 
35457c478bd9Sstevel@tonic-gate 	if (csp->cp_rval != 0) {
35467c478bd9Sstevel@tonic-gate 		freemsg(mp);
35477c478bd9Sstevel@tonic-gate 		return;
35487c478bd9Sstevel@tonic-gate 	}
35497c478bd9Sstevel@tonic-gate 
35507c478bd9Sstevel@tonic-gate 	mutex_enter(asy->asy_excl);
35517c478bd9Sstevel@tonic-gate 
35527c478bd9Sstevel@tonic-gate 	switch (csp->cp_cmd) {
35537c478bd9Sstevel@tonic-gate 	case TIOCMSET:
35547c478bd9Sstevel@tonic-gate 	case TIOCMBIS:
35557c478bd9Sstevel@tonic-gate 	case TIOCMBIC:
35567c478bd9Sstevel@tonic-gate 		if (mp->b_cont == NULL) {
35577c478bd9Sstevel@tonic-gate 			mutex_exit(asy->asy_excl);
35587c478bd9Sstevel@tonic-gate 			miocnak(q, mp, 0, EINVAL);
35597c478bd9Sstevel@tonic-gate 			break;
35607c478bd9Sstevel@tonic-gate 		}
35617c478bd9Sstevel@tonic-gate 
35627c478bd9Sstevel@tonic-gate 		mutex_enter(asy->asy_excl_hi);
35637c478bd9Sstevel@tonic-gate 		(void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
35640280efdcSzk 		    csp->cp_cmd);
35657c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl_hi);
35667c478bd9Sstevel@tonic-gate 
35677c478bd9Sstevel@tonic-gate 		freemsg(mp->b_cont);
35687c478bd9Sstevel@tonic-gate 		mp->b_cont = NULL;
35697c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35707c478bd9Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
35717c478bd9Sstevel@tonic-gate 		break;
35727c478bd9Sstevel@tonic-gate 
35737c478bd9Sstevel@tonic-gate 	case TIOCMGET:
35747c478bd9Sstevel@tonic-gate 		if (mp->b_cont != NULL) {
35757c478bd9Sstevel@tonic-gate 			freemsg(mp->b_cont);
35767c478bd9Sstevel@tonic-gate 			mp->b_cont = NULL;
35777c478bd9Sstevel@tonic-gate 		}
35787c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35797c478bd9Sstevel@tonic-gate 		miocack(q, mp, 0, 0);
35807c478bd9Sstevel@tonic-gate 		break;
35817c478bd9Sstevel@tonic-gate 
35827c478bd9Sstevel@tonic-gate 	default:
35837c478bd9Sstevel@tonic-gate 		mutex_exit(asy->asy_excl);
35847c478bd9Sstevel@tonic-gate 		miocnak(q, mp, 0, EINVAL);
35857c478bd9Sstevel@tonic-gate 		break;
35867c478bd9Sstevel@tonic-gate 	}
35877c478bd9Sstevel@tonic-gate }
35887c478bd9Sstevel@tonic-gate 
35897c478bd9Sstevel@tonic-gate 
35907c478bd9Sstevel@tonic-gate /*
35917c478bd9Sstevel@tonic-gate  * Set or get the modem control status.
35927c478bd9Sstevel@tonic-gate  */
35937c478bd9Sstevel@tonic-gate static int
asymctl(struct asycom * asy,int bits,int how)35947c478bd9Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how)
35957c478bd9Sstevel@tonic-gate {
35967c478bd9Sstevel@tonic-gate 	register int mcr_r, msr_r;
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
35997c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl));
36007c478bd9Sstevel@tonic-gate 
36017c478bd9Sstevel@tonic-gate 	/* Read Modem Control Registers */
36027c478bd9Sstevel@tonic-gate 	mcr_r = INB(MCR);
36037c478bd9Sstevel@tonic-gate 
36047c478bd9Sstevel@tonic-gate 	switch (how) {
36057c478bd9Sstevel@tonic-gate 
36067c478bd9Sstevel@tonic-gate 	case TIOCMSET:
36077c478bd9Sstevel@tonic-gate 		mcr_r = bits;
36087c478bd9Sstevel@tonic-gate 		break;
36097c478bd9Sstevel@tonic-gate 
36107c478bd9Sstevel@tonic-gate 	case TIOCMBIS:
36117c478bd9Sstevel@tonic-gate 		mcr_r |= bits;			/* Set bits from input	*/
36127c478bd9Sstevel@tonic-gate 		break;
36137c478bd9Sstevel@tonic-gate 
36147c478bd9Sstevel@tonic-gate 	case TIOCMBIC:
36157c478bd9Sstevel@tonic-gate 		mcr_r &= ~bits;			/* Set ~bits from input	*/
36167c478bd9Sstevel@tonic-gate 		break;
36177c478bd9Sstevel@tonic-gate 
36187c478bd9Sstevel@tonic-gate 	case TIOCMGET:
36197c478bd9Sstevel@tonic-gate 		/* Read Modem Status Registers */
36207c478bd9Sstevel@tonic-gate 		if (INB(ICR) & MIEN)
36217c478bd9Sstevel@tonic-gate 			msr_r = asy->asy_cached_msr;
36227c478bd9Sstevel@tonic-gate 		else
36237c478bd9Sstevel@tonic-gate 			msr_r = INB(MSR);
36247c478bd9Sstevel@tonic-gate 		return (asytodm(mcr_r, msr_r));
36257c478bd9Sstevel@tonic-gate 	}
36267c478bd9Sstevel@tonic-gate 
36277c478bd9Sstevel@tonic-gate 	OUTB(MCR, mcr_r);
36287c478bd9Sstevel@tonic-gate 
36297c478bd9Sstevel@tonic-gate 	return (mcr_r);
36307c478bd9Sstevel@tonic-gate }
36317c478bd9Sstevel@tonic-gate 
36327c478bd9Sstevel@tonic-gate static int
asytodm(int mcr_r,int msr_r)36337c478bd9Sstevel@tonic-gate asytodm(int mcr_r, int msr_r)
36347c478bd9Sstevel@tonic-gate {
36357c478bd9Sstevel@tonic-gate 	register int b = 0;
36367c478bd9Sstevel@tonic-gate 
36377c478bd9Sstevel@tonic-gate 
36387c478bd9Sstevel@tonic-gate 	/* MCR registers */
36397c478bd9Sstevel@tonic-gate 	if (mcr_r & RTS)
36407c478bd9Sstevel@tonic-gate 		b |= TIOCM_RTS;
36417c478bd9Sstevel@tonic-gate 
36427c478bd9Sstevel@tonic-gate 	if (mcr_r & DTR)
36437c478bd9Sstevel@tonic-gate 		b |= TIOCM_DTR;
36447c478bd9Sstevel@tonic-gate 
36457c478bd9Sstevel@tonic-gate 	/* MSR registers */
36467c478bd9Sstevel@tonic-gate 	if (msr_r & DCD)
36477c478bd9Sstevel@tonic-gate 		b |= TIOCM_CAR;
36487c478bd9Sstevel@tonic-gate 
36497c478bd9Sstevel@tonic-gate 	if (msr_r & CTS)
36507c478bd9Sstevel@tonic-gate 		b |= TIOCM_CTS;
36517c478bd9Sstevel@tonic-gate 
36527c478bd9Sstevel@tonic-gate 	if (msr_r & DSR)
36537c478bd9Sstevel@tonic-gate 		b |= TIOCM_DSR;
36547c478bd9Sstevel@tonic-gate 
36557c478bd9Sstevel@tonic-gate 	if (msr_r & RI)
36567c478bd9Sstevel@tonic-gate 		b |= TIOCM_RNG;
36577c478bd9Sstevel@tonic-gate 
36587c478bd9Sstevel@tonic-gate 	return (b);
36597c478bd9Sstevel@tonic-gate }
36607c478bd9Sstevel@tonic-gate 
36617c478bd9Sstevel@tonic-gate static int
dmtoasy(int bits)36627c478bd9Sstevel@tonic-gate dmtoasy(int bits)
36637c478bd9Sstevel@tonic-gate {
36647c478bd9Sstevel@tonic-gate 	register int b = 0;
36657c478bd9Sstevel@tonic-gate 
36667c478bd9Sstevel@tonic-gate #ifdef	CAN_NOT_SET	/* only DTR and RTS can be set */
36677c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_CAR)
36687c478bd9Sstevel@tonic-gate 		b |= DCD;
36697c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_CTS)
36707c478bd9Sstevel@tonic-gate 		b |= CTS;
36717c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_DSR)
36727c478bd9Sstevel@tonic-gate 		b |= DSR;
36737c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_RNG)
36747c478bd9Sstevel@tonic-gate 		b |= RI;
36757c478bd9Sstevel@tonic-gate #endif
36767c478bd9Sstevel@tonic-gate 
36777c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_RTS)
36787c478bd9Sstevel@tonic-gate 		b |= RTS;
36797c478bd9Sstevel@tonic-gate 	if (bits & TIOCM_DTR)
36807c478bd9Sstevel@tonic-gate 		b |= DTR;
36817c478bd9Sstevel@tonic-gate 
36827c478bd9Sstevel@tonic-gate 	return (b);
36837c478bd9Sstevel@tonic-gate }
36847c478bd9Sstevel@tonic-gate 
36857c478bd9Sstevel@tonic-gate static void
asycheckflowcontrol_hw(struct asycom * asy)36867c478bd9Sstevel@tonic-gate asycheckflowcontrol_hw(struct asycom *asy)
36877c478bd9Sstevel@tonic-gate {
36887c478bd9Sstevel@tonic-gate 	struct asyncline *async;
36897c478bd9Sstevel@tonic-gate 	uchar_t	mcr, flag;
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
36927c478bd9Sstevel@tonic-gate 
36937c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)asy->asy_priv;
36947c478bd9Sstevel@tonic-gate 	ASSERT(async != NULL);
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate 	if (async->async_ttycommon.t_cflag & CRTSXOFF) {
36977c478bd9Sstevel@tonic-gate 		mcr = INB(MCR);
36987c478bd9Sstevel@tonic-gate 		flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
36997c478bd9Sstevel@tonic-gate 		if (((mcr ^ flag) & RTS) != 0) {
37007c478bd9Sstevel@tonic-gate 			OUTB(MCR, (mcr ^ RTS));
37017c478bd9Sstevel@tonic-gate 		}
37027c478bd9Sstevel@tonic-gate 	}
37037c478bd9Sstevel@tonic-gate }
37047c478bd9Sstevel@tonic-gate 
37057c478bd9Sstevel@tonic-gate static boolean_t
asycheckflowcontrol_sw(struct asycom * asy)37067c478bd9Sstevel@tonic-gate asycheckflowcontrol_sw(struct asycom *asy)
37077c478bd9Sstevel@tonic-gate {
37087c478bd9Sstevel@tonic-gate 	uchar_t		ss;
37097c478bd9Sstevel@tonic-gate 	struct asyncline *async;
37107c478bd9Sstevel@tonic-gate 	int rval = B_FALSE;
37117c478bd9Sstevel@tonic-gate 
37127c478bd9Sstevel@tonic-gate 	ASSERT(mutex_owned(asy->asy_excl_hi));
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate 	async = (struct asyncline *)asy->asy_priv;
37157c478bd9Sstevel@tonic-gate 	ASSERT(async != NULL);
37167c478bd9Sstevel@tonic-gate 
37177c478bd9Sstevel@tonic-gate 	if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) {
37187c478bd9Sstevel@tonic-gate 		/*
37197c478bd9Sstevel@tonic-gate 		 * If we get this far, then we know that flowc is non-zero and
37207c478bd9Sstevel@tonic-gate 		 * that there's transmit room available.  We've "handled" the
37217c478bd9Sstevel@tonic-gate 		 * request now, so clear it.  If the user didn't ask for IXOFF,
37227c478bd9Sstevel@tonic-gate 		 * then don't actually send anything, but wait for the next
37237c478bd9Sstevel@tonic-gate 		 * opportunity.
37247c478bd9Sstevel@tonic-gate 		 */
37257c478bd9Sstevel@tonic-gate 		async->async_flowc = '\0';
37267c478bd9Sstevel@tonic-gate 		if (async->async_ttycommon.t_iflag & IXOFF) {
37277c478bd9Sstevel@tonic-gate 			async->async_flags |= ASYNC_BUSY;
37287c478bd9Sstevel@tonic-gate 			OUTB(DAT, ss);
37297c478bd9Sstevel@tonic-gate 			rval = B_TRUE;
37307c478bd9Sstevel@tonic-gate 		}
37317c478bd9Sstevel@tonic-gate 	}
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 	return (rval);
37347c478bd9Sstevel@tonic-gate }
37357c478bd9Sstevel@tonic-gate 
37367c478bd9Sstevel@tonic-gate /*
37377c478bd9Sstevel@tonic-gate  * Check for abort character sequence
37387c478bd9Sstevel@tonic-gate  */
37397c478bd9Sstevel@tonic-gate static boolean_t
abort_charseq_recognize(uchar_t ch)37407c478bd9Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch)
37417c478bd9Sstevel@tonic-gate {
37427c478bd9Sstevel@tonic-gate 	static int state = 0;
37437c478bd9Sstevel@tonic-gate #define	CNTRL(c) ((c)&037)
37447c478bd9Sstevel@tonic-gate 	static char sequence[] = { '\r', '~', CNTRL('b') };
37457c478bd9Sstevel@tonic-gate 
37467c478bd9Sstevel@tonic-gate 	if (ch == sequence[state]) {
37477c478bd9Sstevel@tonic-gate 		if (++state >= sizeof (sequence)) {
37487c478bd9Sstevel@tonic-gate 			state = 0;
37497c478bd9Sstevel@tonic-gate 			return (B_TRUE);
37507c478bd9Sstevel@tonic-gate 		}
37517c478bd9Sstevel@tonic-gate 	} else {
37527c478bd9Sstevel@tonic-gate 		state = (ch == sequence[0]) ? 1 : 0;
37537c478bd9Sstevel@tonic-gate 	}
37547c478bd9Sstevel@tonic-gate 	return (B_FALSE);
37557c478bd9Sstevel@tonic-gate }
3756