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