/*
* 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 (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Pseudo Terminal Master 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.
*
* If O_NONBLOCK or O_NDELAY is set, read on the master side returns -1 with
* errno set to EAGAIN if no data is available, and write returns -1 with errno
* set to EAGAIN if there is internal flow control.
*
* IOCTLS:
*
* ISPTM: determines whether the file descriptor is that of an open master
* device. Return code of zero indicates that the file descriptor
* represents master device.
*
* UNLKPT: unlocks the master and slave devices. It returns 0 on success. On
* failure, the errno is set to EINVAL indicating that the master
* device is not open.
*
* ZONEPT: sets the zone membership of the associated pts device.
*
* GRPPT: sets the group owner of the associated pts 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, pts.c and ptms_conf.c for more information.
*/
#include <sys/sysmacros.h>
#ifdef DEBUG
int ptm_debug = 0;
#else
#define DBG(a)
#endif
/*
* Master Stream Pseudo Terminal Module: stream data structure definitions
*/
0xdead,
"ptm",
0,
512,
512,
128
};
NULL,
(int (*)()) ptmrsrv,
NULL,
&ptm_info,
};
(int (*)()) ptmwput,
(int (*)()) ptmwsrv,
NULL,
NULL,
NULL,
&ptm_info,
};
&ptmrint,
&ptmwint,
NULL,
};
/*
* this will define (struct cb_ops cb_ptm_ops) and (struct dev_ops ptm_ops)
*/
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"Master streams driver 'ptm'",
&ptm_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_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*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 a minor of the master device. Store the write queue pointer and set the
* pt_state field to (PTMOPEN | PTLOCK).
* This code will work properly with both clone opens and direct opens of the
* master device.
*/
static int
int oflag, /* the user open(2) supplied flags */
int sflag, /* open state flag */
{
/* Allow reopen */
return (0);
return (ENXIO);
/*
* This is a direct open to specific master device through an
* artificially created entry with specific minor in
*/
return (ENXIO);
}
/*
* The master open requires that the slave be attached
* before it returns so that attempts to open the slave will
* succeeed
*/
if (ptms_attach_slave() != 0) {
return (ENXIO);
}
DDBG("ptmopen(): mop allocation failed\n", 0);
return (ENOMEM);
}
DDBG("ptmopen(): pty allocation failed\n", 0);
return (ENOMEM);
}
/* Allow slave to send messages to master */
/*
* and add controlling tty if not set
*/
else
/*
* The input, devp, is a major device number, the output is put
* into the same parm as a major,minor pair.
*/
return (0);
}
/*
* Find the address to private data identifying the slave's write queue.
* Send a hang-up message up the slave's read queue to designate the
* nulling out the write queue fields in the private data structure.
*/
/*ARGSUSED1*/
static int
{
DBG(("send hangup message to slave\n"));
}
}
/*
* ptm_rdq should be cleared before call to qprocsoff() to prevent pts
* write procedure to attempt using ptm_rdq after qprocsoff.
*/
/*
* qenable slave side write queue so that it can flush
* its messages as master's read queue is going away
*/
/* Finish the close */
return (0);
}
/*
* The wput procedure will only handle ioctl and flush messages.
*/
static void
{
DBG(("entering ptmwput\n"));
/*
* if write queue request, flush master's write
* queue and send FLUSHR up slave side. If read
* queue request, convert to FLUSHW and putnext().
*/
case M_FLUSH:
{
unsigned char flush_flg = 0;
DBG(("ptm got flush request\n"));
DBG(("got FLUSHW, flush ptm write Q\n"));
/*
* if it is a FLUSHBAND, do flushband.
*/
else
}
DBG(("got FLUSHR, set FLUSHW\n"));
}
DBG(("putnext to pts\n"));
} else
break;
}
case M_IOCTL:
default:
DBG(("got M_IOCTL but no slave\n"));
return;
}
break;
case UNLKPT:
/*FALLTHROUGH*/
case ISPTM:
break;
case ZONEPT:
{
zoneid_t z;
int error;
break;
}
break;
}
if (z < MIN_ZONEID || z > MAX_ZONEID) {
break;
}
break;
}
case OWNERPT:
{
int error;
break;
}
break;
}
break;
}
}
break;
case M_READ:
/* Caused by ldterm - can not pass to slave */
break;
/*
* send other messages to slave
*/
default:
DBG(("got msg. but no slave\n"));
}
return;
}
DBG(("put msg on master's write queue\n"));
break;
}
DBG(("return from ptmwput()\n"));
}
/*
* enable the write side of the slave. This triggers the
* slave to send any messages queued on its write side to
* the read side of this master.
*/
static void
{
DBG(("entering ptmrsrv\n"));
}
DBG(("leaving ptmrsrv\n"));
}
/*
* If there are messages on this queue that can be sent to
* slave, 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 slave no matter what.
*/
static void
{
DBG(("entering ptmwsrv\n"));
/* If there are no messages there's nothing to do. */
DBG(("leaving ptmwsrv (no messages)\n"));
return;
}
DBG(("in master write srv proc but no slave\n"));
/*
* Free messages on the write queue and send
* NAK for any M_IOCTL type messages to wakeup
* the ioctl invocation
*/
do {
else
}
return;
}
/*
* while there are messages on this write queue...
*/
do {
/*
* if don't have control message and cannot put
* msg. on slave's read queue, put it back on
* this queue.
*/
DBG(("put msg. back on queue\n"));
break;
}
/*
* else send the message up slave's stream
*/
DBG(("send message to slave\n"));
DBG(("leaving ptmwsrv\n"));
}