| /* |
| * 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 |
| * or http://www.opensolaris.org/os/licensing. |
| * 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 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. |
| */ |
| |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| /* |
| * University Copyright- Copyright (c) 1982, 1986, 1988 |
| * The Regents of the University of California |
| * All Rights Reserved |
| * |
| * University Acknowledgment- Portions of this document are derived from |
| * software developed by the University of California, Berkeley, and its |
| * contributors. |
| */ |
| |
| /* |
| * Module to intercept old V7 and 4BSD "ioctl" calls. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/signal.h> |
| #include <sys/file.h> |
| #include <sys/termios.h> |
| #include <sys/ttold.h> |
| #include <sys/cmn_err.h> |
| #include <sys/stream.h> |
| #include <sys/stropts.h> |
| #include <sys/strsubr.h> |
| #include <sys/strsun.h> |
| #include <sys/errno.h> |
| #include <sys/debug.h> |
| #include <sys/ttcompat.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/kmem.h> |
| #include <sys/policy.h> |
| |
| /* |
| * This is the loadable module wrapper. |
| */ |
| #include <sys/conf.h> |
| #include <sys/modctl.h> |
| |
| /* See os/streamio.c */ |
| extern int sgttyb_handling; |
| |
| static struct streamtab ttcoinfo; |
| |
| static struct fmodsw fsw = { |
| "ttcompat", |
| &ttcoinfo, |
| D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE |
| }; |
| |
| /* |
| * Module linkage information for the kernel. |
| */ |
| |
| static struct modlstrmod modlstrmod = { |
| &mod_strmodops, |
| "alt ioctl calls", |
| &fsw |
| }; |
| |
| static struct modlinkage modlinkage = { |
| MODREV_1, &modlstrmod, NULL |
| }; |
| |
| int |
| _init(void) |
| { |
| return (mod_install(&modlinkage)); |
| } |
| |
| int |
| _fini(void) |
| { |
| return (mod_remove(&modlinkage)); |
| } |
| |
| int |
| _info(struct modinfo *modinfop) |
| { |
| return (mod_info(&modlinkage, modinfop)); |
| } |
| |
| static int ttcompatopen(queue_t *, dev_t *, int, int, cred_t *); |
| static int ttcompatclose(queue_t *, int, cred_t *); |
| static void ttcompatrput(queue_t *, mblk_t *); |
| static void ttcompatwput(queue_t *, mblk_t *); |
| |
| static struct module_info ttycompatmiinfo = { |
| 0, |
| "ttcompat", |
| 0, |
| INFPSZ, |
| 2048, |
| 128 |
| }; |
| |
| static struct qinit ttycompatrinit = { |
| (int (*)())ttcompatrput, |
| NULL, |
| ttcompatopen, |
| ttcompatclose, |
| NULL, |
| &ttycompatmiinfo |
| }; |
| |
| static struct module_info ttycompatmoinfo = { |
| 42, |
| "ttcompat", |
| 0, |
| INFPSZ, |
| 300, |
| 200 |
| }; |
| |
| static struct qinit ttycompatwinit = { |
| (int (*)())ttcompatwput, |
| NULL, |
| ttcompatopen, |
| ttcompatclose, |
| NULL, |
| &ttycompatmoinfo |
| }; |
| |
| static struct streamtab ttcoinfo = { |
| &ttycompatrinit, |
| &ttycompatwinit, |
| NULL, |
| NULL |
| }; |
| |
| static void ttcompat_do_ioctl(ttcompat_state_t *, queue_t *, mblk_t *); |
| static void ttcompat_ioctl_ack(queue_t *, mblk_t *); |
| static void ttcopyout(queue_t *, mblk_t *); |
| static void ttcompat_ioctl_nak(queue_t *, mblk_t *); |
| static void from_compat(compat_state_t *, struct termios *); |
| static void to_compat(struct termios *, compat_state_t *); |
| |
| /* |
| * Open - get the current modes and translate them to the V7/4BSD equivalent. |
| */ |
| /*ARGSUSED*/ |
| static int |
| ttcompatopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp) |
| { |
| ttcompat_state_t *tp; |
| |
| if (q->q_ptr != NULL) { |
| tp = (ttcompat_state_t *)q->q_ptr; |
| /* fail open if TIOCEXCL was done and its not privileged */ |
| if ((tp->t_new_lflags & XCLUDE) && |
| secpolicy_excl_open(crp) != 0) { |
| return (EBUSY); |
| } |
| return (0); /* already attached */ |
| } |
| tp = kmem_zalloc(sizeof (ttcompat_state_t), KM_SLEEP); |
| q->q_ptr = tp; |
| WR(q)->q_ptr = tp; |
| qprocson(q); |
| |
| return (0); |
| } |
| |
| /* ARGSUSED1 */ |
| static int |
| ttcompatclose(queue_t *q, int flag, cred_t *crp) |
| { |
| ttcompat_state_t *tp = (ttcompat_state_t *)q->q_ptr; |
| mblk_t *mp; |
| |
| /* Dump the state structure, then unlink it */ |
| qprocsoff(q); |
| if (tp->t_bufcallid != 0) { |
| qunbufcall(q, tp->t_bufcallid); |
| tp->t_bufcallid = 0; |
| } |
| if ((mp = tp->t_iocpending) != NULL) |
| freemsg(mp); |
| kmem_free(tp, sizeof (ttcompat_state_t)); |
| q->q_ptr = NULL; |
| |
| return (0); |
| } |
| |
| /* |
| * 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 void |
| ttcompatrput(queue_t *q, 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 void |
| ttcompatwput(queue_t *q, mblk_t *mp) |
| { |
| ttcompat_state_t *tp; |
| struct copyreq *cqp; |
| struct copyresp *csp; |
| struct iocblk *iocbp; |
| |
| tp = (ttcompat_state_t *)q->q_ptr; |
| |
| /* |
| * Process some M_IOCTL messages here; pass everything else down. |
| */ |
| switch (mp->b_datap->db_type) { |
| |
| default: |
| putnext(q, mp); |
| return; |
| |
| case M_IOCTL: |
| iocbp = (struct iocblk *)mp->b_rptr; |
| |
| switch (iocbp->ioc_cmd) { |
| |
| default: |
| /* these are ioctls with no arguments or are known to stream head */ |
| /* process them right away */ |
| ttcompat_do_ioctl(tp, q, mp); |
| return; |
| case TIOCSETN: |
| case TIOCSLTC: |
| case TIOCSETC: |
| case TIOCLBIS: |
| case TIOCLBIC: |
| case TIOCLSET: |
| case TIOCFLUSH: |
| if (iocbp->ioc_count != TRANSPARENT) { |
| putnext(q, mp); |
| return; |
| } |
| |
| mp->b_datap->db_type = M_COPYIN; |
| cqp = (struct copyreq *)mp->b_rptr; |
| cqp->cq_addr = (caddr_t)*(intptr_t *)mp->b_cont->b_rptr; |
| switch (iocbp->ioc_cmd) { |
| case TIOCSETN: |
| cqp->cq_size = sizeof (struct sgttyb); |
| break; |
| case TIOCSLTC: |
| cqp->cq_size = sizeof (struct ltchars); |
| break; |
| case TIOCSETC: |
| cqp->cq_size = sizeof (struct tchars); |
| break; |
| case TIOCLBIS: |
| case TIOCLBIC: |
| case TIOCLSET: |
| case TIOCFLUSH: |
| cqp->cq_size = sizeof (int); |
| break; |
| default: |
| break; |
| } |
| cqp->cq_flag = 0; |
| cqp->cq_private = NULL; |
| freemsg(mp->b_cont); |
| mp->b_cont = NULL; |
| mp->b_wptr = mp->b_rptr + sizeof (struct copyreq); |
| tp->t_ioccmd = iocbp->ioc_cmd; |
| tp->t_state |= TS_W_IN; |
| qreply(q, mp); |
| return; |
| |
| } /* switch ioc_cmd */ |
| case M_IOCDATA: |
| csp = (struct copyresp *)mp->b_rptr; |
| |
| switch (csp->cp_cmd) { |
| |
| default: |
| putnext(q, mp); |
| return; |
| |
| case TIOCSETN: |
| case TIOCSLTC: |
| case TIOCSETC: |
| case TIOCLBIS: |
| case TIOCLBIC: |
| case TIOCLSET: |
| case TIOCFLUSH: |
| tp->t_state &= ~TS_W_IN; |
| if (csp->cp_rval != 0) { /* failure */ |
| freemsg(mp); |
| return; |
| } |
| |
| /* make it look like an ioctl */ |
| mp->b_datap->db_type = M_IOCTL; |
| mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); |
| iocbp = (struct iocblk *)mp->b_rptr; |
| iocbp->ioc_count = MBLKL(mp->b_cont); |
| iocbp->ioc_error = 0; |
| iocbp->ioc_rval = 0; |
| ttcompat_do_ioctl(tp, q, mp); |
| return; |
| |
| case TIOCGLTC: |
| case TIOCLGET: |
| case TIOCGETC: |
| tp->t_state &= ~TS_W_OUT; |
| if (csp->cp_rval != 0) { /* failure */ |
| freemsg(mp); |
| return; |
| } |
| |
| iocbp = (struct iocblk *)mp->b_rptr; |
| iocbp->ioc_count = 0; |
| iocbp->ioc_error = 0; |
| iocbp->ioc_rval = 0; |
| mp->b_datap->db_type = M_IOCACK; |
| qreply(q, mp); |
| return; |
| |
| } /* switch cp_cmd */ |
| } /* end message switch */ |
| } |
| |
| /* |
| * Retry an "ioctl", now that "bufcall" claims we may be able to allocate |
| * the buffer we need. |
| */ |
| static void |
| ttcompat_reioctl(void *arg) |
| { |
| queue_t *q = arg; |
| ttcompat_state_t *tp; |
| mblk_t *mp; |
| |
| tp = (ttcompat_state_t *)q->q_ptr; |
| tp->t_bufcallid = 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(ttcompat_state_t *tp, queue_t *q, mblk_t *mp) |
| { |
| struct iocblk *iocp; |
| int error; |
| |
| /* |
| * Most of the miocpullup()'s below aren't needed because the |
| * ioctls in question are actually transparent M_IOCDATA messages |
| * dummied to look like M_IOCTL messages. However, for clarity and |
| * robustness against future changes, we've included them anyway. |
| */ |
| |
| 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 TIOCGETC: |
| case TIOCLGET: |
| case TIOCGLTC: |
| if (iocp->ioc_count != TRANSPARENT) { |
| miocnak(q, mp, 0, EINVAL); |
| return; |
| } |
| |
| /* |
| * We can get here with t_arg != 0, iff the stream head |
| * has for some reason given up on the ioctl in progress. |
| * The most likely cause is an interrupted ioctl syscall. |
| * We will behave robustly because (given our perimeter) |
| * the ttcompat_state_t will get set up for the new ioctl, |
| * and when the response we were waiting for appears it |
| * will be passed on to the stream head which will discard |
| * it as non-current. |
| */ |
| ASSERT(mp->b_cont != NULL); |
| tp->t_arg = *(intptr_t *)mp->b_cont->b_rptr; |
| /* free the data buffer - it might not be sufficient */ |
| /* driver will allocate one for termios size */ |
| freemsg(mp->b_cont); |
| mp->b_cont = NULL; |
| iocp->ioc_count = 0; |
| /* FALLTHRU */ |
| case TIOCGETP: |
| goto dogets; |
| |
| /* |
| * "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: |
| error = miocpullup(mp, sizeof (struct sgttyb)); |
| if (error != 0) { |
| miocnak(q, mp, 0, error); |
| return; |
| } |
| tp->t_new_sgttyb = *((struct sgttyb *)mp->b_cont->b_rptr); |
| goto dogets; |
| |
| case TIOCSETC: |
| error = miocpullup(mp, sizeof (struct tchars)); |
| if (error != 0) { |
| miocnak(q, mp, 0, error); |
| return; |
| } |
| tp->t_new_tchars = *((struct tchars *)mp->b_cont->b_rptr); |
| goto dogets; |
| |
| case TIOCSLTC: |
| error = miocpullup(mp, sizeof (struct ltchars)); |
| if (error != 0) { |
| miocnak(q, mp, 0, error); |
| return; |
| } |
| tp->t_new_ltchars = *((struct ltchars *)mp->b_cont->b_rptr); |
| goto dogets; |
| |
| case TIOCLBIS: |
| case TIOCLBIC: |
| case TIOCLSET: |
| error = miocpullup(mp, sizeof (int)); |
| if (error != 0) { |
| miocnak(q, mp, 0, error); |
| return; |
| } |
| tp->t_new_lflags = *(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; |
| |
| /* |
| * "set"-style call that sets DTR. Pretend that it was a TIOCMBIS |
| * with TIOCM_DTR set. |
| */ |
| case TIOCSDTR: { |
| mblk_t *datap; |
| |
| if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) |
| goto allocfailure; |
| *(int *)datap->b_wptr = TIOCM_DTR; |
| datap->b_wptr += sizeof (int); |
| 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 */ |
| iocp->ioc_count = sizeof (int); /* in case driver checks */ |
| break; |
| } |
| |
| /* |
| * "set"-style call that clears DTR. Pretend that it was a TIOCMBIC |
| * with TIOCM_DTR set. |
| */ |
| case TIOCCDTR: { |
| mblk_t *datap; |
| |
| if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) |
| goto allocfailure; |
| *(int *)datap->b_wptr = TIOCM_DTR; |
| datap->b_wptr += sizeof (int); |
| 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 */ |
| iocp->ioc_count = sizeof (int); /* in case driver checks */ |
| break; |
| } |
| |
| /* |
| * Translate into the S5 form of TCFLSH. |
| */ |
| case TIOCFLUSH: { |
| int flags; |
| |
| error = miocpullup(mp, sizeof (int)); |
| if (error != 0) { |
| miocnak(q, mp, 0, error); |
| return; |
| } |
| 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: { |
| mblk_t *datap; |
| |
| if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) |
| goto allocfailure; |
| *(int *)datap->b_wptr = 0; /* stop */ |
| datap->b_wptr += sizeof (int); |
| iocp->ioc_cmd = TCXONC; /* turn it into a XONC */ |
| iocp->ioc_count = sizeof (int); |
| if (mp->b_cont != NULL) |
| freemsg(mp->b_cont); |
| mp->b_cont = datap; /* attach the data */ |
| break; |
| } |
| |
| case TIOCSTART: { |
| mblk_t *datap; |
| |
| if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) |
| goto allocfailure; |
| *(int *)datap->b_wptr = 1; /* start */ |
| datap->b_wptr += sizeof (int); |
| iocp->ioc_cmd = TCXONC; /* turn it into a XONC */ |
| iocp->ioc_count = sizeof (int); |
| if (mp->b_cont != NULL) |
| freemsg(mp->b_cont); |
| mp->b_cont = datap; /* attach the data */ |
| break; |
| } |
| case TIOCSETD: |
| case TIOCGETD: |
| case DIOCSETP: |
| case DIOCGETP: |
| case LDOPEN: |
| case LDCLOSE: |
| case LDCHG: |
| case LDSETT: |
| case LDGETT: |
| /* |
| * All of these ioctls are just ACK'd, except for |
| * TIOCSETD, which must be for line discipline zero. |
| */ |
| mp->b_datap->db_type = M_IOCACK; |
| if (iocp->ioc_cmd == TIOCSETD) { |
| iocp->ioc_error = miocpullup(mp, sizeof (uchar_t)); |
| if (iocp->ioc_error == 0 && (*mp->b_cont->b_rptr != 0)) |
| mp->b_datap->db_type = M_IOCNAK; |
| } |
| |
| iocp->ioc_error = 0; |
| iocp->ioc_count = 0; |
| iocp->ioc_rval = 0; |
| qreply(q, mp); |
| return; |
| case IOCTYPE: |
| mp->b_datap->db_type = M_IOCACK; |
| iocp->ioc_error = 0; |
| iocp->ioc_count = 0; |
| iocp->ioc_rval = TIOC; |
| qreply(q, mp); |
| return; |
| case TIOCEXCL: |
| /* check for binary value of XCLUDE flag ???? */ |
| tp->t_new_lflags |= XCLUDE; |
| mp->b_datap->db_type = M_IOCACK; |
| iocp->ioc_error = 0; |
| iocp->ioc_count = 0; |
| iocp->ioc_rval = 0; |
| qreply(q, mp); |
| return; |
| case TIOCNXCL: |
| tp->t_new_lflags &= ~XCLUDE; |
| mp->b_datap->db_type = M_IOCACK; |
| iocp->ioc_error = 0; |
| iocp->ioc_count = 0; |
| iocp->ioc_rval = 0; |
| qreply(q, mp); |
| return; |
| } |
| |
| /* |
| * 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. |
| */ |
| if (tp->t_iocpending != NULL) |
| freemsg(tp->t_iocpending); |
| tp->t_iocpending = mp; /* hold this ioctl */ |
| if (tp->t_bufcallid != 0) |
| qunbufcall(q, tp->t_bufcallid); |
| |
| tp->t_bufcallid = qbufcall(q, sizeof (struct iocblk), BPRI_HI, |
| ttcompat_reioctl, q); |
| } |
| |
| /* |
| * 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(queue_t *q, mblk_t *mp) |
| { |
| ttcompat_state_t *tp; |
| struct iocblk *iocp; |
| 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: { |
| struct sgttyb *cb; |
| |
| to_compat((struct termios *)datap->b_rptr, &tp->t_curstate); |
| datap->b_rptr = datap->b_wptr = datap->b_datap->db_base; |
| /* recycle the reply's buffer */ |
| cb = (struct sgttyb *)datap->b_wptr; |
| /* |
| * This is used for TIOCGETP handling of sg_ispeed and |
| * sg_ospeed. If the current speed is over 38400 (the |
| * sgttyb limit), then we report 38400. Note that |
| * when "compatibility with old releases" is enabled |
| * (sgttyb_handling == 0), then t_[io]speed will have |
| * garbled nonsense, as in prior releases. (See |
| * to_compat() below). |
| */ |
| cb->sg_ispeed = tp->t_curstate.t_ispeed > B38400 ? B38400 : |
| tp->t_curstate.t_ispeed; |
| cb->sg_ospeed = tp->t_curstate.t_ospeed > B38400 ? B38400 : |
| 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); |
| iocp->ioc_count = sizeof (struct sgttyb); |
| |
| /* you are lucky - stream head knows how to copy you out */ |
| |
| tp->t_state &= ~TS_IOCWAIT; /* we got what we wanted */ |
| iocp->ioc_rval = 0; |
| iocp->ioc_cmd = tp->t_ioccmd; |
| putnext(q, mp); |
| return; |
| } |
| |
| case TIOCGETC: |
| to_compat((struct termios *)datap->b_rptr, &tp->t_curstate); |
| datap->b_rptr = datap->b_wptr = datap->b_datap->db_base; |
| /* recycle the reply's buffer */ |
| bcopy(&tp->t_curstate.t_intrc, datap->b_wptr, |
| sizeof (struct tchars)); |
| datap->b_wptr += sizeof (struct tchars); |
| break; |
| |
| case TIOCGLTC: |
| to_compat((struct termios *)datap->b_rptr, &tp->t_curstate); |
| datap->b_rptr = datap->b_wptr = datap->b_datap->db_base; |
| /* recycle the reply's buffer */ |
| bcopy(&tp->t_curstate.t_suspc, datap->b_wptr, |
| sizeof (struct ltchars)); |
| datap->b_wptr += sizeof (struct ltchars); |
| break; |
| |
| case TIOCLGET: |
| to_compat((struct termios *)datap->b_rptr, &tp->t_curstate); |
| datap->b_rptr = 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); |
| 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; |
| /* |
| * For new-style handling, we ignore requests to set |
| * B38400 when the current speed is over B38400. This |
| * means that we change the speed as requested if: |
| * old style (sgttyb_handling == 0) is requested |
| * the requested new speed isn't B38400 |
| * the current speed is at or below B38400 |
| * Note that when old style is requested, both speeds |
| * in t_curstate are set to <= B38400 by to_compat, so |
| * the first test isn't needed here. |
| * Also note that we silently allow the user to set |
| * speeds above B38400 through this interface, |
| * regardless of the style setting. This allows |
| * greater compatibility with current BSD releases. |
| */ |
| if (tp->t_new_sgttyb.sg_ispeed != B38400 || |
| tp->t_curstate.t_ispeed <= B38400) |
| tp->t_curstate.t_ispeed = tp->t_new_sgttyb.sg_ispeed; |
| if (tp->t_new_sgttyb.sg_ospeed != B38400 || |
| tp->t_curstate.t_ospeed <= B38400) |
| 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(&tp->t_new_tchars, |
| &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(&tp->t_new_ltchars, |
| &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 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; |
| |
| case TCSETSF: |
| /* |
| * We're acknowledging the terminal reset ioctl that we sent |
| * when the module was opened. |
| */ |
| tp->t_state &= ~(TS_IOCWAIT | TS_TIOCNAK); |
| freemsg(mp); |
| return; |
| |
| default: |
| cmn_err(CE_WARN, "ttcompat: Unexpected ioctl acknowledgment\n"); |
| } |
| |
| /* |
| * All the calls that return something return 0. |
| */ |
| tp->t_state &= ~TS_IOCWAIT; /* we got what we wanted */ |
| iocp->ioc_rval = 0; |
| |
| /* copy out the data - ioctl transparency */ |
| iocp->ioc_cmd = tp->t_ioccmd; |
| ttcopyout(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; |
| mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); |
| putnext(WR(q), mp); |
| } |
| /* Called from ttcompatrput M_IOCACK processing. */ |
| /* Copies out the data using M_COPYOUT messages */ |
| |
| static void |
| ttcopyout(queue_t *q, mblk_t *mp) |
| { |
| struct copyreq *cqp; |
| ttcompat_state_t *tp; |
| |
| tp = (ttcompat_state_t *)q->q_ptr; |
| |
| mp->b_datap->db_type = M_COPYOUT; |
| cqp = (struct copyreq *)mp->b_rptr; |
| cqp->cq_addr = (caddr_t)tp->t_arg; /* retrieve the 3rd argument */ |
| tp->t_arg = 0; /* clear it since we don't need it anymore */ |
| switch (tp->t_ioccmd) { |
| case TIOCGLTC: |
| cqp->cq_size = sizeof (struct ltchars); |
| break; |
| case TIOCGETC: |
| cqp->cq_size = sizeof (struct tchars); |
| break; |
| case TIOCLGET: |
| cqp->cq_size = sizeof (int); |
| break; |
| default: |
| cmn_err(CE_WARN, |
| "ttcompat: Unknown ioctl to copyout\n"); |
| break; |
| } |
| cqp->cq_flag = 0; |
| cqp->cq_private = NULL; |
| tp->t_state |= TS_W_OUT; |
| putnext(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(queue_t *q, mblk_t *mp) |
| { |
| ttcompat_state_t *tp; |
| 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) { |
| tp->t_state &= ~TS_IOCWAIT; /* this call isn't going through */ |
| tp->t_arg = 0; /* we may have stashed the 3rd argument */ |
| } |
| putnext(q, mp); |
| } |
| |
| #define FROM_COMPAT_CHAR(to, from) { if ((to = from) == 0377) to = 0; } |
| |
| static void |
| from_compat(compat_state_t *csp, struct termios *termiosp) |
| { |
| termiosp->c_iflag = 0; |
| termiosp->c_oflag &= (ONLRET|ONOCR); |
| |
| termiosp->c_cflag = (termiosp->c_cflag & |
| (CRTSCTS|CRTSXOFF|PAREXT|LOBLK|HUPCL)) | CREAD; |
| |
| if (csp->t_ospeed > CBAUD) { |
| termiosp->c_cflag |= ((csp->t_ospeed - CBAUD - 1) & CBAUD) | |
| CBAUDEXT; |
| } else { |
| termiosp->c_cflag |= csp->t_ospeed & CBAUD; |
| } |
| |
| if (csp->t_ospeed != csp->t_ispeed) { |
| if (csp->t_ispeed > (CIBAUD >> IBSHIFT)) { |
| termiosp->c_cflag |= CIBAUDEXT | |
| (((csp->t_ispeed - (CIBAUD >> IBSHIFT) - 1) << |
| IBSHIFT) & CIBAUD); |
| } else { |
| termiosp->c_cflag |= (csp->t_ispeed << IBSHIFT) & |
| CIBAUD; |
| } |
| /* hang up if ispeed=0 */ |
| if (csp->t_ispeed == 0) |
| termiosp->c_cflag &= ~CBAUD & ~CBAUDEXT; |
| } |
| if (csp->t_ispeed == 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); |
| /* is this useful? */ |
| FROM_COMPAT_CHAR(termiosp->c_cc[VDSUSP], csp->t_dsuspc); |
| 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); |
| termiosp->c_cc[VSTATUS] = 0; |
| 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 (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 = (uchar_t)0377; } |
| |
| static void |
| to_compat(struct termios *termiosp, compat_state_t *csp) |
| { |
| csp->t_xflags &= (NOISIG|NOPOST); |
| csp->t_ospeed = termiosp->c_cflag & CBAUD; |
| csp->t_ispeed = (termiosp->c_cflag & CIBAUD) >> IBSHIFT; |
| if (sgttyb_handling > 0) { |
| if (termiosp->c_cflag & CBAUDEXT) |
| csp->t_ospeed += CBAUD + 1; |
| if (termiosp->c_cflag & CIBAUDEXT) |
| csp->t_ispeed += (CIBAUD >> IBSHIFT) + 1; |
| } |
| if (csp->t_ispeed == 0) |
| csp->t_ispeed = csp->t_ospeed; |
| if ((termiosp->c_cflag & CSTOPB) && csp->t_ispeed != 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 = (uchar_t)0377; |
| csp->t_stopc = (uchar_t)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) && (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 (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; |
| } |
| } |