tty_pts.c revision 193974072f41a843678abf5f61979c748687e66b
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
/*
* PTY - Stream "pseudo-tty" device.
* This is the "slave" side.
*/
#include <sys/sysmacros.h>
extern int npty; /* number of pseudo-ttys configured in */
/*
* Most of these should be "void", but the people who defined the "streams"
* data structure for S5 didn't understand data types.
*/
/*
* Slave side. This is a streams device.
*/
/*
* To save instructions, since STREAMS ignores the return value
* from this function, it is defined as void here. Kind of icky, but...
*/
static struct module_info ptslm_info = {
0,
"ptys",
0,
2048,
200
};
putq,
NULL,
};
(int (*)())ptslwput,
NULL,
NULL,
NULL,
NULL,
};
NULL,
};
static void ptslreioctl(void *);
static void pt_sendstop(struct pty *);
static void ptcpollwakeup(struct pty *, int);
char _depends_on[] = "drv/ptc";
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"tty pseudo driver slave 'ptsl'",
&ptsl_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
&modldrv,
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static char *tty_digits = PTY_DIGITS;
/* ARGSUSED */
static int
{
char name[8];
int tty_num;
char *tty_digit = tty_digits;
return (-1);
}
if (*(++tty_digit) == '\0') {
if (*(++tty_bank) == '\0')
break;
}
}
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);
}
/*
* Open the slave side of a pty.
*/
/*ARGSUSED*/
static int
{
return (ENXIO);
/*
* Block waiting for controller to open, unless this is a no-delay
* open.
*/
pty->pt_wbufcid = 0;
secpolicy_excl_open(cred) != 0) {
return (EBUSY);
}
return (EINTR);
}
goto again;
}
}
/*
* queue has already been setup with a pointer to
* the stream head that is being referenced
*/
/*
* Slave is ready to accept messages but master still can't send
* messages to the slave queue since it is not plumbed
* yet. So do qprocson() and finish slave initialization.
*/
qprocson(q);
/*
* Now it is safe to send messages to q, so wakeup master possibly
* waiting for slave queue to finish open.
*/
/* tell master device that slave is ready for writing */
return (0);
}
static int
{
bufcall_id_t pt_wbufcid = 0;
#ifdef lint
#endif
return (ENODEV); /* already been closed once */
/*
* Prevent the queues from being uses by master device.
* This should be done before qprocsoff or writer may attempt
* to use the slave queue after qprocsoff removed it from the stream and
* before entering mutex_enter().
*/
qprocsoff(q);
}
/*
* ptc_lock mutex is not dropped across
* the call to the routine ttycommon_close
*/
/*
* Cancel outstanding "bufcall" request.
*/
if (pty->pt_wbufcid) {
pty->pt_wbufcid = 0;
}
/*
* Clear out all the slave-side state.
*/
}
if (pt_wbufcid)
return (0);
}
/*
* Put procedure for write queue.
* Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
* queue up M_DATA messages for processing by the controller "read"
* routine; discard everything else.
*/
static void
{
case M_STOP:
ptcpollwakeup(pty, 0);
}
break;
case M_START:
ptcpollwakeup(pty, 0);
}
break;
case M_IOCTL:
break;
case M_FLUSH:
/*
* Set the "flush write" flag, so that we
* notify the controller if they're in packet
* or user control mode.
*/
ptcpollwakeup(pty, 0);
}
/*
* Flush our write queue.
*/
}
/*
* Set the "flush read" flag, so that we
* notify the controller if they're in packet
* mode.
*/
ptcpollwakeup(pty, 0);
}
return;
} else
break;
case M_DATA:
/*
* Throw away any leading zero-length blocks, and queue it up
* for the controller to read.
*/
return; /* damp squib of a message */
}
}
} else
break;
case M_CTL:
/*
* We're being asked whether we do canonicalization
* or not. Send a reply back up indicating whether
* we do or not.
*/
MC_NOCANON : MC_DOCANON);
}
break;
default:
/*
* "No, I don't want a subscription to Chain Store Age,
* thank you anyway."
*/
break;
}
}
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
*/
static void
ptslreioctl(void *arg)
{
queue_t *q;
/*
* The bufcall is no longer pending.
*/
if (pty->pt_wbufcid == 0) {
return;
}
pty->pt_wbufcid = 0;
return;
}
/* It's not pending any more. */
}
}
/*
* Process an "ioctl" message sent down to us.
* Drops pty's ptc_lock mutex and then reacquire
*/
static void
{
int cmd;
int error = 0;
switch (cmd) {
case TIOCSTI: {
/*
* The permission checking has already been done at the stream
* head, since it has to be done in the context of the process
* doing the call.
*/
if (error != 0)
goto out;
/*
* Simulate typing of a character at the terminal.
*/
goto out;
} else
(void) putq(
} else {
/*
* XXX - flow control; don't overflow
* this "queue".
*/
} else {
}
pty->pt_stuffqlen++;
ptcpollwakeup(pty, 0);
}
}
} else {
goto out;
}
/*
* Turn the ioctl message into an ioctl ACK message.
*/
goto out;
}
case TIOCSSIZE: {
if (error != 0)
goto out;
/*
* Set the window size, but don't send a SIGWINCH.
*/
/*
* Send an ACK back.
*/
goto out;
}
case TIOCGSIZE: {
if (pty->pt_wbufcid) {
return;
}
if (pty->pt_wbufcid == 0) {
goto out;
}
return;
}
/*
* Return the current size.
*/
goto out;
}
/*
* Imported from ttycommon_ioctl routine
*/
case TCSETSF: {
if (error != 0)
goto out;
/*
* Turn the ioctl message into an ioctl ACK message.
*/
goto ioctldone;
}
case TCSETAF: {
if (error != 0)
goto out;
/*
* Turn the ioctl message into an ioctl ACK message.
*/
goto ioctldone;
}
case TIOCSWINSZ: {
if (error != 0)
goto out;
/*
* If the window size changed, send a SIGWINCH.
*/
} else
/*
* Turn the ioctl message into an ioctl ACK message.
*/
goto ioctldone;
}
/*
* If they were just trying to drain output, that's OK.
* If they are actually trying to send a break it's an error.
*/
case TCSBRK:
if (error != 0)
goto out;
/*
* Turn the ioctl message into an ioctl ACK message.
*/
} else {
}
goto out;
}
/*
* The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
* requires a response containing data to be returned to the user,
* and no mblk could be allocated for the data.
* No such "ioctl" alters our state. Thus, we always go ahead and
* do any state-changes the "ioctl" calls for. If we couldn't allocate
* the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
* we just call "bufcall" to request that we be called back when we
* stand a better chance of allocating the data.
*/
if ((datasize =
if (pty->pt_wbufcid) {
return;
}
if (pty->pt_wbufcid == 0) {
goto out;
}
return;
}
if (error == 0) {
/*
* "ttycommon_ioctl" did most of the work; we just use the
* data it set up.
*/
switch (cmd) {
case TCSETSF:
case TCSETAF:
/*
* Set the "flush read" flag, so that we
* notify the controller if they're in packet
* mode.
*/
ptcpollwakeup(pty, 0);
}
/*FALLTHROUGH*/
case TCSETSW:
case TCSETAW:
break;
case TCSETS:
case TCSETA:
break;
}
}
if (error < 0) {
if (cmd & 0xff) {
}
error = 0; /* XXX */
goto out;
}
}
} else {
(cmd & 0xff)) {
goto out;
}
if (error < 0)
}
out:
if (error != 0) {
}
}
/*
* Service routine for read queue.
* Just wakes the controller side up so it can write some more data
* to that queue.
*/
static int
{
/*
* Build up the link list of messages, then drop
* drop the lock and do putnext()
*/
break;
}
if (!head) {
} else {
}
}
while (head) {
}
return (0);
}
static void
{
int stop;
/*
* Let the controller know, then wake up
*/
ptcpollwakeup(pty, 0);
}
}
if (stop) {
ptcpollwakeup(pty, 0);
}
} else {
if (!stop) {
ptcpollwakeup(pty, 0);
}
}
}
/*
* Wake up controller side. "flag" is 0 if a special packet or
* user control mode message has been queued up (this data is readable,
* so we also treat it as a regular data event; should we send SIGIO,
* though?), FREAD if regular data has been queued up, or FWRITE if
* the slave's read queue has drained sufficiently to allow writing.
*/
static void
{
if (flag == 0) {
/*
* "Exceptional condition" occurred. This means that
* a "read" is now possible, so do a "read" wakeup.
*/
}
/*
* Wake up the parent process as there is regular
* data to read from slave's write queue
*/
}
/*
* Wake up the parent process to write
* data into slave's read queue as the
* read queue has drained enough
*/
}
}