xref: /illumos-gate/usr/src/uts/sun/io/zs_hdlc.c (revision 65d1f074)
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.
2489b43686SBayard Bell  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
25730a650aSPeter Tribble  * Copyright (c) 2019 Peter Tribble.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  *	HDLC protocol handler for Z8530 SCC.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include	<sys/param.h>
347c478bd9Sstevel@tonic-gate #include	<sys/systm.h>
357c478bd9Sstevel@tonic-gate #include	<sys/types.h>
367c478bd9Sstevel@tonic-gate #include	<sys/sysmacros.h>
377c478bd9Sstevel@tonic-gate #include	<sys/kmem.h>
387c478bd9Sstevel@tonic-gate #include	<sys/stropts.h>
397c478bd9Sstevel@tonic-gate #include	<sys/stream.h>
407c478bd9Sstevel@tonic-gate #include	<sys/strsun.h>
417c478bd9Sstevel@tonic-gate #include	<sys/stat.h>
427c478bd9Sstevel@tonic-gate #include	<sys/cred.h>
437c478bd9Sstevel@tonic-gate #include	<sys/user.h>
447c478bd9Sstevel@tonic-gate #include	<sys/proc.h>
457c478bd9Sstevel@tonic-gate #include	<sys/file.h>
467c478bd9Sstevel@tonic-gate #include	<sys/uio.h>
477c478bd9Sstevel@tonic-gate #include	<sys/buf.h>
487c478bd9Sstevel@tonic-gate #include	<sys/mkdev.h>
497c478bd9Sstevel@tonic-gate #include	<sys/cmn_err.h>
507c478bd9Sstevel@tonic-gate #include	<sys/errno.h>
517c478bd9Sstevel@tonic-gate #include	<sys/fcntl.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include	<sys/zsdev.h>
547c478bd9Sstevel@tonic-gate #include	<sys/ser_sync.h>
557c478bd9Sstevel@tonic-gate #include	<sys/conf.h>
567c478bd9Sstevel@tonic-gate #include	<sys/ddi.h>
577c478bd9Sstevel@tonic-gate #include	<sys/sunddi.h>
587c478bd9Sstevel@tonic-gate #include	<sys/dlpi.h>
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate #define	ZSH_TRACING
617c478bd9Sstevel@tonic-gate #ifdef	ZSH_TRACING
627c478bd9Sstevel@tonic-gate #include	<sys/vtrace.h>
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * Temp tracepoint definitions
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate #define	TR_ZSH		50
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #define	TR_ZSH_TXINT	1
707c478bd9Sstevel@tonic-gate #define	TR_ZSH_XSINT	2
717c478bd9Sstevel@tonic-gate #define	TR_ZSH_RXINT	3
727c478bd9Sstevel@tonic-gate #define	TR_ZSH_SRINT	4
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate #define	TR_ZSH_WPUT_START		5
757c478bd9Sstevel@tonic-gate #define	TR_ZSH_WPUT_END			6
767c478bd9Sstevel@tonic-gate #define	TR_ZSH_START_START		7
777c478bd9Sstevel@tonic-gate #define	TR_ZSH_START_END		8
787c478bd9Sstevel@tonic-gate #define	TR_ZSH_SOFT_START		9
797c478bd9Sstevel@tonic-gate #define	TR_ZSH_SOFT_END			10
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #define	TR_ZSH_OPEN	 11
827c478bd9Sstevel@tonic-gate #define	TR_ZSH_CLOSE	12
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate #endif	/* ZSH_TRACING */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate /*
877c478bd9Sstevel@tonic-gate  * Logging definitions
887c478bd9Sstevel@tonic-gate  */
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate  * #define	ZSH_DEBUG
927c478bd9Sstevel@tonic-gate  */
937c478bd9Sstevel@tonic-gate #ifdef ZSH_DEBUG
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate #ifdef ZS_DEBUG_ALL
967c478bd9Sstevel@tonic-gate extern	char	zs_h_log[];
977c478bd9Sstevel@tonic-gate extern	int	zs_h_log_n;
987c478bd9Sstevel@tonic-gate #define	zsh_h_log_add(c) \
997c478bd9Sstevel@tonic-gate 	{ \
1007c478bd9Sstevel@tonic-gate 		if (zs_h_log_n >= ZS_H_LOG_MAX) \
1017c478bd9Sstevel@tonic-gate 			zs_h_log_n = 0; \
1027c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = 'A' + zs->zs_unit; \
1037c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = c; \
1047c478bd9Sstevel@tonic-gate 		zs_h_log[zs_h_log_n] = '\0'; \
1057c478bd9Sstevel@tonic-gate 	}
1067c478bd9Sstevel@tonic-gate #define	zsh_h_log_clear
1077c478bd9Sstevel@tonic-gate #else
1087c478bd9Sstevel@tonic-gate #define	ZSH_H_LOG_MAX   0x8000
1097c478bd9Sstevel@tonic-gate char zsh_h_log[2][ZSH_H_LOG_MAX +10];
1107c478bd9Sstevel@tonic-gate int zsh_h_log_n[2];
1117c478bd9Sstevel@tonic-gate #define	zsh_h_log_add(c) \
1127c478bd9Sstevel@tonic-gate 	{ \
1137c478bd9Sstevel@tonic-gate 		if (zsh_h_log_n[zs->zs_unit] >= ZSH_H_LOG_MAX) \
1147c478bd9Sstevel@tonic-gate 			zsh_h_log_n[zs->zs_unit] = 0; \
1157c478bd9Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]++] = c; \
1167c478bd9Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]] = '\0'; \
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate #define	zsh_h_log_clear \
12087e3123fSToomas Soome 	{ char *p; \
1217c478bd9Sstevel@tonic-gate 	for (p = &zsh_h_log[zs->zs_unit][ZSH_H_LOG_MAX]; \
1227c478bd9Sstevel@tonic-gate 		p >= &zsh_h_log[zs->zs_unit][0]; p--) \
1237c478bd9Sstevel@tonic-gate 		*p = '\0'; \
1247c478bd9Sstevel@tonic-gate 	zsh_h_log_n[zs->zs_unit] = 0; \
1257c478bd9Sstevel@tonic-gate 	}
1267c478bd9Sstevel@tonic-gate #endif
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate #define	ZSH_R0_LOG(r0)  { \
1297c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_RX_READY) zsh_h_log_add('R'); \
1307c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TIMER) zsh_h_log_add('Z'); \
1317c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TX_READY) zsh_h_log_add('T'); \
1327c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_CD) zsh_h_log_add('D'); \
1337c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_SYNC) zsh_h_log_add('S'); \
1347c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_CTS) zsh_h_log_add('C'); \
1357c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_TXUNDER) zsh_h_log_add('U'); \
1367c478bd9Sstevel@tonic-gate 	if (r0 & ZSRR0_BREAK) zsh_h_log_add('B'); \
1377c478bd9Sstevel@tonic-gate 	}
1387c478bd9Sstevel@tonic-gate #endif
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate #ifndef	MAXZSH
1417c478bd9Sstevel@tonic-gate #define	MAXZSH	2
1427c478bd9Sstevel@tonic-gate #define	MAXZSHCLONES	(80)	/* three clone opens per instance */
1437c478bd9Sstevel@tonic-gate #endif	/* MAXZSH */
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate int maxzsh = MAXZSH;
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate int zsh_timer_count = 10;
1487c478bd9Sstevel@tonic-gate int zsh_default_mru = 1024;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate struct ser_str *zsh_str = NULL;
1517c478bd9Sstevel@tonic-gate unsigned char zsh_usedminor[MAXZSHCLONES];
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate /*
1557c478bd9Sstevel@tonic-gate  * The HDLC protocol
1567c478bd9Sstevel@tonic-gate  */
1577c478bd9Sstevel@tonic-gate int zsh_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
1587c478bd9Sstevel@tonic-gate static int  zsh_probe(dev_info_t *dev);
1597c478bd9Sstevel@tonic-gate static int  zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
1607c478bd9Sstevel@tonic-gate static int  zsh_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
1617c478bd9Sstevel@tonic-gate static int  zsh_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
162730a650aSPeter Tribble static int  zsh_close(queue_t *rq, int flag, cred_t *cr);
163*65d1f074SToomas Soome static int zsh_wput(queue_t *wq, mblk_t *mp);
1647c478bd9Sstevel@tonic-gate static int zsh_start(struct zscom *zs, struct syncline *zss);
1657c478bd9Sstevel@tonic-gate static void zsh_ioctl(queue_t *wq, mblk_t *mp);
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate static struct module_info hdlc_minfo = {
1687c478bd9Sstevel@tonic-gate 	0x5a48,		/* module ID number: "ZH" */
1697c478bd9Sstevel@tonic-gate 	"zsh",		/* module name */
1707c478bd9Sstevel@tonic-gate 	0,		/* minimum packet size accepted */
1717c478bd9Sstevel@tonic-gate 	INFPSZ,		/* maximum packet size accepted */
17287e3123fSToomas Soome 	12 * 1024,	/* queue high water mark (bytes) */
17387e3123fSToomas Soome 	4 * 1024	/* queue low water mark (bytes) */
1747c478bd9Sstevel@tonic-gate };
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate static struct qinit hdlc_rinit = {
1777c478bd9Sstevel@tonic-gate 	putq,		/* input put procedure */
1787c478bd9Sstevel@tonic-gate 	NULL,		/* input service procedure */
1797c478bd9Sstevel@tonic-gate 	zsh_open,	/* open procedure */
1807c478bd9Sstevel@tonic-gate 	zsh_close,	/* close procedure */
1817c478bd9Sstevel@tonic-gate 	NULL,		/* reserved */
1827c478bd9Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
1837c478bd9Sstevel@tonic-gate 	NULL		/* reserved */
1847c478bd9Sstevel@tonic-gate };
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate static struct qinit hdlc_winit = {
187*65d1f074SToomas Soome 	zsh_wput,	/* output put procedure */
1887c478bd9Sstevel@tonic-gate 	NULL,		/* output service procedure */
1897c478bd9Sstevel@tonic-gate 	NULL,		/* open procedure */
1907c478bd9Sstevel@tonic-gate 	NULL,		/* close procedure */
1917c478bd9Sstevel@tonic-gate 	NULL,		/* reserved */
1927c478bd9Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
1937c478bd9Sstevel@tonic-gate 	NULL		/* reserved */
1947c478bd9Sstevel@tonic-gate };
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate struct streamtab hdlctab = {
1977c478bd9Sstevel@tonic-gate 	&hdlc_rinit,	/* initialize read queue */
1987c478bd9Sstevel@tonic-gate 	&hdlc_winit,	/* initialize write queue */
1997c478bd9Sstevel@tonic-gate 	NULL,		/* mux read qinit */
2007c478bd9Sstevel@tonic-gate 	NULL		/* mux write qinit */
2017c478bd9Sstevel@tonic-gate };
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(zsh_ops, nulldev, zsh_probe, zsh_attach,
20419397407SSherry Moore     zsh_detach, nodev, zsh_info, D_MP, &hdlctab, ddi_quiesce_not_supported);
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate  * This is the loadable module wrapper.
2087c478bd9Sstevel@tonic-gate  */
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate #include	<sys/errno.h>
2117c478bd9Sstevel@tonic-gate #include	<sys/modctl.h>
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate /*
2147c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
2157c478bd9Sstevel@tonic-gate  */
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2187c478bd9Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
21919397407SSherry Moore 	"Z8530 serial HDLC drv",
2207c478bd9Sstevel@tonic-gate 	&zsh_ops,	/* our own ops for this module */
2217c478bd9Sstevel@tonic-gate };
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2247c478bd9Sstevel@tonic-gate 	MODREV_1,
2257c478bd9Sstevel@tonic-gate 	(void *)&modldrv,
2267c478bd9Sstevel@tonic-gate 	NULL
2277c478bd9Sstevel@tonic-gate };
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate int
_init(void)2307c478bd9Sstevel@tonic-gate _init(void)
2317c478bd9Sstevel@tonic-gate {
2327c478bd9Sstevel@tonic-gate 	return (mod_install(&modlinkage));
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate int
_fini(void)2367c478bd9Sstevel@tonic-gate _fini(void)
2377c478bd9Sstevel@tonic-gate {
2387c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2427c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate /*
2497c478bd9Sstevel@tonic-gate  * The HDLC interrupt entry points.
2507c478bd9Sstevel@tonic-gate  */
2517c478bd9Sstevel@tonic-gate static void	zsh_txint(struct zscom *zs);
2527c478bd9Sstevel@tonic-gate static void	zsh_xsint(struct zscom *zs);
2537c478bd9Sstevel@tonic-gate static void	zsh_rxint(struct zscom *zs);
2547c478bd9Sstevel@tonic-gate static void	zsh_srint(struct zscom *zs);
2557c478bd9Sstevel@tonic-gate static int	zsh_softint(struct zscom *zs);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate struct zsops zsops_hdlc = {
2587c478bd9Sstevel@tonic-gate 	zsh_txint,
2597c478bd9Sstevel@tonic-gate 	zsh_xsint,
2607c478bd9Sstevel@tonic-gate 	zsh_rxint,
2617c478bd9Sstevel@tonic-gate 	zsh_srint,
2627c478bd9Sstevel@tonic-gate 	zsh_softint,
2637c478bd9Sstevel@tonic-gate 	NULL,
2647c478bd9Sstevel@tonic-gate 	NULL
2657c478bd9Sstevel@tonic-gate };
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate static int	zsh_program(struct zscom *zs, struct scc_mode *sm);
2687c478bd9Sstevel@tonic-gate static void	zsh_setmstat(struct zscom *zs, int event);
2697c478bd9Sstevel@tonic-gate static void	zsh_rxbad(struct zscom *zs, struct syncline *zss);
2707c478bd9Sstevel@tonic-gate static void	zsh_txbad(struct zscom *zs, struct syncline *zss);
2717c478bd9Sstevel@tonic-gate static void	zsh_watchdog(void *);
2727c478bd9Sstevel@tonic-gate static void	zsh_callback(void *);
2737c478bd9Sstevel@tonic-gate static int	zsh_hdp_ok_or_rts_state(struct zscom *zs, struct syncline *zss);
2747c478bd9Sstevel@tonic-gate static void	zsh_init_port(struct zscom *zs, struct syncline *zss);
2757c478bd9Sstevel@tonic-gate static int	zsh_setmode(struct zscom *zs, struct syncline *zss,
2767c478bd9Sstevel@tonic-gate 			struct scc_mode *sm);
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate /*
2807c478bd9Sstevel@tonic-gate  * The HDLC Driver.
2817c478bd9Sstevel@tonic-gate  */
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate /*
2857c478bd9Sstevel@tonic-gate  * Special macros to handle STREAMS operations.
2867c478bd9Sstevel@tonic-gate  * These are required to address memory leakage problems.
2877c478bd9Sstevel@tonic-gate  * WARNING : the macro do NOT call ZSSETSOFT
2887c478bd9Sstevel@tonic-gate  */
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate /*
2917c478bd9Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
2927c478bd9Sstevel@tonic-gate  */
2937c478bd9Sstevel@tonic-gate #define	ZSH_GETBLOCK(zs, allocbcount) \
2947c478bd9Sstevel@tonic-gate { \
29587e3123fSToomas Soome 	int n = ZSH_MAX_RSTANDBY; \
2967c478bd9Sstevel@tonic-gate 	while (--n >= 0) { \
29787e3123fSToomas Soome 	    if (zss->sl_rstandby[n] == NULL) { \
2987c478bd9Sstevel@tonic-gate 		if ((zss->sl_rstandby[n] = \
2997c478bd9Sstevel@tonic-gate 		    allocb(zss->sl_mru, BPRI_MED)) == NULL) { \
3007c478bd9Sstevel@tonic-gate 		    if (zss->sl_bufcid == 0) { \
3017c478bd9Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi); \
3027c478bd9Sstevel@tonic-gate 			if (zss->sl_txstate != TX_OFF) { \
3037c478bd9Sstevel@tonic-gate 			    mutex_exit(zs->zs_excl_hi); \
3047c478bd9Sstevel@tonic-gate 			    zss->sl_bufcid = bufcall(zss->sl_mru, \
3057c478bd9Sstevel@tonic-gate 				    BPRI_MED, zsh_callback, zs); \
3067c478bd9Sstevel@tonic-gate 			    break; \
3077c478bd9Sstevel@tonic-gate 			} else \
3087c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi); \
3097c478bd9Sstevel@tonic-gate 		    } \
3107c478bd9Sstevel@tonic-gate 		} \
3117c478bd9Sstevel@tonic-gate 		allocbcount--; \
3127c478bd9Sstevel@tonic-gate 	    } \
3137c478bd9Sstevel@tonic-gate 	} \
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate /*
3177c478bd9Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
3187c478bd9Sstevel@tonic-gate  */
3197c478bd9Sstevel@tonic-gate #define	ZSH_ALLOCB(mp) \
3207c478bd9Sstevel@tonic-gate { \
32187e3123fSToomas Soome 	int n = ZSH_MAX_RSTANDBY; \
3227c478bd9Sstevel@tonic-gate 	mp = NULL; \
3237c478bd9Sstevel@tonic-gate 	while (--n >= 0)  { \
3247c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_rstandby[n]) != NULL) { \
3257c478bd9Sstevel@tonic-gate 			zss->sl_rstandby[n] = NULL; \
3267c478bd9Sstevel@tonic-gate 			break; \
3277c478bd9Sstevel@tonic-gate 		} \
3287c478bd9Sstevel@tonic-gate 	} \
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate #define	ZSH_PUTQ(mp) \
3327c478bd9Sstevel@tonic-gate { \
33387e3123fSToomas Soome 	int wptr, rptr;  \
3347c478bd9Sstevel@tonic-gate 	wptr = zss->sl_rdone_wptr; \
3357c478bd9Sstevel@tonic-gate 	rptr = zss->sl_rdone_rptr; \
3367c478bd9Sstevel@tonic-gate 	zss->sl_rdone[wptr] = mp; \
3377c478bd9Sstevel@tonic-gate 	if ((wptr) + 1 == ZSH_RDONE_MAX) \
3387c478bd9Sstevel@tonic-gate 		zss->sl_rdone_wptr = wptr = 0; \
3397c478bd9Sstevel@tonic-gate 	else \
3407c478bd9Sstevel@tonic-gate 		zss->sl_rdone_wptr = ++wptr; \
3417c478bd9Sstevel@tonic-gate 	if (wptr == rptr) {  /* Should never occur */ \
3427c478bd9Sstevel@tonic-gate 		SCC_BIC(1, ZSWR1_INIT); \
3437c478bd9Sstevel@tonic-gate 		zss->sl_m_error = ENOSR; \
3447c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs); \
3457c478bd9Sstevel@tonic-gate 	} \
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate #define	ZSH_FREEMSG(mp) \
3497c478bd9Sstevel@tonic-gate { \
3507c478bd9Sstevel@tonic-gate 	ZSH_PUTQ(mp); \
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate /*
3557c478bd9Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
3567c478bd9Sstevel@tonic-gate  */
3577c478bd9Sstevel@tonic-gate #define	ZSH_GETQ(mp) \
3587c478bd9Sstevel@tonic-gate { \
3597c478bd9Sstevel@tonic-gate 	if (zss->sl_rdone_rptr != zss->sl_rdone_wptr) { \
3607c478bd9Sstevel@tonic-gate 		mp = zss->sl_rdone[zss->sl_rdone_rptr++]; \
3617c478bd9Sstevel@tonic-gate 		if (zss->sl_rdone_rptr == ZSH_RDONE_MAX) \
3627c478bd9Sstevel@tonic-gate 				zss->sl_rdone_rptr = 0; \
3637c478bd9Sstevel@tonic-gate 	} else \
3647c478bd9Sstevel@tonic-gate 		mp = NULL; \
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate #define	ZSH_FLUSHQ \
3687c478bd9Sstevel@tonic-gate { \
36987e3123fSToomas Soome 	mblk_t *tmp; \
3707c478bd9Sstevel@tonic-gate 	for (;;) { \
3717c478bd9Sstevel@tonic-gate 		ZSH_GETQ(tmp); \
37287e3123fSToomas Soome 		if (tmp == NULL) \
3737c478bd9Sstevel@tonic-gate 			break; \
3747c478bd9Sstevel@tonic-gate 		freemsg(tmp); \
3757c478bd9Sstevel@tonic-gate 	} \
3767c478bd9Sstevel@tonic-gate }
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3797c478bd9Sstevel@tonic-gate static int
zsh_probe(dev_info_t * dev)3807c478bd9Sstevel@tonic-gate zsh_probe(dev_info_t *dev)
3817c478bd9Sstevel@tonic-gate {
3827c478bd9Sstevel@tonic-gate 	return (DDI_PROBE_DONTCARE);
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3867c478bd9Sstevel@tonic-gate static int
zsh_attach(dev_info_t * dev,ddi_attach_cmd_t cmd)3877c478bd9Sstevel@tonic-gate zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd)
3887c478bd9Sstevel@tonic-gate {
38987e3123fSToomas Soome 	int	unit;
39087e3123fSToomas Soome 	char	name[3] = { '\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,
40887e3123fSToomas Soome 	    0, 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 
41487e3123fSToomas Soome 	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,
41987e3123fSToomas Soome 		    2 * unit, DDI_PSEUDO, 0) == 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,
42687e3123fSToomas Soome 		    2 * (unit - 1) + 1, DDI_PSEUDO, 0) == 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,
43887e3123fSToomas Soome     void **result)
4397c478bd9Sstevel@tonic-gate {
44087e3123fSToomas Soome 	dev_t dev = (dev_t)arg;
44187e3123fSToomas Soome 	int unit, error;
44287e3123fSToomas Soome 	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 {
48187e3123fSToomas Soome 	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;
50287e3123fSToomas Soome 			if (zss->sl_wd_id == NULL)
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 {
52287e3123fSToomas Soome 	struct zscom *zs;
52387e3123fSToomas Soome 	struct syncline *zss;
52487e3123fSToomas Soome 	struct ser_str *stp;
52587e3123fSToomas Soome 	int	unit;
52687e3123fSToomas Soome 	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;
56187e3123fSToomas Soome 		stp->str_state = 0;
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) {
58619397407SSherry Moore 			cmn_err(CE_WARN, "zsh_open: can't alloc message block");
58719397407SSherry Moore 			mutex_exit(zs->zs_excl);
58819397407SSherry Moore 			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++)
60287e3123fSToomas Soome 			if (zsh_usedminor[unit] == '\0') {
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,cred_t * cr __unused)636730a650aSPeter Tribble zsh_close(queue_t *rq, int flag, cred_t *cr __unused)
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 {
80687e3123fSToomas Soome 	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  */
826*65d1f074SToomas Soome static int
zsh_wput(queue_t * wq,mblk_t * mp)8277c478bd9Sstevel@tonic-gate zsh_wput(queue_t *wq, mblk_t *mp)
8287c478bd9Sstevel@tonic-gate {
82987e3123fSToomas Soome 	struct ser_str *stp = (struct ser_str *)wq->q_ptr;
83087e3123fSToomas Soome 	struct zscom *zs;
83187e3123fSToomas Soome 	struct syncline *zss = NULL;
83287e3123fSToomas Soome 	ulong_t prim, error = 0;
83387e3123fSToomas Soome 	union DL_primitives *dlp;
83487e3123fSToomas Soome 	int	ppa;
83587e3123fSToomas Soome 	mblk_t *tmp;
83687e3123fSToomas Soome 	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);
843*65d1f074SToomas Soome 		return (0);
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) &&
85019397407SSherry Moore 	    (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);
859*65d1f074SToomas Soome 		return (0);
8607c478bd9Sstevel@tonic-gate 	}
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	if ((zs == NULL) && (mp->b_datap->db_type != M_PROTO)) {
86319397407SSherry Moore 		freemsg(mp);
86419397407SSherry Moore 		cmn_err(CE_WARN,
86519397407SSherry Moore 		    "zsh: clone device %d must be attached before use!",
86619397407SSherry Moore 		    stp->str_inst);
86719397407SSherry Moore 		(void) putnextctl1(RD(wq), M_ERROR, EPROTO);
868*65d1f074SToomas Soome 		return (0);
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);
880*65d1f074SToomas Soome 			return (0);
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);
901*65d1f074SToomas Soome 			return (0);
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) {
90987e3123fSToomas Soome 			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)
914*65d1f074SToomas Soome 				return (0);
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 
927*65d1f074SToomas Soome 			return (0);
9287c478bd9Sstevel@tonic-gate 		}
9297c478bd9Sstevel@tonic-gate 		tmp = NULL;
9307c478bd9Sstevel@tonic-gate again:
93187e3123fSToomas Soome 		if (zss->sl_xstandby == NULL) {
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 
954*65d1f074SToomas Soome 			return (0);
9557c478bd9Sstevel@tonic-gate 		}
9567c478bd9Sstevel@tonic-gate 
95787e3123fSToomas Soome 		if (zss->sl_wd_id == NULL) {
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);
985*65d1f074SToomas Soome 			return (0);
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,
106019397407SSherry Moore 			    (struct scc_mode *)mp->b_cont->b_rptr);
10617c478bd9Sstevel@tonic-gate 			if (error) {
106287e3123fSToomas Soome 				struct iocblk  *iocp =
106319397407SSherry Moore 				    (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);
1125*65d1f074SToomas Soome 	return (0);
11267c478bd9Sstevel@tonic-gate }
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate /*
11297c478bd9Sstevel@tonic-gate  * Get the next message from the write queue, set up the necessary pointers,
11307c478bd9Sstevel@tonic-gate  * state info, etc., and start the transmit "engine" by sending the first
11317c478bd9Sstevel@tonic-gate  * character.  We'll then rotate through txint until done, then get an xsint.
11327c478bd9Sstevel@tonic-gate  */
11337c478bd9Sstevel@tonic-gate static int
zsh_start(struct zscom * zs,struct syncline * zss)11347c478bd9Sstevel@tonic-gate zsh_start(struct zscom *zs, struct syncline *zss)
11357c478bd9Sstevel@tonic-gate {
113687e3123fSToomas Soome 	mblk_t *mp;
113787e3123fSToomas Soome 	uchar_t *wptr;
113887e3123fSToomas Soome 	uchar_t *rptr;
113987e3123fSToomas Soome 	uchar_t sl_flags = zss->sl_flags;
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	/*
11427c478bd9Sstevel@tonic-gate 	 * Attempt to grab the next M_DATA message off the queue (that's
11437c478bd9Sstevel@tonic-gate 	 * all that will be left after wput) and begin transmission.
11447c478bd9Sstevel@tonic-gate 	 * This routine is normally called after completion of a previous
11457c478bd9Sstevel@tonic-gate 	 * frame, or when zsh_wput gets a new message.  If we are in a
11467c478bd9Sstevel@tonic-gate 	 * mode that put us in the TX_RTS state, waiting for CTS, and CTS
11477c478bd9Sstevel@tonic-gate 	 * is not up yet, we have no business here.  Ditto if we're in
11487c478bd9Sstevel@tonic-gate 	 * either the TX_ACTIVE or TX_CRC states.  In these cases we
11497c478bd9Sstevel@tonic-gate 	 * don't clear SF_CALLSTART, so we don't forget there's work to do.
11507c478bd9Sstevel@tonic-gate 	 */
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_START,
11537c478bd9Sstevel@tonic-gate 	    "zsh_start start: zs = %p", zs);
11547c478bd9Sstevel@tonic-gate 
11557c478bd9Sstevel@tonic-gate 	if (sl_flags & SF_PHONY) {
11567c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_PHONY;
11577c478bd9Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
11587c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
11597c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
11607c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
11617c478bd9Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
11627c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
11637c478bd9Sstevel@tonic-gate 		/*
11647c478bd9Sstevel@tonic-gate 		 * if we get another msg by chance zsh_watchog will start
11657c478bd9Sstevel@tonic-gate 		 */
11667c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
11677c478bd9Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
11707c478bd9Sstevel@tonic-gate 		    "zsh_start end: zs = %d", zs);
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate 		return (0);
11737c478bd9Sstevel@tonic-gate 	}
11747c478bd9Sstevel@tonic-gate 	mp = zss->sl_xstandby;
11757c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
11767c478bd9Sstevel@tonic-gate 		if (!(sl_flags & SF_FDXPTP)) {
11777c478bd9Sstevel@tonic-gate 			sl_flags |= SF_PHONY;
11787c478bd9Sstevel@tonic-gate 			ZSH_ALLOCB(mp);
117987e3123fSToomas Soome 			if (mp == NULL)
11807c478bd9Sstevel@tonic-gate 				return (0);
11817c478bd9Sstevel@tonic-gate 			mp->b_datap->db_type = M_RSE;
11827c478bd9Sstevel@tonic-gate 			mp->b_wptr = mp->b_rptr + 1;
11837c478bd9Sstevel@tonic-gate 			goto transmit;
11847c478bd9Sstevel@tonic-gate 		}
11857c478bd9Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
11867c478bd9Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
11897c478bd9Sstevel@tonic-gate 		    "zsh_start end: zs = %p", zs);
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate 		return (0);
11927c478bd9Sstevel@tonic-gate 	}
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate transmit:
11957c478bd9Sstevel@tonic-gate 	zss->sl_xstandby = NULL;
11967c478bd9Sstevel@tonic-gate 	rptr = mp->b_rptr;
11977c478bd9Sstevel@tonic-gate 	wptr = mp->b_wptr;
11987c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs);
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate #ifdef ZSH_DEBUG
12017c478bd9Sstevel@tonic-gate 	if (zss->sl_xhead || zss->sl_xactb) {
12027c478bd9Sstevel@tonic-gate 		debug_enter("xhead1");
12037c478bd9Sstevel@tonic-gate 	}
12047c478bd9Sstevel@tonic-gate #endif
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	zss->sl_xhead = mp;
12077c478bd9Sstevel@tonic-gate 	zss->sl_xactb = mp;
12087c478bd9Sstevel@tonic-gate 	zss->sl_wd_count = zsh_timer_count;
12097c478bd9Sstevel@tonic-gate 	zss->sl_txstate = TX_ACTIVE;
12107c478bd9Sstevel@tonic-gate 	zss->sl_ocnt = 0;
12117c478bd9Sstevel@tonic-gate 	SCC_BIS(10, ZSWR10_UNDERRUN_ABORT);	/* abort on underrun */
12127c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_TXCRC);		/* reset transmit CRC */
12137c478bd9Sstevel@tonic-gate 	zss->sl_ocnt = wptr - rptr;
12147c478bd9Sstevel@tonic-gate 	mp->b_wptr = rptr; /* to tell soft to free this msg */
12157c478bd9Sstevel@tonic-gate 	SCC_WRITEDATA(*rptr++);    /* resets TXINT */
12167c478bd9Sstevel@tonic-gate 	zs->zs_wr_cur = rptr;
12177c478bd9Sstevel@tonic-gate 	zs->zs_wr_lim = wptr;
12187c478bd9Sstevel@tonic-gate 
12197c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_EOM);
12207c478bd9Sstevel@tonic-gate 
12217c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_END,
12227c478bd9Sstevel@tonic-gate 	    "zsh_start end: zs = %p", zs);
12237c478bd9Sstevel@tonic-gate 
12247c478bd9Sstevel@tonic-gate 	zss->sl_flags = sl_flags;
12257c478bd9Sstevel@tonic-gate 	return (1);
12267c478bd9Sstevel@tonic-gate }
12277c478bd9Sstevel@tonic-gate 
12287c478bd9Sstevel@tonic-gate 
12297c478bd9Sstevel@tonic-gate /*
12307c478bd9Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
12317c478bd9Sstevel@tonic-gate  */
12327c478bd9Sstevel@tonic-gate static void
zsh_ioctl(queue_t * wq,mblk_t * mp)12337c478bd9Sstevel@tonic-gate zsh_ioctl(queue_t *wq, mblk_t *mp)
12347c478bd9Sstevel@tonic-gate {
123587e3123fSToomas Soome 	struct ser_str *stp = (struct ser_str *)wq->q_ptr;
123687e3123fSToomas Soome 	struct zscom *zs = (struct zscom *)stp->str_com;
123787e3123fSToomas Soome 	struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
123887e3123fSToomas Soome 	struct iocblk   *iocp = (struct iocblk *)mp->b_rptr;
123987e3123fSToomas Soome 	struct scc_mode *sm;
124087e3123fSToomas Soome 	struct sl_stats *st;
124187e3123fSToomas Soome 	uchar_t	 *msignals;
124287e3123fSToomas Soome 	mblk_t		 *tmp;
124387e3123fSToomas Soome 	int		 error = 0;
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
12467c478bd9Sstevel@tonic-gate 	if ((zs->zs_ops != &zsops_null) &&
12477c478bd9Sstevel@tonic-gate 	    (zs->zs_ops != &zsops_hdlc)) {
12487c478bd9Sstevel@tonic-gate 		/*
12497c478bd9Sstevel@tonic-gate 		 * another protocol got here first
12507c478bd9Sstevel@tonic-gate 		 */
12517c478bd9Sstevel@tonic-gate 		error = (EBUSY);
12527c478bd9Sstevel@tonic-gate 		goto end_zsh_ioctl;
12537c478bd9Sstevel@tonic-gate 	}
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 
12567c478bd9Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	case S_IOCGETMODE:
12597c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (struct scc_mode), BPRI_MED);
12607c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12617c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12627c478bd9Sstevel@tonic-gate 			break;
12637c478bd9Sstevel@tonic-gate 		}
12647c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12657c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct scc_mode), 0);
12667c478bd9Sstevel@tonic-gate 		else
12677c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct scc_mode), NULL, tmp);
12687c478bd9Sstevel@tonic-gate 		sm = (struct scc_mode *)mp->b_cont->b_rptr;
12697c478bd9Sstevel@tonic-gate 		bcopy(&zss->sl_mode, sm, sizeof (struct scc_mode));
12707c478bd9Sstevel@tonic-gate 		break;
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	case S_IOCGETSTATS:
12737c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (struct sl_stats), BPRI_MED);
12747c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12757c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12767c478bd9Sstevel@tonic-gate 			break;
12777c478bd9Sstevel@tonic-gate 		}
12787c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12797c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct sl_stats), 0);
12807c478bd9Sstevel@tonic-gate 		else
12817c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct sl_stats), NULL, tmp);
12827c478bd9Sstevel@tonic-gate 		st = (struct sl_stats *)mp->b_cont->b_rptr;
12837c478bd9Sstevel@tonic-gate 		bcopy(&zss->sl_st, st, sizeof (struct sl_stats));
12847c478bd9Sstevel@tonic-gate 		break;
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	case S_IOCGETSPEED:
12877c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
12887c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
12897c478bd9Sstevel@tonic-gate 			error = EAGAIN;
12907c478bd9Sstevel@tonic-gate 			break;
12917c478bd9Sstevel@tonic-gate 		}
12927c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
12937c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
12947c478bd9Sstevel@tonic-gate 		else
12957c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
12967c478bd9Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mode.sm_baudrate;
12977c478bd9Sstevel@tonic-gate 		break;
12987c478bd9Sstevel@tonic-gate 
12997c478bd9Sstevel@tonic-gate 	case S_IOCGETMCTL:
13007c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (char), BPRI_MED);
13017c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
13027c478bd9Sstevel@tonic-gate 			error = EAGAIN;
13037c478bd9Sstevel@tonic-gate 			break;
13047c478bd9Sstevel@tonic-gate 		}
13057c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
13067c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (char), 0);
13077c478bd9Sstevel@tonic-gate 		else
13087c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (char), NULL, tmp);
13097c478bd9Sstevel@tonic-gate 		msignals = (uchar_t *)mp->b_cont->b_rptr;
13107c478bd9Sstevel@tonic-gate 		*msignals = zss->sl_rr0 & (ZSRR0_CD | ZSRR0_CTS);
13117c478bd9Sstevel@tonic-gate 		break;
13127c478bd9Sstevel@tonic-gate 
13137c478bd9Sstevel@tonic-gate 	case S_IOCGETMRU:
13147c478bd9Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
13157c478bd9Sstevel@tonic-gate 		if (tmp == NULL) {
13167c478bd9Sstevel@tonic-gate 			error = EAGAIN;
13177c478bd9Sstevel@tonic-gate 			break;
13187c478bd9Sstevel@tonic-gate 		}
13197c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
13207c478bd9Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
13217c478bd9Sstevel@tonic-gate 		else
13227c478bd9Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
13237c478bd9Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mru;
13247c478bd9Sstevel@tonic-gate 		break;
13257c478bd9Sstevel@tonic-gate 
13267c478bd9Sstevel@tonic-gate 	case S_IOCSETMODE:
13277c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
13287c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct scc_mode));
13297c478bd9Sstevel@tonic-gate 			if (error != 0)
13307c478bd9Sstevel@tonic-gate 				break;
13317c478bd9Sstevel@tonic-gate 			error = zsh_setmode(zs, zss,
13327c478bd9Sstevel@tonic-gate 			    (struct scc_mode *)mp->b_cont->b_rptr);
13337c478bd9Sstevel@tonic-gate 			if (error == 0)
13347c478bd9Sstevel@tonic-gate 				mioc2ack(mp, NULL, 0, 0);
13357c478bd9Sstevel@tonic-gate 		} else
13367c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (struct scc_mode), NULL);
13377c478bd9Sstevel@tonic-gate 		break;
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 	case S_IOCCLRSTATS:
13407c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
13417c478bd9Sstevel@tonic-gate 		bzero(&zss->sl_st, sizeof (struct sl_stats));
13427c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
13437c478bd9Sstevel@tonic-gate 		mioc2ack(mp, NULL, 0, 0);
13447c478bd9Sstevel@tonic-gate 		break;
13457c478bd9Sstevel@tonic-gate 
13467c478bd9Sstevel@tonic-gate 	case S_IOCSETMRU:
13477c478bd9Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
13487c478bd9Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
13497c478bd9Sstevel@tonic-gate 			if (error != 0)
13507c478bd9Sstevel@tonic-gate 				break;
13517c478bd9Sstevel@tonic-gate 			zss->sl_mru = *(int *)mp->b_cont->b_rptr;
13527c478bd9Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
13537c478bd9Sstevel@tonic-gate 		} else
13547c478bd9Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (int), NULL);
13557c478bd9Sstevel@tonic-gate 		break;
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate 	case S_IOCSETDTR:
13587c478bd9Sstevel@tonic-gate 		/*
13597c478bd9Sstevel@tonic-gate 		 * The first integer of the M_DATA block that should
13607c478bd9Sstevel@tonic-gate 		 * follow indicate if DTR must be set or reset
13617c478bd9Sstevel@tonic-gate 		 */
13627c478bd9Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (int));
13637c478bd9Sstevel@tonic-gate 		if (error != 0)
13647c478bd9Sstevel@tonic-gate 			break;
13657c478bd9Sstevel@tonic-gate 
13667c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
13677c478bd9Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr != 0)
13687c478bd9Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIS);
13697c478bd9Sstevel@tonic-gate 		else
13707c478bd9Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIC);
13717c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
13727c478bd9Sstevel@tonic-gate 		break;
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate 	default:
13757c478bd9Sstevel@tonic-gate 		error = EINVAL;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 	}
13787c478bd9Sstevel@tonic-gate end_zsh_ioctl:
13797c478bd9Sstevel@tonic-gate 	iocp->ioc_error = error;
13807c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = (error) ? M_IOCNAK : M_IOCACK;
13817c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
13827c478bd9Sstevel@tonic-gate 	qreply(wq, mp);
13837c478bd9Sstevel@tonic-gate }
13847c478bd9Sstevel@tonic-gate 
13857c478bd9Sstevel@tonic-gate /*
13867c478bd9Sstevel@tonic-gate  * Set the mode of the zsh port
13877c478bd9Sstevel@tonic-gate  */
13887c478bd9Sstevel@tonic-gate 
13897c478bd9Sstevel@tonic-gate int
zsh_setmode(struct zscom * zs,struct syncline * zss,struct scc_mode * sm)13907c478bd9Sstevel@tonic-gate zsh_setmode(struct zscom *zs, struct syncline *zss, struct scc_mode *sm)
13917c478bd9Sstevel@tonic-gate {
139287e3123fSToomas Soome 	int error = 0;
139387e3123fSToomas Soome 	mblk_t *mp;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
13967c478bd9Sstevel@tonic-gate 	if (sm->sm_rxclock == RXC_IS_PLL) {
13977c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
13987c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
13997c478bd9Sstevel@tonic-gate 		return (EINVAL);		/* not supported */
14007c478bd9Sstevel@tonic-gate 	} else {
14017c478bd9Sstevel@tonic-gate 		if (((zss->sl_mode.sm_config ^ sm->sm_config) &
140219397407SSherry Moore 		    CONN_SIGNAL) != 0) { /* Changing, going... */
14037c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_SIGNAL) { /* ...up. */
14047c478bd9Sstevel@tonic-gate 				if (zss->sl_mstat == NULL) {
140519397407SSherry Moore 					mutex_exit(zs->zs_excl_hi);
140619397407SSherry Moore 					mp = allocb(
140719397407SSherry Moore 					    sizeof (struct sl_status),
140819397407SSherry Moore 					    BPRI_MED);
140919397407SSherry Moore 					mutex_enter(zs->zs_excl_hi);
141019397407SSherry Moore 					zss->sl_mstat = mp;
14117c478bd9Sstevel@tonic-gate 				}
14127c478bd9Sstevel@tonic-gate 			} else {			/* ...down. */
14137c478bd9Sstevel@tonic-gate 				if ((mp = zss->sl_mstat) != NULL)
14147c478bd9Sstevel@tonic-gate 					zss->sl_mstat = NULL;
14157c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14167c478bd9Sstevel@tonic-gate 				if (mp)
14177c478bd9Sstevel@tonic-gate 					freemsg(mp);
14187c478bd9Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
14197c478bd9Sstevel@tonic-gate 			}
14207c478bd9Sstevel@tonic-gate 		}
14217c478bd9Sstevel@tonic-gate 		if (!(sm->sm_config & CONN_IBM)) {
14227c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_HDX) {
14237c478bd9Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_HDX;
14247c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14257c478bd9Sstevel@tonic-gate 				return (EINVAL);
14267c478bd9Sstevel@tonic-gate 			}
14277c478bd9Sstevel@tonic-gate 			if (sm->sm_config & CONN_MPT) {
14287c478bd9Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_MPT;
14297c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
14307c478bd9Sstevel@tonic-gate 				return (EINVAL);
14317c478bd9Sstevel@tonic-gate 			}
14327c478bd9Sstevel@tonic-gate 		}
14337c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FDXPTP;		/* "conmode" */
14347c478bd9Sstevel@tonic-gate 		if ((sm->sm_config & (CONN_HDX | CONN_MPT)) == 0)
14357c478bd9Sstevel@tonic-gate 			zss->sl_flags |= SF_FDXPTP;
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 		error = zsh_program(zs, sm);
14387c478bd9Sstevel@tonic-gate 		if (!error && (zs->zs_ops != &zsops_null))
14397c478bd9Sstevel@tonic-gate 			zsh_init_port(zs, zss);
14407c478bd9Sstevel@tonic-gate 	}
14417c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate 	return (error);
14447c478bd9Sstevel@tonic-gate }
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate /*
14477c478bd9Sstevel@tonic-gate  * Transmit interrupt service procedure
14487c478bd9Sstevel@tonic-gate  */
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate static void
zsh_txint(struct zscom * zs)14517c478bd9Sstevel@tonic-gate zsh_txint(struct zscom *zs)
14527c478bd9Sstevel@tonic-gate {
145387e3123fSToomas Soome 	struct syncline *zss;
145487e3123fSToomas Soome 	mblk_t *mp;
145587e3123fSToomas Soome 	int tmp;
145687e3123fSToomas Soome 	uchar_t *wr_cur;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_TXINT, "zsh_txint: zs = %p", zs);
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	if ((wr_cur =  zs->zs_wr_cur) != NULL && (wr_cur <  zs->zs_wr_lim)) {
14617c478bd9Sstevel@tonic-gate 		SCC_WRITEDATA(*wr_cur++);
14627c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = wr_cur;
14637c478bd9Sstevel@tonic-gate 		return;
14647c478bd9Sstevel@tonic-gate 	}
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 	zss = (struct syncline *)&zs->zs_priv_str;
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	switch (zss->sl_txstate) {
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	/*
14727c478bd9Sstevel@tonic-gate 	 * we here because end of message block lim = cur
14737c478bd9Sstevel@tonic-gate 	 */
14747c478bd9Sstevel@tonic-gate 	case TX_ACTIVE:
14757c478bd9Sstevel@tonic-gate 
14767c478bd9Sstevel@tonic-gate 		mp = zss->sl_xactb;
14777c478bd9Sstevel@tonic-gate 
14787c478bd9Sstevel@tonic-gate again_txint:
14797c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
14807c478bd9Sstevel@tonic-gate 		if (mp) {
14817c478bd9Sstevel@tonic-gate 			zss->sl_xactb = mp;
14827c478bd9Sstevel@tonic-gate 			zss->sl_ocnt += tmp = mp->b_wptr - mp->b_rptr;
148387e3123fSToomas Soome 			if (tmp == 0)
14847c478bd9Sstevel@tonic-gate 				goto again_txint;
14857c478bd9Sstevel@tonic-gate 			zs->zs_wr_cur = mp->b_rptr;
14867c478bd9Sstevel@tonic-gate 			zs->zs_wr_lim = mp->b_wptr;
14877c478bd9Sstevel@tonic-gate 			SCC_WRITEDATA(*zs->zs_wr_cur++);
14887c478bd9Sstevel@tonic-gate 			return;
14897c478bd9Sstevel@tonic-gate 		}
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 		/*
14927c478bd9Sstevel@tonic-gate 		 * This is where the fun starts.  At this point the
14937c478bd9Sstevel@tonic-gate 		 * last character in the frame has been sent.  We
14947c478bd9Sstevel@tonic-gate 		 * issue a RESET_TXINT so we won't get another txint
14957c478bd9Sstevel@tonic-gate 		 * until the CRC has been completely sent.  Also we
14967c478bd9Sstevel@tonic-gate 		 * reset the Abort-On-Underrun bit so that CRC is
14977c478bd9Sstevel@tonic-gate 		 * sent at EOM, rather than an Abort.
14987c478bd9Sstevel@tonic-gate 		 */
14997c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = zs->zs_wr_lim = NULL;
15007c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_CRC;
15017c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15027c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_PHONY)) {
15037c478bd9Sstevel@tonic-gate 			SCC_BIC(10, ZSWR10_UNDERRUN_ABORT);
15047c478bd9Sstevel@tonic-gate 			zss->sl_st.opack++;
15057c478bd9Sstevel@tonic-gate 			zss->sl_st.ochar += zss->sl_ocnt;
15067c478bd9Sstevel@tonic-gate 		}
15077c478bd9Sstevel@tonic-gate 		zss->sl_ocnt = 0;
15087c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
15097c478bd9Sstevel@tonic-gate 		zss->sl_xhead = zss->sl_xactb = NULL;
15107c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
15117c478bd9Sstevel@tonic-gate 		break;
15127c478bd9Sstevel@tonic-gate 	/*
15137c478bd9Sstevel@tonic-gate 	 * This txint means we have sent the CRC bytes at EOF.
15147c478bd9Sstevel@tonic-gate 	 * The next txint will mean we are sending or have sent the
15157c478bd9Sstevel@tonic-gate 	 * flag character at EOF, but we handle that differently, and
15167c478bd9Sstevel@tonic-gate 	 * enter different states,depending on whether we're IBM or not.
15177c478bd9Sstevel@tonic-gate 	 */
15187c478bd9Sstevel@tonic-gate 	case TX_CRC:
15197c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
15207c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_FLAG;	/* HDX path */
15217c478bd9Sstevel@tonic-gate 		} else {	/* FDX path */
15227c478bd9Sstevel@tonic-gate 			if (!zsh_start(zs, zss)) {
15237c478bd9Sstevel@tonic-gate 				zss->sl_txstate = TX_IDLE;
15247c478bd9Sstevel@tonic-gate 				SCC_WRITE0(ZSWR0_RESET_TXINT);
15257c478bd9Sstevel@tonic-gate 			}
15267c478bd9Sstevel@tonic-gate 		}
15277c478bd9Sstevel@tonic-gate 		break;
15287c478bd9Sstevel@tonic-gate 
15297c478bd9Sstevel@tonic-gate 	/*
15307c478bd9Sstevel@tonic-gate 	 * This txint means the closing flag byte is going out the door.
15317c478bd9Sstevel@tonic-gate 	 * We use this state to allow this to complete before dropping RTS.
15327c478bd9Sstevel@tonic-gate 	 */
15337c478bd9Sstevel@tonic-gate 	case TX_FLAG:
15347c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_LAST;
15357c478bd9Sstevel@tonic-gate 		(void) zsh_start(zs, zss);
15367c478bd9Sstevel@tonic-gate 		break;
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 	/*
15397c478bd9Sstevel@tonic-gate 	 * Arriving here means the flag should be out and it's finally
15407c478bd9Sstevel@tonic-gate 	 * time to close the barn door.
15417c478bd9Sstevel@tonic-gate 	 */
15427c478bd9Sstevel@tonic-gate 	case TX_LAST:
15437c478bd9Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
15447c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15457c478bd9Sstevel@tonic-gate 		break;
15467c478bd9Sstevel@tonic-gate 
15477c478bd9Sstevel@tonic-gate 	/*
15487c478bd9Sstevel@tonic-gate 	 * If transmit was aborted, do nothing - watchdog will recover.
15497c478bd9Sstevel@tonic-gate 	 */
15507c478bd9Sstevel@tonic-gate 	case TX_ABORTED:
15517c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15527c478bd9Sstevel@tonic-gate 		break;
15537c478bd9Sstevel@tonic-gate 
15547c478bd9Sstevel@tonic-gate 	default:
15557c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
15567c478bd9Sstevel@tonic-gate 		break;
15577c478bd9Sstevel@tonic-gate 	}
15587c478bd9Sstevel@tonic-gate }
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate /*
15617c478bd9Sstevel@tonic-gate  * External Status Change interrupt service procedure
15627c478bd9Sstevel@tonic-gate  */
15637c478bd9Sstevel@tonic-gate static void
zsh_xsint(struct zscom * zs)15647c478bd9Sstevel@tonic-gate zsh_xsint(struct zscom *zs)
15657c478bd9Sstevel@tonic-gate {
156687e3123fSToomas Soome 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
156787e3123fSToomas Soome 	uchar_t s0, x0;
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_XSINT, "zsh_xsint: zs = %p", zs);
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 	s0 = SCC_READ0();
15727c478bd9Sstevel@tonic-gate 	x0 = s0 ^ zss->sl_rr0;
15737c478bd9Sstevel@tonic-gate 	zss->sl_rr0 = s0;
15747c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 	if (s0 & ZSRR0_TXUNDER) {
15777c478bd9Sstevel@tonic-gate 		switch (zss->sl_txstate) {
15787c478bd9Sstevel@tonic-gate 		/*
15797c478bd9Sstevel@tonic-gate 		 * A transmitter underrun has occurred.  If we are not
15807c478bd9Sstevel@tonic-gate 		 * here as the result of an abort sent by the watchdog
15817c478bd9Sstevel@tonic-gate 		 * timeout routine, we need to send an abort to flush
15827c478bd9Sstevel@tonic-gate 		 * the transmitter.  Otherwise there is a danger of
15837c478bd9Sstevel@tonic-gate 		 * trashing the next frame but still sending a good crc.
15847c478bd9Sstevel@tonic-gate 		 * The TX_ABORTED flag is set so that the watchdog
15857c478bd9Sstevel@tonic-gate 		 * routine can initiate recovery.
15867c478bd9Sstevel@tonic-gate 		 */
15877c478bd9Sstevel@tonic-gate 		case TX_ACTIVE:
15887c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
15897c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
15907c478bd9Sstevel@tonic-gate 			zss->sl_st.underrun++;
15917c478bd9Sstevel@tonic-gate 			zsh_txbad(zs, zss);
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED;
15947c478bd9Sstevel@tonic-gate 			zss->sl_wd_count = 0;
15957c478bd9Sstevel@tonic-gate 			break;
15967c478bd9Sstevel@tonic-gate 
15977c478bd9Sstevel@tonic-gate 		case TX_CRC:
15987c478bd9Sstevel@tonic-gate 			break;
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate 		case TX_FLAG:
16017c478bd9Sstevel@tonic-gate 			break;
16027c478bd9Sstevel@tonic-gate 
16037c478bd9Sstevel@tonic-gate 		case TX_ABORTED:
16047c478bd9Sstevel@tonic-gate 			break;
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 		case TX_OFF:
16077c478bd9Sstevel@tonic-gate 			break;
16087c478bd9Sstevel@tonic-gate 
16097c478bd9Sstevel@tonic-gate 		case TX_LAST:
16107c478bd9Sstevel@tonic-gate 			break;
16117c478bd9Sstevel@tonic-gate 
16127c478bd9Sstevel@tonic-gate 		default:
16137c478bd9Sstevel@tonic-gate 			break;
16147c478bd9Sstevel@tonic-gate 		}
16157c478bd9Sstevel@tonic-gate 	}
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	if ((x0 & ZSRR0_BREAK) && (s0 & ZSRR0_BREAK) && zs->zs_rd_cur) {
16187c478bd9Sstevel@tonic-gate 		zss->sl_st.abort++;
16197c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
16207c478bd9Sstevel@tonic-gate 	} else if ((s0 & ZSRR0_SYNC) && (zs->zs_rd_cur)) {
16217c478bd9Sstevel@tonic-gate 		/*
16227c478bd9Sstevel@tonic-gate 		 * Tricky code to avoid disaster in the case where
16237c478bd9Sstevel@tonic-gate 		 * an abort was detected while receiving a packet,
16247c478bd9Sstevel@tonic-gate 		 * but the abort did not last long enough to be
16257c478bd9Sstevel@tonic-gate 		 * detected by zsh_xsint - this can happen since
16267c478bd9Sstevel@tonic-gate 		 * the ZSRR0_BREAK is not latched.  Since an abort
16277c478bd9Sstevel@tonic-gate 		 * will automatically cause the SCC to enter
16287c478bd9Sstevel@tonic-gate 		 * hunt mode, hopefully, the sync/hunt bit will be
16297c478bd9Sstevel@tonic-gate 		 * set in this case (although if the interrupt is
16307c478bd9Sstevel@tonic-gate 		 * sufficiently delayed, the SCC may have sync'ed
16317c478bd9Sstevel@tonic-gate 		 * in again if it has detected a flag).
16327c478bd9Sstevel@tonic-gate 		 */
16337c478bd9Sstevel@tonic-gate 		zss->sl_st.abort++;
16347c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
16357c478bd9Sstevel@tonic-gate 	}
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 	if (x0 & s0 & ZSRR0_CTS) {
163819397407SSherry Moore 		if (zss->sl_txstate == TX_RTS) {
163919397407SSherry Moore 			if (!(zss->sl_flags & SF_FDXPTP)) {
164019397407SSherry Moore 				SCC_BIS(5, ZSWR5_TX_ENABLE);
164119397407SSherry Moore 			}
164219397407SSherry Moore 			(void) zsh_start(zs, zss);
164319397407SSherry Moore 		} else if ((zss->sl_mode.sm_config &
164419397407SSherry Moore 		    (CONN_IBM | CONN_SIGNAL))) {
164519397407SSherry Moore 			zss->sl_flags &= ~SF_FLUSH_WQ;
164619397407SSherry Moore 			zsh_setmstat(zs, CS_CTS_UP);
16477c478bd9Sstevel@tonic-gate 		}
16487c478bd9Sstevel@tonic-gate 	}
16497c478bd9Sstevel@tonic-gate 
16507c478bd9Sstevel@tonic-gate 	/*
16517c478bd9Sstevel@tonic-gate 	 * We don't care about CTS transitions unless we are in either
16527c478bd9Sstevel@tonic-gate 	 * IBM or SIGNAL mode, or both.  So, if we see CTS drop, and we
16537c478bd9Sstevel@tonic-gate 	 * care, and we are not idle, send up a report message.
16547c478bd9Sstevel@tonic-gate 	 */
16557c478bd9Sstevel@tonic-gate 	if ((x0 & ZSRR0_CTS) && ((s0 & ZSRR0_CTS) == 0) &&
16567c478bd9Sstevel@tonic-gate 	    (zss->sl_txstate != TX_OFF) &&
16577c478bd9Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_IBM | CONN_SIGNAL))) {
16587c478bd9Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
16597c478bd9Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_DOWN);
16607c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
16617c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
16627c478bd9Sstevel@tonic-gate 		zss->sl_st.cts++;
16637c478bd9Sstevel@tonic-gate 		if (zss->sl_txstate != TX_IDLE)
16647c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
16657c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
16667c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
16677c478bd9Sstevel@tonic-gate 		zss->sl_wd_count = 0;
16687c478bd9Sstevel@tonic-gate 		zsh_txbad(zs, zss);
16697c478bd9Sstevel@tonic-gate 	}
16707c478bd9Sstevel@tonic-gate }
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate /*
16747c478bd9Sstevel@tonic-gate  * Receive interrupt service procedure
16757c478bd9Sstevel@tonic-gate  */
16767c478bd9Sstevel@tonic-gate static void
zsh_rxint(struct zscom * zs)16777c478bd9Sstevel@tonic-gate zsh_rxint(struct zscom *zs)
16787c478bd9Sstevel@tonic-gate {
167987e3123fSToomas Soome 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
168087e3123fSToomas Soome 	mblk_t *bp = zss->sl_ractb;
16817c478bd9Sstevel@tonic-gate 	unsigned char *rd_cur;
16827c478bd9Sstevel@tonic-gate 
16837c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_RXINT, "zsh_rxint: zs = %p", zs);
16847c478bd9Sstevel@tonic-gate 
16857c478bd9Sstevel@tonic-gate 	if (((rd_cur = zs->zs_rd_cur) != NULL) && rd_cur < zs->zs_rd_lim) {
16867c478bd9Sstevel@tonic-gate 		*rd_cur++ = SCC_READDATA();
16877c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = rd_cur;
16887c478bd9Sstevel@tonic-gate 		return;
16897c478bd9Sstevel@tonic-gate 	}
16907c478bd9Sstevel@tonic-gate 
169187e3123fSToomas Soome 	if (rd_cur == NULL) { /* Beginning of frame */
169287e3123fSToomas Soome 		if (bp == NULL) {
16937c478bd9Sstevel@tonic-gate 			ZSH_ALLOCB(bp);
16947c478bd9Sstevel@tonic-gate 			zss->sl_ractb = bp;
16957c478bd9Sstevel@tonic-gate 		}
16967c478bd9Sstevel@tonic-gate 		zss->sl_rhead = bp;
16977c478bd9Sstevel@tonic-gate 	} else {	/* end of data block should be cur==lim */
16987c478bd9Sstevel@tonic-gate 		bp->b_wptr = zs->zs_rd_cur;
16997c478bd9Sstevel@tonic-gate 		ZSH_ALLOCB(bp->b_cont);
17007c478bd9Sstevel@tonic-gate 		bp = zss->sl_ractb = bp->b_cont;
17017c478bd9Sstevel@tonic-gate 	}
170287e3123fSToomas Soome 	if (bp == NULL) {
17037c478bd9Sstevel@tonic-gate 		zss->sl_st.nobuffers++;
17047c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
17057c478bd9Sstevel@tonic-gate 		return;
17067c478bd9Sstevel@tonic-gate 	}
17077c478bd9Sstevel@tonic-gate 	zs->zs_rd_cur = bp->b_wptr;
17087c478bd9Sstevel@tonic-gate 	zs->zs_rd_lim = bp->b_datap->db_lim;
17097c478bd9Sstevel@tonic-gate 	*zs->zs_rd_cur++ = SCC_READDATA(); /* Also resets interrupt */
17107c478bd9Sstevel@tonic-gate }
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate /*
17147c478bd9Sstevel@tonic-gate  * Special Receive Condition Interrupt routine
17157c478bd9Sstevel@tonic-gate  */
17167c478bd9Sstevel@tonic-gate static void
zsh_srint(struct zscom * zs)17177c478bd9Sstevel@tonic-gate zsh_srint(struct zscom *zs)
17187c478bd9Sstevel@tonic-gate {
171987e3123fSToomas Soome 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
172087e3123fSToomas Soome 	uchar_t s1;
172187e3123fSToomas Soome 	uchar_t *rd_cur;
17227c478bd9Sstevel@tonic-gate 
17237c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SRINT, "zsh_srint: zs = %p", zs);
17247c478bd9Sstevel@tonic-gate 
17257c478bd9Sstevel@tonic-gate 	SCC_READ(1, s1);
17267c478bd9Sstevel@tonic-gate 
17277c478bd9Sstevel@tonic-gate 	if (s1 & ZSRR1_RXEOF) {			/* end of frame */
17287c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
17297c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17307c478bd9Sstevel@tonic-gate 		if (s1 & ZSRR1_FE) {		/* bad CRC */
17317c478bd9Sstevel@tonic-gate 			zss->sl_st.crc++;
17327c478bd9Sstevel@tonic-gate 			zsh_rxbad(zs, zss);
17337c478bd9Sstevel@tonic-gate 			return;
17347c478bd9Sstevel@tonic-gate 		}
17357c478bd9Sstevel@tonic-gate 
17367c478bd9Sstevel@tonic-gate 		if ((rd_cur = zs->zs_rd_cur) == NULL)
17377c478bd9Sstevel@tonic-gate 			return;
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 		/*
17407c478bd9Sstevel@tonic-gate 		 * Drop one CRC byte from length because it came in
17417c478bd9Sstevel@tonic-gate 		 * before the special interrupt got here.
17427c478bd9Sstevel@tonic-gate 		 */
17437c478bd9Sstevel@tonic-gate 		zss->sl_ractb->b_wptr = rd_cur - 1;
17447c478bd9Sstevel@tonic-gate 
17457c478bd9Sstevel@tonic-gate 		/*
17467c478bd9Sstevel@tonic-gate 		 * put on done queue
17477c478bd9Sstevel@tonic-gate 		 */
17487c478bd9Sstevel@tonic-gate 		ZSH_PUTQ(zss->sl_rhead);
17497c478bd9Sstevel@tonic-gate 		zss->sl_rhead = NULL;
17507c478bd9Sstevel@tonic-gate 		zss->sl_ractb = NULL;
17517c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
17527c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
17537c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 	} else if (s1 & ZSRR1_DO) {
17567c478bd9Sstevel@tonic-gate 		(void) SCC_READDATA();
17577c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17587c478bd9Sstevel@tonic-gate 		zss->sl_st.overrun++;
17597c478bd9Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
17607c478bd9Sstevel@tonic-gate 	} else
17617c478bd9Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
17627c478bd9Sstevel@tonic-gate }
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate /*
17657c478bd9Sstevel@tonic-gate  * Handle a second stage interrupt.
17667c478bd9Sstevel@tonic-gate  * Does mostly lower priority buffer management stuff.
17677c478bd9Sstevel@tonic-gate  */
17687c478bd9Sstevel@tonic-gate static int
zsh_softint(struct zscom * zs)17697c478bd9Sstevel@tonic-gate zsh_softint(struct zscom *zs)
17707c478bd9Sstevel@tonic-gate {
177187e3123fSToomas Soome 	struct syncline *zss;
177287e3123fSToomas Soome 	queue_t *q;
177387e3123fSToomas Soome 	mblk_t *mp, *tmp;
177487e3123fSToomas Soome 	mblk_t *head = NULL, *tail = NULL;
177587e3123fSToomas Soome 	int allocbcount = 0;
17767c478bd9Sstevel@tonic-gate 	int m_error;
17777c478bd9Sstevel@tonic-gate 
17787c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_START, "zsh_soft start: zs = %p", zs);
17797c478bd9Sstevel@tonic-gate 
17807c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
17817c478bd9Sstevel@tonic-gate 	zss = (struct syncline *)zs->zs_priv;
178287e3123fSToomas Soome 	if (zss == NULL || (q = zss->sl_stream.str_rq) == NULL) {
17837c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
17847c478bd9Sstevel@tonic-gate 		return (0);
17857c478bd9Sstevel@tonic-gate 	}
17867c478bd9Sstevel@tonic-gate 	m_error = zss->sl_m_error;
17877c478bd9Sstevel@tonic-gate 
17887c478bd9Sstevel@tonic-gate 	zss->sl_m_error = 0;
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 
179187e3123fSToomas Soome 	if (zss->sl_mstat == NULL)
17927c478bd9Sstevel@tonic-gate 		zss->sl_mstat = allocb(sizeof (struct sl_status), BPRI_MED);
17937c478bd9Sstevel@tonic-gate 
17947c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
17957c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
17967c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
17977c478bd9Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
17987c478bd9Sstevel@tonic-gate 		} else {
179987e3123fSToomas Soome 			uchar_t s0;
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 			s0 = SCC_READ0();
18027c478bd9Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
18037c478bd9Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
18047c478bd9Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
18057c478bd9Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
18067c478bd9Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
18077c478bd9Sstevel@tonic-gate 			}
18087c478bd9Sstevel@tonic-gate 			if (zss->sl_flags & SF_FLUSH_WQ) {
18097c478bd9Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
18107c478bd9Sstevel@tonic-gate 				flushq(WR(q), FLUSHDATA);
18117c478bd9Sstevel@tonic-gate 				goto next;
18127c478bd9Sstevel@tonic-gate 			}
18137c478bd9Sstevel@tonic-gate 		}
18147c478bd9Sstevel@tonic-gate 	}
18157c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate next:
18187c478bd9Sstevel@tonic-gate 	for (;;) {
18197c478bd9Sstevel@tonic-gate 		ZSH_GETQ(mp);
182087e3123fSToomas Soome 		if (mp == NULL)
18217c478bd9Sstevel@tonic-gate 			break;
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 		if (mp->b_rptr == mp->b_wptr) {
18247c478bd9Sstevel@tonic-gate 			if (mp->b_datap->db_type == M_RSE) {
18257c478bd9Sstevel@tonic-gate 				allocbcount++;
18267c478bd9Sstevel@tonic-gate 			}
18277c478bd9Sstevel@tonic-gate 			freemsg(mp);
18287c478bd9Sstevel@tonic-gate 			continue;
18297c478bd9Sstevel@tonic-gate 		}
18307c478bd9Sstevel@tonic-gate 		if (mp->b_datap->db_type == M_DATA) {
18317c478bd9Sstevel@tonic-gate 			zss->sl_st.ichar += msgdsize(mp);
18327c478bd9Sstevel@tonic-gate 			zss->sl_st.ipack++;
18337c478bd9Sstevel@tonic-gate 			if (!(canputnext(q))) {
18347c478bd9Sstevel@tonic-gate 				zss->sl_st.ierror++;
18357c478bd9Sstevel@tonic-gate 				allocbcount++;
18367c478bd9Sstevel@tonic-gate 				freemsg(mp);
18377c478bd9Sstevel@tonic-gate 				continue;
18387c478bd9Sstevel@tonic-gate 			}
18397c478bd9Sstevel@tonic-gate 		} else if (mp->b_datap->db_type == M_PROTO) {
18407c478bd9Sstevel@tonic-gate 			if (!(canputnext(q))) {
18417c478bd9Sstevel@tonic-gate 				freemsg(mp);
18427c478bd9Sstevel@tonic-gate 				continue;
18437c478bd9Sstevel@tonic-gate 			}
18447c478bd9Sstevel@tonic-gate 		}
184587e3123fSToomas Soome 		if (head == NULL) {
18467c478bd9Sstevel@tonic-gate 			allocbcount++;
18477c478bd9Sstevel@tonic-gate 			zss->sl_soft_active = 1;
18487c478bd9Sstevel@tonic-gate 			head = mp;
18497c478bd9Sstevel@tonic-gate 		} else {
185087e3123fSToomas Soome 			if (tail == NULL)
18517c478bd9Sstevel@tonic-gate 				tail = head;
18527c478bd9Sstevel@tonic-gate 			tail->b_next = mp;
18537c478bd9Sstevel@tonic-gate 			tail = mp;
18547c478bd9Sstevel@tonic-gate 		}
18557c478bd9Sstevel@tonic-gate 	}
18567c478bd9Sstevel@tonic-gate 	if (allocbcount)
18577c478bd9Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, allocbcount);
18587c478bd9Sstevel@tonic-gate 
18597c478bd9Sstevel@tonic-gate 	tmp = NULL;
18607c478bd9Sstevel@tonic-gate again:
18617c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
186287e3123fSToomas Soome 	if (zss->sl_xstandby == NULL) {
18637c478bd9Sstevel@tonic-gate 		if (tmp) {
18647c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = tmp;
18657c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
18667c478bd9Sstevel@tonic-gate 		} else {
18677c478bd9Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
18687c478bd9Sstevel@tonic-gate 			if (tmp = getq(WR(q)))
18697c478bd9Sstevel@tonic-gate 				goto again;
18707c478bd9Sstevel@tonic-gate 		}
18717c478bd9Sstevel@tonic-gate 	} else {
18727c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
18737c478bd9Sstevel@tonic-gate 		if (tmp)
18747c478bd9Sstevel@tonic-gate 			(void) putbq(WR(q), tmp);
18757c478bd9Sstevel@tonic-gate 	}
18767c478bd9Sstevel@tonic-gate 
18777c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 	while (head) {
188087e3123fSToomas Soome 		if (tail == NULL) {
18817c478bd9Sstevel@tonic-gate 			putnext(q, head);
18827c478bd9Sstevel@tonic-gate 			break;
18837c478bd9Sstevel@tonic-gate 		}
18847c478bd9Sstevel@tonic-gate 		mp = head;
18857c478bd9Sstevel@tonic-gate 		head = head->b_next;
18867c478bd9Sstevel@tonic-gate 		mp->b_next = NULL;
18877c478bd9Sstevel@tonic-gate 		putnext(q, mp);
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	}
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 	if (m_error)
18927c478bd9Sstevel@tonic-gate 		(void) putnextctl1(q, M_ERROR, m_error);
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 	zss->sl_soft_active = 0;
18957c478bd9Sstevel@tonic-gate 
18967c478bd9Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_END, "zsh_soft end: zs = %p", zs);
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 	return (0);
18997c478bd9Sstevel@tonic-gate }
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate /*
19027c478bd9Sstevel@tonic-gate  * Initialization routine.
19037c478bd9Sstevel@tonic-gate  * Sets Clock sources, baud rate, modes and miscellaneous parameters.
19047c478bd9Sstevel@tonic-gate  */
19057c478bd9Sstevel@tonic-gate static int
zsh_program(struct zscom * zs,struct scc_mode * sm)19067c478bd9Sstevel@tonic-gate zsh_program(struct zscom *zs, struct scc_mode *sm)
19077c478bd9Sstevel@tonic-gate {
190887e3123fSToomas Soome 	struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
190987e3123fSToomas Soome 	struct zs_prog *zspp;
191087e3123fSToomas Soome 	ushort_t	tconst = 0;
191187e3123fSToomas Soome 	int	wr11 = 0;
191287e3123fSToomas Soome 	int	baud = 0;
191387e3123fSToomas Soome 	int	pll = 0;
191487e3123fSToomas Soome 	int	speed = 0;
191587e3123fSToomas Soome 	int	flags = ZSP_SYNC;
19167c478bd9Sstevel@tonic-gate 	int		err = 0;
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs); /* get our house in order */
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	switch (sm->sm_txclock) {
19217c478bd9Sstevel@tonic-gate 	case TXC_IS_TXC:
19227c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_TRXC;
19237c478bd9Sstevel@tonic-gate 		break;
19247c478bd9Sstevel@tonic-gate 	case TXC_IS_RXC:
19257c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_RTXC;
19267c478bd9Sstevel@tonic-gate 		break;
19277c478bd9Sstevel@tonic-gate 	case TXC_IS_BAUD:
19287c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_BAUD;
19297c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TRXC_OUT_ENA + ZSWR11_TRXC_XMIT;
19307c478bd9Sstevel@tonic-gate 		baud++;
19317c478bd9Sstevel@tonic-gate 		break;
19327c478bd9Sstevel@tonic-gate 	case TXC_IS_PLL:
19337c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_DPLL;
19347c478bd9Sstevel@tonic-gate 		pll++;
19357c478bd9Sstevel@tonic-gate 		break;
19367c478bd9Sstevel@tonic-gate 	default:
19377c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_TXC;
19387c478bd9Sstevel@tonic-gate 		err = EINVAL;
19397c478bd9Sstevel@tonic-gate 		goto out;
19407c478bd9Sstevel@tonic-gate 	}
19417c478bd9Sstevel@tonic-gate 	switch (sm->sm_rxclock) {
19427c478bd9Sstevel@tonic-gate 	case RXC_IS_RXC:
19437c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_RTXC;
19447c478bd9Sstevel@tonic-gate 		break;
19457c478bd9Sstevel@tonic-gate 	case RXC_IS_TXC:
19467c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_TRXC;
19477c478bd9Sstevel@tonic-gate 		break;
19487c478bd9Sstevel@tonic-gate 	case RXC_IS_BAUD:
19497c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_BAUD;
19507c478bd9Sstevel@tonic-gate 		baud++;
19517c478bd9Sstevel@tonic-gate 		break;
19527c478bd9Sstevel@tonic-gate 	case RXC_IS_PLL:
19537c478bd9Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_DPLL;
19547c478bd9Sstevel@tonic-gate 		pll++;
19557c478bd9Sstevel@tonic-gate 		break;
19567c478bd9Sstevel@tonic-gate 	default:
19577c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
19587c478bd9Sstevel@tonic-gate 		err = EINVAL;
19597c478bd9Sstevel@tonic-gate 		goto out;
19607c478bd9Sstevel@tonic-gate 	}
19617c478bd9Sstevel@tonic-gate 	if (baud && pll) {
19627c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
19637c478bd9Sstevel@tonic-gate 		err = EINVAL;
19647c478bd9Sstevel@tonic-gate 		goto out;
19657c478bd9Sstevel@tonic-gate 	}
19667c478bd9Sstevel@tonic-gate 	if (pll && !(sm->sm_config & CONN_NRZI)) {
19677c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
19687c478bd9Sstevel@tonic-gate 		err = EINVAL;
19697c478bd9Sstevel@tonic-gate 		goto out;
19707c478bd9Sstevel@tonic-gate 	}
19717c478bd9Sstevel@tonic-gate 
19727c478bd9Sstevel@tonic-gate 	/*
19737c478bd9Sstevel@tonic-gate 	 * If we're going to use the BRG and the speed we want is != 0...
19747c478bd9Sstevel@tonic-gate 	 */
19757c478bd9Sstevel@tonic-gate 	if (baud && (speed = sm->sm_baudrate)) {
19767c478bd9Sstevel@tonic-gate 		tconst = (PCLK + speed) / (2 * speed) - 2;
19777c478bd9Sstevel@tonic-gate 		if (tconst == 0) {
19787c478bd9Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
19797c478bd9Sstevel@tonic-gate 			err = EINVAL;
19807c478bd9Sstevel@tonic-gate 			goto out;
19817c478bd9Sstevel@tonic-gate 		}
19827c478bd9Sstevel@tonic-gate 		sm->sm_baudrate = PCLK / (2 * ((int)tconst + 2));
19837c478bd9Sstevel@tonic-gate 	} else {
19847c478bd9Sstevel@tonic-gate 		tconst = 0;	/* Stop BRG.  Also quiesces pin 24. */
19857c478bd9Sstevel@tonic-gate 	}
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 	if (pll) {
19887c478bd9Sstevel@tonic-gate 		if ((speed  = sm->sm_baudrate * 32) != 0)
19897c478bd9Sstevel@tonic-gate 			tconst = (PCLK + speed) / (2 * speed) - 2;
19907c478bd9Sstevel@tonic-gate 		else
19917c478bd9Sstevel@tonic-gate 			tconst = 0;
19927c478bd9Sstevel@tonic-gate 		if (tconst == 0) {
19937c478bd9Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
19947c478bd9Sstevel@tonic-gate 			err = EINVAL;
19957c478bd9Sstevel@tonic-gate 			goto out;
19967c478bd9Sstevel@tonic-gate 		}
19977c478bd9Sstevel@tonic-gate 		speed = PCLK / (2 * ((int)tconst + 2));
19987c478bd9Sstevel@tonic-gate 		sm->sm_baudrate = speed / 32;
19997c478bd9Sstevel@tonic-gate 		flags |= ZSP_PLL;
20007c478bd9Sstevel@tonic-gate 	}
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 	if ((sm->sm_config & (CONN_LPBK|CONN_ECHO)) == (CONN_LPBK|CONN_ECHO)) {
20037c478bd9Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_LPBKS;
20047c478bd9Sstevel@tonic-gate 		err = EINVAL;
20057c478bd9Sstevel@tonic-gate 		goto out;
20067c478bd9Sstevel@tonic-gate 	}
20077c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_LPBK)
20087c478bd9Sstevel@tonic-gate 		flags |= ZSP_LOOP;
20097c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_NRZI)
20107c478bd9Sstevel@tonic-gate 		flags |= ZSP_NRZI;
20117c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_ECHO)
20127c478bd9Sstevel@tonic-gate 		flags |= ZSP_ECHO;
20137c478bd9Sstevel@tonic-gate 
20147c478bd9Sstevel@tonic-gate 	zspp = &zs_prog[zs->zs_unit];
20157c478bd9Sstevel@tonic-gate 
20167c478bd9Sstevel@tonic-gate 	zspp->zs = zs;
20177c478bd9Sstevel@tonic-gate 	zspp->flags = (uchar_t)flags;
20187c478bd9Sstevel@tonic-gate 	zspp->wr4 = ZSWR4_SDLC;
20197c478bd9Sstevel@tonic-gate 	zspp->wr11 = (uchar_t)wr11;
20207c478bd9Sstevel@tonic-gate 	zspp->wr12 = (uchar_t)(tconst & 0xff);
20217c478bd9Sstevel@tonic-gate 	zspp->wr13 = (uchar_t)((tconst >> 8) & 0xff);
20227c478bd9Sstevel@tonic-gate 	zspp->wr3 = (uchar_t)(ZSWR3_RX_ENABLE | ZSWR3_RXCRC_ENABLE |
20237c478bd9Sstevel@tonic-gate 	    ZSWR3_RX_8);
20247c478bd9Sstevel@tonic-gate 	zspp->wr5 = (uchar_t)(ZSWR5_TX_8 | ZSWR5_DTR | ZSWR5_TXCRC_ENABLE);
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FDXPTP) {
20277c478bd9Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_RTS;
20287c478bd9Sstevel@tonic-gate 		zss->sl_rr0 |= ZSRR0_CTS;		/* Assume CTS is high */
20297c478bd9Sstevel@tonic-gate 	}
20307c478bd9Sstevel@tonic-gate 	if (sm->sm_config & CONN_IBM) {
20317c478bd9Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
20327c478bd9Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC | ZSR15_CTS);
20337c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
20347c478bd9Sstevel@tonic-gate 			zspp->wr15 &= ~ZSR15_CTS;
20357c478bd9Sstevel@tonic-gate 	} else {
20367c478bd9Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_TX_ENABLE;
20377c478bd9Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
20387c478bd9Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC);
20397c478bd9Sstevel@tonic-gate 		if (sm->sm_config & CONN_SIGNAL)
20407c478bd9Sstevel@tonic-gate 			zspp->wr15 |= ZSR15_CTS;
20417c478bd9Sstevel@tonic-gate 	}
20427c478bd9Sstevel@tonic-gate 
20437c478bd9Sstevel@tonic-gate 	zs_program(zspp);
20447c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
20457c478bd9Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
20467c478bd9Sstevel@tonic-gate 	zss->sl_flags |= SF_INITIALIZED;
20477c478bd9Sstevel@tonic-gate 	bzero(&zss->sl_st, sizeof (struct sl_stats));
20487c478bd9Sstevel@tonic-gate 	bcopy(sm, &zss->sl_mode, sizeof (struct scc_mode));
20497c478bd9Sstevel@tonic-gate 	zss->sl_mode.sm_retval = 0;	/* successful */
20507c478bd9Sstevel@tonic-gate out:
20517c478bd9Sstevel@tonic-gate 	return (err);
20527c478bd9Sstevel@tonic-gate }
20537c478bd9Sstevel@tonic-gate 
20547c478bd9Sstevel@tonic-gate /*
20557c478bd9Sstevel@tonic-gate  * Function to store modem signal changes in sl_mstat field.
20567c478bd9Sstevel@tonic-gate  * Note that these events are supposed to be so far apart in time that
20577c478bd9Sstevel@tonic-gate  * we should always be able to send up the event and allocate a message
20587c478bd9Sstevel@tonic-gate  * block before another one happens.  If not, we'll overwrite this one.
20597c478bd9Sstevel@tonic-gate  */
20607c478bd9Sstevel@tonic-gate static void
zsh_setmstat(struct zscom * zs,int event)20617c478bd9Sstevel@tonic-gate zsh_setmstat(struct zscom *zs, int event)
20627c478bd9Sstevel@tonic-gate {
206387e3123fSToomas Soome 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
206487e3123fSToomas Soome 	struct sl_status *mstat;
206587e3123fSToomas Soome 	mblk_t *mp;
20667c478bd9Sstevel@tonic-gate 
20677c478bd9Sstevel@tonic-gate 	if (((mp = zss->sl_mstat) != NULL) &&
20687c478bd9Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_SIGNAL))) {
20697c478bd9Sstevel@tonic-gate 		mstat = (struct sl_status *)mp->b_wptr;
20707c478bd9Sstevel@tonic-gate 		mstat->type = (zss->sl_mode.sm_config & CONN_IBM) ?
20717c478bd9Sstevel@tonic-gate 		    SLS_LINKERR : SLS_MDMSTAT;
20727c478bd9Sstevel@tonic-gate 		mstat->status = event;
20737c478bd9Sstevel@tonic-gate 		gethrestime(&mstat->tstamp);
20747c478bd9Sstevel@tonic-gate 		mp->b_wptr += sizeof (struct sl_status);
20757c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
20767c478bd9Sstevel@tonic-gate 		ZSH_PUTQ(mp);
20777c478bd9Sstevel@tonic-gate 		zss->sl_mstat = NULL;
20787c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
20797c478bd9Sstevel@tonic-gate 	}
20807c478bd9Sstevel@tonic-gate }
20817c478bd9Sstevel@tonic-gate 
20827c478bd9Sstevel@tonic-gate /*
20837c478bd9Sstevel@tonic-gate  * Received Bad Frame procedure
20847c478bd9Sstevel@tonic-gate  */
20857c478bd9Sstevel@tonic-gate static void
zsh_rxbad(struct zscom * zs,struct syncline * zss)20867c478bd9Sstevel@tonic-gate zsh_rxbad(struct zscom *zs, struct syncline *zss)
20877c478bd9Sstevel@tonic-gate {
20887c478bd9Sstevel@tonic-gate 	/*
20897c478bd9Sstevel@tonic-gate 	 * swallow bad characters
20907c478bd9Sstevel@tonic-gate 	 */
20917c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20927c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20937c478bd9Sstevel@tonic-gate 	(void) SCC_READDATA();
20947c478bd9Sstevel@tonic-gate 
20957c478bd9Sstevel@tonic-gate 	SCC_BIS(3, ZSWR3_HUNT);	/* enter hunt mode - ignores rest of frame */
20967c478bd9Sstevel@tonic-gate 
20977c478bd9Sstevel@tonic-gate 	zss->sl_st.ierror++;
20987c478bd9Sstevel@tonic-gate 
20997c478bd9Sstevel@tonic-gate 	/*
21007c478bd9Sstevel@tonic-gate 	 * Free active receive message.
21017c478bd9Sstevel@tonic-gate 	 */
21027c478bd9Sstevel@tonic-gate 	if (zss->sl_rhead) {
21037c478bd9Sstevel@tonic-gate 		zss->sl_rhead->b_wptr = zss->sl_rhead->b_rptr;
21047c478bd9Sstevel@tonic-gate 		zss->sl_rhead->b_datap->db_type = M_RSE;
21057c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_rhead);
21067c478bd9Sstevel@tonic-gate 		zss->sl_ractb = NULL;
21077c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
21087c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
21097c478bd9Sstevel@tonic-gate 	}
21107c478bd9Sstevel@tonic-gate 	if (zss->sl_rhead) {
21117c478bd9Sstevel@tonic-gate 		zss->sl_rhead = NULL;
21127c478bd9Sstevel@tonic-gate 		ZSH_ALLOCB(zss->sl_ractb);
21137c478bd9Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
21147c478bd9Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
21157c478bd9Sstevel@tonic-gate 	}
21167c478bd9Sstevel@tonic-gate 
21177c478bd9Sstevel@tonic-gate 	ZSSETSOFT(zs);
21187c478bd9Sstevel@tonic-gate }
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate /*
21217c478bd9Sstevel@tonic-gate  * Transmit error procedure
21227c478bd9Sstevel@tonic-gate  */
21237c478bd9Sstevel@tonic-gate static void
zsh_txbad(struct zscom * zs,struct syncline * zss)21247c478bd9Sstevel@tonic-gate zsh_txbad(struct zscom *zs, struct syncline *zss)
21257c478bd9Sstevel@tonic-gate {
21267c478bd9Sstevel@tonic-gate 	if (zss->sl_xhead) {		/* free the message we were sending */
21277c478bd9Sstevel@tonic-gate 		zss->sl_xhead->b_wptr = zss->sl_xhead->b_rptr;
21287c478bd9Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
21297c478bd9Sstevel@tonic-gate 		zss->sl_xactb = NULL;
21307c478bd9Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
21317c478bd9Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
21327c478bd9Sstevel@tonic-gate 	}
21337c478bd9Sstevel@tonic-gate 	zss->sl_xhead = NULL;
21347c478bd9Sstevel@tonic-gate 
21357c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_FDXPTP)) {
21367c478bd9Sstevel@tonic-gate 		/*
21377c478bd9Sstevel@tonic-gate 		 * drop RTS and our notion of CTS
21387c478bd9Sstevel@tonic-gate 		 */
21397c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
21407c478bd9Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
21417c478bd9Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
21427c478bd9Sstevel@tonic-gate 	}
21437c478bd9Sstevel@tonic-gate 	zss->sl_txstate = TX_IDLE;
21447c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_PHONY))
21457c478bd9Sstevel@tonic-gate 		zss->sl_st.oerror++;
21467c478bd9Sstevel@tonic-gate }
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate /*
21497c478bd9Sstevel@tonic-gate  * Transmitter watchdog timeout routine
21507c478bd9Sstevel@tonic-gate  */
21517c478bd9Sstevel@tonic-gate static void
zsh_watchdog(void * arg)21527c478bd9Sstevel@tonic-gate zsh_watchdog(void *arg)
21537c478bd9Sstevel@tonic-gate {
21547c478bd9Sstevel@tonic-gate 	struct zscom *zs = arg;
21557c478bd9Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
21567c478bd9Sstevel@tonic-gate 	queue_t *wq;
21577c478bd9Sstevel@tonic-gate 	mblk_t *mp;
21587c478bd9Sstevel@tonic-gate 	int warning = 0;
21597c478bd9Sstevel@tonic-gate 	uchar_t s0;
21607c478bd9Sstevel@tonic-gate 	int do_flushwq = 0;
21617c478bd9Sstevel@tonic-gate 
21627c478bd9Sstevel@tonic-gate 	/*
21637c478bd9Sstevel@tonic-gate 	 * The main reason for this routine is because, under some
21647c478bd9Sstevel@tonic-gate 	 * circumstances, a transmit interrupt may get lost (ie., if
21657c478bd9Sstevel@tonic-gate 	 * underrun occurs after the last character has been sent, and
21667c478bd9Sstevel@tonic-gate 	 * the tx interrupt following the abort gets scheduled before
21677c478bd9Sstevel@tonic-gate 	 * the current tx interrupt has been serviced).  Transmit can
21687c478bd9Sstevel@tonic-gate 	 * also get hung if the cable is pulled out and the clock was
21697c478bd9Sstevel@tonic-gate 	 * coming in from the modem.
21707c478bd9Sstevel@tonic-gate 	 */
21717c478bd9Sstevel@tonic-gate 
21727c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
21737c478bd9Sstevel@tonic-gate 	if (zss->sl_stream.str_rq)
21747c478bd9Sstevel@tonic-gate 		wq = WR(zss->sl_stream.str_rq);
21757c478bd9Sstevel@tonic-gate 	else {
21767c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
21777c478bd9Sstevel@tonic-gate 		return;		/* guard against close/callback race */
21787c478bd9Sstevel@tonic-gate 	}
21797c478bd9Sstevel@tonic-gate 
21807c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
21817c478bd9Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_XMT_INPROG) && wq->q_first) {
21827c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_XMT_INPROG;
21837c478bd9Sstevel@tonic-gate 		if ((zss->sl_flags & SF_FDXPTP) ||
21847c478bd9Sstevel@tonic-gate 		    zsh_hdp_ok_or_rts_state(zs, zss))
21857c478bd9Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
21867c478bd9Sstevel@tonic-gate 		goto end_watchdog;
21877c478bd9Sstevel@tonic-gate 	}
21887c478bd9Sstevel@tonic-gate 
21897c478bd9Sstevel@tonic-gate 	if (zss->sl_wd_count-- > 0)
21907c478bd9Sstevel@tonic-gate 		goto end_watchdog;
21917c478bd9Sstevel@tonic-gate 
21927c478bd9Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
21937c478bd9Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
21947c478bd9Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
21957c478bd9Sstevel@tonic-gate 		else {
21967c478bd9Sstevel@tonic-gate 			s0 = SCC_READ0();
21977c478bd9Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
21987c478bd9Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
21997c478bd9Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
22007c478bd9Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
22017c478bd9Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
22027c478bd9Sstevel@tonic-gate 			}
22037c478bd9Sstevel@tonic-gate 		}
22047c478bd9Sstevel@tonic-gate 	}
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate 	switch (zss->sl_txstate) {
22077c478bd9Sstevel@tonic-gate 
22087c478bd9Sstevel@tonic-gate 	case TX_ABORTED:
22097c478bd9Sstevel@tonic-gate 		/*
22107c478bd9Sstevel@tonic-gate 		 * Transmitter was hung ... try restarting it.
22117c478bd9Sstevel@tonic-gate 		 */
22127c478bd9Sstevel@tonic-gate 		if (zss->sl_flags & SF_FDXPTP) {
22137c478bd9Sstevel@tonic-gate 			zss->sl_flags |= SF_XMT_INPROG;
22147c478bd9Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
22157c478bd9Sstevel@tonic-gate 		} else
22167c478bd9Sstevel@tonic-gate 			do_flushwq = 1;
22177c478bd9Sstevel@tonic-gate 		break;
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 	case TX_ACTIVE:
22207c478bd9Sstevel@tonic-gate 	case TX_CRC:
22217c478bd9Sstevel@tonic-gate 		/*
22227c478bd9Sstevel@tonic-gate 		 * Transmit is hung for some reason. Reset tx interrupt.
22237c478bd9Sstevel@tonic-gate 		 * Flush transmit fifo by sending an abort command
22247c478bd9Sstevel@tonic-gate 		 * which also sets the Underrun/EOM latch in WR0 and in
22257c478bd9Sstevel@tonic-gate 		 * turn generates an External Status interrupt that
22267c478bd9Sstevel@tonic-gate 		 * will reset the necessary message buffer pointers.
22277c478bd9Sstevel@tonic-gate 		 * The watchdog timer will cycle again to allow the SCC
22287c478bd9Sstevel@tonic-gate 		 * to settle down after the abort command.  The next
22297c478bd9Sstevel@tonic-gate 		 * time through we'll see that the state is now TX_ABORTED
22307c478bd9Sstevel@tonic-gate 		 * and call zsh_start to grab a new message.
22317c478bd9Sstevel@tonic-gate 		 */
22327c478bd9Sstevel@tonic-gate 		if (--zss->sl_wd_count <= 0) {
22337c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
22347c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_ERRORS);
22357c478bd9Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
22367c478bd9Sstevel@tonic-gate 			zsh_txbad(zs, zss);
22377c478bd9Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED; /* must be after txbad */
22387c478bd9Sstevel@tonic-gate 			warning = 1;
22397c478bd9Sstevel@tonic-gate 		}
22407c478bd9Sstevel@tonic-gate 		break;
22417c478bd9Sstevel@tonic-gate 
22427c478bd9Sstevel@tonic-gate 	case TX_RTS:
22437c478bd9Sstevel@tonic-gate 		/*
22447c478bd9Sstevel@tonic-gate 		 * Timer expired after we raised RTS.  CTS never came up.
22457c478bd9Sstevel@tonic-gate 		 */
22467c478bd9Sstevel@tonic-gate 		zss->sl_st.cts++;
22477c478bd9Sstevel@tonic-gate 
22487c478bd9Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_TO);
22497c478bd9Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
22507c478bd9Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
22517c478bd9Sstevel@tonic-gate 		ZSSETSOFT(zs);
22527c478bd9Sstevel@tonic-gate 		break;
22537c478bd9Sstevel@tonic-gate 
22547c478bd9Sstevel@tonic-gate 	default:
22557c478bd9Sstevel@tonic-gate 		/*
22567c478bd9Sstevel@tonic-gate 		 * If we time out in an inactive state we set a soft
22577c478bd9Sstevel@tonic-gate 		 * interrupt.  This will call zsh_start which will
22587c478bd9Sstevel@tonic-gate 		 * clear SF_XMT_INPROG if the queue is empty.
22597c478bd9Sstevel@tonic-gate 		 */
22607c478bd9Sstevel@tonic-gate 		break;
22617c478bd9Sstevel@tonic-gate 	}
22627c478bd9Sstevel@tonic-gate end_watchdog:
22637c478bd9Sstevel@tonic-gate 	if (zss->sl_txstate != TX_OFF) {
22647c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22657c478bd9Sstevel@tonic-gate 		zss->sl_wd_id = timeout(zsh_watchdog, zs, SIO_WATCHDOG_TICK);
22667c478bd9Sstevel@tonic-gate 	} else {
22677c478bd9Sstevel@tonic-gate 		zss->sl_wd_id = 0;	/* safety */
22687c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22697c478bd9Sstevel@tonic-gate 	}
22707c478bd9Sstevel@tonic-gate 	if (warning || do_flushwq) {
22717c478bd9Sstevel@tonic-gate 		flushq(wq, FLUSHDATA);
22727c478bd9Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
22737c478bd9Sstevel@tonic-gate 		if ((mp = zss->sl_xstandby) != NULL)
22747c478bd9Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
22757c478bd9Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
22767c478bd9Sstevel@tonic-gate 		if (mp)
22777c478bd9Sstevel@tonic-gate 			freemsg(mp);
22787c478bd9Sstevel@tonic-gate 	}
22797c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
22807c478bd9Sstevel@tonic-gate 	if (warning)
22817c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "zsh%x: transmit hung", zs->zs_unit);
22827c478bd9Sstevel@tonic-gate }
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate static void
zsh_callback(void * arg)22857c478bd9Sstevel@tonic-gate zsh_callback(void *arg)
22867c478bd9Sstevel@tonic-gate {
22877c478bd9Sstevel@tonic-gate 	struct zscom *zs = arg;
22887c478bd9Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
22897c478bd9Sstevel@tonic-gate 	int tmp = ZSH_MAX_RSTANDBY;
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
22927c478bd9Sstevel@tonic-gate 	if (zss->sl_bufcid) {
22937c478bd9Sstevel@tonic-gate 		zss->sl_bufcid = 0;
22947c478bd9Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, tmp);
22957c478bd9Sstevel@tonic-gate 	}
22967c478bd9Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
22977c478bd9Sstevel@tonic-gate }
2298