xref: /onnv-gate/usr/src/uts/sun/io/zs_hdlc.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate /*
30*0Sstevel@tonic-gate  *	HDLC protocol handler for Z8530 SCC.
31*0Sstevel@tonic-gate  */
32*0Sstevel@tonic-gate 
33*0Sstevel@tonic-gate #include	<sys/param.h>
34*0Sstevel@tonic-gate #include	<sys/systm.h>
35*0Sstevel@tonic-gate #include	<sys/types.h>
36*0Sstevel@tonic-gate #include	<sys/sysmacros.h>
37*0Sstevel@tonic-gate #include	<sys/kmem.h>
38*0Sstevel@tonic-gate #include	<sys/stropts.h>
39*0Sstevel@tonic-gate #include	<sys/stream.h>
40*0Sstevel@tonic-gate #include	<sys/strsun.h>
41*0Sstevel@tonic-gate #include	<sys/stat.h>
42*0Sstevel@tonic-gate #include	<sys/cred.h>
43*0Sstevel@tonic-gate #include	<sys/user.h>
44*0Sstevel@tonic-gate #include	<sys/proc.h>
45*0Sstevel@tonic-gate #include	<sys/file.h>
46*0Sstevel@tonic-gate #include	<sys/uio.h>
47*0Sstevel@tonic-gate #include	<sys/buf.h>
48*0Sstevel@tonic-gate #include	<sys/mkdev.h>
49*0Sstevel@tonic-gate #include	<sys/cmn_err.h>
50*0Sstevel@tonic-gate #include	<sys/errno.h>
51*0Sstevel@tonic-gate #include	<sys/fcntl.h>
52*0Sstevel@tonic-gate 
53*0Sstevel@tonic-gate #include	<sys/zsdev.h>
54*0Sstevel@tonic-gate #include	<sys/ser_sync.h>
55*0Sstevel@tonic-gate #include	<sys/conf.h>
56*0Sstevel@tonic-gate #include	<sys/ddi.h>
57*0Sstevel@tonic-gate #include	<sys/sunddi.h>
58*0Sstevel@tonic-gate #include	<sys/dlpi.h>
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate #define	ZSH_TRACING
61*0Sstevel@tonic-gate #ifdef	ZSH_TRACING
62*0Sstevel@tonic-gate #include	<sys/vtrace.h>
63*0Sstevel@tonic-gate 
64*0Sstevel@tonic-gate /*
65*0Sstevel@tonic-gate  * Temp tracepoint definitions
66*0Sstevel@tonic-gate  */
67*0Sstevel@tonic-gate #define	TR_ZSH		50
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #define	TR_ZSH_TXINT	1
70*0Sstevel@tonic-gate #define	TR_ZSH_XSINT	2
71*0Sstevel@tonic-gate #define	TR_ZSH_RXINT	3
72*0Sstevel@tonic-gate #define	TR_ZSH_SRINT	4
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate #define	TR_ZSH_WPUT_START		5
75*0Sstevel@tonic-gate #define	TR_ZSH_WPUT_END			6
76*0Sstevel@tonic-gate #define	TR_ZSH_START_START		7
77*0Sstevel@tonic-gate #define	TR_ZSH_START_END		8
78*0Sstevel@tonic-gate #define	TR_ZSH_SOFT_START		9
79*0Sstevel@tonic-gate #define	TR_ZSH_SOFT_END			10
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate #define	TR_ZSH_OPEN	 11
82*0Sstevel@tonic-gate #define	TR_ZSH_CLOSE	12
83*0Sstevel@tonic-gate 
84*0Sstevel@tonic-gate #endif	/* ZSH_TRACING */
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate /*
87*0Sstevel@tonic-gate  * Logging definitions
88*0Sstevel@tonic-gate  */
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate /*
91*0Sstevel@tonic-gate  * #define	ZSH_DEBUG
92*0Sstevel@tonic-gate  */
93*0Sstevel@tonic-gate #ifdef ZSH_DEBUG
94*0Sstevel@tonic-gate 
95*0Sstevel@tonic-gate #ifdef ZS_DEBUG_ALL
96*0Sstevel@tonic-gate extern	char	zs_h_log[];
97*0Sstevel@tonic-gate extern	int	zs_h_log_n;
98*0Sstevel@tonic-gate #define	zsh_h_log_add(c) \
99*0Sstevel@tonic-gate 	{ \
100*0Sstevel@tonic-gate 		if (zs_h_log_n >= ZS_H_LOG_MAX) \
101*0Sstevel@tonic-gate 			zs_h_log_n = 0; \
102*0Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = 'A' + zs->zs_unit; \
103*0Sstevel@tonic-gate 		zs_h_log[zs_h_log_n++] = c; \
104*0Sstevel@tonic-gate 		zs_h_log[zs_h_log_n] = '\0'; \
105*0Sstevel@tonic-gate 	}
106*0Sstevel@tonic-gate #define	zsh_h_log_clear
107*0Sstevel@tonic-gate #else
108*0Sstevel@tonic-gate #define	ZSH_H_LOG_MAX   0x8000
109*0Sstevel@tonic-gate char zsh_h_log[2][ZSH_H_LOG_MAX +10];
110*0Sstevel@tonic-gate int zsh_h_log_n[2];
111*0Sstevel@tonic-gate #define	zsh_h_log_add(c) \
112*0Sstevel@tonic-gate 	{ \
113*0Sstevel@tonic-gate 		if (zsh_h_log_n[zs->zs_unit] >= ZSH_H_LOG_MAX) \
114*0Sstevel@tonic-gate 			zsh_h_log_n[zs->zs_unit] = 0; \
115*0Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]++] = c; \
116*0Sstevel@tonic-gate 		zsh_h_log[zs->zs_unit][zsh_h_log_n[zs->zs_unit]] = '\0'; \
117*0Sstevel@tonic-gate 	}
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate #define	zsh_h_log_clear \
120*0Sstevel@tonic-gate 	{ register char *p; \
121*0Sstevel@tonic-gate 	for (p = &zsh_h_log[zs->zs_unit][ZSH_H_LOG_MAX]; \
122*0Sstevel@tonic-gate 		p >= &zsh_h_log[zs->zs_unit][0]; p--) \
123*0Sstevel@tonic-gate 		*p = '\0'; \
124*0Sstevel@tonic-gate 	zsh_h_log_n[zs->zs_unit] = 0; \
125*0Sstevel@tonic-gate 	}
126*0Sstevel@tonic-gate #endif
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate #define	ZSH_R0_LOG(r0)  { \
129*0Sstevel@tonic-gate 	if (r0 & ZSRR0_RX_READY) zsh_h_log_add('R'); \
130*0Sstevel@tonic-gate 	if (r0 & ZSRR0_TIMER) zsh_h_log_add('Z'); \
131*0Sstevel@tonic-gate 	if (r0 & ZSRR0_TX_READY) zsh_h_log_add('T'); \
132*0Sstevel@tonic-gate 	if (r0 & ZSRR0_CD) zsh_h_log_add('D'); \
133*0Sstevel@tonic-gate 	if (r0 & ZSRR0_SYNC) zsh_h_log_add('S'); \
134*0Sstevel@tonic-gate 	if (r0 & ZSRR0_CTS) zsh_h_log_add('C'); \
135*0Sstevel@tonic-gate 	if (r0 & ZSRR0_TXUNDER) zsh_h_log_add('U'); \
136*0Sstevel@tonic-gate 	if (r0 & ZSRR0_BREAK) zsh_h_log_add('B'); \
137*0Sstevel@tonic-gate 	}
138*0Sstevel@tonic-gate #endif
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate 
141*0Sstevel@tonic-gate char _depends_on[] = "drv/zs";
142*0Sstevel@tonic-gate 
143*0Sstevel@tonic-gate #ifndef	MAXZSH
144*0Sstevel@tonic-gate #define	MAXZSH	2
145*0Sstevel@tonic-gate #define	MAXZSHCLONES	(80)	/* three clone opens per instance */
146*0Sstevel@tonic-gate #endif	/* MAXZSH */
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate int maxzsh = MAXZSH;
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate int zsh_timer_count = 10;
151*0Sstevel@tonic-gate int zsh_default_mru = 1024;
152*0Sstevel@tonic-gate 
153*0Sstevel@tonic-gate struct ser_str *zsh_str = NULL;
154*0Sstevel@tonic-gate unsigned char zsh_usedminor[MAXZSHCLONES];
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate /*
158*0Sstevel@tonic-gate  * The HDLC protocol
159*0Sstevel@tonic-gate  */
160*0Sstevel@tonic-gate int zsh_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
161*0Sstevel@tonic-gate static int  zsh_probe(dev_info_t *dev);
162*0Sstevel@tonic-gate static int  zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
163*0Sstevel@tonic-gate static int  zsh_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
164*0Sstevel@tonic-gate static int  zsh_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
165*0Sstevel@tonic-gate static int  zsh_close(queue_t *rq, int flag);
166*0Sstevel@tonic-gate static void zsh_wput(queue_t *wq, mblk_t *mp);
167*0Sstevel@tonic-gate static int zsh_start(struct zscom *zs, struct syncline *zss);
168*0Sstevel@tonic-gate static void zsh_ioctl(queue_t *wq, mblk_t *mp);
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate static struct module_info hdlc_minfo = {
171*0Sstevel@tonic-gate 	0x5a48,		/* module ID number: "ZH" */
172*0Sstevel@tonic-gate 	"zsh",		/* module name */
173*0Sstevel@tonic-gate 	0,		/* minimum packet size accepted */
174*0Sstevel@tonic-gate 	INFPSZ,		/* maximum packet size accepted */
175*0Sstevel@tonic-gate 	12*1024,	/* queue high water mark (bytes) */
176*0Sstevel@tonic-gate 	4*1024		/* queue low water mark (bytes) */
177*0Sstevel@tonic-gate };
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate static struct qinit hdlc_rinit = {
180*0Sstevel@tonic-gate 	putq,		/* input put procedure */
181*0Sstevel@tonic-gate 	NULL,		/* input service procedure */
182*0Sstevel@tonic-gate 	zsh_open,	/* open procedure */
183*0Sstevel@tonic-gate 	zsh_close,	/* close procedure */
184*0Sstevel@tonic-gate 	NULL,		/* reserved */
185*0Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
186*0Sstevel@tonic-gate 	NULL		/* reserved */
187*0Sstevel@tonic-gate };
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate static struct qinit hdlc_winit = {
190*0Sstevel@tonic-gate 	(int (*)())zsh_wput,	/* output put procedure */
191*0Sstevel@tonic-gate 	NULL,		/* output service procedure */
192*0Sstevel@tonic-gate 	NULL,		/* open procedure */
193*0Sstevel@tonic-gate 	NULL,		/* close procedure */
194*0Sstevel@tonic-gate 	NULL,		/* reserved */
195*0Sstevel@tonic-gate 	&hdlc_minfo,	/* module info */
196*0Sstevel@tonic-gate 	NULL		/* reserved */
197*0Sstevel@tonic-gate };
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate struct streamtab hdlctab = {
200*0Sstevel@tonic-gate 	&hdlc_rinit,	/* initialize read queue */
201*0Sstevel@tonic-gate 	&hdlc_winit,	/* initialize write queue */
202*0Sstevel@tonic-gate 	NULL,		/* mux read qinit */
203*0Sstevel@tonic-gate 	NULL		/* mux write qinit */
204*0Sstevel@tonic-gate };
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(zsh_ops, nulldev, zsh_probe, zsh_attach,
207*0Sstevel@tonic-gate     zsh_detach, nodev, zsh_info, D_MP, &hdlctab);
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate /*
210*0Sstevel@tonic-gate  * This is the loadable module wrapper.
211*0Sstevel@tonic-gate  */
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate #include	<sys/errno.h>
214*0Sstevel@tonic-gate #include	<sys/modctl.h>
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate /*
217*0Sstevel@tonic-gate  * Module linkage information for the kernel.
218*0Sstevel@tonic-gate  */
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate static struct modldrv modldrv = {
221*0Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a driver */
222*0Sstevel@tonic-gate 	"Z8530 serial HDLC drv V%I%",
223*0Sstevel@tonic-gate 	&zsh_ops,	/* our own ops for this module */
224*0Sstevel@tonic-gate };
225*0Sstevel@tonic-gate 
226*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
227*0Sstevel@tonic-gate 	MODREV_1,
228*0Sstevel@tonic-gate 	(void *)&modldrv,
229*0Sstevel@tonic-gate 	NULL
230*0Sstevel@tonic-gate };
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate int
233*0Sstevel@tonic-gate _init(void)
234*0Sstevel@tonic-gate {
235*0Sstevel@tonic-gate 	return (mod_install(&modlinkage));
236*0Sstevel@tonic-gate }
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate int
239*0Sstevel@tonic-gate _fini(void)
240*0Sstevel@tonic-gate {
241*0Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
242*0Sstevel@tonic-gate }
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate int
245*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
246*0Sstevel@tonic-gate {
247*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
248*0Sstevel@tonic-gate }
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate /*
252*0Sstevel@tonic-gate  * The HDLC interrupt entry points.
253*0Sstevel@tonic-gate  */
254*0Sstevel@tonic-gate static void	zsh_txint(struct zscom *zs);
255*0Sstevel@tonic-gate static void	zsh_xsint(struct zscom *zs);
256*0Sstevel@tonic-gate static void	zsh_rxint(struct zscom *zs);
257*0Sstevel@tonic-gate static void	zsh_srint(struct zscom *zs);
258*0Sstevel@tonic-gate static int	zsh_softint(struct zscom *zs);
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate struct zsops zsops_hdlc = {
261*0Sstevel@tonic-gate 	zsh_txint,
262*0Sstevel@tonic-gate 	zsh_xsint,
263*0Sstevel@tonic-gate 	zsh_rxint,
264*0Sstevel@tonic-gate 	zsh_srint,
265*0Sstevel@tonic-gate 	zsh_softint,
266*0Sstevel@tonic-gate 	NULL,
267*0Sstevel@tonic-gate 	NULL
268*0Sstevel@tonic-gate };
269*0Sstevel@tonic-gate 
270*0Sstevel@tonic-gate static int	zsh_program(struct zscom *zs, struct scc_mode *sm);
271*0Sstevel@tonic-gate static void	zsh_setmstat(struct zscom *zs, int event);
272*0Sstevel@tonic-gate static void	zsh_rxbad(struct zscom *zs, struct syncline *zss);
273*0Sstevel@tonic-gate static void	zsh_txbad(struct zscom *zs, struct syncline *zss);
274*0Sstevel@tonic-gate static void	zsh_watchdog(void *);
275*0Sstevel@tonic-gate static void	zsh_callback(void *);
276*0Sstevel@tonic-gate static int	zsh_hdp_ok_or_rts_state(struct zscom *zs, struct syncline *zss);
277*0Sstevel@tonic-gate static void	zsh_init_port(struct zscom *zs, struct syncline *zss);
278*0Sstevel@tonic-gate static int	zsh_setmode(struct zscom *zs, struct syncline *zss,
279*0Sstevel@tonic-gate 			struct scc_mode *sm);
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 
282*0Sstevel@tonic-gate /*
283*0Sstevel@tonic-gate  * The HDLC Driver.
284*0Sstevel@tonic-gate  */
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 
287*0Sstevel@tonic-gate /*
288*0Sstevel@tonic-gate  * Special macros to handle STREAMS operations.
289*0Sstevel@tonic-gate  * These are required to address memory leakage problems.
290*0Sstevel@tonic-gate  * WARNING : the macro do NOT call ZSSETSOFT
291*0Sstevel@tonic-gate  */
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate /*
294*0Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
295*0Sstevel@tonic-gate  */
296*0Sstevel@tonic-gate #define	ZSH_GETBLOCK(zs, allocbcount) \
297*0Sstevel@tonic-gate { \
298*0Sstevel@tonic-gate 	register int n = ZSH_MAX_RSTANDBY; \
299*0Sstevel@tonic-gate 	while (--n >= 0) { \
300*0Sstevel@tonic-gate 	    if (!zss->sl_rstandby[n]) { \
301*0Sstevel@tonic-gate 		if ((zss->sl_rstandby[n] = \
302*0Sstevel@tonic-gate 		    allocb(zss->sl_mru, BPRI_MED)) == NULL) { \
303*0Sstevel@tonic-gate 		    if (zss->sl_bufcid == 0) { \
304*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi); \
305*0Sstevel@tonic-gate 			if (zss->sl_txstate != TX_OFF) { \
306*0Sstevel@tonic-gate 			    mutex_exit(zs->zs_excl_hi); \
307*0Sstevel@tonic-gate 			    zss->sl_bufcid = bufcall(zss->sl_mru, \
308*0Sstevel@tonic-gate 				    BPRI_MED, zsh_callback, zs); \
309*0Sstevel@tonic-gate 			    break; \
310*0Sstevel@tonic-gate 			} else \
311*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi); \
312*0Sstevel@tonic-gate 		    } \
313*0Sstevel@tonic-gate 		} \
314*0Sstevel@tonic-gate 		allocbcount--; \
315*0Sstevel@tonic-gate 	    } \
316*0Sstevel@tonic-gate 	} \
317*0Sstevel@tonic-gate }
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate /*
320*0Sstevel@tonic-gate  * Should be called holding the spin (zs_excl_hi) mutex.
321*0Sstevel@tonic-gate  */
322*0Sstevel@tonic-gate #define	ZSH_ALLOCB(mp) \
323*0Sstevel@tonic-gate { \
324*0Sstevel@tonic-gate 	register int n = ZSH_MAX_RSTANDBY; \
325*0Sstevel@tonic-gate 	mp = NULL; \
326*0Sstevel@tonic-gate 	while (--n >= 0)  { \
327*0Sstevel@tonic-gate 		if ((mp = zss->sl_rstandby[n]) != NULL) { \
328*0Sstevel@tonic-gate 			zss->sl_rstandby[n] = NULL; \
329*0Sstevel@tonic-gate 			break; \
330*0Sstevel@tonic-gate 		} \
331*0Sstevel@tonic-gate 	} \
332*0Sstevel@tonic-gate }
333*0Sstevel@tonic-gate 
334*0Sstevel@tonic-gate #define	ZSH_PUTQ(mp) \
335*0Sstevel@tonic-gate { \
336*0Sstevel@tonic-gate 	register int wptr, rptr;  \
337*0Sstevel@tonic-gate 	wptr = zss->sl_rdone_wptr; \
338*0Sstevel@tonic-gate 	rptr = zss->sl_rdone_rptr; \
339*0Sstevel@tonic-gate 	zss->sl_rdone[wptr] = mp; \
340*0Sstevel@tonic-gate 	if ((wptr) + 1 == ZSH_RDONE_MAX) \
341*0Sstevel@tonic-gate 		zss->sl_rdone_wptr = wptr = 0; \
342*0Sstevel@tonic-gate 	else \
343*0Sstevel@tonic-gate 		zss->sl_rdone_wptr = ++wptr; \
344*0Sstevel@tonic-gate 	if (wptr == rptr) {  /* Should never occur */ \
345*0Sstevel@tonic-gate 		SCC_BIC(1, ZSWR1_INIT); \
346*0Sstevel@tonic-gate 		zss->sl_m_error = ENOSR; \
347*0Sstevel@tonic-gate 		ZSSETSOFT(zs); \
348*0Sstevel@tonic-gate 	} \
349*0Sstevel@tonic-gate }
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate #define	ZSH_FREEMSG(mp) \
352*0Sstevel@tonic-gate { \
353*0Sstevel@tonic-gate 	ZSH_PUTQ(mp); \
354*0Sstevel@tonic-gate }
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate /*
358*0Sstevel@tonic-gate  * Should be called holding only the adaptive (zs_excl) mutex.
359*0Sstevel@tonic-gate  */
360*0Sstevel@tonic-gate #define	ZSH_GETQ(mp) \
361*0Sstevel@tonic-gate { \
362*0Sstevel@tonic-gate 	if (zss->sl_rdone_rptr != zss->sl_rdone_wptr) { \
363*0Sstevel@tonic-gate 		mp = zss->sl_rdone[zss->sl_rdone_rptr++]; \
364*0Sstevel@tonic-gate 		if (zss->sl_rdone_rptr == ZSH_RDONE_MAX) \
365*0Sstevel@tonic-gate 				zss->sl_rdone_rptr = 0; \
366*0Sstevel@tonic-gate 	} else \
367*0Sstevel@tonic-gate 		mp = NULL; \
368*0Sstevel@tonic-gate }
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate #define	ZSH_FLUSHQ \
371*0Sstevel@tonic-gate { \
372*0Sstevel@tonic-gate 	register mblk_t *tmp; \
373*0Sstevel@tonic-gate 	for (;;) { \
374*0Sstevel@tonic-gate 		ZSH_GETQ(tmp); \
375*0Sstevel@tonic-gate 		if (!(tmp)) \
376*0Sstevel@tonic-gate 			break; \
377*0Sstevel@tonic-gate 		freemsg(tmp); \
378*0Sstevel@tonic-gate 	} \
379*0Sstevel@tonic-gate }
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate /*ARGSUSED*/
382*0Sstevel@tonic-gate static int
383*0Sstevel@tonic-gate zsh_probe(dev_info_t *dev)
384*0Sstevel@tonic-gate {
385*0Sstevel@tonic-gate 	return (DDI_PROBE_DONTCARE);
386*0Sstevel@tonic-gate }
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate /*ARGSUSED*/
389*0Sstevel@tonic-gate static int
390*0Sstevel@tonic-gate zsh_attach(dev_info_t *dev, ddi_attach_cmd_t cmd)
391*0Sstevel@tonic-gate {
392*0Sstevel@tonic-gate 	register int	unit;
393*0Sstevel@tonic-gate 	char		name[3] = {
394*0Sstevel@tonic-gate 		'\0', '\0', '\0' 	};
395*0Sstevel@tonic-gate 
396*0Sstevel@tonic-gate 	/*
397*0Sstevel@tonic-gate 	 * Since zsh is a child of the "pseudo" nexus, we can expect the
398*0Sstevel@tonic-gate 	 * attach routine to be called only once.  We need to create all
399*0Sstevel@tonic-gate 	 * necessary devices in one shot.  There is never more than one
400*0Sstevel@tonic-gate 	 * SCC chip that supports zsh devices.
401*0Sstevel@tonic-gate 	 */
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
404*0Sstevel@tonic-gate 		return (DDI_FAILURE);
405*0Sstevel@tonic-gate 	if (zscom == NULL)
406*0Sstevel@tonic-gate 		return (DDI_FAILURE);	/* zsattach not done */
407*0Sstevel@tonic-gate 	unit = 2 * ddi_get_instance(dev);
408*0Sstevel@tonic-gate 	if (unit > 1)
409*0Sstevel@tonic-gate 		return (DDI_FAILURE);	/* only use cpu ports */
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	if (ddi_create_minor_node(dev, "zsh", S_IFCHR,
412*0Sstevel@tonic-gate 	    NULL, DDI_PSEUDO, CLONE_DEV) == DDI_FAILURE) {
413*0Sstevel@tonic-gate 		ddi_remove_minor_node(dev, NULL);
414*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "zsh clone device creation failed.");
415*0Sstevel@tonic-gate 		return (DDI_FAILURE);
416*0Sstevel@tonic-gate 	}
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	for (; unit < maxzsh/2; unit++) {
419*0Sstevel@tonic-gate 		zscom[unit].zs_hdlc_dip = dev;
420*0Sstevel@tonic-gate 
421*0Sstevel@tonic-gate 		(void) sprintf(name, "%d", unit);
422*0Sstevel@tonic-gate 		if (ddi_create_minor_node(dev, name, S_IFCHR,
423*0Sstevel@tonic-gate 		    2*unit, DDI_PSEUDO, NULL) == DDI_FAILURE) {
424*0Sstevel@tonic-gate 			ddi_remove_minor_node(dev, NULL);
425*0Sstevel@tonic-gate 			return (DDI_FAILURE);
426*0Sstevel@tonic-gate 		}
427*0Sstevel@tonic-gate 		unit++;
428*0Sstevel@tonic-gate 		(void) sprintf(name, "%d", unit);
429*0Sstevel@tonic-gate 		if (ddi_create_minor_node(dev, name, S_IFCHR,
430*0Sstevel@tonic-gate 		    2*(unit-1)+1, DDI_PSEUDO, NULL) == DDI_FAILURE) {
431*0Sstevel@tonic-gate 			ddi_remove_minor_node(dev, NULL);
432*0Sstevel@tonic-gate 			return (DDI_FAILURE);
433*0Sstevel@tonic-gate 		}
434*0Sstevel@tonic-gate 	}
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
437*0Sstevel@tonic-gate }
438*0Sstevel@tonic-gate 
439*0Sstevel@tonic-gate /* ARGSUSED */
440*0Sstevel@tonic-gate int
441*0Sstevel@tonic-gate zsh_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
442*0Sstevel@tonic-gate void **result)
443*0Sstevel@tonic-gate {
444*0Sstevel@tonic-gate 	register dev_t dev = (dev_t)arg;
445*0Sstevel@tonic-gate 	register int unit, error;
446*0Sstevel@tonic-gate 	register struct zscom *zs;
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate 	if ((unit = UNIT(dev)) >= nzs)
449*0Sstevel@tonic-gate 		return (DDI_FAILURE);
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	switch (infocmd) {
452*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
453*0Sstevel@tonic-gate 		if (zscom == NULL) {
454*0Sstevel@tonic-gate 			error = DDI_FAILURE;
455*0Sstevel@tonic-gate 		} else {
456*0Sstevel@tonic-gate 			zs = &zscom[unit];
457*0Sstevel@tonic-gate 			*result = zs->zs_hdlc_dip;
458*0Sstevel@tonic-gate 			error = DDI_SUCCESS;
459*0Sstevel@tonic-gate 		}
460*0Sstevel@tonic-gate 		break;
461*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
462*0Sstevel@tonic-gate 		*result = (void *)(unit / 2);
463*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
464*0Sstevel@tonic-gate 		break;
465*0Sstevel@tonic-gate 	default:
466*0Sstevel@tonic-gate 		error = DDI_FAILURE;
467*0Sstevel@tonic-gate 	}
468*0Sstevel@tonic-gate 	return (error);
469*0Sstevel@tonic-gate }
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate static int
472*0Sstevel@tonic-gate zsh_detach(dev_info_t *dev, ddi_detach_cmd_t cmd)
473*0Sstevel@tonic-gate {
474*0Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
475*0Sstevel@tonic-gate 		return (DDI_FAILURE);
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate 	ddi_remove_minor_node(dev, NULL);
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
480*0Sstevel@tonic-gate }
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate static void
483*0Sstevel@tonic-gate zsh_init_port(struct zscom *zs, struct syncline *zss)
484*0Sstevel@tonic-gate {
485*0Sstevel@tonic-gate 	register uchar_t s0;
486*0Sstevel@tonic-gate 
487*0Sstevel@tonic-gate 	SCC_WRITE(3, (ZSWR3_RX_ENABLE | ZSWR3_RXCRC_ENABLE | ZSWR3_RX_8));
488*0Sstevel@tonic-gate 	SCC_WRITE(5, (ZSWR5_TX_8 | ZSWR5_DTR | ZSWR5_TXCRC_ENABLE));
489*0Sstevel@tonic-gate 	zss->sl_rr0 = SCC_READ0();
490*0Sstevel@tonic-gate 	if (zss->sl_flags & SF_FDXPTP) {
491*0Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_TX_ENABLE);
492*0Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_RTS);
493*0Sstevel@tonic-gate 		s0 = SCC_READ0();
494*0Sstevel@tonic-gate 		if ((s0 & ZSRR0_CTS) ||
495*0Sstevel@tonic-gate 		    !(zss->sl_mode.sm_config & (CONN_SIGNAL | CONN_IBM))) {
496*0Sstevel@tonic-gate 			/*
497*0Sstevel@tonic-gate 			 * send msg that CTS is up
498*0Sstevel@tonic-gate 			 */
499*0Sstevel@tonic-gate 			zss->sl_rr0 |= ZSRR0_CTS;
500*0Sstevel@tonic-gate 			zss->sl_txstate = TX_IDLE;
501*0Sstevel@tonic-gate 		} else {
502*0Sstevel@tonic-gate 			zss->sl_flags |= SF_XMT_INPROG;
503*0Sstevel@tonic-gate 			zss->sl_txstate = TX_RTS;
504*0Sstevel@tonic-gate 			zss->sl_rr0 &= ~ZSRR0_CTS;
505*0Sstevel@tonic-gate 			zss->sl_wd_count = zsh_timer_count;
506*0Sstevel@tonic-gate 			if (!zss->sl_wd_id)
507*0Sstevel@tonic-gate 				zss->sl_wd_id = timeout(zsh_watchdog,
508*0Sstevel@tonic-gate 				    zs, SIO_WATCHDOG_TICK);
509*0Sstevel@tonic-gate 		}
510*0Sstevel@tonic-gate 	} else {
511*0Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
512*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
513*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
514*0Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FLUSH_WQ;
515*0Sstevel@tonic-gate 	}
516*0Sstevel@tonic-gate }
517*0Sstevel@tonic-gate 
518*0Sstevel@tonic-gate /*
519*0Sstevel@tonic-gate  * Open routine.
520*0Sstevel@tonic-gate  */
521*0Sstevel@tonic-gate 
522*0Sstevel@tonic-gate /*ARGSUSED*/
523*0Sstevel@tonic-gate static int
524*0Sstevel@tonic-gate zsh_open(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
525*0Sstevel@tonic-gate {
526*0Sstevel@tonic-gate 	register struct zscom *zs;
527*0Sstevel@tonic-gate 	register struct syncline *zss;
528*0Sstevel@tonic-gate 	register struct ser_str *stp;
529*0Sstevel@tonic-gate 	register int	unit;
530*0Sstevel@tonic-gate 	register int 	tmp;
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate 	if (sflag != CLONEOPEN) {
533*0Sstevel@tonic-gate 		if (rq->q_ptr)
534*0Sstevel@tonic-gate 			return (EBUSY);  /* We got a stream that is in use */
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 		unit = UNIT(*dev);
537*0Sstevel@tonic-gate 		if (unit >= maxzsh)
538*0Sstevel@tonic-gate 			return (ENXIO);  /* unit not configured */
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 		if (zscom == NULL)
541*0Sstevel@tonic-gate 			return (ENXIO);  /* device not found by autoconfig */
542*0Sstevel@tonic-gate 		zs = &zscom[unit];
543*0Sstevel@tonic-gate 
544*0Sstevel@tonic-gate 		if (zs->zs_ops == NULL) {
545*0Sstevel@tonic-gate 			return (ENXIO);  /* device not found by autoconfig */
546*0Sstevel@tonic-gate 		}
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_OPEN, "zsh_open:unit = %d", unit);
549*0Sstevel@tonic-gate 
550*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
551*0Sstevel@tonic-gate 		if ((zs->zs_ops != &zsops_null) &&
552*0Sstevel@tonic-gate 		    (zs->zs_ops != &zsops_hdlc)) {
553*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
554*0Sstevel@tonic-gate 			return (EBUSY);	 /* another protocol got here first */
555*0Sstevel@tonic-gate 		}
556*0Sstevel@tonic-gate 
557*0Sstevel@tonic-gate 		/* Mark device as busy (for power management) */
558*0Sstevel@tonic-gate 		(void) pm_busy_component(zs->zs_dip, unit%2+1);
559*0Sstevel@tonic-gate 		(void) ddi_dev_is_needed(zs->zs_dip, unit%2+1, 1);
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 		zsopinit(zs, &zsops_hdlc);
562*0Sstevel@tonic-gate 
563*0Sstevel@tonic-gate 		zss = (struct syncline *)&zscom[unit].zs_priv_str;
564*0Sstevel@tonic-gate 		stp = &zss->sl_stream;
565*0Sstevel@tonic-gate 		stp->str_state = NULL;
566*0Sstevel@tonic-gate 		stp->str_com = (caddr_t)zs;
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 		zss->sl_xhead = NULL;
569*0Sstevel@tonic-gate 		zss->sl_xactb = NULL;
570*0Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
571*0Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
572*0Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
573*0Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
574*0Sstevel@tonic-gate 		zss->sl_rhead = NULL;
575*0Sstevel@tonic-gate 		zss->sl_ractb = NULL;
576*0Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
577*0Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
578*0Sstevel@tonic-gate 		zss->sl_mstat = NULL;
579*0Sstevel@tonic-gate 		zss->sl_xstandby = NULL;
580*0Sstevel@tonic-gate 		zss->sl_wd_id = 0;
581*0Sstevel@tonic-gate 		zss->sl_soft_active = 0;
582*0Sstevel@tonic-gate 		zss->sl_stream.str_rq = NULL;
583*0Sstevel@tonic-gate 
584*0Sstevel@tonic-gate 		zs->zs_priv = (caddr_t)zss;
585*0Sstevel@tonic-gate 
586*0Sstevel@tonic-gate 		zss->sl_mru = zsh_default_mru;
587*0Sstevel@tonic-gate 		tmp = ZSH_MAX_RSTANDBY;
588*0Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, tmp);
589*0Sstevel@tonic-gate 		if (zss->sl_rstandby[0] == NULL) {
590*0Sstevel@tonic-gate 		    cmn_err(CE_WARN, "zsh_open: can't alloc message block");
591*0Sstevel@tonic-gate 		    mutex_exit(zs->zs_excl);
592*0Sstevel@tonic-gate 		    return (ENOSR);
593*0Sstevel@tonic-gate 		}
594*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
595*0Sstevel@tonic-gate 		ZSH_ALLOCB(zss->sl_ractb);
596*0Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;
597*0Sstevel@tonic-gate 		zss->sl_rr0 = SCC_READ0();
598*0Sstevel@tonic-gate 		zss->sl_flags &= (SF_INITIALIZED | SF_FDXPTP);
599*0Sstevel@tonic-gate 		if (zss->sl_flags & SF_INITIALIZED)
600*0Sstevel@tonic-gate 			zsh_init_port(zs, zss);
601*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
602*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
603*0Sstevel@tonic-gate 	} else {   /* CLONEOPEN */
604*0Sstevel@tonic-gate 		mutex_enter(&zs_curr_lock);
605*0Sstevel@tonic-gate 		for (unit = maxzsh; unit < MAXZSHCLONES; unit++)
606*0Sstevel@tonic-gate 			if (!zsh_usedminor[unit]) {
607*0Sstevel@tonic-gate 				zsh_usedminor[unit] = (unsigned char)unit;
608*0Sstevel@tonic-gate 				break;
609*0Sstevel@tonic-gate 			}
610*0Sstevel@tonic-gate 		mutex_exit(&zs_curr_lock);
611*0Sstevel@tonic-gate 		if (unit >= MAXZSHCLONES)	/* no slots available */
612*0Sstevel@tonic-gate 			return (ENODEV);
613*0Sstevel@tonic-gate 		*dev = makedevice(getmajor(*dev), unit);
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 		stp = kmem_zalloc(sizeof (struct ser_str), KM_NOSLEEP);
616*0Sstevel@tonic-gate 		if (stp == NULL) {
617*0Sstevel@tonic-gate 			cmn_err(CE_WARN,
618*0Sstevel@tonic-gate 			    "zsh clone open failed, no memory, rq=%p\n",
619*0Sstevel@tonic-gate 			    (void *)rq);
620*0Sstevel@tonic-gate 			return (ENOMEM);
621*0Sstevel@tonic-gate 		}
622*0Sstevel@tonic-gate 		stp->str_state = STR_CLONE;
623*0Sstevel@tonic-gate 		stp->str_com = NULL;	/* can't determine without ppa */
624*0Sstevel@tonic-gate 	}
625*0Sstevel@tonic-gate 	stp->str_rq = rq;
626*0Sstevel@tonic-gate 	stp->str_inst = unit;
627*0Sstevel@tonic-gate 
628*0Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = (caddr_t)stp;
629*0Sstevel@tonic-gate 	qprocson(rq);
630*0Sstevel@tonic-gate 	return (0);
631*0Sstevel@tonic-gate }
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate /*
634*0Sstevel@tonic-gate  * Close routine.
635*0Sstevel@tonic-gate  */
636*0Sstevel@tonic-gate int zsh_tx_enable_in_close = 0;
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate /*ARGSUSED*/
639*0Sstevel@tonic-gate static int
640*0Sstevel@tonic-gate zsh_close(queue_t *rq, int flag)
641*0Sstevel@tonic-gate {
642*0Sstevel@tonic-gate 	struct ser_str *stp;
643*0Sstevel@tonic-gate 	struct zscom *zs;
644*0Sstevel@tonic-gate 	struct syncline *zss;
645*0Sstevel@tonic-gate 	mblk_t	*mp;
646*0Sstevel@tonic-gate 	int i;
647*0Sstevel@tonic-gate 	timeout_id_t sl_wd_id;
648*0Sstevel@tonic-gate 	bufcall_id_t sl_bufcid;
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 	/*
651*0Sstevel@tonic-gate 	 * Note that a close is only called on the last close of a
652*0Sstevel@tonic-gate 	 * particular stream.  Assume that we need to do it all.
653*0Sstevel@tonic-gate 	 */
654*0Sstevel@tonic-gate 	qprocsoff(rq);				/* no new business after this */
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 	stp = (struct ser_str *)rq->q_ptr;
657*0Sstevel@tonic-gate 	if (stp == NULL)
658*0Sstevel@tonic-gate 		return (0);			/* already been closed once */
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE) {
661*0Sstevel@tonic-gate 		zsh_usedminor[stp->str_inst] = 0;
662*0Sstevel@tonic-gate 	} else {
663*0Sstevel@tonic-gate 		zs = (struct zscom *)stp->str_com;
664*0Sstevel@tonic-gate 		if (zs == NULL)
665*0Sstevel@tonic-gate 			goto out;
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_CLOSE, "zs = %p", zs);
668*0Sstevel@tonic-gate 
669*0Sstevel@tonic-gate 		zss = (struct syncline *)zs->zs_priv;
670*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
671*0Sstevel@tonic-gate 		flushq(WR(rq), FLUSHALL);
672*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
673*0Sstevel@tonic-gate 		if (zss->sl_xstandby) {
674*0Sstevel@tonic-gate 			zss->sl_xstandby->b_wptr = zss->sl_xstandby->b_rptr;
675*0Sstevel@tonic-gate 			ZSH_FREEMSG(zss->sl_xstandby);
676*0Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
677*0Sstevel@tonic-gate 		}
678*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate 		ZSH_FLUSHQ;
681*0Sstevel@tonic-gate 
682*0Sstevel@tonic-gate 		/*
683*0Sstevel@tonic-gate 		 * Stop the Watchdog Timer.
684*0Sstevel@tonic-gate 		 */
685*0Sstevel@tonic-gate 		if ((sl_wd_id = zss->sl_wd_id) != 0)
686*0Sstevel@tonic-gate 			zss->sl_wd_id = 0;
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate 		/*
689*0Sstevel@tonic-gate 		 * Cancel outstanding "bufcall" request.
690*0Sstevel@tonic-gate 		 */
691*0Sstevel@tonic-gate 		if ((sl_bufcid = zss->sl_bufcid) != 0)
692*0Sstevel@tonic-gate 			zss->sl_bufcid = 0;
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
695*0Sstevel@tonic-gate 		if (zs->zs_wr_cur) {
696*0Sstevel@tonic-gate 			zs->zs_wr_cur = NULL;
697*0Sstevel@tonic-gate 			zs->zs_wr_lim = NULL;
698*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
699*0Sstevel@tonic-gate 			ZSDELAY();
700*0Sstevel@tonic-gate 			ZSDELAY();
701*0Sstevel@tonic-gate 		}
702*0Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;	/* so it can't rearm in close */
703*0Sstevel@tonic-gate 
704*0Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
705*0Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
706*0Sstevel@tonic-gate 		SCC_BIC(15,
707*0Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC | ZSR15_CTS));
708*0Sstevel@tonic-gate 		SCC_WRITE(3, 0);		/* Quiesce receiver */
709*0Sstevel@tonic-gate 		if (zsh_tx_enable_in_close && !(zss->sl_flags & SF_FDXPTP)) {
710*0Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_TX_ENABLE);
711*0Sstevel@tonic-gate 		} else
712*0Sstevel@tonic-gate 			SCC_BIC(5, ZSWR5_TX_ENABLE);
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 		SCC_BIC(5,  (ZSWR5_DTR | ZSWR5_RTS | ZSWR5_TXCRC_ENABLE));
715*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);		/* reset TX */
716*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
717*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
718*0Sstevel@tonic-gate 		(void) SCC_READDATA();			/* reset RX */
719*0Sstevel@tonic-gate 		ZSDELAY();
720*0Sstevel@tonic-gate 		(void) SCC_READDATA();
721*0Sstevel@tonic-gate 		ZSDELAY();
722*0Sstevel@tonic-gate 		(void) SCC_READDATA();
723*0Sstevel@tonic-gate 		ZSDELAY();
724*0Sstevel@tonic-gate 
725*0Sstevel@tonic-gate 
726*0Sstevel@tonic-gate 		/*
727*0Sstevel@tonic-gate 		 * Free up everything we ever allocated.
728*0Sstevel@tonic-gate 		 */
729*0Sstevel@tonic-gate 		if ((mp = zss->sl_rhead) != NULL) {
730*0Sstevel@tonic-gate 			zss->sl_ractb = NULL;	/* already freed */
731*0Sstevel@tonic-gate 			zs->zs_rd_cur = NULL;
732*0Sstevel@tonic-gate 			zs->zs_rd_lim = NULL;
733*0Sstevel@tonic-gate 			zss->sl_rhead = NULL;
734*0Sstevel@tonic-gate 		}
735*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
736*0Sstevel@tonic-gate 		if (mp)
737*0Sstevel@tonic-gate 			freemsg(mp);
738*0Sstevel@tonic-gate 
739*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
740*0Sstevel@tonic-gate 		if ((mp = zss->sl_ractb) != NULL) {
741*0Sstevel@tonic-gate 			zs->zs_rd_cur = NULL;
742*0Sstevel@tonic-gate 			zs->zs_rd_lim = NULL;
743*0Sstevel@tonic-gate 			zss->sl_ractb = NULL;
744*0Sstevel@tonic-gate 		}
745*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
746*0Sstevel@tonic-gate 		if (mp)
747*0Sstevel@tonic-gate 			freemsg(mp);
748*0Sstevel@tonic-gate 
749*0Sstevel@tonic-gate 		for (i = 0; i < ZSH_MAX_RSTANDBY; i++) {
750*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
751*0Sstevel@tonic-gate 			mp = zss->sl_rstandby[i];
752*0Sstevel@tonic-gate 			zss->sl_rstandby[i] = NULL;
753*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
754*0Sstevel@tonic-gate 			if (mp)
755*0Sstevel@tonic-gate 				freemsg(mp);
756*0Sstevel@tonic-gate 		}
757*0Sstevel@tonic-gate 
758*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
759*0Sstevel@tonic-gate 		if ((mp = zss->sl_xhead) != NULL) {
760*0Sstevel@tonic-gate 			zss->sl_xhead = NULL;
761*0Sstevel@tonic-gate 			zss->sl_xactb = NULL;
762*0Sstevel@tonic-gate 		}
763*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
764*0Sstevel@tonic-gate 		if (mp)
765*0Sstevel@tonic-gate 			freemsg(mp);
766*0Sstevel@tonic-gate 
767*0Sstevel@tonic-gate 		ZSH_FLUSHQ;
768*0Sstevel@tonic-gate 
769*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
770*0Sstevel@tonic-gate 		if ((mp = zss->sl_xstandby) != NULL)
771*0Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
772*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
773*0Sstevel@tonic-gate 		if (mp)
774*0Sstevel@tonic-gate 			freemsg(mp);
775*0Sstevel@tonic-gate 
776*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
777*0Sstevel@tonic-gate 		if ((mp = zss->sl_mstat) != NULL)
778*0Sstevel@tonic-gate 			zss->sl_mstat = NULL;
779*0Sstevel@tonic-gate 		zss->sl_txstate = TX_OFF;	/* so it can't rearm in close */
780*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
781*0Sstevel@tonic-gate 		if (mp)
782*0Sstevel@tonic-gate 			freemsg(mp);
783*0Sstevel@tonic-gate 
784*0Sstevel@tonic-gate 		zss->sl_stream.str_rq = NULL;
785*0Sstevel@tonic-gate 		zsopinit(zs, &zsops_null);
786*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
787*0Sstevel@tonic-gate 		if (sl_wd_id)
788*0Sstevel@tonic-gate 			(void) untimeout(sl_wd_id);
789*0Sstevel@tonic-gate 		if (sl_bufcid)
790*0Sstevel@tonic-gate 			unbufcall(sl_bufcid);
791*0Sstevel@tonic-gate 		while (zss->sl_soft_active)
792*0Sstevel@tonic-gate 			drv_usecwait(1);
793*0Sstevel@tonic-gate 
794*0Sstevel@tonic-gate 		/* Mark device as available for power management */
795*0Sstevel@tonic-gate 		(void) pm_idle_component(zs->zs_dip, zs->zs_unit%2+1);
796*0Sstevel@tonic-gate 	}
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE)
799*0Sstevel@tonic-gate 		kmem_free(stp, sizeof (struct ser_str));
800*0Sstevel@tonic-gate 
801*0Sstevel@tonic-gate out:
802*0Sstevel@tonic-gate 	rq->q_ptr = WR(rq)->q_ptr = NULL;
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 	return (0);
805*0Sstevel@tonic-gate }
806*0Sstevel@tonic-gate 
807*0Sstevel@tonic-gate static int
808*0Sstevel@tonic-gate zsh_hdp_ok_or_rts_state(struct zscom *zs, struct syncline *zss)
809*0Sstevel@tonic-gate {
810*0Sstevel@tonic-gate 	register uchar_t s0;
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate 	SCC_BIS(15, ZSR15_CTS);
813*0Sstevel@tonic-gate 	SCC_BIS(5, ZSWR5_RTS);
814*0Sstevel@tonic-gate 	s0 = SCC_READ0();
815*0Sstevel@tonic-gate 	if (s0 & ZSRR0_CTS) {
816*0Sstevel@tonic-gate 		SCC_BIS(5, ZSWR5_TX_ENABLE);
817*0Sstevel@tonic-gate 		zss->sl_rr0 |= ZSRR0_CTS;
818*0Sstevel@tonic-gate 		return (1);
819*0Sstevel@tonic-gate 	}
820*0Sstevel@tonic-gate 	zss->sl_flags |= SF_XMT_INPROG;
821*0Sstevel@tonic-gate 	zss->sl_txstate = TX_RTS;
822*0Sstevel@tonic-gate 	zss->sl_rr0 &= ~ZSRR0_CTS;
823*0Sstevel@tonic-gate 	zss->sl_wd_count = zsh_timer_count;
824*0Sstevel@tonic-gate 	return (0);
825*0Sstevel@tonic-gate }
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate /*
828*0Sstevel@tonic-gate  * Put procedure for write queue.
829*0Sstevel@tonic-gate  */
830*0Sstevel@tonic-gate static void
831*0Sstevel@tonic-gate zsh_wput(queue_t *wq, mblk_t *mp)
832*0Sstevel@tonic-gate {
833*0Sstevel@tonic-gate 	register struct ser_str *stp = (struct ser_str *)wq->q_ptr;
834*0Sstevel@tonic-gate 	register struct zscom *zs;
835*0Sstevel@tonic-gate 	register struct syncline *zss = NULL;
836*0Sstevel@tonic-gate 	register ulong_t prim, error = 0;
837*0Sstevel@tonic-gate 	register union DL_primitives *dlp;
838*0Sstevel@tonic-gate 	register int	ppa;
839*0Sstevel@tonic-gate 	register mblk_t *tmp;
840*0Sstevel@tonic-gate 	register struct copyresp	*resp;
841*0Sstevel@tonic-gate 
842*0Sstevel@tonic-gate 	/*
843*0Sstevel@tonic-gate 	 * stp->str_com supplied by open or DLPI attach.
844*0Sstevel@tonic-gate 	 */
845*0Sstevel@tonic-gate 	if (stp == NULL) {
846*0Sstevel@tonic-gate 		freemsg(mp);
847*0Sstevel@tonic-gate 		return;
848*0Sstevel@tonic-gate 	}
849*0Sstevel@tonic-gate 	zs = (struct zscom *)stp->str_com;
850*0Sstevel@tonic-gate 
851*0Sstevel@tonic-gate 	TRACE_0(TR_ZSH, TR_ZSH_WPUT_START, "zsh_wput start");
852*0Sstevel@tonic-gate 
853*0Sstevel@tonic-gate 	if ((mp->b_datap->db_type == M_FLUSH) &&
854*0Sstevel@tonic-gate 		    (stp->str_state == STR_CLONE)) {
855*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
856*0Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
857*0Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
858*0Sstevel@tonic-gate 		}
859*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR)
860*0Sstevel@tonic-gate 			qreply(wq, mp);  /* let the read queues have at it */
861*0Sstevel@tonic-gate 		else
862*0Sstevel@tonic-gate 			freemsg(mp);
863*0Sstevel@tonic-gate 		return;
864*0Sstevel@tonic-gate 	}
865*0Sstevel@tonic-gate 
866*0Sstevel@tonic-gate 	if ((zs == NULL) && (mp->b_datap->db_type != M_PROTO)) {
867*0Sstevel@tonic-gate 	    freemsg(mp);
868*0Sstevel@tonic-gate 	    cmn_err(CE_WARN,
869*0Sstevel@tonic-gate 		"zsh: clone device %d must be attached before use!",
870*0Sstevel@tonic-gate 		stp->str_inst);
871*0Sstevel@tonic-gate 	    (void) putnextctl1(RD(wq), M_ERROR, EPROTO);
872*0Sstevel@tonic-gate 	    return;
873*0Sstevel@tonic-gate 	}
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 	if (stp->str_state == STR_CLONE) {	/* Clone opened, limited. */
876*0Sstevel@tonic-gate 		if ((mp->b_datap->db_type != M_PROTO) &&
877*0Sstevel@tonic-gate 		    (mp->b_datap->db_type != M_IOCTL) &&
878*0Sstevel@tonic-gate 		    (mp->b_datap->db_type != M_IOCDATA)) {
879*0Sstevel@tonic-gate 			freemsg(mp);
880*0Sstevel@tonic-gate 			cmn_err(CE_WARN,
881*0Sstevel@tonic-gate 			    "zsh%x: invalid operation for clone dev.\n",
882*0Sstevel@tonic-gate 			    stp->str_inst);
883*0Sstevel@tonic-gate 			(void) putnextctl1(RD(wq), M_ERROR, EPROTO);
884*0Sstevel@tonic-gate 			return;
885*0Sstevel@tonic-gate 		}
886*0Sstevel@tonic-gate 	} else {
887*0Sstevel@tonic-gate 		zss = (struct syncline *)zs->zs_priv;
888*0Sstevel@tonic-gate 	}
889*0Sstevel@tonic-gate 
890*0Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate 	case M_DATA:
893*0Sstevel@tonic-gate 		/*
894*0Sstevel@tonic-gate 		 * Queue the message up to be transmitted.
895*0Sstevel@tonic-gate 		 * Set "in progress" flag and call the start routine.
896*0Sstevel@tonic-gate 		 */
897*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
898*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_INITIALIZED)) {
899*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
900*0Sstevel@tonic-gate 			cmn_err(CE_WARN,
901*0Sstevel@tonic-gate 			    "zsh%x not initialized, can't send message",
902*0Sstevel@tonic-gate 			    zs->zs_unit);
903*0Sstevel@tonic-gate 			freemsg(mp);
904*0Sstevel@tonic-gate 			(void) putnextctl1(RD(wq), M_ERROR, ECOMM);
905*0Sstevel@tonic-gate 			return;
906*0Sstevel@tonic-gate 		}
907*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
908*0Sstevel@tonic-gate 		if (zs->zs_flags & ZS_NEEDSOFT) {
909*0Sstevel@tonic-gate 			zs->zs_flags &= ~ZS_NEEDSOFT;
910*0Sstevel@tonic-gate 			(void) zsh_softint(zs);
911*0Sstevel@tonic-gate 		}
912*0Sstevel@tonic-gate 		while (mp->b_wptr == mp->b_rptr) {
913*0Sstevel@tonic-gate 			register mblk_t *mp1;
914*0Sstevel@tonic-gate 			mp1 = unlinkb(mp);
915*0Sstevel@tonic-gate 			freemsg(mp);
916*0Sstevel@tonic-gate 			mp = mp1;
917*0Sstevel@tonic-gate 			if (mp == NULL)
918*0Sstevel@tonic-gate 				return;
919*0Sstevel@tonic-gate 		}
920*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
921*0Sstevel@tonic-gate 		(void) putq(wq, mp);
922*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
923*0Sstevel@tonic-gate 		if (zss->sl_flags & SF_FLUSH_WQ) {
924*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
925*0Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
926*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
927*0Sstevel@tonic-gate 
928*0Sstevel@tonic-gate 			TRACE_1(TR_ZSH, TR_ZSH_WPUT_END,
929*0Sstevel@tonic-gate 			    "zsh_wput end: zs = %p", zs);
930*0Sstevel@tonic-gate 
931*0Sstevel@tonic-gate 			return;
932*0Sstevel@tonic-gate 		}
933*0Sstevel@tonic-gate 		tmp = NULL;
934*0Sstevel@tonic-gate again:
935*0Sstevel@tonic-gate 		if (!zss->sl_xstandby) {
936*0Sstevel@tonic-gate 			if (tmp)
937*0Sstevel@tonic-gate 				zss->sl_xstandby = tmp;
938*0Sstevel@tonic-gate 			else {
939*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
940*0Sstevel@tonic-gate 				tmp = getq(wq);
941*0Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
942*0Sstevel@tonic-gate 				if (tmp)
943*0Sstevel@tonic-gate 					goto again;
944*0Sstevel@tonic-gate 			}
945*0Sstevel@tonic-gate 		} else if (tmp) {
946*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
947*0Sstevel@tonic-gate 			(void) putbq(wq, tmp);
948*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
949*0Sstevel@tonic-gate 		}
950*0Sstevel@tonic-gate 
951*0Sstevel@tonic-gate 		if (zss->sl_flags & SF_XMT_INPROG) {
952*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
953*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate 			TRACE_1(TR_ZSH, TR_ZSH_WPUT_END,
956*0Sstevel@tonic-gate 			    "zsh_wput end: zs = %p", zs);
957*0Sstevel@tonic-gate 
958*0Sstevel@tonic-gate 			return;
959*0Sstevel@tonic-gate 		}
960*0Sstevel@tonic-gate 
961*0Sstevel@tonic-gate 		if (!zss->sl_wd_id) {
962*0Sstevel@tonic-gate 			zss->sl_wd_count = zsh_timer_count;
963*0Sstevel@tonic-gate 			zss->sl_txstate = TX_IDLE;
964*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
965*0Sstevel@tonic-gate 			zss->sl_wd_id = timeout(zsh_watchdog, zs,
966*0Sstevel@tonic-gate 			    SIO_WATCHDOG_TICK);
967*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
968*0Sstevel@tonic-gate 		}
969*0Sstevel@tonic-gate 
970*0Sstevel@tonic-gate 		zss->sl_flags |= SF_XMT_INPROG;
971*0Sstevel@tonic-gate 		if ((zss->sl_flags & SF_FDXPTP) ||
972*0Sstevel@tonic-gate 		    zsh_hdp_ok_or_rts_state(zs, zss))
973*0Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
974*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
975*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
976*0Sstevel@tonic-gate 		break;
977*0Sstevel@tonic-gate 
978*0Sstevel@tonic-gate 	case M_PROTO:
979*0Sstevel@tonic-gate 		/*
980*0Sstevel@tonic-gate 		 * Here is where a clone device finds out about the
981*0Sstevel@tonic-gate 		 * hardware it is going to attach to.  The request is
982*0Sstevel@tonic-gate 		 * validated and a ppa is extracted from it and validated.
983*0Sstevel@tonic-gate 		 * This number is used to index the hardware data structure
984*0Sstevel@tonic-gate 		 * and the protocol data structure, in case the latter
985*0Sstevel@tonic-gate 		 * was not provided by a data-path open before this.
986*0Sstevel@tonic-gate 		 */
987*0Sstevel@tonic-gate 		if (stp->str_state != STR_CLONE) {
988*0Sstevel@tonic-gate 			freemsg(mp);
989*0Sstevel@tonic-gate 			return;
990*0Sstevel@tonic-gate 		}
991*0Sstevel@tonic-gate 
992*0Sstevel@tonic-gate 		if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) {
993*0Sstevel@tonic-gate 			prim = DL_ATTACH_REQ;
994*0Sstevel@tonic-gate 			error = DL_BADPRIM;
995*0Sstevel@tonic-gate 			goto end_proto;
996*0Sstevel@tonic-gate 		}
997*0Sstevel@tonic-gate 		dlp = (union DL_primitives *)mp->b_rptr;
998*0Sstevel@tonic-gate 		prim = dlp->dl_primitive;
999*0Sstevel@tonic-gate 		if (prim != DL_ATTACH_REQ) {
1000*0Sstevel@tonic-gate 			error = DL_BADPRIM;
1001*0Sstevel@tonic-gate 			goto end_proto;
1002*0Sstevel@tonic-gate 		}
1003*0Sstevel@tonic-gate 		ppa = dlp->attach_req.dl_ppa;
1004*0Sstevel@tonic-gate 		ppa = (ppa%2) ? ((ppa-1)*2 +1) : (ppa*2);
1005*0Sstevel@tonic-gate 		if (ppa >= maxzsh) {
1006*0Sstevel@tonic-gate 			error = DL_BADPPA;
1007*0Sstevel@tonic-gate 			goto end_proto;
1008*0Sstevel@tonic-gate 		}
1009*0Sstevel@tonic-gate 		zs = &zscom[ppa];
1010*0Sstevel@tonic-gate 		if (zs->zs_ops == NULL) {
1011*0Sstevel@tonic-gate 			error = ENXIO;
1012*0Sstevel@tonic-gate 			goto end_proto;
1013*0Sstevel@tonic-gate 		}
1014*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl);
1015*0Sstevel@tonic-gate 		if ((zs->zs_ops != &zsops_null) &&
1016*0Sstevel@tonic-gate 		    (zs->zs_ops != &zsops_hdlc)) {
1017*0Sstevel@tonic-gate 			/*
1018*0Sstevel@tonic-gate 			 * another protocol got here first
1019*0Sstevel@tonic-gate 			 */
1020*0Sstevel@tonic-gate 			error = (EBUSY);
1021*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
1022*0Sstevel@tonic-gate 			goto end_proto;
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 		}
1025*0Sstevel@tonic-gate 
1026*0Sstevel@tonic-gate 		stp->str_com = (caddr_t)zs;
1027*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
1028*0Sstevel@tonic-gate end_proto:
1029*0Sstevel@tonic-gate 		if (error)
1030*0Sstevel@tonic-gate 			dlerrorack(wq, mp, prim, error, 0);
1031*0Sstevel@tonic-gate 		else
1032*0Sstevel@tonic-gate 			dlokack(wq, mp, DL_ATTACH_REQ);
1033*0Sstevel@tonic-gate 		break;
1034*0Sstevel@tonic-gate 
1035*0Sstevel@tonic-gate 	case M_IOCTL:
1036*0Sstevel@tonic-gate 		zsh_ioctl(wq, mp);
1037*0Sstevel@tonic-gate 		break;
1038*0Sstevel@tonic-gate 
1039*0Sstevel@tonic-gate 	case M_IOCDATA:
1040*0Sstevel@tonic-gate 		resp = (struct copyresp *)mp->b_rptr;
1041*0Sstevel@tonic-gate 		if (resp->cp_rval) {
1042*0Sstevel@tonic-gate 			/*
1043*0Sstevel@tonic-gate 			 * Just free message on failure.
1044*0Sstevel@tonic-gate 			 */
1045*0Sstevel@tonic-gate 			freemsg(mp);
1046*0Sstevel@tonic-gate 			break;
1047*0Sstevel@tonic-gate 		}
1048*0Sstevel@tonic-gate 
1049*0Sstevel@tonic-gate 		switch (resp->cp_cmd) {
1050*0Sstevel@tonic-gate 
1051*0Sstevel@tonic-gate 		case S_IOCGETMODE:
1052*0Sstevel@tonic-gate 		case S_IOCGETSTATS:
1053*0Sstevel@tonic-gate 		case S_IOCGETSPEED:
1054*0Sstevel@tonic-gate 		case S_IOCGETMCTL:
1055*0Sstevel@tonic-gate 		case S_IOCGETMRU:
1056*0Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
1057*0Sstevel@tonic-gate 			qreply(wq, mp);
1058*0Sstevel@tonic-gate 			break;
1059*0Sstevel@tonic-gate 
1060*0Sstevel@tonic-gate 		case S_IOCSETMODE:
1061*0Sstevel@tonic-gate 			zss  = (struct syncline *)&zs->zs_priv_str;
1062*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
1063*0Sstevel@tonic-gate 			error = zsh_setmode(zs, zss,
1064*0Sstevel@tonic-gate 				(struct scc_mode *)mp->b_cont->b_rptr);
1065*0Sstevel@tonic-gate 			if (error) {
1066*0Sstevel@tonic-gate 				register struct iocblk  *iocp =
1067*0Sstevel@tonic-gate 					(struct iocblk *)mp->b_rptr;
1068*0Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
1069*0Sstevel@tonic-gate 				iocp->ioc_error = error;
1070*0Sstevel@tonic-gate 			} else
1071*0Sstevel@tonic-gate 				mioc2ack(mp, NULL, 0, 0);
1072*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
1073*0Sstevel@tonic-gate 			qreply(wq, mp);
1074*0Sstevel@tonic-gate 			break;
1075*0Sstevel@tonic-gate 
1076*0Sstevel@tonic-gate 		case S_IOCSETMRU:
1077*0Sstevel@tonic-gate 			zss  = (struct syncline *)&zs->zs_priv_str;
1078*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
1079*0Sstevel@tonic-gate 			zss->sl_mru = *(int *)mp->b_cont->b_rptr;
1080*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
1081*0Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
1082*0Sstevel@tonic-gate 			qreply(wq, mp);
1083*0Sstevel@tonic-gate 			break;
1084*0Sstevel@tonic-gate 		default:
1085*0Sstevel@tonic-gate 			freemsg(mp);
1086*0Sstevel@tonic-gate 		}
1087*0Sstevel@tonic-gate 		break;
1088*0Sstevel@tonic-gate 
1089*0Sstevel@tonic-gate 		/*
1090*0Sstevel@tonic-gate 		 * We're at the bottom of the food chain, so we flush our
1091*0Sstevel@tonic-gate 		 * write queue, clear the FLUSHW bit so it doesn't go round
1092*0Sstevel@tonic-gate 		 * and round forever, then flush our read queue (since there's
1093*0Sstevel@tonic-gate 		 * no read put procedure down here) and pass it up for any
1094*0Sstevel@tonic-gate 		 * higher modules to deal with in their own way.
1095*0Sstevel@tonic-gate 		 */
1096*0Sstevel@tonic-gate 	case M_FLUSH:
1097*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
1098*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
1099*0Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
1100*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl_hi);
1101*0Sstevel@tonic-gate 			tmp = zss->sl_xstandby;
1102*0Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
1103*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
1104*0Sstevel@tonic-gate 			if (tmp)
1105*0Sstevel@tonic-gate 				freemsg(tmp);
1106*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
1107*0Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
1108*0Sstevel@tonic-gate 		}
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
1111*0Sstevel@tonic-gate 			mutex_enter(zs->zs_excl);
1112*0Sstevel@tonic-gate 			ZSH_FLUSHQ;
1113*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl);
1114*0Sstevel@tonic-gate 			qreply(wq, mp);  /* let the read queues have at it */
1115*0Sstevel@tonic-gate 		} else
1116*0Sstevel@tonic-gate 			freemsg(mp);
1117*0Sstevel@tonic-gate 		break;
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate 	default:
1120*0Sstevel@tonic-gate 		/*
1121*0Sstevel@tonic-gate 		 * "No, I don't want a subscription to Chain Store Age,
1122*0Sstevel@tonic-gate 		 * thank you anyway."
1123*0Sstevel@tonic-gate 		 */
1124*0Sstevel@tonic-gate 		freemsg(mp);
1125*0Sstevel@tonic-gate 		break;
1126*0Sstevel@tonic-gate 	}
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_WPUT_END, "zsh_wput end: zs = %p", zs);
1129*0Sstevel@tonic-gate }
1130*0Sstevel@tonic-gate 
1131*0Sstevel@tonic-gate /*
1132*0Sstevel@tonic-gate  * Get the next message from the write queue, set up the necessary pointers,
1133*0Sstevel@tonic-gate  * state info, etc., and start the transmit "engine" by sending the first
1134*0Sstevel@tonic-gate  * character.  We'll then rotate through txint until done, then get an xsint.
1135*0Sstevel@tonic-gate  */
1136*0Sstevel@tonic-gate static int
1137*0Sstevel@tonic-gate zsh_start(struct zscom *zs, struct syncline *zss)
1138*0Sstevel@tonic-gate {
1139*0Sstevel@tonic-gate 	register mblk_t *mp;
1140*0Sstevel@tonic-gate 	register uchar_t *wptr;
1141*0Sstevel@tonic-gate 	register uchar_t *rptr;
1142*0Sstevel@tonic-gate 	register uchar_t sl_flags = zss->sl_flags;
1143*0Sstevel@tonic-gate 
1144*0Sstevel@tonic-gate 	/*
1145*0Sstevel@tonic-gate 	 * Attempt to grab the next M_DATA message off the queue (that's
1146*0Sstevel@tonic-gate 	 * all that will be left after wput) and begin transmission.
1147*0Sstevel@tonic-gate 	 * This routine is normally called after completion of a previous
1148*0Sstevel@tonic-gate 	 * frame, or when zsh_wput gets a new message.  If we are in a
1149*0Sstevel@tonic-gate 	 * mode that put us in the TX_RTS state, waiting for CTS, and CTS
1150*0Sstevel@tonic-gate 	 * is not up yet, we have no business here.  Ditto if we're in
1151*0Sstevel@tonic-gate 	 * either the TX_ACTIVE or TX_CRC states.  In these cases we
1152*0Sstevel@tonic-gate 	 * don't clear SF_CALLSTART, so we don't forget there's work to do.
1153*0Sstevel@tonic-gate 	 */
1154*0Sstevel@tonic-gate 
1155*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_START,
1156*0Sstevel@tonic-gate 	    "zsh_start start: zs = %p", zs);
1157*0Sstevel@tonic-gate 
1158*0Sstevel@tonic-gate 	if (sl_flags & SF_PHONY) {
1159*0Sstevel@tonic-gate 		sl_flags &= ~SF_PHONY;
1160*0Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
1161*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
1162*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1163*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
1164*0Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
1165*0Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
1166*0Sstevel@tonic-gate 		/*
1167*0Sstevel@tonic-gate 		 * if we get another msg by chance zsh_watchog will start
1168*0Sstevel@tonic-gate 		 */
1169*0Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
1170*0Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
1171*0Sstevel@tonic-gate 
1172*0Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
1173*0Sstevel@tonic-gate 		    "zsh_start end: zs = %d", zs);
1174*0Sstevel@tonic-gate 
1175*0Sstevel@tonic-gate 		return (0);
1176*0Sstevel@tonic-gate 	}
1177*0Sstevel@tonic-gate 	mp = zss->sl_xstandby;
1178*0Sstevel@tonic-gate 	if (mp == NULL) {
1179*0Sstevel@tonic-gate 		if (!(sl_flags & SF_FDXPTP)) {
1180*0Sstevel@tonic-gate 			sl_flags |= SF_PHONY;
1181*0Sstevel@tonic-gate 			ZSH_ALLOCB(mp);
1182*0Sstevel@tonic-gate 			if (!mp)
1183*0Sstevel@tonic-gate 				return (0);
1184*0Sstevel@tonic-gate 			mp->b_datap->db_type = M_RSE;
1185*0Sstevel@tonic-gate 			mp->b_wptr = mp->b_rptr + 1;
1186*0Sstevel@tonic-gate 			goto transmit;
1187*0Sstevel@tonic-gate 		}
1188*0Sstevel@tonic-gate 		sl_flags &= ~SF_XMT_INPROG;
1189*0Sstevel@tonic-gate 		zss->sl_flags = sl_flags;
1190*0Sstevel@tonic-gate 
1191*0Sstevel@tonic-gate 		TRACE_1(TR_ZSH, TR_ZSH_START_END,
1192*0Sstevel@tonic-gate 		    "zsh_start end: zs = %p", zs);
1193*0Sstevel@tonic-gate 
1194*0Sstevel@tonic-gate 		return (0);
1195*0Sstevel@tonic-gate 	}
1196*0Sstevel@tonic-gate 
1197*0Sstevel@tonic-gate transmit:
1198*0Sstevel@tonic-gate 	zss->sl_xstandby = NULL;
1199*0Sstevel@tonic-gate 	rptr = mp->b_rptr;
1200*0Sstevel@tonic-gate 	wptr = mp->b_wptr;
1201*0Sstevel@tonic-gate 	ZSSETSOFT(zs);
1202*0Sstevel@tonic-gate 
1203*0Sstevel@tonic-gate #ifdef ZSH_DEBUG
1204*0Sstevel@tonic-gate 	if (zss->sl_xhead || zss->sl_xactb) {
1205*0Sstevel@tonic-gate 		debug_enter("xhead1");
1206*0Sstevel@tonic-gate 	}
1207*0Sstevel@tonic-gate #endif
1208*0Sstevel@tonic-gate 
1209*0Sstevel@tonic-gate 	zss->sl_xhead = mp;
1210*0Sstevel@tonic-gate 	zss->sl_xactb = mp;
1211*0Sstevel@tonic-gate 	zss->sl_wd_count = zsh_timer_count;
1212*0Sstevel@tonic-gate 	zss->sl_txstate = TX_ACTIVE;
1213*0Sstevel@tonic-gate 	zss->sl_ocnt = 0;
1214*0Sstevel@tonic-gate 	SCC_BIS(10, ZSWR10_UNDERRUN_ABORT);	/* abort on underrun */
1215*0Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_TXCRC);		/* reset transmit CRC */
1216*0Sstevel@tonic-gate 	zss->sl_ocnt = wptr - rptr;
1217*0Sstevel@tonic-gate 	mp->b_wptr = rptr; /* to tell soft to free this msg */
1218*0Sstevel@tonic-gate 	SCC_WRITEDATA(*rptr++);    /* resets TXINT */
1219*0Sstevel@tonic-gate 	zs->zs_wr_cur = rptr;
1220*0Sstevel@tonic-gate 	zs->zs_wr_lim = wptr;
1221*0Sstevel@tonic-gate 
1222*0Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_EOM);
1223*0Sstevel@tonic-gate 
1224*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_START_END,
1225*0Sstevel@tonic-gate 	    "zsh_start end: zs = %p", zs);
1226*0Sstevel@tonic-gate 
1227*0Sstevel@tonic-gate 	zss->sl_flags = sl_flags;
1228*0Sstevel@tonic-gate 	return (1);
1229*0Sstevel@tonic-gate }
1230*0Sstevel@tonic-gate 
1231*0Sstevel@tonic-gate 
1232*0Sstevel@tonic-gate /*
1233*0Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
1234*0Sstevel@tonic-gate  */
1235*0Sstevel@tonic-gate static void
1236*0Sstevel@tonic-gate zsh_ioctl(queue_t *wq, mblk_t *mp)
1237*0Sstevel@tonic-gate {
1238*0Sstevel@tonic-gate 	register struct ser_str *stp = (struct ser_str *)wq->q_ptr;
1239*0Sstevel@tonic-gate 	register struct zscom *zs = (struct zscom *)stp->str_com;
1240*0Sstevel@tonic-gate 	register struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
1241*0Sstevel@tonic-gate 	register struct iocblk   *iocp = (struct iocblk *)mp->b_rptr;
1242*0Sstevel@tonic-gate 	register struct scc_mode *sm;
1243*0Sstevel@tonic-gate 	register struct sl_stats *st;
1244*0Sstevel@tonic-gate 	register uchar_t	 *msignals;
1245*0Sstevel@tonic-gate 	register mblk_t		 *tmp;
1246*0Sstevel@tonic-gate 	register int		 error = 0;
1247*0Sstevel@tonic-gate 
1248*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
1249*0Sstevel@tonic-gate 	if ((zs->zs_ops != &zsops_null) &&
1250*0Sstevel@tonic-gate 	    (zs->zs_ops != &zsops_hdlc)) {
1251*0Sstevel@tonic-gate 		/*
1252*0Sstevel@tonic-gate 		 * another protocol got here first
1253*0Sstevel@tonic-gate 		 */
1254*0Sstevel@tonic-gate 		error = (EBUSY);
1255*0Sstevel@tonic-gate 		goto end_zsh_ioctl;
1256*0Sstevel@tonic-gate 	}
1257*0Sstevel@tonic-gate 
1258*0Sstevel@tonic-gate 
1259*0Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
1260*0Sstevel@tonic-gate 
1261*0Sstevel@tonic-gate 	case S_IOCGETMODE:
1262*0Sstevel@tonic-gate 		tmp = allocb(sizeof (struct scc_mode), BPRI_MED);
1263*0Sstevel@tonic-gate 		if (tmp == NULL) {
1264*0Sstevel@tonic-gate 			error = EAGAIN;
1265*0Sstevel@tonic-gate 			break;
1266*0Sstevel@tonic-gate 		}
1267*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
1268*0Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct scc_mode), 0);
1269*0Sstevel@tonic-gate 		else
1270*0Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct scc_mode), NULL, tmp);
1271*0Sstevel@tonic-gate 		sm = (struct scc_mode *)mp->b_cont->b_rptr;
1272*0Sstevel@tonic-gate 		bcopy(&zss->sl_mode, sm, sizeof (struct scc_mode));
1273*0Sstevel@tonic-gate 		break;
1274*0Sstevel@tonic-gate 
1275*0Sstevel@tonic-gate 	case S_IOCGETSTATS:
1276*0Sstevel@tonic-gate 		tmp = allocb(sizeof (struct sl_stats), BPRI_MED);
1277*0Sstevel@tonic-gate 		if (tmp == NULL) {
1278*0Sstevel@tonic-gate 			error = EAGAIN;
1279*0Sstevel@tonic-gate 			break;
1280*0Sstevel@tonic-gate 		}
1281*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
1282*0Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (struct sl_stats), 0);
1283*0Sstevel@tonic-gate 		else
1284*0Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (struct sl_stats), NULL, tmp);
1285*0Sstevel@tonic-gate 		st = (struct sl_stats *)mp->b_cont->b_rptr;
1286*0Sstevel@tonic-gate 		bcopy(&zss->sl_st, st, sizeof (struct sl_stats));
1287*0Sstevel@tonic-gate 		break;
1288*0Sstevel@tonic-gate 
1289*0Sstevel@tonic-gate 	case S_IOCGETSPEED:
1290*0Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
1291*0Sstevel@tonic-gate 		if (tmp == NULL) {
1292*0Sstevel@tonic-gate 			error = EAGAIN;
1293*0Sstevel@tonic-gate 			break;
1294*0Sstevel@tonic-gate 		}
1295*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
1296*0Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
1297*0Sstevel@tonic-gate 		else
1298*0Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
1299*0Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mode.sm_baudrate;
1300*0Sstevel@tonic-gate 		break;
1301*0Sstevel@tonic-gate 
1302*0Sstevel@tonic-gate 	case S_IOCGETMCTL:
1303*0Sstevel@tonic-gate 		tmp = allocb(sizeof (char), BPRI_MED);
1304*0Sstevel@tonic-gate 		if (tmp == NULL) {
1305*0Sstevel@tonic-gate 			error = EAGAIN;
1306*0Sstevel@tonic-gate 			break;
1307*0Sstevel@tonic-gate 		}
1308*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
1309*0Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (char), 0);
1310*0Sstevel@tonic-gate 		else
1311*0Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (char), NULL, tmp);
1312*0Sstevel@tonic-gate 		msignals = (uchar_t *)mp->b_cont->b_rptr;
1313*0Sstevel@tonic-gate 		*msignals = zss->sl_rr0 & (ZSRR0_CD | ZSRR0_CTS);
1314*0Sstevel@tonic-gate 		break;
1315*0Sstevel@tonic-gate 
1316*0Sstevel@tonic-gate 	case S_IOCGETMRU:
1317*0Sstevel@tonic-gate 		tmp = allocb(sizeof (int), BPRI_MED);
1318*0Sstevel@tonic-gate 		if (tmp == NULL) {
1319*0Sstevel@tonic-gate 			error = EAGAIN;
1320*0Sstevel@tonic-gate 			break;
1321*0Sstevel@tonic-gate 		}
1322*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT)
1323*0Sstevel@tonic-gate 			mioc2ack(mp, tmp, sizeof (int), 0);
1324*0Sstevel@tonic-gate 		else
1325*0Sstevel@tonic-gate 			mcopyout(mp, NULL, sizeof (int), NULL, tmp);
1326*0Sstevel@tonic-gate 		*(int *)mp->b_cont->b_rptr = zss->sl_mru;
1327*0Sstevel@tonic-gate 		break;
1328*0Sstevel@tonic-gate 
1329*0Sstevel@tonic-gate 	case S_IOCSETMODE:
1330*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
1331*0Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (struct scc_mode));
1332*0Sstevel@tonic-gate 			if (error != 0)
1333*0Sstevel@tonic-gate 				break;
1334*0Sstevel@tonic-gate 			error = zsh_setmode(zs, zss,
1335*0Sstevel@tonic-gate 			    (struct scc_mode *)mp->b_cont->b_rptr);
1336*0Sstevel@tonic-gate 			if (error == 0)
1337*0Sstevel@tonic-gate 				mioc2ack(mp, NULL, 0, 0);
1338*0Sstevel@tonic-gate 		} else
1339*0Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (struct scc_mode), NULL);
1340*0Sstevel@tonic-gate 		break;
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 	case S_IOCCLRSTATS:
1343*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
1344*0Sstevel@tonic-gate 		bzero(&zss->sl_st, sizeof (struct sl_stats));
1345*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
1346*0Sstevel@tonic-gate 		mioc2ack(mp, NULL, 0, 0);
1347*0Sstevel@tonic-gate 		break;
1348*0Sstevel@tonic-gate 
1349*0Sstevel@tonic-gate 	case S_IOCSETMRU:
1350*0Sstevel@tonic-gate 		if (iocp->ioc_count != TRANSPARENT) {
1351*0Sstevel@tonic-gate 			error = miocpullup(mp, sizeof (int));
1352*0Sstevel@tonic-gate 			if (error != 0)
1353*0Sstevel@tonic-gate 				break;
1354*0Sstevel@tonic-gate 			zss->sl_mru = *(int *)mp->b_cont->b_rptr;
1355*0Sstevel@tonic-gate 			mioc2ack(mp, NULL, 0, 0);
1356*0Sstevel@tonic-gate 		} else
1357*0Sstevel@tonic-gate 			mcopyin(mp, NULL, sizeof (int), NULL);
1358*0Sstevel@tonic-gate 		break;
1359*0Sstevel@tonic-gate 
1360*0Sstevel@tonic-gate 	case S_IOCSETDTR:
1361*0Sstevel@tonic-gate 		/*
1362*0Sstevel@tonic-gate 		 * The first integer of the M_DATA block that should
1363*0Sstevel@tonic-gate 		 * follow indicate if DTR must be set or reset
1364*0Sstevel@tonic-gate 		 */
1365*0Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (int));
1366*0Sstevel@tonic-gate 		if (error != 0)
1367*0Sstevel@tonic-gate 			break;
1368*0Sstevel@tonic-gate 
1369*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
1370*0Sstevel@tonic-gate 		if (*(int *)mp->b_cont->b_rptr != 0)
1371*0Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIS);
1372*0Sstevel@tonic-gate 		else
1373*0Sstevel@tonic-gate 			(void) zsmctl(zs, ZSWR5_DTR, DMBIC);
1374*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
1375*0Sstevel@tonic-gate 		break;
1376*0Sstevel@tonic-gate 
1377*0Sstevel@tonic-gate 	default:
1378*0Sstevel@tonic-gate 		error = EINVAL;
1379*0Sstevel@tonic-gate 
1380*0Sstevel@tonic-gate 	}
1381*0Sstevel@tonic-gate end_zsh_ioctl:
1382*0Sstevel@tonic-gate 	iocp->ioc_error = error;
1383*0Sstevel@tonic-gate 	mp->b_datap->db_type = (error) ? M_IOCNAK : M_IOCACK;
1384*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
1385*0Sstevel@tonic-gate 	qreply(wq, mp);
1386*0Sstevel@tonic-gate }
1387*0Sstevel@tonic-gate 
1388*0Sstevel@tonic-gate /*
1389*0Sstevel@tonic-gate  * Set the mode of the zsh port
1390*0Sstevel@tonic-gate  */
1391*0Sstevel@tonic-gate 
1392*0Sstevel@tonic-gate int
1393*0Sstevel@tonic-gate zsh_setmode(struct zscom *zs, struct syncline *zss, struct scc_mode *sm)
1394*0Sstevel@tonic-gate {
1395*0Sstevel@tonic-gate 	register int error = 0;
1396*0Sstevel@tonic-gate 	register mblk_t *mp;
1397*0Sstevel@tonic-gate 
1398*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
1399*0Sstevel@tonic-gate 	if (sm->sm_rxclock == RXC_IS_PLL) {
1400*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
1401*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
1402*0Sstevel@tonic-gate 		return (EINVAL);		/* not supported */
1403*0Sstevel@tonic-gate 	} else {
1404*0Sstevel@tonic-gate 		if (((zss->sl_mode.sm_config ^ sm->sm_config) &
1405*0Sstevel@tonic-gate 			CONN_SIGNAL) != 0) { /* Changing, going... */
1406*0Sstevel@tonic-gate 			if (sm->sm_config & CONN_SIGNAL) { /* ...up. */
1407*0Sstevel@tonic-gate 				if (zss->sl_mstat == NULL) {
1408*0Sstevel@tonic-gate 				    mutex_exit(zs->zs_excl_hi);
1409*0Sstevel@tonic-gate 				    mp = allocb(
1410*0Sstevel@tonic-gate 					sizeof (struct sl_status), BPRI_MED);
1411*0Sstevel@tonic-gate 				    mutex_enter(zs->zs_excl_hi);
1412*0Sstevel@tonic-gate 				    zss->sl_mstat = mp;
1413*0Sstevel@tonic-gate 				}
1414*0Sstevel@tonic-gate 			} else {			/* ...down. */
1415*0Sstevel@tonic-gate 				if ((mp = zss->sl_mstat) != NULL)
1416*0Sstevel@tonic-gate 					zss->sl_mstat = NULL;
1417*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
1418*0Sstevel@tonic-gate 				if (mp)
1419*0Sstevel@tonic-gate 					freemsg(mp);
1420*0Sstevel@tonic-gate 				mutex_enter(zs->zs_excl_hi);
1421*0Sstevel@tonic-gate 			}
1422*0Sstevel@tonic-gate 		}
1423*0Sstevel@tonic-gate 		if (!(sm->sm_config & CONN_IBM)) {
1424*0Sstevel@tonic-gate 			if (sm->sm_config & CONN_HDX) {
1425*0Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_HDX;
1426*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
1427*0Sstevel@tonic-gate 				return (EINVAL);
1428*0Sstevel@tonic-gate 			}
1429*0Sstevel@tonic-gate 			if (sm->sm_config & CONN_MPT) {
1430*0Sstevel@tonic-gate 				zss->sl_mode.sm_retval = SMERR_MPT;
1431*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
1432*0Sstevel@tonic-gate 				return (EINVAL);
1433*0Sstevel@tonic-gate 			}
1434*0Sstevel@tonic-gate 		}
1435*0Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FDXPTP;		/* "conmode" */
1436*0Sstevel@tonic-gate 		if ((sm->sm_config & (CONN_HDX | CONN_MPT)) == 0)
1437*0Sstevel@tonic-gate 			zss->sl_flags |= SF_FDXPTP;
1438*0Sstevel@tonic-gate 
1439*0Sstevel@tonic-gate 		error = zsh_program(zs, sm);
1440*0Sstevel@tonic-gate 		if (!error && (zs->zs_ops != &zsops_null))
1441*0Sstevel@tonic-gate 			zsh_init_port(zs, zss);
1442*0Sstevel@tonic-gate 	}
1443*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
1444*0Sstevel@tonic-gate 
1445*0Sstevel@tonic-gate 	return (error);
1446*0Sstevel@tonic-gate }
1447*0Sstevel@tonic-gate 
1448*0Sstevel@tonic-gate /*
1449*0Sstevel@tonic-gate  * Transmit interrupt service procedure
1450*0Sstevel@tonic-gate  */
1451*0Sstevel@tonic-gate 
1452*0Sstevel@tonic-gate static void
1453*0Sstevel@tonic-gate zsh_txint(struct zscom *zs)
1454*0Sstevel@tonic-gate {
1455*0Sstevel@tonic-gate 	register struct syncline *zss;
1456*0Sstevel@tonic-gate 	register mblk_t *mp;
1457*0Sstevel@tonic-gate 	register int tmp;
1458*0Sstevel@tonic-gate 	register uchar_t *wr_cur;
1459*0Sstevel@tonic-gate 
1460*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_TXINT, "zsh_txint: zs = %p", zs);
1461*0Sstevel@tonic-gate 
1462*0Sstevel@tonic-gate 	if ((wr_cur =  zs->zs_wr_cur) != NULL && (wr_cur <  zs->zs_wr_lim)) {
1463*0Sstevel@tonic-gate 		SCC_WRITEDATA(*wr_cur++);
1464*0Sstevel@tonic-gate 		zs->zs_wr_cur = wr_cur;
1465*0Sstevel@tonic-gate 		return;
1466*0Sstevel@tonic-gate 	}
1467*0Sstevel@tonic-gate 
1468*0Sstevel@tonic-gate 
1469*0Sstevel@tonic-gate 	zss = (struct syncline *)&zs->zs_priv_str;
1470*0Sstevel@tonic-gate 
1471*0Sstevel@tonic-gate 	switch (zss->sl_txstate) {
1472*0Sstevel@tonic-gate 
1473*0Sstevel@tonic-gate 	/*
1474*0Sstevel@tonic-gate 	 * we here because end of message block lim = cur
1475*0Sstevel@tonic-gate 	 */
1476*0Sstevel@tonic-gate 	case TX_ACTIVE:
1477*0Sstevel@tonic-gate 
1478*0Sstevel@tonic-gate 		mp = zss->sl_xactb;
1479*0Sstevel@tonic-gate 
1480*0Sstevel@tonic-gate again_txint:
1481*0Sstevel@tonic-gate 		mp = mp->b_cont;
1482*0Sstevel@tonic-gate 		if (mp) {
1483*0Sstevel@tonic-gate 			zss->sl_xactb = mp;
1484*0Sstevel@tonic-gate 			zss->sl_ocnt += tmp = mp->b_wptr - mp->b_rptr;
1485*0Sstevel@tonic-gate 			if (!tmp)
1486*0Sstevel@tonic-gate 				goto again_txint;
1487*0Sstevel@tonic-gate 			zs->zs_wr_cur = mp->b_rptr;
1488*0Sstevel@tonic-gate 			zs->zs_wr_lim = mp->b_wptr;
1489*0Sstevel@tonic-gate 			SCC_WRITEDATA(*zs->zs_wr_cur++);
1490*0Sstevel@tonic-gate 			return;
1491*0Sstevel@tonic-gate 		}
1492*0Sstevel@tonic-gate 
1493*0Sstevel@tonic-gate 		/*
1494*0Sstevel@tonic-gate 		 * This is where the fun starts.  At this point the
1495*0Sstevel@tonic-gate 		 * last character in the frame has been sent.  We
1496*0Sstevel@tonic-gate 		 * issue a RESET_TXINT so we won't get another txint
1497*0Sstevel@tonic-gate 		 * until the CRC has been completely sent.  Also we
1498*0Sstevel@tonic-gate 		 * reset the Abort-On-Underrun bit so that CRC is
1499*0Sstevel@tonic-gate 		 * sent at EOM, rather than an Abort.
1500*0Sstevel@tonic-gate 		 */
1501*0Sstevel@tonic-gate 		zs->zs_wr_cur = zs->zs_wr_lim = NULL;
1502*0Sstevel@tonic-gate 		zss->sl_txstate = TX_CRC;
1503*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1504*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_PHONY)) {
1505*0Sstevel@tonic-gate 			SCC_BIC(10, ZSWR10_UNDERRUN_ABORT);
1506*0Sstevel@tonic-gate 			zss->sl_st.opack++;
1507*0Sstevel@tonic-gate 			zss->sl_st.ochar += zss->sl_ocnt;
1508*0Sstevel@tonic-gate 		}
1509*0Sstevel@tonic-gate 		zss->sl_ocnt = 0;
1510*0Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
1511*0Sstevel@tonic-gate 		zss->sl_xhead = zss->sl_xactb = NULL;
1512*0Sstevel@tonic-gate 		ZSSETSOFT(zs);
1513*0Sstevel@tonic-gate 		break;
1514*0Sstevel@tonic-gate 	/*
1515*0Sstevel@tonic-gate 	 * This txint means we have sent the CRC bytes at EOF.
1516*0Sstevel@tonic-gate 	 * The next txint will mean we are sending or have sent the
1517*0Sstevel@tonic-gate 	 * flag character at EOF, but we handle that differently, and
1518*0Sstevel@tonic-gate 	 * enter different states,depending on whether we're IBM or not.
1519*0Sstevel@tonic-gate 	 */
1520*0Sstevel@tonic-gate 	case TX_CRC:
1521*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
1522*0Sstevel@tonic-gate 			zss->sl_txstate = TX_FLAG;	/* HDX path */
1523*0Sstevel@tonic-gate 		} else {	/* FDX path */
1524*0Sstevel@tonic-gate 			if (!zsh_start(zs, zss)) {
1525*0Sstevel@tonic-gate 				zss->sl_txstate = TX_IDLE;
1526*0Sstevel@tonic-gate 				SCC_WRITE0(ZSWR0_RESET_TXINT);
1527*0Sstevel@tonic-gate 			}
1528*0Sstevel@tonic-gate 		}
1529*0Sstevel@tonic-gate 		break;
1530*0Sstevel@tonic-gate 
1531*0Sstevel@tonic-gate 	/*
1532*0Sstevel@tonic-gate 	 * This txint means the closing flag byte is going out the door.
1533*0Sstevel@tonic-gate 	 * We use this state to allow this to complete before dropping RTS.
1534*0Sstevel@tonic-gate 	 */
1535*0Sstevel@tonic-gate 	case TX_FLAG:
1536*0Sstevel@tonic-gate 		zss->sl_txstate = TX_LAST;
1537*0Sstevel@tonic-gate 		(void) zsh_start(zs, zss);
1538*0Sstevel@tonic-gate 		break;
1539*0Sstevel@tonic-gate 
1540*0Sstevel@tonic-gate 	/*
1541*0Sstevel@tonic-gate 	 * Arriving here means the flag should be out and it's finally
1542*0Sstevel@tonic-gate 	 * time to close the barn door.
1543*0Sstevel@tonic-gate 	 */
1544*0Sstevel@tonic-gate 	case TX_LAST:
1545*0Sstevel@tonic-gate 		zss->sl_txstate = TX_IDLE;
1546*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1547*0Sstevel@tonic-gate 		break;
1548*0Sstevel@tonic-gate 
1549*0Sstevel@tonic-gate 	/*
1550*0Sstevel@tonic-gate 	 * If transmit was aborted, do nothing - watchdog will recover.
1551*0Sstevel@tonic-gate 	 */
1552*0Sstevel@tonic-gate 	case TX_ABORTED:
1553*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1554*0Sstevel@tonic-gate 		break;
1555*0Sstevel@tonic-gate 
1556*0Sstevel@tonic-gate 	default:
1557*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1558*0Sstevel@tonic-gate 		break;
1559*0Sstevel@tonic-gate 	}
1560*0Sstevel@tonic-gate }
1561*0Sstevel@tonic-gate 
1562*0Sstevel@tonic-gate /*
1563*0Sstevel@tonic-gate  * External Status Change interrupt service procedure
1564*0Sstevel@tonic-gate  */
1565*0Sstevel@tonic-gate static void
1566*0Sstevel@tonic-gate zsh_xsint(struct zscom *zs)
1567*0Sstevel@tonic-gate {
1568*0Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
1569*0Sstevel@tonic-gate 	register uchar_t s0, x0;
1570*0Sstevel@tonic-gate 
1571*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_XSINT, "zsh_xsint: zs = %p", zs);
1572*0Sstevel@tonic-gate 
1573*0Sstevel@tonic-gate 	s0 = SCC_READ0();
1574*0Sstevel@tonic-gate 	x0 = s0 ^ zss->sl_rr0;
1575*0Sstevel@tonic-gate 	zss->sl_rr0 = s0;
1576*0Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);
1577*0Sstevel@tonic-gate 
1578*0Sstevel@tonic-gate 	if (s0 & ZSRR0_TXUNDER) {
1579*0Sstevel@tonic-gate 		switch (zss->sl_txstate) {
1580*0Sstevel@tonic-gate 		/*
1581*0Sstevel@tonic-gate 		 * A transmitter underrun has occurred.  If we are not
1582*0Sstevel@tonic-gate 		 * here as the result of an abort sent by the watchdog
1583*0Sstevel@tonic-gate 		 * timeout routine, we need to send an abort to flush
1584*0Sstevel@tonic-gate 		 * the transmitter.  Otherwise there is a danger of
1585*0Sstevel@tonic-gate 		 * trashing the next frame but still sending a good crc.
1586*0Sstevel@tonic-gate 		 * The TX_ABORTED flag is set so that the watchdog
1587*0Sstevel@tonic-gate 		 * routine can initiate recovery.
1588*0Sstevel@tonic-gate 		 */
1589*0Sstevel@tonic-gate 		case TX_ACTIVE:
1590*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
1591*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
1592*0Sstevel@tonic-gate 			zss->sl_st.underrun++;
1593*0Sstevel@tonic-gate 			zsh_txbad(zs, zss);
1594*0Sstevel@tonic-gate 
1595*0Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED;
1596*0Sstevel@tonic-gate 			zss->sl_wd_count = 0;
1597*0Sstevel@tonic-gate 			break;
1598*0Sstevel@tonic-gate 
1599*0Sstevel@tonic-gate 		case TX_CRC:
1600*0Sstevel@tonic-gate 			break;
1601*0Sstevel@tonic-gate 
1602*0Sstevel@tonic-gate 		case TX_FLAG:
1603*0Sstevel@tonic-gate 			break;
1604*0Sstevel@tonic-gate 
1605*0Sstevel@tonic-gate 		case TX_ABORTED:
1606*0Sstevel@tonic-gate 			break;
1607*0Sstevel@tonic-gate 
1608*0Sstevel@tonic-gate 		case TX_OFF:
1609*0Sstevel@tonic-gate 			break;
1610*0Sstevel@tonic-gate 
1611*0Sstevel@tonic-gate 		case TX_LAST:
1612*0Sstevel@tonic-gate 			break;
1613*0Sstevel@tonic-gate 
1614*0Sstevel@tonic-gate 		default:
1615*0Sstevel@tonic-gate 			break;
1616*0Sstevel@tonic-gate 		}
1617*0Sstevel@tonic-gate 	}
1618*0Sstevel@tonic-gate 
1619*0Sstevel@tonic-gate 	if ((x0 & ZSRR0_BREAK) && (s0 & ZSRR0_BREAK) && zs->zs_rd_cur) {
1620*0Sstevel@tonic-gate 		zss->sl_st.abort++;
1621*0Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
1622*0Sstevel@tonic-gate 	} else if ((s0 & ZSRR0_SYNC) && (zs->zs_rd_cur)) {
1623*0Sstevel@tonic-gate 		/*
1624*0Sstevel@tonic-gate 		 * Tricky code to avoid disaster in the case where
1625*0Sstevel@tonic-gate 		 * an abort was detected while receiving a packet,
1626*0Sstevel@tonic-gate 		 * but the abort did not last long enough to be
1627*0Sstevel@tonic-gate 		 * detected by zsh_xsint - this can happen since
1628*0Sstevel@tonic-gate 		 * the ZSRR0_BREAK is not latched.  Since an abort
1629*0Sstevel@tonic-gate 		 * will automatically cause the SCC to enter
1630*0Sstevel@tonic-gate 		 * hunt mode, hopefully, the sync/hunt bit will be
1631*0Sstevel@tonic-gate 		 * set in this case (although if the interrupt is
1632*0Sstevel@tonic-gate 		 * sufficiently delayed, the SCC may have sync'ed
1633*0Sstevel@tonic-gate 		 * in again if it has detected a flag).
1634*0Sstevel@tonic-gate 		 */
1635*0Sstevel@tonic-gate 		zss->sl_st.abort++;
1636*0Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
1637*0Sstevel@tonic-gate 	}
1638*0Sstevel@tonic-gate 
1639*0Sstevel@tonic-gate 	if (x0 & s0 & ZSRR0_CTS) {
1640*0Sstevel@tonic-gate 	    if (zss->sl_txstate == TX_RTS) {
1641*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
1642*0Sstevel@tonic-gate 			SCC_BIS(5, ZSWR5_TX_ENABLE);
1643*0Sstevel@tonic-gate 		}
1644*0Sstevel@tonic-gate 		(void) zsh_start(zs, zss);
1645*0Sstevel@tonic-gate 	    } else if ((zss->sl_mode.sm_config & (CONN_IBM | CONN_SIGNAL))) {
1646*0Sstevel@tonic-gate 		zss->sl_flags &= ~SF_FLUSH_WQ;
1647*0Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_UP);
1648*0Sstevel@tonic-gate 	    }
1649*0Sstevel@tonic-gate 	}
1650*0Sstevel@tonic-gate 
1651*0Sstevel@tonic-gate 	/*
1652*0Sstevel@tonic-gate 	 * We don't care about CTS transitions unless we are in either
1653*0Sstevel@tonic-gate 	 * IBM or SIGNAL mode, or both.  So, if we see CTS drop, and we
1654*0Sstevel@tonic-gate 	 * care, and we are not idle, send up a report message.
1655*0Sstevel@tonic-gate 	 */
1656*0Sstevel@tonic-gate 	if ((x0 & ZSRR0_CTS) && ((s0 & ZSRR0_CTS) == 0) &&
1657*0Sstevel@tonic-gate 	    (zss->sl_txstate != TX_OFF) &&
1658*0Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_IBM | CONN_SIGNAL))) {
1659*0Sstevel@tonic-gate 		SCC_BIC(15, ZSR15_CTS);
1660*0Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_DOWN);
1661*0Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
1662*0Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
1663*0Sstevel@tonic-gate 		zss->sl_st.cts++;
1664*0Sstevel@tonic-gate 		if (zss->sl_txstate != TX_IDLE)
1665*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
1666*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
1667*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_TXINT);
1668*0Sstevel@tonic-gate 		zss->sl_wd_count = 0;
1669*0Sstevel@tonic-gate 		zsh_txbad(zs, zss);
1670*0Sstevel@tonic-gate 	}
1671*0Sstevel@tonic-gate }
1672*0Sstevel@tonic-gate 
1673*0Sstevel@tonic-gate 
1674*0Sstevel@tonic-gate /*
1675*0Sstevel@tonic-gate  * Receive interrupt service procedure
1676*0Sstevel@tonic-gate  */
1677*0Sstevel@tonic-gate static void
1678*0Sstevel@tonic-gate zsh_rxint(struct zscom *zs)
1679*0Sstevel@tonic-gate {
1680*0Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
1681*0Sstevel@tonic-gate 	register mblk_t *bp = zss->sl_ractb;
1682*0Sstevel@tonic-gate 	unsigned char *rd_cur;
1683*0Sstevel@tonic-gate 
1684*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_RXINT, "zsh_rxint: zs = %p", zs);
1685*0Sstevel@tonic-gate 
1686*0Sstevel@tonic-gate 	if (((rd_cur = zs->zs_rd_cur) != NULL) && rd_cur < zs->zs_rd_lim) {
1687*0Sstevel@tonic-gate 		*rd_cur++ = SCC_READDATA();
1688*0Sstevel@tonic-gate 		zs->zs_rd_cur = rd_cur;
1689*0Sstevel@tonic-gate 		return;
1690*0Sstevel@tonic-gate 	}
1691*0Sstevel@tonic-gate 
1692*0Sstevel@tonic-gate 	if (!rd_cur) { /* Beginning of frame */
1693*0Sstevel@tonic-gate 		if (!bp) {
1694*0Sstevel@tonic-gate 			ZSH_ALLOCB(bp);
1695*0Sstevel@tonic-gate 			zss->sl_ractb = bp;
1696*0Sstevel@tonic-gate 		}
1697*0Sstevel@tonic-gate 		zss->sl_rhead = bp;
1698*0Sstevel@tonic-gate 	} else {	/* end of data block should be cur==lim */
1699*0Sstevel@tonic-gate 		bp->b_wptr = zs->zs_rd_cur;
1700*0Sstevel@tonic-gate 		ZSH_ALLOCB(bp->b_cont);
1701*0Sstevel@tonic-gate 		bp = zss->sl_ractb = bp->b_cont;
1702*0Sstevel@tonic-gate 	}
1703*0Sstevel@tonic-gate 	if (!bp) {
1704*0Sstevel@tonic-gate 		zss->sl_st.nobuffers++;
1705*0Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
1706*0Sstevel@tonic-gate 		return;
1707*0Sstevel@tonic-gate 	}
1708*0Sstevel@tonic-gate 	zs->zs_rd_cur = bp->b_wptr;
1709*0Sstevel@tonic-gate 	zs->zs_rd_lim = bp->b_datap->db_lim;
1710*0Sstevel@tonic-gate 	*zs->zs_rd_cur++ = SCC_READDATA(); /* Also resets interrupt */
1711*0Sstevel@tonic-gate }
1712*0Sstevel@tonic-gate 
1713*0Sstevel@tonic-gate 
1714*0Sstevel@tonic-gate /*
1715*0Sstevel@tonic-gate  * Special Receive Condition Interrupt routine
1716*0Sstevel@tonic-gate  */
1717*0Sstevel@tonic-gate static void
1718*0Sstevel@tonic-gate zsh_srint(struct zscom *zs)
1719*0Sstevel@tonic-gate {
1720*0Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
1721*0Sstevel@tonic-gate 	register uchar_t s1;
1722*0Sstevel@tonic-gate 	register uchar_t *rd_cur;
1723*0Sstevel@tonic-gate 
1724*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SRINT, "zsh_srint: zs = %p", zs);
1725*0Sstevel@tonic-gate 
1726*0Sstevel@tonic-gate 	SCC_READ(1, s1);
1727*0Sstevel@tonic-gate 
1728*0Sstevel@tonic-gate 	if (s1 & ZSRR1_RXEOF) {			/* end of frame */
1729*0Sstevel@tonic-gate 		(void) SCC_READDATA();
1730*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
1731*0Sstevel@tonic-gate 		if (s1 & ZSRR1_FE) {		/* bad CRC */
1732*0Sstevel@tonic-gate 			zss->sl_st.crc++;
1733*0Sstevel@tonic-gate 			zsh_rxbad(zs, zss);
1734*0Sstevel@tonic-gate 			return;
1735*0Sstevel@tonic-gate 		}
1736*0Sstevel@tonic-gate 
1737*0Sstevel@tonic-gate 		if ((rd_cur = zs->zs_rd_cur) == NULL)
1738*0Sstevel@tonic-gate 			return;
1739*0Sstevel@tonic-gate 
1740*0Sstevel@tonic-gate 		/*
1741*0Sstevel@tonic-gate 		 * Drop one CRC byte from length because it came in
1742*0Sstevel@tonic-gate 		 * before the special interrupt got here.
1743*0Sstevel@tonic-gate 		 */
1744*0Sstevel@tonic-gate 		zss->sl_ractb->b_wptr = rd_cur - 1;
1745*0Sstevel@tonic-gate 
1746*0Sstevel@tonic-gate 		/*
1747*0Sstevel@tonic-gate 		 * put on done queue
1748*0Sstevel@tonic-gate 		 */
1749*0Sstevel@tonic-gate 		ZSH_PUTQ(zss->sl_rhead);
1750*0Sstevel@tonic-gate 		zss->sl_rhead = NULL;
1751*0Sstevel@tonic-gate 		zss->sl_ractb = NULL;
1752*0Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
1753*0Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
1754*0Sstevel@tonic-gate 		ZSSETSOFT(zs);
1755*0Sstevel@tonic-gate 
1756*0Sstevel@tonic-gate 	} else if (s1 & ZSRR1_DO) {
1757*0Sstevel@tonic-gate 		(void) SCC_READDATA();
1758*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
1759*0Sstevel@tonic-gate 		zss->sl_st.overrun++;
1760*0Sstevel@tonic-gate 		zsh_rxbad(zs, zss);
1761*0Sstevel@tonic-gate 	} else
1762*0Sstevel@tonic-gate 		SCC_WRITE0(ZSWR0_RESET_ERRORS);
1763*0Sstevel@tonic-gate }
1764*0Sstevel@tonic-gate 
1765*0Sstevel@tonic-gate /*
1766*0Sstevel@tonic-gate  * Handle a second stage interrupt.
1767*0Sstevel@tonic-gate  * Does mostly lower priority buffer management stuff.
1768*0Sstevel@tonic-gate  */
1769*0Sstevel@tonic-gate static int
1770*0Sstevel@tonic-gate zsh_softint(struct zscom *zs)
1771*0Sstevel@tonic-gate {
1772*0Sstevel@tonic-gate 	register struct syncline *zss;
1773*0Sstevel@tonic-gate 	register queue_t *q;
1774*0Sstevel@tonic-gate 	register mblk_t *mp, *tmp;
1775*0Sstevel@tonic-gate 	register mblk_t *head = NULL, *tail = NULL;
1776*0Sstevel@tonic-gate 	register int allocbcount = 0;
1777*0Sstevel@tonic-gate 	int m_error;
1778*0Sstevel@tonic-gate 
1779*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_START, "zsh_soft start: zs = %p", zs);
1780*0Sstevel@tonic-gate 
1781*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
1782*0Sstevel@tonic-gate 	zss = (struct syncline *)zs->zs_priv;
1783*0Sstevel@tonic-gate 	if (!zss || (q = zss->sl_stream.str_rq) == NULL) {
1784*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
1785*0Sstevel@tonic-gate 		return (0);
1786*0Sstevel@tonic-gate 	}
1787*0Sstevel@tonic-gate 	m_error = zss->sl_m_error;
1788*0Sstevel@tonic-gate 
1789*0Sstevel@tonic-gate 	zss->sl_m_error = 0;
1790*0Sstevel@tonic-gate 
1791*0Sstevel@tonic-gate 
1792*0Sstevel@tonic-gate 	if (!zss->sl_mstat)
1793*0Sstevel@tonic-gate 		zss->sl_mstat = allocb(sizeof (struct sl_status), BPRI_MED);
1794*0Sstevel@tonic-gate 
1795*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
1796*0Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
1797*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP)) {
1798*0Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
1799*0Sstevel@tonic-gate 		} else {
1800*0Sstevel@tonic-gate 			register uchar_t s0;
1801*0Sstevel@tonic-gate 
1802*0Sstevel@tonic-gate 			s0 = SCC_READ0();
1803*0Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
1804*0Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
1805*0Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
1806*0Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
1807*0Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
1808*0Sstevel@tonic-gate 			}
1809*0Sstevel@tonic-gate 			if (zss->sl_flags & SF_FLUSH_WQ) {
1810*0Sstevel@tonic-gate 				mutex_exit(zs->zs_excl_hi);
1811*0Sstevel@tonic-gate 				flushq(WR(q), FLUSHDATA);
1812*0Sstevel@tonic-gate 				goto next;
1813*0Sstevel@tonic-gate 			}
1814*0Sstevel@tonic-gate 		}
1815*0Sstevel@tonic-gate 	}
1816*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl_hi);
1817*0Sstevel@tonic-gate 
1818*0Sstevel@tonic-gate next:
1819*0Sstevel@tonic-gate 	for (;;) {
1820*0Sstevel@tonic-gate 		ZSH_GETQ(mp);
1821*0Sstevel@tonic-gate 		if (!mp)
1822*0Sstevel@tonic-gate 			break;
1823*0Sstevel@tonic-gate 
1824*0Sstevel@tonic-gate 		if (mp->b_rptr == mp->b_wptr) {
1825*0Sstevel@tonic-gate 			if (mp->b_datap->db_type == M_RSE) {
1826*0Sstevel@tonic-gate 				allocbcount++;
1827*0Sstevel@tonic-gate 			}
1828*0Sstevel@tonic-gate 			freemsg(mp);
1829*0Sstevel@tonic-gate 			continue;
1830*0Sstevel@tonic-gate 		}
1831*0Sstevel@tonic-gate 		if (mp->b_datap->db_type == M_DATA) {
1832*0Sstevel@tonic-gate 			zss->sl_st.ichar += msgdsize(mp);
1833*0Sstevel@tonic-gate 			zss->sl_st.ipack++;
1834*0Sstevel@tonic-gate 			if (!(canputnext(q))) {
1835*0Sstevel@tonic-gate 				zss->sl_st.ierror++;
1836*0Sstevel@tonic-gate 				allocbcount++;
1837*0Sstevel@tonic-gate 				freemsg(mp);
1838*0Sstevel@tonic-gate 				continue;
1839*0Sstevel@tonic-gate 			}
1840*0Sstevel@tonic-gate 		} else if (mp->b_datap->db_type == M_PROTO) {
1841*0Sstevel@tonic-gate 			if (!(canputnext(q))) {
1842*0Sstevel@tonic-gate 				freemsg(mp);
1843*0Sstevel@tonic-gate 				continue;
1844*0Sstevel@tonic-gate 			}
1845*0Sstevel@tonic-gate 		}
1846*0Sstevel@tonic-gate 		if (!head) {
1847*0Sstevel@tonic-gate 			allocbcount++;
1848*0Sstevel@tonic-gate 			zss->sl_soft_active = 1;
1849*0Sstevel@tonic-gate 			head = mp;
1850*0Sstevel@tonic-gate 		} else {
1851*0Sstevel@tonic-gate 			if (!tail)
1852*0Sstevel@tonic-gate 				tail = head;
1853*0Sstevel@tonic-gate 			tail->b_next = mp;
1854*0Sstevel@tonic-gate 			tail = mp;
1855*0Sstevel@tonic-gate 		}
1856*0Sstevel@tonic-gate 	}
1857*0Sstevel@tonic-gate 	if (allocbcount)
1858*0Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, allocbcount);
1859*0Sstevel@tonic-gate 
1860*0Sstevel@tonic-gate 	tmp = NULL;
1861*0Sstevel@tonic-gate again:
1862*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
1863*0Sstevel@tonic-gate 	if (!zss->sl_xstandby) {
1864*0Sstevel@tonic-gate 		if (tmp) {
1865*0Sstevel@tonic-gate 			zss->sl_xstandby = tmp;
1866*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
1867*0Sstevel@tonic-gate 		} else {
1868*0Sstevel@tonic-gate 			mutex_exit(zs->zs_excl_hi);
1869*0Sstevel@tonic-gate 			if (tmp = getq(WR(q)))
1870*0Sstevel@tonic-gate 				goto again;
1871*0Sstevel@tonic-gate 		}
1872*0Sstevel@tonic-gate 	} else {
1873*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
1874*0Sstevel@tonic-gate 		if (tmp)
1875*0Sstevel@tonic-gate 			(void) putbq(WR(q), tmp);
1876*0Sstevel@tonic-gate 	}
1877*0Sstevel@tonic-gate 
1878*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
1879*0Sstevel@tonic-gate 
1880*0Sstevel@tonic-gate 	while (head) {
1881*0Sstevel@tonic-gate 		if (!tail) {
1882*0Sstevel@tonic-gate 			putnext(q, head);
1883*0Sstevel@tonic-gate 			break;
1884*0Sstevel@tonic-gate 		}
1885*0Sstevel@tonic-gate 		mp = head;
1886*0Sstevel@tonic-gate 		head = head->b_next;
1887*0Sstevel@tonic-gate 		mp->b_next = NULL;
1888*0Sstevel@tonic-gate 		putnext(q, mp);
1889*0Sstevel@tonic-gate 
1890*0Sstevel@tonic-gate 	}
1891*0Sstevel@tonic-gate 
1892*0Sstevel@tonic-gate 	if (m_error)
1893*0Sstevel@tonic-gate 		(void) putnextctl1(q, M_ERROR, m_error);
1894*0Sstevel@tonic-gate 
1895*0Sstevel@tonic-gate 	zss->sl_soft_active = 0;
1896*0Sstevel@tonic-gate 
1897*0Sstevel@tonic-gate 	TRACE_1(TR_ZSH, TR_ZSH_SOFT_END, "zsh_soft end: zs = %p", zs);
1898*0Sstevel@tonic-gate 
1899*0Sstevel@tonic-gate 	return (0);
1900*0Sstevel@tonic-gate }
1901*0Sstevel@tonic-gate 
1902*0Sstevel@tonic-gate /*
1903*0Sstevel@tonic-gate  * Initialization routine.
1904*0Sstevel@tonic-gate  * Sets Clock sources, baud rate, modes and miscellaneous parameters.
1905*0Sstevel@tonic-gate  */
1906*0Sstevel@tonic-gate static int
1907*0Sstevel@tonic-gate zsh_program(struct zscom *zs, struct scc_mode *sm)
1908*0Sstevel@tonic-gate {
1909*0Sstevel@tonic-gate 	register struct syncline *zss  = (struct syncline *)&zs->zs_priv_str;
1910*0Sstevel@tonic-gate 	register struct zs_prog *zspp;
1911*0Sstevel@tonic-gate 	register ushort_t	tconst = 0;
1912*0Sstevel@tonic-gate 	register int	wr11 = 0;
1913*0Sstevel@tonic-gate 	register int	baud = 0;
1914*0Sstevel@tonic-gate 	register int	pll = 0;
1915*0Sstevel@tonic-gate 	register int	speed = 0;
1916*0Sstevel@tonic-gate 	register int	flags = ZSP_SYNC;
1917*0Sstevel@tonic-gate 	int		err = 0;
1918*0Sstevel@tonic-gate 
1919*0Sstevel@tonic-gate 	ZSSETSOFT(zs); /* get our house in order */
1920*0Sstevel@tonic-gate 
1921*0Sstevel@tonic-gate 	switch (sm->sm_txclock) {
1922*0Sstevel@tonic-gate 	case TXC_IS_TXC:
1923*0Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_TRXC;
1924*0Sstevel@tonic-gate 		break;
1925*0Sstevel@tonic-gate 	case TXC_IS_RXC:
1926*0Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_RTXC;
1927*0Sstevel@tonic-gate 		break;
1928*0Sstevel@tonic-gate 	case TXC_IS_BAUD:
1929*0Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_BAUD;
1930*0Sstevel@tonic-gate 		wr11 |= ZSWR11_TRXC_OUT_ENA + ZSWR11_TRXC_XMIT;
1931*0Sstevel@tonic-gate 		baud++;
1932*0Sstevel@tonic-gate 		break;
1933*0Sstevel@tonic-gate 	case TXC_IS_PLL:
1934*0Sstevel@tonic-gate 		wr11 |= ZSWR11_TXCLK_DPLL;
1935*0Sstevel@tonic-gate 		pll++;
1936*0Sstevel@tonic-gate 		break;
1937*0Sstevel@tonic-gate 	default:
1938*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_TXC;
1939*0Sstevel@tonic-gate 		err = EINVAL;
1940*0Sstevel@tonic-gate 		goto out;
1941*0Sstevel@tonic-gate 	}
1942*0Sstevel@tonic-gate 	switch (sm->sm_rxclock) {
1943*0Sstevel@tonic-gate 	case RXC_IS_RXC:
1944*0Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_RTXC;
1945*0Sstevel@tonic-gate 		break;
1946*0Sstevel@tonic-gate 	case RXC_IS_TXC:
1947*0Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_TRXC;
1948*0Sstevel@tonic-gate 		break;
1949*0Sstevel@tonic-gate 	case RXC_IS_BAUD:
1950*0Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_BAUD;
1951*0Sstevel@tonic-gate 		baud++;
1952*0Sstevel@tonic-gate 		break;
1953*0Sstevel@tonic-gate 	case RXC_IS_PLL:
1954*0Sstevel@tonic-gate 		wr11 |= ZSWR11_RXCLK_DPLL;
1955*0Sstevel@tonic-gate 		pll++;
1956*0Sstevel@tonic-gate 		break;
1957*0Sstevel@tonic-gate 	default:
1958*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_RXC;
1959*0Sstevel@tonic-gate 		err = EINVAL;
1960*0Sstevel@tonic-gate 		goto out;
1961*0Sstevel@tonic-gate 	}
1962*0Sstevel@tonic-gate 	if (baud && pll) {
1963*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
1964*0Sstevel@tonic-gate 		err = EINVAL;
1965*0Sstevel@tonic-gate 		goto out;
1966*0Sstevel@tonic-gate 	}
1967*0Sstevel@tonic-gate 	if (pll && !(sm->sm_config & CONN_NRZI)) {
1968*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_PLL;
1969*0Sstevel@tonic-gate 		err = EINVAL;
1970*0Sstevel@tonic-gate 		goto out;
1971*0Sstevel@tonic-gate 	}
1972*0Sstevel@tonic-gate 
1973*0Sstevel@tonic-gate 	/*
1974*0Sstevel@tonic-gate 	 * If we're going to use the BRG and the speed we want is != 0...
1975*0Sstevel@tonic-gate 	 */
1976*0Sstevel@tonic-gate 	if (baud && (speed = sm->sm_baudrate)) {
1977*0Sstevel@tonic-gate 		tconst = (PCLK + speed) / (2 * speed) - 2;
1978*0Sstevel@tonic-gate 		if (tconst == 0) {
1979*0Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
1980*0Sstevel@tonic-gate 			err = EINVAL;
1981*0Sstevel@tonic-gate 			goto out;
1982*0Sstevel@tonic-gate 		}
1983*0Sstevel@tonic-gate 		sm->sm_baudrate = PCLK / (2 * ((int)tconst + 2));
1984*0Sstevel@tonic-gate 	} else {
1985*0Sstevel@tonic-gate 		tconst = 0;	/* Stop BRG.  Also quiesces pin 24. */
1986*0Sstevel@tonic-gate 	}
1987*0Sstevel@tonic-gate 
1988*0Sstevel@tonic-gate 	if (pll) {
1989*0Sstevel@tonic-gate 		if ((speed  = sm->sm_baudrate * 32) != 0)
1990*0Sstevel@tonic-gate 			tconst = (PCLK + speed) / (2 * speed) - 2;
1991*0Sstevel@tonic-gate 		else
1992*0Sstevel@tonic-gate 			tconst = 0;
1993*0Sstevel@tonic-gate 		if (tconst == 0) {
1994*0Sstevel@tonic-gate 			zss->sl_mode.sm_retval = SMERR_BAUDRATE;
1995*0Sstevel@tonic-gate 			err = EINVAL;
1996*0Sstevel@tonic-gate 			goto out;
1997*0Sstevel@tonic-gate 		}
1998*0Sstevel@tonic-gate 		speed = PCLK / (2 * ((int)tconst + 2));
1999*0Sstevel@tonic-gate 		sm->sm_baudrate = speed / 32;
2000*0Sstevel@tonic-gate 		flags |= ZSP_PLL;
2001*0Sstevel@tonic-gate 	}
2002*0Sstevel@tonic-gate 
2003*0Sstevel@tonic-gate 	if ((sm->sm_config & (CONN_LPBK|CONN_ECHO)) == (CONN_LPBK|CONN_ECHO)) {
2004*0Sstevel@tonic-gate 		zss->sl_mode.sm_retval = SMERR_LPBKS;
2005*0Sstevel@tonic-gate 		err = EINVAL;
2006*0Sstevel@tonic-gate 		goto out;
2007*0Sstevel@tonic-gate 	}
2008*0Sstevel@tonic-gate 	if (sm->sm_config & CONN_LPBK)
2009*0Sstevel@tonic-gate 		flags |= ZSP_LOOP;
2010*0Sstevel@tonic-gate 	if (sm->sm_config & CONN_NRZI)
2011*0Sstevel@tonic-gate 		flags |= ZSP_NRZI;
2012*0Sstevel@tonic-gate 	if (sm->sm_config & CONN_ECHO)
2013*0Sstevel@tonic-gate 		flags |= ZSP_ECHO;
2014*0Sstevel@tonic-gate 
2015*0Sstevel@tonic-gate 	zspp = &zs_prog[zs->zs_unit];
2016*0Sstevel@tonic-gate 
2017*0Sstevel@tonic-gate 	zspp->zs = zs;
2018*0Sstevel@tonic-gate 	zspp->flags = (uchar_t)flags;
2019*0Sstevel@tonic-gate 	zspp->wr4 = ZSWR4_SDLC;
2020*0Sstevel@tonic-gate 	zspp->wr11 = (uchar_t)wr11;
2021*0Sstevel@tonic-gate 	zspp->wr12 = (uchar_t)(tconst & 0xff);
2022*0Sstevel@tonic-gate 	zspp->wr13 = (uchar_t)((tconst >> 8) & 0xff);
2023*0Sstevel@tonic-gate 	zspp->wr3 = (uchar_t)(ZSWR3_RX_ENABLE | ZSWR3_RXCRC_ENABLE |
2024*0Sstevel@tonic-gate 	    ZSWR3_RX_8);
2025*0Sstevel@tonic-gate 	zspp->wr5 = (uchar_t)(ZSWR5_TX_8 | ZSWR5_DTR | ZSWR5_TXCRC_ENABLE);
2026*0Sstevel@tonic-gate 
2027*0Sstevel@tonic-gate 	if (zss->sl_flags & SF_FDXPTP) {
2028*0Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_RTS;
2029*0Sstevel@tonic-gate 		zss->sl_rr0 |= ZSRR0_CTS;		/* Assume CTS is high */
2030*0Sstevel@tonic-gate 	}
2031*0Sstevel@tonic-gate 	if (sm->sm_config & CONN_IBM) {
2032*0Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
2033*0Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC | ZSR15_CTS);
2034*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
2035*0Sstevel@tonic-gate 			zspp->wr15 &= ~ZSR15_CTS;
2036*0Sstevel@tonic-gate 	} else {
2037*0Sstevel@tonic-gate 		zspp->wr5 |= ZSWR5_TX_ENABLE;
2038*0Sstevel@tonic-gate 		zspp->wr15 = (uchar_t)
2039*0Sstevel@tonic-gate 		    (ZSR15_TX_UNDER | ZSR15_BREAK | ZSR15_SYNC);
2040*0Sstevel@tonic-gate 		if (sm->sm_config & CONN_SIGNAL)
2041*0Sstevel@tonic-gate 			zspp->wr15 |= ZSR15_CTS;
2042*0Sstevel@tonic-gate 	}
2043*0Sstevel@tonic-gate 
2044*0Sstevel@tonic-gate 	zs_program(zspp);
2045*0Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
2046*0Sstevel@tonic-gate 	SCC_WRITE0(ZSWR0_RESET_STATUS);		/* reset XS */
2047*0Sstevel@tonic-gate 	zss->sl_flags |= SF_INITIALIZED;
2048*0Sstevel@tonic-gate 	bzero(&zss->sl_st, sizeof (struct sl_stats));
2049*0Sstevel@tonic-gate 	bcopy(sm, &zss->sl_mode, sizeof (struct scc_mode));
2050*0Sstevel@tonic-gate 	zss->sl_mode.sm_retval = 0;	/* successful */
2051*0Sstevel@tonic-gate out:
2052*0Sstevel@tonic-gate 	return (err);
2053*0Sstevel@tonic-gate }
2054*0Sstevel@tonic-gate 
2055*0Sstevel@tonic-gate /*
2056*0Sstevel@tonic-gate  * Function to store modem signal changes in sl_mstat field.
2057*0Sstevel@tonic-gate  * Note that these events are supposed to be so far apart in time that
2058*0Sstevel@tonic-gate  * we should always be able to send up the event and allocate a message
2059*0Sstevel@tonic-gate  * block before another one happens.  If not, we'll overwrite this one.
2060*0Sstevel@tonic-gate  */
2061*0Sstevel@tonic-gate static void
2062*0Sstevel@tonic-gate zsh_setmstat(struct zscom *zs, int event)
2063*0Sstevel@tonic-gate {
2064*0Sstevel@tonic-gate 	register struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
2065*0Sstevel@tonic-gate 	register struct sl_status *mstat;
2066*0Sstevel@tonic-gate 	register mblk_t *mp;
2067*0Sstevel@tonic-gate 
2068*0Sstevel@tonic-gate 	if (((mp = zss->sl_mstat) != NULL) &&
2069*0Sstevel@tonic-gate 	    (zss->sl_mode.sm_config & (CONN_SIGNAL))) {
2070*0Sstevel@tonic-gate 		mstat = (struct sl_status *)mp->b_wptr;
2071*0Sstevel@tonic-gate 		mstat->type = (zss->sl_mode.sm_config & CONN_IBM) ?
2072*0Sstevel@tonic-gate 		    SLS_LINKERR : SLS_MDMSTAT;
2073*0Sstevel@tonic-gate 		mstat->status = event;
2074*0Sstevel@tonic-gate 		gethrestime(&mstat->tstamp);
2075*0Sstevel@tonic-gate 		mp->b_wptr += sizeof (struct sl_status);
2076*0Sstevel@tonic-gate 		mp->b_datap->db_type = M_PROTO;
2077*0Sstevel@tonic-gate 		ZSH_PUTQ(mp);
2078*0Sstevel@tonic-gate 		zss->sl_mstat = NULL;
2079*0Sstevel@tonic-gate 		ZSSETSOFT(zs);
2080*0Sstevel@tonic-gate 	}
2081*0Sstevel@tonic-gate }
2082*0Sstevel@tonic-gate 
2083*0Sstevel@tonic-gate /*
2084*0Sstevel@tonic-gate  * Received Bad Frame procedure
2085*0Sstevel@tonic-gate  */
2086*0Sstevel@tonic-gate static void
2087*0Sstevel@tonic-gate zsh_rxbad(struct zscom *zs, struct syncline *zss)
2088*0Sstevel@tonic-gate {
2089*0Sstevel@tonic-gate 	/*
2090*0Sstevel@tonic-gate 	 * swallow bad characters
2091*0Sstevel@tonic-gate 	 */
2092*0Sstevel@tonic-gate 	(void) SCC_READDATA();
2093*0Sstevel@tonic-gate 	(void) SCC_READDATA();
2094*0Sstevel@tonic-gate 	(void) SCC_READDATA();
2095*0Sstevel@tonic-gate 
2096*0Sstevel@tonic-gate 	SCC_BIS(3, ZSWR3_HUNT);	/* enter hunt mode - ignores rest of frame */
2097*0Sstevel@tonic-gate 
2098*0Sstevel@tonic-gate 	zss->sl_st.ierror++;
2099*0Sstevel@tonic-gate 
2100*0Sstevel@tonic-gate 	/*
2101*0Sstevel@tonic-gate 	 * Free active receive message.
2102*0Sstevel@tonic-gate 	 */
2103*0Sstevel@tonic-gate 	if (zss->sl_rhead) {
2104*0Sstevel@tonic-gate 		zss->sl_rhead->b_wptr = zss->sl_rhead->b_rptr;
2105*0Sstevel@tonic-gate 		zss->sl_rhead->b_datap->db_type = M_RSE;
2106*0Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_rhead);
2107*0Sstevel@tonic-gate 		zss->sl_ractb = NULL;
2108*0Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
2109*0Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
2110*0Sstevel@tonic-gate 	}
2111*0Sstevel@tonic-gate 	if (zss->sl_rhead) {
2112*0Sstevel@tonic-gate 		zss->sl_rhead = NULL;
2113*0Sstevel@tonic-gate 		ZSH_ALLOCB(zss->sl_ractb);
2114*0Sstevel@tonic-gate 		zs->zs_rd_cur = NULL;
2115*0Sstevel@tonic-gate 		zs->zs_rd_lim = NULL;
2116*0Sstevel@tonic-gate 	}
2117*0Sstevel@tonic-gate 
2118*0Sstevel@tonic-gate 	ZSSETSOFT(zs);
2119*0Sstevel@tonic-gate }
2120*0Sstevel@tonic-gate 
2121*0Sstevel@tonic-gate /*
2122*0Sstevel@tonic-gate  * Transmit error procedure
2123*0Sstevel@tonic-gate  */
2124*0Sstevel@tonic-gate static void
2125*0Sstevel@tonic-gate zsh_txbad(struct zscom *zs, struct syncline *zss)
2126*0Sstevel@tonic-gate {
2127*0Sstevel@tonic-gate 	if (zss->sl_xhead) {		/* free the message we were sending */
2128*0Sstevel@tonic-gate 		zss->sl_xhead->b_wptr = zss->sl_xhead->b_rptr;
2129*0Sstevel@tonic-gate 		ZSH_FREEMSG(zss->sl_xhead);
2130*0Sstevel@tonic-gate 		zss->sl_xactb = NULL;
2131*0Sstevel@tonic-gate 		zs->zs_wr_cur = NULL;
2132*0Sstevel@tonic-gate 		zs->zs_wr_lim = NULL;
2133*0Sstevel@tonic-gate 	}
2134*0Sstevel@tonic-gate 	zss->sl_xhead = NULL;
2135*0Sstevel@tonic-gate 
2136*0Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_FDXPTP)) {
2137*0Sstevel@tonic-gate 		/*
2138*0Sstevel@tonic-gate 		 * drop RTS and our notion of CTS
2139*0Sstevel@tonic-gate 		 */
2140*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_RTS);
2141*0Sstevel@tonic-gate 		SCC_BIC(5, ZSWR5_TX_ENABLE);
2142*0Sstevel@tonic-gate 		zss->sl_rr0 &= ~ZSRR0_CTS;
2143*0Sstevel@tonic-gate 	}
2144*0Sstevel@tonic-gate 	zss->sl_txstate = TX_IDLE;
2145*0Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_PHONY))
2146*0Sstevel@tonic-gate 		zss->sl_st.oerror++;
2147*0Sstevel@tonic-gate }
2148*0Sstevel@tonic-gate 
2149*0Sstevel@tonic-gate /*
2150*0Sstevel@tonic-gate  * Transmitter watchdog timeout routine
2151*0Sstevel@tonic-gate  */
2152*0Sstevel@tonic-gate static void
2153*0Sstevel@tonic-gate zsh_watchdog(void *arg)
2154*0Sstevel@tonic-gate {
2155*0Sstevel@tonic-gate 	struct zscom *zs = arg;
2156*0Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
2157*0Sstevel@tonic-gate 	queue_t *wq;
2158*0Sstevel@tonic-gate 	mblk_t *mp;
2159*0Sstevel@tonic-gate 	int warning = 0;
2160*0Sstevel@tonic-gate 	uchar_t s0;
2161*0Sstevel@tonic-gate 	int do_flushwq = 0;
2162*0Sstevel@tonic-gate 
2163*0Sstevel@tonic-gate 	/*
2164*0Sstevel@tonic-gate 	 * The main reason for this routine is because, under some
2165*0Sstevel@tonic-gate 	 * circumstances, a transmit interrupt may get lost (ie., if
2166*0Sstevel@tonic-gate 	 * underrun occurs after the last character has been sent, and
2167*0Sstevel@tonic-gate 	 * the tx interrupt following the abort gets scheduled before
2168*0Sstevel@tonic-gate 	 * the current tx interrupt has been serviced).  Transmit can
2169*0Sstevel@tonic-gate 	 * also get hung if the cable is pulled out and the clock was
2170*0Sstevel@tonic-gate 	 * coming in from the modem.
2171*0Sstevel@tonic-gate 	 */
2172*0Sstevel@tonic-gate 
2173*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
2174*0Sstevel@tonic-gate 	if (zss->sl_stream.str_rq)
2175*0Sstevel@tonic-gate 		wq = WR(zss->sl_stream.str_rq);
2176*0Sstevel@tonic-gate 	else {
2177*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl);
2178*0Sstevel@tonic-gate 		return;		/* guard against close/callback race */
2179*0Sstevel@tonic-gate 	}
2180*0Sstevel@tonic-gate 
2181*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl_hi);
2182*0Sstevel@tonic-gate 	if (!(zss->sl_flags & SF_XMT_INPROG) && wq->q_first) {
2183*0Sstevel@tonic-gate 		zss->sl_flags |= SF_XMT_INPROG;
2184*0Sstevel@tonic-gate 		if ((zss->sl_flags & SF_FDXPTP) ||
2185*0Sstevel@tonic-gate 		    zsh_hdp_ok_or_rts_state(zs, zss))
2186*0Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
2187*0Sstevel@tonic-gate 		goto end_watchdog;
2188*0Sstevel@tonic-gate 	}
2189*0Sstevel@tonic-gate 
2190*0Sstevel@tonic-gate 	if (zss->sl_wd_count-- > 0)
2191*0Sstevel@tonic-gate 		goto end_watchdog;
2192*0Sstevel@tonic-gate 
2193*0Sstevel@tonic-gate 	if (zss->sl_flags & SF_FLUSH_WQ) {
2194*0Sstevel@tonic-gate 		if (!(zss->sl_flags & SF_FDXPTP))
2195*0Sstevel@tonic-gate 			zss->sl_flags &= ~SF_FLUSH_WQ;
2196*0Sstevel@tonic-gate 		else {
2197*0Sstevel@tonic-gate 			s0 = SCC_READ0();
2198*0Sstevel@tonic-gate 			if (s0 & ZSRR0_CTS) {
2199*0Sstevel@tonic-gate 				zss->sl_rr0 |= ZSRR0_CTS;
2200*0Sstevel@tonic-gate 				SCC_BIS(15, ZSR15_CTS);
2201*0Sstevel@tonic-gate 				zss->sl_flags &= ~SF_FLUSH_WQ;
2202*0Sstevel@tonic-gate 				zsh_setmstat(zs, CS_CTS_UP);
2203*0Sstevel@tonic-gate 			}
2204*0Sstevel@tonic-gate 		}
2205*0Sstevel@tonic-gate 	}
2206*0Sstevel@tonic-gate 
2207*0Sstevel@tonic-gate 	switch (zss->sl_txstate) {
2208*0Sstevel@tonic-gate 
2209*0Sstevel@tonic-gate 	case TX_ABORTED:
2210*0Sstevel@tonic-gate 		/*
2211*0Sstevel@tonic-gate 		 * Transmitter was hung ... try restarting it.
2212*0Sstevel@tonic-gate 		 */
2213*0Sstevel@tonic-gate 		if (zss->sl_flags & SF_FDXPTP) {
2214*0Sstevel@tonic-gate 			zss->sl_flags |= SF_XMT_INPROG;
2215*0Sstevel@tonic-gate 			(void) zsh_start(zs, zss);
2216*0Sstevel@tonic-gate 		} else
2217*0Sstevel@tonic-gate 			do_flushwq = 1;
2218*0Sstevel@tonic-gate 		break;
2219*0Sstevel@tonic-gate 
2220*0Sstevel@tonic-gate 	case TX_ACTIVE:
2221*0Sstevel@tonic-gate 	case TX_CRC:
2222*0Sstevel@tonic-gate 		/*
2223*0Sstevel@tonic-gate 		 * Transmit is hung for some reason. Reset tx interrupt.
2224*0Sstevel@tonic-gate 		 * Flush transmit fifo by sending an abort command
2225*0Sstevel@tonic-gate 		 * which also sets the Underrun/EOM latch in WR0 and in
2226*0Sstevel@tonic-gate 		 * turn generates an External Status interrupt that
2227*0Sstevel@tonic-gate 		 * will reset the necessary message buffer pointers.
2228*0Sstevel@tonic-gate 		 * The watchdog timer will cycle again to allow the SCC
2229*0Sstevel@tonic-gate 		 * to settle down after the abort command.  The next
2230*0Sstevel@tonic-gate 		 * time through we'll see that the state is now TX_ABORTED
2231*0Sstevel@tonic-gate 		 * and call zsh_start to grab a new message.
2232*0Sstevel@tonic-gate 		 */
2233*0Sstevel@tonic-gate 		if (--zss->sl_wd_count <= 0) {
2234*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_SEND_ABORT);
2235*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_ERRORS);
2236*0Sstevel@tonic-gate 			SCC_WRITE0(ZSWR0_RESET_TXINT);
2237*0Sstevel@tonic-gate 			zsh_txbad(zs, zss);
2238*0Sstevel@tonic-gate 			zss->sl_txstate = TX_ABORTED; /* must be after txbad */
2239*0Sstevel@tonic-gate 			warning = 1;
2240*0Sstevel@tonic-gate 		}
2241*0Sstevel@tonic-gate 		break;
2242*0Sstevel@tonic-gate 
2243*0Sstevel@tonic-gate 	case TX_RTS:
2244*0Sstevel@tonic-gate 		/*
2245*0Sstevel@tonic-gate 		 * Timer expired after we raised RTS.  CTS never came up.
2246*0Sstevel@tonic-gate 		 */
2247*0Sstevel@tonic-gate 		zss->sl_st.cts++;
2248*0Sstevel@tonic-gate 
2249*0Sstevel@tonic-gate 		zsh_setmstat(zs, CS_CTS_TO);
2250*0Sstevel@tonic-gate 		zss->sl_flags &= ~SF_XMT_INPROG;
2251*0Sstevel@tonic-gate 		zss->sl_flags |= SF_FLUSH_WQ;
2252*0Sstevel@tonic-gate 		ZSSETSOFT(zs);
2253*0Sstevel@tonic-gate 		break;
2254*0Sstevel@tonic-gate 
2255*0Sstevel@tonic-gate 	default:
2256*0Sstevel@tonic-gate 		/*
2257*0Sstevel@tonic-gate 		 * If we time out in an inactive state we set a soft
2258*0Sstevel@tonic-gate 		 * interrupt.  This will call zsh_start which will
2259*0Sstevel@tonic-gate 		 * clear SF_XMT_INPROG if the queue is empty.
2260*0Sstevel@tonic-gate 		 */
2261*0Sstevel@tonic-gate 		break;
2262*0Sstevel@tonic-gate 	}
2263*0Sstevel@tonic-gate end_watchdog:
2264*0Sstevel@tonic-gate 	if (zss->sl_txstate != TX_OFF) {
2265*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
2266*0Sstevel@tonic-gate 		zss->sl_wd_id = timeout(zsh_watchdog, zs, SIO_WATCHDOG_TICK);
2267*0Sstevel@tonic-gate 	} else {
2268*0Sstevel@tonic-gate 		zss->sl_wd_id = 0;	/* safety */
2269*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
2270*0Sstevel@tonic-gate 	}
2271*0Sstevel@tonic-gate 	if (warning || do_flushwq) {
2272*0Sstevel@tonic-gate 		flushq(wq, FLUSHDATA);
2273*0Sstevel@tonic-gate 		mutex_enter(zs->zs_excl_hi);
2274*0Sstevel@tonic-gate 		if ((mp = zss->sl_xstandby) != NULL)
2275*0Sstevel@tonic-gate 			zss->sl_xstandby = NULL;
2276*0Sstevel@tonic-gate 		mutex_exit(zs->zs_excl_hi);
2277*0Sstevel@tonic-gate 		if (mp)
2278*0Sstevel@tonic-gate 			freemsg(mp);
2279*0Sstevel@tonic-gate 	}
2280*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
2281*0Sstevel@tonic-gate 	if (warning)
2282*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "zsh%x: transmit hung", zs->zs_unit);
2283*0Sstevel@tonic-gate }
2284*0Sstevel@tonic-gate 
2285*0Sstevel@tonic-gate static void
2286*0Sstevel@tonic-gate zsh_callback(void *arg)
2287*0Sstevel@tonic-gate {
2288*0Sstevel@tonic-gate 	struct zscom *zs = arg;
2289*0Sstevel@tonic-gate 	struct syncline *zss = (struct syncline *)&zs->zs_priv_str;
2290*0Sstevel@tonic-gate 	int tmp = ZSH_MAX_RSTANDBY;
2291*0Sstevel@tonic-gate 
2292*0Sstevel@tonic-gate 	mutex_enter(zs->zs_excl);
2293*0Sstevel@tonic-gate 	if (zss->sl_bufcid) {
2294*0Sstevel@tonic-gate 		zss->sl_bufcid = 0;
2295*0Sstevel@tonic-gate 		ZSH_GETBLOCK(zs, tmp);
2296*0Sstevel@tonic-gate 	}
2297*0Sstevel@tonic-gate 	mutex_exit(zs->zs_excl);
2298*0Sstevel@tonic-gate }
2299