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