xref: /onnv-gate/usr/src/uts/common/io/zcons.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  * Zone Console Driver.
31*0Sstevel@tonic-gate  *
32*0Sstevel@tonic-gate  * This driver, derived from the pts/ptm drivers, is the pseudo console driver
33*0Sstevel@tonic-gate  * for system zones.  Its implementation is straightforward.  Each instance
34*0Sstevel@tonic-gate  * of the driver represents a global-zone/local-zone pair (this maps in a
35*0Sstevel@tonic-gate  * straightforward way to the commonly used terminal notion of "master side"
36*0Sstevel@tonic-gate  * and "slave side", and we use that terminology throughout).
37*0Sstevel@tonic-gate  *
38*0Sstevel@tonic-gate  * Instances of zcons are onlined as children of /pseudo/zconsnex@1/
39*0Sstevel@tonic-gate  * by zoneadmd in userland, using the devctl framework; thus the driver
40*0Sstevel@tonic-gate  * does not need to maintain any sort of "admin" node.
41*0Sstevel@tonic-gate  *
42*0Sstevel@tonic-gate  * The driver shuttles I/O from master side to slave side and back.  In a break
43*0Sstevel@tonic-gate  * from the pts/ptm semantics, if one side is not open, I/O directed towards
44*0Sstevel@tonic-gate  * it will simply be discarded.  This is so that if zoneadmd is not holding
45*0Sstevel@tonic-gate  * the master side console open (i.e. it has died somehow), processes in
46*0Sstevel@tonic-gate  * the zone do not experience any errors and I/O to the console does not
47*0Sstevel@tonic-gate  * hang.
48*0Sstevel@tonic-gate  *
49*0Sstevel@tonic-gate  * TODO: we may want to revisit the other direction; i.e. we may want
50*0Sstevel@tonic-gate  * zoneadmd to be able to detect whether no zone processes are holding the
51*0Sstevel@tonic-gate  * console open, an unusual situation.
52*0Sstevel@tonic-gate  */
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate #include <sys/types.h>
55*0Sstevel@tonic-gate #include <sys/cmn_err.h>
56*0Sstevel@tonic-gate #include <sys/conf.h>
57*0Sstevel@tonic-gate #include <sys/cred.h>
58*0Sstevel@tonic-gate #include <sys/ddi.h>
59*0Sstevel@tonic-gate #include <sys/debug.h>
60*0Sstevel@tonic-gate #include <sys/devops.h>
61*0Sstevel@tonic-gate #include <sys/errno.h>
62*0Sstevel@tonic-gate #include <sys/file.h>
63*0Sstevel@tonic-gate #include <sys/modctl.h>
64*0Sstevel@tonic-gate #include <sys/param.h>
65*0Sstevel@tonic-gate #include <sys/stat.h>
66*0Sstevel@tonic-gate #include <sys/stream.h>
67*0Sstevel@tonic-gate #include <sys/stropts.h>
68*0Sstevel@tonic-gate #include <sys/strsun.h>
69*0Sstevel@tonic-gate #include <sys/sunddi.h>
70*0Sstevel@tonic-gate #include <sys/sysmacros.h>
71*0Sstevel@tonic-gate #include <sys/systm.h>
72*0Sstevel@tonic-gate #include <sys/types.h>
73*0Sstevel@tonic-gate #include <sys/zcons.h>
74*0Sstevel@tonic-gate 
75*0Sstevel@tonic-gate static int zc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
76*0Sstevel@tonic-gate static int zc_attach(dev_info_t *, ddi_attach_cmd_t);
77*0Sstevel@tonic-gate static int zc_detach(dev_info_t *, ddi_detach_cmd_t);
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate static int zc_open(queue_t *, dev_t *, int, int, cred_t *);
80*0Sstevel@tonic-gate static int zc_close(queue_t *, int, cred_t *);
81*0Sstevel@tonic-gate static void zc_wput(queue_t *, mblk_t *);
82*0Sstevel@tonic-gate static void zc_rsrv(queue_t *);
83*0Sstevel@tonic-gate static void zc_wsrv(queue_t *);
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate /*
86*0Sstevel@tonic-gate  * The instance number is encoded in the dev_t in the minor number; the lowest
87*0Sstevel@tonic-gate  * bit of the minor number is used to track the master vs. slave side of the
88*0Sstevel@tonic-gate  * virtual console.  The rest of the bits in the minor number are the instance.
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate #define	ZC_MASTER_MINOR	0
91*0Sstevel@tonic-gate #define	ZC_SLAVE_MINOR	1
92*0Sstevel@tonic-gate 
93*0Sstevel@tonic-gate #define	ZC_INSTANCE(x)	(getminor((x)) >> 1)
94*0Sstevel@tonic-gate #define	ZC_NODE(x)	(getminor((x)) & 0x01)
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate int zcons_debug = 0;
97*0Sstevel@tonic-gate #define	DBG(a)   if (zcons_debug) cmn_err(CE_NOTE, a)
98*0Sstevel@tonic-gate #define	DBG1(a, b)   if (zcons_debug) cmn_err(CE_NOTE, a, b)
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate 
101*0Sstevel@tonic-gate /*
102*0Sstevel@tonic-gate  * Zone Console Pseudo Terminal Module: stream data structure definitions
103*0Sstevel@tonic-gate  */
104*0Sstevel@tonic-gate static struct module_info zc_info = {
105*0Sstevel@tonic-gate 	31337,	/* c0z we r hAx0rs */
106*0Sstevel@tonic-gate 	"zcons",
107*0Sstevel@tonic-gate 	0,
108*0Sstevel@tonic-gate 	INFPSZ,
109*0Sstevel@tonic-gate 	2048,
110*0Sstevel@tonic-gate 	128
111*0Sstevel@tonic-gate };
112*0Sstevel@tonic-gate 
113*0Sstevel@tonic-gate static struct qinit zc_rinit = {
114*0Sstevel@tonic-gate 	NULL,
115*0Sstevel@tonic-gate 	(int (*)()) zc_rsrv,
116*0Sstevel@tonic-gate 	zc_open,
117*0Sstevel@tonic-gate 	zc_close,
118*0Sstevel@tonic-gate 	NULL,
119*0Sstevel@tonic-gate 	&zc_info,
120*0Sstevel@tonic-gate 	NULL
121*0Sstevel@tonic-gate };
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate static struct qinit zc_winit = {
124*0Sstevel@tonic-gate 	(int (*)()) zc_wput,
125*0Sstevel@tonic-gate 	(int (*)()) zc_wsrv,
126*0Sstevel@tonic-gate 	NULL,
127*0Sstevel@tonic-gate 	NULL,
128*0Sstevel@tonic-gate 	NULL,
129*0Sstevel@tonic-gate 	&zc_info,
130*0Sstevel@tonic-gate 	NULL
131*0Sstevel@tonic-gate };
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate static struct streamtab zc_tab_info = {
134*0Sstevel@tonic-gate 	&zc_rinit,
135*0Sstevel@tonic-gate 	&zc_winit,
136*0Sstevel@tonic-gate 	NULL,
137*0Sstevel@tonic-gate 	NULL
138*0Sstevel@tonic-gate };
139*0Sstevel@tonic-gate 
140*0Sstevel@tonic-gate #define	ZC_CONF_FLAG	(D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL)
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate /*
143*0Sstevel@tonic-gate  * this will define (struct cb_ops cb_zc_ops) and (struct dev_ops zc_ops)
144*0Sstevel@tonic-gate  */
145*0Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(zc_ops, nulldev, nulldev,	zc_attach, zc_detach, nodev, \
146*0Sstevel@tonic-gate 	zc_getinfo, ZC_CONF_FLAG, &zc_tab_info);
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate /*
149*0Sstevel@tonic-gate  * Module linkage information for the kernel.
150*0Sstevel@tonic-gate  */
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate static struct modldrv modldrv = {
153*0Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
154*0Sstevel@tonic-gate 	"Zone console driver 'zcons' %I%",
155*0Sstevel@tonic-gate 	&zc_ops		/* driver ops */
156*0Sstevel@tonic-gate };
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
159*0Sstevel@tonic-gate 	MODREV_1,
160*0Sstevel@tonic-gate 	&modldrv,
161*0Sstevel@tonic-gate 	NULL
162*0Sstevel@tonic-gate };
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate typedef struct zc_state {
165*0Sstevel@tonic-gate 	dev_info_t *zc_devinfo;
166*0Sstevel@tonic-gate 	queue_t *zc_master_rdq;
167*0Sstevel@tonic-gate 	queue_t *zc_slave_rdq;
168*0Sstevel@tonic-gate 	int zc_state;
169*0Sstevel@tonic-gate } zc_state_t;
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate #define	ZC_STATE_MOPEN	0x01
172*0Sstevel@tonic-gate #define	ZC_STATE_SOPEN	0x02
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate static void *zc_soft_state;
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate int
177*0Sstevel@tonic-gate _init(void)
178*0Sstevel@tonic-gate {
179*0Sstevel@tonic-gate 	int err;
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate 	if ((err = ddi_soft_state_init(&zc_soft_state,
182*0Sstevel@tonic-gate 	    sizeof (zc_state_t), 0)) != 0) {
183*0Sstevel@tonic-gate 		return (err);
184*0Sstevel@tonic-gate 	}
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	if ((err = mod_install(&modlinkage)) != 0)
187*0Sstevel@tonic-gate 		ddi_soft_state_fini(zc_soft_state);
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	return (err);
190*0Sstevel@tonic-gate }
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate int
194*0Sstevel@tonic-gate _fini(void)
195*0Sstevel@tonic-gate {
196*0Sstevel@tonic-gate 	int err;
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate 	if ((err = mod_remove(&modlinkage)) != 0) {
199*0Sstevel@tonic-gate 		return (err);
200*0Sstevel@tonic-gate 	}
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	ddi_soft_state_fini(&zc_soft_state);
203*0Sstevel@tonic-gate 	return (0);
204*0Sstevel@tonic-gate }
205*0Sstevel@tonic-gate 
206*0Sstevel@tonic-gate int
207*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
208*0Sstevel@tonic-gate {
209*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
210*0Sstevel@tonic-gate }
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate static int
213*0Sstevel@tonic-gate zc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
214*0Sstevel@tonic-gate {
215*0Sstevel@tonic-gate 	zc_state_t *zcs;
216*0Sstevel@tonic-gate 	int instance;
217*0Sstevel@tonic-gate 
218*0Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
219*0Sstevel@tonic-gate 		return (DDI_FAILURE);
220*0Sstevel@tonic-gate 
221*0Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
222*0Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(zc_soft_state, instance) != DDI_SUCCESS)
223*0Sstevel@tonic-gate 		return (DDI_FAILURE);
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate 	if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR,
226*0Sstevel@tonic-gate 	    instance << 1 | ZC_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
227*0Sstevel@tonic-gate 	    (ddi_create_minor_node(dip, ZCONS_MASTER_NAME, S_IFCHR,
228*0Sstevel@tonic-gate 	    instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
229*0Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
230*0Sstevel@tonic-gate 		ddi_soft_state_free(zc_soft_state, instance);
231*0Sstevel@tonic-gate 		return (DDI_FAILURE);
232*0Sstevel@tonic-gate 	}
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) {
235*0Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
236*0Sstevel@tonic-gate 		ddi_soft_state_free(zc_soft_state, instance);
237*0Sstevel@tonic-gate 		return (DDI_FAILURE);
238*0Sstevel@tonic-gate 	}
239*0Sstevel@tonic-gate 	zcs->zc_devinfo = dip;
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
242*0Sstevel@tonic-gate }
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate static int
245*0Sstevel@tonic-gate zc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
246*0Sstevel@tonic-gate {
247*0Sstevel@tonic-gate 	zc_state_t *zcs;
248*0Sstevel@tonic-gate 	int instance;
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
251*0Sstevel@tonic-gate 		return (DDI_FAILURE);
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
254*0Sstevel@tonic-gate 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
255*0Sstevel@tonic-gate 		return (DDI_FAILURE);
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	if ((zcs->zc_state & ZC_STATE_MOPEN) ||
258*0Sstevel@tonic-gate 	    (zcs->zc_state & ZC_STATE_SOPEN)) {
259*0Sstevel@tonic-gate 		DBG1("zc_detach: device (dip=%p) still open\n", (void *)dip);
260*0Sstevel@tonic-gate 		return (DDI_FAILURE);
261*0Sstevel@tonic-gate 	}
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
264*0Sstevel@tonic-gate 	ddi_soft_state_free(zc_soft_state, instance);
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
267*0Sstevel@tonic-gate }
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate /*
270*0Sstevel@tonic-gate  * zc_getinfo()
271*0Sstevel@tonic-gate  *	getinfo(9e) entrypoint.
272*0Sstevel@tonic-gate  */
273*0Sstevel@tonic-gate /*ARGSUSED*/
274*0Sstevel@tonic-gate static int
275*0Sstevel@tonic-gate zc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
276*0Sstevel@tonic-gate {
277*0Sstevel@tonic-gate 	zc_state_t *zcs;
278*0Sstevel@tonic-gate 	int instance = ZC_INSTANCE((dev_t)arg);
279*0Sstevel@tonic-gate 
280*0Sstevel@tonic-gate 	switch (infocmd) {
281*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
282*0Sstevel@tonic-gate 		if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
283*0Sstevel@tonic-gate 			return (DDI_FAILURE);
284*0Sstevel@tonic-gate 		*result = zcs->zc_devinfo;
285*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
286*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
287*0Sstevel@tonic-gate 		*result = (void *)(uintptr_t)instance;
288*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
289*0Sstevel@tonic-gate 	}
290*0Sstevel@tonic-gate 	return (DDI_FAILURE);
291*0Sstevel@tonic-gate }
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate /*
294*0Sstevel@tonic-gate  * Return the equivalent queue from the other side of the relationship.
295*0Sstevel@tonic-gate  * e.g.: given the slave's write queue, return the master's write queue.
296*0Sstevel@tonic-gate  */
297*0Sstevel@tonic-gate static queue_t *
298*0Sstevel@tonic-gate zc_switch(queue_t *qp)
299*0Sstevel@tonic-gate {
300*0Sstevel@tonic-gate 	zc_state_t *zcs = qp->q_ptr;
301*0Sstevel@tonic-gate 	ASSERT(zcs != NULL);
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	if (qp == zcs->zc_master_rdq)
304*0Sstevel@tonic-gate 		return (zcs->zc_slave_rdq);
305*0Sstevel@tonic-gate 	else if (OTHERQ(qp) == zcs->zc_master_rdq && zcs->zc_slave_rdq != NULL)
306*0Sstevel@tonic-gate 		return (OTHERQ(zcs->zc_slave_rdq));
307*0Sstevel@tonic-gate 	else if (qp == zcs->zc_slave_rdq)
308*0Sstevel@tonic-gate 		return (zcs->zc_master_rdq);
309*0Sstevel@tonic-gate 	else if (OTHERQ(qp) == zcs->zc_slave_rdq && zcs->zc_master_rdq != NULL)
310*0Sstevel@tonic-gate 		return (OTHERQ(zcs->zc_master_rdq));
311*0Sstevel@tonic-gate 	else
312*0Sstevel@tonic-gate 		return (NULL);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate /*
316*0Sstevel@tonic-gate  * For debugging and outputting messages.  Returns the name of the side of
317*0Sstevel@tonic-gate  * the relationship associated with this queue.
318*0Sstevel@tonic-gate  */
319*0Sstevel@tonic-gate static const char *
320*0Sstevel@tonic-gate zc_side(queue_t *qp)
321*0Sstevel@tonic-gate {
322*0Sstevel@tonic-gate 	zc_state_t *zcs = qp->q_ptr;
323*0Sstevel@tonic-gate 	ASSERT(zcs != NULL);
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 	if (qp == zcs->zc_master_rdq ||
326*0Sstevel@tonic-gate 	    OTHERQ(qp) == zcs->zc_master_rdq) {
327*0Sstevel@tonic-gate 		return ("master");
328*0Sstevel@tonic-gate 	}
329*0Sstevel@tonic-gate 	ASSERT(qp == zcs->zc_slave_rdq || OTHERQ(qp) == zcs->zc_slave_rdq);
330*0Sstevel@tonic-gate 	return ("slave");
331*0Sstevel@tonic-gate }
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate /*ARGSUSED*/
334*0Sstevel@tonic-gate static int
335*0Sstevel@tonic-gate zc_master_open(zc_state_t *zcs,
336*0Sstevel@tonic-gate     queue_t	*rqp,	/* pointer to the read side queue */
337*0Sstevel@tonic-gate     dev_t	*devp,	/* pointer to stream tail's dev */
338*0Sstevel@tonic-gate     int		oflag,	/* the user open(2) supplied flags */
339*0Sstevel@tonic-gate     int		sflag,	/* open state flag */
340*0Sstevel@tonic-gate     cred_t	*credp)	/* credentials */
341*0Sstevel@tonic-gate {
342*0Sstevel@tonic-gate 	mblk_t *mop;
343*0Sstevel@tonic-gate 	struct stroptions *sop;
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 	/*
346*0Sstevel@tonic-gate 	 * Enforce exclusivity on the master side; the only consumer should
347*0Sstevel@tonic-gate 	 * be the zoneadmd for the zone.
348*0Sstevel@tonic-gate 	 */
349*0Sstevel@tonic-gate 	if ((zcs->zc_state & ZC_STATE_MOPEN) != 0)
350*0Sstevel@tonic-gate 		return (EBUSY);
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
353*0Sstevel@tonic-gate 		DBG("zc_master_open(): mop allocation failed\n");
354*0Sstevel@tonic-gate 		return (ENOMEM);
355*0Sstevel@tonic-gate 	}
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 	zcs->zc_state |= ZC_STATE_MOPEN;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/*
360*0Sstevel@tonic-gate 	 * q_ptr stores driver private data; stash the soft state data on both
361*0Sstevel@tonic-gate 	 * read and write sides of the queue.
362*0Sstevel@tonic-gate 	 */
363*0Sstevel@tonic-gate 	WR(rqp)->q_ptr = rqp->q_ptr = zcs;
364*0Sstevel@tonic-gate 	qprocson(rqp);
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 	/*
367*0Sstevel@tonic-gate 	 * Following qprocson(), the master side is fully plumbed into the
368*0Sstevel@tonic-gate 	 * STREAM and may send/receive messages.  Setting zcs->zc_master_rdq
369*0Sstevel@tonic-gate 	 * will allow the slave to send messages to us (the master).
370*0Sstevel@tonic-gate 	 * This cannot occur before qprocson() because the master is not
371*0Sstevel@tonic-gate 	 * ready to process them until that point.
372*0Sstevel@tonic-gate 	 */
373*0Sstevel@tonic-gate 	zcs->zc_master_rdq = rqp;
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	/*
376*0Sstevel@tonic-gate 	 * set up hi/lo water marks on stream head read queue and add
377*0Sstevel@tonic-gate 	 * controlling tty as needed.
378*0Sstevel@tonic-gate 	 */
379*0Sstevel@tonic-gate 	mop->b_datap->db_type = M_SETOPTS;
380*0Sstevel@tonic-gate 	mop->b_wptr += sizeof (struct stroptions);
381*0Sstevel@tonic-gate 	sop = (struct stroptions *)mop->b_rptr;
382*0Sstevel@tonic-gate 	if (oflag & FNOCTTY)
383*0Sstevel@tonic-gate 		sop->so_flags = SO_HIWAT | SO_LOWAT;
384*0Sstevel@tonic-gate 	else
385*0Sstevel@tonic-gate 		sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
386*0Sstevel@tonic-gate 	sop->so_hiwat = 512;
387*0Sstevel@tonic-gate 	sop->so_lowat = 256;
388*0Sstevel@tonic-gate 	putnext(rqp, mop);
389*0Sstevel@tonic-gate 
390*0Sstevel@tonic-gate 	return (0);
391*0Sstevel@tonic-gate }
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate /*ARGSUSED*/
394*0Sstevel@tonic-gate static int
395*0Sstevel@tonic-gate zc_slave_open(zc_state_t *zcs,
396*0Sstevel@tonic-gate     queue_t	*rqp,	/* pointer to the read side queue */
397*0Sstevel@tonic-gate     dev_t	*devp,	/* pointer to stream tail's dev */
398*0Sstevel@tonic-gate     int		oflag,	/* the user open(2) supplied flags */
399*0Sstevel@tonic-gate     int		sflag,	/* open state flag */
400*0Sstevel@tonic-gate     cred_t	*credp)	/* credentials */
401*0Sstevel@tonic-gate {
402*0Sstevel@tonic-gate 	mblk_t *mop;
403*0Sstevel@tonic-gate 	struct stroptions *sop;
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	/*
406*0Sstevel@tonic-gate 	 * The slave side can be opened as many times as needed.
407*0Sstevel@tonic-gate 	 */
408*0Sstevel@tonic-gate 	if ((zcs->zc_state & ZC_STATE_SOPEN) != 0) {
409*0Sstevel@tonic-gate 		ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zcs));
410*0Sstevel@tonic-gate 		return (0);
411*0Sstevel@tonic-gate 	}
412*0Sstevel@tonic-gate 
413*0Sstevel@tonic-gate 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
414*0Sstevel@tonic-gate 		DBG("zc_slave_open(): mop allocation failed\n");
415*0Sstevel@tonic-gate 		return (ENOMEM);
416*0Sstevel@tonic-gate 	}
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 	zcs->zc_state |= ZC_STATE_SOPEN;
419*0Sstevel@tonic-gate 
420*0Sstevel@tonic-gate 	/*
421*0Sstevel@tonic-gate 	 * q_ptr stores driver private data; stash the soft state data on both
422*0Sstevel@tonic-gate 	 * read and write sides of the queue.
423*0Sstevel@tonic-gate 	 */
424*0Sstevel@tonic-gate 	WR(rqp)->q_ptr = rqp->q_ptr = zcs;
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	qprocson(rqp);
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	/*
429*0Sstevel@tonic-gate 	 * Must follow qprocson(), since we aren't ready to process until then.
430*0Sstevel@tonic-gate 	 */
431*0Sstevel@tonic-gate 	zcs->zc_slave_rdq = rqp;
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	/*
434*0Sstevel@tonic-gate 	 * set up hi/lo water marks on stream head read queue and add
435*0Sstevel@tonic-gate 	 * controlling tty as needed.
436*0Sstevel@tonic-gate 	 */
437*0Sstevel@tonic-gate 	mop->b_datap->db_type = M_SETOPTS;
438*0Sstevel@tonic-gate 	mop->b_wptr += sizeof (struct stroptions);
439*0Sstevel@tonic-gate 	sop = (struct stroptions *)mop->b_rptr;
440*0Sstevel@tonic-gate 	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
441*0Sstevel@tonic-gate 	sop->so_hiwat = 512;
442*0Sstevel@tonic-gate 	sop->so_lowat = 256;
443*0Sstevel@tonic-gate 	putnext(rqp, mop);
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate 	return (0);
446*0Sstevel@tonic-gate }
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate /*
449*0Sstevel@tonic-gate  * open(9e) entrypoint; checks sflag, and rejects anything unordinary.
450*0Sstevel@tonic-gate  */
451*0Sstevel@tonic-gate static int
452*0Sstevel@tonic-gate zc_open(queue_t *rqp,		/* pointer to the read side queue */
453*0Sstevel@tonic-gate 	dev_t   *devp,		/* pointer to stream tail's dev */
454*0Sstevel@tonic-gate 	int	oflag,		/* the user open(2) supplied flags */
455*0Sstevel@tonic-gate 	int	sflag,		/* open state flag */
456*0Sstevel@tonic-gate 	cred_t  *credp)		/* credentials */
457*0Sstevel@tonic-gate {
458*0Sstevel@tonic-gate 	int instance = ZC_INSTANCE(*devp);
459*0Sstevel@tonic-gate 	int ret;
460*0Sstevel@tonic-gate 	zc_state_t *zcs;
461*0Sstevel@tonic-gate 
462*0Sstevel@tonic-gate 	if (sflag != 0)
463*0Sstevel@tonic-gate 		return (EINVAL);
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
466*0Sstevel@tonic-gate 		return (ENXIO);
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate 	switch (ZC_NODE(*devp)) {
469*0Sstevel@tonic-gate 	case ZC_MASTER_MINOR:
470*0Sstevel@tonic-gate 		ret = zc_master_open(zcs, rqp, devp, oflag, sflag, credp);
471*0Sstevel@tonic-gate 		break;
472*0Sstevel@tonic-gate 	case ZC_SLAVE_MINOR:
473*0Sstevel@tonic-gate 		ret = zc_slave_open(zcs, rqp, devp, oflag, sflag, credp);
474*0Sstevel@tonic-gate 		break;
475*0Sstevel@tonic-gate 	default:
476*0Sstevel@tonic-gate 		ret = ENXIO;
477*0Sstevel@tonic-gate 		break;
478*0Sstevel@tonic-gate 	}
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate 	return (ret);
481*0Sstevel@tonic-gate }
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate /*
484*0Sstevel@tonic-gate  * close(9e) entrypoint.
485*0Sstevel@tonic-gate  */
486*0Sstevel@tonic-gate /*ARGSUSED1*/
487*0Sstevel@tonic-gate static int
488*0Sstevel@tonic-gate zc_close(queue_t *rqp, int flag, cred_t *credp)
489*0Sstevel@tonic-gate {
490*0Sstevel@tonic-gate 	queue_t *wqp;
491*0Sstevel@tonic-gate 	mblk_t	*bp;
492*0Sstevel@tonic-gate 	zc_state_t *zcs;
493*0Sstevel@tonic-gate 
494*0Sstevel@tonic-gate 	zcs = (zc_state_t *)rqp->q_ptr;
495*0Sstevel@tonic-gate 
496*0Sstevel@tonic-gate 	if (rqp == zcs->zc_master_rdq) {
497*0Sstevel@tonic-gate 		DBG("Closing master side");
498*0Sstevel@tonic-gate 
499*0Sstevel@tonic-gate 		zcs->zc_master_rdq = NULL;
500*0Sstevel@tonic-gate 		zcs->zc_state &= ~ZC_STATE_MOPEN;
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 		/*
503*0Sstevel@tonic-gate 		 * qenable slave side write queue so that it can flush
504*0Sstevel@tonic-gate 		 * its messages as master's read queue is going away
505*0Sstevel@tonic-gate 		 */
506*0Sstevel@tonic-gate 		if (zcs->zc_slave_rdq != NULL) {
507*0Sstevel@tonic-gate 			qenable(WR(zcs->zc_slave_rdq));
508*0Sstevel@tonic-gate 		}
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 		qprocsoff(rqp);
511*0Sstevel@tonic-gate 		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	} else if (rqp == zcs->zc_slave_rdq) {
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 		DBG("Closing slave side");
516*0Sstevel@tonic-gate 		zcs->zc_state &= ~ZC_STATE_SOPEN;
517*0Sstevel@tonic-gate 		zcs->zc_slave_rdq = NULL;
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 		wqp = WR(rqp);
520*0Sstevel@tonic-gate 		while ((bp = getq(wqp)) != NULL) {
521*0Sstevel@tonic-gate 			if (zcs->zc_master_rdq != NULL)
522*0Sstevel@tonic-gate 				putnext(zcs->zc_master_rdq, bp);
523*0Sstevel@tonic-gate 			else if (bp->b_datap->db_type == M_IOCTL)
524*0Sstevel@tonic-gate 				miocnak(wqp, bp, 0, 0);
525*0Sstevel@tonic-gate 			else
526*0Sstevel@tonic-gate 				freemsg(bp);
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate 		/*
530*0Sstevel@tonic-gate 		 * Qenable master side write queue so that it can flush its
531*0Sstevel@tonic-gate 		 * messages as slaves's read queue is going away.
532*0Sstevel@tonic-gate 		 */
533*0Sstevel@tonic-gate 		if (zcs->zc_master_rdq != NULL)
534*0Sstevel@tonic-gate 			qenable(WR(zcs->zc_master_rdq));
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 		qprocsoff(rqp);
537*0Sstevel@tonic-gate 		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
538*0Sstevel@tonic-gate 	}
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 	return (0);
541*0Sstevel@tonic-gate }
542*0Sstevel@tonic-gate 
543*0Sstevel@tonic-gate static void
544*0Sstevel@tonic-gate handle_mflush(queue_t *qp, mblk_t *mp)
545*0Sstevel@tonic-gate {
546*0Sstevel@tonic-gate 	mblk_t *nmp;
547*0Sstevel@tonic-gate 	DBG1("M_FLUSH on %s side", zc_side(qp));
548*0Sstevel@tonic-gate 
549*0Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHW) {
550*0Sstevel@tonic-gate 		DBG1("M_FLUSH, FLUSHW, %s side", zc_side(qp));
551*0Sstevel@tonic-gate 		flushq(qp, FLUSHDATA);
552*0Sstevel@tonic-gate 		*mp->b_rptr &= ~FLUSHW;
553*0Sstevel@tonic-gate 		if ((*mp->b_rptr & FLUSHR) == 0) {
554*0Sstevel@tonic-gate 			/*
555*0Sstevel@tonic-gate 			 * FLUSHW only. Change to FLUSHR and putnext other side,
556*0Sstevel@tonic-gate 			 * then we are done.
557*0Sstevel@tonic-gate 			 */
558*0Sstevel@tonic-gate 			*mp->b_rptr |= FLUSHR;
559*0Sstevel@tonic-gate 			if (zc_switch(RD(qp)) != NULL) {
560*0Sstevel@tonic-gate 				putnext(zc_switch(RD(qp)), mp);
561*0Sstevel@tonic-gate 				return;
562*0Sstevel@tonic-gate 			}
563*0Sstevel@tonic-gate 		} else if ((zc_switch(RD(qp)) != NULL) &&
564*0Sstevel@tonic-gate 		    (nmp = copyb(mp)) != NULL) {
565*0Sstevel@tonic-gate 			/*
566*0Sstevel@tonic-gate 			 * It is a FLUSHRW; we copy the mblk and send
567*0Sstevel@tonic-gate 			 * it to the other side, since we still need to use
568*0Sstevel@tonic-gate 			 * the mblk in FLUSHR processing, below.
569*0Sstevel@tonic-gate 			 */
570*0Sstevel@tonic-gate 			putnext(zc_switch(RD(qp)), nmp);
571*0Sstevel@tonic-gate 		}
572*0Sstevel@tonic-gate 	}
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate 	if (*mp->b_rptr & FLUSHR) {
575*0Sstevel@tonic-gate 		DBG("qreply(qp) turning FLUSHR around\n");
576*0Sstevel@tonic-gate 		qreply(qp, mp);
577*0Sstevel@tonic-gate 		return;
578*0Sstevel@tonic-gate 	}
579*0Sstevel@tonic-gate 	freemsg(mp);
580*0Sstevel@tonic-gate }
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate /*
583*0Sstevel@tonic-gate  * wput(9E) is symmetric for master and slave sides, so this handles both
584*0Sstevel@tonic-gate  * without splitting the codepath.
585*0Sstevel@tonic-gate  *
586*0Sstevel@tonic-gate  * zc_wput() looks at the other side; if there is no process holding that
587*0Sstevel@tonic-gate  * side open, it frees the message.  This prevents processes from hanging
588*0Sstevel@tonic-gate  * if no one is holding open the console.  Otherwise, it putnext's high
589*0Sstevel@tonic-gate  * priority messages, putnext's normal messages if possible, and otherwise
590*0Sstevel@tonic-gate  * enqueues the messages; in the case that something is enqueued, wsrv(9E)
591*0Sstevel@tonic-gate  * will take care of eventually shuttling I/O to the other side.
592*0Sstevel@tonic-gate  */
593*0Sstevel@tonic-gate static void
594*0Sstevel@tonic-gate zc_wput(queue_t *qp, mblk_t *mp)
595*0Sstevel@tonic-gate {
596*0Sstevel@tonic-gate 	unsigned char type = mp->b_datap->db_type;
597*0Sstevel@tonic-gate 
598*0Sstevel@tonic-gate 	ASSERT(qp->q_ptr);
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 	DBG1("entering zc_wput, %s side", zc_side(qp));
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	if (zc_switch(RD(qp)) == NULL) {
603*0Sstevel@tonic-gate 		DBG1("wput to %s side (no one listening)", zc_side(qp));
604*0Sstevel@tonic-gate 		switch (type) {
605*0Sstevel@tonic-gate 		case M_FLUSH:
606*0Sstevel@tonic-gate 			handle_mflush(qp, mp);
607*0Sstevel@tonic-gate 			break;
608*0Sstevel@tonic-gate 		case M_IOCTL:
609*0Sstevel@tonic-gate 			miocnak(qp, mp, 0, 0);
610*0Sstevel@tonic-gate 			break;
611*0Sstevel@tonic-gate 		default:
612*0Sstevel@tonic-gate 			freemsg(mp);
613*0Sstevel@tonic-gate 			break;
614*0Sstevel@tonic-gate 		}
615*0Sstevel@tonic-gate 		return;
616*0Sstevel@tonic-gate 	}
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	if (type >= QPCTL) {
619*0Sstevel@tonic-gate 		DBG1("(hipri) wput, %s side", zc_side(qp));
620*0Sstevel@tonic-gate 		switch (type) {
621*0Sstevel@tonic-gate 		case M_READ:		/* supposedly from ldterm? */
622*0Sstevel@tonic-gate 			DBG("zc_wput: tossing M_READ\n");
623*0Sstevel@tonic-gate 			freemsg(mp);
624*0Sstevel@tonic-gate 			break;
625*0Sstevel@tonic-gate 		case M_FLUSH:
626*0Sstevel@tonic-gate 			handle_mflush(qp, mp);
627*0Sstevel@tonic-gate 			break;
628*0Sstevel@tonic-gate 		default:
629*0Sstevel@tonic-gate 			/*
630*0Sstevel@tonic-gate 			 * Put this to the other side.
631*0Sstevel@tonic-gate 			 */
632*0Sstevel@tonic-gate 			ASSERT(zc_switch(RD(qp)) != NULL);
633*0Sstevel@tonic-gate 			putnext(zc_switch(RD(qp)), mp);
634*0Sstevel@tonic-gate 			break;
635*0Sstevel@tonic-gate 		}
636*0Sstevel@tonic-gate 		DBG1("done (hipri) wput, %s side", zc_side(qp));
637*0Sstevel@tonic-gate 		return;
638*0Sstevel@tonic-gate 	}
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 	/*
641*0Sstevel@tonic-gate 	 * Only putnext if there isn't already something in the queue.
642*0Sstevel@tonic-gate 	 * otherwise things would wind up out of order.
643*0Sstevel@tonic-gate 	 */
644*0Sstevel@tonic-gate 	if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) {
645*0Sstevel@tonic-gate 		DBG("wput: putting message to other side\n");
646*0Sstevel@tonic-gate 		putnext(RD(zc_switch(qp)), mp);
647*0Sstevel@tonic-gate 	} else {
648*0Sstevel@tonic-gate 		DBG("wput: putting msg onto queue\n");
649*0Sstevel@tonic-gate 		(void) putq(qp, mp);
650*0Sstevel@tonic-gate 	}
651*0Sstevel@tonic-gate 	DBG1("done wput, %s side", zc_side(qp));
652*0Sstevel@tonic-gate }
653*0Sstevel@tonic-gate 
654*0Sstevel@tonic-gate /*
655*0Sstevel@tonic-gate  * rsrv(9E) is symmetric for master and slave, so zc_rsrv() handles both
656*0Sstevel@tonic-gate  * without splitting up the codepath.
657*0Sstevel@tonic-gate  *
658*0Sstevel@tonic-gate  * Enable the write side of the partner.  This triggers the partner to send
659*0Sstevel@tonic-gate  * messages queued on its write side to this queue's read side.
660*0Sstevel@tonic-gate  */
661*0Sstevel@tonic-gate static void
662*0Sstevel@tonic-gate zc_rsrv(queue_t *qp)
663*0Sstevel@tonic-gate {
664*0Sstevel@tonic-gate 	zc_state_t *zcs;
665*0Sstevel@tonic-gate 	zcs = (zc_state_t *)qp->q_ptr;
666*0Sstevel@tonic-gate 
667*0Sstevel@tonic-gate 	/*
668*0Sstevel@tonic-gate 	 * Care must be taken here, as either of the master or slave side
669*0Sstevel@tonic-gate 	 * qptr could be NULL.
670*0Sstevel@tonic-gate 	 */
671*0Sstevel@tonic-gate 	ASSERT(qp == zcs->zc_master_rdq || qp == zcs->zc_slave_rdq);
672*0Sstevel@tonic-gate 	if (zc_switch(qp) == NULL) {
673*0Sstevel@tonic-gate 		DBG("zc_rsrv: other side isn't listening\n");
674*0Sstevel@tonic-gate 		return;
675*0Sstevel@tonic-gate 	}
676*0Sstevel@tonic-gate 	qenable(WR(zc_switch(qp)));
677*0Sstevel@tonic-gate }
678*0Sstevel@tonic-gate 
679*0Sstevel@tonic-gate /*
680*0Sstevel@tonic-gate  * This routine is symmetric for master and slave, so it handles both without
681*0Sstevel@tonic-gate  * splitting up the codepath.
682*0Sstevel@tonic-gate  *
683*0Sstevel@tonic-gate  * If there are messages on this queue that can be sent to the other, send
684*0Sstevel@tonic-gate  * them via putnext(). Else, if queued messages cannot be sent, leave them
685*0Sstevel@tonic-gate  * on this queue.
686*0Sstevel@tonic-gate  */
687*0Sstevel@tonic-gate static void
688*0Sstevel@tonic-gate zc_wsrv(queue_t *qp)
689*0Sstevel@tonic-gate {
690*0Sstevel@tonic-gate 	mblk_t *mp;
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	DBG1("zc_wsrv master (%s) side", zc_side(qp));
693*0Sstevel@tonic-gate 
694*0Sstevel@tonic-gate 	/*
695*0Sstevel@tonic-gate 	 * Partner has no read queue, so take the data, and throw it away.
696*0Sstevel@tonic-gate 	 */
697*0Sstevel@tonic-gate 	if (zc_switch(RD(qp)) == NULL) {
698*0Sstevel@tonic-gate 		DBG("zc_wsrv: other side isn't listening");
699*0Sstevel@tonic-gate 		while ((mp = getq(qp)) != NULL) {
700*0Sstevel@tonic-gate 			if (mp->b_datap->db_type == M_IOCTL)
701*0Sstevel@tonic-gate 				miocnak(qp, mp, 0, 0);
702*0Sstevel@tonic-gate 			else
703*0Sstevel@tonic-gate 				freemsg(mp);
704*0Sstevel@tonic-gate 		}
705*0Sstevel@tonic-gate 		flushq(qp, FLUSHALL);
706*0Sstevel@tonic-gate 		return;
707*0Sstevel@tonic-gate 	}
708*0Sstevel@tonic-gate 
709*0Sstevel@tonic-gate 	/*
710*0Sstevel@tonic-gate 	 * while there are messages on this write queue...
711*0Sstevel@tonic-gate 	 */
712*0Sstevel@tonic-gate 	while ((mp = getq(qp)) != NULL) {
713*0Sstevel@tonic-gate 		/*
714*0Sstevel@tonic-gate 		 * Due to the way zc_wput is implemented, we should never
715*0Sstevel@tonic-gate 		 * see a control message here.
716*0Sstevel@tonic-gate 		 */
717*0Sstevel@tonic-gate 		ASSERT(mp->b_datap->db_type < QPCTL);
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 		if (bcanputnext(RD(zc_switch(qp)), mp->b_band)) {
720*0Sstevel@tonic-gate 			DBG("wsrv: send message to other side\n");
721*0Sstevel@tonic-gate 			putnext(RD(zc_switch(qp)), mp);
722*0Sstevel@tonic-gate 		} else {
723*0Sstevel@tonic-gate 			DBG("wsrv: putting msg back on queue\n");
724*0Sstevel@tonic-gate 			(void) putbq(qp, mp);
725*0Sstevel@tonic-gate 			break;
726*0Sstevel@tonic-gate 		}
727*0Sstevel@tonic-gate 	}
728*0Sstevel@tonic-gate }
729