tty_pty.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2005 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. For each "controller" side
* it connects to a "slave" side.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
extern int npty; /* number of pseudo-ttys configured in */
static void ptc_init(void), ptc_uninit(void);
struct cb_ops ptc_cb_ops = {
ptcopen, /* open */
ptcclose, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
ptcread, /* read */
ptcwrite, /* write */
ptcioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
ptcpoll, /* poll */
ddi_prop_op, /* prop_op */
0, /* streamtab */
};
DEVO_REV, /* devo_rev */
0, /* refcnt */
ptc_info, /* info */
nulldev, /* identify */
nulldev, /* probe */
ptc_attach, /* attach */
nodev, /* detach */
nodev, /* reset */
&ptc_cb_ops, /* driver operations */
(struct bus_ops *)0 /* bus operations */
};
extern int dseekneg_flag;
extern struct mod_ops mod_driverops;
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"tty pseudo driver control 'ptc' %I%",
&ptc_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
&modldrv,
};
int
_init()
{
int rc;
ptc_init();
return (rc);
}
int
_fini()
{
int rc;
ptc_uninit();
return (rc);
}
int
{
}
static char *pty_digits = PTY_DIGITS;
/* ARGSUSED */
static int
{
char name[8];
int pty_num;
char *pty_digit = pty_digits;
return (-1);
}
if (*(++pty_digit) == '\0') {
if (*(++pty_bank) == '\0')
break;
}
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
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);
}
static void
ptc_init(void)
{
}
}
static void
ptc_uninit(void)
{
}
}
/*
* Controller side. This is not, alas, a streams device; there are too
* many old features that we must support and that don't work well
* with streams.
*/
/*ARGSUSED*/
int
{
queue_t *q;
return (ENXIO);
}
return (EIO); /* controller is exclusive use */
/* XXX - should be EBUSY! */
}
}
/*
* Send an un-hangup to the slave, since "carrier" is
* coming back up. Make sure we're doing canonicalization.
*/
(void) putctl(q, M_UNHANGUP);
}
return (0);
}
/*ARGSUSED1*/
int
{
queue_t *q;
/*
* Send a hangup to the slave, since "carrier" is dropping.
*/
}
/*
* Clear out all the controller-side state. This also
* clears PF_CARR_ON, which is correct because the
* "carrier" is dropping since the controller process
* is going away.
*/
else
pty->pt_stuffqlen--;
}
return (0);
}
int
{
queue_t *q;
unsigned char tmp;
int error;
#ifdef lint
#endif
for (;;) {
}
/*
* If there's a TIOCPKT packet waiting, pass it back.
*/
if (error) {
goto out;
}
goto out;
}
/*
* If there's a user-control packet waiting, pass the
* "ioctl" code back.
*/
if (error) {
goto out;
}
goto out;
}
/*
* If there's any data waiting, pass it back.
*/
/*
* We're about to begin a move in packet or
* user-control mode; precede the data with a
* data header.
*/
if (error != 0)
goto out;
goto out;
}
goto out;
if ((q == NULL) ||
goto out;
}
}
if (error != 0) {
goto out;
}
}
/*
* Strip off zero-length blocks from the front of
* what we're putting back on the queue.
*/
goto out; /* nothing left */
}
if (q != NULL)
else
goto out;
}
/*
* If there's any TIOCSTI-stuffed characters, pass
* them back. (They currently arrive after all output;
* is this correct?)
*/
while (error == 0 &&
pty->pt_stuffqlen--;
else
}
goto out;
}
/*
* There's no data available.
* We want to block until the slave is open, and there's
* something to read; but if we lost the slave or we're NBIO,
* then return the appropriate error instead. POSIX-style
* non-block has top billing and gives -1 with errno = EAGAIN,
* BSD-style comes next and gives -1 with errno = EWOULDBLOCK,
* SVID-style comes last and gives 0.
*/
goto out;
}
goto out;
}
error = EWOULDBLOCK;
goto out;
}
goto out;
return (EINTR);
}
}
out:
return (error);
}
int
{
queue_t *q;
int written;
int fmode = 0;
int error = 0;
#ifdef lint
#endif
}
/*
* Wait for slave to open.
*/
goto out;
}
goto out;
}
error = EWOULDBLOCK;
goto out;
}
goto out;
return (EINTR);
}
goto again;
}
/*
* If in remote mode, even zero-length writes generate messages.
*/
written = 0;
do {
while (!canput(q)) {
/*
* Wait for slave's read queue to unclog.
*/
goto out;
}
if (!written)
goto out;
}
if (!written)
error = EWOULDBLOCK;
goto out;
}
goto out;
return (EINTR);
}
}
}
}
if (fmode)
if (error != 0) {
goto out;
if (!written)
goto out;
}
if (!written)
error = EWOULDBLOCK;
goto out;
}
goto out;
"ptcwrite: non null return from"
" makemsg");
}
/*
* Check again for safety; since "uiomove" can take a
* page fault, there's no guarantee that "pt_flags"
* didn't change while it was happening.
*/
if (mp)
goto out;
}
if (mp)
written = 1;
}
out:
return (error);
}
return (EFAULT)
return (EFAULT)
int
int *rvalp)
{
queue_t *q;
int d_arg;
int err;
switch (cmd) {
case TIOCPKT:
if (d_arg) {
return (EINVAL);
}
} else
break;
case TIOCUCNTL:
if (d_arg) {
return (EINVAL);
}
} else
break;
case TIOCTCNTL:
if (d_arg) {
return (EINVAL);
}
} else
break;
case TIOCREMOTE:
if (d_arg) {
} else {
}
break;
case TIOCSIGNAL:
/*
* Blast a M_PCSIG message up the slave stream; the
* signal number is the argument to the "ioctl".
*/
break;
case FIONBIO:
if (d_arg)
else
break;
case FIOASYNC:
if (d_arg)
else
break;
/*
* These, at least, can work on the controller-side process
* group.
*/
case FIOGETOWN:
break;
case FIOSETOWN:
break;
case FIONREAD: {
/*
* Return the total number of bytes of data in all messages
* in slave write queue, which is master read queue, unless a
* special message would be read.
*/
/*
* Will return whatever data is queued up.
*/
/*
* Will return STI'ed data.
*/
}
/*
* Under LP64 we could have more than INT_MAX bytes to report,
* but the interface is defined in terms of int, so we cap it.
*/
break;
}
case TIOCSWINSZ:
/*
* Unfortunately, TIOCSWINSZ and the old TIOCSSIZE "ioctl"s
* share the same code. If the upper 16 bits of the number
* of lines is non-zero, it was probably a TIOCSWINSZ,
* with both "ws_row" and "ws_col" non-zero.
*/
return (EFAULT);
/*
* It's a TIOCSWINSZ.
*/
/*
* If the window size changed, send a SIGWINCH.
*/
}
break;
}
/* FALLTHROUGH */
case TIOCSSIZE:
return (EFAULT);
break;
case TIOCGWINSZ:
sizeof (struct winsize)) != 0)
return (EFAULT);
break;
case TIOCGSIZE:
sizeof (struct ttysize)) != 0)
return (EFAULT);
break;
/*
* XXX These should not be here. The only reason why an
* "ioctl" on the controller side should get the
* slave side's process group is so that the process on
* the controller side can send a signal to the slave
* side's process group; however, this is better done
* with TIOCSIGNAL, both because it doesn't require us
* to know about the slave side's process group and because
* the controller side process may not have permission to
* send that signal to the entire process group.
*
* However, since vanilla 4BSD doesn't provide TIOCSIGNAL,
* we can't just get rid of them.
*/
case TIOCGPGRP:
case TIOCSPGRP:
/*
* This is amazingly disgusting, but the stupid semantics of
* 4BSD pseudo-ttys makes us do it. If we do one of these guys
* on the controller side, it really applies to the slave-side
* stream. It should NEVER have been possible to do ANY sort
* of tty operations on the controller side, but it's too late
* to fix that now. However, we won't waste our time implementing
* anything that the original pseudo-tty driver didn't handle.
*/
case TIOCGETP:
case TIOCSETP:
case TIOCSETN:
case TIOCGETC:
case TIOCSETC:
case TIOCGLTC:
case TIOCSLTC:
case TIOCLGET:
case TIOCLSET:
case TIOCLBIS:
case TIOCLBIC:
return (EIO);
}
return (err);
default:
return (ENOTTY);
}
return (0);
}
int
short events,
int anyyet,
short *reventsp,
{
queue_t *q;
int pos = 0;
#ifdef lint
#endif
*reventsp = 0;
/*
* A non NULL pollhead pointer should be returned in case
* user polls for 0 events.
*/
return (0);
}
/*
* Regular data is available.
*/
pos++;
}
/*
* A control packet is available.
*/
pos++;
}
/*
* "ioctl" or TIOCSTI data is available.
*/
pos++;
}
pos++;
}
}
canput(q)) {
pos++;
}
}
pos++;
}
if (events == 0) { /* "exceptional conditions" */
pos++;
}
pos++;
}
}
/*
* Arrange to have poll waken up when event occurs.
* if (!anyyet)
*/
if (!pos) {
*reventsp = 0;
}
return (0);
}
void
{
sigsend_t v;
bzero(&v, sizeof (v));
v.perm = 0;
v.checkperm = 1;
(void) sigsendset(&set, &v);
}
static int
{
int error;
/*
* Create data part of message, if any.
*/
if (count >= 0) {
return (ENOSR);
if (error) {
return (error);
}
}
return (0);
}