xref: /onnv-gate/usr/src/uts/sun/io/zs_async.c (revision 796:e9b20e3531f8)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*796Smathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  *	Asynchronous protocol handler for Z8530 chips
310Sstevel@tonic-gate  *	Handles normal UNIX support for terminals & modems
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/param.h>
360Sstevel@tonic-gate #include <sys/systm.h>
370Sstevel@tonic-gate #include <sys/sysmacros.h>
380Sstevel@tonic-gate #include <sys/signal.h>
390Sstevel@tonic-gate #include <sys/kmem.h>
400Sstevel@tonic-gate #include <sys/termios.h>
410Sstevel@tonic-gate #include <sys/stropts.h>
420Sstevel@tonic-gate #include <sys/stream.h>
430Sstevel@tonic-gate #include <sys/strsun.h>
440Sstevel@tonic-gate #include <sys/tty.h>
450Sstevel@tonic-gate #include <sys/ptyvar.h>
460Sstevel@tonic-gate #include <sys/cred.h>
470Sstevel@tonic-gate #include <sys/user.h>
480Sstevel@tonic-gate #include <sys/proc.h>
490Sstevel@tonic-gate #include <sys/file.h>
500Sstevel@tonic-gate #include <sys/uio.h>
510Sstevel@tonic-gate #include <sys/buf.h>
520Sstevel@tonic-gate #include <sys/mkdev.h>
530Sstevel@tonic-gate #include <sys/cmn_err.h>
540Sstevel@tonic-gate #include <sys/strtty.h>
550Sstevel@tonic-gate #include <sys/consdev.h>
560Sstevel@tonic-gate #include <sys/zsdev.h>
570Sstevel@tonic-gate #include <sys/ser_async.h>
580Sstevel@tonic-gate #include <sys/debug.h>
590Sstevel@tonic-gate #include <sys/kbio.h>
600Sstevel@tonic-gate #include <sys/conf.h>
610Sstevel@tonic-gate #include <sys/ddi.h>
620Sstevel@tonic-gate #include <sys/sunddi.h>
630Sstevel@tonic-gate #include <sys/promif.h>
640Sstevel@tonic-gate #include <sys/policy.h>
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /*
670Sstevel@tonic-gate  * PPS (Pulse Per Second) support.
680Sstevel@tonic-gate  */
690Sstevel@tonic-gate extern void ddi_hardpps(struct timeval *, int);
700Sstevel@tonic-gate static struct ppsclockev ppsclockev;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate #ifdef PPSCLOCKLED
730Sstevel@tonic-gate /* XXX Use these to observe PPS latencies and jitter on a scope */
740Sstevel@tonic-gate #define	LED_ON
750Sstevel@tonic-gate #define	LED_OFF
760Sstevel@tonic-gate #else
770Sstevel@tonic-gate #define	LED_ON
780Sstevel@tonic-gate #define	LED_OFF
790Sstevel@tonic-gate #endif
800Sstevel@tonic-gate 
810Sstevel@tonic-gate #define	ZSA_RCV_SIZE	64
820Sstevel@tonic-gate #define	ZA_KICK_RCV_COUNT	3
830Sstevel@tonic-gate #define	ZSA_GRACE_MIN_FLOW_CONTROL	5
840Sstevel@tonic-gate #define	ZSA_GRACE_MAX_FLOW_CONTROL	20
850Sstevel@tonic-gate 
860Sstevel@tonic-gate int zsasoftdtr = 0;	/* if nonzero, softcarrier raises dtr at attach */
870Sstevel@tonic-gate int zsb134_weird = 0;	/* if set, old weird B134 behavior */
880Sstevel@tonic-gate int g_zsticks = 0;	/* if set, becomes the global zsticks value */
890Sstevel@tonic-gate int g_nocluster = 0;	/* if set, disables clustering of received data */
900Sstevel@tonic-gate 
910Sstevel@tonic-gate unsigned int zsa_rstandby = ZSA_MIN_RSTANDBY;
920Sstevel@tonic-gate unsigned int zsa_rdone = ZSA_RDONE_MIN;
930Sstevel@tonic-gate unsigned int zsa_grace_flow_control = ZSA_GRACE_MIN_FLOW_CONTROL;
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 
960Sstevel@tonic-gate #define	NSPEED	18	/* max # of speeds */
970Sstevel@tonic-gate ushort_t zs_speeds[NSPEED] = {
980Sstevel@tonic-gate 	0,
990Sstevel@tonic-gate 	ZSPEED(50),	/* 1 */
1000Sstevel@tonic-gate 	ZSPEED(75),	/* 2 */
1010Sstevel@tonic-gate 	ZSPEED(110),	/* 3 */
1020Sstevel@tonic-gate #ifdef lint
1030Sstevel@tonic-gate 	ZSPEED(134),	/* 4 */
1040Sstevel@tonic-gate #else
1050Sstevel@tonic-gate 	ZSPEED(269/2),			/* XXX - This is sleazy */
1060Sstevel@tonic-gate #endif
1070Sstevel@tonic-gate 	ZSPEED(150),	/* 5 */
1080Sstevel@tonic-gate 	ZSPEED(200),	/* 6 */
1090Sstevel@tonic-gate 	ZSPEED(300),	/* 7 */
1100Sstevel@tonic-gate 	ZSPEED(600),	/* 8 */
1110Sstevel@tonic-gate 	ZSPEED(1200),	/* 9 */
1120Sstevel@tonic-gate 	ZSPEED(1800),	/* 10 */
1130Sstevel@tonic-gate 	ZSPEED(2400),	/* 11 */
1140Sstevel@tonic-gate 	ZSPEED(4800),	/* 12 */
1150Sstevel@tonic-gate 	ZSPEED(9600),	/* 13 */
1160Sstevel@tonic-gate 	ZSPEED(19200),	/* 14 */
1170Sstevel@tonic-gate 	ZSPEED(38400),	/* 15 */
1180Sstevel@tonic-gate 	ZSPEED(57680),	/* 16 */
1190Sstevel@tonic-gate 	ZSPEED(76800)	/* 17 */
1200Sstevel@tonic-gate };
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate ushort_t zsticks[NSPEED] = {
1230Sstevel@tonic-gate 	3,		/* 0 */
1240Sstevel@tonic-gate 	3,		/* 1 */
1250Sstevel@tonic-gate 	3,		/* 2 */
1260Sstevel@tonic-gate 	3,		/* 3 */
1270Sstevel@tonic-gate 	3,		/* 4 */
1280Sstevel@tonic-gate 	3,		/* 5 */
1290Sstevel@tonic-gate 	3,		/* 6 */
1300Sstevel@tonic-gate 	3,		/* 7 */
1310Sstevel@tonic-gate 	3,		/* 8 */
1320Sstevel@tonic-gate 	3,		/* 9 */
1330Sstevel@tonic-gate 	3,		/* 10 */
1340Sstevel@tonic-gate 	3,		/* 11 */
1350Sstevel@tonic-gate 	3,		/* 12 */
1360Sstevel@tonic-gate 	3,		/* 13 */
1370Sstevel@tonic-gate 	2,		/* 14 */
1380Sstevel@tonic-gate 	1,		/* 15 */
1390Sstevel@tonic-gate 	1,		/* 16 */
1400Sstevel@tonic-gate 	1		/* 17 */
1410Sstevel@tonic-gate };
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate #define	ztdelay(nsp)	(zsdelay[(nsp)]*(hz/100))
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate ushort_t zsdelay[NSPEED] = {
1460Sstevel@tonic-gate 	0,
1470Sstevel@tonic-gate 	ZDELAY(50),	/* 1 */
1480Sstevel@tonic-gate 	ZDELAY(75),	/* 2 */
1490Sstevel@tonic-gate 	ZDELAY(110),    /* 3 */
1500Sstevel@tonic-gate #ifdef lint
1510Sstevel@tonic-gate 	ZDELAY(134),    /* 4 */
1520Sstevel@tonic-gate #else
1530Sstevel@tonic-gate 	ZDELAY(269/2),
1540Sstevel@tonic-gate #endif
1550Sstevel@tonic-gate 	ZDELAY(150),    /* 5 */
1560Sstevel@tonic-gate 	ZDELAY(200),    /* 6 */
1570Sstevel@tonic-gate 	ZDELAY(300),    /* 7 */
1580Sstevel@tonic-gate 	ZDELAY(600),    /* 8 */
1590Sstevel@tonic-gate 	ZDELAY(1200),   /* 9 */
1600Sstevel@tonic-gate 	ZDELAY(1800),   /* 10 */
1610Sstevel@tonic-gate 	ZDELAY(2400),   /* 11 */
1620Sstevel@tonic-gate 	ZDELAY(4800),   /* 12 */
1630Sstevel@tonic-gate 	ZDELAY(9600),   /* 13 */
1640Sstevel@tonic-gate 	ZDELAY(19200),  /* 14 */
1650Sstevel@tonic-gate 	ZDELAY(38400),  /* 15 */
1660Sstevel@tonic-gate 	ZDELAY(57600),  /* 16 */
1670Sstevel@tonic-gate 	ZDELAY(76800)	/* 17 */
1680Sstevel@tonic-gate };
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate ushort_t zslowat[NSPEED] = {
1710Sstevel@tonic-gate 	3,		/* 0 */
1720Sstevel@tonic-gate 	3,		/* 1 */
1730Sstevel@tonic-gate 	3,		/* 2 */
1740Sstevel@tonic-gate 	3,		/* 3 */
1750Sstevel@tonic-gate 	3,		/* 4 */
1760Sstevel@tonic-gate 	3,		/* 5 */
1770Sstevel@tonic-gate 	3,		/* 6 */
1780Sstevel@tonic-gate 	2,		/* 7 */
1790Sstevel@tonic-gate 	2,		/* 8 */
1800Sstevel@tonic-gate 	2,		/* 9 */
1810Sstevel@tonic-gate 	2,		/* 10 */
1820Sstevel@tonic-gate 	1,		/* 11 */
1830Sstevel@tonic-gate 	1,		/* 12 */
1840Sstevel@tonic-gate 	1,		/* 13 */
1850Sstevel@tonic-gate 	1,		/* 14 */
1860Sstevel@tonic-gate 	1,		/* 15 */
1870Sstevel@tonic-gate 	1,		/* 16 */
1880Sstevel@tonic-gate 	1		/* 17 */
1890Sstevel@tonic-gate };
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate ushort_t zshiwat[NSPEED] = {
1920Sstevel@tonic-gate 	0,		/* 0 */
1930Sstevel@tonic-gate 	1,		/* 1 */
1940Sstevel@tonic-gate 	1,		/* 2 */
1950Sstevel@tonic-gate 	1,		/* 3 */
1960Sstevel@tonic-gate 	1,		/* 4 */
1970Sstevel@tonic-gate 	1,		/* 5 */
1980Sstevel@tonic-gate 	1,		/* 6 */
1990Sstevel@tonic-gate 	1,		/* 7 */
2000Sstevel@tonic-gate 	1,		/* 8 */
2010Sstevel@tonic-gate 	1,		/* 9 */
2020Sstevel@tonic-gate 	1,		/* 10 */
2030Sstevel@tonic-gate 	1,		/* 11 */
2040Sstevel@tonic-gate 	1,		/* 12 */
2050Sstevel@tonic-gate 	3,		/* 13 */
2060Sstevel@tonic-gate 	3,		/* 14 */
2070Sstevel@tonic-gate 	4,		/* 15 */
2080Sstevel@tonic-gate 	4,		/* 16 */
2090Sstevel@tonic-gate 	4		/* 17 */
2100Sstevel@tonic-gate };
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate #define	SLAVIO_BUG	/* this workaround required to fix bug 1102778 */
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate #define	SPEED(cflag) \
2150Sstevel@tonic-gate 	((cflag) & CBAUDEXT) ? \
2160Sstevel@tonic-gate 		(((cflag) & 0x1) + CBAUD + 1) : ((cflag) & CBAUD)
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate /*
2190Sstevel@tonic-gate  * Special macros to handle STREAMS operations.
2200Sstevel@tonic-gate  * These are required to address memory leakage problems.
2210Sstevel@tonic-gate  * WARNING : the macro do NOT call ZSSETSOFT
2220Sstevel@tonic-gate  */
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate /*
2250Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
2260Sstevel@tonic-gate  */
2270Sstevel@tonic-gate #define	ZSA_GETBLOCK(zs, allocbcount) \
2280Sstevel@tonic-gate { \
2290Sstevel@tonic-gate 	register int n = zsa_rstandby; \
2300Sstevel@tonic-gate 	while (--n >= 0 && allocbcount > 0) { \
2310Sstevel@tonic-gate 		if (!za->za_rstandby[n]) { \
2320Sstevel@tonic-gate 			if ((za->za_rstandby[n] = allocb(ZSA_RCV_SIZE, \
2330Sstevel@tonic-gate 			    BPRI_MED)) == NULL) { \
2340Sstevel@tonic-gate 				if (za->za_bufcid == 0) { \
2350Sstevel@tonic-gate 					za->za_bufcid = bufcall(ZSA_RCV_SIZE, \
2360Sstevel@tonic-gate 					    BPRI_MED, \
2370Sstevel@tonic-gate 					    zsa_callback, zs); \
2380Sstevel@tonic-gate 					break; \
2390Sstevel@tonic-gate 				} \
2400Sstevel@tonic-gate 			} \
2410Sstevel@tonic-gate 			allocbcount--; \
2420Sstevel@tonic-gate 		} \
2430Sstevel@tonic-gate 	} \
2440Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSXOFF) { \
2450Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi); \
2460Sstevel@tonic-gate 		if (!(zs->zs_wreg[5] & ZSWR5_RTS)) { \
2470Sstevel@tonic-gate 			register int usedcnt = 0; \
2480Sstevel@tonic-gate 			for (n = 0; n < zsa_rstandby; n++) \
2490Sstevel@tonic-gate 				if (!za->za_rstandby[n]) \
2500Sstevel@tonic-gate 					usedcnt++; \
2510Sstevel@tonic-gate 			if ((ushort_t)usedcnt <= \
2520Sstevel@tonic-gate 			    zslowat[SPEED(za->za_ttycommon.t_cflag)]) \
2530Sstevel@tonic-gate 				SCC_BIS(5, ZSWR5_RTS); \
2540Sstevel@tonic-gate 		} \
2550Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi); \
2560Sstevel@tonic-gate 	} \
2570Sstevel@tonic-gate }
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate /*
2600Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
2610Sstevel@tonic-gate  */
2620Sstevel@tonic-gate #define	ZSA_ALLOCB(mp) \
2630Sstevel@tonic-gate { \
2640Sstevel@tonic-gate 	register int n = zsa_rstandby; \
2650Sstevel@tonic-gate 	while (--n >= 0) { \
2660Sstevel@tonic-gate 		if ((mp = za->za_rstandby[n]) != NULL) { \
2670Sstevel@tonic-gate 			za->za_rstandby[n] = NULL; \
2680Sstevel@tonic-gate 			break; \
2690Sstevel@tonic-gate 		} \
2700Sstevel@tonic-gate 	} \
2710Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSXOFF) { \
2720Sstevel@tonic-gate 		if (!mp) { \
2730Sstevel@tonic-gate 			if (zs->zs_wreg[5] & ZSWR5_RTS) \
2740Sstevel@tonic-gate 				SCC_BIC(5, ZSWR5_RTS); \
2750Sstevel@tonic-gate 			cmn_err(CE_WARN, "zs%d: message lost\n", \
2760Sstevel@tonic-gate 				UNIT(za->za_dev)); \
2770Sstevel@tonic-gate 		} else if (zs->zs_wreg[5] & ZSWR5_RTS) { \
2780Sstevel@tonic-gate 			register int usedcnt = 0; \
2790Sstevel@tonic-gate 			for (n = 0; n < zsa_rstandby; n++) \
2800Sstevel@tonic-gate 				if (!za->za_rstandby[n]) \
2810Sstevel@tonic-gate 					usedcnt++; \
2820Sstevel@tonic-gate 			if ((ushort_t)usedcnt >= (zsa_rstandby - \
2830Sstevel@tonic-gate 			    zshiwat[SPEED(za->za_ttycommon.t_cflag)])) \
2840Sstevel@tonic-gate 				SCC_BIC(5, ZSWR5_RTS); \
2850Sstevel@tonic-gate 		} \
2860Sstevel@tonic-gate 	} \
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate /*
2900Sstevel@tonic-gate  * Should get the spin (zs_excl_hi) mutex.
2910Sstevel@tonic-gate  */
2920Sstevel@tonic-gate #define	ZSA_QREPLY(q, mp) \
2930Sstevel@tonic-gate { \
2940Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi); \
2950Sstevel@tonic-gate 	ZSA_PUTQ(mp); \
2960Sstevel@tonic-gate 	ZSSETSOFT(zs); \
2970Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi); \
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
3020Sstevel@tonic-gate  */
3030Sstevel@tonic-gate #define	ZSA_PUTQ(mp) \
3040Sstevel@tonic-gate { \
3050Sstevel@tonic-gate 	register int wptr, rptr; \
3060Sstevel@tonic-gate 	wptr = za->za_rdone_wptr; \
3070Sstevel@tonic-gate 	rptr = za->za_rdone_rptr; \
3080Sstevel@tonic-gate 	za->za_rdone[wptr] = mp; \
3090Sstevel@tonic-gate 	if ((wptr)+1 == zsa_rdone) { \
3100Sstevel@tonic-gate 		za->za_rdone_wptr = wptr = 0; \
3110Sstevel@tonic-gate 	} else \
3120Sstevel@tonic-gate 		za->za_rdone_wptr = ++wptr; \
3130Sstevel@tonic-gate 	if (wptr == rptr) { \
3140Sstevel@tonic-gate 		SCC_BIC(1, ZSWR1_INIT); \
3150Sstevel@tonic-gate 		cmn_err(CE_WARN, "zs%d disabled: input buffer overflow", \
3160Sstevel@tonic-gate 			UNIT(za->za_dev)); \
3170Sstevel@tonic-gate 	} \
3180Sstevel@tonic-gate }
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
3220Sstevel@tonic-gate  */
3230Sstevel@tonic-gate #define	ZSA_KICK_RCV \
3240Sstevel@tonic-gate { \
3250Sstevel@tonic-gate 	register mblk_t *mp = za->za_rcvblk; \
3260Sstevel@tonic-gate 	if (mp) { \
3270Sstevel@tonic-gate 		if (zs->zs_rd_cur) {	/* M_DATA */ \
3280Sstevel@tonic-gate 			mp->b_wptr = zs->zs_rd_cur; \
3290Sstevel@tonic-gate 			zs->zs_rd_cur = NULL; \
3300Sstevel@tonic-gate 			zs->zs_rd_lim = NULL; \
3310Sstevel@tonic-gate 		} \
3320Sstevel@tonic-gate 		za->za_rcvblk = NULL; \
3330Sstevel@tonic-gate 		ZSA_PUTQ(mp); \
3340Sstevel@tonic-gate 		ZSSETSOFT(zs); \
3350Sstevel@tonic-gate 	} \
3360Sstevel@tonic-gate }
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate #define	ZSA_SEEQ(mp) \
3390Sstevel@tonic-gate { \
3400Sstevel@tonic-gate 		if (za->za_rdone_rptr != za->za_rdone_wptr) { \
3410Sstevel@tonic-gate 			mp = za->za_rdone[za->za_rdone_rptr]; \
3420Sstevel@tonic-gate 		} else { \
3430Sstevel@tonic-gate 			mp = NULL; \
3440Sstevel@tonic-gate 		} \
3450Sstevel@tonic-gate }
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate /*
3490Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
3500Sstevel@tonic-gate  */
3510Sstevel@tonic-gate #define	ZSA_GETQ(mp) \
3520Sstevel@tonic-gate { \
3530Sstevel@tonic-gate 	if (za->za_rdone_rptr != za->za_rdone_wptr) { \
3540Sstevel@tonic-gate 		mp = za->za_rdone[za->za_rdone_rptr]; \
3550Sstevel@tonic-gate 		za->za_rdone[za->za_rdone_rptr++] = NULL; \
3560Sstevel@tonic-gate 		if (za->za_rdone_rptr == zsa_rdone) \
3570Sstevel@tonic-gate 			za->za_rdone_rptr = 0; \
3580Sstevel@tonic-gate 	} else { \
3590Sstevel@tonic-gate 		mp = NULL; \
3600Sstevel@tonic-gate 	} \
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
3650Sstevel@tonic-gate  */
3660Sstevel@tonic-gate #define	ZSA_FLUSHQ \
3670Sstevel@tonic-gate { \
3680Sstevel@tonic-gate 	register mblk_t *tmp; \
3690Sstevel@tonic-gate 	for (;;) { \
3700Sstevel@tonic-gate 		ZSA_GETQ(tmp); \
3710Sstevel@tonic-gate 		if (!(tmp)) \
3720Sstevel@tonic-gate 			break; \
3730Sstevel@tonic-gate 		freemsg(tmp); \
3740Sstevel@tonic-gate 	} \
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 
3780Sstevel@tonic-gate /*
3790Sstevel@tonic-gate  * Logging definitions
3800Sstevel@tonic-gate  */
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate #ifdef ZSA_DEBUG
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate #ifdef ZS_DEBUG_ALL
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate extern	char	zs_h_log[];
3870Sstevel@tonic-gate extern	int	zs_h_log_n;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate #define	zsa_h_log_clear
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate #define	zsa_h_log_add(c) \
3920Sstevel@tonic-gate { \
3930Sstevel@tonic-gate 	if (zs_h_log_n >= ZS_H_LOG_MAX) \
3940Sstevel@tonic-gate 		zs_h_log_n = 0; \
3950Sstevel@tonic-gate 	zs_h_log[zs_h_log_n++] = 'A' + zs->zs_unit; \
3960Sstevel@tonic-gate 	zs_h_log[zs_h_log_n++] = c; \
3970Sstevel@tonic-gate 	zs_h_log[zs_h_log_n] = '\0'; \
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate #else /* ZS_DEBUG_ALL */
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate #define	ZSA_H_LOG_MAX	0x4000
4030Sstevel@tonic-gate char zsa_h_log[40][ZSA_H_LOG_MAX +10];
4040Sstevel@tonic-gate int zsa_h_log_n[40];
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate #define	zsa_h_log_add(c) \
4070Sstevel@tonic-gate { \
4080Sstevel@tonic-gate 	if (zsa_h_log_n[zs->zs_unit] >= ZSA_H_LOG_MAX) \
4090Sstevel@tonic-gate 		zsa_h_log_n[zs->zs_unit] = 0; \
4100Sstevel@tonic-gate 	zsa_h_log[zs->zs_unit][zsa_h_log_n[zs->zs_unit]++] = c; \
4110Sstevel@tonic-gate 	zsa_h_log[zs->zs_unit][zsa_h_log_n[zs->zs_unit]] = '\0'; \
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate #define	zsa_h_log_clear \
4150Sstevel@tonic-gate { \
4160Sstevel@tonic-gate 	register char *p; \
4170Sstevel@tonic-gate 	for (p = &zsa_h_log[zs->zs_unit][ZSA_H_LOG_MAX]; \
4180Sstevel@tonic-gate 		p >= &zsa_h_log[zs->zs_unit][0]; /* null */) \
4190Sstevel@tonic-gate 		*p-- = '\0'; \
4200Sstevel@tonic-gate 	zsa_h_log_n[zs->zs_unit] = 0; \
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate #endif /* ZS_DEBUG_ALL */
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate #define	ZSA_R0_LOG(r0) \
4260Sstevel@tonic-gate { \
4270Sstevel@tonic-gate 	if (r0 & ZSRR0_RX_READY) zsa_h_log_add('R'); \
4280Sstevel@tonic-gate 	if (r0 & ZSRR0_TIMER) zsa_h_log_add('Z'); \
4290Sstevel@tonic-gate 	if (r0 & ZSRR0_TX_READY) zsa_h_log_add('T'); \
4300Sstevel@tonic-gate 	if (r0 & ZSRR0_CD) zsa_h_log_add('D'); \
4310Sstevel@tonic-gate 	if (r0 & ZSRR0_SYNC) zsa_h_log_add('S'); \
4320Sstevel@tonic-gate 	if (r0 & ZSRR0_CTS) zsa_h_log_add('C'); \
4330Sstevel@tonic-gate 	if (r0 & ZSRR0_TXUNDER) zsa_h_log_add('U'); \
4340Sstevel@tonic-gate 	if (r0 & ZSRR0_BREAK) zsa_h_log_add('B'); \
4350Sstevel@tonic-gate }
4360Sstevel@tonic-gate 
4370Sstevel@tonic-gate #else /* ZSA_DEBUG */
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate #define	zsa_h_log_clear
4400Sstevel@tonic-gate #define	zsa_h_log_add(c)
4410Sstevel@tonic-gate #define	 ZSA_R0_LOG(r0)
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate #endif /* ZSA_DEBUG */
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 
4470Sstevel@tonic-gate static int zsa_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
4480Sstevel@tonic-gate static int zsa_close(queue_t *q, int flag);
4490Sstevel@tonic-gate static void zsa_wput(queue_t *q, mblk_t *mp);
4500Sstevel@tonic-gate static void zsa_rsrv(queue_t *q);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate static struct module_info asyncm_info = {
4530Sstevel@tonic-gate 	0,
4540Sstevel@tonic-gate 	"zs",
4550Sstevel@tonic-gate 	0,
4560Sstevel@tonic-gate 	INFPSZ,
4570Sstevel@tonic-gate 	2048,
4580Sstevel@tonic-gate 	128
4590Sstevel@tonic-gate };
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate static struct qinit async_rinit = {
4620Sstevel@tonic-gate 	putq,
4630Sstevel@tonic-gate 	(int (*)())zsa_rsrv,
4640Sstevel@tonic-gate 	zsa_open,
4650Sstevel@tonic-gate 	zsa_close,
4660Sstevel@tonic-gate 	NULL,
4670Sstevel@tonic-gate 	&asyncm_info,
4680Sstevel@tonic-gate 	NULL
4690Sstevel@tonic-gate };
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate static struct qinit async_winit = {
4720Sstevel@tonic-gate 	(int (*)())zsa_wput,
4730Sstevel@tonic-gate 	NULL,
4740Sstevel@tonic-gate 	NULL,
4750Sstevel@tonic-gate 	NULL,
4760Sstevel@tonic-gate 	NULL,
4770Sstevel@tonic-gate 	&asyncm_info,
4780Sstevel@tonic-gate 	NULL
4790Sstevel@tonic-gate };
4800Sstevel@tonic-gate 
4810Sstevel@tonic-gate struct streamtab asynctab = {
4820Sstevel@tonic-gate 	&async_rinit,
4830Sstevel@tonic-gate 	&async_winit,
4840Sstevel@tonic-gate 	NULL,
4850Sstevel@tonic-gate 	NULL,
4860Sstevel@tonic-gate };
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate /*
4890Sstevel@tonic-gate  * The async interrupt entry points.
4900Sstevel@tonic-gate  */
4910Sstevel@tonic-gate static void	zsa_txint(struct zscom *zs);
4920Sstevel@tonic-gate static void	zsa_xsint(struct zscom *zs);
4930Sstevel@tonic-gate static void	zsa_rxint(struct zscom *zs);
4940Sstevel@tonic-gate static void	zsa_srint(struct zscom *zs);
4950Sstevel@tonic-gate static int	zsa_softint(struct zscom *zs);
4960Sstevel@tonic-gate static int	zsa_suspend(struct zscom *zs);
4970Sstevel@tonic-gate static int	zsa_resume(struct zscom *zs);
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate static void
5000Sstevel@tonic-gate zsa_null(struct zscom *zs)
5010Sstevel@tonic-gate {
5020Sstevel@tonic-gate 	/* LINTED */
5030Sstevel@tonic-gate 	register short	c;
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_TXINT);
5060Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);
5070Sstevel@tonic-gate 	c = SCC_READDATA();
5080Sstevel@tonic-gate 	ZSDELAY();
5090Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_ERRORS);
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate /*ARGSUSED*/
5130Sstevel@tonic-gate static int
5140Sstevel@tonic-gate zsa_null_int(struct zscom *zs)
5150Sstevel@tonic-gate {
5160Sstevel@tonic-gate 	return (0);
5170Sstevel@tonic-gate }
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate struct zsops zsops_null_async = {
5200Sstevel@tonic-gate 	zsa_null,
5210Sstevel@tonic-gate 	zsa_null,
5220Sstevel@tonic-gate 	zsa_null,
5230Sstevel@tonic-gate 	zsa_null,
5240Sstevel@tonic-gate 	zsa_null_int,
5250Sstevel@tonic-gate 	zsa_null_int,
5260Sstevel@tonic-gate 	zsa_null_int
5270Sstevel@tonic-gate };
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate struct zsops zsops_async = {
5300Sstevel@tonic-gate 	zsa_txint,
5310Sstevel@tonic-gate 	zsa_xsint,
5320Sstevel@tonic-gate 	zsa_rxint,
5330Sstevel@tonic-gate 	zsa_srint,
5340Sstevel@tonic-gate 	zsa_softint,
5350Sstevel@tonic-gate 	zsa_suspend,
5360Sstevel@tonic-gate 	zsa_resume
5370Sstevel@tonic-gate };
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate static int	dmtozs(int bits);
5400Sstevel@tonic-gate static int	zstodm(int bits);
5410Sstevel@tonic-gate static void	zsa_restart(void *);
5420Sstevel@tonic-gate static void	zsa_reioctl(void *);
5430Sstevel@tonic-gate static void	zsa_ioctl(struct asyncline *za, queue_t *q, mblk_t *mp);
5440Sstevel@tonic-gate static void	zsa_program(struct asyncline *za, int setibaud);
5450Sstevel@tonic-gate static void	zsa_start(struct zscom *zs);
5460Sstevel@tonic-gate static void 	zsa_kick_rcv(void *);
5470Sstevel@tonic-gate static void 	zsa_callback(void *);
5480Sstevel@tonic-gate static void	zsa_set_za_rcv_flags_mask(struct asyncline *za);
5490Sstevel@tonic-gate int		zsgetspeed(dev_t dev);
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch);
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate /* ARGSUSED */
5540Sstevel@tonic-gate int
5550Sstevel@tonic-gate zsc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
5560Sstevel@tonic-gate     void **result)
5570Sstevel@tonic-gate {
5580Sstevel@tonic-gate 	register dev_t dev = (dev_t)arg;
5590Sstevel@tonic-gate 	register int unit, error;
5600Sstevel@tonic-gate 	register struct zscom *zs;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	if ((unit = UNIT(dev)) >= nzs)
5630Sstevel@tonic-gate 		return (DDI_FAILURE);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	switch (infocmd) {
5660Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
5670Sstevel@tonic-gate 		zs = &zscom[unit];
5680Sstevel@tonic-gate 		*result = zs->zs_dip;
5690Sstevel@tonic-gate 		error = DDI_SUCCESS;
5700Sstevel@tonic-gate 		break;
5710Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
572*796Smathue 		*result = (void *)(uintptr_t)(unit / 2);
5730Sstevel@tonic-gate 		error = DDI_SUCCESS;
5740Sstevel@tonic-gate 		break;
5750Sstevel@tonic-gate 	default:
5760Sstevel@tonic-gate 		error = DDI_FAILURE;
5770Sstevel@tonic-gate 	}
5780Sstevel@tonic-gate 	return (error);
5790Sstevel@tonic-gate }
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate /*
5820Sstevel@tonic-gate  * The Asynchronous Driver.
5830Sstevel@tonic-gate  */
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate /*
5860Sstevel@tonic-gate  * Determine if the zsminor device is in use as either a stdin or stdout
5870Sstevel@tonic-gate  * device, so we can be careful about how we initialize the DUART, if
5880Sstevel@tonic-gate  * it is, in fact, in use.
5890Sstevel@tonic-gate  *
5900Sstevel@tonic-gate  * Since this is expensive, we do it once and store away the answers,
5910Sstevel@tonic-gate  * since this gets called a number of times per phyical zs device.
5920Sstevel@tonic-gate  * Perhaps, this should be in a loadable module, so it can get thrown
5930Sstevel@tonic-gate  * away after all the zs devices are attached?
5940Sstevel@tonic-gate  */
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate /*
5970Sstevel@tonic-gate  * To determine if a given unit is being used by the PROM,
5980Sstevel@tonic-gate  * we need to map stdin/stdout devices as known to the PROM
5990Sstevel@tonic-gate  * to zs internal minor device numbers:
6000Sstevel@tonic-gate  *
6010Sstevel@tonic-gate  * PROM (real device)	zs minor	device
6020Sstevel@tonic-gate  *
6030Sstevel@tonic-gate  * "zs", 0, "a"		 0		ttya
6040Sstevel@tonic-gate  * "zs", 0, "b"		 1		ttyb
6050Sstevel@tonic-gate  * "zs", 1, "a"		 2		keyboard
6060Sstevel@tonic-gate  * "zs", 1, "b"		 3		mouse
6070Sstevel@tonic-gate  * "zs", 2, "a"		 4		ttyc
6080Sstevel@tonic-gate  * "zs", 2, "b"		 5		ttyd
6090Sstevel@tonic-gate  *
6100Sstevel@tonic-gate  * The following value mapping lines assume that insource
6110Sstevel@tonic-gate  * and outsink map as "screen, a, b, c, d, ...", and that
6120Sstevel@tonic-gate  * zs minors are "a, b, kbd, mouse, c, d, ...".
6130Sstevel@tonic-gate  */
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate static int zsa_inuse;		/* Strictly for debugging */
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate int
6180Sstevel@tonic-gate zsa_channel_is_active_in_rom(dev_info_t *dev, int zsminor)
6190Sstevel@tonic-gate {
6200Sstevel@tonic-gate 	char pathname[OBP_MAXPATHLEN];
6210Sstevel@tonic-gate 	char default_pathname[OBP_MAXPATHLEN];
6220Sstevel@tonic-gate 	char *stdioname;
6230Sstevel@tonic-gate 	char minordata[3];
6240Sstevel@tonic-gate 
6250Sstevel@tonic-gate 	/*
6260Sstevel@tonic-gate 	 * Basically, get my name and compare it to stdio devnames
6270Sstevel@tonic-gate 	 * and if we get a match, then the device is in use as either
6280Sstevel@tonic-gate 	 * stdin or stdout device (console tip line or keyboard device).
6290Sstevel@tonic-gate 	 *
6300Sstevel@tonic-gate 	 * We get two forms of the pathname, one complete with the
6310Sstevel@tonic-gate 	 * the channel number, and if the channel is 'a', then
6320Sstevel@tonic-gate 	 * we also deal with the user's ability to default to
6330Sstevel@tonic-gate 	 * channel 'a', by omitting the channel number option.
6340Sstevel@tonic-gate 	 * We then compare these pathnames to both the stdin and
6350Sstevel@tonic-gate 	 * stdout pathnames. If any of these match, then the channel
6360Sstevel@tonic-gate 	 * is in use.
6370Sstevel@tonic-gate 	 */
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	(void) ddi_pathname(dev, pathname);	/* device pathname */
6400Sstevel@tonic-gate 	default_pathname[0] = (char)0;	/* default pathname if channel 'a' */
6410Sstevel@tonic-gate 	if ((zsminor & 1) == 0)
6420Sstevel@tonic-gate 		(void) strcpy(default_pathname, pathname);
6430Sstevel@tonic-gate 	minordata[0] = ':';
6440Sstevel@tonic-gate 	minordata[1] = (char)('a' + (zsminor & 1));
6450Sstevel@tonic-gate 	minordata[2] = (char)0;
6460Sstevel@tonic-gate 	(void) strcat(pathname, minordata);
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	stdioname = prom_stdinpath();
6490Sstevel@tonic-gate 	if (strcmp(pathname, stdioname) == 0) {
6500Sstevel@tonic-gate 		zsa_inuse |= (1 << zsminor);
6510Sstevel@tonic-gate 		return (1);
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 	if (strcmp(default_pathname, stdioname) == 0) {
6540Sstevel@tonic-gate 		zsa_inuse |= (1 << zsminor);
6550Sstevel@tonic-gate 		return (1);
6560Sstevel@tonic-gate 	}
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate 	stdioname = prom_stdoutpath();
6590Sstevel@tonic-gate 	if (strcmp(pathname, stdioname) == 0) {
6600Sstevel@tonic-gate 		zsa_inuse |= (1 << zsminor);
6610Sstevel@tonic-gate 		return (1);
6620Sstevel@tonic-gate 	}
6630Sstevel@tonic-gate 	if (strcmp(default_pathname, stdioname) == 0) {
6640Sstevel@tonic-gate 		zsa_inuse |= (1 << zsminor);
6650Sstevel@tonic-gate 		return (1);
6660Sstevel@tonic-gate 	}
6670Sstevel@tonic-gate 
6680Sstevel@tonic-gate 	return (0);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate /*
6720Sstevel@tonic-gate  * Initialize zs
6730Sstevel@tonic-gate  */
6740Sstevel@tonic-gate void
6750Sstevel@tonic-gate zsa_init(struct zscom *zs)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate 	/*
6780Sstevel@tonic-gate 	 * This routine is called near the end of the zs module's attach
6790Sstevel@tonic-gate 	 * process. It initializes the TTY protocol-private data for this
6800Sstevel@tonic-gate 	 * channel that needs to be in place before interrupts are enabled.
6810Sstevel@tonic-gate 	 */
6820Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
6830Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 	/*
6860Sstevel@tonic-gate 	 * Raise modem control lines on serial ports associated
6870Sstevel@tonic-gate 	 * with the console and (optionally) softcarrier lines.
6880Sstevel@tonic-gate 	 * Drop modem control lines on all others so that modems
6890Sstevel@tonic-gate 	 * will not answer and portselectors will skip these
6900Sstevel@tonic-gate 	 * lines until they are opened by a getty.
6910Sstevel@tonic-gate 	 */
6920Sstevel@tonic-gate 	if (zsa_channel_is_active_in_rom(zs->zs_dip, zs->zs_unit))
6930Sstevel@tonic-gate 		(void) zsmctl(zs, ZS_ON, DMSET);	/* raise dtr */
6940Sstevel@tonic-gate 	else if (zsasoftdtr && (zssoftCAR[zs->zs_unit]))
6950Sstevel@tonic-gate 		(void) zsmctl(zs, ZS_ON, DMSET);	/* raise dtr */
6960Sstevel@tonic-gate 	else
6970Sstevel@tonic-gate 		(void) zsmctl(zs, ZS_OFF, DMSET);	/* drop dtr */
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 	if (zsa_rstandby > ZSA_MAX_RSTANDBY)
7000Sstevel@tonic-gate 		zsa_rstandby = ZSA_MAX_RSTANDBY;
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	if (zsa_rdone > ZSA_RDONE_MAX)
7030Sstevel@tonic-gate 		zsa_rdone = ZSA_RDONE_MAX;
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate 	if (zsa_grace_flow_control > ZSA_GRACE_MAX_FLOW_CONTROL)
7060Sstevel@tonic-gate 		zsa_grace_flow_control = ZSA_GRACE_MAX_FLOW_CONTROL;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
7090Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate /*
7140Sstevel@tonic-gate  * Open routine.
7150Sstevel@tonic-gate  */
7160Sstevel@tonic-gate /*ARGSUSED*/
7170Sstevel@tonic-gate static int
7180Sstevel@tonic-gate zsa_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
7190Sstevel@tonic-gate {
7200Sstevel@tonic-gate 	register struct zscom *zs;
7210Sstevel@tonic-gate 	register struct asyncline *za;
7220Sstevel@tonic-gate 	register int	speed, unit;
7230Sstevel@tonic-gate 	struct termios *termiosp;
7240Sstevel@tonic-gate 	int len;
7250Sstevel@tonic-gate 	register int allocbcount = zsa_rstandby;
7260Sstevel@tonic-gate 	boolean_t set_zsoptinit = B_FALSE;
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	unit = UNIT(*dev);
7290Sstevel@tonic-gate 	if (unit >= nzs)
7300Sstevel@tonic-gate 		return (ENXIO);		/* unit not configured */
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 	/* zscom is allocated by zsattach, and thus cannot be NULL here */
7330Sstevel@tonic-gate 	zs = &zscom[unit];
7340Sstevel@tonic-gate 	if (zs->zs_ops == NULL) {
7350Sstevel@tonic-gate 		return (ENXIO);	 /* device not found by autoconfig */
7360Sstevel@tonic-gate 	}
7370Sstevel@tonic-gate 
7380Sstevel@tonic-gate 	mutex_enter(zs->zs_ocexcl);
7390Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
7400Sstevel@tonic-gate again:
7410Sstevel@tonic-gate 	if ((zs->zs_ops != &zsops_null) &&
7420Sstevel@tonic-gate 	    (zs->zs_ops != &zsops_async)) {
7430Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
7440Sstevel@tonic-gate 		mutex_exit(zs->zs_ocexcl);
7450Sstevel@tonic-gate 		return (EBUSY);	 /* another protocol got here first */
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	za = (struct asyncline *)&zs->zs_priv_str;
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	if (zs->zs_suspended) {
7510Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
7520Sstevel@tonic-gate 		mutex_exit(zs->zs_ocexcl);
7530Sstevel@tonic-gate 		(void) ddi_dev_is_needed(zs->zs_dip, 0, 1);
7540Sstevel@tonic-gate 		mutex_enter(zs->zs_ocexcl);
7550Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
7560Sstevel@tonic-gate 	}
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	/* Mark device as busy (for power management) */
7590Sstevel@tonic-gate 	(void) pm_busy_component(zs->zs_dip, unit%2+1);
7600Sstevel@tonic-gate 
7610Sstevel@tonic-gate 	if (zs->zs_ops == &zsops_null) {
7620Sstevel@tonic-gate 		bzero(za, sizeof (zs->zs_priv_str));
7630Sstevel@tonic-gate 		za->za_common = zs;
7640Sstevel@tonic-gate 		if (zssoftCAR[zs->zs_unit])
7650Sstevel@tonic-gate 			za->za_ttycommon.t_flags |= TS_SOFTCAR;
7660Sstevel@tonic-gate 		zsopinit(zs, &zsops_async);
7670Sstevel@tonic-gate 		set_zsoptinit = B_TRUE;
7680Sstevel@tonic-gate 		za->za_rdone_wptr = 0;
7690Sstevel@tonic-gate 		za->za_rdone_rptr = 0;
7700Sstevel@tonic-gate 	}
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 	zs->zs_priv = (caddr_t)za;
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 	/*
7750Sstevel@tonic-gate 	 * Block waiting for carrier to come up,
7760Sstevel@tonic-gate 	 * unless this is a no-delay open.
7770Sstevel@tonic-gate 	 */
7780Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
7790Sstevel@tonic-gate 	if (!(za->za_flags & ZAS_ISOPEN)) {
7800Sstevel@tonic-gate 		/*
7810Sstevel@tonic-gate 		 * Get the default termios settings (cflag).
7820Sstevel@tonic-gate 		 * These are stored as a property in the
7830Sstevel@tonic-gate 		 * "options" node.
7840Sstevel@tonic-gate 		 */
7850Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7860Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY,
7870Sstevel@tonic-gate 		    ddi_root_node(), 0, "ttymodes",
7880Sstevel@tonic-gate 		    (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
7890Sstevel@tonic-gate 		    len == sizeof (struct termios)) {
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 			za->za_ttycommon.t_cflag = termiosp->c_cflag;
7920Sstevel@tonic-gate 			kmem_free(termiosp, len);
7930Sstevel@tonic-gate 		} else {
7940Sstevel@tonic-gate 			/*
7950Sstevel@tonic-gate 			 * Gack! Whine about it.
7960Sstevel@tonic-gate 			 */
7970Sstevel@tonic-gate 			cmn_err(CE_WARN,
7980Sstevel@tonic-gate 			    "zs: Couldn't get ttymodes property!");
7990Sstevel@tonic-gate 		}
8000Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
8010Sstevel@tonic-gate 		if ((*dev == rconsdev) || (*dev == kbddev) ||
8020Sstevel@tonic-gate 		    (*dev == stdindev)) {
8030Sstevel@tonic-gate 			speed = zsgetspeed(*dev);
8040Sstevel@tonic-gate 			za->za_ttycommon.t_cflag &= ~(CBAUD);
8050Sstevel@tonic-gate 			if (speed > CBAUD) {
8060Sstevel@tonic-gate 				za->za_ttycommon.t_cflag |= CBAUDEXT;
8070Sstevel@tonic-gate 				za->za_ttycommon.t_cflag |=
8080Sstevel@tonic-gate 					((speed - CBAUD - 1) & CBAUD);
8090Sstevel@tonic-gate 			} else {
8100Sstevel@tonic-gate 				za->za_ttycommon.t_cflag &= ~CBAUDEXT;
8110Sstevel@tonic-gate 				za->za_ttycommon.t_cflag |= (speed & CBAUD);
8120Sstevel@tonic-gate 			}
8130Sstevel@tonic-gate 		}
8140Sstevel@tonic-gate 		za->za_overrun = 0;
8150Sstevel@tonic-gate 		za->za_ttycommon.t_iflag = 0;
8160Sstevel@tonic-gate 		za->za_ttycommon.t_iocpending = NULL;
8170Sstevel@tonic-gate 		za->za_ttycommon.t_size.ws_row = 0;
8180Sstevel@tonic-gate 		za->za_ttycommon.t_size.ws_col = 0;
8190Sstevel@tonic-gate 		za->za_ttycommon.t_size.ws_xpixel = 0;
8200Sstevel@tonic-gate 		za->za_ttycommon.t_size.ws_ypixel = 0;
8210Sstevel@tonic-gate 		za->za_dev = *dev;
8220Sstevel@tonic-gate 		za->za_wbufcid = 0;
8230Sstevel@tonic-gate 		zsa_program(za, za->za_ttycommon.t_cflag & (CIBAUDEXT|CIBAUD));
8240Sstevel@tonic-gate 		zsa_set_za_rcv_flags_mask(za);
8250Sstevel@tonic-gate 	} else if ((za->za_ttycommon.t_flags & TS_XCLUDE) &&
8260Sstevel@tonic-gate 						secpolicy_excl_open(cr) != 0) {
8270Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
8280Sstevel@tonic-gate 		if (set_zsoptinit && !(za->za_flags & ISOPEN))
8290Sstevel@tonic-gate 			zsopinit(zs, &zsops_null);
8300Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
8310Sstevel@tonic-gate 		mutex_exit(zs->zs_ocexcl);
8320Sstevel@tonic-gate 		return (EBUSY);
8330Sstevel@tonic-gate 	} else if ((*dev & OUTLINE) && !(za->za_flags & ZAS_OUT)) {
8340Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
8350Sstevel@tonic-gate 		if (set_zsoptinit && !(za->za_flags & ISOPEN))
8360Sstevel@tonic-gate 			zsopinit(zs, &zsops_null);
8370Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
8380Sstevel@tonic-gate 		mutex_exit(zs->zs_ocexcl);
8390Sstevel@tonic-gate 		return (EBUSY);
8400Sstevel@tonic-gate 	}
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 	if (*dev & OUTLINE)
8430Sstevel@tonic-gate 		za->za_flags |= ZAS_OUT;
8440Sstevel@tonic-gate 	(void) zsmctl(zs, ZS_ON, DMSET);
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	/*
8470Sstevel@tonic-gate 	 * Check carrier.
8480Sstevel@tonic-gate 	 */
8490Sstevel@tonic-gate 	if ((za->za_ttycommon.t_flags & TS_SOFTCAR) ||
8500Sstevel@tonic-gate 	    (zsmctl(zs, 0, DMGET) & ZSRR0_CD))
8510Sstevel@tonic-gate 		za->za_flags |= ZAS_CARR_ON;
8520Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	/*
8550Sstevel@tonic-gate 	 * If FNDELAY and FNONBLOCK are clear, block until carrier up.
8560Sstevel@tonic-gate 	 * Quit on interrupt.
8570Sstevel@tonic-gate 	 */
8580Sstevel@tonic-gate 	if (!(flag & (FNDELAY|FNONBLOCK)) &&
8590Sstevel@tonic-gate 	    !(za->za_ttycommon.t_cflag & CLOCAL)) {
8600Sstevel@tonic-gate 		if (!(za->za_flags & (ZAS_CARR_ON|ZAS_OUT)) ||
8610Sstevel@tonic-gate 		    ((za->za_flags & ZAS_OUT) && !(*dev & OUTLINE))) {
8620Sstevel@tonic-gate 			za->za_flags |= ZAS_WOPEN;
8630Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
8640Sstevel@tonic-gate 			if (cv_wait_sig(&zs->zs_flags_cv, zs->zs_ocexcl) == 0) {
8650Sstevel@tonic-gate 				mutex_enter(zs->zs_excl);
8660Sstevel@tonic-gate 				if (zs->zs_suspended) {
8670Sstevel@tonic-gate 					mutex_exit(zs->zs_excl);
8680Sstevel@tonic-gate 					mutex_exit(zs->zs_ocexcl);
8690Sstevel@tonic-gate 					(void) ddi_dev_is_needed(zs->zs_dip,
8700Sstevel@tonic-gate 									0, 1);
8710Sstevel@tonic-gate 					mutex_enter(zs->zs_ocexcl);
8720Sstevel@tonic-gate 					mutex_enter(zs->zs_excl);
8730Sstevel@tonic-gate 				}
8740Sstevel@tonic-gate 				za->za_flags &= ~ZAS_WOPEN;
8750Sstevel@tonic-gate 				if (set_zsoptinit && !(za->za_flags & ISOPEN))
8760Sstevel@tonic-gate 					zsopinit(zs, &zsops_null);
8770Sstevel@tonic-gate 				mutex_exit(zs->zs_excl);
8780Sstevel@tonic-gate 				mutex_exit(zs->zs_ocexcl);
8790Sstevel@tonic-gate 				return (EINTR);
8800Sstevel@tonic-gate 			}
8810Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
8820Sstevel@tonic-gate 			za->za_flags &= ~ZAS_WOPEN;
8830Sstevel@tonic-gate 			if ((zs->zs_ops == &zsops_null) ||
8840Sstevel@tonic-gate 			    (zs->zs_ops == &zsops_async))
8850Sstevel@tonic-gate 				goto again;
8860Sstevel@tonic-gate 			else {
8870Sstevel@tonic-gate 				if (set_zsoptinit && !(za->za_flags & ISOPEN))
8880Sstevel@tonic-gate 					zsopinit(zs, &zsops_null);
8890Sstevel@tonic-gate 				mutex_exit(zs->zs_excl);
8900Sstevel@tonic-gate 				mutex_exit(zs->zs_ocexcl);
8910Sstevel@tonic-gate 				return (EBUSY);
8920Sstevel@tonic-gate 			}
8930Sstevel@tonic-gate 		}
8940Sstevel@tonic-gate 	} else if ((za->za_flags & ZAS_OUT) && !(*dev & OUTLINE)) {
8950Sstevel@tonic-gate 		if (set_zsoptinit && !(za->za_flags & ISOPEN))
8960Sstevel@tonic-gate 			zsopinit(zs, &zsops_null);
8970Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
8980Sstevel@tonic-gate 		mutex_exit(zs->zs_ocexcl);
8990Sstevel@tonic-gate 		return (EBUSY);
9000Sstevel@tonic-gate 	}
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 	za->za_ttycommon.t_readq = rq;
9030Sstevel@tonic-gate 	za->za_ttycommon.t_writeq = WR(rq);
9040Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)za;
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 	za->za_flags |= ZAS_ISOPEN;
9070Sstevel@tonic-gate 	ZSA_GETBLOCK(zs, allocbcount);
9080Sstevel@tonic-gate 	qprocson(rq);
9090Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
9100Sstevel@tonic-gate 	mutex_exit(zs->zs_ocexcl);
9110Sstevel@tonic-gate 	return (0);
9120Sstevel@tonic-gate }
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate static void
9150Sstevel@tonic-gate zs_progress_check(void *arg)
9160Sstevel@tonic-gate {
9170Sstevel@tonic-gate 	struct asyncline *za = arg;
9180Sstevel@tonic-gate 	struct zscom *zs = za->za_common;
9190Sstevel@tonic-gate 	mblk_t *bp;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 	/*
9220Sstevel@tonic-gate 	 * We define "progress" as either waiting on a timed break or delay, or
9230Sstevel@tonic-gate 	 * having had at least one transmitter interrupt.  If none of these are
9240Sstevel@tonic-gate 	 * true, then just terminate the output and wake up that close thread.
9250Sstevel@tonic-gate 	 */
9260Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
9270Sstevel@tonic-gate 	if (!(zs->zs_flags & ZS_PROGRESS) &&
9280Sstevel@tonic-gate 	    !(za->za_flags & (ZAS_BREAK|ZAS_DELAY))) {
9290Sstevel@tonic-gate 		za->za_flags &= ~ZAS_BUSY;
9300Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
9310Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
9320Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
9330Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
9340Sstevel@tonic-gate 		bp = za->za_xmitblk;
9350Sstevel@tonic-gate 		za->za_xmitblk = NULL;
9360Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
9370Sstevel@tonic-gate 		zs->zs_timer = 0;
9380Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
9390Sstevel@tonic-gate 		if (bp != NULL)
9400Sstevel@tonic-gate 			freeb(bp);
9410Sstevel@tonic-gate 		/*
9420Sstevel@tonic-gate 		 * Since this timer is running, we know that we're in exit(2).
9430Sstevel@tonic-gate 		 * That means that the user can't possibly be waiting on any
9440Sstevel@tonic-gate 		 * valid ioctl(2) completion anymore, and we should just flush
9450Sstevel@tonic-gate 		 * everything.
9460Sstevel@tonic-gate 		 */
9470Sstevel@tonic-gate 		flushq(za->za_ttycommon.t_writeq, FLUSHALL);
9480Sstevel@tonic-gate 		cv_broadcast(&zs->zs_flags_cv);
9490Sstevel@tonic-gate 	} else {
9500Sstevel@tonic-gate 		zs->zs_flags &= ~ZS_PROGRESS;
9510Sstevel@tonic-gate 		zs->zs_timer = timeout(zs_progress_check, za,
9520Sstevel@tonic-gate 		    drv_usectohz(zs_drain_check));
9530Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
9540Sstevel@tonic-gate 	}
9550Sstevel@tonic-gate }
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate /*
9580Sstevel@tonic-gate  * Close routine.
9590Sstevel@tonic-gate  *
9600Sstevel@tonic-gate  * Important locking note: the zs_ocexcl lock is not held at all in this
9610Sstevel@tonic-gate  * routine.  This is intentional.  That lock is used to coordinate multiple
9620Sstevel@tonic-gate  * simultaneous opens on a stream, and there's no such thing as multiple
9630Sstevel@tonic-gate  * simultaneous closes on a stream.
9640Sstevel@tonic-gate  */
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate /*ARGSUSED*/
9670Sstevel@tonic-gate static int
9680Sstevel@tonic-gate zsa_close(queue_t *q, int flag)
9690Sstevel@tonic-gate {
9700Sstevel@tonic-gate 	struct asyncline *za;
9710Sstevel@tonic-gate 	struct zscom *zs;
9720Sstevel@tonic-gate 	int i;
9730Sstevel@tonic-gate 	mblk_t *bp;
9740Sstevel@tonic-gate 	timeout_id_t za_zsa_restart_id, za_kick_rcv_id;
9750Sstevel@tonic-gate 	bufcall_id_t za_bufcid, za_wbufcid;
9760Sstevel@tonic-gate 	int	 tmp;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	za = q->q_ptr;
9790Sstevel@tonic-gate 	ASSERT(za != NULL);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	zs = za->za_common;
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
9840Sstevel@tonic-gate 	zs->zs_flags |= ZS_CLOSING;
9850Sstevel@tonic-gate 
9860Sstevel@tonic-gate 	/*
9870Sstevel@tonic-gate 	 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
9880Sstevel@tonic-gate 	 * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
9890Sstevel@tonic-gate 	 * write queue and there's a timer running, so we don't have to worry
9900Sstevel@tonic-gate 	 * about them.  For the untimed case, though, the user obviously made a
9910Sstevel@tonic-gate 	 * mistake, because these are handled immediately.  We'll terminate the
9920Sstevel@tonic-gate 	 * break now and honor his implicit request by discarding the rest of
9930Sstevel@tonic-gate 	 * the data.
9940Sstevel@tonic-gate 	 */
9950Sstevel@tonic-gate 	if (!(za->za_flags & ZAS_BREAK) && (zs->zs_wreg[5] & ZSWR5_BREAK))
9960Sstevel@tonic-gate 		goto nodrain;
9970Sstevel@tonic-gate 
9980Sstevel@tonic-gate 	/*
9990Sstevel@tonic-gate 	 * If the user told us not to delay the close ("non-blocking"), then
10000Sstevel@tonic-gate 	 * don't bother trying to drain.
10010Sstevel@tonic-gate 	 *
10020Sstevel@tonic-gate 	 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
10030Sstevel@tonic-gate 	 * getting an M_START (since these messages aren't enqueued), and the
10040Sstevel@tonic-gate 	 * only other way to clear the stop condition is by loss of DCD, which
10050Sstevel@tonic-gate 	 * would discard the queue data.  Thus, we drop the output data if
10060Sstevel@tonic-gate 	 * ASYNC_STOPPED is set.
10070Sstevel@tonic-gate 	 */
10080Sstevel@tonic-gate 	if ((flag & (FNDELAY|FNONBLOCK)) || (za->za_flags & ZAS_STOPPED))
10090Sstevel@tonic-gate 		goto nodrain;
10100Sstevel@tonic-gate 
10110Sstevel@tonic-gate 	/*
10120Sstevel@tonic-gate 	 * If there's any pending output, then we have to try to drain it.
10130Sstevel@tonic-gate 	 * There are two main cases to be handled:
10140Sstevel@tonic-gate 	 *	- called by close(2): need to drain until done or until
10150Sstevel@tonic-gate 	 *	  a signal is received.  No timeout.
10160Sstevel@tonic-gate 	 *	- called by exit(2): need to drain while making progress
10170Sstevel@tonic-gate 	 *	  or until a timeout occurs.  No signals.
10180Sstevel@tonic-gate 	 *
10190Sstevel@tonic-gate 	 * If we can't rely on receiving a signal to get us out of a hung
10200Sstevel@tonic-gate 	 * session, then we have to use a timer.  In this case, we set a timer
10210Sstevel@tonic-gate 	 * to check for progress in sending the output data -- all that we ask
10220Sstevel@tonic-gate 	 * (at each interval) is that there's been some progress made.  Since
10230Sstevel@tonic-gate 	 * the interrupt routine grabs buffers from the write queue, we can't
10240Sstevel@tonic-gate 	 * trust changes in zs_wr_cur.  Instead, we use a progress flag.
10250Sstevel@tonic-gate 	 *
10260Sstevel@tonic-gate 	 * Note that loss of carrier will cause the output queue to be flushed,
10270Sstevel@tonic-gate 	 * and we'll wake up again and finish normally.
10280Sstevel@tonic-gate 	 */
10290Sstevel@tonic-gate 	if (!ddi_can_receive_sig() && zs_drain_check != 0) {
10300Sstevel@tonic-gate 		zs->zs_flags &= ~ZS_PROGRESS;
10310Sstevel@tonic-gate 		zs->zs_timer = timeout(zs_progress_check, za,
10320Sstevel@tonic-gate 		    drv_usectohz(zs_drain_check));
10330Sstevel@tonic-gate 	}
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate 	while (zs->zs_wr_cur != NULL ||
10360Sstevel@tonic-gate 	    za->za_ttycommon.t_writeq->q_first != NULL ||
10370Sstevel@tonic-gate 	    (za->za_flags & (ZAS_BUSY|ZAS_DELAY|ZAS_BREAK))) {
10380Sstevel@tonic-gate 		if (cv_wait_sig(&zs->zs_flags_cv, zs->zs_excl) == 0)
10390Sstevel@tonic-gate 			break;
10400Sstevel@tonic-gate 	}
10410Sstevel@tonic-gate 
10420Sstevel@tonic-gate 	if (zs->zs_timer != 0) {
10430Sstevel@tonic-gate 		(void) untimeout(zs->zs_timer);
10440Sstevel@tonic-gate 		zs->zs_timer = 0;
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate nodrain:
10480Sstevel@tonic-gate 	/*
10490Sstevel@tonic-gate 	 * If break is in progress, stop it.
10500Sstevel@tonic-gate 	 */
10510Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
10520Sstevel@tonic-gate 	if (zs->zs_wreg[5] & ZSWR5_BREAK) {
10530Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_BREAK);
10540Sstevel@tonic-gate 		za->za_flags &= ~ZAS_BREAK;
10550Sstevel@tonic-gate 	}
10560Sstevel@tonic-gate 
10570Sstevel@tonic-gate 	za_wbufcid = za->za_wbufcid;
10580Sstevel@tonic-gate 	za_bufcid = za->za_bufcid;
10590Sstevel@tonic-gate 	za_zsa_restart_id = za->za_zsa_restart_id;
10600Sstevel@tonic-gate 	za_kick_rcv_id = za->za_kick_rcv_id;
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	za->za_wbufcid = za->za_bufcid = 0;
10630Sstevel@tonic-gate 	za->za_zsa_restart_id = za->za_kick_rcv_id = 0;
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate 	/*
10660Sstevel@tonic-gate 	 * If line has HUPCL set or is incompletely opened,
10670Sstevel@tonic-gate 	 * and it is not the console or the keyboard,
10680Sstevel@tonic-gate 	 * fix up the modem lines.
10690Sstevel@tonic-gate 	 */
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	zsopinit(zs, &zsops_null_async);
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 	/*
10740Sstevel@tonic-gate 	 * Nobody, zsh or zs can now open this port until
10750Sstevel@tonic-gate 	 * zsopinit(zs, &zsops_null);
10760Sstevel@tonic-gate 	 *
10770Sstevel@tonic-gate 	 */
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	if ((za->za_dev != rconsdev) && (za->za_dev != kbddev) &&
10800Sstevel@tonic-gate 	    (za->za_dev != stdindev) &&
10810Sstevel@tonic-gate 	    (((za->za_flags & (ZAS_WOPEN|ZAS_ISOPEN)) != ZAS_ISOPEN) ||
10820Sstevel@tonic-gate 	    (za->za_ttycommon.t_cflag & HUPCL))) {
10830Sstevel@tonic-gate 		/*
10840Sstevel@tonic-gate 		 * If DTR is being held high by softcarrier,
10850Sstevel@tonic-gate 		 * set up the ZS_ON set; if not, hang up.
10860Sstevel@tonic-gate 		 */
10870Sstevel@tonic-gate 		if (zsasoftdtr && (za->za_ttycommon.t_flags & TS_SOFTCAR))
10880Sstevel@tonic-gate 			(void) zsmctl(zs, ZS_ON, DMSET);
10890Sstevel@tonic-gate 		else
10900Sstevel@tonic-gate 			(void) zsmctl(zs, ZS_OFF, DMSET);
10910Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
10920Sstevel@tonic-gate 		/*
10930Sstevel@tonic-gate 		 * Don't let an interrupt in the middle of close
10940Sstevel@tonic-gate 		 * bounce us back to the top; just continue
10950Sstevel@tonic-gate 		 * closing as if nothing had happened.
10960Sstevel@tonic-gate 		 */
10970Sstevel@tonic-gate 		tmp = cv_timedwait_sig(&zs->zs_flags_cv, zs->zs_excl,
10980Sstevel@tonic-gate 		    ddi_get_lbolt() + drv_usectohz(10000));
10990Sstevel@tonic-gate 		if (zs->zs_suspended) {
11000Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
11010Sstevel@tonic-gate 			(void) ddi_dev_is_needed(zs->zs_dip, 0, 1);
11020Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
11030Sstevel@tonic-gate 		}
11040Sstevel@tonic-gate 		if (tmp == 0)
11050Sstevel@tonic-gate 			goto out;
11060Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
11070Sstevel@tonic-gate 	}
11080Sstevel@tonic-gate 
11090Sstevel@tonic-gate 	/*
11100Sstevel@tonic-gate 	 * If nobody's now using it, turn off receiver interrupts.
11110Sstevel@tonic-gate 	 */
11120Sstevel@tonic-gate 	if ((za->za_flags & (ZAS_ISOPEN|ZAS_WOPEN)) == 0)
11130Sstevel@tonic-gate 		SCC_BIC(1, ZSWR1_RIE);
11140Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
11150Sstevel@tonic-gate 
11160Sstevel@tonic-gate out:
11170Sstevel@tonic-gate 	/*
11180Sstevel@tonic-gate 	 * Clear out device state.
11190Sstevel@tonic-gate 	 */
11200Sstevel@tonic-gate 	ttycommon_close(&za->za_ttycommon);
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	za->za_ttycommon.t_readq = NULL;
11230Sstevel@tonic-gate 	za->za_ttycommon.t_writeq = NULL;
11240Sstevel@tonic-gate 
11250Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
11260Sstevel@tonic-gate 	za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
11270Sstevel@tonic-gate 	zs->zs_wr_cur = NULL;
11280Sstevel@tonic-gate 	zs->zs_wr_lim = NULL;
11290Sstevel@tonic-gate 	bp = za->za_xmitblk;
11300Sstevel@tonic-gate 	za->za_xmitblk = NULL;
11310Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
11320Sstevel@tonic-gate 	if (bp)
11330Sstevel@tonic-gate 		freemsg(bp);
11340Sstevel@tonic-gate 
11350Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
11360Sstevel@tonic-gate 	zs->zs_rd_cur = NULL;
11370Sstevel@tonic-gate 	zs->zs_rd_lim = NULL;
11380Sstevel@tonic-gate 	bp = za->za_rcvblk;
11390Sstevel@tonic-gate 	za->za_rcvblk = NULL;
11400Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
11410Sstevel@tonic-gate 	if (bp)
11420Sstevel@tonic-gate 		freemsg(bp);
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	for (i = 0; i < zsa_rstandby; i++) {
11450Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
11460Sstevel@tonic-gate 		bp = za->za_rstandby[i];
11470Sstevel@tonic-gate 		za->za_rstandby[i] = NULL;
11480Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
11490Sstevel@tonic-gate 		if (bp)
11500Sstevel@tonic-gate 			freemsg(bp);
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate 	if (za->za_soft_active || za->za_kick_active) {
11540Sstevel@tonic-gate 		zs->zs_flags |= ZS_CLOSED;
11550Sstevel@tonic-gate 		while (za->za_soft_active || za->za_kick_active)
11560Sstevel@tonic-gate 			cv_wait(&zs->zs_flags_cv, zs->zs_excl);
11570Sstevel@tonic-gate 	}
11580Sstevel@tonic-gate 	if (zs->zs_suspended) {
11590Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
11600Sstevel@tonic-gate 		(void) ddi_dev_is_needed(zs->zs_dip, 0, 1);
11610Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
11620Sstevel@tonic-gate 	}
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	ZSA_FLUSHQ;
11650Sstevel@tonic-gate 	bzero(za, sizeof (struct asyncline));
11660Sstevel@tonic-gate 	qprocsoff(q);
11670Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	/*
11700Sstevel@tonic-gate 	 * Cancel outstanding "bufcall" request.
11710Sstevel@tonic-gate 	 */
11720Sstevel@tonic-gate 	if (za_wbufcid)
11730Sstevel@tonic-gate 		unbufcall(za_wbufcid);
11740Sstevel@tonic-gate 	if (za_bufcid)
11750Sstevel@tonic-gate 		unbufcall(za_bufcid);
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	/*
11780Sstevel@tonic-gate 	 * Cancel outstanding timeout.
11790Sstevel@tonic-gate 	 */
11800Sstevel@tonic-gate 	if (za_zsa_restart_id)
11810Sstevel@tonic-gate 		(void) untimeout(za_zsa_restart_id);
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	if (za_kick_rcv_id)
11840Sstevel@tonic-gate 		(void) untimeout(za_kick_rcv_id);
11850Sstevel@tonic-gate 
11860Sstevel@tonic-gate 	q->q_ptr = WR(q)->q_ptr = NULL;
11870Sstevel@tonic-gate 	zsopinit(zs, &zsops_null);
11880Sstevel@tonic-gate 	cv_broadcast(&zs->zs_flags_cv);
11890Sstevel@tonic-gate 
11900Sstevel@tonic-gate 	/* Mark device as available for power management */
11910Sstevel@tonic-gate 	(void) pm_idle_component(zs->zs_dip, zs->zs_unit%2+1);
11920Sstevel@tonic-gate 	return (0);
11930Sstevel@tonic-gate }
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate /*
11960Sstevel@tonic-gate  * Put procedure for write queue.
11970Sstevel@tonic-gate  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
11980Sstevel@tonic-gate  * set the flow control character for M_STOPI and M_STARTI messages;
11990Sstevel@tonic-gate  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
12000Sstevel@tonic-gate  * by the start routine, and then call the start routine; discard
12010Sstevel@tonic-gate  * everything else. Note that this driver does not incorporate any
12020Sstevel@tonic-gate  * mechanism to negotiate to handle the canonicalization process.
12030Sstevel@tonic-gate  * It expects that these functions are handled in upper module(s),
12040Sstevel@tonic-gate  * as we do in ldterm.
12050Sstevel@tonic-gate  */
12060Sstevel@tonic-gate static void
12070Sstevel@tonic-gate zsa_wput(queue_t *q, mblk_t *mp)
12080Sstevel@tonic-gate {
12090Sstevel@tonic-gate 	register struct asyncline	*za;
12100Sstevel@tonic-gate 	register struct zscom		*zs;
12110Sstevel@tonic-gate 	register struct copyresp	*resp;
12120Sstevel@tonic-gate 	register mblk_t			*bp = NULL;
12130Sstevel@tonic-gate 	int				error;
12140Sstevel@tonic-gate 	struct iocblk			*iocp;
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 	za = (struct asyncline *)q->q_ptr;
12170Sstevel@tonic-gate 	zs = za->za_common;
12180Sstevel@tonic-gate 	if (zs->zs_flags & ZS_NEEDSOFT) {
12190Sstevel@tonic-gate 		zs->zs_flags &= ~ZS_NEEDSOFT;
12200Sstevel@tonic-gate 		(void) zsa_softint(zs);
12210Sstevel@tonic-gate 	}
12220Sstevel@tonic-gate 
12230Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
12240Sstevel@tonic-gate 
12250Sstevel@tonic-gate 	case M_STOP:
12260Sstevel@tonic-gate 		/*
12270Sstevel@tonic-gate 		 * Since we don't do real DMA, we can just let the
12280Sstevel@tonic-gate 		 * chip coast to a stop after applying the brakes.
12290Sstevel@tonic-gate 		 */
12300Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
12310Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
12320Sstevel@tonic-gate 		za->za_flags |= ZAS_STOPPED;
12330Sstevel@tonic-gate 		if ((zs->zs_wr_cur) != NULL) {
12340Sstevel@tonic-gate 			za->za_flags &= ~ZAS_BUSY;
12350Sstevel@tonic-gate 			za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
12360Sstevel@tonic-gate 			bp = za->za_xmitblk;
12370Sstevel@tonic-gate 			bp->b_rptr = zs->zs_wr_cur;
12380Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
12390Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
12400Sstevel@tonic-gate 			za->za_xmitblk = NULL;
12410Sstevel@tonic-gate 		}
12420Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
12430Sstevel@tonic-gate 		if (bp)
12440Sstevel@tonic-gate 			(void) putbq(q, bp);
12450Sstevel@tonic-gate 		freemsg(mp);
12460Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
12470Sstevel@tonic-gate 		break;
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 	case M_START:
12500Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
12510Sstevel@tonic-gate 		if (za->za_flags & ZAS_STOPPED) {
12520Sstevel@tonic-gate 			za->za_flags &= ~ZAS_STOPPED;
12530Sstevel@tonic-gate 			/*
12540Sstevel@tonic-gate 			 * If an output operation is in progress,
12550Sstevel@tonic-gate 			 * resume it. Otherwise, prod the start
12560Sstevel@tonic-gate 			 * routine.
12570Sstevel@tonic-gate 			 */
12580Sstevel@tonic-gate 			zsa_start(zs);
12590Sstevel@tonic-gate 		}
12600Sstevel@tonic-gate 		freemsg(mp);
12610Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
12620Sstevel@tonic-gate 		break;
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	case M_IOCTL:
12650Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
12660Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
12670Sstevel@tonic-gate 
12680Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
12690Sstevel@tonic-gate 
12700Sstevel@tonic-gate 		case TIOCGPPS:
12710Sstevel@tonic-gate 			/*
12720Sstevel@tonic-gate 			 * Get PPS state.
12730Sstevel@tonic-gate 			 */
12740Sstevel@tonic-gate 			if (mp->b_cont != NULL)
12750Sstevel@tonic-gate 				freemsg(mp->b_cont);
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 			mp->b_cont = allocb(sizeof (int), BPRI_HI);
12780Sstevel@tonic-gate 			if (mp->b_cont == NULL) {
12790Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
12800Sstevel@tonic-gate 				iocp->ioc_error = ENOMEM;
12810Sstevel@tonic-gate 				ZSA_QREPLY(q, mp);
12820Sstevel@tonic-gate 				break;
12830Sstevel@tonic-gate 			}
12840Sstevel@tonic-gate 			if (za->za_pps)
12850Sstevel@tonic-gate 				*(int *)mp->b_cont->b_wptr = 1;
12860Sstevel@tonic-gate 			else
12870Sstevel@tonic-gate 				*(int *)mp->b_cont->b_wptr = 0;
12880Sstevel@tonic-gate 			mp->b_cont->b_wptr += sizeof (int);
12890Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
12900Sstevel@tonic-gate 			iocp->ioc_count = sizeof (int);
12910Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
12920Sstevel@tonic-gate 			break;
12930Sstevel@tonic-gate 
12940Sstevel@tonic-gate 		case TIOCSPPS:
12950Sstevel@tonic-gate 			/*
12960Sstevel@tonic-gate 			 * Set PPS state.
12970Sstevel@tonic-gate 			 */
12980Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
12990Sstevel@tonic-gate 			if (error != 0) {
13000Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
13010Sstevel@tonic-gate 				iocp->ioc_error = error;
13020Sstevel@tonic-gate 				ZSA_QREPLY(q, mp);
13030Sstevel@tonic-gate 				break;
13040Sstevel@tonic-gate 			}
13050Sstevel@tonic-gate 
13060Sstevel@tonic-gate 			za->za_pps = (*(int *)mp->b_cont->b_rptr != 0);
13070Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
13080Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
13090Sstevel@tonic-gate 			break;
13100Sstevel@tonic-gate 
13110Sstevel@tonic-gate 		case TIOCGPPSEV:
13120Sstevel@tonic-gate 		{
13130Sstevel@tonic-gate 			/*
13140Sstevel@tonic-gate 			 * Get PPS event data.
13150Sstevel@tonic-gate 			 */
13160Sstevel@tonic-gate 			void *buf;
13170Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
13180Sstevel@tonic-gate 			struct ppsclockev32 p32;
13190Sstevel@tonic-gate #endif
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 			if (mp->b_cont != NULL) {
13220Sstevel@tonic-gate 				freemsg(mp->b_cont);
13230Sstevel@tonic-gate 				mp->b_cont = NULL;
13240Sstevel@tonic-gate 			}
13250Sstevel@tonic-gate 			if (za->za_pps == NULL) {
13260Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
13270Sstevel@tonic-gate 				iocp->ioc_error = ENXIO;
13280Sstevel@tonic-gate 				ZSA_QREPLY(q, mp);
13290Sstevel@tonic-gate 				break;
13300Sstevel@tonic-gate 			}
13310Sstevel@tonic-gate 
13320Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
13330Sstevel@tonic-gate 			if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
13340Sstevel@tonic-gate 				TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
13350Sstevel@tonic-gate 				p32.serial = ppsclockev.serial;
13360Sstevel@tonic-gate 				buf = &p32;
13370Sstevel@tonic-gate 				iocp->ioc_count = sizeof (struct ppsclockev32);
13380Sstevel@tonic-gate 			} else
13390Sstevel@tonic-gate #endif
13400Sstevel@tonic-gate 			{
13410Sstevel@tonic-gate 				buf = &ppsclockev;
13420Sstevel@tonic-gate 				iocp->ioc_count = sizeof (struct ppsclockev);
13430Sstevel@tonic-gate 			}
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 			if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
13460Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
13470Sstevel@tonic-gate 				iocp->ioc_error = ENOMEM;
13480Sstevel@tonic-gate 				ZSA_QREPLY(q, mp);
13490Sstevel@tonic-gate 				break;
13500Sstevel@tonic-gate 			}
13510Sstevel@tonic-gate 			mp->b_cont = bp;
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 			bcopy(buf, bp->b_wptr, iocp->ioc_count);
13540Sstevel@tonic-gate 			bp->b_wptr += iocp->ioc_count;
13550Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
13560Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
13570Sstevel@tonic-gate 			break;
13580Sstevel@tonic-gate 		}
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 		case TCSETSW:
13610Sstevel@tonic-gate 		case TCSETSF:
13620Sstevel@tonic-gate 		case TCSETAW:
13630Sstevel@tonic-gate 		case TCSETAF:
13640Sstevel@tonic-gate 		case TCSBRK:
13650Sstevel@tonic-gate 			/*
13660Sstevel@tonic-gate 			 * The changes do not take effect until all
13670Sstevel@tonic-gate 			 * output queued before them is drained.
13680Sstevel@tonic-gate 			 * Put this message on the queue, so that
13690Sstevel@tonic-gate 			 * "zsa_start" will see it when it's done
13700Sstevel@tonic-gate 			 * with the output before it. Poke the
13710Sstevel@tonic-gate 			 * start routine, just in case.
13720Sstevel@tonic-gate 			 */
13730Sstevel@tonic-gate 			(void) putq(q, mp);
13740Sstevel@tonic-gate 			zsa_start(zs);
13750Sstevel@tonic-gate 			break;
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 		default:
13780Sstevel@tonic-gate 			/*
13790Sstevel@tonic-gate 			 * Do it now.
13800Sstevel@tonic-gate 			 */
13810Sstevel@tonic-gate 			zsa_ioctl(za, q, mp);
13820Sstevel@tonic-gate 			break;
13830Sstevel@tonic-gate 		}
13840Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
13850Sstevel@tonic-gate 		break;
13860Sstevel@tonic-gate 
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate 	case M_IOCDATA:
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
13910Sstevel@tonic-gate 		resp = (struct copyresp *)mp->b_rptr;
13920Sstevel@tonic-gate 		if (resp->cp_rval) {
13930Sstevel@tonic-gate 			/*
13940Sstevel@tonic-gate 			 * Just free message on failure.
13950Sstevel@tonic-gate 			 */
13960Sstevel@tonic-gate 			freemsg(mp);
13970Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
13980Sstevel@tonic-gate 			break;
13990Sstevel@tonic-gate 		}
14000Sstevel@tonic-gate 		switch (resp->cp_cmd) {
14010Sstevel@tonic-gate 
14020Sstevel@tonic-gate 		case TIOCMSET:
14030Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
14040Sstevel@tonic-gate 			(void) zsmctl(zs, dmtozs(*(int *)mp->b_cont->b_rptr),
14050Sstevel@tonic-gate 			    DMSET);
14060Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
14070Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
14080Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
14090Sstevel@tonic-gate 			break;
14100Sstevel@tonic-gate 
14110Sstevel@tonic-gate 		case TIOCMBIS:
14120Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
14130Sstevel@tonic-gate 			(void) zsmctl(zs, dmtozs(*(int *)mp->b_cont->b_rptr),
14140Sstevel@tonic-gate 			    DMBIS);
14150Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
14160Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
14170Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
14180Sstevel@tonic-gate 			break;
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 		case TIOCMBIC:
14210Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
14220Sstevel@tonic-gate 			(void) zsmctl(zs, dmtozs(*(int *)mp->b_cont->b_rptr),
14230Sstevel@tonic-gate 			    DMBIC);
14240Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
14250Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
14260Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
14270Sstevel@tonic-gate 			break;
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 		case TIOCMGET:
14300Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
14310Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
14320Sstevel@tonic-gate 			break;
14330Sstevel@tonic-gate 
14340Sstevel@tonic-gate 		default:
14350Sstevel@tonic-gate 			freemsg(mp);
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 		}
14380Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
14390Sstevel@tonic-gate 		break;
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 	case M_FLUSH:
14430Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
14440Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 			/*
14470Sstevel@tonic-gate 			 * Abort any output in progress.
14480Sstevel@tonic-gate 			 */
14490Sstevel@tonic-gate 			if (za->za_flags & ZAS_BUSY) {
14500Sstevel@tonic-gate 				za->za_flags &= ~ZAS_BUSY;
14510Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
14520Sstevel@tonic-gate 				za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
14530Sstevel@tonic-gate 				zs->zs_wr_cur = NULL;
14540Sstevel@tonic-gate 				zs->zs_wr_lim = NULL;
14550Sstevel@tonic-gate 				bp = za->za_xmitblk;
14560Sstevel@tonic-gate 				za->za_xmitblk = NULL;
14570Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14580Sstevel@tonic-gate 				if (bp)
14590Sstevel@tonic-gate 					freemsg(bp);
14600Sstevel@tonic-gate 			}
14610Sstevel@tonic-gate 			/*
14620Sstevel@tonic-gate 			 * Flush our write queue.
14630Sstevel@tonic-gate 			 */
14640Sstevel@tonic-gate 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
14650Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
14660Sstevel@tonic-gate 		}
14670Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
14680Sstevel@tonic-gate 			/*
14690Sstevel@tonic-gate 			 * Flush any data in the temporary receive buffer
14700Sstevel@tonic-gate 			 */
14710Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
14720Sstevel@tonic-gate 			if ((za->za_ttycommon.t_flags & TS_SOFTCAR) ||
14730Sstevel@tonic-gate 			    (SCC_READ0() & ZSRR0_CD)) {
14740Sstevel@tonic-gate 				ZSA_KICK_RCV;
14750Sstevel@tonic-gate 			} else {
14760Sstevel@tonic-gate 			    ZSA_KICK_RCV;
14770Sstevel@tonic-gate 			    if (!(SCC_READ0() & ZSRR0_RX_READY)) {
14780Sstevel@tonic-gate 				/*
14790Sstevel@tonic-gate 				 * settle time for 1 character shift
14800Sstevel@tonic-gate 				 */
14810Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14820Sstevel@tonic-gate 				mutex_exit(zs->zs_excl);
14830Sstevel@tonic-gate 				delay(ztdelay(
14840Sstevel@tonic-gate 				    SPEED(za->za_ttycommon.t_cflag))/3 + 1);
14850Sstevel@tonic-gate 				mutex_enter(zs->zs_excl);
14860Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
14870Sstevel@tonic-gate 				if (!(SCC_READ0() & ZSRR0_CD))
14880Sstevel@tonic-gate 					ZSA_KICK_RCV;
14890Sstevel@tonic-gate 			    }
14900Sstevel@tonic-gate 			    while ((SCC_READ0() &
14910Sstevel@tonic-gate 				(ZSRR0_CD|ZSRR0_RX_READY)) == ZSRR0_RX_READY) {
14920Sstevel@tonic-gate 				/*
14930Sstevel@tonic-gate 				 * Empty Receiver
14940Sstevel@tonic-gate 				 */
14950Sstevel@tonic-gate 				(void) SCC_READDATA();
14960Sstevel@tonic-gate 			    }
14970Sstevel@tonic-gate 			}
14980Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
14990Sstevel@tonic-gate 			flushq(RD(q), FLUSHDATA);
15000Sstevel@tonic-gate 			ZSA_QREPLY(q, mp);
15010Sstevel@tonic-gate 			/*
15020Sstevel@tonic-gate 			 * give the read queues a crack at it
15030Sstevel@tonic-gate 			 */
15040Sstevel@tonic-gate 		} else
15050Sstevel@tonic-gate 			freemsg(mp);
15060Sstevel@tonic-gate 
15070Sstevel@tonic-gate 		/*
15080Sstevel@tonic-gate 		 * We must make sure we process messages that survive the
15090Sstevel@tonic-gate 		 * write-side flush. Without this call, the close protocol
15100Sstevel@tonic-gate 		 * with ldterm can hang forever.  (ldterm will have sent us a
15110Sstevel@tonic-gate 		 * TCSBRK ioctl that it expects a response to.)
15120Sstevel@tonic-gate 		 */
15130Sstevel@tonic-gate 		zsa_start(zs);
15140Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
15150Sstevel@tonic-gate 		break;
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	case M_BREAK:
15180Sstevel@tonic-gate 	case M_DELAY:
15190Sstevel@tonic-gate 	case M_DATA:
15200Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
15210Sstevel@tonic-gate 		/*
15220Sstevel@tonic-gate 		 * Queue the message up to be transmitted,
15230Sstevel@tonic-gate 		 * and poke the start routine.
15240Sstevel@tonic-gate 		 */
15250Sstevel@tonic-gate 		(void) putq(q, mp);
15260Sstevel@tonic-gate 		zsa_start(zs);
15270Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
15280Sstevel@tonic-gate 		break;
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate 	case M_STOPI:
15310Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
15320Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
15330Sstevel@tonic-gate 		za->za_flowc = za->za_ttycommon.t_stopc;
15340Sstevel@tonic-gate 		if ((zs->zs_wr_cur) != NULL) {
15350Sstevel@tonic-gate 			za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
15360Sstevel@tonic-gate 			bp = za->za_xmitblk;
15370Sstevel@tonic-gate 			bp->b_rptr = zs->zs_wr_cur;
15380Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
15390Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
15400Sstevel@tonic-gate 			za->za_xmitblk = NULL;
15410Sstevel@tonic-gate 		}
15420Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
15430Sstevel@tonic-gate 		if (bp)
15440Sstevel@tonic-gate 			(void) putbq(q, bp);
15450Sstevel@tonic-gate 		else
15460Sstevel@tonic-gate 			zsa_start(zs);		/* poke the start routine */
15470Sstevel@tonic-gate 		freemsg(mp);
15480Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
15490Sstevel@tonic-gate 		break;
15500Sstevel@tonic-gate 
15510Sstevel@tonic-gate 	case M_STARTI:
15520Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
15530Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
15540Sstevel@tonic-gate 		za->za_flowc = za->za_ttycommon.t_startc;
15550Sstevel@tonic-gate 		if ((zs->zs_wr_cur) != NULL) {
15560Sstevel@tonic-gate 			za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
15570Sstevel@tonic-gate 			bp = za->za_xmitblk;
15580Sstevel@tonic-gate 			bp->b_rptr = zs->zs_wr_cur;
15590Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
15600Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
15610Sstevel@tonic-gate 			za->za_xmitblk = NULL;
15620Sstevel@tonic-gate 		}
15630Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
15640Sstevel@tonic-gate 		if (bp)
15650Sstevel@tonic-gate 			(void) putbq(q, bp);
15660Sstevel@tonic-gate 		else
15670Sstevel@tonic-gate 			zsa_start(zs);		/* poke the start routine */
15680Sstevel@tonic-gate 		freemsg(mp);
15690Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
15700Sstevel@tonic-gate 		break;
15710Sstevel@tonic-gate 
15720Sstevel@tonic-gate 	case M_CTL:
15730Sstevel@tonic-gate 		if (MBLKL(mp) >= sizeof (struct iocblk) &&
15740Sstevel@tonic-gate 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
15750Sstevel@tonic-gate 			((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
15760Sstevel@tonic-gate 			qreply(q, mp);
15770Sstevel@tonic-gate 		} else {
15780Sstevel@tonic-gate 			/*
15790Sstevel@tonic-gate 			 * These MC_SERVICE type messages are used by upper
15800Sstevel@tonic-gate 			 * modules to tell this driver to send input up
15810Sstevel@tonic-gate 			 * immediately, or that it can wait for normal
15820Sstevel@tonic-gate 			 * processing that may or may not be done. Sun
15830Sstevel@tonic-gate 			 * requires these for the mouse module.
15840Sstevel@tonic-gate 			 */
15850Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
15860Sstevel@tonic-gate 			switch (*mp->b_rptr) {
15870Sstevel@tonic-gate 
15880Sstevel@tonic-gate 			case MC_SERVICEIMM:
15890Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
15900Sstevel@tonic-gate 				za->za_flags |= ZAS_SERVICEIMM;
15910Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
15920Sstevel@tonic-gate 				break;
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 			case MC_SERVICEDEF:
15950Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
15960Sstevel@tonic-gate 				za->za_flags &= ~ZAS_SERVICEIMM;
15970Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
15980Sstevel@tonic-gate 				break;
15990Sstevel@tonic-gate 			}
16000Sstevel@tonic-gate 			freemsg(mp);
16010Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
16020Sstevel@tonic-gate 		}
16030Sstevel@tonic-gate 		break;
16040Sstevel@tonic-gate 
16050Sstevel@tonic-gate 	default:
16060Sstevel@tonic-gate 		/*
16070Sstevel@tonic-gate 		 * "No, I don't want a subscription to Chain Store Age,
16080Sstevel@tonic-gate 		 * thank you anyway."
16090Sstevel@tonic-gate 		 */
16100Sstevel@tonic-gate 		freemsg(mp);
16110Sstevel@tonic-gate 		break;
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate }
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate /*
16160Sstevel@tonic-gate  * zs read service procedure
16170Sstevel@tonic-gate  */
16180Sstevel@tonic-gate static void
16190Sstevel@tonic-gate zsa_rsrv(queue_t *q)
16200Sstevel@tonic-gate {
16210Sstevel@tonic-gate 	struct asyncline	*za;
16220Sstevel@tonic-gate 	struct zscom		*zs;
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	if (((za = (struct asyncline *)q->q_ptr) != NULL) &&
16250Sstevel@tonic-gate 	    (za->za_ttycommon.t_cflag & CRTSXOFF)) {
16260Sstevel@tonic-gate 		zs = za->za_common;
16270Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
16280Sstevel@tonic-gate 		ZSSETSOFT(zs);
16290Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
16300Sstevel@tonic-gate 	}
16310Sstevel@tonic-gate }
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate /*
16340Sstevel@tonic-gate  * Transmitter interrupt service routine.
16350Sstevel@tonic-gate  * If there's more data to transmit in the current pseudo-DMA block,
16360Sstevel@tonic-gate  * and the transmitter is ready, send the next character if output
16370Sstevel@tonic-gate  * is not stopped or draining.
16380Sstevel@tonic-gate  * Otherwise, queue up a soft interrupt.
16390Sstevel@tonic-gate  */
16400Sstevel@tonic-gate static void
16410Sstevel@tonic-gate zsa_txint(struct zscom *zs)
16420Sstevel@tonic-gate {
16430Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
16440Sstevel@tonic-gate 	register uchar_t *wr_cur;
16450Sstevel@tonic-gate 	register uchar_t s0;
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate 	s0 = SCC_READ0();
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	if ((wr_cur = zs->zs_wr_cur) != NULL) {
16500Sstevel@tonic-gate 		if (wr_cur < zs->zs_wr_lim) {
16510Sstevel@tonic-gate 			if ((za->za_ttycommon.t_cflag & CRTSCTS) &&
16520Sstevel@tonic-gate 			    !(s0 & ZSRR0_CTS)) {
16530Sstevel@tonic-gate 				SCC_WRITE0(ZSWR0_RESET_TXINT);
16540Sstevel@tonic-gate 				za->za_rcv_flags_mask |= DO_RETRANSMIT;
16550Sstevel@tonic-gate 				return;
16560Sstevel@tonic-gate 			}
16570Sstevel@tonic-gate 			SCC_WRITEDATA(*wr_cur++);
16580Sstevel@tonic-gate #ifdef ZSA_DEBUG
16590Sstevel@tonic-gate 			za->za_wr++;
16600Sstevel@tonic-gate #endif
16610Sstevel@tonic-gate 			zs->zs_wr_cur = wr_cur;
16620Sstevel@tonic-gate 			zs->zs_flags |= ZS_PROGRESS;
16630Sstevel@tonic-gate 			return;
16640Sstevel@tonic-gate 		} else {
16650Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
16660Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
16670Sstevel@tonic-gate 			/*
16680Sstevel@tonic-gate 			 * Use the rcv_flags_mask as it is set and
16690Sstevel@tonic-gate 			 * test while holding the zs_excl_hi mutex
16700Sstevel@tonic-gate 			 */
16710Sstevel@tonic-gate 			za->za_rcv_flags_mask |= DO_TRANSMIT;
16720Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
16730Sstevel@tonic-gate 			ZSSETSOFT(zs);
16740Sstevel@tonic-gate 			return;
16750Sstevel@tonic-gate 		}
16760Sstevel@tonic-gate 	}
16770Sstevel@tonic-gate 
16780Sstevel@tonic-gate 	if (za->za_flowc != '\0' && (!(za->za_flags & ZAS_DRAINING))) {
16790Sstevel@tonic-gate 		if ((za->za_ttycommon.t_cflag & CRTSCTS) &&
16800Sstevel@tonic-gate 		    !(s0 & ZSRR0_CTS)) {
16810Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
16820Sstevel@tonic-gate 			return;
16830Sstevel@tonic-gate 		}
16840Sstevel@tonic-gate 		SCC_WRITEDATA(za->za_flowc);
16850Sstevel@tonic-gate 		za->za_flowc = '\0';
16860Sstevel@tonic-gate 		return;
16870Sstevel@tonic-gate 	}
16880Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_TXINT);
16890Sstevel@tonic-gate 	/*
16900Sstevel@tonic-gate 	 * Set DO_TRANSMIT bit so that the soft interrupt can
16910Sstevel@tonic-gate 	 * test it and unset the ZAS_BUSY in za_flags while holding
16920Sstevel@tonic-gate 	 * the mutex zs_excl and zs_excl_hi
16930Sstevel@tonic-gate 	 */
16940Sstevel@tonic-gate 	za->za_rcv_flags_mask |= DO_TRANSMIT;
16950Sstevel@tonic-gate 	ZSSETSOFT(zs);
16960Sstevel@tonic-gate }
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate /*
16990Sstevel@tonic-gate  * External/Status interrupt.
17000Sstevel@tonic-gate  */
17010Sstevel@tonic-gate static void
17020Sstevel@tonic-gate zsa_xsint(struct zscom *zs)
17030Sstevel@tonic-gate {
17040Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
17050Sstevel@tonic-gate 	register uchar_t s0, x0;
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	s0 = SCC_READ0();
17080Sstevel@tonic-gate 	ZSA_R0_LOG(s0);
17090Sstevel@tonic-gate 	x0 = s0 ^ za->za_rr0;
17100Sstevel@tonic-gate 	za->za_rr0 = s0;
17110Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);
17120Sstevel@tonic-gate 
17130Sstevel@tonic-gate 	/*
17140Sstevel@tonic-gate 	 * PPS (Pulse Per Second) support.
17150Sstevel@tonic-gate 	 */
17160Sstevel@tonic-gate 	if (za->za_pps && (x0 & ZSRR0_CD) && (s0 & ZSRR0_CD)) {
17170Sstevel@tonic-gate 		/*
17180Sstevel@tonic-gate 		 * This code captures a timestamp at the designated
17190Sstevel@tonic-gate 		 * transition of the PPS signal (CD asserted).  The
17200Sstevel@tonic-gate 		 * code provides a pointer to the timestamp, as well
17210Sstevel@tonic-gate 		 * as the hardware counter value at the capture.
17220Sstevel@tonic-gate 		 *
17230Sstevel@tonic-gate 		 * Note: the kernel has nano based time values while
17240Sstevel@tonic-gate 		 * NTP requires micro based, an in-line fast algorithm
17250Sstevel@tonic-gate 		 * to convert nsec to usec is used here -- see hrt2ts()
17260Sstevel@tonic-gate 		 * in common/os/timers.c for a full description.
17270Sstevel@tonic-gate 		 */
17280Sstevel@tonic-gate 		struct timeval *tvp = &ppsclockev.tv;
17290Sstevel@tonic-gate 		timespec_t ts;
17300Sstevel@tonic-gate 		int nsec, usec;
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate 		LED_OFF;
17330Sstevel@tonic-gate 		gethrestime(&ts);
17340Sstevel@tonic-gate 		LED_ON;
17350Sstevel@tonic-gate 		nsec = ts.tv_nsec;
17360Sstevel@tonic-gate 		usec = nsec + (nsec >> 2);
17370Sstevel@tonic-gate 		usec = nsec + (usec >> 1);
17380Sstevel@tonic-gate 		usec = nsec + (usec >> 2);
17390Sstevel@tonic-gate 		usec = nsec + (usec >> 4);
17400Sstevel@tonic-gate 		usec = nsec - (usec >> 3);
17410Sstevel@tonic-gate 		usec = nsec + (usec >> 2);
17420Sstevel@tonic-gate 		usec = nsec + (usec >> 3);
17430Sstevel@tonic-gate 		usec = nsec + (usec >> 4);
17440Sstevel@tonic-gate 		usec = nsec + (usec >> 1);
17450Sstevel@tonic-gate 		usec = nsec + (usec >> 6);
17460Sstevel@tonic-gate 		tvp->tv_usec = usec >> 10;
17470Sstevel@tonic-gate 		tvp->tv_sec = ts.tv_sec;
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 		++ppsclockev.serial;
17500Sstevel@tonic-gate 
17510Sstevel@tonic-gate 		/*
17520Sstevel@tonic-gate 		 * Because the kernel keeps a high-resolution time, pass the
17530Sstevel@tonic-gate 		 * current highres timestamp in tvp and zero in usec.
17540Sstevel@tonic-gate 		 */
17550Sstevel@tonic-gate 		ddi_hardpps(tvp, 0);
17560Sstevel@tonic-gate 	}
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	ZSA_KICK_RCV;
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 	if ((x0 & ZSRR0_BREAK) && (s0 & ZSRR0_BREAK) == 0) {
17610Sstevel@tonic-gate #ifdef SLAVIO_BUG
17620Sstevel@tonic-gate 		/*
17630Sstevel@tonic-gate 		 * ZSRR0_BREAK turned off.  This means that the break sequence
17640Sstevel@tonic-gate 		 * has completed (i.e., the stop bit finally arrived).
17650Sstevel@tonic-gate 		 */
17660Sstevel@tonic-gate 		if ((s0 & ZSRR0_RX_READY) == 0) {
17670Sstevel@tonic-gate 			/*
17680Sstevel@tonic-gate 			 * SLAVIO will generate a separate STATUS change
17690Sstevel@tonic-gate 			 * interrupt when the break sequence completes.
17700Sstevel@tonic-gate 			 * SCC will combine both, taking the higher priority
17710Sstevel@tonic-gate 			 * one, the receive.  Should still see the ext/stat.
17720Sstevel@tonic-gate 			 * bit in REG3 on SCC.  If no ext/stat, it must be
17730Sstevel@tonic-gate 			 * a SLAVIO.
17740Sstevel@tonic-gate 			 */
17750Sstevel@tonic-gate 			za->za_breakoff = 1;
17760Sstevel@tonic-gate 		} else {
17770Sstevel@tonic-gate 			/*
17780Sstevel@tonic-gate 			 * The NUL character in the receiver is part of the
17790Sstevel@tonic-gate 			 * break sequence; it is discarded.
17800Sstevel@tonic-gate 			 */
17810Sstevel@tonic-gate 			(void) SCC_READDATA(); /* swallow null */
17820Sstevel@tonic-gate 		}
17830Sstevel@tonic-gate #else /* SLAVIO_BUG */
17840Sstevel@tonic-gate 		/*
17850Sstevel@tonic-gate 		 * ZSRR0_BREAK turned off.  This means that the break sequence
17860Sstevel@tonic-gate 		 * has completed (i.e., the stop bit finally arrived).  The NUL
17870Sstevel@tonic-gate 		 * character in the receiver is part of the break sequence;
17880Sstevel@tonic-gate 		 * it is discarded.
17890Sstevel@tonic-gate 		 */
17900Sstevel@tonic-gate 		(void) SCC_READDATA(); /* swallow null */
17910Sstevel@tonic-gate #endif /* SLAVIO_BUG */
17920Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate 		/*
17950Sstevel@tonic-gate 		 * Note: this will cause an abort if a break occurs on
17960Sstevel@tonic-gate 		 * the "keyboard device", regardless of whether the
17970Sstevel@tonic-gate 		 * "keyboard device" is a real keyboard or just a
17980Sstevel@tonic-gate 		 * terminal on a serial line. This permits you to
17990Sstevel@tonic-gate 		 * abort a workstation by unplugging the keyboard,
18000Sstevel@tonic-gate 		 * even if the normal abort key sequence isn't working.
18010Sstevel@tonic-gate 		 */
18020Sstevel@tonic-gate 		if ((za->za_dev == kbddev) ||
18030Sstevel@tonic-gate 		    ((za->za_dev == rconsdev) || (za->za_dev == stdindev)) &&
18040Sstevel@tonic-gate 		    (abort_enable != KIOCABORTALTERNATE)) {
18050Sstevel@tonic-gate 			abort_sequence_enter((char *)NULL);
18060Sstevel@tonic-gate 			/*
18070Sstevel@tonic-gate 			 * We just broke into the monitor or debugger,
18080Sstevel@tonic-gate 			 * ignore the break in this case so whatever
18090Sstevel@tonic-gate 			 * random program that was running doesn't get
18100Sstevel@tonic-gate 			 * a SIGINT.
18110Sstevel@tonic-gate 			 */
18120Sstevel@tonic-gate 			return;
18130Sstevel@tonic-gate 		}
18140Sstevel@tonic-gate 		za->za_break = 1;
18150Sstevel@tonic-gate 	}
18160Sstevel@tonic-gate 
18170Sstevel@tonic-gate 	/*
18180Sstevel@tonic-gate 	 * If hardware flow control is enabled, (re)start output
18190Sstevel@tonic-gate 	 * when CTS is reasserted.
18200Sstevel@tonic-gate 	 */
18210Sstevel@tonic-gate 	if ((za->za_ttycommon.t_cflag & CRTSCTS) &&
18220Sstevel@tonic-gate 	    (x0 & ZSRR0_CTS) && (s0 & ZSRR0_CTS) &&
18230Sstevel@tonic-gate 	    (za->za_rcv_flags_mask & DO_RETRANSMIT))
18240Sstevel@tonic-gate 			za->za_rcv_flags_mask |= DO_TRANSMIT;
18250Sstevel@tonic-gate 
18260Sstevel@tonic-gate 	za->za_ext = 1;
18270Sstevel@tonic-gate 	ZSSETSOFT(zs);
18280Sstevel@tonic-gate }
18290Sstevel@tonic-gate 
18300Sstevel@tonic-gate /*
18310Sstevel@tonic-gate  * Receive Interrupt
18320Sstevel@tonic-gate  */
18330Sstevel@tonic-gate static void
18340Sstevel@tonic-gate zsa_rxint(struct zscom *zs)
18350Sstevel@tonic-gate {
18360Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
18370Sstevel@tonic-gate 	register uchar_t c;
18380Sstevel@tonic-gate 	register uchar_t *rd_cur = zs->zs_rd_cur;
18390Sstevel@tonic-gate 	register uchar_t *rd_lim = zs->zs_rd_lim;
18400Sstevel@tonic-gate 	register mblk_t	*bp;
18410Sstevel@tonic-gate 	register uint_t	fm = za->za_rcv_flags_mask;
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 
18440Sstevel@tonic-gate #ifdef ZSA_DEBUG
18450Sstevel@tonic-gate 	za->za_rd++;
18460Sstevel@tonic-gate #endif
18470Sstevel@tonic-gate 	c = (fm >> 16) & (SCC_READDATA());
18480Sstevel@tonic-gate 
18490Sstevel@tonic-gate 	/*
18500Sstevel@tonic-gate 	 * Check for character break sequence
18510Sstevel@tonic-gate 	 */
18520Sstevel@tonic-gate 	if ((abort_enable == KIOCABORTALTERNATE) && (za->za_dev == rconsdev)) {
18530Sstevel@tonic-gate 		if (abort_charseq_recognize(c))
18540Sstevel@tonic-gate 			abort_sequence_enter((char *)NULL);
18550Sstevel@tonic-gate 	}
18560Sstevel@tonic-gate 
18570Sstevel@tonic-gate 	if (!rd_cur) {
18580Sstevel@tonic-gate #ifdef SLAVIO_BUG
18590Sstevel@tonic-gate 		/*
18600Sstevel@tonic-gate 		 * SLAVIO generates FE for the start of break and
18610Sstevel@tonic-gate 		 * during break when parity is set.  End of break is
18620Sstevel@tonic-gate 		 * detected when the first character is received.
18630Sstevel@tonic-gate 		 * This character is always garbage and is thrown away.
18640Sstevel@tonic-gate 		 */
18650Sstevel@tonic-gate 		if (za->za_slav_break) {
18660Sstevel@tonic-gate 			za->za_slav_break = 0;
18670Sstevel@tonic-gate 			za->za_rr0 |= ZSRR0_BREAK;
18680Sstevel@tonic-gate 			zsa_xsint(zs);
18690Sstevel@tonic-gate 			return;
18700Sstevel@tonic-gate 		}
18710Sstevel@tonic-gate #endif /* SLAVIO_BUG */
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 		if (c == 0 && (za->za_rr0 & ZSRR0_BREAK)) {
18740Sstevel@tonic-gate 			/*
18750Sstevel@tonic-gate 			 * A break sequence was under way, and a NUL character
18760Sstevel@tonic-gate 			 * was received. Discard the NUL character, as it is
18770Sstevel@tonic-gate 			 * part of the break sequence; if ZSRR0_BREAK turned
18780Sstevel@tonic-gate 			 * off, indicating that the break sequence has com-
18790Sstevel@tonic-gate 			 * pleted, call "zsa_xsint" to properly handle the
18800Sstevel@tonic-gate 			 * error. It would appear that External/Status
18810Sstevel@tonic-gate 			 * interrupts get lost occasionally, so this ensures
18820Sstevel@tonic-gate 			 * that one is delivered.
18830Sstevel@tonic-gate 			 */
18840Sstevel@tonic-gate 			c = SCC_READ0();
18850Sstevel@tonic-gate 			if (!(c & ZSRR0_BREAK))
18860Sstevel@tonic-gate 				zsa_xsint(zs);
18870Sstevel@tonic-gate 			return;
18880Sstevel@tonic-gate 		}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate #ifdef SLAVIO_BUG
18910Sstevel@tonic-gate 		if (c == 0 && za->za_breakoff) {
18920Sstevel@tonic-gate 			/*
18930Sstevel@tonic-gate 			 * A break sequence completed, but SLAVIO generates
18940Sstevel@tonic-gate 			 * the NULL character interrupt late, so we throw the
18950Sstevel@tonic-gate 			 * NULL away now.
18960Sstevel@tonic-gate 			 */
18970Sstevel@tonic-gate 			return;
18980Sstevel@tonic-gate 		}
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate 		/*
19010Sstevel@tonic-gate 		 * make sure it gets cleared.
19020Sstevel@tonic-gate 		 */
19030Sstevel@tonic-gate 		za->za_breakoff = 0;
19040Sstevel@tonic-gate #endif /* SLAVIO_BUG */
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 		ZSA_KICK_RCV;	/* We can have M_BREAK msg */
19070Sstevel@tonic-gate 		ZSA_ALLOCB(bp);
19080Sstevel@tonic-gate 		if (!bp) {
19090Sstevel@tonic-gate 			za->za_sw_overrun++;
19100Sstevel@tonic-gate 			ZSSETSOFT(zs);
19110Sstevel@tonic-gate 			return;
19120Sstevel@tonic-gate 		}
19130Sstevel@tonic-gate 		za->za_rcvblk = bp;
19140Sstevel@tonic-gate 		zs->zs_rd_cur = rd_cur = bp->b_wptr;
19150Sstevel@tonic-gate 		zs->zs_rd_lim = rd_lim = bp->b_datap->db_lim;
19160Sstevel@tonic-gate 		if (za->za_kick_rcv_id == 0)
19170Sstevel@tonic-gate 			ZSSETSOFT(zs);
19180Sstevel@tonic-gate 	}
19190Sstevel@tonic-gate 	if (c == 0377 && (fm & DO_ESC)) {
19200Sstevel@tonic-gate 		if (rd_lim < rd_cur + 2) {
19210Sstevel@tonic-gate 			ZSA_ALLOCB(bp);
19220Sstevel@tonic-gate 			ZSA_KICK_RCV;
19230Sstevel@tonic-gate 			if (!bp) {
19240Sstevel@tonic-gate 				za->za_sw_overrun++;
19250Sstevel@tonic-gate 				return;
19260Sstevel@tonic-gate 			}
19270Sstevel@tonic-gate 			za->za_rcvblk = bp;
19280Sstevel@tonic-gate 			zs->zs_rd_cur = rd_cur = bp->b_wptr;
19290Sstevel@tonic-gate 			zs->zs_rd_lim = rd_lim = bp->b_datap->db_lim;
19300Sstevel@tonic-gate 		}
19310Sstevel@tonic-gate 		*rd_cur++ = c;
19320Sstevel@tonic-gate 	}
19330Sstevel@tonic-gate 
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 	*rd_cur++ = c;
19360Sstevel@tonic-gate 	zs->zs_rd_cur = rd_cur;
19370Sstevel@tonic-gate 
19380Sstevel@tonic-gate 	if (rd_cur == rd_lim) {
19390Sstevel@tonic-gate 		ZSA_KICK_RCV;
19400Sstevel@tonic-gate 	} else if ((fm & DO_STOPC) && (c == (fm & 0xff))) {
19410Sstevel@tonic-gate 		za->za_do_kick_rcv_in_softint = 1;
19420Sstevel@tonic-gate 		ZSSETSOFT(zs);
19430Sstevel@tonic-gate 	}
19440Sstevel@tonic-gate 
19450Sstevel@tonic-gate 	if ((za->za_flags & ZAS_SERVICEIMM) || g_nocluster) {
19460Sstevel@tonic-gate 		/*
19470Sstevel@tonic-gate 		 * Send the data up immediately
19480Sstevel@tonic-gate 		 */
19490Sstevel@tonic-gate 		ZSA_KICK_RCV;
19500Sstevel@tonic-gate 	}
19510Sstevel@tonic-gate }
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate /*
19540Sstevel@tonic-gate  * Special receive condition interrupt handler.
19550Sstevel@tonic-gate  */
19560Sstevel@tonic-gate static void
19570Sstevel@tonic-gate zsa_srint(struct zscom *zs)
19580Sstevel@tonic-gate {
19590Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
19600Sstevel@tonic-gate 	register short s1;
19610Sstevel@tonic-gate 	register uchar_t c;
19620Sstevel@tonic-gate 	register uchar_t c1;
19630Sstevel@tonic-gate 	register mblk_t *bp = za->za_rcvblk;
19640Sstevel@tonic-gate 	register uchar_t *rd_cur = zs->zs_rd_cur;
19650Sstevel@tonic-gate 
19660Sstevel@tonic-gate 	SCC_READ(1, s1);
19670Sstevel@tonic-gate 	if (s1 & (ZSRR1_FE | ZSRR1_PE | ZSRR1_DO)) {
19680Sstevel@tonic-gate 		c = SCC_READDATA();	/* swallow bad character */
19690Sstevel@tonic-gate 	}
19700Sstevel@tonic-gate #ifdef SLAVIO_BUG
19710Sstevel@tonic-gate 	/*
19720Sstevel@tonic-gate 	 * SLAVIO does not handle breaks properly when parity is enabled.
19730Sstevel@tonic-gate 	 *
19740Sstevel@tonic-gate 	 * In general, if a null character is received when a framing
19750Sstevel@tonic-gate 	 * error occurs then it is a break condition and not a real
19760Sstevel@tonic-gate 	 * framing error. The null character must be limited to the
19770Sstevel@tonic-gate 	 * number of bits including the parity bit. For example, a 6
19780Sstevel@tonic-gate 	 * bit character with parity would be null if the lower 7 bits
19790Sstevel@tonic-gate 	 * read from the receive fifo were 0. (The higher order bits are
19800Sstevel@tonic-gate 	 * padded with 1 and/or the stop bits.) The only exception to this
19810Sstevel@tonic-gate 	 * general rule would be an 8 bit null character with parity being
19820Sstevel@tonic-gate 	 * a 1 in the parity bit and a framing error. This exception
19830Sstevel@tonic-gate 	 * can be determined by examining the parity error bit in RREG 1.
19840Sstevel@tonic-gate 	 *
19850Sstevel@tonic-gate 	 * A null character, even parity, 8 bits, no parity error,
19860Sstevel@tonic-gate 	 * (0 0000 0000) with framing error is a break condition.
19870Sstevel@tonic-gate 	 *
19880Sstevel@tonic-gate 	 * A null character, even parity, 8 bits, parity error,
19890Sstevel@tonic-gate 	 * (1 0000 0000) with framing error is a framing error.
19900Sstevel@tonic-gate 	 *
19910Sstevel@tonic-gate 	 * A null character, odd parity, 8 bits, parity error
19920Sstevel@tonic-gate 	 * (0 0000 0000) with framing error is a break condition.
19930Sstevel@tonic-gate 	 *
19940Sstevel@tonic-gate 	 * A null character, odd parity, 8 bits, no parity error,
19950Sstevel@tonic-gate 	 * (1 0000 0000) with framing error is a framing error.
19960Sstevel@tonic-gate 	 */
19970Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & PARENB) {
19980Sstevel@tonic-gate 		switch (za->za_ttycommon.t_cflag & CSIZE) {
19990Sstevel@tonic-gate 
20000Sstevel@tonic-gate 		case CS5:
20010Sstevel@tonic-gate 			c1 = c & 0x3f;
20020Sstevel@tonic-gate 			break;
20030Sstevel@tonic-gate 
20040Sstevel@tonic-gate 		case CS6:
20050Sstevel@tonic-gate 			c1 = c & 0x7f;
20060Sstevel@tonic-gate 			break;
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 		case CS7:
20090Sstevel@tonic-gate 			c1 = c & 0xff;
20100Sstevel@tonic-gate 			break;
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 		case CS8:
20130Sstevel@tonic-gate 			if ((za->za_ttycommon.t_cflag & PARODD) &&
20140Sstevel@tonic-gate 			    !(s1 & ZSRR1_PE))
20150Sstevel@tonic-gate 				c1 = 0xff;
20160Sstevel@tonic-gate 			else if (!(za->za_ttycommon.t_cflag & PARODD) &&
20170Sstevel@tonic-gate 			    (s1 & ZSRR1_PE))
20180Sstevel@tonic-gate 				c1 = 0xff;
20190Sstevel@tonic-gate 			else
20200Sstevel@tonic-gate 				c1 = c;
20210Sstevel@tonic-gate 			break;
20220Sstevel@tonic-gate 		}
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 		/*
20250Sstevel@tonic-gate 		 * We fake start of break condition.
20260Sstevel@tonic-gate 		 */
20270Sstevel@tonic-gate 		if ((s1 & ZSRR1_FE) && c1 == 0) {
20280Sstevel@tonic-gate 			za->za_slav_break = 1;
20290Sstevel@tonic-gate 			return;
20300Sstevel@tonic-gate 		}
20310Sstevel@tonic-gate 	}
20320Sstevel@tonic-gate #endif /* SLAVIO_BUG */
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	if (s1 & ZSRR1_PE) {
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 		/*
20370Sstevel@tonic-gate 		 * Mark the parity error so zsa_process will
20380Sstevel@tonic-gate 		 * notice it and send it up in an M_BREAK
20390Sstevel@tonic-gate 		 * message; ldterm will do the actual parity error
20400Sstevel@tonic-gate 		 * processing
20410Sstevel@tonic-gate 		 */
20420Sstevel@tonic-gate 
20430Sstevel@tonic-gate 		if (bp && zs->zs_rd_cur) {	/* M_DATA msg */
20440Sstevel@tonic-gate 			ZSA_KICK_RCV;
20450Sstevel@tonic-gate 			bp = NULL;
20460Sstevel@tonic-gate 		}
20470Sstevel@tonic-gate 		if (!bp)
20480Sstevel@tonic-gate 			ZSA_ALLOCB(bp);
20490Sstevel@tonic-gate 		if (!bp) {
20500Sstevel@tonic-gate 			za->za_sw_overrun++;
20510Sstevel@tonic-gate 			ZSSETSOFT(zs);
20520Sstevel@tonic-gate 		} else {
20530Sstevel@tonic-gate 			za->za_rcvblk = bp;
20540Sstevel@tonic-gate 			zs->zs_rd_cur = rd_cur = bp->b_wptr;
20550Sstevel@tonic-gate 			zs->zs_rd_lim = bp->b_datap->db_lim;
20560Sstevel@tonic-gate 			*rd_cur++ = c;
20570Sstevel@tonic-gate 			zs->zs_rd_cur = rd_cur;
20580Sstevel@tonic-gate 			bp->b_datap->db_type = M_BREAK;
20590Sstevel@tonic-gate 			if (bp->b_datap->db_lim <= rd_cur)
20600Sstevel@tonic-gate 				ZSA_KICK_RCV;
20610Sstevel@tonic-gate 			za->za_do_kick_rcv_in_softint = 1;
20620Sstevel@tonic-gate 			ZSSETSOFT(zs);
20630Sstevel@tonic-gate 
20640Sstevel@tonic-gate 		}
20650Sstevel@tonic-gate 	}
20660Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_ERRORS);
20670Sstevel@tonic-gate 	if (s1 & ZSRR1_DO) {
20680Sstevel@tonic-gate 		za->za_hw_overrun++;
20690Sstevel@tonic-gate 		ZSSETSOFT(zs);
20700Sstevel@tonic-gate 	}
20710Sstevel@tonic-gate }
20720Sstevel@tonic-gate 
20730Sstevel@tonic-gate /*
20740Sstevel@tonic-gate  * Process software interrupts (or poll)
20750Sstevel@tonic-gate  * Crucial points:
20760Sstevel@tonic-gate  * 3.	BUG - breaks are handled "out-of-band" - their relative position
20770Sstevel@tonic-gate  *	among input events is lost, as well as multiple breaks together.
20780Sstevel@tonic-gate  *	This is probably not a problem in practice.
20790Sstevel@tonic-gate  */
20800Sstevel@tonic-gate static int
20810Sstevel@tonic-gate zsa_softint(struct zscom *zs)
20820Sstevel@tonic-gate {
20830Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
20840Sstevel@tonic-gate 	register uchar_t r0;
20850Sstevel@tonic-gate 	register uchar_t za_kick_active;
20860Sstevel@tonic-gate 	register int	m_error;
20870Sstevel@tonic-gate 	register int	allocbcount = 0;
20880Sstevel@tonic-gate 	register int 	do_ttycommon_qfull = 0;
20890Sstevel@tonic-gate 	boolean_t	hangup = B_FALSE, unhangup = B_FALSE;
20900Sstevel@tonic-gate 	boolean_t	m_break = B_FALSE, wakeup = B_FALSE;
20910Sstevel@tonic-gate 	register queue_t *q;
20920Sstevel@tonic-gate 	register mblk_t	*bp;
20930Sstevel@tonic-gate 	register mblk_t *head = NULL, *tail = NULL;
20940Sstevel@tonic-gate 
20950Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
20960Sstevel@tonic-gate 	if (zs->zs_suspended || (zs->zs_flags & ZS_CLOSED)) {
20970Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
20980Sstevel@tonic-gate 		return (0);
20990Sstevel@tonic-gate 	}
21000Sstevel@tonic-gate 	q = za->za_ttycommon.t_readq;
21010Sstevel@tonic-gate 	if (za->za_flags & ZAS_WOPEN && !q) {
21020Sstevel@tonic-gate 		if (za->za_ext) {
21030Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
21040Sstevel@tonic-gate 			r0 = SCC_READ0();
21050Sstevel@tonic-gate 			za->za_ext = 0;
21060Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
21070Sstevel@tonic-gate 			/*
21080Sstevel@tonic-gate 			 * carrier up?
21090Sstevel@tonic-gate 			 */
21100Sstevel@tonic-gate 			if ((r0 & ZSRR0_CD) ||
21110Sstevel@tonic-gate 			    (za->za_ttycommon.t_flags & TS_SOFTCAR)) {
21120Sstevel@tonic-gate 				/*
21130Sstevel@tonic-gate 				 * carrier present
21140Sstevel@tonic-gate 				 */
21150Sstevel@tonic-gate 				if ((za->za_flags & ZAS_CARR_ON) == 0) {
21160Sstevel@tonic-gate 					za->za_flags |= ZAS_CARR_ON;
21170Sstevel@tonic-gate 					mutex_exit(zs->zs_excl);
21180Sstevel@tonic-gate 					cv_broadcast(&zs->zs_flags_cv);
21190Sstevel@tonic-gate 					return (0);
21200Sstevel@tonic-gate 				}
21210Sstevel@tonic-gate 			}
21220Sstevel@tonic-gate 		}
21230Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
21240Sstevel@tonic-gate 		return (0);
21250Sstevel@tonic-gate 	}
21260Sstevel@tonic-gate 	q = za->za_ttycommon.t_readq;
21270Sstevel@tonic-gate 	if (!q) {
21280Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
21290Sstevel@tonic-gate 		return (0);
21300Sstevel@tonic-gate 	}
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 	m_error = za->za_m_error;
21330Sstevel@tonic-gate 	za->za_m_error = 0;
21340Sstevel@tonic-gate 
21350Sstevel@tonic-gate 	if (za->za_do_kick_rcv_in_softint) {
21360Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
21370Sstevel@tonic-gate 		ZSA_KICK_RCV;
21380Sstevel@tonic-gate 		za->za_do_kick_rcv_in_softint = 0;
21390Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
21400Sstevel@tonic-gate 	}
21410Sstevel@tonic-gate 
21420Sstevel@tonic-gate 	za_kick_active = za->za_kick_active;
21430Sstevel@tonic-gate 
21440Sstevel@tonic-gate 	while (!za_kick_active) {
21450Sstevel@tonic-gate 	    ZSA_SEEQ(bp);
21460Sstevel@tonic-gate 	    if (!bp)
21470Sstevel@tonic-gate 		break;
21480Sstevel@tonic-gate 
21490Sstevel@tonic-gate 	    allocbcount++;
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate 	    if (bp->b_datap->db_type <= QPCTL) {
21520Sstevel@tonic-gate 		if (!(canputnext(q))) {
21530Sstevel@tonic-gate 			if (za->za_grace_flow_control >=
21540Sstevel@tonic-gate 				zsa_grace_flow_control) {
21550Sstevel@tonic-gate 			    if (za->za_ttycommon.t_cflag & CRTSXOFF) {
21560Sstevel@tonic-gate 					allocbcount--;
21570Sstevel@tonic-gate 					break;
21580Sstevel@tonic-gate 			    }
21590Sstevel@tonic-gate 			    ZSA_GETQ(bp);
21600Sstevel@tonic-gate 			    freemsg(bp);
21610Sstevel@tonic-gate 			    do_ttycommon_qfull = 1;
21620Sstevel@tonic-gate 			    continue;
21630Sstevel@tonic-gate 			} else
21640Sstevel@tonic-gate 			    za->za_grace_flow_control++;
21650Sstevel@tonic-gate 		} else
21660Sstevel@tonic-gate 			za->za_grace_flow_control = 0;
21670Sstevel@tonic-gate 	    }
21680Sstevel@tonic-gate 	    ZSA_GETQ(bp);
21690Sstevel@tonic-gate 	    if (!head) {
21700Sstevel@tonic-gate 		head = bp;
21710Sstevel@tonic-gate 	    } else {
21720Sstevel@tonic-gate 		if (!tail)
21730Sstevel@tonic-gate 			tail = head;
21740Sstevel@tonic-gate 		tail->b_next = bp;
21750Sstevel@tonic-gate 		tail = bp;
21760Sstevel@tonic-gate 	    }
21770Sstevel@tonic-gate 	}
21780Sstevel@tonic-gate 
21790Sstevel@tonic-gate 	if (allocbcount)
21800Sstevel@tonic-gate 		ZSA_GETBLOCK(zs, allocbcount);
21810Sstevel@tonic-gate 
21820Sstevel@tonic-gate 	if (za->za_ext) {
21830Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
21840Sstevel@tonic-gate 		r0 = SCC_READ0();
21850Sstevel@tonic-gate 		za->za_ext = 0;
21860Sstevel@tonic-gate 		/*
21870Sstevel@tonic-gate 		 * carrier up?
21880Sstevel@tonic-gate 		 */
21890Sstevel@tonic-gate 		if ((r0 & ZSRR0_CD) ||
21900Sstevel@tonic-gate 			(za->za_ttycommon.t_flags & TS_SOFTCAR)) {
21910Sstevel@tonic-gate 			/*
21920Sstevel@tonic-gate 			 * carrier present
21930Sstevel@tonic-gate 			 */
21940Sstevel@tonic-gate 			if ((za->za_flags & ZAS_CARR_ON) == 0) {
21950Sstevel@tonic-gate 				za->za_flags |= ZAS_CARR_ON;
21960Sstevel@tonic-gate 				unhangup = B_TRUE;
21970Sstevel@tonic-gate 				wakeup = B_TRUE;
21980Sstevel@tonic-gate 			}
21990Sstevel@tonic-gate 		} else {
22000Sstevel@tonic-gate 			if ((za->za_flags & ZAS_CARR_ON) &&
22010Sstevel@tonic-gate 			    !(za->za_ttycommon.t_cflag & CLOCAL)) {
22020Sstevel@tonic-gate 				/*
22030Sstevel@tonic-gate 				 * Carrier went away.
22040Sstevel@tonic-gate 				 * Drop DTR, abort any output in progress,
22050Sstevel@tonic-gate 				 * indicate that output is not stopped, and
22060Sstevel@tonic-gate 				 * send a hangup notification upstream.
22070Sstevel@tonic-gate 				 */
22080Sstevel@tonic-gate 				(void) zsmctl(zs, ZSWR5_DTR, DMBIC);
22090Sstevel@tonic-gate 				if ((za->za_flags & ZAS_BUSY) &&
22100Sstevel@tonic-gate 				    (zs->zs_wr_cur != NULL)) {
22110Sstevel@tonic-gate 					zs->zs_wr_cur = NULL;
22120Sstevel@tonic-gate 					zs->zs_wr_lim = NULL;
22130Sstevel@tonic-gate 				}
22140Sstevel@tonic-gate 				hangup = B_TRUE;
22150Sstevel@tonic-gate 				wakeup = B_TRUE;
22160Sstevel@tonic-gate 				za->za_flags &= ~(ZAS_STOPPED | ZAS_CARR_ON |
22170Sstevel@tonic-gate 				    ZAS_BUSY);
22180Sstevel@tonic-gate 				za->za_rcv_flags_mask &= ~(DO_TRANSMIT |
22190Sstevel@tonic-gate 				    DO_RETRANSMIT);
22200Sstevel@tonic-gate 			}
22210Sstevel@tonic-gate 		}
22220Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22230Sstevel@tonic-gate 		if (hangup && (bp = za->za_xmitblk) != NULL) {
22240Sstevel@tonic-gate 			za->za_xmitblk = NULL;
22250Sstevel@tonic-gate 			freeb(bp);
22260Sstevel@tonic-gate 		}
22270Sstevel@tonic-gate 	}
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 	if (za->za_break != 0) {
22300Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
22310Sstevel@tonic-gate 		r0 = SCC_READ0();
22320Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22330Sstevel@tonic-gate 		if ((r0 & ZSRR0_BREAK) == 0) {
22340Sstevel@tonic-gate 			za->za_break = 0;
22350Sstevel@tonic-gate 			m_break = B_TRUE;
22360Sstevel@tonic-gate 		}
22370Sstevel@tonic-gate 	}
22380Sstevel@tonic-gate 
22390Sstevel@tonic-gate 	/*
22400Sstevel@tonic-gate 	 * If a transmission has finished, indicate that it's
22410Sstevel@tonic-gate 	 * finished, and start that line up again.
22420Sstevel@tonic-gate 	 */
22430Sstevel@tonic-gate 
22440Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
22450Sstevel@tonic-gate 	if (za->za_rcv_flags_mask & DO_TRANSMIT) {
22460Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~DO_TRANSMIT;
22470Sstevel@tonic-gate 		za->za_flags &= ~ZAS_BUSY;
22480Sstevel@tonic-gate 
22490Sstevel@tonic-gate 		if ((za->za_ttycommon.t_cflag & CRTSCTS) &&
22500Sstevel@tonic-gate 		    (za->za_rcv_flags_mask & DO_RETRANSMIT) &&
22510Sstevel@tonic-gate 		    zs->zs_wr_cur)
22520Sstevel@tonic-gate 			bp = NULL;
22530Sstevel@tonic-gate 		else {
22540Sstevel@tonic-gate 			za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
22550Sstevel@tonic-gate 			bp = za->za_xmitblk;
22560Sstevel@tonic-gate 			za->za_xmitblk = 0;
22570Sstevel@tonic-gate 		}
22580Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22590Sstevel@tonic-gate 		if (bp)
22600Sstevel@tonic-gate 			freemsg(bp);
22610Sstevel@tonic-gate 		zsa_start(zs);
22620Sstevel@tonic-gate 		/* if we didn't start anything, then notify waiters */
22630Sstevel@tonic-gate 		if (!(za->za_flags & ZAS_BUSY))
22640Sstevel@tonic-gate 			wakeup = B_TRUE;
22650Sstevel@tonic-gate 	} else {
22660Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22670Sstevel@tonic-gate 	}
22680Sstevel@tonic-gate 
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate 	/*
22710Sstevel@tonic-gate 	 * A note about these overrun bits: all they do is *tell* someone
22720Sstevel@tonic-gate 	 * about an error- They do not track multiple errors. In fact,
22730Sstevel@tonic-gate 	 * you could consider them latched register bits if you like.
22740Sstevel@tonic-gate 	 * We are only interested in printing the error message once for
22750Sstevel@tonic-gate 	 * any cluster of overrun errrors.
22760Sstevel@tonic-gate 	 */
22770Sstevel@tonic-gate 	if ((!za->za_kick_rcv_id) && (zs->zs_rd_cur || za_kick_active)) {
22780Sstevel@tonic-gate 	    if (g_zsticks)
22790Sstevel@tonic-gate 		za->za_kick_rcv_id = timeout(zsa_kick_rcv, zs, g_zsticks);
22800Sstevel@tonic-gate 	    else
22810Sstevel@tonic-gate 		za->za_kick_rcv_id = timeout(zsa_kick_rcv, zs,
22820Sstevel@tonic-gate 		    zsticks[SPEED(za->za_ttycommon.t_cflag)]);
22830Sstevel@tonic-gate 	    za->za_kick_rcv_count = ZA_KICK_RCV_COUNT;
22840Sstevel@tonic-gate 	}
22850Sstevel@tonic-gate 	za->za_soft_active = 1;
22860Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
22870Sstevel@tonic-gate 
22880Sstevel@tonic-gate 	if (!hangup && do_ttycommon_qfull) {
22890Sstevel@tonic-gate 		ttycommon_qfull(&za->za_ttycommon, q);
22900Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
22910Sstevel@tonic-gate 		zsa_start(zs);
22920Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
22930Sstevel@tonic-gate 	}
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate 	if (za->za_hw_overrun > 10) {
22960Sstevel@tonic-gate 		cmn_err(CE_NOTE, "zs%d: silo overflow\n", UNIT(za->za_dev));
22970Sstevel@tonic-gate 		za->za_hw_overrun = 0;
22980Sstevel@tonic-gate 	}
22990Sstevel@tonic-gate 
23000Sstevel@tonic-gate 	if (za->za_sw_overrun > 10) {
23010Sstevel@tonic-gate 		cmn_err(CE_NOTE, "zs%d:ring buffer overflow\n",
23020Sstevel@tonic-gate 		    UNIT(za->za_dev));
23030Sstevel@tonic-gate 		za->za_sw_overrun = 0;
23040Sstevel@tonic-gate 	}
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate 	if (unhangup)
23070Sstevel@tonic-gate 		(void) putnextctl(q, M_UNHANGUP);
23080Sstevel@tonic-gate 
23090Sstevel@tonic-gate 	if (m_break)
23100Sstevel@tonic-gate 		(void) putnextctl(q, M_BREAK);
23110Sstevel@tonic-gate 
23120Sstevel@tonic-gate 	while (head) {
23130Sstevel@tonic-gate 		if (!tail) {
23140Sstevel@tonic-gate 			putnext(q, head);
23150Sstevel@tonic-gate 			break;
23160Sstevel@tonic-gate 		}
23170Sstevel@tonic-gate 		bp = head;
23180Sstevel@tonic-gate 		head = head->b_next;
23190Sstevel@tonic-gate 		bp->b_next = NULL;
23200Sstevel@tonic-gate 		putnext(q, bp);
23210Sstevel@tonic-gate 	}
23220Sstevel@tonic-gate 
23230Sstevel@tonic-gate 	if (hangup) {
23240Sstevel@tonic-gate 		int flushflag;
23250Sstevel@tonic-gate 
23260Sstevel@tonic-gate 		/*
23270Sstevel@tonic-gate 		 * If we're in the midst of close, then flush everything.  Don't
23280Sstevel@tonic-gate 		 * leave stale ioctls lying about.
23290Sstevel@tonic-gate 		 */
23300Sstevel@tonic-gate 		flushflag = (zs->zs_flags & ZS_CLOSING) ? FLUSHALL : FLUSHDATA;
23310Sstevel@tonic-gate 		flushq(za->za_ttycommon.t_writeq, flushflag);
23320Sstevel@tonic-gate 		(void) putnextctl(q, M_HANGUP);
23330Sstevel@tonic-gate 	}
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate 	if (m_error)
23360Sstevel@tonic-gate 		(void) putnextctl1(q, M_ERROR, m_error);
23370Sstevel@tonic-gate 
23380Sstevel@tonic-gate 	za->za_soft_active = 0;
23390Sstevel@tonic-gate 
23400Sstevel@tonic-gate 	if (wakeup || (zs->zs_flags & ZS_CLOSED))
23410Sstevel@tonic-gate 		cv_broadcast(&zs->zs_flags_cv);
23420Sstevel@tonic-gate 
23430Sstevel@tonic-gate 	return (0);
23440Sstevel@tonic-gate }
23450Sstevel@tonic-gate 
23460Sstevel@tonic-gate /*
23470Sstevel@tonic-gate  * Start output on a line, unless it's busy, frozen, or otherwise.
23480Sstevel@tonic-gate  */
23490Sstevel@tonic-gate static void
23500Sstevel@tonic-gate zsa_start(struct zscom *zs)
23510Sstevel@tonic-gate {
23520Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
23530Sstevel@tonic-gate 	register int cc;
23540Sstevel@tonic-gate 	register queue_t *q;
23550Sstevel@tonic-gate 	register mblk_t *bp;
23560Sstevel@tonic-gate 	uchar_t *rptr, *wptr;
23570Sstevel@tonic-gate 
23580Sstevel@tonic-gate 	/*
23590Sstevel@tonic-gate 	 * If the chip is busy (i.e., we're waiting for a break timeout
23600Sstevel@tonic-gate 	 * to expire, or for the current transmission to finish, or for
23610Sstevel@tonic-gate 	 * output to finish draining from chip), don't grab anything new.
23620Sstevel@tonic-gate 	 */
23630Sstevel@tonic-gate 	if ((za->za_flags & (ZAS_BREAK|ZAS_BUSY|ZAS_DRAINING)) ||
23640Sstevel@tonic-gate 	    zs->zs_suspended)
23650Sstevel@tonic-gate 		return;
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSCTS) {
23680Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
23690Sstevel@tonic-gate 		if (za->za_rcv_flags_mask & DO_RETRANSMIT) {
23700Sstevel@tonic-gate 			rptr = zs->zs_wr_cur;
23710Sstevel@tonic-gate 			wptr = zs->zs_wr_lim;
23720Sstevel@tonic-gate 			goto zsa_start_retransmit;
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate 		}
23750Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
23760Sstevel@tonic-gate 	}
23770Sstevel@tonic-gate 
23780Sstevel@tonic-gate 	/*
23790Sstevel@tonic-gate 	 * If we have a flow-control character to transmit, do it now.
23800Sstevel@tonic-gate 	 */
23810Sstevel@tonic-gate 	if (za->za_flowc != '\0') {
23820Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
23830Sstevel@tonic-gate 		if (za->za_ttycommon.t_cflag & CRTSCTS) {
23840Sstevel@tonic-gate 			if ((SCC_READ0() & (ZSRR0_CTS|ZSRR0_TX_READY)) !=
23850Sstevel@tonic-gate 			    (ZSRR0_CTS|ZSRR0_TX_READY)) {
23860Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
23870Sstevel@tonic-gate 				return;
23880Sstevel@tonic-gate 			}
23890Sstevel@tonic-gate 		} else if (!(SCC_READ0() & ZSRR0_TX_READY)) {
23900Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
23910Sstevel@tonic-gate 			return;
23920Sstevel@tonic-gate 		}
23930Sstevel@tonic-gate 
23940Sstevel@tonic-gate 		ZSDELAY();
23950Sstevel@tonic-gate 		SCC_WRITEDATA(za->za_flowc);
23960Sstevel@tonic-gate 		za->za_flowc = '\0';
23970Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
23980Sstevel@tonic-gate 		return;
23990Sstevel@tonic-gate 	}
24000Sstevel@tonic-gate 
24010Sstevel@tonic-gate 	/*
24020Sstevel@tonic-gate 	 * If we're waiting for a delay timeout to expire, don't grab
24030Sstevel@tonic-gate 	 * anything new.
24040Sstevel@tonic-gate 	 */
24050Sstevel@tonic-gate 	if (za->za_flags & ZAS_DELAY)
24060Sstevel@tonic-gate 		return;
24070Sstevel@tonic-gate 
24080Sstevel@tonic-gate 	if ((q = za->za_ttycommon.t_writeq) == NULL)
24090Sstevel@tonic-gate 		return;	/* not attached to a stream */
24100Sstevel@tonic-gate 
24110Sstevel@tonic-gate zsa_start_again:
24120Sstevel@tonic-gate 	for (;;) {
24130Sstevel@tonic-gate 		if ((bp = getq(q)) == NULL)
24140Sstevel@tonic-gate 			return;	/* no data to transmit */
24150Sstevel@tonic-gate 
24160Sstevel@tonic-gate 		/*
24170Sstevel@tonic-gate 		 * We have a message block to work on.
24180Sstevel@tonic-gate 		 * Check whether it's a break, a delay, or an ioctl (the latter
24190Sstevel@tonic-gate 		 * occurs if the ioctl in question was waiting for the output
24200Sstevel@tonic-gate 		 * to drain). If it's one of those, process it immediately.
24210Sstevel@tonic-gate 		 */
24220Sstevel@tonic-gate 		switch (bp->b_datap->db_type) {
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate 		case M_BREAK:
24250Sstevel@tonic-gate 			/*
24260Sstevel@tonic-gate 			 * Set the break bit, and arrange for "zsa_restart"
24270Sstevel@tonic-gate 			 * to be called in 1/4 second; it will turn the
24280Sstevel@tonic-gate 			 * break bit off, and call "zsa_start" to grab
24290Sstevel@tonic-gate 			 * the next message.
24300Sstevel@tonic-gate 			 */
24310Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
24320Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_BREAK);
24330Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
24340Sstevel@tonic-gate 			if (!za->za_zsa_restart_id) {
24350Sstevel@tonic-gate 				za->za_zsa_restart_id =
24360Sstevel@tonic-gate 				    timeout(zsa_restart, zs, hz/4);
24370Sstevel@tonic-gate 			}
24380Sstevel@tonic-gate 			za->za_flags |= ZAS_BREAK;
24390Sstevel@tonic-gate 			freemsg(bp);
24400Sstevel@tonic-gate 			return;	/* wait for this to finish */
24410Sstevel@tonic-gate 
24420Sstevel@tonic-gate 		case M_DELAY:
24430Sstevel@tonic-gate 			/*
24440Sstevel@tonic-gate 			 * Arrange for "zsa_restart" to be called when the
24450Sstevel@tonic-gate 			 * delay expires; it will turn MTS_DELAY off,
24460Sstevel@tonic-gate 			 * and call "zsa_start" to grab the next message.
24470Sstevel@tonic-gate 			 */
24480Sstevel@tonic-gate 			if (! za->za_zsa_restart_id) {
24490Sstevel@tonic-gate 				za->za_zsa_restart_id = timeout(zsa_restart,
24500Sstevel@tonic-gate 				    zs,
24510Sstevel@tonic-gate 				    (int)(*(unsigned char *)bp->b_rptr + 6));
24520Sstevel@tonic-gate 			}
24530Sstevel@tonic-gate 			za->za_flags |= ZAS_DELAY;
24540Sstevel@tonic-gate 			freemsg(bp);
24550Sstevel@tonic-gate 			return;	/* wait for this to finish */
24560Sstevel@tonic-gate 
24570Sstevel@tonic-gate 		case M_IOCTL:
24580Sstevel@tonic-gate 			/*
24590Sstevel@tonic-gate 			 * This ioctl was waiting for the output ahead of
24600Sstevel@tonic-gate 			 * it to drain; obviously, it has. Do it, and
24610Sstevel@tonic-gate 			 * then grab the next message after it.
24620Sstevel@tonic-gate 			 */
24630Sstevel@tonic-gate 			zsa_ioctl(za, q, bp);
24640Sstevel@tonic-gate 			continue;
24650Sstevel@tonic-gate 		default: /* M_DATA */
24660Sstevel@tonic-gate 			goto zsa_start_transmit;
24670Sstevel@tonic-gate 		}
24680Sstevel@tonic-gate 
24690Sstevel@tonic-gate 	}
24700Sstevel@tonic-gate zsa_start_transmit:
24710Sstevel@tonic-gate 	/*
24720Sstevel@tonic-gate 	 * We have data to transmit. If output is stopped, put
24730Sstevel@tonic-gate 	 * it back and try again later.
24740Sstevel@tonic-gate 	 */
24750Sstevel@tonic-gate 	if (za->za_flags & ZAS_STOPPED) {
24760Sstevel@tonic-gate 		(void) putbq(q, bp);
24770Sstevel@tonic-gate 		return;
24780Sstevel@tonic-gate 	}
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 	za->za_xmitblk = bp;
24810Sstevel@tonic-gate 	rptr = bp->b_rptr;
24820Sstevel@tonic-gate 	wptr = bp->b_wptr;
24830Sstevel@tonic-gate 	cc = wptr - rptr;
24840Sstevel@tonic-gate 	bp = bp->b_cont;
24850Sstevel@tonic-gate 	if (bp != NULL) {
24860Sstevel@tonic-gate 		za->za_xmitblk->b_cont = NULL;
24870Sstevel@tonic-gate 		(void) putbq(q, bp);	/* not done with this message yet */
24880Sstevel@tonic-gate 	}
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 	if (rptr >= wptr) {
24910Sstevel@tonic-gate 		freeb(za->za_xmitblk);
24920Sstevel@tonic-gate 		za->za_xmitblk = NULL;
24930Sstevel@tonic-gate 		goto zsa_start_again;
24940Sstevel@tonic-gate 	}
24950Sstevel@tonic-gate 
24960Sstevel@tonic-gate 	/*
24970Sstevel@tonic-gate 	 * In 5-bit mode, the high order bits are used
24980Sstevel@tonic-gate 	 * to indicate character sizes less than five,
24990Sstevel@tonic-gate 	 * so we need to explicitly mask before transmitting
25000Sstevel@tonic-gate 	 */
25010Sstevel@tonic-gate 	if ((za->za_ttycommon.t_cflag & CSIZE) == CS5) {
25020Sstevel@tonic-gate 		register unsigned char *p = rptr;
25030Sstevel@tonic-gate 		register int cnt = cc;
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate 		while (cnt--)
25060Sstevel@tonic-gate 			*p++ &= (unsigned char) 0x1f;
25070Sstevel@tonic-gate 	}
25080Sstevel@tonic-gate 
25090Sstevel@tonic-gate 	/*
25100Sstevel@tonic-gate 	 * Set up this block for pseudo-DMA.
25110Sstevel@tonic-gate 	 */
25120Sstevel@tonic-gate 
25130Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
25140Sstevel@tonic-gate 	zs->zs_wr_cur = rptr;
25150Sstevel@tonic-gate 	zs->zs_wr_lim = wptr;
25160Sstevel@tonic-gate 
25170Sstevel@tonic-gate zsa_start_retransmit:
25180Sstevel@tonic-gate 	za->za_rcv_flags_mask &= ~DO_TRANSMIT;
25190Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSCTS) {
25200Sstevel@tonic-gate 		if ((SCC_READ0() & (ZSRR0_CTS|ZSRR0_TX_READY)) !=
25210Sstevel@tonic-gate 			(ZSRR0_CTS|ZSRR0_TX_READY)) {
25220Sstevel@tonic-gate 			za->za_rcv_flags_mask |= DO_RETRANSMIT;
25230Sstevel@tonic-gate 			za->za_flags |= ZAS_BUSY;
25240Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
25250Sstevel@tonic-gate 			return;
25260Sstevel@tonic-gate 		}
25270Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
25280Sstevel@tonic-gate 	} else if (!(SCC_READ0() & ZSRR0_TX_READY)) {
25290Sstevel@tonic-gate 			za->za_flags |= ZAS_BUSY;
25300Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
25310Sstevel@tonic-gate 			return;
25320Sstevel@tonic-gate 	}
25330Sstevel@tonic-gate 	/*
25340Sstevel@tonic-gate 	 * If the transmitter is ready, shove the first
25350Sstevel@tonic-gate 	 * character out.
25360Sstevel@tonic-gate 	 */
25370Sstevel@tonic-gate 	ZSDELAY();
25380Sstevel@tonic-gate 	SCC_WRITEDATA(*rptr++);
25390Sstevel@tonic-gate #ifdef ZSA_DEBUG
25400Sstevel@tonic-gate 	za->za_wr++;
25410Sstevel@tonic-gate #endif
25420Sstevel@tonic-gate 	zs->zs_wr_cur = rptr;
25430Sstevel@tonic-gate 	za->za_flags |= ZAS_BUSY;
25440Sstevel@tonic-gate 	zs->zs_flags |= ZS_PROGRESS;
25450Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
25460Sstevel@tonic-gate }
25470Sstevel@tonic-gate 
25480Sstevel@tonic-gate /*
25490Sstevel@tonic-gate  * Restart output on a line after a delay or break timer expired.
25500Sstevel@tonic-gate  */
25510Sstevel@tonic-gate static void
25520Sstevel@tonic-gate zsa_restart(void *arg)
25530Sstevel@tonic-gate {
25540Sstevel@tonic-gate 	struct zscom *zs = arg;
25550Sstevel@tonic-gate 	struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
25560Sstevel@tonic-gate 
25570Sstevel@tonic-gate 	/*
25580Sstevel@tonic-gate 	 * If break timer expired, turn off the break bit.
25590Sstevel@tonic-gate 	 */
25600Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
25610Sstevel@tonic-gate 	if (!za->za_zsa_restart_id) {
25620Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
25630Sstevel@tonic-gate 		return;
25640Sstevel@tonic-gate 	}
25650Sstevel@tonic-gate 	za->za_zsa_restart_id = 0;
25660Sstevel@tonic-gate 	if (za->za_flags & ZAS_BREAK) {
25670Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
25680Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_BREAK);
25690Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
25700Sstevel@tonic-gate 	}
25710Sstevel@tonic-gate 	za->za_flags &= ~(ZAS_DELAY|ZAS_BREAK);
25720Sstevel@tonic-gate 	if (za->za_ttycommon.t_writeq != NULL)
25730Sstevel@tonic-gate 		zsa_start(zs);
25740Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
25750Sstevel@tonic-gate 	cv_broadcast(&zs->zs_flags_cv);
25760Sstevel@tonic-gate }
25770Sstevel@tonic-gate 
25780Sstevel@tonic-gate /*
25790Sstevel@tonic-gate  * See if the receiver has any data after zs_tick delay
25800Sstevel@tonic-gate  */
25810Sstevel@tonic-gate static void
25820Sstevel@tonic-gate zsa_kick_rcv(void *arg)
25830Sstevel@tonic-gate {
25840Sstevel@tonic-gate 	struct zscom *zs = arg;
25850Sstevel@tonic-gate 	struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
25860Sstevel@tonic-gate 	queue_t *q;
25870Sstevel@tonic-gate 	int	tmp;
25880Sstevel@tonic-gate 	mblk_t	*mp;
25890Sstevel@tonic-gate 	uchar_t za_soft_active, za_kick_active;
25900Sstevel@tonic-gate 	int	allocbcount = 0;
25910Sstevel@tonic-gate 	int do_ttycommon_qfull = 0;
25920Sstevel@tonic-gate 	mblk_t *head = NULL, *tail = NULL;
25930Sstevel@tonic-gate 
25940Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
25950Sstevel@tonic-gate 	if (za->za_kick_rcv_id == 0 || (zs->zs_flags & ZS_CLOSED)) {
25960Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
25970Sstevel@tonic-gate 		return;
25980Sstevel@tonic-gate 	}
25990Sstevel@tonic-gate 	za_soft_active = za->za_soft_active;
26000Sstevel@tonic-gate 	za_kick_active = za->za_kick_active;
26010Sstevel@tonic-gate 	q = za->za_ttycommon.t_readq;
26020Sstevel@tonic-gate 	if (!q) {
26030Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
26040Sstevel@tonic-gate 		return;
26050Sstevel@tonic-gate 	}
26060Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
26070Sstevel@tonic-gate 	if (zs->zs_rd_cur) {
26080Sstevel@tonic-gate 		ZSA_KICK_RCV;
26090Sstevel@tonic-gate 		za->za_kick_rcv_count = tmp = ZA_KICK_RCV_COUNT;
26100Sstevel@tonic-gate 	} else
26110Sstevel@tonic-gate 		tmp = --za->za_kick_rcv_count;
26120Sstevel@tonic-gate 	if (tmp > 0 || za_soft_active || za_kick_active) {
26130Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
26140Sstevel@tonic-gate 		if (g_zsticks)
26150Sstevel@tonic-gate 			za->za_kick_rcv_id = timeout(zsa_kick_rcv,
26160Sstevel@tonic-gate 			    zs, g_zsticks);
26170Sstevel@tonic-gate 		else
26180Sstevel@tonic-gate 			za->za_kick_rcv_id = timeout(zsa_kick_rcv,
26190Sstevel@tonic-gate 			    zs, zsticks[SPEED(za->za_ttycommon.t_cflag)]);
26200Sstevel@tonic-gate 		if (za_soft_active || za_kick_active) {
26210Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
26220Sstevel@tonic-gate 			return;
26230Sstevel@tonic-gate 		}
26240Sstevel@tonic-gate 	} else {
26250Sstevel@tonic-gate 		za->za_kick_rcv_id = 0;
26260Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
26270Sstevel@tonic-gate 	}
26280Sstevel@tonic-gate 
26290Sstevel@tonic-gate 
26300Sstevel@tonic-gate 	for (;;) {
26310Sstevel@tonic-gate 	    ZSA_SEEQ(mp);
26320Sstevel@tonic-gate 	    if (!mp)
26330Sstevel@tonic-gate 		break;
26340Sstevel@tonic-gate 
26350Sstevel@tonic-gate 	    allocbcount++;
26360Sstevel@tonic-gate 
26370Sstevel@tonic-gate 	    if (mp->b_datap->db_type <= QPCTL) {
26380Sstevel@tonic-gate 		if (!(canputnext(q))) {
26390Sstevel@tonic-gate 			if (za->za_grace_flow_control >=
26400Sstevel@tonic-gate 				zsa_grace_flow_control) {
26410Sstevel@tonic-gate 			    if (za->za_ttycommon.t_cflag & CRTSXOFF) {
26420Sstevel@tonic-gate 					allocbcount--;
26430Sstevel@tonic-gate 					break;
26440Sstevel@tonic-gate 			    }
26450Sstevel@tonic-gate 			    ZSA_GETQ(mp);
26460Sstevel@tonic-gate 			    freemsg(mp);
26470Sstevel@tonic-gate 			    do_ttycommon_qfull = 1;
26480Sstevel@tonic-gate 			    continue;
26490Sstevel@tonic-gate 			} else
26500Sstevel@tonic-gate 			    za->za_grace_flow_control++;
26510Sstevel@tonic-gate 		} else
26520Sstevel@tonic-gate 			za->za_grace_flow_control = 0;
26530Sstevel@tonic-gate 	    }
26540Sstevel@tonic-gate 	    ZSA_GETQ(mp);
26550Sstevel@tonic-gate 	    if (!head) {
26560Sstevel@tonic-gate 		head = mp;
26570Sstevel@tonic-gate 	    } else {
26580Sstevel@tonic-gate 		if (!tail)
26590Sstevel@tonic-gate 			tail = head;
26600Sstevel@tonic-gate 		tail->b_next = mp;
26610Sstevel@tonic-gate 		tail = mp;
26620Sstevel@tonic-gate 	    }
26630Sstevel@tonic-gate 	}
26640Sstevel@tonic-gate 
26650Sstevel@tonic-gate 	if (allocbcount)
26660Sstevel@tonic-gate 		ZSA_GETBLOCK(zs, allocbcount);
26670Sstevel@tonic-gate 
26680Sstevel@tonic-gate 	za->za_kick_active = 1;
26690Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
26700Sstevel@tonic-gate 
26710Sstevel@tonic-gate 	if (do_ttycommon_qfull) {
26720Sstevel@tonic-gate 		ttycommon_qfull(&za->za_ttycommon, q);
26730Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
26740Sstevel@tonic-gate 		zsa_start(zs);
26750Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
26760Sstevel@tonic-gate 	}
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate 	while (head) {
26790Sstevel@tonic-gate 		if (!tail) {
26800Sstevel@tonic-gate 			putnext(q, head);
26810Sstevel@tonic-gate 			break;
26820Sstevel@tonic-gate 		}
26830Sstevel@tonic-gate 		mp = head;
26840Sstevel@tonic-gate 		head = head->b_next;
26850Sstevel@tonic-gate 		mp->b_next = NULL;
26860Sstevel@tonic-gate 		putnext(q, mp);
26870Sstevel@tonic-gate 
26880Sstevel@tonic-gate 	}
26890Sstevel@tonic-gate 	za->za_kick_active = 0;
26900Sstevel@tonic-gate 
26910Sstevel@tonic-gate 	if (zs->zs_flags & ZS_CLOSED)
26920Sstevel@tonic-gate 		cv_broadcast(&zs->zs_flags_cv);
26930Sstevel@tonic-gate }
26940Sstevel@tonic-gate 
26950Sstevel@tonic-gate /*
26960Sstevel@tonic-gate  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
26970Sstevel@tonic-gate  * the buffer we need.
26980Sstevel@tonic-gate  */
26990Sstevel@tonic-gate static void
27000Sstevel@tonic-gate zsa_reioctl(void *arg)
27010Sstevel@tonic-gate {
27020Sstevel@tonic-gate 	struct asyncline *za = arg;
27030Sstevel@tonic-gate 	struct zscom *zs = za->za_common;
27040Sstevel@tonic-gate 	queue_t *q;
27050Sstevel@tonic-gate 	mblk_t	 *mp;
27060Sstevel@tonic-gate 
27070Sstevel@tonic-gate 	/*
27080Sstevel@tonic-gate 	 * The bufcall is no longer pending.
27090Sstevel@tonic-gate 	 */
27100Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
27110Sstevel@tonic-gate 	if (!za->za_wbufcid) {
27120Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
27130Sstevel@tonic-gate 		return;
27140Sstevel@tonic-gate 	}
27150Sstevel@tonic-gate 	za->za_wbufcid = 0;
27160Sstevel@tonic-gate 	if ((q = za->za_ttycommon.t_writeq) == NULL) {
27170Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
27180Sstevel@tonic-gate 		return;
27190Sstevel@tonic-gate 	}
27200Sstevel@tonic-gate 	if ((mp = za->za_ttycommon.t_iocpending) != NULL) {
27210Sstevel@tonic-gate 		/*
27220Sstevel@tonic-gate 		 * not pending any more
27230Sstevel@tonic-gate 		 */
27240Sstevel@tonic-gate 		za->za_ttycommon.t_iocpending = NULL;
27250Sstevel@tonic-gate 		zsa_ioctl(za, q, mp);
27260Sstevel@tonic-gate 	}
27270Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
27280Sstevel@tonic-gate }
27290Sstevel@tonic-gate 
27300Sstevel@tonic-gate /*
27310Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
27320Sstevel@tonic-gate  * Note that we don't need to get any locks until we are ready to access
27330Sstevel@tonic-gate  * the hardware. Nothing we access until then is going to be altered
27340Sstevel@tonic-gate  * outside of the STREAMS framework, so we should be safe.
27350Sstevel@tonic-gate  */
27360Sstevel@tonic-gate static void
27370Sstevel@tonic-gate zsa_ioctl(struct asyncline *za, queue_t *wq, mblk_t *mp)
27380Sstevel@tonic-gate {
27390Sstevel@tonic-gate 	register struct zscom *zs = za->za_common;
27400Sstevel@tonic-gate 	register struct iocblk *iocp;
27410Sstevel@tonic-gate 	register unsigned datasize;
27420Sstevel@tonic-gate 	int error;
27430Sstevel@tonic-gate 	register mblk_t *tmp;
27440Sstevel@tonic-gate 
27450Sstevel@tonic-gate 	if (za->za_ttycommon.t_iocpending != NULL) {
27460Sstevel@tonic-gate 		/*
27470Sstevel@tonic-gate 		 * We were holding an "ioctl" response pending the
27480Sstevel@tonic-gate 		 * availability of an "mblk" to hold data to be passed up;
27490Sstevel@tonic-gate 		 * another "ioctl" came through, which means that "ioctl"
27500Sstevel@tonic-gate 		 * must have timed out or been aborted.
27510Sstevel@tonic-gate 		 */
27520Sstevel@tonic-gate 		freemsg(za->za_ttycommon.t_iocpending);
27530Sstevel@tonic-gate 		za->za_ttycommon.t_iocpending = NULL;
27540Sstevel@tonic-gate 	}
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
27570Sstevel@tonic-gate 
27580Sstevel@tonic-gate 	/*
27590Sstevel@tonic-gate 	 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
27600Sstevel@tonic-gate 	 * requires a response containing data to be returned to the user,
27610Sstevel@tonic-gate 	 * and no mblk could be allocated for the data.
27620Sstevel@tonic-gate 	 * No such "ioctl" alters our state. Thus, we always go ahead and
27630Sstevel@tonic-gate 	 * do any state-changes the "ioctl" calls for. If we couldn't allocate
27640Sstevel@tonic-gate 	 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
27650Sstevel@tonic-gate 	 * we just call "bufcall" to request that we be called back when we
27660Sstevel@tonic-gate 	 * stand a better chance of allocating the data.
27670Sstevel@tonic-gate 	 */
27680Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
27690Sstevel@tonic-gate 	datasize = ttycommon_ioctl(&za->za_ttycommon, wq, mp, &error);
27700Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
27710Sstevel@tonic-gate 	if (za->za_ttycommon.t_flags & TS_SOFTCAR)
27720Sstevel@tonic-gate 		zssoftCAR[zs->zs_unit] = 1;
27730Sstevel@tonic-gate 	else
27740Sstevel@tonic-gate 		zssoftCAR[zs->zs_unit] = 0;
27750Sstevel@tonic-gate 	if (datasize != 0) {
27760Sstevel@tonic-gate 		if (za->za_wbufcid)
27770Sstevel@tonic-gate 			unbufcall(za->za_wbufcid);
27780Sstevel@tonic-gate 		za->za_wbufcid = bufcall(datasize, BPRI_HI, zsa_reioctl, za);
27790Sstevel@tonic-gate 		return;
27800Sstevel@tonic-gate 	}
27810Sstevel@tonic-gate 
27820Sstevel@tonic-gate 
27830Sstevel@tonic-gate 	if (error == 0) {
27840Sstevel@tonic-gate 		/*
27850Sstevel@tonic-gate 		 * "ttycommon_ioctl" did most of the work; we just use the
27860Sstevel@tonic-gate 		 * data it set up.
27870Sstevel@tonic-gate 		 */
27880Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
27890Sstevel@tonic-gate 
27900Sstevel@tonic-gate 		case TCSETS:
27910Sstevel@tonic-gate 		case TCSETSW:
27920Sstevel@tonic-gate 		case TCSETSF:
27930Sstevel@tonic-gate 		case TCSETA:
27940Sstevel@tonic-gate 		case TCSETAW:
27950Sstevel@tonic-gate 		case TCSETAF:
27960Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
27970Sstevel@tonic-gate 			zsa_program(za, 1);
27980Sstevel@tonic-gate 			zsa_set_za_rcv_flags_mask(za);
27990Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
28000Sstevel@tonic-gate 			break;
28010Sstevel@tonic-gate 		}
28020Sstevel@tonic-gate 	} else if (error < 0) {
28030Sstevel@tonic-gate 		/*
28040Sstevel@tonic-gate 		 * "ttycommon_ioctl" didn't do anything; we process it here.
28050Sstevel@tonic-gate 		 */
28060Sstevel@tonic-gate 		error = 0;
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
28090Sstevel@tonic-gate 
28100Sstevel@tonic-gate 		case TCSBRK:
28110Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
28120Sstevel@tonic-gate 			if (error != 0)
28130Sstevel@tonic-gate 				break;
28140Sstevel@tonic-gate 
28150Sstevel@tonic-gate 			if (*(int *)mp->b_cont->b_rptr == 0) {
28160Sstevel@tonic-gate 				/*
28170Sstevel@tonic-gate 				 * The delay ensures that a 3 byte transmit
28180Sstevel@tonic-gate 				 * fifo is empty.
28190Sstevel@tonic-gate 				 */
28200Sstevel@tonic-gate 				mutex_exit(zs->zs_excl);
28210Sstevel@tonic-gate 				delay(ztdelay(SPEED(za->za_ttycommon.t_cflag)));
28220Sstevel@tonic-gate 				mutex_enter(zs->zs_excl);
28230Sstevel@tonic-gate 
28240Sstevel@tonic-gate 				/*
28250Sstevel@tonic-gate 				 * Set the break bit, and arrange for
28260Sstevel@tonic-gate 				 * "zsa_restart" to be called in 1/4 second;
28270Sstevel@tonic-gate 				 * it will turn the break bit off, and call
28280Sstevel@tonic-gate 				 * "zsa_start" to grab the next message.
28290Sstevel@tonic-gate 				 */
28300Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
28310Sstevel@tonic-gate 				SCC_BIS(5, ZSWR5_BREAK);
28320Sstevel@tonic-gate 				if (!za->za_zsa_restart_id) {
28330Sstevel@tonic-gate 					mutex_exit(zs->zs_excl_hi);
28340Sstevel@tonic-gate 					za->za_zsa_restart_id =
28350Sstevel@tonic-gate 					    timeout(zsa_restart, zs, hz / 4);
28360Sstevel@tonic-gate 					mutex_enter(zs->zs_excl_hi);
28370Sstevel@tonic-gate 				}
28380Sstevel@tonic-gate 				za->za_flags |= ZAS_BREAK;
28390Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
28400Sstevel@tonic-gate 			}
28410Sstevel@tonic-gate 			break;
28420Sstevel@tonic-gate 
28430Sstevel@tonic-gate 		case TIOCSBRK:
28440Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
28450Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_BREAK);
28460Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
28470Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
28480Sstevel@tonic-gate 			break;
28490Sstevel@tonic-gate 
28500Sstevel@tonic-gate 		case TIOCCBRK:
28510Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
28520Sstevel@tonic-gate 			SCC_BIC(5, ZSWR5_BREAK);
28530Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
28540Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
28550Sstevel@tonic-gate 			break;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 		case TIOCMSET:
28580Sstevel@tonic-gate 		case TIOCMBIS:
28590Sstevel@tonic-gate 		case TIOCMBIC: {
28600Sstevel@tonic-gate 			int mlines;
28610Sstevel@tonic-gate 
28620Sstevel@tonic-gate 			if (iocp->ioc_count == TRANSPARENT) {
28630Sstevel@tonic-gate 				mcopyin(mp, NULL, sizeof (int), NULL);
28640Sstevel@tonic-gate 				break;
28650Sstevel@tonic-gate 			}
28660Sstevel@tonic-gate 
28670Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
28680Sstevel@tonic-gate 			if (error != 0)
28690Sstevel@tonic-gate 				break;
28700Sstevel@tonic-gate 
28710Sstevel@tonic-gate 			mlines = *(int *)mp->b_cont->b_rptr;
28720Sstevel@tonic-gate 
28730Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
28740Sstevel@tonic-gate 			switch (iocp->ioc_cmd) {
28750Sstevel@tonic-gate 			case TIOCMSET:
28760Sstevel@tonic-gate 				(void) zsmctl(zs, dmtozs(mlines), DMSET);
28770Sstevel@tonic-gate 				break;
28780Sstevel@tonic-gate 			case TIOCMBIS:
28790Sstevel@tonic-gate 				(void) zsmctl(zs, dmtozs(mlines), DMBIS);
28800Sstevel@tonic-gate 				break;
28810Sstevel@tonic-gate 			case TIOCMBIC:
28820Sstevel@tonic-gate 				(void) zsmctl(zs, dmtozs(mlines), DMBIC);
28830Sstevel@tonic-gate 				break;
28840Sstevel@tonic-gate 			}
28850Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
28860Sstevel@tonic-gate 
28870Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
28880Sstevel@tonic-gate 			break;
28890Sstevel@tonic-gate 		}
28900Sstevel@tonic-gate 
28910Sstevel@tonic-gate 		case TIOCMGET:
28920Sstevel@tonic-gate 			tmp = allocb(sizeof (int), BPRI_MED);
28930Sstevel@tonic-gate 			if (tmp == NULL) {
28940Sstevel@tonic-gate 				error = EAGAIN;
28950Sstevel@tonic-gate 				break;
28960Sstevel@tonic-gate 			}
28970Sstevel@tonic-gate 			if (iocp->ioc_count != TRANSPARENT)
28980Sstevel@tonic-gate 				mioc2ack(mp, tmp, sizeof (int), 0);
28990Sstevel@tonic-gate 			else
29000Sstevel@tonic-gate 				mcopyout(mp, NULL, sizeof (int), NULL, tmp);
29010Sstevel@tonic-gate 
29020Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
29030Sstevel@tonic-gate 			*(int *)mp->b_cont->b_rptr =
29040Sstevel@tonic-gate 			    zstodm(zsmctl(zs, 0, DMGET));
29050Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
29060Sstevel@tonic-gate 			/*
29070Sstevel@tonic-gate 			 * qreply done below
29080Sstevel@tonic-gate 			 */
29090Sstevel@tonic-gate 			break;
29100Sstevel@tonic-gate 
29110Sstevel@tonic-gate 		default:
29120Sstevel@tonic-gate 			/*
29130Sstevel@tonic-gate 			 * If we don't understand it, it's an error. NAK it.
29140Sstevel@tonic-gate 			 */
29150Sstevel@tonic-gate 			error = EINVAL;
29160Sstevel@tonic-gate 			break;
29170Sstevel@tonic-gate 		}
29180Sstevel@tonic-gate 	}
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 	if (error != 0) {
29210Sstevel@tonic-gate 		iocp->ioc_error = error;
29220Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCNAK;
29230Sstevel@tonic-gate 	}
29240Sstevel@tonic-gate 
29250Sstevel@tonic-gate 	ZSA_QREPLY(wq, mp);
29260Sstevel@tonic-gate }
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 
29290Sstevel@tonic-gate static int
29300Sstevel@tonic-gate dmtozs(int bits)
29310Sstevel@tonic-gate {
29320Sstevel@tonic-gate 	register int b = 0;
29330Sstevel@tonic-gate 
29340Sstevel@tonic-gate 	if (bits & TIOCM_CAR)
29350Sstevel@tonic-gate 		b |= ZSRR0_CD;
29360Sstevel@tonic-gate 	if (bits & TIOCM_CTS)
29370Sstevel@tonic-gate 		b |= ZSRR0_CTS;
29380Sstevel@tonic-gate 	if (bits & TIOCM_RTS)
29390Sstevel@tonic-gate 		b |= ZSWR5_RTS;
29400Sstevel@tonic-gate 	if (bits & TIOCM_DTR)
29410Sstevel@tonic-gate 		b |= ZSWR5_DTR;
29420Sstevel@tonic-gate 	return (b);
29430Sstevel@tonic-gate }
29440Sstevel@tonic-gate 
29450Sstevel@tonic-gate static int
29460Sstevel@tonic-gate zstodm(int bits)
29470Sstevel@tonic-gate {
29480Sstevel@tonic-gate 	register int b;
29490Sstevel@tonic-gate 
29500Sstevel@tonic-gate 	b = 0;
29510Sstevel@tonic-gate 	if (bits & ZSRR0_CD)
29520Sstevel@tonic-gate 		b |= TIOCM_CAR;
29530Sstevel@tonic-gate 	if (bits & ZSRR0_CTS)
29540Sstevel@tonic-gate 		b |= TIOCM_CTS;
29550Sstevel@tonic-gate 	if (bits & ZSWR5_RTS)
29560Sstevel@tonic-gate 		b |= TIOCM_RTS;
29570Sstevel@tonic-gate 	if (bits & ZSWR5_DTR)
29580Sstevel@tonic-gate 		b |= TIOCM_DTR;
29590Sstevel@tonic-gate 	return (b);
29600Sstevel@tonic-gate }
29610Sstevel@tonic-gate 
29620Sstevel@tonic-gate /*
29630Sstevel@tonic-gate  * Assemble registers and flags necessary to program the port to our liking.
29640Sstevel@tonic-gate  * For async operation, most of this is based on the values of
29650Sstevel@tonic-gate  * the "c_iflag" and "c_cflag" fields supplied to us.
29660Sstevel@tonic-gate  */
29670Sstevel@tonic-gate static void
29680Sstevel@tonic-gate zsa_program(struct asyncline *za, int setibaud)
29690Sstevel@tonic-gate {
29700Sstevel@tonic-gate 	register struct zscom *zs = za->za_common;
29710Sstevel@tonic-gate 	register struct zs_prog *zspp;
29720Sstevel@tonic-gate 	register int wr3, wr4, wr5, wr15, speed, baudrate, flags = 0;
29730Sstevel@tonic-gate 
29740Sstevel@tonic-gate 	if ((baudrate = SPEED(za->za_ttycommon.t_cflag)) == 0) {
29750Sstevel@tonic-gate 		/*
29760Sstevel@tonic-gate 		 * Hang up line.
29770Sstevel@tonic-gate 		 */
29780Sstevel@tonic-gate 		(void) zsmctl(zs, ZS_OFF, DMSET);
29790Sstevel@tonic-gate 		return;
29800Sstevel@tonic-gate 	}
29810Sstevel@tonic-gate 
29820Sstevel@tonic-gate 	/*
29830Sstevel@tonic-gate 	 * set input speed same as output, as split speed not supported
29840Sstevel@tonic-gate 	 */
29850Sstevel@tonic-gate 	if (setibaud) {
29860Sstevel@tonic-gate 		za->za_ttycommon.t_cflag &= ~(CIBAUD);
29870Sstevel@tonic-gate 		if (baudrate > CBAUD) {
29880Sstevel@tonic-gate 			za->za_ttycommon.t_cflag |= CIBAUDEXT;
29890Sstevel@tonic-gate 			za->za_ttycommon.t_cflag |=
29900Sstevel@tonic-gate 				(((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
29910Sstevel@tonic-gate 		} else {
29920Sstevel@tonic-gate 			za->za_ttycommon.t_cflag &= ~CIBAUDEXT;
29930Sstevel@tonic-gate 			za->za_ttycommon.t_cflag |=
29940Sstevel@tonic-gate 				((baudrate << IBSHIFT) & CIBAUD);
29950Sstevel@tonic-gate 		}
29960Sstevel@tonic-gate 	}
29970Sstevel@tonic-gate 
29980Sstevel@tonic-gate 	/*
29990Sstevel@tonic-gate 	 * Do not allow the console/keyboard device to have its receiver
30000Sstevel@tonic-gate 	 * disabled; doing that would mean you couldn't type an abort
30010Sstevel@tonic-gate 	 * sequence.
30020Sstevel@tonic-gate 	 */
30030Sstevel@tonic-gate 	if ((za->za_dev == rconsdev) || (za->za_dev == kbddev) ||
30040Sstevel@tonic-gate 	    (za->za_dev == stdindev) || (za->za_ttycommon.t_cflag & CREAD))
30050Sstevel@tonic-gate 		wr3 = ZSWR3_RX_ENABLE;
30060Sstevel@tonic-gate 	else
30070Sstevel@tonic-gate 		wr3 = 0;
30080Sstevel@tonic-gate 	wr4 = ZSWR4_X16_CLK;
30090Sstevel@tonic-gate 	wr5 = (zs->zs_wreg[5] & (ZSWR5_RTS|ZSWR5_DTR)) | ZSWR5_TX_ENABLE;
30100Sstevel@tonic-gate 
30110Sstevel@tonic-gate 	if (zsb134_weird && baudrate == B134) {	/* what a joke! */
30120Sstevel@tonic-gate 		/*
30130Sstevel@tonic-gate 		 * XXX - should B134 set all this crap in the compatibility
30140Sstevel@tonic-gate 		 * module, leaving this stuff fairly clean?
30150Sstevel@tonic-gate 		 */
30160Sstevel@tonic-gate 		flags |= ZSP_PARITY_SPECIAL;
30170Sstevel@tonic-gate 		wr3 |= ZSWR3_RX_6;
30180Sstevel@tonic-gate 		wr4 |= ZSWR4_PARITY_ENABLE | ZSWR4_PARITY_EVEN;
30190Sstevel@tonic-gate 		wr4 |= ZSWR4_1_5_STOP;
30200Sstevel@tonic-gate 		wr5 |= ZSWR5_TX_6;
30210Sstevel@tonic-gate 	} else {
30220Sstevel@tonic-gate 
30230Sstevel@tonic-gate 		switch (za->za_ttycommon.t_cflag & CSIZE) {
30240Sstevel@tonic-gate 
30250Sstevel@tonic-gate 		case CS5:
30260Sstevel@tonic-gate 			wr3 |= ZSWR3_RX_5;
30270Sstevel@tonic-gate 			wr5 |= ZSWR5_TX_5;
30280Sstevel@tonic-gate 			break;
30290Sstevel@tonic-gate 
30300Sstevel@tonic-gate 		case CS6:
30310Sstevel@tonic-gate 			wr3 |= ZSWR3_RX_6;
30320Sstevel@tonic-gate 			wr5 |= ZSWR5_TX_6;
30330Sstevel@tonic-gate 			break;
30340Sstevel@tonic-gate 
30350Sstevel@tonic-gate 		case CS7:
30360Sstevel@tonic-gate 			wr3 |= ZSWR3_RX_7;
30370Sstevel@tonic-gate 			wr5 |= ZSWR5_TX_7;
30380Sstevel@tonic-gate 			break;
30390Sstevel@tonic-gate 
30400Sstevel@tonic-gate 		case CS8:
30410Sstevel@tonic-gate 			wr3 |= ZSWR3_RX_8;
30420Sstevel@tonic-gate 			wr5 |= ZSWR5_TX_8;
30430Sstevel@tonic-gate 			break;
30440Sstevel@tonic-gate 		}
30450Sstevel@tonic-gate 
30460Sstevel@tonic-gate 		if (za->za_ttycommon.t_cflag & PARENB) {
30470Sstevel@tonic-gate 			/*
30480Sstevel@tonic-gate 			 * The PARITY_SPECIAL bit causes a special rx
30490Sstevel@tonic-gate 			 * interrupt on parity errors. Turn it on if
30500Sstevel@tonic-gate 			 * we're checking the parity of characters.
30510Sstevel@tonic-gate 			 */
30520Sstevel@tonic-gate 			if (za->za_ttycommon.t_iflag & INPCK)
30530Sstevel@tonic-gate 				flags |= ZSP_PARITY_SPECIAL;
30540Sstevel@tonic-gate 			wr4 |= ZSWR4_PARITY_ENABLE;
30550Sstevel@tonic-gate 			if (!(za->za_ttycommon.t_cflag & PARODD))
30560Sstevel@tonic-gate 				wr4 |= ZSWR4_PARITY_EVEN;
30570Sstevel@tonic-gate 		}
30580Sstevel@tonic-gate 		wr4 |= (za->za_ttycommon.t_cflag & CSTOPB) ?
30590Sstevel@tonic-gate 		    ZSWR4_2_STOP : ZSWR4_1_STOP;
30600Sstevel@tonic-gate 	}
30610Sstevel@tonic-gate 
30620Sstevel@tonic-gate #if 0
30630Sstevel@tonic-gate 	/*
30640Sstevel@tonic-gate 	 * The AUTO_CD_CTS flag enables the hardware flow control feature of
30650Sstevel@tonic-gate 	 * the 8530, which allows the state of CTS and DCD to control the
30660Sstevel@tonic-gate 	 * enabling of the transmitter and receiver, respectively. The
30670Sstevel@tonic-gate 	 * receiver and transmitter still must have their enable bits set in
30680Sstevel@tonic-gate 	 * WR3 and WR5, respectively, for CTS and DCD to be monitored this way.
30690Sstevel@tonic-gate 	 * Hardware flow control can thus be implemented with no help from
30700Sstevel@tonic-gate 	 * software.
30710Sstevel@tonic-gate 	 */
30720Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSCTS)
30730Sstevel@tonic-gate 		wr3 |= ZSWR3_AUTO_CD_CTS;
30740Sstevel@tonic-gate #endif
30750Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CRTSCTS)
30760Sstevel@tonic-gate 		wr15 = ZSR15_BREAK | ZSR15_TX_UNDER | ZSR15_CD | ZSR15_CTS;
30770Sstevel@tonic-gate 	else
30780Sstevel@tonic-gate 		wr15 = ZSR15_BREAK | ZSR15_TX_UNDER | ZSR15_CD;
30790Sstevel@tonic-gate 
30800Sstevel@tonic-gate 	speed = zs->zs_wreg[12] + (zs->zs_wreg[13] << 8);
30810Sstevel@tonic-gate 
30820Sstevel@tonic-gate 	/*
30830Sstevel@tonic-gate 	 * Here we assemble a set of changes to be passed to zs_program.
30840Sstevel@tonic-gate 	 * Note: Write Register 15 must be set to enable BREAK and UNDERrun
30850Sstevel@tonic-gate 	 * interrupts.  It must also enable CD interrupts which, although
30860Sstevel@tonic-gate 	 * not processed by the hardware interrupt handler, will be processed
30870Sstevel@tonic-gate 	 * by zsa_process, indirectly resulting in a SIGHUP being delivered
30880Sstevel@tonic-gate 	 * to the controlling process if CD drops.  CTS interrupts must NOT
30890Sstevel@tonic-gate 	 * be enabled.  We don't use them at all, and they will hang IPC/IPX
30900Sstevel@tonic-gate 	 * systems at boot time if synchronous modems that supply transmit
30910Sstevel@tonic-gate 	 * clock are attached to any of their serial ports.
30920Sstevel@tonic-gate 	 */
30930Sstevel@tonic-gate 	if (((zs->zs_wreg[1] & ZSWR1_PARITY_SPECIAL) &&
30940Sstevel@tonic-gate 	    !(flags & ZSP_PARITY_SPECIAL)) ||
30950Sstevel@tonic-gate 	    (!(zs->zs_wreg[1] & ZSWR1_PARITY_SPECIAL) &&
30960Sstevel@tonic-gate 	    (flags & ZSP_PARITY_SPECIAL)) ||
30970Sstevel@tonic-gate 	    wr3 != zs->zs_wreg[3] || wr4 != zs->zs_wreg[4] ||
30980Sstevel@tonic-gate 	    wr5 != zs->zs_wreg[5] || wr15 != zs->zs_wreg[15] ||
30990Sstevel@tonic-gate 	    speed != zs_speeds[baudrate]) {
31000Sstevel@tonic-gate 
31010Sstevel@tonic-gate 		za->za_flags |= ZAS_DRAINING;
31020Sstevel@tonic-gate 		zspp = &zs_prog[zs->zs_unit];
31030Sstevel@tonic-gate 		zspp->zs = zs;
31040Sstevel@tonic-gate 		zspp->flags = (uchar_t)flags;
31050Sstevel@tonic-gate 		zspp->wr4 = (uchar_t)wr4;
31060Sstevel@tonic-gate 		zspp->wr11 = (uchar_t)(ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD);
31070Sstevel@tonic-gate 
31080Sstevel@tonic-gate 		speed = zs_speeds[baudrate];
31090Sstevel@tonic-gate 		zspp->wr12 = (uchar_t)(speed & 0xff);
31100Sstevel@tonic-gate 		zspp->wr13 = (uchar_t)((speed >> 8) & 0xff);
31110Sstevel@tonic-gate 		zspp->wr3 = (uchar_t)wr3;
31120Sstevel@tonic-gate 		zspp->wr5 = (uchar_t)wr5;
31130Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)wr15;
31140Sstevel@tonic-gate 
31150Sstevel@tonic-gate 		zs_program(zspp);
31160Sstevel@tonic-gate 		za->za_flags &= ~ZAS_DRAINING;
31170Sstevel@tonic-gate 	}
31180Sstevel@tonic-gate }
31190Sstevel@tonic-gate 
31200Sstevel@tonic-gate /*
31210Sstevel@tonic-gate  * Get the current speed of the console and turn it into something
31220Sstevel@tonic-gate  * UNIX knows about - used to preserve console speed when UNIX comes up.
31230Sstevel@tonic-gate  */
31240Sstevel@tonic-gate int
31250Sstevel@tonic-gate zsgetspeed(dev_t dev)
31260Sstevel@tonic-gate {
31270Sstevel@tonic-gate 	register struct zscom *zs;
31280Sstevel@tonic-gate 	register int uspeed, zspeed;
31290Sstevel@tonic-gate 	register uchar_t rr;
31300Sstevel@tonic-gate 
31310Sstevel@tonic-gate 	zs = &zscom[UNIT(dev)];
31320Sstevel@tonic-gate 	SCC_READ(12, zspeed);
31330Sstevel@tonic-gate 	SCC_READ(13, rr);
31340Sstevel@tonic-gate 	zspeed |= rr << 8;
31350Sstevel@tonic-gate 	for (uspeed = 0; uspeed < NSPEED; uspeed++)
31360Sstevel@tonic-gate 		if (zs_speeds[uspeed] == zspeed)
31370Sstevel@tonic-gate 			return (uspeed);
31380Sstevel@tonic-gate 	/*
31390Sstevel@tonic-gate 	 * 9600 baud if we can't figure it out
31400Sstevel@tonic-gate 	 */
31410Sstevel@tonic-gate 	return (ISPEED);
31420Sstevel@tonic-gate }
31430Sstevel@tonic-gate 
31440Sstevel@tonic-gate /*
31450Sstevel@tonic-gate  * callback routine when enough memory is available.
31460Sstevel@tonic-gate  */
31470Sstevel@tonic-gate static void
31480Sstevel@tonic-gate zsa_callback(void *arg)
31490Sstevel@tonic-gate {
31500Sstevel@tonic-gate 	struct zscom *zs = arg;
31510Sstevel@tonic-gate 	struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
31520Sstevel@tonic-gate 	int allocbcount = zsa_rstandby;
31530Sstevel@tonic-gate 
31540Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
31550Sstevel@tonic-gate 	if (za->za_bufcid) {
31560Sstevel@tonic-gate 		za->za_bufcid = 0;
31570Sstevel@tonic-gate 		ZSA_GETBLOCK(zs, allocbcount);
31580Sstevel@tonic-gate 	}
31590Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
31600Sstevel@tonic-gate }
31610Sstevel@tonic-gate 
31620Sstevel@tonic-gate /*
31630Sstevel@tonic-gate  * Set the receiver flags
31640Sstevel@tonic-gate  */
31650Sstevel@tonic-gate static void
31660Sstevel@tonic-gate zsa_set_za_rcv_flags_mask(struct asyncline *za)
31670Sstevel@tonic-gate {
31680Sstevel@tonic-gate 	register uint_t mask;
31690Sstevel@tonic-gate 
31700Sstevel@tonic-gate 	za->za_rcv_flags_mask &= ~0xFF;
31710Sstevel@tonic-gate 	switch (za->za_ttycommon.t_cflag & CSIZE) {
31720Sstevel@tonic-gate 	case CS5:
31730Sstevel@tonic-gate 		mask = 0x1f;
31740Sstevel@tonic-gate 		break;
31750Sstevel@tonic-gate 	case CS6:
31760Sstevel@tonic-gate 		mask = 0x3f;
31770Sstevel@tonic-gate 		break;
31780Sstevel@tonic-gate 	case CS7:
31790Sstevel@tonic-gate 		mask = 0x7f;
31800Sstevel@tonic-gate 		break;
31810Sstevel@tonic-gate 	default:
31820Sstevel@tonic-gate 		mask = 0xff;
31830Sstevel@tonic-gate 	}
31840Sstevel@tonic-gate 
31850Sstevel@tonic-gate 	za->za_rcv_flags_mask &= ~(0xFF << 16);
31860Sstevel@tonic-gate 	za->za_rcv_flags_mask |=  mask << 16;
31870Sstevel@tonic-gate 
31880Sstevel@tonic-gate 	if ((za->za_ttycommon.t_iflag & PARMRK) &&
31890Sstevel@tonic-gate 	    !(za->za_ttycommon.t_iflag & (IGNPAR|ISTRIP))) {
31900Sstevel@tonic-gate 		za->za_rcv_flags_mask |= DO_ESC;
31910Sstevel@tonic-gate 	} else
31920Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~DO_ESC;
31930Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IXON) {
31940Sstevel@tonic-gate 		za->za_rcv_flags_mask |= DO_STOPC;
31950Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~0xFF;
31960Sstevel@tonic-gate 		za->za_rcv_flags_mask |= za->za_ttycommon.t_stopc;
31970Sstevel@tonic-gate 	} else
31980Sstevel@tonic-gate 		za->za_rcv_flags_mask &= ~DO_STOPC;
31990Sstevel@tonic-gate }
32000Sstevel@tonic-gate 
32010Sstevel@tonic-gate static int
32020Sstevel@tonic-gate zsa_suspend(struct zscom *zs)
32030Sstevel@tonic-gate {
32040Sstevel@tonic-gate 	struct asyncline	*za;
32050Sstevel@tonic-gate 	queue_t			*q;
32060Sstevel@tonic-gate 	mblk_t			*bp = NULL;
32070Sstevel@tonic-gate 	timeout_id_t		restart_id, kick_rcv_id;
32080Sstevel@tonic-gate 	struct zs_prog		*zspp;
32090Sstevel@tonic-gate 
32100Sstevel@tonic-gate 	za = (struct asyncline *)&zs->zs_priv_str;
32110Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
32120Sstevel@tonic-gate 	if (zs->zs_suspended) {
32130Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
32140Sstevel@tonic-gate 		return (DDI_SUCCESS);
32150Sstevel@tonic-gate 	}
32160Sstevel@tonic-gate 	zs->zs_suspended = 1;
32170Sstevel@tonic-gate 
32180Sstevel@tonic-gate 	/*
32190Sstevel@tonic-gate 	 * Turn off interrupts and get any bytes in receiver
32200Sstevel@tonic-gate 	 */
32210Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
32220Sstevel@tonic-gate 	SCC_BIC(1, ZSWR1_INIT);
32230Sstevel@tonic-gate 	ZSA_KICK_RCV;
32240Sstevel@tonic-gate 	restart_id = za->za_zsa_restart_id;
32250Sstevel@tonic-gate 	za->za_zsa_restart_id = 0;
32260Sstevel@tonic-gate 	kick_rcv_id = za->za_kick_rcv_id;
32270Sstevel@tonic-gate 	za->za_kick_rcv_id = 0;
32280Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
32290Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
32300Sstevel@tonic-gate 
32310Sstevel@tonic-gate 	/*
32320Sstevel@tonic-gate 	 * Cancel any timeouts
32330Sstevel@tonic-gate 	 */
32340Sstevel@tonic-gate 	if (restart_id)
32350Sstevel@tonic-gate 		(void) untimeout(restart_id);
32360Sstevel@tonic-gate 	if (kick_rcv_id)
32370Sstevel@tonic-gate 		(void) untimeout(kick_rcv_id);
32380Sstevel@tonic-gate 
32390Sstevel@tonic-gate 	/*
32400Sstevel@tonic-gate 	 * Since we have turned off interrupts, zsa_txint will not be called
32410Sstevel@tonic-gate 	 * and no new chars will given to the chip. We just wait for the
32420Sstevel@tonic-gate 	 * current character(s) to drain.
32430Sstevel@tonic-gate 	 */
32440Sstevel@tonic-gate 	delay(ztdelay(za->za_ttycommon.t_cflag & CBAUD));
32450Sstevel@tonic-gate 
32460Sstevel@tonic-gate 	/*
32470Sstevel@tonic-gate 	 * Return remains of partially sent message to queue
32480Sstevel@tonic-gate 	 */
32490Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
32500Sstevel@tonic-gate 	if ((q = za->za_ttycommon.t_writeq) != NULL) {
32510Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
32520Sstevel@tonic-gate 		if ((zs->zs_wr_cur) != NULL) {
32530Sstevel@tonic-gate 			za->za_flags &= ~ZAS_BUSY;
32540Sstevel@tonic-gate 			za->za_rcv_flags_mask &= ~DO_RETRANSMIT;
32550Sstevel@tonic-gate 			bp = za->za_xmitblk;
32560Sstevel@tonic-gate 			bp->b_rptr = zs->zs_wr_cur;
32570Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
32580Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
32590Sstevel@tonic-gate 			za->za_xmitblk = NULL;
32600Sstevel@tonic-gate 		}
32610Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
32620Sstevel@tonic-gate 		if (bp)
32630Sstevel@tonic-gate 			(void) putbq(q, bp);
32640Sstevel@tonic-gate 	}
32650Sstevel@tonic-gate 
32660Sstevel@tonic-gate 	/*
32670Sstevel@tonic-gate 	 * Stop any breaks in progress.
32680Sstevel@tonic-gate 	 */
32690Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
32700Sstevel@tonic-gate 	if (zs->zs_wreg[5] & ZSWR5_BREAK) {
32710Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_BREAK);
32720Sstevel@tonic-gate 		za->za_flags &= ~ZAS_BREAK;
32730Sstevel@tonic-gate 	}
32740Sstevel@tonic-gate 
32750Sstevel@tonic-gate 	/*
32760Sstevel@tonic-gate 	 * Now get a copy of current registers setting.
32770Sstevel@tonic-gate 	 */
32780Sstevel@tonic-gate 	zspp = &zs_prog[zs->zs_unit];
32790Sstevel@tonic-gate 	zspp->zs = zs;
32800Sstevel@tonic-gate 	zspp->flags = 0;
32810Sstevel@tonic-gate 	zspp->wr3 = zs->zs_wreg[3];
32820Sstevel@tonic-gate 	zspp->wr4 = zs->zs_wreg[4];
32830Sstevel@tonic-gate 	zspp->wr5 = zs->zs_wreg[5];
32840Sstevel@tonic-gate 	zspp->wr11 = zs->zs_wreg[11];
32850Sstevel@tonic-gate 	zspp->wr12 = zs->zs_wreg[12];
32860Sstevel@tonic-gate 	zspp->wr13 = zs->zs_wreg[13];
32870Sstevel@tonic-gate 	zspp->wr15 = zs->zs_wreg[15];
32880Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
32890Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
32900Sstevel@tonic-gate 	/*
32910Sstevel@tonic-gate 	 * We do this on the off chance that zsa_close is waiting on a timed
32920Sstevel@tonic-gate 	 * break to complete and nothing else.
32930Sstevel@tonic-gate 	 */
32940Sstevel@tonic-gate 	cv_broadcast(&zs->zs_flags_cv);
32950Sstevel@tonic-gate 	return (DDI_SUCCESS);
32960Sstevel@tonic-gate }
32970Sstevel@tonic-gate 
32980Sstevel@tonic-gate static int
32990Sstevel@tonic-gate zsa_resume(struct zscom *zs)
33000Sstevel@tonic-gate {
33010Sstevel@tonic-gate 	register struct asyncline *za;
33020Sstevel@tonic-gate 	struct zs_prog	*zspp;
33030Sstevel@tonic-gate 
33040Sstevel@tonic-gate 	za = (struct asyncline *)&zs->zs_priv_str;
33050Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
33060Sstevel@tonic-gate 	if (!(zs->zs_suspended)) {
33070Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
33080Sstevel@tonic-gate 		return (DDI_SUCCESS);
33090Sstevel@tonic-gate 	}
33100Sstevel@tonic-gate 
33110Sstevel@tonic-gate 	/*
33120Sstevel@tonic-gate 	 * Restore H/W state
33130Sstevel@tonic-gate 	 */
33140Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
33150Sstevel@tonic-gate 	zspp = &zs_prog[zs->zs_unit];
33160Sstevel@tonic-gate 	zs_program(zspp);
33170Sstevel@tonic-gate 
33180Sstevel@tonic-gate 	/*
33190Sstevel@tonic-gate 	 * Enable all interrupts for this chip and delay to let chip settle
33200Sstevel@tonic-gate 	 */
33210Sstevel@tonic-gate 	SCC_WRITE(9, ZSWR9_MASTER_IE | ZSWR9_VECTOR_INCL_STAT);
33220Sstevel@tonic-gate 	DELAY(4000);
33230Sstevel@tonic-gate 
33240Sstevel@tonic-gate 	/*
33250Sstevel@tonic-gate 	 * Restart receiving and transmitting
33260Sstevel@tonic-gate 	 */
33270Sstevel@tonic-gate 	zs->zs_suspended = 0;
33280Sstevel@tonic-gate 	za->za_rcv_flags_mask |= DO_TRANSMIT;
33290Sstevel@tonic-gate 	za->za_ext = 1;
33300Sstevel@tonic-gate 	ZSSETSOFT(zs);
33310Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
33320Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
33330Sstevel@tonic-gate 
33340Sstevel@tonic-gate 	return (DDI_SUCCESS);
33350Sstevel@tonic-gate }
33360Sstevel@tonic-gate 
33370Sstevel@tonic-gate #ifdef ZSA_DEBUG
33380Sstevel@tonic-gate static void
33390Sstevel@tonic-gate zsa_print_info(struct zscom *zs)
33400Sstevel@tonic-gate {
33410Sstevel@tonic-gate 	register struct asyncline *za = (struct asyncline *)&zs->zs_priv_str;
33420Sstevel@tonic-gate 	register queue_t *q = za->za_ttycommon.t_writeq;
33430Sstevel@tonic-gate 
33440Sstevel@tonic-gate 	printf(" next q=%s\n", (RD(q))->q_next->q_qinfo->qi_minfo->mi_idname);
33450Sstevel@tonic-gate 	printf("unit=%d\n", zs->zs_unit);
33460Sstevel@tonic-gate 	printf("tflag:\n");
33470Sstevel@tonic-gate 	if (za->za_ttycommon.t_flags & TS_SOFTCAR) printf(" t_fl:TS_SOFTCAR");
33480Sstevel@tonic-gate 	if (za->za_ttycommon.t_flags & TS_XCLUDE) printf(" t_fl:TS_XCLUDE");
33490Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IGNBRK) printf(" t_ifl:IGNBRK");
33500Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & BRKINT) printf(" t_ifl:BRKINT");
33510Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IGNPAR) printf(" t_ifl:IGNPAR");
33520Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & PARMRK) printf(" t_ifl:PARMRK");
33530Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & INPCK) printf(" t_ifl:INPCK");
33540Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & ISTRIP) printf(" t_ifl:ISTRIP");
33550Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & INLCR) printf(" t_ifl:INLCR");
33560Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IGNCR) printf(" t_ifl:IGNCR");
33570Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & ICRNL) printf(" t_ifl:ICRNL");
33580Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IUCLC) printf(" t_ifl:IUCLC");
33590Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IXON) printf(" t_ifl:IXON");
33600Sstevel@tonic-gate 	if (za->za_ttycommon.t_iflag & IXOFF) printf(" t_ifl:IXOFF");
33610Sstevel@tonic-gate 
33620Sstevel@tonic-gate 	printf("\n");
33630Sstevel@tonic-gate 
33640Sstevel@tonic-gate 
33650Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CSIZE == CS5) printf(" t_cfl:CS5");
33660Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CSIZE == CS6) printf(" t_cfl:CS6");
33670Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CSIZE == CS7) printf(" t_cfl:CS7");
33680Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CSIZE == CS8) printf(" t_cfl:CS8");
33690Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CSTOPB) printf(" t_cfl:CSTOPB");
33700Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CREAD) printf(" t_cfl:CREAD");
33710Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & PARENB) printf(" t_cfl:PARENB");
33720Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & PARODD) printf(" t_cfl:PARODD");
33730Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & HUPCL) printf(" t_cfl:HUPCL");
33740Sstevel@tonic-gate 	if (za->za_ttycommon.t_cflag & CLOCAL) printf(" t_cfl:CLOCAL");
33750Sstevel@tonic-gate 	printf(" t_stopc=%x", za->za_ttycommon.t_stopc);
33760Sstevel@tonic-gate 	printf("\n");
33770Sstevel@tonic-gate }
33780Sstevel@tonic-gate #endif
33790Sstevel@tonic-gate 
33800Sstevel@tonic-gate /*
33810Sstevel@tonic-gate  * Check for abort character sequence
33820Sstevel@tonic-gate  */
33830Sstevel@tonic-gate static boolean_t
33840Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch)
33850Sstevel@tonic-gate {
33860Sstevel@tonic-gate 	static int state = 0;
33870Sstevel@tonic-gate #define	CNTRL(c) ((c)&037)
33880Sstevel@tonic-gate 	static char sequence[] = { '\r', '~', CNTRL('b') };
33890Sstevel@tonic-gate 
33900Sstevel@tonic-gate 	if (ch == sequence[state]) {
33910Sstevel@tonic-gate 		if (++state >= sizeof (sequence)) {
33920Sstevel@tonic-gate 			state = 0;
33930Sstevel@tonic-gate 			return (B_TRUE);
33940Sstevel@tonic-gate 		}
33950Sstevel@tonic-gate 	} else {
33960Sstevel@tonic-gate 		state = (ch == sequence[0]) ? 1 : 0;
33970Sstevel@tonic-gate 	}
33980Sstevel@tonic-gate 	return (B_FALSE);
33990Sstevel@tonic-gate }
3400