xref: /illumos-gate/usr/src/uts/sun/io/zs_hdlc.c (revision 360e6f5e7a29d5950aa1985f56811731715da7e5)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*360e6f5eSmathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  *	HDLC protocol handler for Z8530 SCC.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include	<sys/param.h>
347c478bd9Sstevel@tonic-gate #include	<sys/systm.h>
357c478bd9Sstevel@tonic-gate #include	<sys/types.h>
367c478bd9Sstevel@tonic-gate #include	<sys/sysmacros.h>
377c478bd9Sstevel@tonic-gate #include	<sys/kmem.h>
387c478bd9Sstevel@tonic-gate #include	<sys/stropts.h>
397c478bd9Sstevel@tonic-gate #include	<sys/stream.h>
407c478bd9Sstevel@tonic-gate #include	<sys/strsun.h>
417c478bd9Sstevel@tonic-gate #include	<sys/stat.h>
427c478bd9Sstevel@tonic-gate #include	<sys/cred.h>
437c478bd9Sstevel@tonic-gate #include	<sys/user.h>
447c478bd9Sstevel@tonic-gate #include	<sys/proc.h>
457c478bd9Sstevel@tonic-gate #include	<sys/file.h>
467c478bd9Sstevel@tonic-gate #include	<sys/uio.h>
477c478bd9Sstevel@tonic-gate #include	<sys/buf.h>
487c478bd9Sstevel@tonic-gate #include	<sys/mkdev.h>
497c478bd9Sstevel@tonic-gate #include	<sys/cmn_err.h>
507c478bd9Sstevel@tonic-gate #include	<sys/errno.h>
517c478bd9Sstevel@tonic-gate #include	<sys/fcntl.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include	<sys/zsdev.h>
547c478bd9Sstevel@tonic-gate #include	<sys/ser_sync.h>
557c478bd9Sstevel@tonic-gate #include	<sys/conf.h>
567c478bd9Sstevel@tonic-gate #include	<sys/ddi.h>
577c478bd9Sstevel@tonic-gate #include	<sys/sunddi.h>
587c478bd9Sstevel@tonic-gate #include	<sys/dlpi.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #define	ZSH_TRACING
617c478bd9Sstevel@tonic-gate #ifdef	ZSH_TRACING
627c478bd9Sstevel@tonic-gate #include	<sys/vtrace.h>
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * Temp tracepoint definitions
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate #define	TR_ZSH		50
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #define	TR_ZSH_TXINT	1
707c478bd9Sstevel@tonic-gate #define	TR_ZSH_XSINT	2
717c478bd9Sstevel@tonic-gate #define	TR_ZSH_RXINT	3
727c478bd9Sstevel@tonic-gate #define	TR_ZSH_SRINT	4
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #define	TR_ZSH_WPUT_START		5
757c478bd9Sstevel@tonic-gate #define	TR_ZSH_WPUT_END			6
767c478bd9Sstevel@tonic-gate #define	TR_ZSH_START_START		7
777c478bd9Sstevel@tonic-gate #define	TR_ZSH_START_END		8
787c478bd9Sstevel@tonic-gate #define	TR_ZSH_SOFT_START		9
797c478bd9Sstevel@tonic-gate #define	TR_ZSH_SOFT_END			10
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #define	TR_ZSH_OPEN	 11
827c478bd9Sstevel@tonic-gate #define	TR_ZSH_CLOSE	12
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate #endif	/* ZSH_TRACING */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate /*
877c478bd9Sstevel@tonic-gate  * Logging definitions
887c478bd9Sstevel@tonic-gate  */
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * #define	ZSH_DEBUG
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate #ifdef ZSH_DEBUG
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate #ifdef ZS_DEBUG_ALL
967c478bd9Sstevel@tonic-gate extern	char	zs_h_log[];
977c478bd9Sstevel@tonic-gate extern	int	zs_h_log_n;
987c478bd9Sstevel@tonic-gate #define	zsh_h_log_add(c) \
997c478bd9Sstevel@tonic-gate 	{ \
1007c478bd9Sstevel@tonic-gate 		if (zs_h_log_n >= ZS_H_LOG_MAX) \
1017c478bd9Sstevel@tonic-gate 			zs_h_log_n = 0; \
1027c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = 'A' + zs->zs_unit; \
1037c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = c; \
1047c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n] = '\0'; \
1057c478bd9Sstevel@tonic-gate 	}
1067c478bd9Sstevel@tonic-gate #define	zsh_h_log_clear
1077c478bd9Sstevel@tonic-gate #else
1087c478bd9Sstevel@tonic-gate #define	ZSH_H_LOG_MAX   0x8000
1097c478bd9Sstevel@tonic-gate char zsh_h_log[2][ZSH_H_LOG_MAX +10];
1107c478bd9Sstevel@tonic-gate int zsh_h_log_n[2];
1117c478bd9Sstevel@tonic-gate #define	zsh_h_log_add(c) \
1127c478bd9Sstevel@tonic-gate 	{ \
1137c478bd9Sstevel@tonic-gate 		if (zsh_h_log_n[zs->zs_unit] >= ZSH_H_LOG_MAX) \
1147c478bd9Sstevel@tonic-gate 			zsh_h_log_n[zs->zs_unit] = 0; \
1157c478bd9Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]++] = c; \
1167c478bd9Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]] = '\0'; \
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate #define	zsh_h_log_clear \
1207c478bd9Sstevel@tonic-gate 	{ register char *p; \
1217c478bd9Sstevel@tonic-gate 	for (p = &zsh_h_log[zs->zs_unit][ZSH_H_LOG_MAX]; \
1227c478bd9Sstevel@tonic-gate 		p >= &zsh_h_log[zs->zs_unit][0]; p--) \
1237c478bd9Sstevel@tonic-gate 		*p = '\0'; \
1247c478bd9Sstevel@tonic-gate 	zsh_h_log_n[zs->zs_unit] = 0; \
1257c478bd9Sstevel@tonic-gate 	}
1267c478bd9Sstevel@tonic-gate #endif
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate #define	ZSH_R0_LOG(r0)  { \
1297c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_RX_READY) zsh_h_log_add('R'); \
1307c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TIMER) zsh_h_log_add('Z'); \
1317c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TX_READY) zsh_h_log_add('T'); \
1327c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_CD) zsh_h_log_add('D'); \
1337c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_SYNC) zsh_h_log_add('S'); \
1347c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_CTS) zsh_h_log_add('C'); \
1357c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TXUNDER) zsh_h_log_add('U'); \
1367c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_BREAK) zsh_h_log_add('B'); \
1377c478bd9Sstevel@tonic-gate 	}
1387c478bd9Sstevel@tonic-gate #endif
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate char _depends_on[] = "drv/zs";
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate #ifndef	MAXZSH
1447c478bd9Sstevel@tonic-gate #define	MAXZSH	2
1457c478bd9Sstevel@tonic-gate #define	MAXZSHCLONES	(80)	/* three clone opens per instance */
1467c478bd9Sstevel@tonic-gate #endif	/* MAXZSH */
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate int maxzsh = MAXZSH;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate int zsh_timer_count = 10;
1517c478bd9Sstevel@tonic-gate int zsh_default_mru = 1024;
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate struct ser_str *zsh_str = NULL;
1547c478bd9Sstevel@tonic-gate unsigned char zsh_usedminor[MAXZSHCLONES];
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * The HDLC protocol
1597c478bd9Sstevel@tonic-gate  */
1607c478bd9Sstevel@tonic-gate int zsh_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
1617c478bd9Sstevel@tonic-gate static int  zsh_probe(dev_info_t *dev);
1627c478bd9Sstevel@tonic-gate static int  zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1637c478bd9Sstevel@tonic-gate static int  zsh_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1647c478bd9Sstevel@tonic-gate static int  zsh_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
1657c478bd9Sstevel@tonic-gate static int  zsh_close(queue_t *rq, int flag);
1667c478bd9Sstevel@tonic-gate static void zsh_wput(queue_t *wq, mblk_t *mp);
1677c478bd9Sstevel@tonic-gate static int zsh_start(struct zscom *zs, struct syncline *zss);
1687c478bd9Sstevel@tonic-gate static void zsh_ioctl(queue_t *wq, mblk_t *mp);
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate static struct module_info hdlc_minfo = {
1717c478bd9Sstevel@tonic-gate 	0x5a48,		/* module ID number: "ZH" */
1727c478bd9Sstevel@tonic-gate 	"zsh",		/* module name */
1737c478bd9Sstevel@tonic-gate 	0,		/* minimum packet size accepted */
1747c478bd9Sstevel@tonic-gate 	INFPSZ,		/* maximum packet size accepted */
1757c478bd9Sstevel@tonic-gate 	12*1024,	/* queue high water mark (bytes) */
1767c478bd9Sstevel@tonic-gate 	4*1024		/* queue low water mark (bytes) */
1777c478bd9Sstevel@tonic-gate };
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate static struct qinit hdlc_rinit = {
1807c478bd9Sstevel@tonic-gate 	putq,		/* input put procedure */
1817c478bd9Sstevel@tonic-gate 	NULL,		/* input service procedure */
1827c478bd9Sstevel@tonic-gate 	zsh_open,	/* open procedure */
1837c478bd9Sstevel@tonic-gate 	zsh_close,	/* close procedure */
1847c478bd9Sstevel@tonic-gate 	NULL,		/* reserved */
1857c478bd9Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
1867c478bd9Sstevel@tonic-gate 	NULL		/* reserved */
1877c478bd9Sstevel@tonic-gate };
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate static struct qinit hdlc_winit = {
1907c478bd9Sstevel@tonic-gate 	(int (*)())zsh_wput,	/* output put procedure */
1917c478bd9Sstevel@tonic-gate 	NULL,		/* output service procedure */
1927c478bd9Sstevel@tonic-gate 	NULL,		/* open procedure */
1937c478bd9Sstevel@tonic-gate 	NULL,		/* close procedure */
1947c478bd9Sstevel@tonic-gate 	NULL,		/* reserved */
1957c478bd9Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
1967c478bd9Sstevel@tonic-gate 	NULL		/* reserved */
1977c478bd9Sstevel@tonic-gate };
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate struct streamtab hdlctab = {
2007c478bd9Sstevel@tonic-gate 	&hdlc_rinit,	/* initialize read queue */
2017c478bd9Sstevel@tonic-gate 	&hdlc_winit,	/* initialize write queue */
2027c478bd9Sstevel@tonic-gate 	NULL,		/* mux read qinit */
2037c478bd9Sstevel@tonic-gate 	NULL		/* mux write qinit */
2047c478bd9Sstevel@tonic-gate };
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(zsh_ops, nulldev, zsh_probe, zsh_attach,
2077c478bd9Sstevel@tonic-gate     zsh_detach, nodev, zsh_info, D_MP, &hdlctab);
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate  * This is the loadable module wrapper.
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate #include	<sys/errno.h>
2147c478bd9Sstevel@tonic-gate #include	<sys/modctl.h>
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate /*
2177c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
2187c478bd9Sstevel@tonic-gate  */
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2217c478bd9Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
2227c478bd9Sstevel@tonic-gate 	"Z8530 serial HDLC drv V%I%",
2237c478bd9Sstevel@tonic-gate 	&zsh_ops,	/* our own ops for this module */
2247c478bd9Sstevel@tonic-gate };
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2277c478bd9Sstevel@tonic-gate 	MODREV_1,
2287c478bd9Sstevel@tonic-gate 	(void *)&modldrv,
2297c478bd9Sstevel@tonic-gate 	NULL
2307c478bd9Sstevel@tonic-gate };
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate int
2337c478bd9Sstevel@tonic-gate _init(void)
2347c478bd9Sstevel@tonic-gate {
2357c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
2367c478bd9Sstevel@tonic-gate }
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate int
2397c478bd9Sstevel@tonic-gate _fini(void)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate int
2457c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2467c478bd9Sstevel@tonic-gate {
2477c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /*
2527c478bd9Sstevel@tonic-gate  * The HDLC interrupt entry points.
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate static void	zsh_txint(struct zscom *zs);
2557c478bd9Sstevel@tonic-gate static void	zsh_xsint(struct zscom *zs);
2567c478bd9Sstevel@tonic-gate static void	zsh_rxint(struct zscom *zs);
2577c478bd9Sstevel@tonic-gate static void	zsh_srint(struct zscom *zs);
2587c478bd9Sstevel@tonic-gate static int	zsh_softint(struct zscom *zs);
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate struct zsops zsops_hdlc = {
2617c478bd9Sstevel@tonic-gate 	zsh_txint,
2627c478bd9Sstevel@tonic-gate 	zsh_xsint,
2637c478bd9Sstevel@tonic-gate 	zsh_rxint,
2647c478bd9Sstevel@tonic-gate 	zsh_srint,
2657c478bd9Sstevel@tonic-gate 	zsh_softint,
2667c478bd9Sstevel@tonic-gate 	NULL,
2677c478bd9Sstevel@tonic-gate 	NULL
2687c478bd9Sstevel@tonic-gate };
2697c478bd9Sstevel@tonic-gate 
2707c478bd9Sstevel@tonic-gate static int	zsh_program(struct zscom *zs, struct scc_mode *sm);
2717c478bd9Sstevel@tonic-gate static void	zsh_setmstat(struct zscom *zs, int event);
2727c478bd9Sstevel@tonic-gate static void	zsh_rxbad(struct zscom *zs, struct syncline *zss);
2737c478bd9Sstevel@tonic-gate static void	zsh_txbad(struct zscom *zs, struct syncline *zss);
2747c478bd9Sstevel@tonic-gate static void	zsh_watchdog(void *);
2757c478bd9Sstevel@tonic-gate static void	zsh_callback(void *);
2767c478bd9Sstevel@tonic-gate static int	zsh_hdp_ok_or_rts_state(struct zscom *zs, struct syncline *zss);
2777c478bd9Sstevel@tonic-gate static void	zsh_init_port(struct zscom *zs, struct syncline *zss);
2787c478bd9Sstevel@tonic-gate static int	zsh_setmode(struct zscom *zs, struct syncline *zss,
2797c478bd9Sstevel@tonic-gate 			struct scc_mode *sm);
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate /*
2837c478bd9Sstevel@tonic-gate  * The HDLC Driver.
2847c478bd9Sstevel@tonic-gate  */
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate /*
2887c478bd9Sstevel@tonic-gate  * Special macros to handle STREAMS operations.
2897c478bd9Sstevel@tonic-gate  * These are required to address memory leakage problems.
2907c478bd9Sstevel@tonic-gate  * WARNING : the macro do NOT call ZSSETSOFT
2917c478bd9Sstevel@tonic-gate  */
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
2957c478bd9Sstevel@tonic-gate  */
2967c478bd9Sstevel@tonic-gate #define	ZSH_GETBLOCK(zs, allocbcount) \
2977c478bd9Sstevel@tonic-gate { \
2987c478bd9Sstevel@tonic-gate 	register int n = ZSH_MAX_RSTANDBY; \
2997c478bd9Sstevel@tonic-gate 	while (--n >= 0) { \
3007c478bd9Sstevel@tonic-gate 	    if (!zss->sl_rstandby[n]) { \
3017c478bd9Sstevel@tonic-gate 		if ((zss->sl_rstandby[n] = \
3027c478bd9Sstevel@tonic-gate 		    allocb(zss->sl_mru, BPRI_MED)) == NULL) { \
3037c478bd9Sstevel@tonic-gate 		    if (zss->sl_bufcid == 0) { \
3047c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi); \
3057c478bd9Sstevel@tonic-gate 			if (zss->sl_txstate != TX_OFF) { \
3067c478bd9Sstevel@tonic-gate 			    mutex_exit(zs->zs_excl_hi); \
3077c478bd9Sstevel@tonic-gate 			    zss->sl_bufcid = bufcall(zss->sl_mru, \
3087c478bd9Sstevel@tonic-gate 				    BPRI_MED, zsh_callback, zs); \
3097c478bd9Sstevel@tonic-gate 			    break; \
3107c478bd9Sstevel@tonic-gate 			} else \
3117c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi); \
3127c478bd9Sstevel@tonic-gate 		    } \
3137c478bd9Sstevel@tonic-gate 		} \
3147c478bd9Sstevel@tonic-gate 		allocbcount--; \
3157c478bd9Sstevel@tonic-gate 	    } \
3167c478bd9Sstevel@tonic-gate 	} \
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate /*
3207c478bd9Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
3217c478bd9Sstevel@tonic-gate  */
3227c478bd9Sstevel@tonic-gate #define	ZSH_ALLOCB(mp) \
3237c478bd9Sstevel@tonic-gate { \
3247c478bd9Sstevel@tonic-gate 	register int n = ZSH_MAX_RSTANDBY; \
3257c478bd9Sstevel@tonic-gate 	mp = NULL; \
3267c478bd9Sstevel@tonic-gate 	while (--n >= 0)  { \
3277c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_rstandby[n]) != NULL) { \
3287c478bd9Sstevel@tonic-gate 			zss->sl_rstandby[n] = NULL; \
3297c478bd9Sstevel@tonic-gate 			break; \
3307c478bd9Sstevel@tonic-gate 		} \
3317c478bd9Sstevel@tonic-gate 	} \
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate #define	ZSH_PUTQ(mp) \
3357c478bd9Sstevel@tonic-gate { \
3367c478bd9Sstevel@tonic-gate 	register int wptr, rptr;  \
3377c478bd9Sstevel@tonic-gate 	wptr = zss->sl_rdone_wptr; \
3387c478bd9Sstevel@tonic-gate 	rptr = zss->sl_rdone_rptr; \
3397c478bd9Sstevel@tonic-gate 	zss->sl_rdone[wptr] = mp; \
3407c478bd9Sstevel@tonic-gate 	if ((wptr) + 1 == ZSH_RDONE_MAX) \
3417c478bd9Sstevel@tonic-gate 		zss->sl_rdone_wptr = wptr = 0; \
3427c478bd9Sstevel@tonic-gate 	else \
3437c478bd9Sstevel@tonic-gate 		zss->sl_rdone_wptr = ++wptr; \
3447c478bd9Sstevel@tonic-gate 	if (wptr == rptr) {  /* Should never occur */ \
3457c478bd9Sstevel@tonic-gate 		SCC_BIC(1, ZSWR1_INIT); \
3467c478bd9Sstevel@tonic-gate 		zss->sl_m_error = ENOSR; \
3477c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs); \
3487c478bd9Sstevel@tonic-gate 	} \
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate #define	ZSH_FREEMSG(mp) \
3527c478bd9Sstevel@tonic-gate { \
3537c478bd9Sstevel@tonic-gate 	ZSH_PUTQ(mp); \
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate /*
3587c478bd9Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
3597c478bd9Sstevel@tonic-gate  */
3607c478bd9Sstevel@tonic-gate #define	ZSH_GETQ(mp) \
3617c478bd9Sstevel@tonic-gate { \
3627c478bd9Sstevel@tonic-gate 	if (zss->sl_rdone_rptr != zss->sl_rdone_wptr) { \
3637c478bd9Sstevel@tonic-gate 		mp = zss->sl_rdone[zss->sl_rdone_rptr++]; \
3647c478bd9Sstevel@tonic-gate 		if (zss->sl_rdone_rptr == ZSH_RDONE_MAX) \
3657c478bd9Sstevel@tonic-gate 				zss->sl_rdone_rptr = 0; \
3667c478bd9Sstevel@tonic-gate 	} else \
3677c478bd9Sstevel@tonic-gate 		mp = NULL; \
3687c478bd9Sstevel@tonic-gate }
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate #define	ZSH_FLUSHQ \
3717c478bd9Sstevel@tonic-gate { \
3727c478bd9Sstevel@tonic-gate 	register mblk_t *tmp; \
3737c478bd9Sstevel@tonic-gate 	for (;;) { \
3747c478bd9Sstevel@tonic-gate 		ZSH_GETQ(tmp); \
3757c478bd9Sstevel@tonic-gate 		if (!(tmp)) \
3767c478bd9Sstevel@tonic-gate 			break; \
3777c478bd9Sstevel@tonic-gate 		freemsg(tmp); \
3787c478bd9Sstevel@tonic-gate 	} \
3797c478bd9Sstevel@tonic-gate }
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3827c478bd9Sstevel@tonic-gate static int
3837c478bd9Sstevel@tonic-gate zsh_probe(dev_info_t *dev)
3847c478bd9Sstevel@tonic-gate {
3857c478bd9Sstevel@tonic-gate 	return (DDI_PROBE_DONTCARE);
3867c478bd9Sstevel@tonic-gate }
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3897c478bd9Sstevel@tonic-gate static int
3907c478bd9Sstevel@tonic-gate zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd)
3917c478bd9Sstevel@tonic-gate {
3927c478bd9Sstevel@tonic-gate 	register int	unit;
3937c478bd9Sstevel@tonic-gate 	char		name[3] = {
3947c478bd9Sstevel@tonic-gate 		'\0', '\0', '\0' 	};
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/*
3977c478bd9Sstevel@tonic-gate 	 * Since zsh is a child of the "pseudo" nexus, we can expect the
3987c478bd9Sstevel@tonic-gate 	 * attach routine to be called only once.  We need to create all
3997c478bd9Sstevel@tonic-gate 	 * necessary devices in one shot.  There is never more than one
4007c478bd9Sstevel@tonic-gate 	 * SCC chip that supports zsh devices.
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
4047c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4057c478bd9Sstevel@tonic-gate 	if (zscom == NULL)
4067c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);	/* zsattach not done */
4077c478bd9Sstevel@tonic-gate 	unit = 2 * ddi_get_instance(dev);
4087c478bd9Sstevel@tonic-gate 	if (unit > 1)
4097c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);	/* only use cpu ports */
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dev, "zsh", S_IFCHR,
4127c478bd9Sstevel@tonic-gate 	    NULL, DDI_PSEUDO, CLONE_DEV) == DDI_FAILURE) {
4137c478bd9Sstevel@tonic-gate 		ddi_remove_minor_node(dev, NULL);
4147c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "zsh clone device creation failed.");
4157c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4167c478bd9Sstevel@tonic-gate 	}
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	for (; unit < maxzsh/2; unit++) {
4197c478bd9Sstevel@tonic-gate 		zscom[unit].zs_hdlc_dip = dev;
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", unit);
4227c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dev, name, S_IFCHR,
4237c478bd9Sstevel@tonic-gate 		    2*unit, DDI_PSEUDO, NULL) == DDI_FAILURE) {
4247c478bd9Sstevel@tonic-gate 			ddi_remove_minor_node(dev, NULL);
4257c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 		unit++;
4287c478bd9Sstevel@tonic-gate 		(void) sprintf(name, "%d", unit);
4297c478bd9Sstevel@tonic-gate 		if (ddi_create_minor_node(dev, name, S_IFCHR,
4307c478bd9Sstevel@tonic-gate 		    2*(unit-1)+1, DDI_PSEUDO, NULL) == DDI_FAILURE) {
4317c478bd9Sstevel@tonic-gate 			ddi_remove_minor_node(dev, NULL);
4327c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
4337c478bd9Sstevel@tonic-gate 		}
4347c478bd9Sstevel@tonic-gate 	}
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4377c478bd9Sstevel@tonic-gate }
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate /* ARGSUSED */
4407c478bd9Sstevel@tonic-gate int
4417c478bd9Sstevel@tonic-gate zsh_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
4427c478bd9Sstevel@tonic-gate void **result)
4437c478bd9Sstevel@tonic-gate {
4447c478bd9Sstevel@tonic-gate 	register dev_t dev = (dev_t)arg;
4457c478bd9Sstevel@tonic-gate 	register int unit, error;
4467c478bd9Sstevel@tonic-gate 	register struct zscom *zs;
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	if ((unit = UNIT(dev)) >= nzs)
4497c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	switch (infocmd) {
4527c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
4537c478bd9Sstevel@tonic-gate 		if (zscom == NULL) {
4547c478bd9Sstevel@tonic-gate 			error = DDI_FAILURE;
4557c478bd9Sstevel@tonic-gate 		} else {
4567c478bd9Sstevel@tonic-gate 			zs = &zscom[unit];
4577c478bd9Sstevel@tonic-gate 			*result = zs->zs_hdlc_dip;
4587c478bd9Sstevel@tonic-gate 			error = DDI_SUCCESS;
4597c478bd9Sstevel@tonic-gate 		}
4607c478bd9Sstevel@tonic-gate 		break;
4617c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
462*360e6f5eSmathue 		*result = (void *)(uintptr_t)(unit / 2);
4637c478bd9Sstevel@tonic-gate 		error = DDI_SUCCESS;
4647c478bd9Sstevel@tonic-gate 		break;
4657c478bd9Sstevel@tonic-gate 	default:
4667c478bd9Sstevel@tonic-gate 		error = DDI_FAILURE;
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate 	return (error);
4697c478bd9Sstevel@tonic-gate }
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate static int
4727c478bd9Sstevel@tonic-gate zsh_detach(dev_info_t *dev, ddi_detach_cmd_t cmd)
4737c478bd9Sstevel@tonic-gate {
4747c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
4757c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dev, NULL);
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4807c478bd9Sstevel@tonic-gate }
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate static void
4837c478bd9Sstevel@tonic-gate zsh_init_port(struct zscom *zs, struct syncline *zss)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate 	register uchar_t s0;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	SCC_WRITE(3, (ZSWR3_RX_ENABLE | ZSWR3_RXCRC_ENABLE | ZSWR3_RX_8));
4887c478bd9Sstevel@tonic-gate 	SCC_WRITE(5, (ZSWR5_TX_8 | ZSWR5_DTR | ZSWR5_TXCRC_ENABLE));
4897c478bd9Sstevel@tonic-gate 	zss->sl_rr0 = SCC_READ0();
4907c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FDXPTP) {
4917c478bd9Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_TX_ENABLE);
4927c478bd9Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_RTS);
4937c478bd9Sstevel@tonic-gate 		s0 = SCC_READ0();
4947c478bd9Sstevel@tonic-gate 		if ((s0 & ZSRR0_CTS) ||
4957c478bd9Sstevel@tonic-gate 		    !(zss->sl_mode.sm_config & (CONN_SIGNAL | CONN_IBM))) {
4967c478bd9Sstevel@tonic-gate 			/*
4977c478bd9Sstevel@tonic-gate 			 * send msg that CTS is up
4987c478bd9Sstevel@tonic-gate 			 */
4997c478bd9Sstevel@tonic-gate 			zss->sl_rr0 |= ZSRR0_CTS;
5007c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_IDLE;
5017c478bd9Sstevel@tonic-gate 		} else {
5027c478bd9Sstevel@tonic-gate 			zss->sl_flags |= SF_XMT_INPROG;
5037c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_RTS;
5047c478bd9Sstevel@tonic-gate 			zss->sl_rr0 &= ~ZSRR0_CTS;
5057c478bd9Sstevel@tonic-gate 			zss->sl_wd_count = zsh_timer_count;
5067c478bd9Sstevel@tonic-gate 			if (!zss->sl_wd_id)
5077c478bd9Sstevel@tonic-gate 				zss->sl_wd_id = timeout(zsh_watchdog,
5087c478bd9Sstevel@tonic-gate 				    zs, SIO_WATCHDOG_TICK);
5097c478bd9Sstevel@tonic-gate 		}
5107c478bd9Sstevel@tonic-gate 	} else {
5117c478bd9Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
5127c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
5137c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
5147c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FLUSH_WQ;
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate /*
5197c478bd9Sstevel@tonic-gate  * Open routine.
5207c478bd9Sstevel@tonic-gate  */
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5237c478bd9Sstevel@tonic-gate static int
5247c478bd9Sstevel@tonic-gate zsh_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
5257c478bd9Sstevel@tonic-gate {
5267c478bd9Sstevel@tonic-gate 	register struct zscom *zs;
5277c478bd9Sstevel@tonic-gate 	register struct syncline *zss;
5287c478bd9Sstevel@tonic-gate 	register struct ser_str *stp;
5297c478bd9Sstevel@tonic-gate 	register int	unit;
5307c478bd9Sstevel@tonic-gate 	register int 	tmp;
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	if (sflag != CLONEOPEN) {
5337c478bd9Sstevel@tonic-gate 		if (rq->q_ptr)
5347c478bd9Sstevel@tonic-gate 			return (EBUSY);  /* We got a stream that is in use */
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 		unit = UNIT(*dev);
5377c478bd9Sstevel@tonic-gate 		if (unit >= maxzsh)
5387c478bd9Sstevel@tonic-gate 			return (ENXIO);  /* unit not configured */
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 		if (zscom == NULL)
5417c478bd9Sstevel@tonic-gate 			return (ENXIO);  /* device not found by autoconfig */
5427c478bd9Sstevel@tonic-gate 		zs = &zscom[unit];
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 		if (zs->zs_ops == NULL) {
5457c478bd9Sstevel@tonic-gate 			return (ENXIO);  /* device not found by autoconfig */
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_OPEN, "zsh_open:unit = %d", unit);
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
5517c478bd9Sstevel@tonic-gate 		if ((zs->zs_ops != &zsops_null) &&
5527c478bd9Sstevel@tonic-gate 		    (zs->zs_ops != &zsops_hdlc)) {
5537c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
5547c478bd9Sstevel@tonic-gate 			return (EBUSY);	 /* another protocol got here first */
5557c478bd9Sstevel@tonic-gate 		}
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 		/* Mark device as busy (for power management) */
5587c478bd9Sstevel@tonic-gate 		(void) pm_busy_component(zs->zs_dip, unit%2+1);
5597c478bd9Sstevel@tonic-gate 		(void) ddi_dev_is_needed(zs->zs_dip, unit%2+1, 1);
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 		zsopinit(zs, &zsops_hdlc);
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 		zss = (struct syncline *)&zscom[unit].zs_priv_str;
5647c478bd9Sstevel@tonic-gate 		stp = &zss->sl_stream;
5657c478bd9Sstevel@tonic-gate 		stp->str_state = NULL;
5667c478bd9Sstevel@tonic-gate 		stp->str_com = (caddr_t)zs;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 		zss->sl_xhead = NULL;
5697c478bd9Sstevel@tonic-gate 		zss->sl_xactb = NULL;
5707c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
5717c478bd9Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
5727c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
5737c478bd9Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
5747c478bd9Sstevel@tonic-gate 		zss->sl_rhead = NULL;
5757c478bd9Sstevel@tonic-gate 		zss->sl_ractb = NULL;
5767c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
5777c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
5787c478bd9Sstevel@tonic-gate 		zss->sl_mstat = NULL;
5797c478bd9Sstevel@tonic-gate 		zss->sl_xstandby = NULL;
5807c478bd9Sstevel@tonic-gate 		zss->sl_wd_id = 0;
5817c478bd9Sstevel@tonic-gate 		zss->sl_soft_active = 0;
5827c478bd9Sstevel@tonic-gate 		zss->sl_stream.str_rq = NULL;
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 		zs->zs_priv = (caddr_t)zss;
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 		zss->sl_mru = zsh_default_mru;
5877c478bd9Sstevel@tonic-gate 		tmp = ZSH_MAX_RSTANDBY;
5887c478bd9Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, tmp);
5897c478bd9Sstevel@tonic-gate 		if (zss->sl_rstandby[0] == NULL) {
5907c478bd9Sstevel@tonic-gate 		    cmn_err(CE_WARN, "zsh_open: can't alloc message block");
5917c478bd9Sstevel@tonic-gate 		    mutex_exit(zs->zs_excl);
5927c478bd9Sstevel@tonic-gate 		    return (ENOSR);
5937c478bd9Sstevel@tonic-gate 		}
5947c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
5957c478bd9Sstevel@tonic-gate 		ZSH_ALLOCB(zss->sl_ractb);
5967c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;
5977c478bd9Sstevel@tonic-gate 		zss->sl_rr0 = SCC_READ0();
5987c478bd9Sstevel@tonic-gate 		zss->sl_flags &= (SF_INITIALIZED | SF_FDXPTP);
5997c478bd9Sstevel@tonic-gate 		if (zss->sl_flags & SF_INITIALIZED)
6007c478bd9Sstevel@tonic-gate 			zsh_init_port(zs, zss);
6017c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
6027c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
6037c478bd9Sstevel@tonic-gate 	} else {   /* CLONEOPEN */
6047c478bd9Sstevel@tonic-gate 		mutex_enter(&zs_curr_lock);
6057c478bd9Sstevel@tonic-gate 		for (unit = maxzsh; unit < MAXZSHCLONES; unit++)
6067c478bd9Sstevel@tonic-gate 			if (!zsh_usedminor[unit]) {
6077c478bd9Sstevel@tonic-gate 				zsh_usedminor[unit] = (unsigned char)unit;
6087c478bd9Sstevel@tonic-gate 				break;
6097c478bd9Sstevel@tonic-gate 			}
6107c478bd9Sstevel@tonic-gate 		mutex_exit(&zs_curr_lock);
6117c478bd9Sstevel@tonic-gate 		if (unit >= MAXZSHCLONES)	/* no slots available */
6127c478bd9Sstevel@tonic-gate 			return (ENODEV);
6137c478bd9Sstevel@tonic-gate 		*dev = makedevice(getmajor(*dev), unit);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 		stp = kmem_zalloc(sizeof (struct ser_str), KM_NOSLEEP);
6167c478bd9Sstevel@tonic-gate 		if (stp == NULL) {
6177c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
6187c478bd9Sstevel@tonic-gate 			    "zsh clone open failed, no memory, rq=%p\n",
6197c478bd9Sstevel@tonic-gate 			    (void *)rq);
6207c478bd9Sstevel@tonic-gate 			return (ENOMEM);
6217c478bd9Sstevel@tonic-gate 		}
6227c478bd9Sstevel@tonic-gate 		stp->str_state = STR_CLONE;
6237c478bd9Sstevel@tonic-gate 		stp->str_com = NULL;	/* can't determine without ppa */
6247c478bd9Sstevel@tonic-gate 	}
6257c478bd9Sstevel@tonic-gate 	stp->str_rq = rq;
6267c478bd9Sstevel@tonic-gate 	stp->str_inst = unit;
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)stp;
6297c478bd9Sstevel@tonic-gate 	qprocson(rq);
6307c478bd9Sstevel@tonic-gate 	return (0);
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate /*
6347c478bd9Sstevel@tonic-gate  * Close routine.
6357c478bd9Sstevel@tonic-gate  */
6367c478bd9Sstevel@tonic-gate int zsh_tx_enable_in_close = 0;
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6397c478bd9Sstevel@tonic-gate static int
6407c478bd9Sstevel@tonic-gate zsh_close(queue_t *rq, int flag)
6417c478bd9Sstevel@tonic-gate {
6427c478bd9Sstevel@tonic-gate 	struct ser_str *stp;
6437c478bd9Sstevel@tonic-gate 	struct zscom *zs;
6447c478bd9Sstevel@tonic-gate 	struct syncline *zss;
6457c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
6467c478bd9Sstevel@tonic-gate 	int i;
6477c478bd9Sstevel@tonic-gate 	timeout_id_t sl_wd_id;
6487c478bd9Sstevel@tonic-gate 	bufcall_id_t sl_bufcid;
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	/*
6517c478bd9Sstevel@tonic-gate 	 * Note that a close is only called on the last close of a
6527c478bd9Sstevel@tonic-gate 	 * particular stream.  Assume that we need to do it all.
6537c478bd9Sstevel@tonic-gate 	 */
6547c478bd9Sstevel@tonic-gate 	qprocsoff(rq);				/* no new business after this */
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	stp = (struct ser_str *)rq->q_ptr;
6577c478bd9Sstevel@tonic-gate 	if (stp == NULL)
6587c478bd9Sstevel@tonic-gate 		return (0);			/* already been closed once */
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE) {
6617c478bd9Sstevel@tonic-gate 		zsh_usedminor[stp->str_inst] = 0;
6627c478bd9Sstevel@tonic-gate 	} else {
6637c478bd9Sstevel@tonic-gate 		zs = (struct zscom *)stp->str_com;
6647c478bd9Sstevel@tonic-gate 		if (zs == NULL)
6657c478bd9Sstevel@tonic-gate 			goto out;
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_CLOSE, "zs = %p", zs);
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 		zss = (struct syncline *)zs->zs_priv;
6707c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
6717c478bd9Sstevel@tonic-gate 		flushq(WR(rq), FLUSHALL);
6727c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
6737c478bd9Sstevel@tonic-gate 		if (zss->sl_xstandby) {
6747c478bd9Sstevel@tonic-gate 			zss->sl_xstandby->b_wptr = zss->sl_xstandby->b_rptr;
6757c478bd9Sstevel@tonic-gate 			ZSH_FREEMSG(zss->sl_xstandby);
6767c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
6777c478bd9Sstevel@tonic-gate 		}
6787c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 		ZSH_FLUSHQ;
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 		/*
6837c478bd9Sstevel@tonic-gate 		 * Stop the Watchdog Timer.
6847c478bd9Sstevel@tonic-gate 		 */
6857c478bd9Sstevel@tonic-gate 		if ((sl_wd_id = zss->sl_wd_id) != 0)
6867c478bd9Sstevel@tonic-gate 			zss->sl_wd_id = 0;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 		/*
6897c478bd9Sstevel@tonic-gate 		 * Cancel outstanding "bufcall" request.
6907c478bd9Sstevel@tonic-gate 		 */
6917c478bd9Sstevel@tonic-gate 		if ((sl_bufcid = zss->sl_bufcid) != 0)
6927c478bd9Sstevel@tonic-gate 			zss->sl_bufcid = 0;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
6957c478bd9Sstevel@tonic-gate 		if (zs->zs_wr_cur) {
6967c478bd9Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
6977c478bd9Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
6987c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
6997c478bd9Sstevel@tonic-gate 			ZSDELAY();
7007c478bd9Sstevel@tonic-gate 			ZSDELAY();
7017c478bd9Sstevel@tonic-gate 		}
7027c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;	/* so it can't rearm in close */
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
7057c478bd9Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
7067c478bd9Sstevel@tonic-gate 		SCC_BIC(15,
7077c478bd9Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC | ZSR15_CTS));
7087c478bd9Sstevel@tonic-gate 		SCC_WRITE(3, 0);		/* Quiesce receiver */
7097c478bd9Sstevel@tonic-gate 		if (zsh_tx_enable_in_close && !(zss->sl_flags & SF_FDXPTP)) {
7107c478bd9Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_TX_ENABLE);
7117c478bd9Sstevel@tonic-gate 		} else
7127c478bd9Sstevel@tonic-gate 			SCC_BIC(5, ZSWR5_TX_ENABLE);
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 		SCC_BIC(5,  (ZSWR5_DTR | ZSWR5_RTS | ZSWR5_TXCRC_ENABLE));
7157c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);		/* reset TX */
7167c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
7177c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
7187c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();			/* reset RX */
7197c478bd9Sstevel@tonic-gate 		ZSDELAY();
7207c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
7217c478bd9Sstevel@tonic-gate 		ZSDELAY();
7227c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
7237c478bd9Sstevel@tonic-gate 		ZSDELAY();
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 		/*
7277c478bd9Sstevel@tonic-gate 		 * Free up everything we ever allocated.
7287c478bd9Sstevel@tonic-gate 		 */
7297c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_rhead) != NULL) {
7307c478bd9Sstevel@tonic-gate 			zss->sl_ractb = NULL;	/* already freed */
7317c478bd9Sstevel@tonic-gate 			zs->zs_rd_cur = NULL;
7327c478bd9Sstevel@tonic-gate 			zs->zs_rd_lim = NULL;
7337c478bd9Sstevel@tonic-gate 			zss->sl_rhead = NULL;
7347c478bd9Sstevel@tonic-gate 		}
7357c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7367c478bd9Sstevel@tonic-gate 		if (mp)
7377c478bd9Sstevel@tonic-gate 			freemsg(mp);
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
7407c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_ractb) != NULL) {
7417c478bd9Sstevel@tonic-gate 			zs->zs_rd_cur = NULL;
7427c478bd9Sstevel@tonic-gate 			zs->zs_rd_lim = NULL;
7437c478bd9Sstevel@tonic-gate 			zss->sl_ractb = NULL;
7447c478bd9Sstevel@tonic-gate 		}
7457c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7467c478bd9Sstevel@tonic-gate 		if (mp)
7477c478bd9Sstevel@tonic-gate 			freemsg(mp);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 		for (i = 0; i < ZSH_MAX_RSTANDBY; i++) {
7507c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
7517c478bd9Sstevel@tonic-gate 			mp = zss->sl_rstandby[i];
7527c478bd9Sstevel@tonic-gate 			zss->sl_rstandby[i] = NULL;
7537c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
7547c478bd9Sstevel@tonic-gate 			if (mp)
7557c478bd9Sstevel@tonic-gate 				freemsg(mp);
7567c478bd9Sstevel@tonic-gate 		}
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
7597c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_xhead) != NULL) {
7607c478bd9Sstevel@tonic-gate 			zss->sl_xhead = NULL;
7617c478bd9Sstevel@tonic-gate 			zss->sl_xactb = NULL;
7627c478bd9Sstevel@tonic-gate 		}
7637c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7647c478bd9Sstevel@tonic-gate 		if (mp)
7657c478bd9Sstevel@tonic-gate 			freemsg(mp);
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 		ZSH_FLUSHQ;
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
7707c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_xstandby) != NULL)
7717c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
7727c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7737c478bd9Sstevel@tonic-gate 		if (mp)
7747c478bd9Sstevel@tonic-gate 			freemsg(mp);
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
7777c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_mstat) != NULL)
7787c478bd9Sstevel@tonic-gate 			zss->sl_mstat = NULL;
7797c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;	/* so it can't rearm in close */
7807c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
7817c478bd9Sstevel@tonic-gate 		if (mp)
7827c478bd9Sstevel@tonic-gate 			freemsg(mp);
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 		zss->sl_stream.str_rq = NULL;
7857c478bd9Sstevel@tonic-gate 		zsopinit(zs, &zsops_null);
7867c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
7877c478bd9Sstevel@tonic-gate 		if (sl_wd_id)
7887c478bd9Sstevel@tonic-gate 			(void) untimeout(sl_wd_id);
7897c478bd9Sstevel@tonic-gate 		if (sl_bufcid)
7907c478bd9Sstevel@tonic-gate 			unbufcall(sl_bufcid);
7917c478bd9Sstevel@tonic-gate 		while (zss->sl_soft_active)
7927c478bd9Sstevel@tonic-gate 			drv_usecwait(1);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 		/* Mark device as available for power management */
7957c478bd9Sstevel@tonic-gate 		(void) pm_idle_component(zs->zs_dip, zs->zs_unit%2+1);
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE)
7997c478bd9Sstevel@tonic-gate 		kmem_free(stp, sizeof (struct ser_str));
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate out:
8027c478bd9Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = NULL;
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	return (0);
8057c478bd9Sstevel@tonic-gate }
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate static int
8087c478bd9Sstevel@tonic-gate zsh_hdp_ok_or_rts_state(struct zscom *zs, struct syncline *zss)
8097c478bd9Sstevel@tonic-gate {
8107c478bd9Sstevel@tonic-gate 	register uchar_t s0;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	SCC_BIS(15, ZSR15_CTS);
8137c478bd9Sstevel@tonic-gate 	SCC_BIS(5, ZSWR5_RTS);
8147c478bd9Sstevel@tonic-gate 	s0 = SCC_READ0();
8157c478bd9Sstevel@tonic-gate 	if (s0 & ZSRR0_CTS) {
8167c478bd9Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_TX_ENABLE);
8177c478bd9Sstevel@tonic-gate 		zss->sl_rr0 |= ZSRR0_CTS;
8187c478bd9Sstevel@tonic-gate 		return (1);
8197c478bd9Sstevel@tonic-gate 	}
8207c478bd9Sstevel@tonic-gate 	zss->sl_flags |= SF_XMT_INPROG;
8217c478bd9Sstevel@tonic-gate 	zss->sl_txstate = TX_RTS;
8227c478bd9Sstevel@tonic-gate 	zss->sl_rr0 &= ~ZSRR0_CTS;
8237c478bd9Sstevel@tonic-gate 	zss->sl_wd_count = zsh_timer_count;
8247c478bd9Sstevel@tonic-gate 	return (0);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate /*
8287c478bd9Sstevel@tonic-gate  * Put procedure for write queue.
8297c478bd9Sstevel@tonic-gate  */
8307c478bd9Sstevel@tonic-gate static void
8317c478bd9Sstevel@tonic-gate zsh_wput(queue_t *wq, mblk_t *mp)
8327c478bd9Sstevel@tonic-gate {
8337c478bd9Sstevel@tonic-gate 	register struct ser_str *stp = (struct ser_str *)wq->q_ptr;
8347c478bd9Sstevel@tonic-gate 	register struct zscom *zs;
8357c478bd9Sstevel@tonic-gate 	register struct syncline *zss = NULL;
8367c478bd9Sstevel@tonic-gate 	register ulong_t prim, error = 0;
8377c478bd9Sstevel@tonic-gate 	register union DL_primitives *dlp;
8387c478bd9Sstevel@tonic-gate 	register int	ppa;
8397c478bd9Sstevel@tonic-gate 	register mblk_t *tmp;
8407c478bd9Sstevel@tonic-gate 	register struct copyresp	*resp;
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	/*
8437c478bd9Sstevel@tonic-gate 	 * stp->str_com supplied by open or DLPI attach.
8447c478bd9Sstevel@tonic-gate 	 */
8457c478bd9Sstevel@tonic-gate 	if (stp == NULL) {
8467c478bd9Sstevel@tonic-gate 		freemsg(mp);
8477c478bd9Sstevel@tonic-gate 		return;
8487c478bd9Sstevel@tonic-gate 	}
8497c478bd9Sstevel@tonic-gate 	zs = (struct zscom *)stp->str_com;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	TRACE_0(TR_ZSH, TR_ZSH_WPUT_START, "zsh_wput start");
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 	if ((mp->b_datap->db_type == M_FLUSH) &&
8547c478bd9Sstevel@tonic-gate 		    (stp->str_state == STR_CLONE)) {
8557c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
8567c478bd9Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
8577c478bd9Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
8587c478bd9Sstevel@tonic-gate 		}
8597c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
8607c478bd9Sstevel@tonic-gate 			qreply(wq, mp);  /* let the read queues have at it */
8617c478bd9Sstevel@tonic-gate 		else
8627c478bd9Sstevel@tonic-gate 			freemsg(mp);
8637c478bd9Sstevel@tonic-gate 		return;
8647c478bd9Sstevel@tonic-gate 	}
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	if ((zs == NULL) && (mp->b_datap->db_type != M_PROTO)) {
8677c478bd9Sstevel@tonic-gate 	    freemsg(mp);
8687c478bd9Sstevel@tonic-gate 	    cmn_err(CE_WARN,
8697c478bd9Sstevel@tonic-gate 		"zsh: clone device %d must be attached before use!",
8707c478bd9Sstevel@tonic-gate 		stp->str_inst);
8717c478bd9Sstevel@tonic-gate 	    (void) putnextctl1(RD(wq), M_ERROR, EPROTO);
8727c478bd9Sstevel@tonic-gate 	    return;
8737c478bd9Sstevel@tonic-gate 	}
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE) {	/* Clone opened, limited. */
8767c478bd9Sstevel@tonic-gate 		if ((mp->b_datap->db_type != M_PROTO) &&
8777c478bd9Sstevel@tonic-gate 		    (mp->b_datap->db_type != M_IOCTL) &&
8787c478bd9Sstevel@tonic-gate 		    (mp->b_datap->db_type != M_IOCDATA)) {
8797c478bd9Sstevel@tonic-gate 			freemsg(mp);
8807c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
8817c478bd9Sstevel@tonic-gate 			    "zsh%x: invalid operation for clone dev.\n",
8827c478bd9Sstevel@tonic-gate 			    stp->str_inst);
8837c478bd9Sstevel@tonic-gate 			(void) putnextctl1(RD(wq), M_ERROR, EPROTO);
8847c478bd9Sstevel@tonic-gate 			return;
8857c478bd9Sstevel@tonic-gate 		}
8867c478bd9Sstevel@tonic-gate 	} else {
8877c478bd9Sstevel@tonic-gate 		zss = (struct syncline *)zs->zs_priv;
8887c478bd9Sstevel@tonic-gate 	}
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	case M_DATA:
8937c478bd9Sstevel@tonic-gate 		/*
8947c478bd9Sstevel@tonic-gate 		 * Queue the message up to be transmitted.
8957c478bd9Sstevel@tonic-gate 		 * Set "in progress" flag and call the start routine.
8967c478bd9Sstevel@tonic-gate 		 */
8977c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
8987c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_INITIALIZED)) {
8997c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
9007c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN,
9017c478bd9Sstevel@tonic-gate 			    "zsh%x not initialized, can't send message",
9027c478bd9Sstevel@tonic-gate 			    zs->zs_unit);
9037c478bd9Sstevel@tonic-gate 			freemsg(mp);
9047c478bd9Sstevel@tonic-gate 			(void) putnextctl1(RD(wq), M_ERROR, ECOMM);
9057c478bd9Sstevel@tonic-gate 			return;
9067c478bd9Sstevel@tonic-gate 		}
9077c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
9087c478bd9Sstevel@tonic-gate 		if (zs->zs_flags & ZS_NEEDSOFT) {
9097c478bd9Sstevel@tonic-gate 			zs->zs_flags &= ~ZS_NEEDSOFT;
9107c478bd9Sstevel@tonic-gate 			(void) zsh_softint(zs);
9117c478bd9Sstevel@tonic-gate 		}
9127c478bd9Sstevel@tonic-gate 		while (mp->b_wptr == mp->b_rptr) {
9137c478bd9Sstevel@tonic-gate 			register mblk_t *mp1;
9147c478bd9Sstevel@tonic-gate 			mp1 = unlinkb(mp);
9157c478bd9Sstevel@tonic-gate 			freemsg(mp);
9167c478bd9Sstevel@tonic-gate 			mp = mp1;
9177c478bd9Sstevel@tonic-gate 			if (mp == NULL)
9187c478bd9Sstevel@tonic-gate 				return;
9197c478bd9Sstevel@tonic-gate 		}
9207c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
9217c478bd9Sstevel@tonic-gate 		(void) putq(wq, mp);
9227c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
9237c478bd9Sstevel@tonic-gate 		if (zss->sl_flags & SF_FLUSH_WQ) {
9247c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
9257c478bd9Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
9267c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 			TRACE_1(TR_ZSH, TR_ZSH_WPUT_END,
9297c478bd9Sstevel@tonic-gate 			    "zsh_wput end: zs = %p", zs);
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 			return;
9327c478bd9Sstevel@tonic-gate 		}
9337c478bd9Sstevel@tonic-gate 		tmp = NULL;
9347c478bd9Sstevel@tonic-gate again:
9357c478bd9Sstevel@tonic-gate 		if (!zss->sl_xstandby) {
9367c478bd9Sstevel@tonic-gate 			if (tmp)
9377c478bd9Sstevel@tonic-gate 				zss->sl_xstandby = tmp;
9387c478bd9Sstevel@tonic-gate 			else {
9397c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
9407c478bd9Sstevel@tonic-gate 				tmp = getq(wq);
9417c478bd9Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
9427c478bd9Sstevel@tonic-gate 				if (tmp)
9437c478bd9Sstevel@tonic-gate 					goto again;
9447c478bd9Sstevel@tonic-gate 			}
9457c478bd9Sstevel@tonic-gate 		} else if (tmp) {
9467c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
9477c478bd9Sstevel@tonic-gate 			(void) putbq(wq, tmp);
9487c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
9497c478bd9Sstevel@tonic-gate 		}
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 		if (zss->sl_flags & SF_XMT_INPROG) {
9527c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
9537c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 			TRACE_1(TR_ZSH, TR_ZSH_WPUT_END,
9567c478bd9Sstevel@tonic-gate 			    "zsh_wput end: zs = %p", zs);
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 			return;
9597c478bd9Sstevel@tonic-gate 		}
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 		if (!zss->sl_wd_id) {
9627c478bd9Sstevel@tonic-gate 			zss->sl_wd_count = zsh_timer_count;
9637c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_IDLE;
9647c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
9657c478bd9Sstevel@tonic-gate 			zss->sl_wd_id = timeout(zsh_watchdog, zs,
9667c478bd9Sstevel@tonic-gate 			    SIO_WATCHDOG_TICK);
9677c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
9687c478bd9Sstevel@tonic-gate 		}
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_XMT_INPROG;
9717c478bd9Sstevel@tonic-gate 		if ((zss->sl_flags & SF_FDXPTP) ||
9727c478bd9Sstevel@tonic-gate 		    zsh_hdp_ok_or_rts_state(zs, zss))
9737c478bd9Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
9747c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
9757c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
9767c478bd9Sstevel@tonic-gate 		break;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	case M_PROTO:
9797c478bd9Sstevel@tonic-gate 		/*
9807c478bd9Sstevel@tonic-gate 		 * Here is where a clone device finds out about the
9817c478bd9Sstevel@tonic-gate 		 * hardware it is going to attach to.  The request is
9827c478bd9Sstevel@tonic-gate 		 * validated and a ppa is extracted from it and validated.
9837c478bd9Sstevel@tonic-gate 		 * This number is used to index the hardware data structure
9847c478bd9Sstevel@tonic-gate 		 * and the protocol data structure, in case the latter
9857c478bd9Sstevel@tonic-gate 		 * was not provided by a data-path open before this.
9867c478bd9Sstevel@tonic-gate 		 */
9877c478bd9Sstevel@tonic-gate 		if (stp->str_state != STR_CLONE) {
9887c478bd9Sstevel@tonic-gate 			freemsg(mp);
9897c478bd9Sstevel@tonic-gate 			return;
9907c478bd9Sstevel@tonic-gate 		}
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) {
9937c478bd9Sstevel@tonic-gate 			prim = DL_ATTACH_REQ;
9947c478bd9Sstevel@tonic-gate 			error = DL_BADPRIM;
9957c478bd9Sstevel@tonic-gate 			goto end_proto;
9967c478bd9Sstevel@tonic-gate 		}
9977c478bd9Sstevel@tonic-gate 		dlp = (union DL_primitives *)mp->b_rptr;
9987c478bd9Sstevel@tonic-gate 		prim = dlp->dl_primitive;
9997c478bd9Sstevel@tonic-gate 		if (prim != DL_ATTACH_REQ) {
10007c478bd9Sstevel@tonic-gate 			error = DL_BADPRIM;
10017c478bd9Sstevel@tonic-gate 			goto end_proto;
10027c478bd9Sstevel@tonic-gate 		}
10037c478bd9Sstevel@tonic-gate 		ppa = dlp->attach_req.dl_ppa;
10047c478bd9Sstevel@tonic-gate 		ppa = (ppa%2) ? ((ppa-1)*2 +1) : (ppa*2);
10057c478bd9Sstevel@tonic-gate 		if (ppa >= maxzsh) {
10067c478bd9Sstevel@tonic-gate 			error = DL_BADPPA;
10077c478bd9Sstevel@tonic-gate 			goto end_proto;
10087c478bd9Sstevel@tonic-gate 		}
10097c478bd9Sstevel@tonic-gate 		zs = &zscom[ppa];
10107c478bd9Sstevel@tonic-gate 		if (zs->zs_ops == NULL) {
10117c478bd9Sstevel@tonic-gate 			error = ENXIO;
10127c478bd9Sstevel@tonic-gate 			goto end_proto;
10137c478bd9Sstevel@tonic-gate 		}
10147c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
10157c478bd9Sstevel@tonic-gate 		if ((zs->zs_ops != &zsops_null) &&
10167c478bd9Sstevel@tonic-gate 		    (zs->zs_ops != &zsops_hdlc)) {
10177c478bd9Sstevel@tonic-gate 			/*
10187c478bd9Sstevel@tonic-gate 			 * another protocol got here first
10197c478bd9Sstevel@tonic-gate 			 */
10207c478bd9Sstevel@tonic-gate 			error = (EBUSY);
10217c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
10227c478bd9Sstevel@tonic-gate 			goto end_proto;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 		}
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 		stp->str_com = (caddr_t)zs;
10277c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
10287c478bd9Sstevel@tonic-gate end_proto:
10297c478bd9Sstevel@tonic-gate 		if (error)
10307c478bd9Sstevel@tonic-gate 			dlerrorack(wq, mp, prim, error, 0);
10317c478bd9Sstevel@tonic-gate 		else
10327c478bd9Sstevel@tonic-gate 			dlokack(wq, mp, DL_ATTACH_REQ);
10337c478bd9Sstevel@tonic-gate 		break;
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 	case M_IOCTL:
10367c478bd9Sstevel@tonic-gate 		zsh_ioctl(wq, mp);
10377c478bd9Sstevel@tonic-gate 		break;
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 	case M_IOCDATA:
10407c478bd9Sstevel@tonic-gate 		resp = (struct copyresp *)mp->b_rptr;
10417c478bd9Sstevel@tonic-gate 		if (resp->cp_rval) {
10427c478bd9Sstevel@tonic-gate 			/*
10437c478bd9Sstevel@tonic-gate 			 * Just free message on failure.
10447c478bd9Sstevel@tonic-gate 			 */
10457c478bd9Sstevel@tonic-gate 			freemsg(mp);
10467c478bd9Sstevel@tonic-gate 			break;
10477c478bd9Sstevel@tonic-gate 		}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 		switch (resp->cp_cmd) {
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 		case S_IOCGETMODE:
10527c478bd9Sstevel@tonic-gate 		case S_IOCGETSTATS:
10537c478bd9Sstevel@tonic-gate 		case S_IOCGETSPEED:
10547c478bd9Sstevel@tonic-gate 		case S_IOCGETMCTL:
10557c478bd9Sstevel@tonic-gate 		case S_IOCGETMRU:
10567c478bd9Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
10577c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
10587c478bd9Sstevel@tonic-gate 			break;
10597c478bd9Sstevel@tonic-gate 
10607c478bd9Sstevel@tonic-gate 		case S_IOCSETMODE:
10617c478bd9Sstevel@tonic-gate 			zss  = (struct syncline *)&zs->zs_priv_str;
10627c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
10637c478bd9Sstevel@tonic-gate 			error = zsh_setmode(zs, zss,
10647c478bd9Sstevel@tonic-gate 				(struct scc_mode *)mp->b_cont->b_rptr);
10657c478bd9Sstevel@tonic-gate 			if (error) {
10667c478bd9Sstevel@tonic-gate 				register struct iocblk  *iocp =
10677c478bd9Sstevel@tonic-gate 					(struct iocblk *)mp->b_rptr;
10687c478bd9Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
10697c478bd9Sstevel@tonic-gate 				iocp->ioc_error = error;
10707c478bd9Sstevel@tonic-gate 			} else
10717c478bd9Sstevel@tonic-gate 				mioc2ack(mp, NULL, 0, 0);
10727c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
10737c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
10747c478bd9Sstevel@tonic-gate 			break;
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate 		case S_IOCSETMRU:
10777c478bd9Sstevel@tonic-gate 			zss  = (struct syncline *)&zs->zs_priv_str;
10787c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
10797c478bd9Sstevel@tonic-gate 			zss->sl_mru = *(int *)mp->b_cont->b_rptr;
10807c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
10817c478bd9Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
10827c478bd9Sstevel@tonic-gate 			qreply(wq, mp);
10837c478bd9Sstevel@tonic-gate 			break;
10847c478bd9Sstevel@tonic-gate 		default:
10857c478bd9Sstevel@tonic-gate 			freemsg(mp);
10867c478bd9Sstevel@tonic-gate 		}
10877c478bd9Sstevel@tonic-gate 		break;
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 		/*
10907c478bd9Sstevel@tonic-gate 		 * We're at the bottom of the food chain, so we flush our
10917c478bd9Sstevel@tonic-gate 		 * write queue, clear the FLUSHW bit so it doesn't go round
10927c478bd9Sstevel@tonic-gate 		 * and round forever, then flush our read queue (since there's
10937c478bd9Sstevel@tonic-gate 		 * no read put procedure down here) and pass it up for any
10947c478bd9Sstevel@tonic-gate 		 * higher modules to deal with in their own way.
10957c478bd9Sstevel@tonic-gate 		 */
10967c478bd9Sstevel@tonic-gate 	case M_FLUSH:
10977c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
10987c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
10997c478bd9Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
11007c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
11017c478bd9Sstevel@tonic-gate 			tmp = zss->sl_xstandby;
11027c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
11037c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
11047c478bd9Sstevel@tonic-gate 			if (tmp)
11057c478bd9Sstevel@tonic-gate 				freemsg(tmp);
11067c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
11077c478bd9Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
11087c478bd9Sstevel@tonic-gate 		}
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
11117c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
11127c478bd9Sstevel@tonic-gate 			ZSH_FLUSHQ;
11137c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
11147c478bd9Sstevel@tonic-gate 			qreply(wq, mp);  /* let the read queues have at it */
11157c478bd9Sstevel@tonic-gate 		} else
11167c478bd9Sstevel@tonic-gate 			freemsg(mp);
11177c478bd9Sstevel@tonic-gate 		break;
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate 	default:
11207c478bd9Sstevel@tonic-gate 		/*
11217c478bd9Sstevel@tonic-gate 		 * "No, I don't want a subscription to Chain Store Age,
11227c478bd9Sstevel@tonic-gate 		 * thank you anyway."
11237c478bd9Sstevel@tonic-gate 		 */
11247c478bd9Sstevel@tonic-gate 		freemsg(mp);
11257c478bd9Sstevel@tonic-gate 		break;
11267c478bd9Sstevel@tonic-gate 	}
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_WPUT_END, "zsh_wput end: zs = %p", zs);
11297c478bd9Sstevel@tonic-gate }
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate /*
11327c478bd9Sstevel@tonic-gate  * Get the next message from the write queue, set up the necessary pointers,
11337c478bd9Sstevel@tonic-gate  * state info, etc., and start the transmit "engine" by sending the first
11347c478bd9Sstevel@tonic-gate  * character.  We'll then rotate through txint until done, then get an xsint.
11357c478bd9Sstevel@tonic-gate  */
11367c478bd9Sstevel@tonic-gate static int
11377c478bd9Sstevel@tonic-gate zsh_start(struct zscom *zs, struct syncline *zss)
11387c478bd9Sstevel@tonic-gate {
11397c478bd9Sstevel@tonic-gate 	register mblk_t *mp;
11407c478bd9Sstevel@tonic-gate 	register uchar_t *wptr;
11417c478bd9Sstevel@tonic-gate 	register uchar_t *rptr;
11427c478bd9Sstevel@tonic-gate 	register uchar_t sl_flags = zss->sl_flags;
11437c478bd9Sstevel@tonic-gate 
11447c478bd9Sstevel@tonic-gate 	/*
11457c478bd9Sstevel@tonic-gate 	 * Attempt to grab the next M_DATA message off the queue (that's
11467c478bd9Sstevel@tonic-gate 	 * all that will be left after wput) and begin transmission.
11477c478bd9Sstevel@tonic-gate 	 * This routine is normally called after completion of a previous
11487c478bd9Sstevel@tonic-gate 	 * frame, or when zsh_wput gets a new message.  If we are in a
11497c478bd9Sstevel@tonic-gate 	 * mode that put us in the TX_RTS state, waiting for CTS, and CTS
11507c478bd9Sstevel@tonic-gate 	 * is not up yet, we have no business here.  Ditto if we're in
11517c478bd9Sstevel@tonic-gate 	 * either the TX_ACTIVE or TX_CRC states.  In these cases we
11527c478bd9Sstevel@tonic-gate 	 * don't clear SF_CALLSTART, so we don't forget there's work to do.
11537c478bd9Sstevel@tonic-gate 	 */
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_START,
11567c478bd9Sstevel@tonic-gate 	    "zsh_start start: zs = %p", zs);
11577c478bd9Sstevel@tonic-gate 
11587c478bd9Sstevel@tonic-gate 	if (sl_flags & SF_PHONY) {
11597c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_PHONY;
11607c478bd9Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
11617c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
11627c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
11637c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
11647c478bd9Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
11657c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
11667c478bd9Sstevel@tonic-gate 		/*
11677c478bd9Sstevel@tonic-gate 		 * if we get another msg by chance zsh_watchog will start
11687c478bd9Sstevel@tonic-gate 		 */
11697c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
11707c478bd9Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
11737c478bd9Sstevel@tonic-gate 		    "zsh_start end: zs = %d", zs);
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 		return (0);
11767c478bd9Sstevel@tonic-gate 	}
11777c478bd9Sstevel@tonic-gate 	mp = zss->sl_xstandby;
11787c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
11797c478bd9Sstevel@tonic-gate 		if (!(sl_flags & SF_FDXPTP)) {
11807c478bd9Sstevel@tonic-gate 			sl_flags |= SF_PHONY;
11817c478bd9Sstevel@tonic-gate 			ZSH_ALLOCB(mp);
11827c478bd9Sstevel@tonic-gate 			if (!mp)
11837c478bd9Sstevel@tonic-gate 				return (0);
11847c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_RSE;
11857c478bd9Sstevel@tonic-gate 			mp->b_wptr = mp->b_rptr + 1;
11867c478bd9Sstevel@tonic-gate 			goto transmit;
11877c478bd9Sstevel@tonic-gate 		}
11887c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
11897c478bd9Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
11927c478bd9Sstevel@tonic-gate 		    "zsh_start end: zs = %p", zs);
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 		return (0);
11957c478bd9Sstevel@tonic-gate 	}
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate transmit:
11987c478bd9Sstevel@tonic-gate 	zss->sl_xstandby = NULL;
11997c478bd9Sstevel@tonic-gate 	rptr = mp->b_rptr;
12007c478bd9Sstevel@tonic-gate 	wptr = mp->b_wptr;
12017c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs);
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate #ifdef ZSH_DEBUG
12047c478bd9Sstevel@tonic-gate 	if (zss->sl_xhead || zss->sl_xactb) {
12057c478bd9Sstevel@tonic-gate 		debug_enter("xhead1");
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate #endif
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	zss->sl_xhead = mp;
12107c478bd9Sstevel@tonic-gate 	zss->sl_xactb = mp;
12117c478bd9Sstevel@tonic-gate 	zss->sl_wd_count = zsh_timer_count;
12127c478bd9Sstevel@tonic-gate 	zss->sl_txstate = TX_ACTIVE;
12137c478bd9Sstevel@tonic-gate 	zss->sl_ocnt = 0;
12147c478bd9Sstevel@tonic-gate 	SCC_BIS(10, ZSWR10_UNDERRUN_ABORT);	/* abort on underrun */
12157c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_TXCRC);		/* reset transmit CRC */
12167c478bd9Sstevel@tonic-gate 	zss->sl_ocnt = wptr - rptr;
12177c478bd9Sstevel@tonic-gate 	mp->b_wptr = rptr; /* to tell soft to free this msg */
12187c478bd9Sstevel@tonic-gate 	SCC_WRITEDATA(*rptr++);    /* resets TXINT */
12197c478bd9Sstevel@tonic-gate 	zs->zs_wr_cur = rptr;
12207c478bd9Sstevel@tonic-gate 	zs->zs_wr_lim = wptr;
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_EOM);
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_END,
12257c478bd9Sstevel@tonic-gate 	    "zsh_start end: zs = %p", zs);
12267c478bd9Sstevel@tonic-gate 
12277c478bd9Sstevel@tonic-gate 	zss->sl_flags = sl_flags;
12287c478bd9Sstevel@tonic-gate 	return (1);
12297c478bd9Sstevel@tonic-gate }
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate /*
12337c478bd9Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
12347c478bd9Sstevel@tonic-gate  */
12357c478bd9Sstevel@tonic-gate static void
12367c478bd9Sstevel@tonic-gate zsh_ioctl(queue_t *wq, mblk_t *mp)
12377c478bd9Sstevel@tonic-gate {
12387c478bd9Sstevel@tonic-gate 	register struct ser_str *stp = (struct ser_str *)wq->q_ptr;
12397c478bd9Sstevel@tonic-gate 	register struct zscom *zs = (struct zscom *)stp->str_com;
12407c478bd9Sstevel@tonic-gate 	register struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
12417c478bd9Sstevel@tonic-gate 	register struct iocblk   *iocp = (struct iocblk *)mp->b_rptr;
12427c478bd9Sstevel@tonic-gate 	register struct scc_mode *sm;
12437c478bd9Sstevel@tonic-gate 	register struct sl_stats *st;
12447c478bd9Sstevel@tonic-gate 	register uchar_t	 *msignals;
12457c478bd9Sstevel@tonic-gate 	register mblk_t		 *tmp;
12467c478bd9Sstevel@tonic-gate 	register int		 error = 0;
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
12497c478bd9Sstevel@tonic-gate 	if ((zs->zs_ops != &zsops_null) &&
12507c478bd9Sstevel@tonic-gate 	    (zs->zs_ops != &zsops_hdlc)) {
12517c478bd9Sstevel@tonic-gate 		/*
12527c478bd9Sstevel@tonic-gate 		 * another protocol got here first
12537c478bd9Sstevel@tonic-gate 		 */
12547c478bd9Sstevel@tonic-gate 		error = (EBUSY);
12557c478bd9Sstevel@tonic-gate 		goto end_zsh_ioctl;
12567c478bd9Sstevel@tonic-gate 	}
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 
12597c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate 	case S_IOCGETMODE:
12627c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (struct scc_mode), BPRI_MED);
12637c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12647c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12657c478bd9Sstevel@tonic-gate 			break;
12667c478bd9Sstevel@tonic-gate 		}
12677c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12687c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct scc_mode), 0);
12697c478bd9Sstevel@tonic-gate 		else
12707c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct scc_mode), NULL, tmp);
12717c478bd9Sstevel@tonic-gate 		sm = (struct scc_mode *)mp->b_cont->b_rptr;
12727c478bd9Sstevel@tonic-gate 		bcopy(&zss->sl_mode, sm, sizeof (struct scc_mode));
12737c478bd9Sstevel@tonic-gate 		break;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	case S_IOCGETSTATS:
12767c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (struct sl_stats), BPRI_MED);
12777c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12787c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12797c478bd9Sstevel@tonic-gate 			break;
12807c478bd9Sstevel@tonic-gate 		}
12817c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12827c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct sl_stats), 0);
12837c478bd9Sstevel@tonic-gate 		else
12847c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct sl_stats), NULL, tmp);
12857c478bd9Sstevel@tonic-gate 		st = (struct sl_stats *)mp->b_cont->b_rptr;
12867c478bd9Sstevel@tonic-gate 		bcopy(&zss->sl_st, st, sizeof (struct sl_stats));
12877c478bd9Sstevel@tonic-gate 		break;
12887c478bd9Sstevel@tonic-gate 
12897c478bd9Sstevel@tonic-gate 	case S_IOCGETSPEED:
12907c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
12917c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12927c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12937c478bd9Sstevel@tonic-gate 			break;
12947c478bd9Sstevel@tonic-gate 		}
12957c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12967c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
12977c478bd9Sstevel@tonic-gate 		else
12987c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
12997c478bd9Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mode.sm_baudrate;
13007c478bd9Sstevel@tonic-gate 		break;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	case S_IOCGETMCTL:
13037c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (char), BPRI_MED);
13047c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
13057c478bd9Sstevel@tonic-gate 			error = EAGAIN;
13067c478bd9Sstevel@tonic-gate 			break;
13077c478bd9Sstevel@tonic-gate 		}
13087c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
13097c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (char), 0);
13107c478bd9Sstevel@tonic-gate 		else
13117c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (char), NULL, tmp);
13127c478bd9Sstevel@tonic-gate 		msignals = (uchar_t *)mp->b_cont->b_rptr;
13137c478bd9Sstevel@tonic-gate 		*msignals = zss->sl_rr0 & (ZSRR0_CD | ZSRR0_CTS);
13147c478bd9Sstevel@tonic-gate 		break;
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 	case S_IOCGETMRU:
13177c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
13187c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
13197c478bd9Sstevel@tonic-gate 			error = EAGAIN;
13207c478bd9Sstevel@tonic-gate 			break;
13217c478bd9Sstevel@tonic-gate 		}
13227c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
13237c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
13247c478bd9Sstevel@tonic-gate 		else
13257c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
13267c478bd9Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mru;
13277c478bd9Sstevel@tonic-gate 		break;
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	case S_IOCSETMODE:
13307c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
13317c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct scc_mode));
13327c478bd9Sstevel@tonic-gate 			if (error != 0)
13337c478bd9Sstevel@tonic-gate 				break;
13347c478bd9Sstevel@tonic-gate 			error = zsh_setmode(zs, zss,
13357c478bd9Sstevel@tonic-gate 			    (struct scc_mode *)mp->b_cont->b_rptr);
13367c478bd9Sstevel@tonic-gate 			if (error == 0)
13377c478bd9Sstevel@tonic-gate 				mioc2ack(mp, NULL, 0, 0);
13387c478bd9Sstevel@tonic-gate 		} else
13397c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (struct scc_mode), NULL);
13407c478bd9Sstevel@tonic-gate 		break;
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 	case S_IOCCLRSTATS:
13437c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
13447c478bd9Sstevel@tonic-gate 		bzero(&zss->sl_st, sizeof (struct sl_stats));
13457c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
13467c478bd9Sstevel@tonic-gate 		mioc2ack(mp, NULL, 0, 0);
13477c478bd9Sstevel@tonic-gate 		break;
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 	case S_IOCSETMRU:
13507c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
13517c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
13527c478bd9Sstevel@tonic-gate 			if (error != 0)
13537c478bd9Sstevel@tonic-gate 				break;
13547c478bd9Sstevel@tonic-gate 			zss->sl_mru = *(int *)mp->b_cont->b_rptr;
13557c478bd9Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
13567c478bd9Sstevel@tonic-gate 		} else
13577c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (int), NULL);
13587c478bd9Sstevel@tonic-gate 		break;
13597c478bd9Sstevel@tonic-gate 
13607c478bd9Sstevel@tonic-gate 	case S_IOCSETDTR:
13617c478bd9Sstevel@tonic-gate 		/*
13627c478bd9Sstevel@tonic-gate 		 * The first integer of the M_DATA block that should
13637c478bd9Sstevel@tonic-gate 		 * follow indicate if DTR must be set or reset
13647c478bd9Sstevel@tonic-gate 		 */
13657c478bd9Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (int));
13667c478bd9Sstevel@tonic-gate 		if (error != 0)
13677c478bd9Sstevel@tonic-gate 			break;
13687c478bd9Sstevel@tonic-gate 
13697c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
13707c478bd9Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr != 0)
13717c478bd9Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIS);
13727c478bd9Sstevel@tonic-gate 		else
13737c478bd9Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIC);
13747c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
13757c478bd9Sstevel@tonic-gate 		break;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 	default:
13787c478bd9Sstevel@tonic-gate 		error = EINVAL;
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	}
13817c478bd9Sstevel@tonic-gate end_zsh_ioctl:
13827c478bd9Sstevel@tonic-gate 	iocp->ioc_error = error;
13837c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = (error) ? M_IOCNAK : M_IOCACK;
13847c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
13857c478bd9Sstevel@tonic-gate 	qreply(wq, mp);
13867c478bd9Sstevel@tonic-gate }
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate /*
13897c478bd9Sstevel@tonic-gate  * Set the mode of the zsh port
13907c478bd9Sstevel@tonic-gate  */
13917c478bd9Sstevel@tonic-gate 
13927c478bd9Sstevel@tonic-gate int
13937c478bd9Sstevel@tonic-gate zsh_setmode(struct zscom *zs, struct syncline *zss, struct scc_mode *sm)
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate 	register int error = 0;
13967c478bd9Sstevel@tonic-gate 	register mblk_t *mp;
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
13997c478bd9Sstevel@tonic-gate 	if (sm->sm_rxclock == RXC_IS_PLL) {
14007c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
14017c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
14027c478bd9Sstevel@tonic-gate 		return (EINVAL);		/* not supported */
14037c478bd9Sstevel@tonic-gate 	} else {
14047c478bd9Sstevel@tonic-gate 		if (((zss->sl_mode.sm_config ^ sm->sm_config) &
14057c478bd9Sstevel@tonic-gate 			CONN_SIGNAL) != 0) { /* Changing, going... */
14067c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_SIGNAL) { /* ...up. */
14077c478bd9Sstevel@tonic-gate 				if (zss->sl_mstat == NULL) {
14087c478bd9Sstevel@tonic-gate 				    mutex_exit(zs->zs_excl_hi);
14097c478bd9Sstevel@tonic-gate 				    mp = allocb(
14107c478bd9Sstevel@tonic-gate 					sizeof (struct sl_status), BPRI_MED);
14117c478bd9Sstevel@tonic-gate 				    mutex_enter(zs->zs_excl_hi);
14127c478bd9Sstevel@tonic-gate 				    zss->sl_mstat = mp;
14137c478bd9Sstevel@tonic-gate 				}
14147c478bd9Sstevel@tonic-gate 			} else {			/* ...down. */
14157c478bd9Sstevel@tonic-gate 				if ((mp = zss->sl_mstat) != NULL)
14167c478bd9Sstevel@tonic-gate 					zss->sl_mstat = NULL;
14177c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14187c478bd9Sstevel@tonic-gate 				if (mp)
14197c478bd9Sstevel@tonic-gate 					freemsg(mp);
14207c478bd9Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
14217c478bd9Sstevel@tonic-gate 			}
14227c478bd9Sstevel@tonic-gate 		}
14237c478bd9Sstevel@tonic-gate 		if (!(sm->sm_config & CONN_IBM)) {
14247c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_HDX) {
14257c478bd9Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_HDX;
14267c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14277c478bd9Sstevel@tonic-gate 				return (EINVAL);
14287c478bd9Sstevel@tonic-gate 			}
14297c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_MPT) {
14307c478bd9Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_MPT;
14317c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14327c478bd9Sstevel@tonic-gate 				return (EINVAL);
14337c478bd9Sstevel@tonic-gate 			}
14347c478bd9Sstevel@tonic-gate 		}
14357c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FDXPTP;		/* "conmode" */
14367c478bd9Sstevel@tonic-gate 		if ((sm->sm_config & (CONN_HDX | CONN_MPT)) == 0)
14377c478bd9Sstevel@tonic-gate 			zss->sl_flags |= SF_FDXPTP;
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 		error = zsh_program(zs, sm);
14407c478bd9Sstevel@tonic-gate 		if (!error && (zs->zs_ops != &zsops_null))
14417c478bd9Sstevel@tonic-gate 			zsh_init_port(zs, zss);
14427c478bd9Sstevel@tonic-gate 	}
14437c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	return (error);
14467c478bd9Sstevel@tonic-gate }
14477c478bd9Sstevel@tonic-gate 
14487c478bd9Sstevel@tonic-gate /*
14497c478bd9Sstevel@tonic-gate  * Transmit interrupt service procedure
14507c478bd9Sstevel@tonic-gate  */
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate static void
14537c478bd9Sstevel@tonic-gate zsh_txint(struct zscom *zs)
14547c478bd9Sstevel@tonic-gate {
14557c478bd9Sstevel@tonic-gate 	register struct syncline *zss;
14567c478bd9Sstevel@tonic-gate 	register mblk_t *mp;
14577c478bd9Sstevel@tonic-gate 	register int tmp;
14587c478bd9Sstevel@tonic-gate 	register uchar_t *wr_cur;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_TXINT, "zsh_txint: zs = %p", zs);
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	if ((wr_cur =  zs->zs_wr_cur) != NULL && (wr_cur <  zs->zs_wr_lim)) {
14637c478bd9Sstevel@tonic-gate 		SCC_WRITEDATA(*wr_cur++);
14647c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = wr_cur;
14657c478bd9Sstevel@tonic-gate 		return;
14667c478bd9Sstevel@tonic-gate 	}
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	zss = (struct syncline *)&zs->zs_priv_str;
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	switch (zss->sl_txstate) {
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 	/*
14747c478bd9Sstevel@tonic-gate 	 * we here because end of message block lim = cur
14757c478bd9Sstevel@tonic-gate 	 */
14767c478bd9Sstevel@tonic-gate 	case TX_ACTIVE:
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate 		mp = zss->sl_xactb;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate again_txint:
14817c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
14827c478bd9Sstevel@tonic-gate 		if (mp) {
14837c478bd9Sstevel@tonic-gate 			zss->sl_xactb = mp;
14847c478bd9Sstevel@tonic-gate 			zss->sl_ocnt += tmp = mp->b_wptr - mp->b_rptr;
14857c478bd9Sstevel@tonic-gate 			if (!tmp)
14867c478bd9Sstevel@tonic-gate 				goto again_txint;
14877c478bd9Sstevel@tonic-gate 			zs->zs_wr_cur = mp->b_rptr;
14887c478bd9Sstevel@tonic-gate 			zs->zs_wr_lim = mp->b_wptr;
14897c478bd9Sstevel@tonic-gate 			SCC_WRITEDATA(*zs->zs_wr_cur++);
14907c478bd9Sstevel@tonic-gate 			return;
14917c478bd9Sstevel@tonic-gate 		}
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate 		/*
14947c478bd9Sstevel@tonic-gate 		 * This is where the fun starts.  At this point the
14957c478bd9Sstevel@tonic-gate 		 * last character in the frame has been sent.  We
14967c478bd9Sstevel@tonic-gate 		 * issue a RESET_TXINT so we won't get another txint
14977c478bd9Sstevel@tonic-gate 		 * until the CRC has been completely sent.  Also we
14987c478bd9Sstevel@tonic-gate 		 * reset the Abort-On-Underrun bit so that CRC is
14997c478bd9Sstevel@tonic-gate 		 * sent at EOM, rather than an Abort.
15007c478bd9Sstevel@tonic-gate 		 */
15017c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = zs->zs_wr_lim = NULL;
15027c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_CRC;
15037c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15047c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_PHONY)) {
15057c478bd9Sstevel@tonic-gate 			SCC_BIC(10, ZSWR10_UNDERRUN_ABORT);
15067c478bd9Sstevel@tonic-gate 			zss->sl_st.opack++;
15077c478bd9Sstevel@tonic-gate 			zss->sl_st.ochar += zss->sl_ocnt;
15087c478bd9Sstevel@tonic-gate 		}
15097c478bd9Sstevel@tonic-gate 		zss->sl_ocnt = 0;
15107c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
15117c478bd9Sstevel@tonic-gate 		zss->sl_xhead = zss->sl_xactb = NULL;
15127c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
15137c478bd9Sstevel@tonic-gate 		break;
15147c478bd9Sstevel@tonic-gate 	/*
15157c478bd9Sstevel@tonic-gate 	 * This txint means we have sent the CRC bytes at EOF.
15167c478bd9Sstevel@tonic-gate 	 * The next txint will mean we are sending or have sent the
15177c478bd9Sstevel@tonic-gate 	 * flag character at EOF, but we handle that differently, and
15187c478bd9Sstevel@tonic-gate 	 * enter different states,depending on whether we're IBM or not.
15197c478bd9Sstevel@tonic-gate 	 */
15207c478bd9Sstevel@tonic-gate 	case TX_CRC:
15217c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
15227c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_FLAG;	/* HDX path */
15237c478bd9Sstevel@tonic-gate 		} else {	/* FDX path */
15247c478bd9Sstevel@tonic-gate 			if (!zsh_start(zs, zss)) {
15257c478bd9Sstevel@tonic-gate 				zss->sl_txstate = TX_IDLE;
15267c478bd9Sstevel@tonic-gate 				SCC_WRITE0(ZSWR0_RESET_TXINT);
15277c478bd9Sstevel@tonic-gate 			}
15287c478bd9Sstevel@tonic-gate 		}
15297c478bd9Sstevel@tonic-gate 		break;
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 	/*
15327c478bd9Sstevel@tonic-gate 	 * This txint means the closing flag byte is going out the door.
15337c478bd9Sstevel@tonic-gate 	 * We use this state to allow this to complete before dropping RTS.
15347c478bd9Sstevel@tonic-gate 	 */
15357c478bd9Sstevel@tonic-gate 	case TX_FLAG:
15367c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_LAST;
15377c478bd9Sstevel@tonic-gate 		(void) zsh_start(zs, zss);
15387c478bd9Sstevel@tonic-gate 		break;
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 	/*
15417c478bd9Sstevel@tonic-gate 	 * Arriving here means the flag should be out and it's finally
15427c478bd9Sstevel@tonic-gate 	 * time to close the barn door.
15437c478bd9Sstevel@tonic-gate 	 */
15447c478bd9Sstevel@tonic-gate 	case TX_LAST:
15457c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
15467c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15477c478bd9Sstevel@tonic-gate 		break;
15487c478bd9Sstevel@tonic-gate 
15497c478bd9Sstevel@tonic-gate 	/*
15507c478bd9Sstevel@tonic-gate 	 * If transmit was aborted, do nothing - watchdog will recover.
15517c478bd9Sstevel@tonic-gate 	 */
15527c478bd9Sstevel@tonic-gate 	case TX_ABORTED:
15537c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15547c478bd9Sstevel@tonic-gate 		break;
15557c478bd9Sstevel@tonic-gate 
15567c478bd9Sstevel@tonic-gate 	default:
15577c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15587c478bd9Sstevel@tonic-gate 		break;
15597c478bd9Sstevel@tonic-gate 	}
15607c478bd9Sstevel@tonic-gate }
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate /*
15637c478bd9Sstevel@tonic-gate  * External Status Change interrupt service procedure
15647c478bd9Sstevel@tonic-gate  */
15657c478bd9Sstevel@tonic-gate static void
15667c478bd9Sstevel@tonic-gate zsh_xsint(struct zscom *zs)
15677c478bd9Sstevel@tonic-gate {
15687c478bd9Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
15697c478bd9Sstevel@tonic-gate 	register uchar_t s0, x0;
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_XSINT, "zsh_xsint: zs = %p", zs);
15727c478bd9Sstevel@tonic-gate 
15737c478bd9Sstevel@tonic-gate 	s0 = SCC_READ0();
15747c478bd9Sstevel@tonic-gate 	x0 = s0 ^ zss->sl_rr0;
15757c478bd9Sstevel@tonic-gate 	zss->sl_rr0 = s0;
15767c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);
15777c478bd9Sstevel@tonic-gate 
15787c478bd9Sstevel@tonic-gate 	if (s0 & ZSRR0_TXUNDER) {
15797c478bd9Sstevel@tonic-gate 		switch (zss->sl_txstate) {
15807c478bd9Sstevel@tonic-gate 		/*
15817c478bd9Sstevel@tonic-gate 		 * A transmitter underrun has occurred.  If we are not
15827c478bd9Sstevel@tonic-gate 		 * here as the result of an abort sent by the watchdog
15837c478bd9Sstevel@tonic-gate 		 * timeout routine, we need to send an abort to flush
15847c478bd9Sstevel@tonic-gate 		 * the transmitter.  Otherwise there is a danger of
15857c478bd9Sstevel@tonic-gate 		 * trashing the next frame but still sending a good crc.
15867c478bd9Sstevel@tonic-gate 		 * The TX_ABORTED flag is set so that the watchdog
15877c478bd9Sstevel@tonic-gate 		 * routine can initiate recovery.
15887c478bd9Sstevel@tonic-gate 		 */
15897c478bd9Sstevel@tonic-gate 		case TX_ACTIVE:
15907c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
15917c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
15927c478bd9Sstevel@tonic-gate 			zss->sl_st.underrun++;
15937c478bd9Sstevel@tonic-gate 			zsh_txbad(zs, zss);
15947c478bd9Sstevel@tonic-gate 
15957c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED;
15967c478bd9Sstevel@tonic-gate 			zss->sl_wd_count = 0;
15977c478bd9Sstevel@tonic-gate 			break;
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 		case TX_CRC:
16007c478bd9Sstevel@tonic-gate 			break;
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate 		case TX_FLAG:
16037c478bd9Sstevel@tonic-gate 			break;
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate 		case TX_ABORTED:
16067c478bd9Sstevel@tonic-gate 			break;
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate 		case TX_OFF:
16097c478bd9Sstevel@tonic-gate 			break;
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 		case TX_LAST:
16127c478bd9Sstevel@tonic-gate 			break;
16137c478bd9Sstevel@tonic-gate 
16147c478bd9Sstevel@tonic-gate 		default:
16157c478bd9Sstevel@tonic-gate 			break;
16167c478bd9Sstevel@tonic-gate 		}
16177c478bd9Sstevel@tonic-gate 	}
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	if ((x0 & ZSRR0_BREAK) && (s0 & ZSRR0_BREAK) && zs->zs_rd_cur) {
16207c478bd9Sstevel@tonic-gate 		zss->sl_st.abort++;
16217c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
16227c478bd9Sstevel@tonic-gate 	} else if ((s0 & ZSRR0_SYNC) && (zs->zs_rd_cur)) {
16237c478bd9Sstevel@tonic-gate 		/*
16247c478bd9Sstevel@tonic-gate 		 * Tricky code to avoid disaster in the case where
16257c478bd9Sstevel@tonic-gate 		 * an abort was detected while receiving a packet,
16267c478bd9Sstevel@tonic-gate 		 * but the abort did not last long enough to be
16277c478bd9Sstevel@tonic-gate 		 * detected by zsh_xsint - this can happen since
16287c478bd9Sstevel@tonic-gate 		 * the ZSRR0_BREAK is not latched.  Since an abort
16297c478bd9Sstevel@tonic-gate 		 * will automatically cause the SCC to enter
16307c478bd9Sstevel@tonic-gate 		 * hunt mode, hopefully, the sync/hunt bit will be
16317c478bd9Sstevel@tonic-gate 		 * set in this case (although if the interrupt is
16327c478bd9Sstevel@tonic-gate 		 * sufficiently delayed, the SCC may have sync'ed
16337c478bd9Sstevel@tonic-gate 		 * in again if it has detected a flag).
16347c478bd9Sstevel@tonic-gate 		 */
16357c478bd9Sstevel@tonic-gate 		zss->sl_st.abort++;
16367c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
16377c478bd9Sstevel@tonic-gate 	}
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate 	if (x0 & s0 & ZSRR0_CTS) {
16407c478bd9Sstevel@tonic-gate 	    if (zss->sl_txstate == TX_RTS) {
16417c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
16427c478bd9Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_TX_ENABLE);
16437c478bd9Sstevel@tonic-gate 		}
16447c478bd9Sstevel@tonic-gate 		(void) zsh_start(zs, zss);
16457c478bd9Sstevel@tonic-gate 	    } else if ((zss->sl_mode.sm_config & (CONN_IBM | CONN_SIGNAL))) {
16467c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FLUSH_WQ;
16477c478bd9Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_UP);
16487c478bd9Sstevel@tonic-gate 	    }
16497c478bd9Sstevel@tonic-gate 	}
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 	/*
16527c478bd9Sstevel@tonic-gate 	 * We don't care about CTS transitions unless we are in either
16537c478bd9Sstevel@tonic-gate 	 * IBM or SIGNAL mode, or both.  So, if we see CTS drop, and we
16547c478bd9Sstevel@tonic-gate 	 * care, and we are not idle, send up a report message.
16557c478bd9Sstevel@tonic-gate 	 */
16567c478bd9Sstevel@tonic-gate 	if ((x0 & ZSRR0_CTS) && ((s0 & ZSRR0_CTS) == 0) &&
16577c478bd9Sstevel@tonic-gate 	    (zss->sl_txstate != TX_OFF) &&
16587c478bd9Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_IBM | CONN_SIGNAL))) {
16597c478bd9Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
16607c478bd9Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_DOWN);
16617c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
16627c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
16637c478bd9Sstevel@tonic-gate 		zss->sl_st.cts++;
16647c478bd9Sstevel@tonic-gate 		if (zss->sl_txstate != TX_IDLE)
16657c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
16667c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
16677c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
16687c478bd9Sstevel@tonic-gate 		zss->sl_wd_count = 0;
16697c478bd9Sstevel@tonic-gate 		zsh_txbad(zs, zss);
16707c478bd9Sstevel@tonic-gate 	}
16717c478bd9Sstevel@tonic-gate }
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 
16747c478bd9Sstevel@tonic-gate /*
16757c478bd9Sstevel@tonic-gate  * Receive interrupt service procedure
16767c478bd9Sstevel@tonic-gate  */
16777c478bd9Sstevel@tonic-gate static void
16787c478bd9Sstevel@tonic-gate zsh_rxint(struct zscom *zs)
16797c478bd9Sstevel@tonic-gate {
16807c478bd9Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
16817c478bd9Sstevel@tonic-gate 	register mblk_t *bp = zss->sl_ractb;
16827c478bd9Sstevel@tonic-gate 	unsigned char *rd_cur;
16837c478bd9Sstevel@tonic-gate 
16847c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_RXINT, "zsh_rxint: zs = %p", zs);
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	if (((rd_cur = zs->zs_rd_cur) != NULL) && rd_cur < zs->zs_rd_lim) {
16877c478bd9Sstevel@tonic-gate 		*rd_cur++ = SCC_READDATA();
16887c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = rd_cur;
16897c478bd9Sstevel@tonic-gate 		return;
16907c478bd9Sstevel@tonic-gate 	}
16917c478bd9Sstevel@tonic-gate 
16927c478bd9Sstevel@tonic-gate 	if (!rd_cur) { /* Beginning of frame */
16937c478bd9Sstevel@tonic-gate 		if (!bp) {
16947c478bd9Sstevel@tonic-gate 			ZSH_ALLOCB(bp);
16957c478bd9Sstevel@tonic-gate 			zss->sl_ractb = bp;
16967c478bd9Sstevel@tonic-gate 		}
16977c478bd9Sstevel@tonic-gate 		zss->sl_rhead = bp;
16987c478bd9Sstevel@tonic-gate 	} else {	/* end of data block should be cur==lim */
16997c478bd9Sstevel@tonic-gate 		bp->b_wptr = zs->zs_rd_cur;
17007c478bd9Sstevel@tonic-gate 		ZSH_ALLOCB(bp->b_cont);
17017c478bd9Sstevel@tonic-gate 		bp = zss->sl_ractb = bp->b_cont;
17027c478bd9Sstevel@tonic-gate 	}
17037c478bd9Sstevel@tonic-gate 	if (!bp) {
17047c478bd9Sstevel@tonic-gate 		zss->sl_st.nobuffers++;
17057c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
17067c478bd9Sstevel@tonic-gate 		return;
17077c478bd9Sstevel@tonic-gate 	}
17087c478bd9Sstevel@tonic-gate 	zs->zs_rd_cur = bp->b_wptr;
17097c478bd9Sstevel@tonic-gate 	zs->zs_rd_lim = bp->b_datap->db_lim;
17107c478bd9Sstevel@tonic-gate 	*zs->zs_rd_cur++ = SCC_READDATA(); /* Also resets interrupt */
17117c478bd9Sstevel@tonic-gate }
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 
17147c478bd9Sstevel@tonic-gate /*
17157c478bd9Sstevel@tonic-gate  * Special Receive Condition Interrupt routine
17167c478bd9Sstevel@tonic-gate  */
17177c478bd9Sstevel@tonic-gate static void
17187c478bd9Sstevel@tonic-gate zsh_srint(struct zscom *zs)
17197c478bd9Sstevel@tonic-gate {
17207c478bd9Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
17217c478bd9Sstevel@tonic-gate 	register uchar_t s1;
17227c478bd9Sstevel@tonic-gate 	register uchar_t *rd_cur;
17237c478bd9Sstevel@tonic-gate 
17247c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SRINT, "zsh_srint: zs = %p", zs);
17257c478bd9Sstevel@tonic-gate 
17267c478bd9Sstevel@tonic-gate 	SCC_READ(1, s1);
17277c478bd9Sstevel@tonic-gate 
17287c478bd9Sstevel@tonic-gate 	if (s1 & ZSRR1_RXEOF) {			/* end of frame */
17297c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
17307c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17317c478bd9Sstevel@tonic-gate 		if (s1 & ZSRR1_FE) {		/* bad CRC */
17327c478bd9Sstevel@tonic-gate 			zss->sl_st.crc++;
17337c478bd9Sstevel@tonic-gate 			zsh_rxbad(zs, zss);
17347c478bd9Sstevel@tonic-gate 			return;
17357c478bd9Sstevel@tonic-gate 		}
17367c478bd9Sstevel@tonic-gate 
17377c478bd9Sstevel@tonic-gate 		if ((rd_cur = zs->zs_rd_cur) == NULL)
17387c478bd9Sstevel@tonic-gate 			return;
17397c478bd9Sstevel@tonic-gate 
17407c478bd9Sstevel@tonic-gate 		/*
17417c478bd9Sstevel@tonic-gate 		 * Drop one CRC byte from length because it came in
17427c478bd9Sstevel@tonic-gate 		 * before the special interrupt got here.
17437c478bd9Sstevel@tonic-gate 		 */
17447c478bd9Sstevel@tonic-gate 		zss->sl_ractb->b_wptr = rd_cur - 1;
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 		/*
17477c478bd9Sstevel@tonic-gate 		 * put on done queue
17487c478bd9Sstevel@tonic-gate 		 */
17497c478bd9Sstevel@tonic-gate 		ZSH_PUTQ(zss->sl_rhead);
17507c478bd9Sstevel@tonic-gate 		zss->sl_rhead = NULL;
17517c478bd9Sstevel@tonic-gate 		zss->sl_ractb = NULL;
17527c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
17537c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
17547c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 	} else if (s1 & ZSRR1_DO) {
17577c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
17587c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17597c478bd9Sstevel@tonic-gate 		zss->sl_st.overrun++;
17607c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
17617c478bd9Sstevel@tonic-gate 	} else
17627c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17637c478bd9Sstevel@tonic-gate }
17647c478bd9Sstevel@tonic-gate 
17657c478bd9Sstevel@tonic-gate /*
17667c478bd9Sstevel@tonic-gate  * Handle a second stage interrupt.
17677c478bd9Sstevel@tonic-gate  * Does mostly lower priority buffer management stuff.
17687c478bd9Sstevel@tonic-gate  */
17697c478bd9Sstevel@tonic-gate static int
17707c478bd9Sstevel@tonic-gate zsh_softint(struct zscom *zs)
17717c478bd9Sstevel@tonic-gate {
17727c478bd9Sstevel@tonic-gate 	register struct syncline *zss;
17737c478bd9Sstevel@tonic-gate 	register queue_t *q;
17747c478bd9Sstevel@tonic-gate 	register mblk_t *mp, *tmp;
17757c478bd9Sstevel@tonic-gate 	register mblk_t *head = NULL, *tail = NULL;
17767c478bd9Sstevel@tonic-gate 	register int allocbcount = 0;
17777c478bd9Sstevel@tonic-gate 	int m_error;
17787c478bd9Sstevel@tonic-gate 
17797c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_START, "zsh_soft start: zs = %p", zs);
17807c478bd9Sstevel@tonic-gate 
17817c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
17827c478bd9Sstevel@tonic-gate 	zss = (struct syncline *)zs->zs_priv;
17837c478bd9Sstevel@tonic-gate 	if (!zss || (q = zss->sl_stream.str_rq) == NULL) {
17847c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
17857c478bd9Sstevel@tonic-gate 		return (0);
17867c478bd9Sstevel@tonic-gate 	}
17877c478bd9Sstevel@tonic-gate 	m_error = zss->sl_m_error;
17887c478bd9Sstevel@tonic-gate 
17897c478bd9Sstevel@tonic-gate 	zss->sl_m_error = 0;
17907c478bd9Sstevel@tonic-gate 
17917c478bd9Sstevel@tonic-gate 
17927c478bd9Sstevel@tonic-gate 	if (!zss->sl_mstat)
17937c478bd9Sstevel@tonic-gate 		zss->sl_mstat = allocb(sizeof (struct sl_status), BPRI_MED);
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
17967c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
17977c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
17987c478bd9Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
17997c478bd9Sstevel@tonic-gate 		} else {
18007c478bd9Sstevel@tonic-gate 			register uchar_t s0;
18017c478bd9Sstevel@tonic-gate 
18027c478bd9Sstevel@tonic-gate 			s0 = SCC_READ0();
18037c478bd9Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
18047c478bd9Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
18057c478bd9Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
18067c478bd9Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
18077c478bd9Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
18087c478bd9Sstevel@tonic-gate 			}
18097c478bd9Sstevel@tonic-gate 			if (zss->sl_flags & SF_FLUSH_WQ) {
18107c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
18117c478bd9Sstevel@tonic-gate 				flushq(WR(q), FLUSHDATA);
18127c478bd9Sstevel@tonic-gate 				goto next;
18137c478bd9Sstevel@tonic-gate 			}
18147c478bd9Sstevel@tonic-gate 		}
18157c478bd9Sstevel@tonic-gate 	}
18167c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
18177c478bd9Sstevel@tonic-gate 
18187c478bd9Sstevel@tonic-gate next:
18197c478bd9Sstevel@tonic-gate 	for (;;) {
18207c478bd9Sstevel@tonic-gate 		ZSH_GETQ(mp);
18217c478bd9Sstevel@tonic-gate 		if (!mp)
18227c478bd9Sstevel@tonic-gate 			break;
18237c478bd9Sstevel@tonic-gate 
18247c478bd9Sstevel@tonic-gate 		if (mp->b_rptr == mp->b_wptr) {
18257c478bd9Sstevel@tonic-gate 			if (mp->b_datap->db_type == M_RSE) {
18267c478bd9Sstevel@tonic-gate 				allocbcount++;
18277c478bd9Sstevel@tonic-gate 			}
18287c478bd9Sstevel@tonic-gate 			freemsg(mp);
18297c478bd9Sstevel@tonic-gate 			continue;
18307c478bd9Sstevel@tonic-gate 		}
18317c478bd9Sstevel@tonic-gate 		if (mp->b_datap->db_type == M_DATA) {
18327c478bd9Sstevel@tonic-gate 			zss->sl_st.ichar += msgdsize(mp);
18337c478bd9Sstevel@tonic-gate 			zss->sl_st.ipack++;
18347c478bd9Sstevel@tonic-gate 			if (!(canputnext(q))) {
18357c478bd9Sstevel@tonic-gate 				zss->sl_st.ierror++;
18367c478bd9Sstevel@tonic-gate 				allocbcount++;
18377c478bd9Sstevel@tonic-gate 				freemsg(mp);
18387c478bd9Sstevel@tonic-gate 				continue;
18397c478bd9Sstevel@tonic-gate 			}
18407c478bd9Sstevel@tonic-gate 		} else if (mp->b_datap->db_type == M_PROTO) {
18417c478bd9Sstevel@tonic-gate 			if (!(canputnext(q))) {
18427c478bd9Sstevel@tonic-gate 				freemsg(mp);
18437c478bd9Sstevel@tonic-gate 				continue;
18447c478bd9Sstevel@tonic-gate 			}
18457c478bd9Sstevel@tonic-gate 		}
18467c478bd9Sstevel@tonic-gate 		if (!head) {
18477c478bd9Sstevel@tonic-gate 			allocbcount++;
18487c478bd9Sstevel@tonic-gate 			zss->sl_soft_active = 1;
18497c478bd9Sstevel@tonic-gate 			head = mp;
18507c478bd9Sstevel@tonic-gate 		} else {
18517c478bd9Sstevel@tonic-gate 			if (!tail)
18527c478bd9Sstevel@tonic-gate 				tail = head;
18537c478bd9Sstevel@tonic-gate 			tail->b_next = mp;
18547c478bd9Sstevel@tonic-gate 			tail = mp;
18557c478bd9Sstevel@tonic-gate 		}
18567c478bd9Sstevel@tonic-gate 	}
18577c478bd9Sstevel@tonic-gate 	if (allocbcount)
18587c478bd9Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, allocbcount);
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate 	tmp = NULL;
18617c478bd9Sstevel@tonic-gate again:
18627c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
18637c478bd9Sstevel@tonic-gate 	if (!zss->sl_xstandby) {
18647c478bd9Sstevel@tonic-gate 		if (tmp) {
18657c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = tmp;
18667c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
18677c478bd9Sstevel@tonic-gate 		} else {
18687c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
18697c478bd9Sstevel@tonic-gate 			if (tmp = getq(WR(q)))
18707c478bd9Sstevel@tonic-gate 				goto again;
18717c478bd9Sstevel@tonic-gate 		}
18727c478bd9Sstevel@tonic-gate 	} else {
18737c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
18747c478bd9Sstevel@tonic-gate 		if (tmp)
18757c478bd9Sstevel@tonic-gate 			(void) putbq(WR(q), tmp);
18767c478bd9Sstevel@tonic-gate 	}
18777c478bd9Sstevel@tonic-gate 
18787c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 	while (head) {
18817c478bd9Sstevel@tonic-gate 		if (!tail) {
18827c478bd9Sstevel@tonic-gate 			putnext(q, head);
18837c478bd9Sstevel@tonic-gate 			break;
18847c478bd9Sstevel@tonic-gate 		}
18857c478bd9Sstevel@tonic-gate 		mp = head;
18867c478bd9Sstevel@tonic-gate 		head = head->b_next;
18877c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
18887c478bd9Sstevel@tonic-gate 		putnext(q, mp);
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 	}
18917c478bd9Sstevel@tonic-gate 
18927c478bd9Sstevel@tonic-gate 	if (m_error)
18937c478bd9Sstevel@tonic-gate 		(void) putnextctl1(q, M_ERROR, m_error);
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate 	zss->sl_soft_active = 0;
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_END, "zsh_soft end: zs = %p", zs);
18987c478bd9Sstevel@tonic-gate 
18997c478bd9Sstevel@tonic-gate 	return (0);
19007c478bd9Sstevel@tonic-gate }
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate /*
19037c478bd9Sstevel@tonic-gate  * Initialization routine.
19047c478bd9Sstevel@tonic-gate  * Sets Clock sources, baud rate, modes and miscellaneous parameters.
19057c478bd9Sstevel@tonic-gate  */
19067c478bd9Sstevel@tonic-gate static int
19077c478bd9Sstevel@tonic-gate zsh_program(struct zscom *zs, struct scc_mode *sm)
19087c478bd9Sstevel@tonic-gate {
19097c478bd9Sstevel@tonic-gate 	register struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
19107c478bd9Sstevel@tonic-gate 	register struct zs_prog *zspp;
19117c478bd9Sstevel@tonic-gate 	register ushort_t	tconst = 0;
19127c478bd9Sstevel@tonic-gate 	register int	wr11 = 0;
19137c478bd9Sstevel@tonic-gate 	register int	baud = 0;
19147c478bd9Sstevel@tonic-gate 	register int	pll = 0;
19157c478bd9Sstevel@tonic-gate 	register int	speed = 0;
19167c478bd9Sstevel@tonic-gate 	register int	flags = ZSP_SYNC;
19177c478bd9Sstevel@tonic-gate 	int		err = 0;
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs); /* get our house in order */
19207c478bd9Sstevel@tonic-gate 
19217c478bd9Sstevel@tonic-gate 	switch (sm->sm_txclock) {
19227c478bd9Sstevel@tonic-gate 	case TXC_IS_TXC:
19237c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_TRXC;
19247c478bd9Sstevel@tonic-gate 		break;
19257c478bd9Sstevel@tonic-gate 	case TXC_IS_RXC:
19267c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_RTXC;
19277c478bd9Sstevel@tonic-gate 		break;
19287c478bd9Sstevel@tonic-gate 	case TXC_IS_BAUD:
19297c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_BAUD;
19307c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TRXC_OUT_ENA + ZSWR11_TRXC_XMIT;
19317c478bd9Sstevel@tonic-gate 		baud++;
19327c478bd9Sstevel@tonic-gate 		break;
19337c478bd9Sstevel@tonic-gate 	case TXC_IS_PLL:
19347c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_DPLL;
19357c478bd9Sstevel@tonic-gate 		pll++;
19367c478bd9Sstevel@tonic-gate 		break;
19377c478bd9Sstevel@tonic-gate 	default:
19387c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_TXC;
19397c478bd9Sstevel@tonic-gate 		err = EINVAL;
19407c478bd9Sstevel@tonic-gate 		goto out;
19417c478bd9Sstevel@tonic-gate 	}
19427c478bd9Sstevel@tonic-gate 	switch (sm->sm_rxclock) {
19437c478bd9Sstevel@tonic-gate 	case RXC_IS_RXC:
19447c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_RTXC;
19457c478bd9Sstevel@tonic-gate 		break;
19467c478bd9Sstevel@tonic-gate 	case RXC_IS_TXC:
19477c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_TRXC;
19487c478bd9Sstevel@tonic-gate 		break;
19497c478bd9Sstevel@tonic-gate 	case RXC_IS_BAUD:
19507c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_BAUD;
19517c478bd9Sstevel@tonic-gate 		baud++;
19527c478bd9Sstevel@tonic-gate 		break;
19537c478bd9Sstevel@tonic-gate 	case RXC_IS_PLL:
19547c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_DPLL;
19557c478bd9Sstevel@tonic-gate 		pll++;
19567c478bd9Sstevel@tonic-gate 		break;
19577c478bd9Sstevel@tonic-gate 	default:
19587c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
19597c478bd9Sstevel@tonic-gate 		err = EINVAL;
19607c478bd9Sstevel@tonic-gate 		goto out;
19617c478bd9Sstevel@tonic-gate 	}
19627c478bd9Sstevel@tonic-gate 	if (baud && pll) {
19637c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
19647c478bd9Sstevel@tonic-gate 		err = EINVAL;
19657c478bd9Sstevel@tonic-gate 		goto out;
19667c478bd9Sstevel@tonic-gate 	}
19677c478bd9Sstevel@tonic-gate 	if (pll && !(sm->sm_config & CONN_NRZI)) {
19687c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
19697c478bd9Sstevel@tonic-gate 		err = EINVAL;
19707c478bd9Sstevel@tonic-gate 		goto out;
19717c478bd9Sstevel@tonic-gate 	}
19727c478bd9Sstevel@tonic-gate 
19737c478bd9Sstevel@tonic-gate 	/*
19747c478bd9Sstevel@tonic-gate 	 * If we're going to use the BRG and the speed we want is != 0...
19757c478bd9Sstevel@tonic-gate 	 */
19767c478bd9Sstevel@tonic-gate 	if (baud && (speed = sm->sm_baudrate)) {
19777c478bd9Sstevel@tonic-gate 		tconst = (PCLK + speed) / (2 * speed) - 2;
19787c478bd9Sstevel@tonic-gate 		if (tconst == 0) {
19797c478bd9Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
19807c478bd9Sstevel@tonic-gate 			err = EINVAL;
19817c478bd9Sstevel@tonic-gate 			goto out;
19827c478bd9Sstevel@tonic-gate 		}
19837c478bd9Sstevel@tonic-gate 		sm->sm_baudrate = PCLK / (2 * ((int)tconst + 2));
19847c478bd9Sstevel@tonic-gate 	} else {
19857c478bd9Sstevel@tonic-gate 		tconst = 0;	/* Stop BRG.  Also quiesces pin 24. */
19867c478bd9Sstevel@tonic-gate 	}
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate 	if (pll) {
19897c478bd9Sstevel@tonic-gate 		if ((speed  = sm->sm_baudrate * 32) != 0)
19907c478bd9Sstevel@tonic-gate 			tconst = (PCLK + speed) / (2 * speed) - 2;
19917c478bd9Sstevel@tonic-gate 		else
19927c478bd9Sstevel@tonic-gate 			tconst = 0;
19937c478bd9Sstevel@tonic-gate 		if (tconst == 0) {
19947c478bd9Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
19957c478bd9Sstevel@tonic-gate 			err = EINVAL;
19967c478bd9Sstevel@tonic-gate 			goto out;
19977c478bd9Sstevel@tonic-gate 		}
19987c478bd9Sstevel@tonic-gate 		speed = PCLK / (2 * ((int)tconst + 2));
19997c478bd9Sstevel@tonic-gate 		sm->sm_baudrate = speed / 32;
20007c478bd9Sstevel@tonic-gate 		flags |= ZSP_PLL;
20017c478bd9Sstevel@tonic-gate 	}
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 	if ((sm->sm_config & (CONN_LPBK|CONN_ECHO)) == (CONN_LPBK|CONN_ECHO)) {
20047c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_LPBKS;
20057c478bd9Sstevel@tonic-gate 		err = EINVAL;
20067c478bd9Sstevel@tonic-gate 		goto out;
20077c478bd9Sstevel@tonic-gate 	}
20087c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_LPBK)
20097c478bd9Sstevel@tonic-gate 		flags |= ZSP_LOOP;
20107c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_NRZI)
20117c478bd9Sstevel@tonic-gate 		flags |= ZSP_NRZI;
20127c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_ECHO)
20137c478bd9Sstevel@tonic-gate 		flags |= ZSP_ECHO;
20147c478bd9Sstevel@tonic-gate 
20157c478bd9Sstevel@tonic-gate 	zspp = &zs_prog[zs->zs_unit];
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate 	zspp->zs = zs;
20187c478bd9Sstevel@tonic-gate 	zspp->flags = (uchar_t)flags;
20197c478bd9Sstevel@tonic-gate 	zspp->wr4 = ZSWR4_SDLC;
20207c478bd9Sstevel@tonic-gate 	zspp->wr11 = (uchar_t)wr11;
20217c478bd9Sstevel@tonic-gate 	zspp->wr12 = (uchar_t)(tconst & 0xff);
20227c478bd9Sstevel@tonic-gate 	zspp->wr13 = (uchar_t)((tconst >> 8) & 0xff);
20237c478bd9Sstevel@tonic-gate 	zspp->wr3 = (uchar_t)(ZSWR3_RX_ENABLE | ZSWR3_RXCRC_ENABLE |
20247c478bd9Sstevel@tonic-gate 	    ZSWR3_RX_8);
20257c478bd9Sstevel@tonic-gate 	zspp->wr5 = (uchar_t)(ZSWR5_TX_8 | ZSWR5_DTR | ZSWR5_TXCRC_ENABLE);
20267c478bd9Sstevel@tonic-gate 
20277c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FDXPTP) {
20287c478bd9Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_RTS;
20297c478bd9Sstevel@tonic-gate 		zss->sl_rr0 |= ZSRR0_CTS;		/* Assume CTS is high */
20307c478bd9Sstevel@tonic-gate 	}
20317c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_IBM) {
20327c478bd9Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
20337c478bd9Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC | ZSR15_CTS);
20347c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
20357c478bd9Sstevel@tonic-gate 			zspp->wr15 &= ~ZSR15_CTS;
20367c478bd9Sstevel@tonic-gate 	} else {
20377c478bd9Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_TX_ENABLE;
20387c478bd9Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
20397c478bd9Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC);
20407c478bd9Sstevel@tonic-gate 		if (sm->sm_config & CONN_SIGNAL)
20417c478bd9Sstevel@tonic-gate 			zspp->wr15 |= ZSR15_CTS;
20427c478bd9Sstevel@tonic-gate 	}
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	zs_program(zspp);
20457c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
20467c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
20477c478bd9Sstevel@tonic-gate 	zss->sl_flags |= SF_INITIALIZED;
20487c478bd9Sstevel@tonic-gate 	bzero(&zss->sl_st, sizeof (struct sl_stats));
20497c478bd9Sstevel@tonic-gate 	bcopy(sm, &zss->sl_mode, sizeof (struct scc_mode));
20507c478bd9Sstevel@tonic-gate 	zss->sl_mode.sm_retval = 0;	/* successful */
20517c478bd9Sstevel@tonic-gate out:
20527c478bd9Sstevel@tonic-gate 	return (err);
20537c478bd9Sstevel@tonic-gate }
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate /*
20567c478bd9Sstevel@tonic-gate  * Function to store modem signal changes in sl_mstat field.
20577c478bd9Sstevel@tonic-gate  * Note that these events are supposed to be so far apart in time that
20587c478bd9Sstevel@tonic-gate  * we should always be able to send up the event and allocate a message
20597c478bd9Sstevel@tonic-gate  * block before another one happens.  If not, we'll overwrite this one.
20607c478bd9Sstevel@tonic-gate  */
20617c478bd9Sstevel@tonic-gate static void
20627c478bd9Sstevel@tonic-gate zsh_setmstat(struct zscom *zs, int event)
20637c478bd9Sstevel@tonic-gate {
20647c478bd9Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
20657c478bd9Sstevel@tonic-gate 	register struct sl_status *mstat;
20667c478bd9Sstevel@tonic-gate 	register mblk_t *mp;
20677c478bd9Sstevel@tonic-gate 
20687c478bd9Sstevel@tonic-gate 	if (((mp = zss->sl_mstat) != NULL) &&
20697c478bd9Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_SIGNAL))) {
20707c478bd9Sstevel@tonic-gate 		mstat = (struct sl_status *)mp->b_wptr;
20717c478bd9Sstevel@tonic-gate 		mstat->type = (zss->sl_mode.sm_config & CONN_IBM) ?
20727c478bd9Sstevel@tonic-gate 		    SLS_LINKERR : SLS_MDMSTAT;
20737c478bd9Sstevel@tonic-gate 		mstat->status = event;
20747c478bd9Sstevel@tonic-gate 		gethrestime(&mstat->tstamp);
20757c478bd9Sstevel@tonic-gate 		mp->b_wptr += sizeof (struct sl_status);
20767c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
20777c478bd9Sstevel@tonic-gate 		ZSH_PUTQ(mp);
20787c478bd9Sstevel@tonic-gate 		zss->sl_mstat = NULL;
20797c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
20807c478bd9Sstevel@tonic-gate 	}
20817c478bd9Sstevel@tonic-gate }
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate /*
20847c478bd9Sstevel@tonic-gate  * Received Bad Frame procedure
20857c478bd9Sstevel@tonic-gate  */
20867c478bd9Sstevel@tonic-gate static void
20877c478bd9Sstevel@tonic-gate zsh_rxbad(struct zscom *zs, struct syncline *zss)
20887c478bd9Sstevel@tonic-gate {
20897c478bd9Sstevel@tonic-gate 	/*
20907c478bd9Sstevel@tonic-gate 	 * swallow bad characters
20917c478bd9Sstevel@tonic-gate 	 */
20927c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20937c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20947c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	SCC_BIS(3, ZSWR3_HUNT);	/* enter hunt mode - ignores rest of frame */
20977c478bd9Sstevel@tonic-gate 
20987c478bd9Sstevel@tonic-gate 	zss->sl_st.ierror++;
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	/*
21017c478bd9Sstevel@tonic-gate 	 * Free active receive message.
21027c478bd9Sstevel@tonic-gate 	 */
21037c478bd9Sstevel@tonic-gate 	if (zss->sl_rhead) {
21047c478bd9Sstevel@tonic-gate 		zss->sl_rhead->b_wptr = zss->sl_rhead->b_rptr;
21057c478bd9Sstevel@tonic-gate 		zss->sl_rhead->b_datap->db_type = M_RSE;
21067c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_rhead);
21077c478bd9Sstevel@tonic-gate 		zss->sl_ractb = NULL;
21087c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
21097c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
21107c478bd9Sstevel@tonic-gate 	}
21117c478bd9Sstevel@tonic-gate 	if (zss->sl_rhead) {
21127c478bd9Sstevel@tonic-gate 		zss->sl_rhead = NULL;
21137c478bd9Sstevel@tonic-gate 		ZSH_ALLOCB(zss->sl_ractb);
21147c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
21157c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
21167c478bd9Sstevel@tonic-gate 	}
21177c478bd9Sstevel@tonic-gate 
21187c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs);
21197c478bd9Sstevel@tonic-gate }
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate /*
21227c478bd9Sstevel@tonic-gate  * Transmit error procedure
21237c478bd9Sstevel@tonic-gate  */
21247c478bd9Sstevel@tonic-gate static void
21257c478bd9Sstevel@tonic-gate zsh_txbad(struct zscom *zs, struct syncline *zss)
21267c478bd9Sstevel@tonic-gate {
21277c478bd9Sstevel@tonic-gate 	if (zss->sl_xhead) {		/* free the message we were sending */
21287c478bd9Sstevel@tonic-gate 		zss->sl_xhead->b_wptr = zss->sl_xhead->b_rptr;
21297c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
21307c478bd9Sstevel@tonic-gate 		zss->sl_xactb = NULL;
21317c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
21327c478bd9Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
21337c478bd9Sstevel@tonic-gate 	}
21347c478bd9Sstevel@tonic-gate 	zss->sl_xhead = NULL;
21357c478bd9Sstevel@tonic-gate 
21367c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_FDXPTP)) {
21377c478bd9Sstevel@tonic-gate 		/*
21387c478bd9Sstevel@tonic-gate 		 * drop RTS and our notion of CTS
21397c478bd9Sstevel@tonic-gate 		 */
21407c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
21417c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
21427c478bd9Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
21437c478bd9Sstevel@tonic-gate 	}
21447c478bd9Sstevel@tonic-gate 	zss->sl_txstate = TX_IDLE;
21457c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_PHONY))
21467c478bd9Sstevel@tonic-gate 		zss->sl_st.oerror++;
21477c478bd9Sstevel@tonic-gate }
21487c478bd9Sstevel@tonic-gate 
21497c478bd9Sstevel@tonic-gate /*
21507c478bd9Sstevel@tonic-gate  * Transmitter watchdog timeout routine
21517c478bd9Sstevel@tonic-gate  */
21527c478bd9Sstevel@tonic-gate static void
21537c478bd9Sstevel@tonic-gate zsh_watchdog(void *arg)
21547c478bd9Sstevel@tonic-gate {
21557c478bd9Sstevel@tonic-gate 	struct zscom *zs = arg;
21567c478bd9Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
21577c478bd9Sstevel@tonic-gate 	queue_t *wq;
21587c478bd9Sstevel@tonic-gate 	mblk_t *mp;
21597c478bd9Sstevel@tonic-gate 	int warning = 0;
21607c478bd9Sstevel@tonic-gate 	uchar_t s0;
21617c478bd9Sstevel@tonic-gate 	int do_flushwq = 0;
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate 	/*
21647c478bd9Sstevel@tonic-gate 	 * The main reason for this routine is because, under some
21657c478bd9Sstevel@tonic-gate 	 * circumstances, a transmit interrupt may get lost (ie., if
21667c478bd9Sstevel@tonic-gate 	 * underrun occurs after the last character has been sent, and
21677c478bd9Sstevel@tonic-gate 	 * the tx interrupt following the abort gets scheduled before
21687c478bd9Sstevel@tonic-gate 	 * the current tx interrupt has been serviced).  Transmit can
21697c478bd9Sstevel@tonic-gate 	 * also get hung if the cable is pulled out and the clock was
21707c478bd9Sstevel@tonic-gate 	 * coming in from the modem.
21717c478bd9Sstevel@tonic-gate 	 */
21727c478bd9Sstevel@tonic-gate 
21737c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
21747c478bd9Sstevel@tonic-gate 	if (zss->sl_stream.str_rq)
21757c478bd9Sstevel@tonic-gate 		wq = WR(zss->sl_stream.str_rq);
21767c478bd9Sstevel@tonic-gate 	else {
21777c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
21787c478bd9Sstevel@tonic-gate 		return;		/* guard against close/callback race */
21797c478bd9Sstevel@tonic-gate 	}
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
21827c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_XMT_INPROG) && wq->q_first) {
21837c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_XMT_INPROG;
21847c478bd9Sstevel@tonic-gate 		if ((zss->sl_flags & SF_FDXPTP) ||
21857c478bd9Sstevel@tonic-gate 		    zsh_hdp_ok_or_rts_state(zs, zss))
21867c478bd9Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
21877c478bd9Sstevel@tonic-gate 		goto end_watchdog;
21887c478bd9Sstevel@tonic-gate 	}
21897c478bd9Sstevel@tonic-gate 
21907c478bd9Sstevel@tonic-gate 	if (zss->sl_wd_count-- > 0)
21917c478bd9Sstevel@tonic-gate 		goto end_watchdog;
21927c478bd9Sstevel@tonic-gate 
21937c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
21947c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
21957c478bd9Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
21967c478bd9Sstevel@tonic-gate 		else {
21977c478bd9Sstevel@tonic-gate 			s0 = SCC_READ0();
21987c478bd9Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
21997c478bd9Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
22007c478bd9Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
22017c478bd9Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
22027c478bd9Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
22037c478bd9Sstevel@tonic-gate 			}
22047c478bd9Sstevel@tonic-gate 		}
22057c478bd9Sstevel@tonic-gate 	}
22067c478bd9Sstevel@tonic-gate 
22077c478bd9Sstevel@tonic-gate 	switch (zss->sl_txstate) {
22087c478bd9Sstevel@tonic-gate 
22097c478bd9Sstevel@tonic-gate 	case TX_ABORTED:
22107c478bd9Sstevel@tonic-gate 		/*
22117c478bd9Sstevel@tonic-gate 		 * Transmitter was hung ... try restarting it.
22127c478bd9Sstevel@tonic-gate 		 */
22137c478bd9Sstevel@tonic-gate 		if (zss->sl_flags & SF_FDXPTP) {
22147c478bd9Sstevel@tonic-gate 			zss->sl_flags |= SF_XMT_INPROG;
22157c478bd9Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
22167c478bd9Sstevel@tonic-gate 		} else
22177c478bd9Sstevel@tonic-gate 			do_flushwq = 1;
22187c478bd9Sstevel@tonic-gate 		break;
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 	case TX_ACTIVE:
22217c478bd9Sstevel@tonic-gate 	case TX_CRC:
22227c478bd9Sstevel@tonic-gate 		/*
22237c478bd9Sstevel@tonic-gate 		 * Transmit is hung for some reason. Reset tx interrupt.
22247c478bd9Sstevel@tonic-gate 		 * Flush transmit fifo by sending an abort command
22257c478bd9Sstevel@tonic-gate 		 * which also sets the Underrun/EOM latch in WR0 and in
22267c478bd9Sstevel@tonic-gate 		 * turn generates an External Status interrupt that
22277c478bd9Sstevel@tonic-gate 		 * will reset the necessary message buffer pointers.
22287c478bd9Sstevel@tonic-gate 		 * The watchdog timer will cycle again to allow the SCC
22297c478bd9Sstevel@tonic-gate 		 * to settle down after the abort command.  The next
22307c478bd9Sstevel@tonic-gate 		 * time through we'll see that the state is now TX_ABORTED
22317c478bd9Sstevel@tonic-gate 		 * and call zsh_start to grab a new message.
22327c478bd9Sstevel@tonic-gate 		 */
22337c478bd9Sstevel@tonic-gate 		if (--zss->sl_wd_count <= 0) {
22347c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
22357c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_ERRORS);
22367c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
22377c478bd9Sstevel@tonic-gate 			zsh_txbad(zs, zss);
22387c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED; /* must be after txbad */
22397c478bd9Sstevel@tonic-gate 			warning = 1;
22407c478bd9Sstevel@tonic-gate 		}
22417c478bd9Sstevel@tonic-gate 		break;
22427c478bd9Sstevel@tonic-gate 
22437c478bd9Sstevel@tonic-gate 	case TX_RTS:
22447c478bd9Sstevel@tonic-gate 		/*
22457c478bd9Sstevel@tonic-gate 		 * Timer expired after we raised RTS.  CTS never came up.
22467c478bd9Sstevel@tonic-gate 		 */
22477c478bd9Sstevel@tonic-gate 		zss->sl_st.cts++;
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_TO);
22507c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
22517c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
22527c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
22537c478bd9Sstevel@tonic-gate 		break;
22547c478bd9Sstevel@tonic-gate 
22557c478bd9Sstevel@tonic-gate 	default:
22567c478bd9Sstevel@tonic-gate 		/*
22577c478bd9Sstevel@tonic-gate 		 * If we time out in an inactive state we set a soft
22587c478bd9Sstevel@tonic-gate 		 * interrupt.  This will call zsh_start which will
22597c478bd9Sstevel@tonic-gate 		 * clear SF_XMT_INPROG if the queue is empty.
22607c478bd9Sstevel@tonic-gate 		 */
22617c478bd9Sstevel@tonic-gate 		break;
22627c478bd9Sstevel@tonic-gate 	}
22637c478bd9Sstevel@tonic-gate end_watchdog:
22647c478bd9Sstevel@tonic-gate 	if (zss->sl_txstate != TX_OFF) {
22657c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22667c478bd9Sstevel@tonic-gate 		zss->sl_wd_id = timeout(zsh_watchdog, zs, SIO_WATCHDOG_TICK);
22677c478bd9Sstevel@tonic-gate 	} else {
22687c478bd9Sstevel@tonic-gate 		zss->sl_wd_id = 0;	/* safety */
22697c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22707c478bd9Sstevel@tonic-gate 	}
22717c478bd9Sstevel@tonic-gate 	if (warning || do_flushwq) {
22727c478bd9Sstevel@tonic-gate 		flushq(wq, FLUSHDATA);
22737c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
22747c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_xstandby) != NULL)
22757c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
22767c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22777c478bd9Sstevel@tonic-gate 		if (mp)
22787c478bd9Sstevel@tonic-gate 			freemsg(mp);
22797c478bd9Sstevel@tonic-gate 	}
22807c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
22817c478bd9Sstevel@tonic-gate 	if (warning)
22827c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "zsh%x: transmit hung", zs->zs_unit);
22837c478bd9Sstevel@tonic-gate }
22847c478bd9Sstevel@tonic-gate 
22857c478bd9Sstevel@tonic-gate static void
22867c478bd9Sstevel@tonic-gate zsh_callback(void *arg)
22877c478bd9Sstevel@tonic-gate {
22887c478bd9Sstevel@tonic-gate 	struct zscom *zs = arg;
22897c478bd9Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
22907c478bd9Sstevel@tonic-gate 	int tmp = ZSH_MAX_RSTANDBY;
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
22937c478bd9Sstevel@tonic-gate 	if (zss->sl_bufcid) {
22947c478bd9Sstevel@tonic-gate 		zss->sl_bufcid = 0;
22957c478bd9Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, tmp);
22967c478bd9Sstevel@tonic-gate 	}
22977c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
22987c478bd9Sstevel@tonic-gate }
2299