/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 */
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI" /* from S5R4 1.13 */
/*
* Description:
*
* The PTEM streams module is used as a pseudo driver emulator. Its purpose
* is to emulate the ioctl() functions of a terminal device driver.
*/
"ptem",
&pteminfo,
};
};
};
int
_init()
{
return (mod_install(&modlinkage));
}
int
_fini()
{
return (mod_remove(&modlinkage));
}
int
{
}
/*
* stream data structure definitions
*/
0xabcd,
"ptem",
0,
512,
512,
128
};
(int (*)()) ptemrput,
NULL,
NULL,
};
(int (*)()) ptemwput,
(int (*)()) ptemwsrv,
};
NULL,
};
/*
* ptemopen - open routine gets called when the module gets pushed onto the
* stream.
*/
/* ARGSUSED */
static int
queue_t *q, /* pointer to the read side queue */
int oflag, /* the user open(2) supplied flags */
int sflag, /* open state flag */
{
int len;
return (EINVAL);
/* It's already attached. */
return (0);
}
/*
* Allocate state structure.
*/
/*
* Allocate a message block, used to pass the zero length message for
* "stty 0".
*
* NOTE: it's better to find out if such a message block can be
* allocated before it's needed than to not be able to
* deliver (for possible lack of buffers) when a hang-up
* occurs.
*/
return (EAGAIN);
}
/*
* stream head read queue and add controlling tty if not set.
*/
return (EAGAIN);
}
/*
* Cross-link.
*/
/*
* Get termios defaults. These are stored as
* a property in the "options" node.
*/
} else {
/*
* Gack! Whine about it.
*/
}
/*
* Commit to the open and send the M_SETOPTS off to the stream head.
*/
qprocson(q);
return (0);
}
/*
* ptemclose - This routine gets called when the module gets popped off of the
* stream.
*/
/* ARGSUSED */
static int
{
qprocsoff(q);
return (0);
}
/*
* ptemrput - Module read queue put procedure.
*
* This is called from the module or driver downstream.
*/
static void
{
int error;
case M_DELAY:
case M_READ:
break;
case M_IOCTL:
case TCSBRK:
/*
* Send a break message upstream.
*
* XXX: Shouldn't the argument come into play in
* determining whether or not so send an M_BREAK?
* It certainly does in the write-side direction.
*/
if (error != 0) {
break;
}
if (!putnextctl(q, M_BREAK)) {
/*
* Send an NAK reply back
*/
break;
}
}
/*
* ACK it.
*/
break;
case JWINSIZE:
case TIOCGWINSZ:
case TIOCSWINSZ:
break;
case TIOCSIGNAL:
/*
* The following subtle logic is due to the fact that
* `mp' may be in any one of three distinct formats:
*
* 1. A transparent M_IOCTL with an intptr_t-sized
* payload containing the signal number.
*
* 2. An I_STR M_IOCTL with an int-sized payload
* containing the signal number.
*
* 3. An M_IOCDATA with an int-sized payload
* containing the signal number.
*/
/*
* it's transparent with pointer
* to the arg
*/
break;
}
}
break;
case TIOCREMOTE:
else {
}
break;
default:
break;
}
break;
case M_IOCDATA:
/*
* Just free message on failure.
*/
break;
}
/*
* Only need to copy data for the SET case.
*/
case TIOCSWINSZ:
case TIOCSIGNAL:
case TIOCREMOTE:
break;
case JWINSIZE:
case TIOCGWINSZ:
break;
default:
break;
}
break;
case M_IOCACK:
case M_IOCNAK:
/*
* We only pass write-side ioctls through to the master that
* we've already ACKed or NAKed to the stream head. Thus, we
* discard ones arriving from below, since they're redundant
* from the point of view of modules above us.
*/
break;
case M_HANGUP:
/*
* clear blocked state.
*/
{
}
}
default:
break;
}
}
/*
* ptemwput - Module write queue put procedure.
*
* This is called from the module or stream head upstream.
*
* XXX: This routine is quite lazy about handling allocation failures,
* basically just giving up and reporting failure. It really ought to
* set up bufcalls and only fail when it's absolutely necessary.
*/
static void
{
switch (type) {
case M_IOCDATA:
/*
* Just free message on failure.
*/
break;
}
/*
* Only need to copy data for the SET case.
*/
case TIOCSWINSZ:
break;
case JWINSIZE:
case TIOCGWINSZ:
break;
default:
}
break;
case M_FLUSH:
else
}
break;
case M_READ:
break;
case M_STOP:
/*
* Set the output flow control state.
*/
break;
case M_START:
/*
* Relieve the output flow control state.
*/
qenable(q);
break;
default:
break;
}
return;
}
/*
* If our queue is nonempty or flow control persists
* downstream or module in stopped state, queue this message.
*/
/*
* Exception: ioctls, except for those defined to
* take effect after output has drained, should be
* processed immediately.
*/
switch (type) {
case M_IOCTL:
/*
* Queue these.
*/
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
break;
/*
* Handle all others immediately.
*/
default:
return;
}
break;
case M_DELAY: /* tty delays not supported */
return;
case M_DATA:
/*
* Free all bad length messages.
*/
return;
return;
}
}
}
return;
}
/*
* fast path into ptemwmsg to dispose of mp.
*/
}
/*
* ptem write queue service procedure.
*/
static void
{
break;
}
}
}
/*
* This routine is called from both ptemwput and ptemwsrv to do the
* actual work of dealing with mp. ptmewput will have already
* dealt with high priority messages.
*
* Return 1 if the message was processed completely and 0 if not.
*/
static int
{
int error;
case M_IOCTL:
/*
* Note: for each "set" type operation a copy
* of the M_IOCTL message is made and passed
* downstream. Eventually the PCKT module, if
* it has been pushed, should pick up this message.
* If the PCKT module has not been pushed the master
* side stream head will free it.
*/
case TCSETAF:
case TCSETSF:
/*
* Flush the read queue.
*/
break;
}
/* FALLTHROUGH */
case TCSETA:
case TCSETAW:
case TCSETS:
case TCSETSW:
case TCSETAF:
case TCSETA:
case TCSETAW:
if (error != 0) {
goto out;
}
break;
case TCSETSF:
case TCSETS:
case TCSETSW:
if (error != 0) {
goto out;
}
break;
}
/*
* Hang-up: Send a zero length message.
*/
if (dack_ptr) {
/*
* Send a zero length message
* downstream.
*/
}
} else {
/*
* Make a copy of this message and pass it on
* to the PCKT module.
*/
break;
}
}
/*
* Send ACK upstream.
*/
out:
break;
case TCGETA:
break;
}
break;
case TCGETS:
break;
}
break;
case TCSBRK:
if (error != 0) {
break;
}
/*
* Need a copy of this message to pass it on to
* the PCKT module.
*/
break;
}
/*
* Send a copy of the M_IOCTL to the PCKT module.
*/
/*
* TCSBRK meaningful if data part of message is 0
* cf. termio(7).
*/
(void) putnextctl(q, M_BREAK);
/*
* ACK the ioctl.
*/
break;
case JWINSIZE:
case TIOCGWINSZ:
case TIOCSWINSZ:
break;
case TIOCSTI:
/*
* Simulate typing of a character at the terminal. In
* all cases, we acknowledge the ioctl and pass a copy
* of it along for the PCKT module to encapsulate. If
* not in remote mode, we also process the ioctl
* itself, looping the character given as its argument
* back around to the read side.
*/
/*
* Need a copy of this message to pass on to the PCKT
* module.
*/
break;
}
if (error != 0) {
break;
}
/*
* 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.
*/
break;
}
/*
* XXX: Is EAGAIN really the right response to
* flow control blockage?
*/
break;
}
}
break;
case PTSSTTY:
} else {
}
break;
default:
/*
* End of the line. The slave driver doesn't see any
* ioctls that we don't explicitly pass along to it.
*/
break;
}
break;
case M_DELAY: /* tty delays not supported */
break;
case M_DATA:
/*
* Free all bad length messages.
*/
break;
break;
}
}
return (0);
default:
break;
}
return (1);
}
/*
* Message must be of type M_IOCTL or M_IOCDATA for this routine to be called.
*/
static void
{
int error;
case JWINSIZE:
/*
* For compatibility: If all zeros, NAK the message for dumb
* terminals.
*/
return;
}
return;
}
else
return;
case TIOCGWINSZ:
/*
* If all zeros NAK the message for dumb terminals.
*/
return;
}
return;
}
return;
case TIOCSWINSZ:
if (error != 0) {
return;
}
/*
* changed.
*/
/*
* SIGWINCH is always sent upstream.
*/
/*
* Message may have come in as an M_IOCDATA; pass it
* to the master side as an M_IOCTL.
*/
/*
* Need a copy of this message to pass on to
* the PCKT module, only if the M_IOCTL
* orginated from the slave side.
*/
return;
}
}
}
return;
case TIOCSIGNAL: {
/*
* This ioctl can emanate from the master side in remote
* mode only.
*/
int sig;
if (error != 0) {
return;
}
}
else
return;
}
/*
* Send an M_PCSIG message up the slave's read side and
* respond back to the master with an ACK or NAK as
* appropriate.
*/
return;
}
return;
}
case TIOCREMOTE: {
int onoff;
if (error != 0) {
return;
}
}
/*
* Send M_CTL up using the iocblk format.
*/
return;
}
/*
* ACK the ioctl.
*/
/*
* Record state change.
*/
if (onoff)
else
return;
}
default:
return;
}
}