1116 lines
29 KiB
C
1116 lines
29 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)tty_ttcompat.c 1.1 92/07/30 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Module to intercept old V7/4BSD "ioctl" calls.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/file.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/ttold.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/stropts.h>
|
|
|
|
/*
|
|
* Old-style terminal state.
|
|
*/
|
|
typedef struct {
|
|
int t_flags; /* flags */
|
|
char t_ispeed, t_ospeed; /* speeds */
|
|
char t_erase; /* erase last character */
|
|
char t_kill; /* erase entire line */
|
|
char t_intrc; /* interrupt */
|
|
char t_quitc; /* quit */
|
|
char t_startc; /* start output */
|
|
char t_stopc; /* stop output */
|
|
char t_eofc; /* end-of-file */
|
|
char t_brkc; /* input delimiter (like nl) */
|
|
char t_suspc; /* stop process signal */
|
|
char t_dsuspc; /* delayed stop process signal */
|
|
char t_rprntc; /* reprint line */
|
|
char t_flushc; /* flush output (toggles) */
|
|
char t_werasc; /* word erase */
|
|
char t_lnextc; /* literal next character */
|
|
int t_xflags; /* XXX extended flags */
|
|
} compat_state_t;
|
|
|
|
/*
|
|
* Per-tty structure.
|
|
*/
|
|
typedef struct {
|
|
mblk_t *t_savbp;
|
|
mblk_t *t_iocpending; /* ioctl pending successful alloc */
|
|
compat_state_t t_curstate; /* current emulated state */
|
|
struct sgttyb t_new_sgttyb; /* new sgttyb from TIOCSET[PN] */
|
|
struct tchars t_new_tchars; /* new tchars from TIOCSETC */
|
|
struct ltchars t_new_ltchars; /* new ltchars from TIOCSLTC */
|
|
int t_new_lflags; /* new lflags from TIOCLSET/LBIS/LBIC */
|
|
int t_new_xflags; /* new flags from TIOCSETX */
|
|
int t_state; /* state bits */
|
|
int t_iocid; /* ID of "ioctl" we handle specially */
|
|
int t_ioccmd; /* ioctl code for that "ioctl" */
|
|
int t_wbufcid; /* ID of pending write-side bufcall */
|
|
} ttcompat_state_t;
|
|
|
|
#define TS_IOCWAIT 0x00000001 /* waiting for IOCACK to come up */
|
|
|
|
/*
|
|
* Most of these should be "void", but the people who defined the "streams"
|
|
* data structures for S5 didn't understand data types.
|
|
*/
|
|
static int ttcompatopen(/*queue_t *q, int dev, int oflag, int sflag*/);
|
|
static int ttcompatclose(/*queue_t *q*/);
|
|
static int ttcompatrput(/*queue_t *q, mblk_t *mp*/);
|
|
static int ttcompatwput(/*queue_t *q, mblk_t *mp*/);
|
|
|
|
static struct module_info ttycompatmiinfo = {
|
|
0,
|
|
"ttcompat",
|
|
0,
|
|
INFPSZ,
|
|
2048,
|
|
128
|
|
};
|
|
|
|
static struct qinit ttycompatrinit = {
|
|
ttcompatrput,
|
|
NULL,
|
|
ttcompatopen,
|
|
ttcompatclose,
|
|
NULL,
|
|
&ttycompatmiinfo
|
|
};
|
|
|
|
static struct module_info ttycompatmoinfo = {
|
|
42,
|
|
"ttcompat",
|
|
0,
|
|
INFPSZ,
|
|
300,
|
|
200
|
|
};
|
|
|
|
static struct qinit ttycompatwinit = {
|
|
ttcompatwput,
|
|
NULL,
|
|
ttcompatopen,
|
|
ttcompatclose,
|
|
NULL,
|
|
&ttycompatmoinfo
|
|
};
|
|
|
|
struct streamtab ttycompatinfo = {
|
|
&ttycompatrinit,
|
|
&ttycompatwinit,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
static void ttcompat_do_ioctl(/*queue_t *q, mblk_t *mp*/);
|
|
static void ttcompat_ioctl_ack(/*queue_t *q, mblk_t *mp*/);
|
|
static void ttcompat_ioctl_nak(/*queue_t *q, mblk_t *mp*/);
|
|
static void from_compat(/*compat_state_t *csp, struct termios *termiosp*/);
|
|
static void to_compat(/*struct termios *termiosp, compat_state_t *csp*/);
|
|
|
|
/*
|
|
* Open - get the current modes and translate them to the V7/4BSD equivalent.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static int
|
|
ttcompatopen(q, dev, oflag, sflag)
|
|
queue_t *q;
|
|
int dev, oflag, sflag;
|
|
{
|
|
register ttcompat_state_t *tp;
|
|
register mblk_t *bp;
|
|
|
|
if (q->q_ptr != NULL)
|
|
return (0); /* already attached */
|
|
|
|
if ((bp = allocb((int)sizeof (ttcompat_state_t), BPRI_MED)) == NULL) {
|
|
printf("ttcompatopen: open fails, can't alloc state struct\n");
|
|
return (OPENFAIL);
|
|
}
|
|
bp->b_wptr += sizeof (ttcompat_state_t);
|
|
tp = (ttcompat_state_t *)bp->b_rptr;
|
|
tp->t_savbp = bp;
|
|
tp->t_iocpending = NULL;
|
|
tp->t_wbufcid = 0;
|
|
tp->t_state = 0;
|
|
tp->t_iocid = 0;
|
|
tp->t_ioccmd = 0;
|
|
|
|
tp->t_curstate.t_flags = 0;
|
|
tp->t_curstate.t_ispeed = B0;
|
|
tp->t_curstate.t_ospeed = B0;
|
|
tp->t_curstate.t_erase = '\0';
|
|
tp->t_curstate.t_kill = '\0';
|
|
tp->t_curstate.t_intrc = '\0';
|
|
tp->t_curstate.t_quitc = '\0';
|
|
tp->t_curstate.t_startc = '\0';
|
|
tp->t_curstate.t_stopc = '\0';
|
|
tp->t_curstate.t_eofc = '\0';
|
|
tp->t_curstate.t_brkc = '\0';
|
|
tp->t_curstate.t_suspc = '\0';
|
|
tp->t_curstate.t_dsuspc = '\0';
|
|
tp->t_curstate.t_rprntc = '\0';
|
|
tp->t_curstate.t_flushc = '\0';
|
|
tp->t_curstate.t_werasc = '\0';
|
|
tp->t_curstate.t_lnextc = '\0';
|
|
tp->t_curstate.t_xflags = 0;
|
|
|
|
q->q_ptr = (caddr_t)tp;
|
|
WR(q)->q_ptr = (caddr_t)tp;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
ttcompatclose(q)
|
|
register queue_t *q;
|
|
{
|
|
register ttcompat_state_t *tp = (ttcompat_state_t *)q->q_ptr;
|
|
register mblk_t *mp;
|
|
|
|
/* Dump the state structure, then unlink it */
|
|
if ((mp = tp->t_iocpending) != NULL)
|
|
freemsg(mp);
|
|
/*
|
|
* Cancel outstanding "bufcall" request.
|
|
*/
|
|
if (tp->t_wbufcid)
|
|
unbufcall(tp->t_wbufcid);
|
|
freeb(tp->t_savbp);
|
|
q->q_ptr = NULL;
|
|
}
|
|
|
|
/*
|
|
* Put procedure for input from driver end of stream (read queue).
|
|
* Most messages just get passed to the next guy up; we intercept
|
|
* "ioctl" replies, and if it's an "ioctl" whose reply we plan to do
|
|
* something with, we do it.
|
|
*/
|
|
static int
|
|
ttcompatrput(q, mp)
|
|
register queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
switch (mp->b_datap->db_type) {
|
|
|
|
case M_IOCACK:
|
|
ttcompat_ioctl_ack(q, mp);
|
|
break;
|
|
|
|
case M_IOCNAK:
|
|
ttcompat_ioctl_nak(q, mp);
|
|
break;
|
|
|
|
default:
|
|
putnext(q, mp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Line discipline output queue put procedure: speeds M_IOCTL
|
|
* messages.
|
|
*/
|
|
static int
|
|
ttcompatwput(q, mp)
|
|
register queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
/*
|
|
* Process some M_IOCTL messages here; pass everything else down.
|
|
*/
|
|
if (mp->b_datap->db_type == M_IOCTL)
|
|
ttcompat_do_ioctl((ttcompat_state_t *)q->q_ptr, q, mp);
|
|
else
|
|
putnext(q, mp);
|
|
}
|
|
|
|
/*
|
|
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
|
|
* the buffer we need.
|
|
*/
|
|
static int
|
|
ttcompat_reioctl(addr)
|
|
long addr;
|
|
{
|
|
register queue_t *q = (queue_t *)addr;
|
|
register ttcompat_state_t *tp;
|
|
register mblk_t *mp;
|
|
|
|
if ((tp = (ttcompat_state_t *)q->q_ptr) == NULL)
|
|
return;
|
|
/*
|
|
* The bufcall is no longer pending.
|
|
*/
|
|
tp->t_wbufcid = 0;
|
|
if ((mp = tp->t_iocpending) != NULL) {
|
|
tp->t_iocpending = NULL; /* not pending any more */
|
|
ttcompat_do_ioctl(tp, q, mp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle old-style "ioctl" messages; pass the rest down unmolested.
|
|
*/
|
|
static void
|
|
ttcompat_do_ioctl(tp, q, mp)
|
|
register ttcompat_state_t *tp;
|
|
queue_t *q;
|
|
mblk_t *mp;
|
|
{
|
|
register struct iocblk *iocp;
|
|
int s;
|
|
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
|
|
switch (iocp->ioc_cmd) {
|
|
|
|
/*
|
|
* "get"-style calls that get translated data from the "termios"
|
|
* structure. Save the existing code and pass it down as a TCGETS.
|
|
*/
|
|
case TIOCGETP:
|
|
case TIOCGETC:
|
|
case TIOCGLTC:
|
|
case TIOCLGET:
|
|
case TIOCGETX:
|
|
tp->t_ioccmd = iocp->ioc_cmd;
|
|
tp->t_iocid = iocp->ioc_id;
|
|
tp->t_state |= TS_IOCWAIT;
|
|
iocp->ioc_cmd = TCGETS; /* turn it into a GETS */
|
|
break;
|
|
|
|
/*
|
|
* "get"-style calls that get translated data from the "winsize"
|
|
* structure. Save the existing code and pass it down as a TIOCGWINSZ.
|
|
*/
|
|
case TIOCGSIZE:
|
|
case _O_TIOCGSIZE: /* XXX */
|
|
tp->t_ioccmd = TIOCGSIZE; /* map old GSIZE into new */
|
|
tp->t_iocid = iocp->ioc_id;
|
|
tp->t_state |= TS_IOCWAIT;
|
|
iocp->ioc_cmd = TIOCGWINSZ; /* turn it into a TIOCGWINSZ */
|
|
break;
|
|
|
|
/*
|
|
* "set"-style calls that set translated data into a "termios"
|
|
* structure. Set our idea of the new state from the value
|
|
* given to us. We then have to get the current state, so we
|
|
* turn this guy into a TCGETS and pass it down. When the
|
|
* ACK comes back, we modify the state we got back and shove it
|
|
* back down as the appropriate type of TCSETS.
|
|
*/
|
|
case TIOCSETP:
|
|
case TIOCSETN:
|
|
tp->t_new_sgttyb = *((struct sgttyb *)mp->b_cont->b_rptr);
|
|
goto dogets;
|
|
|
|
case TIOCSETC:
|
|
tp->t_new_tchars = *((struct tchars *)mp->b_cont->b_rptr);
|
|
goto dogets;
|
|
|
|
case TIOCSLTC:
|
|
tp->t_new_ltchars = *((struct ltchars *)mp->b_cont->b_rptr);
|
|
goto dogets;
|
|
|
|
case TIOCLBIS:
|
|
case TIOCLBIC:
|
|
case TIOCLSET:
|
|
tp->t_new_lflags = *(int *)mp->b_cont->b_rptr;
|
|
goto dogets;
|
|
|
|
case TIOCSETX:
|
|
tp->t_new_xflags = *(int *)mp->b_cont->b_rptr;
|
|
goto dogets;
|
|
|
|
/*
|
|
* "set"-style call that sets a particular bit in a "termios"
|
|
* structure. We then have to get the current state, so we
|
|
* turn this guy into a TCGETS and pass it down. When the
|
|
* ACK comes back, we modify the state we got back and shove it
|
|
* back down as the appropriate type of TCSETS.
|
|
*/
|
|
case TIOCHPCL:
|
|
dogets:
|
|
tp->t_ioccmd = iocp->ioc_cmd;
|
|
tp->t_iocid = iocp->ioc_id;
|
|
tp->t_state |= TS_IOCWAIT;
|
|
iocp->ioc_cmd = TCGETS;
|
|
iocp->ioc_count = 0; /* no data returned unless we say so */
|
|
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. Otherwise, it was
|
|
* probably an old TIOCSSIZE "ioctl"; we turn it into a
|
|
* new-style TIOCSSIZE "ioctl" so that nobody below us has to
|
|
* know about this stuff.
|
|
*/
|
|
if ((((struct ttysize *)mp->b_cont->b_rptr)->ts_lines &
|
|
0xffff0000) == 0) {
|
|
/*
|
|
* It's an old-style TIOCSSIZE; map it into a new-style
|
|
* one.
|
|
*/
|
|
iocp->ioc_cmd = TIOCSSIZE;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* "set"-style call that sets DTR. Pretend that it was a TIOCMBIS
|
|
* with TIOCM_DTR set.
|
|
*/
|
|
case TIOCSDTR: {
|
|
register mblk_t *datap;
|
|
|
|
if ((datap = allocb((int)sizeof (int), BPRI_HI)) == NULL)
|
|
goto allocfailure;
|
|
*(int *)datap->b_wptr = TIOCM_DTR;
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_cmd = TIOCMBIS; /* turn it into a TIOCMBIS */
|
|
if (mp->b_cont != NULL)
|
|
freemsg(mp->b_cont);
|
|
mp->b_cont = datap; /* attach the data */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* "set"-style call that clears DTR. Pretend that it was a TIOCMBIC
|
|
* with TIOCM_DTR set.
|
|
*/
|
|
case TIOCCDTR: {
|
|
register mblk_t *datap;
|
|
|
|
if ((datap = allocb((int)sizeof (int), BPRI_HI)) == NULL)
|
|
goto allocfailure;
|
|
*(int *)datap->b_wptr = TIOCM_DTR;
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_cmd = TIOCMBIC; /* turn it into a TIOCMBIC */
|
|
if (mp->b_cont != NULL)
|
|
freemsg(mp->b_cont);
|
|
mp->b_cont = datap; /* attach the data */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Translate into the S5 form of TCFLSH.
|
|
*/
|
|
case TIOCFLUSH: {
|
|
register int flags = *(int *)mp->b_cont->b_rptr;
|
|
|
|
switch (flags&(FREAD|FWRITE)) {
|
|
|
|
case 0:
|
|
case FREAD|FWRITE:
|
|
flags = 2; /* flush 'em both */
|
|
break;
|
|
|
|
case FREAD:
|
|
flags = 0; /* flush read */
|
|
break;
|
|
|
|
case FWRITE:
|
|
flags = 1; /* flush write */
|
|
break;
|
|
}
|
|
iocp->ioc_cmd = TCFLSH; /* turn it into a TCFLSH */
|
|
*(int *)mp->b_cont->b_rptr = flags; /* fiddle the arg */
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Turn into a TCXONC.
|
|
*/
|
|
case TIOCSTOP: {
|
|
register mblk_t *datap;
|
|
|
|
if ((datap = allocb((int)sizeof (int), BPRI_HI)) == NULL)
|
|
goto allocfailure;
|
|
*(int *)datap->b_wptr = 0; /* stop */
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_cmd = TCXONC; /* turn it into a XONC */
|
|
if (mp->b_cont != NULL)
|
|
freemsg(mp->b_cont);
|
|
mp->b_cont = datap; /* attach the data */
|
|
break;
|
|
}
|
|
|
|
case TIOCSTART: {
|
|
register mblk_t *datap;
|
|
|
|
if ((datap = allocb((int)sizeof (int), BPRI_HI)) == NULL)
|
|
goto allocfailure;
|
|
*(int *)datap->b_wptr = 1; /* start */
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_cmd = TCXONC; /* turn it into a XONC */
|
|
if (mp->b_cont != NULL)
|
|
freemsg(mp->b_cont);
|
|
mp->b_cont = datap; /* attach the data */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We don't reply to most calls, we just pass them down,
|
|
* possibly after modifying the arguments.
|
|
*/
|
|
putnext(q, mp);
|
|
return;
|
|
|
|
allocfailure:
|
|
/*
|
|
* We needed to allocate something to handle this "ioctl", but
|
|
* couldn't; save this "ioctl" and arrange to get called back when
|
|
* it's more likely that we can get what we need.
|
|
* If there's already one being saved, throw it out, since it
|
|
* must have timed out.
|
|
*/
|
|
s = splstr();
|
|
if (tp->t_iocpending != NULL)
|
|
freemsg(tp->t_iocpending);
|
|
tp->t_iocpending = mp; /* hold this ioctl */
|
|
/*
|
|
* Arrange to get called back only if there isn't an outstanding
|
|
* "bufcall" request since the allocation size is constant.
|
|
*/
|
|
if (tp->t_wbufcid == 0)
|
|
tp->t_wbufcid = bufcall((u_int)sizeof (struct iocblk),
|
|
BPRI_HI, ttcompat_reioctl, (long)q);
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* Called when an M_IOCACK message is seen on the read queue; if this
|
|
* is the response we were waiting for, we either:
|
|
* modify the data going up (if the "ioctl" read data); since in all
|
|
* cases, the old-style returned information is smaller than or the same
|
|
* size as the new-style returned information, we just overwrite the old
|
|
* stuff with the new stuff (beware of changing structure sizes, in case
|
|
* you invalidate this)
|
|
* or
|
|
* take this data, modify it appropriately, and send it back down (if
|
|
* the "ioctl" wrote data).
|
|
* In either case, we cancel the "wait"; the final response to a "write"
|
|
* ioctl goes back up to the user.
|
|
* If this wasn't the response we were waiting for, just pass it up.
|
|
*/
|
|
static void
|
|
ttcompat_ioctl_ack(q, mp)
|
|
queue_t *q;
|
|
mblk_t *mp;
|
|
{
|
|
register ttcompat_state_t *tp;
|
|
register struct iocblk *iocp;
|
|
register mblk_t *datap;
|
|
|
|
tp = (ttcompat_state_t *)q->q_ptr;
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
|
|
if (!(tp->t_state&TS_IOCWAIT) || iocp->ioc_id != tp->t_iocid) {
|
|
/*
|
|
* This isn't the reply we're looking for. Move along.
|
|
*/
|
|
putnext(q, mp);
|
|
return;
|
|
}
|
|
|
|
datap = mp->b_cont; /* mblk containing data going up */
|
|
|
|
switch (tp->t_ioccmd) {
|
|
|
|
case TIOCGETP: {
|
|
register struct sgttyb *cb;
|
|
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
cb = (struct sgttyb *)datap->b_wptr;
|
|
cb->sg_ispeed = tp->t_curstate.t_ispeed;
|
|
cb->sg_ospeed = tp->t_curstate.t_ospeed;
|
|
cb->sg_erase = tp->t_curstate.t_erase;
|
|
cb->sg_kill = tp->t_curstate.t_kill;
|
|
cb->sg_flags = tp->t_curstate.t_flags;
|
|
datap->b_wptr +=
|
|
(sizeof (struct sgttyb)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (struct sgttyb);
|
|
break;
|
|
}
|
|
|
|
case TIOCGETC:
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
bcopy((caddr_t)&tp->t_curstate.t_intrc, (caddr_t)datap->b_wptr,
|
|
sizeof (struct tchars));
|
|
datap->b_wptr +=
|
|
(sizeof (struct tchars)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (struct tchars);
|
|
break;
|
|
|
|
case TIOCGLTC:
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
bcopy((caddr_t)&tp->t_curstate.t_suspc, (caddr_t)datap->b_wptr,
|
|
sizeof (struct ltchars));
|
|
datap->b_wptr +=
|
|
(sizeof (struct ltchars)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (struct ltchars);
|
|
break;
|
|
|
|
case TIOCLGET:
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
*(int *)datap->b_wptr =
|
|
((unsigned)tp->t_curstate.t_flags) >> 16;
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (int);
|
|
break;
|
|
|
|
case TIOCGETX:
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
*(int *)datap->b_wptr = tp->t_curstate.t_xflags;
|
|
datap->b_wptr += (sizeof (int)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (int);
|
|
break;
|
|
|
|
case TIOCGSIZE: {
|
|
struct winsize winsize;
|
|
register struct ttysize *cb;
|
|
|
|
winsize = *(struct winsize *)datap->b_rptr;
|
|
datap->b_wptr = datap->b_datap->db_base;
|
|
/* recycle the reply's buffer */
|
|
cb = (struct ttysize *)datap->b_wptr;
|
|
cb->ts_lines = winsize.ws_row;
|
|
cb->ts_cols = winsize.ws_col;
|
|
datap->b_wptr +=
|
|
(sizeof (struct ttysize)) / (sizeof *datap->b_wptr);
|
|
iocp->ioc_count = sizeof (struct ttysize);
|
|
break;
|
|
}
|
|
|
|
case TIOCSETP:
|
|
case TIOCSETN:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
tp->t_curstate.t_erase = tp->t_new_sgttyb.sg_erase;
|
|
tp->t_curstate.t_kill = tp->t_new_sgttyb.sg_kill;
|
|
tp->t_curstate.t_ispeed = tp->t_new_sgttyb.sg_ispeed;
|
|
tp->t_curstate.t_ospeed = tp->t_new_sgttyb.sg_ospeed;
|
|
tp->t_curstate.t_flags =
|
|
(tp->t_curstate.t_flags & 0xffff0000) |
|
|
(tp->t_new_sgttyb.sg_flags & 0xffff);
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS or TCSETSF.
|
|
*/
|
|
iocp->ioc_cmd = (tp->t_ioccmd == TIOCSETP) ? TCSETSF : TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCSETC:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
bcopy((caddr_t)&tp->t_new_tchars,
|
|
(caddr_t)&tp->t_curstate.t_intrc, sizeof (struct tchars));
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCSLTC:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
bcopy((caddr_t)&tp->t_new_ltchars,
|
|
(caddr_t)&tp->t_curstate.t_suspc, sizeof (struct ltchars));
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCLBIS:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCLBIC:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
tp->t_curstate.t_flags &= ~(tp->t_new_lflags << 16);
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCLSET:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
tp->t_curstate.t_flags &= 0xffff;
|
|
tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCSETX:
|
|
/*
|
|
* Get the current state from the GETS data, and
|
|
* update it.
|
|
*/
|
|
to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
|
|
tp->t_curstate.t_xflags = tp->t_new_xflags;
|
|
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
case TIOCHPCL:
|
|
/*
|
|
* Replace the data that came up with the updated data.
|
|
*/
|
|
((struct termios *)datap->b_rptr)->c_cflag |= HUPCL;
|
|
|
|
/*
|
|
* Send it back down as a TCSETS.
|
|
*/
|
|
iocp->ioc_cmd = TCSETS;
|
|
goto senddown;
|
|
|
|
default:
|
|
panic("ttcompat: unexpected ioctl acknowledgment");
|
|
}
|
|
|
|
/*
|
|
* All the calls that return something return 0.
|
|
*/
|
|
tp->t_state &= ~TS_IOCWAIT; /* we got what we wanted */
|
|
iocp->ioc_rval = 0;
|
|
putnext(q, mp);
|
|
return;
|
|
|
|
senddown:
|
|
/*
|
|
* Send a "get state" reply back down, with suitably-modified
|
|
* state, as a "set state" "ioctl".
|
|
*/
|
|
tp->t_state &= ~TS_IOCWAIT;
|
|
mp->b_datap->db_type = M_IOCTL;
|
|
putnext(WR(q), mp);
|
|
}
|
|
|
|
/*
|
|
* Called when an M_IOCNAK message is seen on the read queue; if this is
|
|
* the response we were waiting for, cancel the wait. Pass the reply up;
|
|
* if we were waiting for this response, we can't complete the "ioctl" and
|
|
* the NAK will tell that to the guy above us.
|
|
* If this wasn't the response we were waiting for, just pass it up.
|
|
*/
|
|
static void
|
|
ttcompat_ioctl_nak(q, mp)
|
|
queue_t *q;
|
|
register mblk_t *mp;
|
|
{
|
|
register ttcompat_state_t *tp;
|
|
register struct iocblk *iocp;
|
|
|
|
iocp = (struct iocblk *)mp->b_rptr;
|
|
tp = (ttcompat_state_t *)q->q_ptr;
|
|
|
|
if (tp->t_state&TS_IOCWAIT && iocp->ioc_id == tp->t_iocid) {
|
|
/* This call isn't going through. */
|
|
tp->t_state &= ~TS_IOCWAIT;
|
|
}
|
|
putnext(q, mp);
|
|
}
|
|
|
|
#define FROM_COMPAT_CHAR(to, from) if ((to = from) == 0377) to = 0
|
|
|
|
static void
|
|
from_compat(csp, termiosp)
|
|
register compat_state_t *csp;
|
|
register struct termios *termiosp;
|
|
{
|
|
termiosp->c_iflag = 0;
|
|
termiosp->c_oflag &= (ONLRET|ONOCR);
|
|
termiosp->c_cflag = (termiosp->c_cflag & (CRTSCTS|LOBLK|HUPCL)) |
|
|
(csp->t_ospeed & CBAUD) | CREAD;
|
|
if (csp->t_ospeed != csp->t_ispeed) {
|
|
termiosp->c_cflag |= (csp->t_ispeed&CBAUD)<<IBSHIFT;
|
|
if (csp->t_ispeed == 0)
|
|
termiosp->c_cflag &= ~CBAUD; /* hang up */
|
|
}
|
|
if ((csp->t_ispeed & CBAUD) == B110 || csp->t_xflags & STOPB)
|
|
termiosp->c_cflag |= CSTOPB;
|
|
termiosp->c_lflag = ECHOK;
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VERASE], csp->t_erase);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VKILL], csp->t_kill);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VINTR], csp->t_intrc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VQUIT], csp->t_quitc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VSTART], csp->t_startc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VSTOP], csp->t_stopc);
|
|
termiosp->c_cc[VEOL2] = 0;
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VSUSP], csp->t_suspc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VDSUSP], csp->t_dsuspc); /* is this useful? */
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VREPRINT], csp->t_rprntc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VDISCARD], csp->t_flushc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VWERASE], csp->t_werasc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VLNEXT], csp->t_lnextc);
|
|
if (csp->t_flags & O_TANDEM)
|
|
termiosp->c_iflag |= IXOFF;
|
|
if (csp->t_flags & O_LCASE) {
|
|
termiosp->c_iflag |= IUCLC;
|
|
termiosp->c_oflag |= OLCUC;
|
|
termiosp->c_lflag |= XCASE;
|
|
}
|
|
if (csp->t_flags & O_ECHO)
|
|
termiosp->c_lflag |= ECHO;
|
|
if (csp->t_flags & O_CRMOD) {
|
|
termiosp->c_iflag |= ICRNL;
|
|
termiosp->c_oflag |= ONLCR;
|
|
switch (csp->t_flags & O_CRDELAY) {
|
|
|
|
case O_CR1:
|
|
termiosp->c_oflag |= CR2;
|
|
break;
|
|
|
|
case O_CR2:
|
|
termiosp->c_oflag |= CR3;
|
|
break;
|
|
}
|
|
} else {
|
|
if ((csp->t_flags & O_NLDELAY) == O_NL1)
|
|
termiosp->c_oflag |= ONLRET|CR1; /* tty37 */
|
|
}
|
|
if ((csp->t_flags & O_NLDELAY) == O_NL2)
|
|
termiosp->c_oflag |= NL1;
|
|
/*
|
|
* When going into RAW mode, the special characters controlled by the
|
|
* POSIX IEXTEN bit no longer apply; when leaving, they do.
|
|
*/
|
|
if (csp->t_flags & O_RAW) {
|
|
termiosp->c_cflag |= CS8;
|
|
termiosp->c_iflag &= ~(ICRNL|IUCLC);
|
|
termiosp->c_lflag &= ~(XCASE|IEXTEN);
|
|
} else {
|
|
termiosp->c_iflag |= IMAXBEL|BRKINT|IGNPAR;
|
|
if (termiosp->c_cc[VSTOP] != 0 && termiosp->c_cc[VSTART] != 0)
|
|
termiosp->c_iflag |= IXON;
|
|
if (csp->t_flags & O_LITOUT)
|
|
termiosp->c_cflag |= CS8;
|
|
else {
|
|
if (csp->t_flags & O_PASS8)
|
|
termiosp->c_cflag |= CS8;
|
|
/* XXX - what about 8 bits plus parity? */
|
|
else {
|
|
switch (csp->t_flags & (O_EVENP|O_ODDP)) {
|
|
|
|
case 0:
|
|
termiosp->c_iflag |= ISTRIP;
|
|
termiosp->c_cflag |= CS8;
|
|
break;
|
|
|
|
case O_EVENP:
|
|
termiosp->c_iflag |= INPCK|ISTRIP;
|
|
termiosp->c_cflag |= CS7|PARENB;
|
|
break;
|
|
|
|
case O_ODDP:
|
|
termiosp->c_iflag |= INPCK|ISTRIP;
|
|
termiosp->c_cflag |= CS7|PARENB|PARODD;
|
|
break;
|
|
|
|
case O_EVENP|O_ODDP:
|
|
termiosp->c_iflag |= ISTRIP;
|
|
termiosp->c_cflag |= CS7|PARENB;
|
|
break;
|
|
}
|
|
}
|
|
if (!(csp->t_xflags & NOPOST))
|
|
termiosp->c_oflag |= OPOST;
|
|
}
|
|
termiosp->c_lflag |= IEXTEN;
|
|
if (!(csp->t_xflags & NOISIG))
|
|
termiosp->c_lflag |= ISIG;
|
|
if (!(csp->t_flags & O_CBREAK))
|
|
termiosp->c_lflag |= ICANON;
|
|
if (csp->t_flags & O_CTLECH)
|
|
termiosp->c_lflag |= ECHOCTL;
|
|
}
|
|
switch (csp->t_flags & O_TBDELAY) {
|
|
|
|
case O_TAB1:
|
|
termiosp->c_oflag |= TAB1;
|
|
break;
|
|
|
|
case O_TAB2:
|
|
termiosp->c_oflag |= TAB2;
|
|
break;
|
|
|
|
case O_XTABS:
|
|
termiosp->c_oflag |= TAB3;
|
|
break;
|
|
}
|
|
if (csp->t_flags & O_VTDELAY)
|
|
termiosp->c_oflag |= FFDLY;
|
|
if (csp->t_flags & O_BSDELAY)
|
|
termiosp->c_oflag |= BSDLY;
|
|
if (csp->t_flags & O_PRTERA)
|
|
termiosp->c_lflag |= ECHOPRT;
|
|
if (csp->t_flags & O_CRTERA)
|
|
termiosp->c_lflag |= ECHOE;
|
|
if (csp->t_flags & O_TOSTOP)
|
|
termiosp->c_lflag |= TOSTOP;
|
|
if (csp->t_flags & O_FLUSHO)
|
|
termiosp->c_lflag |= FLUSHO;
|
|
if (csp->t_flags & O_NOHANG)
|
|
termiosp->c_cflag |= CLOCAL;
|
|
#if 0
|
|
if (csp->t_flags & O_MDMBUF)
|
|
???;
|
|
#endif
|
|
if (csp->t_flags & O_CRTKIL)
|
|
termiosp->c_lflag |= ECHOKE;
|
|
if (csp->t_flags & O_PENDIN)
|
|
termiosp->c_lflag |= PENDIN;
|
|
if (!(csp->t_flags & O_DECCTQ))
|
|
termiosp->c_iflag |= IXANY;
|
|
if (csp->t_flags & O_NOFLSH)
|
|
termiosp->c_lflag |= NOFLSH;
|
|
if (termiosp->c_lflag & ICANON) {
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VEOF], csp->t_eofc);
|
|
FROM_COMPAT_CHAR(termiosp->c_cc[VEOL], csp->t_brkc);
|
|
} else {
|
|
termiosp->c_cc[VMIN] = 1;
|
|
termiosp->c_cc[VTIME] = 0;
|
|
}
|
|
}
|
|
|
|
#define TO_COMPAT_CHAR(to, from) if ((to = from) == 0) to = 0377
|
|
|
|
static void
|
|
to_compat(termiosp, csp)
|
|
register struct termios *termiosp;
|
|
register compat_state_t *csp;
|
|
{
|
|
csp->t_xflags &= (NOISIG|NOPOST);
|
|
csp->t_ospeed = termiosp->c_cflag & CBAUD;
|
|
if ((csp->t_ispeed = ((termiosp->c_cflag & CIBAUD)>>IBSHIFT)) == 0)
|
|
csp->t_ispeed = csp->t_ospeed;
|
|
if (termiosp->c_cflag & CSTOPB && (csp->t_ispeed & CBAUD) != B110)
|
|
csp->t_xflags |= STOPB;
|
|
TO_COMPAT_CHAR(csp->t_erase, termiosp->c_cc[VERASE]);
|
|
TO_COMPAT_CHAR(csp->t_kill, termiosp->c_cc[VKILL]);
|
|
TO_COMPAT_CHAR(csp->t_intrc, termiosp->c_cc[VINTR]);
|
|
TO_COMPAT_CHAR(csp->t_quitc, termiosp->c_cc[VQUIT]);
|
|
TO_COMPAT_CHAR(csp->t_startc, termiosp->c_cc[VSTART]);
|
|
TO_COMPAT_CHAR(csp->t_stopc, termiosp->c_cc[VSTOP]);
|
|
TO_COMPAT_CHAR(csp->t_suspc, termiosp->c_cc[VSUSP]);
|
|
TO_COMPAT_CHAR(csp->t_dsuspc, termiosp->c_cc[VDSUSP]);
|
|
TO_COMPAT_CHAR(csp->t_rprntc, termiosp->c_cc[VREPRINT]);
|
|
TO_COMPAT_CHAR(csp->t_flushc, termiosp->c_cc[VDISCARD]);
|
|
TO_COMPAT_CHAR(csp->t_werasc, termiosp->c_cc[VWERASE]);
|
|
TO_COMPAT_CHAR(csp->t_lnextc, termiosp->c_cc[VLNEXT]);
|
|
csp->t_flags &= (O_CTLECH|O_LITOUT|O_PASS8|O_ODDP|O_EVENP);
|
|
if (termiosp->c_iflag & IXOFF)
|
|
csp->t_flags |= O_TANDEM;
|
|
if (!(termiosp->c_iflag &
|
|
(IMAXBEL|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|INLCR|
|
|
IGNCR|ICRNL|IUCLC|IXON)) &&
|
|
!(termiosp->c_oflag & OPOST) &&
|
|
(termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
|
|
!(termiosp->c_lflag & (ISIG|ICANON|XCASE|IEXTEN)))
|
|
csp->t_flags |= O_RAW;
|
|
else {
|
|
if (!(termiosp->c_iflag & IXON)) {
|
|
csp->t_startc = 0377;
|
|
csp->t_stopc = 0377;
|
|
}
|
|
if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
|
|
!(termiosp->c_oflag & OPOST))
|
|
csp->t_flags |= O_LITOUT;
|
|
else {
|
|
csp->t_flags &= ~O_LITOUT;
|
|
if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8) {
|
|
if (!(termiosp->c_iflag & ISTRIP))
|
|
csp->t_flags |= O_PASS8;
|
|
} else {
|
|
csp->t_flags &= ~(O_ODDP|O_EVENP|O_PASS8);
|
|
if (termiosp->c_cflag & PARODD)
|
|
csp->t_flags |= O_ODDP;
|
|
else if (termiosp->c_iflag & INPCK)
|
|
csp->t_flags |= O_EVENP;
|
|
else
|
|
csp->t_flags |= O_ODDP|O_EVENP;
|
|
}
|
|
if (!(termiosp->c_oflag & OPOST))
|
|
csp->t_xflags |= NOPOST;
|
|
else
|
|
csp->t_xflags &= ~NOPOST;
|
|
}
|
|
if (!(termiosp->c_lflag & ISIG))
|
|
csp->t_xflags |= NOISIG;
|
|
else
|
|
csp->t_xflags &= ~NOISIG;
|
|
if (!(termiosp->c_lflag & ICANON))
|
|
csp->t_flags |= O_CBREAK;
|
|
if (termiosp->c_lflag & ECHOCTL)
|
|
csp->t_flags |= O_CTLECH;
|
|
else
|
|
csp->t_flags &= ~O_CTLECH;
|
|
}
|
|
if (termiosp->c_oflag & OLCUC)
|
|
csp->t_flags |= O_LCASE;
|
|
if (termiosp->c_lflag&ECHO)
|
|
csp->t_flags |= O_ECHO;
|
|
if (termiosp->c_oflag & ONLCR) {
|
|
csp->t_flags |= O_CRMOD;
|
|
switch (termiosp->c_oflag & CRDLY) {
|
|
|
|
case CR2:
|
|
csp->t_flags |= O_CR1;
|
|
break;
|
|
|
|
case CR3:
|
|
csp->t_flags |= O_CR2;
|
|
break;
|
|
}
|
|
} else {
|
|
if ((termiosp->c_oflag & CR1) &&
|
|
(termiosp->c_oflag & ONLRET))
|
|
csp->t_flags |= O_NL1; /* tty37 */
|
|
}
|
|
if (termiosp->c_oflag & ONLRET)
|
|
if (termiosp->c_oflag & NL1)
|
|
csp->t_flags |= O_NL2;
|
|
switch (termiosp->c_oflag & TABDLY) {
|
|
|
|
case TAB1:
|
|
csp->t_flags |= O_TAB1;
|
|
break;
|
|
|
|
case TAB2:
|
|
csp->t_flags |= O_TAB2;
|
|
break;
|
|
|
|
case XTABS:
|
|
csp->t_flags |= O_XTABS;
|
|
break;
|
|
}
|
|
if (termiosp->c_oflag & FFDLY)
|
|
csp->t_flags |= O_VTDELAY;
|
|
if (termiosp->c_oflag & BSDLY)
|
|
csp->t_flags |= O_BSDELAY;
|
|
if (termiosp->c_lflag & ECHOPRT)
|
|
csp->t_flags |= O_PRTERA;
|
|
if (termiosp->c_lflag & ECHOE)
|
|
csp->t_flags |= (O_CRTERA|O_CRTBS);
|
|
if (termiosp->c_lflag & TOSTOP)
|
|
csp->t_flags |= O_TOSTOP;
|
|
if (termiosp->c_lflag & FLUSHO)
|
|
csp->t_flags |= O_FLUSHO;
|
|
if (termiosp->c_cflag & CLOCAL)
|
|
csp->t_flags |= O_NOHANG;
|
|
#if 0
|
|
if (???)
|
|
csp->t_flags |= O_MDMBUF;
|
|
#endif
|
|
if (termiosp->c_lflag & ECHOKE)
|
|
csp->t_flags |= O_CRTKIL;
|
|
if (termiosp->c_lflag & PENDIN)
|
|
csp->t_flags |= O_PENDIN;
|
|
if (!(termiosp->c_iflag & IXANY))
|
|
csp->t_flags |= O_DECCTQ;
|
|
if (termiosp->c_lflag & NOFLSH)
|
|
csp->t_flags |= O_NOFLSH;
|
|
if (termiosp->c_lflag & ICANON) {
|
|
TO_COMPAT_CHAR(csp->t_eofc, termiosp->c_cc[VEOF]);
|
|
TO_COMPAT_CHAR(csp->t_brkc, termiosp->c_cc[VEOL]);
|
|
} else {
|
|
termiosp->c_cc[VMIN] = 1;
|
|
termiosp->c_cc[VTIME] = 0;
|
|
}
|
|
}
|