/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Pseudo Terminal Slave Driver.
*
* The pseudo-tty subsystem simulates a terminal connection, where the master
* side represents the terminal and the slave represents the user process's
* special device end point. The master device is set up as a cloned device
* where its major device number is the major for the clone device and its minor
* device number is the major for the ptm driver. There are no nodes in the file
* system for master devices. The master pseudo driver is opened using the
* finds the next available minor device for the ptm major device.
*
* A master device is available only if it and its corresponding slave device
* are not already open. When the master device is opened, the corresponding
* slave device is automatically locked out. Only one open is allowed on a
* master device. Multiple opens are allowed on the slave device. After both
* the master and slave have been opened, the user has two file descriptors
* which are the end points of a full duplex connection composed of two streams
* which are automatically connected at the master and slave drivers. The user
* may then push modules onto either side of the stream pair.
*
* The master and slave drivers pass all messages to their adjacent queues.
* Only the M_FLUSH needs some processing. Because the read queue of one side
* is connected to the write queue of the other, the FLUSHR flag is changed to
* the FLUSHW flag and vice versa. When the master device is closed an M_HANGUP
* message is sent to the slave device which will render the device
* unusable. The process on the slave side gets the EIO when attempting to write
* on that stream but it will be able to read any data remaining on the stream
* head read queue. When all the data has been read, read() returns 0
* indicating that the stream can no longer be used. On the last close of the
* slave device, a 0-length message is sent to the master device. When the
* application on the master side issues a read() or getmsg() and 0 is returned,
* the user of the master device decides whether to issue a close() that
* dismantles the pseudo-terminal subsystem. If the master device is not closed,
* the pseudo-tty subsystem will be available to another user to open the slave
* device.
*
* Synchronization:
*
* ptms_lock mutex which is initialized at system boot time from
* ptms_initspace (called from space.c).
*
* Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
* pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
*
* PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
* which allow reader locks to be reacquired by the same thread (usual
* a thread to acquire a lock it already holds, even as a reader). The sole
* purpose of these macros is to guarantee that the peer queue will not
* disappear (due to closing peer) while it is used. It is safe to use
* PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
* they are not real locks but reference counts).
*
* PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave
* be set to appropriate queues *after* qprocson() is called during open (to
* prevent peer from accessing the queue with incomplete plumbing) and set to
* NULL before qprocsoff() is called during close.
*
* protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
* holds.
*
* Lock Ordering:
*
* If both ptms_lock and per-pty lock should be held, ptms_lock should always
* be entered first, followed by per-pty lock.
*
* See ptms.h, ptm.c and ptms_conf.c fore more information.
*
*/
#include <sys/sysmacros.h>
#ifdef DEBUG
int pts_debug = 0;
#else
#define DBG(a)
#endif
/*
* Slave Stream Pseudo Terminal Module: stream data structure definitions
*/
0xface,
"pts",
0,
512,
512,
128
};
NULL,
(int (*)()) ptsrsrv,
NULL,
&pts_info,
};
(int (*)()) ptswput,
(int (*)()) ptswsrv,
NULL,
NULL,
NULL,
&pts_info,
};
&ptsrint,
&ptswint,
NULL,
};
/*
* this will define (struct cb_ops cb_pts_ops) and (struct dev_ops pts_ops)
*/
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"Slave Stream Pseudo Terminal driver 'pts'",
&pts_ops, /* driver ops */
};
&modldrv,
};
int
_init(void)
{
int rc;
ptms_init();
return (rc);
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static int
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
/*
* For now, pts cannot be detached.
*/
return (DDI_FAILURE);
}
/*ARGSUSED*/
static int
void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_FAILURE;
} else {
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/* ARGSUSED */
/*
* Open the slave device. Reject a clone open and do not allow the
* the master is not open, return EACCESS.
* Upon success, store the write queue pointer in private data and
* set the PTSOPEN bit in the pt_state field.
*/
static int
int oflag, /* the user open(2) supplied flags */
int sflag, /* open state flag */
{
if (sflag != 0) {
return (EINVAL);
}
return (ENXIO);
}
/*
* Prevent opens from zones other than the one blessed by ptm. We
* can't even allow the global zone to open all pts's, as it would
* otherwise inproperly be able to claim pts's already opened by zones.
*/
return (EPERM);
}
/*
* Allow reopen of this device.
*/
return (0);
}
return (EAGAIN);
}
/*
* if already, open simply return...
*/
return (0);
}
/*
* Allocate message block for setting stream head options.
*/
return (ENOMEM);
}
/*
* Slave should send zero-length message to a master when it is
* closing. If memory is low at that time, master will not detect slave
* closes, this pty will not be deallocated. So, preallocate this
* zero-length message block early.
*/
return (ENOMEM);
}
/*
* After qprocson pts driver is fully plumbed into the stream and can
* messages to the slave. This setting can't occur before qprocson() is
* finished because slave is not ready to process them.
*/
/*
* and add controlling tty if not set
*/
return (0);
}
/*
* Find the address to private data identifying the slave's write
* queue. Send a 0-length msg up the slave's read queue to designate
* the master is closing. Uattach the master from the slave by nulling
* out master's write queue field in private data.
*/
/*ARGSUSED1*/
static int
{
/*
* q_ptr should never be NULL in the close routine and it is checked in
* DEBUG kernel by ASSERT. For non-DEBUG kernel the attempt is made to
* behave gracefully.
*/
return (0);
}
/*
* Slave is going to close and doesn't want any new messages coming
* from the master side, so set pts_rdq to NULL. This should be done
* before call to qprocsoff() since slave can't process additional
* messages from the master after qprocsoff is called.
*/
/*
* Drain the ouput
*/
} else {
}
}
/*
* qenable master side write queue so that it can flush
* its messages as slaves's read queue is going away
*/
if (mp)
else
} else
return (0);
}
/*
* The wput procedure will only handle flush messages.
* All other messages are queued and the write side
* service procedure sends them off to the master side.
*/
static void
{
DBG(("entering ptswput\n"));
DBG(("in write put proc but no master\n"));
/*
* NAK ioctl as slave side read queue is gone.
* Or else free the message.
*/
} else
return;
}
switch (type) {
/*
* if write queue request, flush slave's write
* queue and send FLUSHR to ptm. If read queue
* request, send FLUSHR to ptm.
*/
case M_FLUSH:
DBG(("pts got flush request\n"));
DBG(("got FLUSHW, flush pts write Q\n"));
/*
* if it is a FLUSHBAND, do flushband.
*/
else
/*
* FLUSHW only. Change to FLUSHR and putnext
* to ptm, then we are done.
*/
break;
} else {
/* It is a FLUSHRW. Duplicate the mblk */
if (nmp) {
/*
* Change FLUSHW to FLUSHR before
* putnext to ptm.
*/
DBG(("putnext nmp(FLUSHR) to ptm\n"));
}
}
}
/*
* Since the packet module will toss any
* M_FLUSHES sent to the master's stream head
* read queue, we simply turn it around here.
*/
DBG(("qreply(qp) turning FLUSHR around\n"));
} else {
}
break;
case M_READ:
/* Caused by ldterm - can not pass to master */
break;
default:
break;
}
return;
}
switch (type) {
case M_IOCTL:
/*
* For case PTSSTTY set the flag PTSTTY and ACK
* the ioctl so that the user program can push
* the associated modules to get tty semantics.
* See bugid 4025044
*/
default:
break;
case PTSSTTY:
} else {
}
return;
}
default:
/*
* send other messages to the master
*/
DBG(("put msg on slave's write queue\n"));
break;
}
DBG(("return from ptswput()\n"));
}
/*
* enable the write side of the master. This triggers the
* master to send any messages queued on its write side to
* the read side of this slave.
*/
static void
{
DBG(("entering ptsrsrv\n"));
DBG(("in read srv proc but no master\n"));
return;
}
DBG(("leaving ptsrsrv\n"));
}
/*
* If there are messages on this queue that can be sent to
* master, send them via putnext(). Else, if queued messages
* cannot be sent, leave them on this queue. If priority
* messages on this queue, send them to master no matter what.
*/
static void
{
DBG(("entering ptswsrv\n"));
DBG(("in write srv proc but no master\n"));
/*
* Free messages on the write queue and send
* NAK for any M_IOCTL type messages to wakeup
* the ioctl invocation
*/
} else
}
return;
} else {
}
/*
* while there are messages on this write queue...
*/
/*
* if don't have control message and cannot put
* msg. on master's read queue, put it back on
* this queue.
*/
DBG(("put msg. back on Q\n"));
break;
}
/*
* else send the message up master's stream
*/
DBG(("send message to master\n"));
}
DBG(("leaving ptswsrv\n"));
}