xref: /onnv-gate/usr/src/uts/common/io/pts.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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27*0Sstevel@tonic-gate /*	  All Rights Reserved  	*/
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVR4 1.13    */
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate /*
33*0Sstevel@tonic-gate  * Pseudo Terminal Slave Driver.
34*0Sstevel@tonic-gate  *
35*0Sstevel@tonic-gate  * The pseudo-tty subsystem simulates a terminal connection, where the master
36*0Sstevel@tonic-gate  * side represents the terminal and the slave represents the user process's
37*0Sstevel@tonic-gate  * special device end point. The master device is set up as a cloned device
38*0Sstevel@tonic-gate  * where its major device number is the major for the clone device and its minor
39*0Sstevel@tonic-gate  * device number is the major for the ptm driver. There are no nodes in the file
40*0Sstevel@tonic-gate  * system for master devices. The master pseudo driver is opened using the
41*0Sstevel@tonic-gate  * open(2) system call with /dev/ptmx as the device parameter.  The clone open
42*0Sstevel@tonic-gate  * finds the next available minor device for the ptm major device.
43*0Sstevel@tonic-gate  *
44*0Sstevel@tonic-gate  * A master device is available only if it and its corresponding slave device
45*0Sstevel@tonic-gate  * are not already open. When the master device is opened, the corresponding
46*0Sstevel@tonic-gate  * slave device is automatically locked out. Only one open is allowed on a
47*0Sstevel@tonic-gate  * master device.  Multiple opens are allowed on the slave device.  After both
48*0Sstevel@tonic-gate  * the master and slave have been opened, the user has two file descriptors
49*0Sstevel@tonic-gate  * which are the end points of a full duplex connection composed of two streams
50*0Sstevel@tonic-gate  * which are automatically connected at the master and slave drivers. The user
51*0Sstevel@tonic-gate  * may then push modules onto either side of the stream pair.
52*0Sstevel@tonic-gate  *
53*0Sstevel@tonic-gate  * The master and slave drivers pass all messages to their adjacent queues.
54*0Sstevel@tonic-gate  * Only the M_FLUSH needs some processing.  Because the read queue of one side
55*0Sstevel@tonic-gate  * is connected to the write queue of the other, the FLUSHR flag is changed to
56*0Sstevel@tonic-gate  * the FLUSHW flag and vice versa. When the master device is closed an M_HANGUP
57*0Sstevel@tonic-gate  * message is sent to the slave device which will render the device
58*0Sstevel@tonic-gate  * unusable. The process on the slave side gets the EIO when attempting to write
59*0Sstevel@tonic-gate  * on that stream but it will be able to read any data remaining on the stream
60*0Sstevel@tonic-gate  * head read queue.  When all the data has been read, read() returns 0
61*0Sstevel@tonic-gate  * indicating that the stream can no longer be used.  On the last close of the
62*0Sstevel@tonic-gate  * slave device, a 0-length message is sent to the master device. When the
63*0Sstevel@tonic-gate  * application on the master side issues a read() or getmsg() and 0 is returned,
64*0Sstevel@tonic-gate  * the user of the master device decides whether to issue a close() that
65*0Sstevel@tonic-gate  * dismantles the pseudo-terminal subsystem. If the master device is not closed,
66*0Sstevel@tonic-gate  * the pseudo-tty subsystem will be available to another user to open the slave
67*0Sstevel@tonic-gate  * device.
68*0Sstevel@tonic-gate  *
69*0Sstevel@tonic-gate  * Synchronization:
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  *   All global data synchronization between ptm/pts is done via global
72*0Sstevel@tonic-gate  *   ptms_lock mutex which is initialized at system boot time from
73*0Sstevel@tonic-gate  *   ptms_initspace (called from space.c).
74*0Sstevel@tonic-gate  *
75*0Sstevel@tonic-gate  *   Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
76*0Sstevel@tonic-gate  *   pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
77*0Sstevel@tonic-gate  *
78*0Sstevel@tonic-gate  *   PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
79*0Sstevel@tonic-gate  *   which allow reader locks to be reacquired by the same thread (usual
80*0Sstevel@tonic-gate  *   reader/writer locks can't be used for that purpose since it is illegal for
81*0Sstevel@tonic-gate  *   a thread to acquire a lock it already holds, even as a reader). The sole
82*0Sstevel@tonic-gate  *   purpose of these macros is to guarantee that the peer queue will not
83*0Sstevel@tonic-gate  *   disappear (due to closing peer) while it is used. It is safe to use
84*0Sstevel@tonic-gate  *   PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
85*0Sstevel@tonic-gate  *   they are not real locks but reference counts).
86*0Sstevel@tonic-gate  *
87*0Sstevel@tonic-gate  *   PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave
88*0Sstevel@tonic-gate  *   open/close paths to modify ptm_rdq and pts_rdq fields. These fields should
89*0Sstevel@tonic-gate  *   be set to appropriate queues *after* qprocson() is called during open (to
90*0Sstevel@tonic-gate  *   prevent peer from accessing the queue with incomplete plumbing) and set to
91*0Sstevel@tonic-gate  *   NULL before qprocsoff() is called during close.
92*0Sstevel@tonic-gate  *
93*0Sstevel@tonic-gate  *   The pt_nullmsg field is only used in open/close routines and it is also
94*0Sstevel@tonic-gate  *   protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
95*0Sstevel@tonic-gate  *   holds.
96*0Sstevel@tonic-gate  *
97*0Sstevel@tonic-gate  * Lock Ordering:
98*0Sstevel@tonic-gate  *
99*0Sstevel@tonic-gate  *   If both ptms_lock and per-pty lock should be held, ptms_lock should always
100*0Sstevel@tonic-gate  *   be entered first, followed by per-pty lock.
101*0Sstevel@tonic-gate  *
102*0Sstevel@tonic-gate  * See ptms.h, ptm.c and ptms_conf.c fore more information.
103*0Sstevel@tonic-gate  *
104*0Sstevel@tonic-gate  */
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate #include <sys/types.h>
107*0Sstevel@tonic-gate #include <sys/param.h>
108*0Sstevel@tonic-gate #include <sys/sysmacros.h>
109*0Sstevel@tonic-gate #include <sys/stream.h>
110*0Sstevel@tonic-gate #include <sys/stropts.h>
111*0Sstevel@tonic-gate #include <sys/stat.h>
112*0Sstevel@tonic-gate #include <sys/errno.h>
113*0Sstevel@tonic-gate #include <sys/debug.h>
114*0Sstevel@tonic-gate #include <sys/cmn_err.h>
115*0Sstevel@tonic-gate #include <sys/ptms.h>
116*0Sstevel@tonic-gate #include <sys/systm.h>
117*0Sstevel@tonic-gate #include <sys/modctl.h>
118*0Sstevel@tonic-gate #include <sys/conf.h>
119*0Sstevel@tonic-gate #include <sys/ddi.h>
120*0Sstevel@tonic-gate #include <sys/sunddi.h>
121*0Sstevel@tonic-gate #include <sys/cred.h>
122*0Sstevel@tonic-gate #include <sys/zone.h>
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate #ifdef DEBUG
125*0Sstevel@tonic-gate int pts_debug = 0;
126*0Sstevel@tonic-gate #define	DBG(a)	 if (pts_debug) cmn_err(CE_NOTE, a)
127*0Sstevel@tonic-gate #else
128*0Sstevel@tonic-gate #define	DBG(a)
129*0Sstevel@tonic-gate #endif
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate static int ptsopen(queue_t *, dev_t *, int, int, cred_t *);
132*0Sstevel@tonic-gate static int ptsclose(queue_t *, int, cred_t *);
133*0Sstevel@tonic-gate static void ptswput(queue_t *, mblk_t *);
134*0Sstevel@tonic-gate static void ptsrsrv(queue_t *);
135*0Sstevel@tonic-gate static void ptswsrv(queue_t *);
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate /*
138*0Sstevel@tonic-gate  * Slave Stream Pseudo Terminal Module: stream data structure definitions
139*0Sstevel@tonic-gate  */
140*0Sstevel@tonic-gate static struct module_info pts_info = {
141*0Sstevel@tonic-gate 	0xface,
142*0Sstevel@tonic-gate 	"pts",
143*0Sstevel@tonic-gate 	0,
144*0Sstevel@tonic-gate 	512,
145*0Sstevel@tonic-gate 	512,
146*0Sstevel@tonic-gate 	128
147*0Sstevel@tonic-gate };
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate static struct qinit ptsrint = {
150*0Sstevel@tonic-gate 	NULL,
151*0Sstevel@tonic-gate 	(int (*)()) ptsrsrv,
152*0Sstevel@tonic-gate 	ptsopen,
153*0Sstevel@tonic-gate 	ptsclose,
154*0Sstevel@tonic-gate 	NULL,
155*0Sstevel@tonic-gate 	&pts_info,
156*0Sstevel@tonic-gate 	NULL
157*0Sstevel@tonic-gate };
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate static struct qinit ptswint = {
160*0Sstevel@tonic-gate 	(int (*)()) ptswput,
161*0Sstevel@tonic-gate 	(int (*)()) ptswsrv,
162*0Sstevel@tonic-gate 	NULL,
163*0Sstevel@tonic-gate 	NULL,
164*0Sstevel@tonic-gate 	NULL,
165*0Sstevel@tonic-gate 	&pts_info,
166*0Sstevel@tonic-gate 	NULL
167*0Sstevel@tonic-gate };
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate static struct streamtab ptsinfo = {
170*0Sstevel@tonic-gate 	&ptsrint,
171*0Sstevel@tonic-gate 	&ptswint,
172*0Sstevel@tonic-gate 	NULL,
173*0Sstevel@tonic-gate 	NULL
174*0Sstevel@tonic-gate };
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate static int pts_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
177*0Sstevel@tonic-gate static int pts_attach(dev_info_t *, ddi_attach_cmd_t);
178*0Sstevel@tonic-gate static int pts_detach(dev_info_t *, ddi_detach_cmd_t);
179*0Sstevel@tonic-gate 
180*0Sstevel@tonic-gate #define	PTS_CONF_FLAG	(D_NEW | D_MP)
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate /*
183*0Sstevel@tonic-gate  * this will define (struct cb_ops cb_pts_ops) and (struct dev_ops pts_ops)
184*0Sstevel@tonic-gate  */
185*0Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(pts_ops, nulldev, nulldev,	\
186*0Sstevel@tonic-gate 	pts_attach, pts_detach, nodev,			\
187*0Sstevel@tonic-gate 	pts_devinfo, PTS_CONF_FLAG, &ptsinfo);
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate /*
190*0Sstevel@tonic-gate  * Module linkage information for the kernel.
191*0Sstevel@tonic-gate  */
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate static struct modldrv modldrv = {
194*0Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
195*0Sstevel@tonic-gate 	"Slave Stream Pseudo Terminal driver 'pts'",
196*0Sstevel@tonic-gate 	&pts_ops,	/* driver ops */
197*0Sstevel@tonic-gate };
198*0Sstevel@tonic-gate 
199*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
200*0Sstevel@tonic-gate 	MODREV_1,
201*0Sstevel@tonic-gate 	&modldrv,
202*0Sstevel@tonic-gate 	NULL
203*0Sstevel@tonic-gate };
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate int
206*0Sstevel@tonic-gate _init(void)
207*0Sstevel@tonic-gate {
208*0Sstevel@tonic-gate 	int rc;
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 	if ((rc = mod_install(&modlinkage)) == 0)
211*0Sstevel@tonic-gate 		ptms_init();
212*0Sstevel@tonic-gate 	return (rc);
213*0Sstevel@tonic-gate }
214*0Sstevel@tonic-gate 
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate int
217*0Sstevel@tonic-gate _fini(void)
218*0Sstevel@tonic-gate {
219*0Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate int
223*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
224*0Sstevel@tonic-gate {
225*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
226*0Sstevel@tonic-gate }
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate static int
229*0Sstevel@tonic-gate pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
230*0Sstevel@tonic-gate {
231*0Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
232*0Sstevel@tonic-gate 		return (DDI_FAILURE);
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate 	return (ptms_create_pts_nodes(devi));
235*0Sstevel@tonic-gate }
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate static int
238*0Sstevel@tonic-gate pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
239*0Sstevel@tonic-gate {
240*0Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
241*0Sstevel@tonic-gate 		return (DDI_FAILURE);
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	return (ptms_destroy_pts_nodes(devi));
244*0Sstevel@tonic-gate }
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate /*ARGSUSED*/
247*0Sstevel@tonic-gate static int
248*0Sstevel@tonic-gate pts_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
249*0Sstevel@tonic-gate     void **result)
250*0Sstevel@tonic-gate {
251*0Sstevel@tonic-gate 	int error;
252*0Sstevel@tonic-gate 
253*0Sstevel@tonic-gate 	switch (infocmd) {
254*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
255*0Sstevel@tonic-gate 		if (pts_dip == NULL) {
256*0Sstevel@tonic-gate 			error = DDI_FAILURE;
257*0Sstevel@tonic-gate 		} else {
258*0Sstevel@tonic-gate 			*result = (void *)pts_dip;
259*0Sstevel@tonic-gate 			error = DDI_SUCCESS;
260*0Sstevel@tonic-gate 		}
261*0Sstevel@tonic-gate 		break;
262*0Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
263*0Sstevel@tonic-gate 		*result = (void *)0;
264*0Sstevel@tonic-gate 		error = DDI_SUCCESS;
265*0Sstevel@tonic-gate 		break;
266*0Sstevel@tonic-gate 	default:
267*0Sstevel@tonic-gate 		error = DDI_FAILURE;
268*0Sstevel@tonic-gate 	}
269*0Sstevel@tonic-gate 	return (error);
270*0Sstevel@tonic-gate }
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate /* ARGSUSED */
273*0Sstevel@tonic-gate /*
274*0Sstevel@tonic-gate  * Open the slave device. Reject a clone open and do not allow the
275*0Sstevel@tonic-gate  * driver to be pushed. If the slave/master pair is locked or if
276*0Sstevel@tonic-gate  * the master is not open, return EACCESS.
277*0Sstevel@tonic-gate  * Upon success, store the write queue pointer in private data and
278*0Sstevel@tonic-gate  * set the PTSOPEN bit in the pt_state field.
279*0Sstevel@tonic-gate  */
280*0Sstevel@tonic-gate static int
281*0Sstevel@tonic-gate ptsopen(
282*0Sstevel@tonic-gate 	queue_t *rqp,		/* pointer to the read side queue */
283*0Sstevel@tonic-gate 	dev_t   *devp,		/* pointer to stream tail's dev */
284*0Sstevel@tonic-gate 	int	oflag,		/* the user open(2) supplied flags */
285*0Sstevel@tonic-gate 	int	sflag,		/* open state flag */
286*0Sstevel@tonic-gate 	cred_t  *credp)		/* credentials */
287*0Sstevel@tonic-gate {
288*0Sstevel@tonic-gate 	struct pt_ttys	*ptsp;
289*0Sstevel@tonic-gate 	mblk_t		*mp;
290*0Sstevel@tonic-gate 	mblk_t		*mop;	/* ptr to a setopts message block */
291*0Sstevel@tonic-gate 	minor_t		dminor = getminor(*devp);
292*0Sstevel@tonic-gate 	struct stroptions *sop;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	DDBG("entering ptsopen(%d)", dminor);
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	if (sflag != 0) {
297*0Sstevel@tonic-gate 		return (EINVAL);
298*0Sstevel@tonic-gate 	}
299*0Sstevel@tonic-gate 
300*0Sstevel@tonic-gate 	mutex_enter(&ptms_lock);
301*0Sstevel@tonic-gate 	ptsp = ptms_minor2ptty(dminor);
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	if (ptsp == NULL) {
304*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
305*0Sstevel@tonic-gate 		return (ENXIO);
306*0Sstevel@tonic-gate 	}
307*0Sstevel@tonic-gate 	mutex_enter(&ptsp->pt_lock);
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	/*
310*0Sstevel@tonic-gate 	 * Prevent opens from zones other than the one blessed by ptm.  We
311*0Sstevel@tonic-gate 	 * can't even allow the global zone to open all pts's, as it would
312*0Sstevel@tonic-gate 	 * otherwise inproperly be able to claim pts's already opened by zones.
313*0Sstevel@tonic-gate 	 */
314*0Sstevel@tonic-gate 	if (ptsp->pt_zoneid != getzoneid()) {
315*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
316*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
317*0Sstevel@tonic-gate 		return (EPERM);
318*0Sstevel@tonic-gate 	}
319*0Sstevel@tonic-gate 
320*0Sstevel@tonic-gate 	/*
321*0Sstevel@tonic-gate 	 * Allow reopen of this device.
322*0Sstevel@tonic-gate 	 */
323*0Sstevel@tonic-gate 	if (rqp->q_ptr != NULL) {
324*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
325*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
326*0Sstevel@tonic-gate 		return (0);
327*0Sstevel@tonic-gate 	}
328*0Sstevel@tonic-gate 
329*0Sstevel@tonic-gate 	DDBGP("ptsopen: p = %p\n", (uintptr_t)ptsp);
330*0Sstevel@tonic-gate 	DDBG("ptsopen: state = %x\n", ptsp->pt_state);
331*0Sstevel@tonic-gate 
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	ASSERT(ptsp->pt_minor == dminor);
334*0Sstevel@tonic-gate 
335*0Sstevel@tonic-gate 	if ((ptsp->pt_state & PTLOCK) || !(ptsp->pt_state & PTMOPEN)) {
336*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
337*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
338*0Sstevel@tonic-gate 		return (EAGAIN);
339*0Sstevel@tonic-gate 	}
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	/*
342*0Sstevel@tonic-gate 	 * if already, open simply return...
343*0Sstevel@tonic-gate 	 */
344*0Sstevel@tonic-gate 	if (ptsp->pt_state & PTSOPEN) {
345*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
346*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
347*0Sstevel@tonic-gate 		return (0);
348*0Sstevel@tonic-gate 	}
349*0Sstevel@tonic-gate 
350*0Sstevel@tonic-gate 	/*
351*0Sstevel@tonic-gate 	 * Allocate message block for setting stream head options.
352*0Sstevel@tonic-gate 	 */
353*0Sstevel@tonic-gate 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
354*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
355*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
356*0Sstevel@tonic-gate 		return (ENOMEM);
357*0Sstevel@tonic-gate 	}
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 	/*
360*0Sstevel@tonic-gate 	 * Slave should send zero-length message to a master when it is
361*0Sstevel@tonic-gate 	 * closing. If memory is low at that time, master will not detect slave
362*0Sstevel@tonic-gate 	 * closes, this pty will not be deallocated. So, preallocate this
363*0Sstevel@tonic-gate 	 * zero-length message block early.
364*0Sstevel@tonic-gate 	 */
365*0Sstevel@tonic-gate 	if ((mp = allocb(0, BPRI_MED)) == NULL) {
366*0Sstevel@tonic-gate 		mutex_exit(&ptsp->pt_lock);
367*0Sstevel@tonic-gate 		mutex_exit(&ptms_lock);
368*0Sstevel@tonic-gate 		freemsg(mop);
369*0Sstevel@tonic-gate 		return (ENOMEM);
370*0Sstevel@tonic-gate 	}
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	ptsp->pt_state |= PTSOPEN;
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 	WR(rqp)->q_ptr = rqp->q_ptr = ptsp;
375*0Sstevel@tonic-gate 
376*0Sstevel@tonic-gate 	mutex_exit(&ptsp->pt_lock);
377*0Sstevel@tonic-gate 	mutex_exit(&ptms_lock);
378*0Sstevel@tonic-gate 
379*0Sstevel@tonic-gate 	qprocson(rqp);
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate 	/*
382*0Sstevel@tonic-gate 	 * After qprocson pts driver is fully plumbed into the stream and can
383*0Sstevel@tonic-gate 	 * send/receive messages. Setting pts_rdq will allow master side to send
384*0Sstevel@tonic-gate 	 * messages to the slave. This setting can't occur before qprocson() is
385*0Sstevel@tonic-gate 	 * finished because slave is not ready to process them.
386*0Sstevel@tonic-gate 	 */
387*0Sstevel@tonic-gate 	PT_ENTER_WRITE(ptsp);
388*0Sstevel@tonic-gate 	ptsp->pts_rdq = rqp;
389*0Sstevel@tonic-gate 	ASSERT(ptsp->pt_nullmsg == NULL);
390*0Sstevel@tonic-gate 	ptsp->pt_nullmsg = mp;
391*0Sstevel@tonic-gate 	PT_EXIT_WRITE(ptsp);
392*0Sstevel@tonic-gate 
393*0Sstevel@tonic-gate 	/*
394*0Sstevel@tonic-gate 	 * set up hi/lo water marks on stream head read queue
395*0Sstevel@tonic-gate 	 * and add controlling tty if not set
396*0Sstevel@tonic-gate 	 */
397*0Sstevel@tonic-gate 
398*0Sstevel@tonic-gate 	mop->b_datap->db_type = M_SETOPTS;
399*0Sstevel@tonic-gate 	mop->b_wptr += sizeof (struct stroptions);
400*0Sstevel@tonic-gate 	sop = (struct stroptions *)mop->b_rptr;
401*0Sstevel@tonic-gate 	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
402*0Sstevel@tonic-gate 	sop->so_hiwat = 512;
403*0Sstevel@tonic-gate 	sop->so_lowat = 256;
404*0Sstevel@tonic-gate 	putnext(rqp, mop);
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 	return (0);
407*0Sstevel@tonic-gate }
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate 
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate /*
412*0Sstevel@tonic-gate  * Find the address to private data identifying the slave's write
413*0Sstevel@tonic-gate  * queue. Send a 0-length msg up the slave's read queue to designate
414*0Sstevel@tonic-gate  * the master is closing. Uattach the master from the slave by nulling
415*0Sstevel@tonic-gate  * out master's write queue field in private data.
416*0Sstevel@tonic-gate  */
417*0Sstevel@tonic-gate /*ARGSUSED1*/
418*0Sstevel@tonic-gate static int
419*0Sstevel@tonic-gate ptsclose(queue_t *rqp, int flag, cred_t *credp)
420*0Sstevel@tonic-gate {
421*0Sstevel@tonic-gate 	struct pt_ttys	*ptsp;
422*0Sstevel@tonic-gate 	queue_t *wqp;
423*0Sstevel@tonic-gate 	mblk_t	*mp;
424*0Sstevel@tonic-gate 	mblk_t	*bp;
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/*
427*0Sstevel@tonic-gate 	 * q_ptr should never be NULL in the close routine and it is checked in
428*0Sstevel@tonic-gate 	 * DEBUG kernel by ASSERT. For non-DEBUG kernel the attempt is made to
429*0Sstevel@tonic-gate 	 * behave gracefully.
430*0Sstevel@tonic-gate 	 */
431*0Sstevel@tonic-gate 	ASSERT(rqp->q_ptr != NULL);
432*0Sstevel@tonic-gate 	if (rqp->q_ptr == NULL) {
433*0Sstevel@tonic-gate 		qprocsoff(rqp);
434*0Sstevel@tonic-gate 		return (0);
435*0Sstevel@tonic-gate 	}
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	ptsp = (struct pt_ttys *)rqp->q_ptr;
438*0Sstevel@tonic-gate 
439*0Sstevel@tonic-gate 	/*
440*0Sstevel@tonic-gate 	 * Slave is going to close and doesn't want any new  messages coming
441*0Sstevel@tonic-gate 	 * from the master side, so set pts_rdq to NULL. This should be done
442*0Sstevel@tonic-gate 	 * before call to qprocsoff() since slave can't process additional
443*0Sstevel@tonic-gate 	 * messages from the master after qprocsoff is called.
444*0Sstevel@tonic-gate 	 */
445*0Sstevel@tonic-gate 	PT_ENTER_WRITE(ptsp);
446*0Sstevel@tonic-gate 	mp = ptsp->pt_nullmsg;
447*0Sstevel@tonic-gate 	ptsp->pt_nullmsg = NULL;
448*0Sstevel@tonic-gate 	ptsp->pts_rdq = NULL;
449*0Sstevel@tonic-gate 	PT_EXIT_WRITE(ptsp);
450*0Sstevel@tonic-gate 
451*0Sstevel@tonic-gate 	/*
452*0Sstevel@tonic-gate 	 * Drain the ouput
453*0Sstevel@tonic-gate 	 */
454*0Sstevel@tonic-gate 	wqp = WR(rqp);
455*0Sstevel@tonic-gate 	PT_ENTER_READ(ptsp);
456*0Sstevel@tonic-gate 	while ((bp = getq(wqp)) != NULL) {
457*0Sstevel@tonic-gate 		if (ptsp->ptm_rdq) {
458*0Sstevel@tonic-gate 			putnext(ptsp->ptm_rdq, bp);
459*0Sstevel@tonic-gate 		} else if (bp->b_datap->db_type == M_IOCTL) {
460*0Sstevel@tonic-gate 			bp->b_datap->db_type = M_IOCNAK;
461*0Sstevel@tonic-gate 			freemsg(bp->b_cont);
462*0Sstevel@tonic-gate 			bp->b_cont = NULL;
463*0Sstevel@tonic-gate 			qreply(wqp, bp);
464*0Sstevel@tonic-gate 		} else {
465*0Sstevel@tonic-gate 			freemsg(bp);
466*0Sstevel@tonic-gate 		}
467*0Sstevel@tonic-gate 	}
468*0Sstevel@tonic-gate 	/*
469*0Sstevel@tonic-gate 	 * qenable master side write queue so that it can flush
470*0Sstevel@tonic-gate 	 * its messages as slaves's read queue is going away
471*0Sstevel@tonic-gate 	 */
472*0Sstevel@tonic-gate 	if (ptsp->ptm_rdq) {
473*0Sstevel@tonic-gate 		if (mp)
474*0Sstevel@tonic-gate 			putnext(ptsp->ptm_rdq, mp);
475*0Sstevel@tonic-gate 		else
476*0Sstevel@tonic-gate 			qenable(WR(ptsp->ptm_rdq));
477*0Sstevel@tonic-gate 	} else
478*0Sstevel@tonic-gate 		freemsg(mp);
479*0Sstevel@tonic-gate 	PT_EXIT_READ(ptsp);
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	qprocsoff(rqp);
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	rqp->q_ptr = NULL;
484*0Sstevel@tonic-gate 	WR(rqp)->q_ptr = NULL;
485*0Sstevel@tonic-gate 
486*0Sstevel@tonic-gate 	ptms_close(ptsp, PTSOPEN | PTSTTY);
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 	return (0);
489*0Sstevel@tonic-gate }
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate /*
493*0Sstevel@tonic-gate  * The wput procedure will only handle flush messages.
494*0Sstevel@tonic-gate  * All other messages are queued and the write side
495*0Sstevel@tonic-gate  * service procedure sends them off to the master side.
496*0Sstevel@tonic-gate  */
497*0Sstevel@tonic-gate static void
498*0Sstevel@tonic-gate ptswput(queue_t *qp, mblk_t *mp)
499*0Sstevel@tonic-gate {
500*0Sstevel@tonic-gate 	struct pt_ttys *ptsp;
501*0Sstevel@tonic-gate 	struct iocblk  *iocp;
502*0Sstevel@tonic-gate 	unsigned char type = mp->b_datap->db_type;
503*0Sstevel@tonic-gate 
504*0Sstevel@tonic-gate 	DBG(("entering ptswput\n"));
505*0Sstevel@tonic-gate 	ASSERT(qp->q_ptr);
506*0Sstevel@tonic-gate 
507*0Sstevel@tonic-gate 	ptsp = (struct pt_ttys *)qp->q_ptr;
508*0Sstevel@tonic-gate 	PT_ENTER_READ(ptsp);
509*0Sstevel@tonic-gate 	if (ptsp->ptm_rdq == NULL) {
510*0Sstevel@tonic-gate 		DBG(("in write put proc but no master\n"));
511*0Sstevel@tonic-gate 		/*
512*0Sstevel@tonic-gate 		 * NAK ioctl as slave side read queue is gone.
513*0Sstevel@tonic-gate 		 * Or else free the message.
514*0Sstevel@tonic-gate 		 */
515*0Sstevel@tonic-gate 		if (mp->b_datap->db_type == M_IOCTL) {
516*0Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCNAK;
517*0Sstevel@tonic-gate 			freemsg(mp->b_cont);
518*0Sstevel@tonic-gate 			mp->b_cont = NULL;
519*0Sstevel@tonic-gate 			qreply(qp, mp);
520*0Sstevel@tonic-gate 		} else
521*0Sstevel@tonic-gate 			freemsg(mp);
522*0Sstevel@tonic-gate 		PT_EXIT_READ(ptsp);
523*0Sstevel@tonic-gate 		return;
524*0Sstevel@tonic-gate 	}
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 	if (type >= QPCTL) {
527*0Sstevel@tonic-gate 	    switch (type) {
528*0Sstevel@tonic-gate 
529*0Sstevel@tonic-gate 		/*
530*0Sstevel@tonic-gate 		 * if write queue request, flush slave's write
531*0Sstevel@tonic-gate 		 * queue and send FLUSHR to ptm. If read queue
532*0Sstevel@tonic-gate 		 * request, send FLUSHR to ptm.
533*0Sstevel@tonic-gate 		 */
534*0Sstevel@tonic-gate 	    case M_FLUSH:
535*0Sstevel@tonic-gate 		DBG(("pts got flush request\n"));
536*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
537*0Sstevel@tonic-gate 
538*0Sstevel@tonic-gate 			DBG(("got FLUSHW, flush pts write Q\n"));
539*0Sstevel@tonic-gate 			if (*mp->b_rptr & FLUSHBAND)
540*0Sstevel@tonic-gate 				/*
541*0Sstevel@tonic-gate 				 * if it is a FLUSHBAND, do flushband.
542*0Sstevel@tonic-gate 				 */
543*0Sstevel@tonic-gate 				flushband(qp, *(mp->b_rptr + 1), FLUSHDATA);
544*0Sstevel@tonic-gate 			else
545*0Sstevel@tonic-gate 				flushq(qp, FLUSHDATA);
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;
548*0Sstevel@tonic-gate 			if ((*mp->b_rptr & FLUSHR) == 0) {
549*0Sstevel@tonic-gate 				/*
550*0Sstevel@tonic-gate 				 * FLUSHW only. Change to FLUSHR and putnext
551*0Sstevel@tonic-gate 				 * to ptm, then we are done.
552*0Sstevel@tonic-gate 				 */
553*0Sstevel@tonic-gate 				*mp->b_rptr |= FLUSHR;
554*0Sstevel@tonic-gate 				if (ptsp->ptm_rdq)
555*0Sstevel@tonic-gate 					putnext(ptsp->ptm_rdq, mp);
556*0Sstevel@tonic-gate 				break;
557*0Sstevel@tonic-gate 			} else {
558*0Sstevel@tonic-gate 				mblk_t *nmp;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 				/* It is a FLUSHRW. Duplicate the mblk */
561*0Sstevel@tonic-gate 				nmp = copyb(mp);
562*0Sstevel@tonic-gate 				if (nmp) {
563*0Sstevel@tonic-gate 					/*
564*0Sstevel@tonic-gate 					 * Change FLUSHW to FLUSHR before
565*0Sstevel@tonic-gate 					 * putnext to ptm.
566*0Sstevel@tonic-gate 					 */
567*0Sstevel@tonic-gate 					DBG(("putnext nmp(FLUSHR) to ptm\n"));
568*0Sstevel@tonic-gate 					*nmp->b_rptr |= FLUSHR;
569*0Sstevel@tonic-gate 					if (ptsp->ptm_rdq)
570*0Sstevel@tonic-gate 						putnext(ptsp->ptm_rdq, nmp);
571*0Sstevel@tonic-gate 				}
572*0Sstevel@tonic-gate 			}
573*0Sstevel@tonic-gate 		}
574*0Sstevel@tonic-gate 		/*
575*0Sstevel@tonic-gate 		 * Since the packet module will toss any
576*0Sstevel@tonic-gate 		 * M_FLUSHES sent to the master's stream head
577*0Sstevel@tonic-gate 		 * read queue, we simply turn it around here.
578*0Sstevel@tonic-gate 		 */
579*0Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
580*0Sstevel@tonic-gate 			ASSERT(RD(qp)->q_first == NULL);
581*0Sstevel@tonic-gate 			DBG(("qreply(qp) turning FLUSHR around\n"));
582*0Sstevel@tonic-gate 			qreply(qp, mp);
583*0Sstevel@tonic-gate 		} else {
584*0Sstevel@tonic-gate 			freemsg(mp);
585*0Sstevel@tonic-gate 		}
586*0Sstevel@tonic-gate 		break;
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 	    case M_READ:
589*0Sstevel@tonic-gate 		/* Caused by ldterm - can not pass to master */
590*0Sstevel@tonic-gate 		freemsg(mp);
591*0Sstevel@tonic-gate 		break;
592*0Sstevel@tonic-gate 
593*0Sstevel@tonic-gate 	    default:
594*0Sstevel@tonic-gate 		if (ptsp->ptm_rdq)
595*0Sstevel@tonic-gate 			putnext(ptsp->ptm_rdq, mp);
596*0Sstevel@tonic-gate 		break;
597*0Sstevel@tonic-gate 	    }
598*0Sstevel@tonic-gate 	    PT_EXIT_READ(ptsp);
599*0Sstevel@tonic-gate 	    return;
600*0Sstevel@tonic-gate 	}
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	switch (type) {
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	case M_IOCTL:
605*0Sstevel@tonic-gate 		/*
606*0Sstevel@tonic-gate 		 * For case PTSSTTY set the flag PTSTTY and ACK
607*0Sstevel@tonic-gate 		 * the ioctl so that the user program can push
608*0Sstevel@tonic-gate 		 * the associated modules to get tty semantics.
609*0Sstevel@tonic-gate 		 * See bugid 4025044
610*0Sstevel@tonic-gate 		 */
611*0Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
612*0Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
613*0Sstevel@tonic-gate 		default:
614*0Sstevel@tonic-gate 			break;
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 		case PTSSTTY:
617*0Sstevel@tonic-gate 			if (ptsp->pt_state & PTSTTY) {
618*0Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
619*0Sstevel@tonic-gate 				iocp->ioc_error = EEXIST;
620*0Sstevel@tonic-gate 			} else {
621*0Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCACK;
622*0Sstevel@tonic-gate 				mutex_enter(&ptsp->pt_lock);
623*0Sstevel@tonic-gate 				ptsp->pt_state |= PTSTTY;
624*0Sstevel@tonic-gate 				mutex_exit(&ptsp->pt_lock);
625*0Sstevel@tonic-gate 				iocp->ioc_error = 0;
626*0Sstevel@tonic-gate 			}
627*0Sstevel@tonic-gate 			iocp->ioc_count = 0;
628*0Sstevel@tonic-gate 			qreply(qp, mp);
629*0Sstevel@tonic-gate 			PT_EXIT_READ(ptsp);
630*0Sstevel@tonic-gate 			return;
631*0Sstevel@tonic-gate 		}
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate 	default:
634*0Sstevel@tonic-gate 		/*
635*0Sstevel@tonic-gate 		 * send other messages to the master
636*0Sstevel@tonic-gate 		 */
637*0Sstevel@tonic-gate 		DBG(("put msg on slave's write queue\n"));
638*0Sstevel@tonic-gate 		(void) putq(qp, mp);
639*0Sstevel@tonic-gate 		break;
640*0Sstevel@tonic-gate 	}
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 	PT_EXIT_READ(ptsp);
643*0Sstevel@tonic-gate 	DBG(("return from ptswput()\n"));
644*0Sstevel@tonic-gate }
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 
647*0Sstevel@tonic-gate /*
648*0Sstevel@tonic-gate  * enable the write side of the master. This triggers the
649*0Sstevel@tonic-gate  * master to send any messages queued on its write side to
650*0Sstevel@tonic-gate  * the read side of this slave.
651*0Sstevel@tonic-gate  */
652*0Sstevel@tonic-gate static void
653*0Sstevel@tonic-gate ptsrsrv(queue_t *qp)
654*0Sstevel@tonic-gate {
655*0Sstevel@tonic-gate 	struct pt_ttys *ptsp;
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 	DBG(("entering ptsrsrv\n"));
658*0Sstevel@tonic-gate 	ASSERT(qp->q_ptr);
659*0Sstevel@tonic-gate 
660*0Sstevel@tonic-gate 	ptsp = (struct pt_ttys *)qp->q_ptr;
661*0Sstevel@tonic-gate 	PT_ENTER_READ(ptsp);
662*0Sstevel@tonic-gate 	if (ptsp->ptm_rdq == NULL) {
663*0Sstevel@tonic-gate 		DBG(("in read srv proc but no master\n"));
664*0Sstevel@tonic-gate 		PT_EXIT_READ(ptsp);
665*0Sstevel@tonic-gate 		return;
666*0Sstevel@tonic-gate 	}
667*0Sstevel@tonic-gate 	qenable(WR(ptsp->ptm_rdq));
668*0Sstevel@tonic-gate 	PT_EXIT_READ(ptsp);
669*0Sstevel@tonic-gate 	DBG(("leaving ptsrsrv\n"));
670*0Sstevel@tonic-gate }
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate /*
673*0Sstevel@tonic-gate  * If there are messages on this queue that can be sent to
674*0Sstevel@tonic-gate  * master, send them via putnext(). Else, if queued messages
675*0Sstevel@tonic-gate  * cannot be sent, leave them on this queue. If priority
676*0Sstevel@tonic-gate  * messages on this queue, send them to master no matter what.
677*0Sstevel@tonic-gate  */
678*0Sstevel@tonic-gate static void
679*0Sstevel@tonic-gate ptswsrv(queue_t *qp)
680*0Sstevel@tonic-gate {
681*0Sstevel@tonic-gate 	struct pt_ttys *ptsp;
682*0Sstevel@tonic-gate 	queue_t *ptm_rdq;
683*0Sstevel@tonic-gate 	mblk_t *mp;
684*0Sstevel@tonic-gate 
685*0Sstevel@tonic-gate 	DBG(("entering ptswsrv\n"));
686*0Sstevel@tonic-gate 	ASSERT(qp->q_ptr);
687*0Sstevel@tonic-gate 
688*0Sstevel@tonic-gate 	ptsp = (struct pt_ttys *)qp->q_ptr;
689*0Sstevel@tonic-gate 	PT_ENTER_READ(ptsp);
690*0Sstevel@tonic-gate 	if (ptsp->ptm_rdq == NULL) {
691*0Sstevel@tonic-gate 		DBG(("in write srv proc but no master\n"));
692*0Sstevel@tonic-gate 		/*
693*0Sstevel@tonic-gate 		 * Free messages on the write queue and send
694*0Sstevel@tonic-gate 		 * NAK for any M_IOCTL type messages to wakeup
695*0Sstevel@tonic-gate 		 * the user process waiting for ACK/NAK from
696*0Sstevel@tonic-gate 		 * the ioctl invocation
697*0Sstevel@tonic-gate 		 */
698*0Sstevel@tonic-gate 		while ((mp = getq(qp)) != NULL) {
699*0Sstevel@tonic-gate 			if (mp->b_datap->db_type == M_IOCTL) {
700*0Sstevel@tonic-gate 				mp->b_datap->db_type = M_IOCNAK;
701*0Sstevel@tonic-gate 				freemsg(mp->b_cont);
702*0Sstevel@tonic-gate 				mp->b_cont = NULL;
703*0Sstevel@tonic-gate 				qreply(qp, mp);
704*0Sstevel@tonic-gate 			} else
705*0Sstevel@tonic-gate 				freemsg(mp);
706*0Sstevel@tonic-gate 		}
707*0Sstevel@tonic-gate 		PT_EXIT_READ(ptsp);
708*0Sstevel@tonic-gate 		return;
709*0Sstevel@tonic-gate 	} else {
710*0Sstevel@tonic-gate 		ptm_rdq = ptsp->ptm_rdq;
711*0Sstevel@tonic-gate 	}
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate 	/*
714*0Sstevel@tonic-gate 	 * while there are messages on this write queue...
715*0Sstevel@tonic-gate 	 */
716*0Sstevel@tonic-gate 	while ((mp = getq(qp)) != NULL) {
717*0Sstevel@tonic-gate 		/*
718*0Sstevel@tonic-gate 		 * if don't have control message and cannot put
719*0Sstevel@tonic-gate 		 * msg. on master's read queue, put it back on
720*0Sstevel@tonic-gate 		 * this queue.
721*0Sstevel@tonic-gate 		 */
722*0Sstevel@tonic-gate 		if (mp->b_datap->db_type <= QPCTL &&
723*0Sstevel@tonic-gate 		    !bcanputnext(ptm_rdq, mp->b_band)) {
724*0Sstevel@tonic-gate 			DBG(("put msg. back on Q\n"));
725*0Sstevel@tonic-gate 			(void) putbq(qp, mp);
726*0Sstevel@tonic-gate 			break;
727*0Sstevel@tonic-gate 		}
728*0Sstevel@tonic-gate 		/*
729*0Sstevel@tonic-gate 		 * else send the message up master's stream
730*0Sstevel@tonic-gate 		 */
731*0Sstevel@tonic-gate 		DBG(("send message to master\n"));
732*0Sstevel@tonic-gate 		putnext(ptm_rdq, mp);
733*0Sstevel@tonic-gate 	}
734*0Sstevel@tonic-gate 	DBG(("leaving ptswsrv\n"));
735*0Sstevel@tonic-gate 	PT_EXIT_READ(ptsp);
736*0Sstevel@tonic-gate }
737