/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This is the loadable module wrapper. */ #include #include /* 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 int ttcompatrput(queue_t *, mblk_t *); static int ttcompatwput(queue_t *, mblk_t *); 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 }; 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 int 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; } return (0); } /* * Line discipline output queue put procedure: speeds M_IOCTL * messages. */ static int 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 (0); 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 (0); case TIOCSETN: case TIOCSLTC: case TIOCSETC: case TIOCLBIS: case TIOCLBIC: case TIOCLSET: case TIOCFLUSH: if (iocbp->ioc_count != TRANSPARENT) { putnext(q, mp); return (0); } 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 (0); } /* switch ioc_cmd */ case M_IOCDATA: csp = (struct copyresp *)mp->b_rptr; switch (csp->cp_cmd) { default: putnext(q, mp); return (0); 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 (0); } /* 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 (0); case TIOCGLTC: case TIOCLGET: case TIOCGETC: tp->t_state &= ~TS_W_OUT; if (csp->cp_rval != 0) { /* failure */ freemsg(mp); return (0); } 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 (0); } /* switch cp_cmd */ } /* end message switch */ return (0); } /* * 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; } }