1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  27 /*        All Rights Reserved   */
  28 
  29 /*
  30  * University Copyright- Copyright (c) 1982, 1986, 1988
  31  * The Regents of the University of California
  32  * All Rights Reserved
  33  *
  34  * University Acknowledgment- Portions of this document are derived from
  35  * software developed by the University of California, Berkeley, and its
  36  * contributors.
  37  */
  38 
  39 /*
  40  * Module to intercept old V7 and 4BSD "ioctl" calls.
  41  */
  42 
  43 #include <sys/types.h>
  44 #include <sys/param.h>
  45 #include <sys/signal.h>
  46 #include <sys/file.h>
  47 #include <sys/termios.h>
  48 #include <sys/ttold.h>
  49 #include <sys/cmn_err.h>
  50 #include <sys/stream.h>
  51 #include <sys/stropts.h>
  52 #include <sys/strsubr.h>
  53 #include <sys/strsun.h>
  54 #include <sys/errno.h>
  55 #include <sys/debug.h>
  56 #include <sys/ttcompat.h>
  57 #include <sys/ddi.h>
  58 #include <sys/sunddi.h>
  59 #include <sys/kmem.h>
  60 #include <sys/policy.h>
  61 
  62 /*
  63  * This is the loadable module wrapper.
  64  */
  65 #include <sys/conf.h>
  66 #include <sys/modctl.h>
  67 
  68 /* See os/streamio.c */
  69 extern int sgttyb_handling;
  70 
  71 static struct streamtab ttcoinfo;
  72 
  73 static struct fmodsw fsw = {
  74         "ttcompat",
  75         &ttcoinfo,
  76         D_MTQPAIR | D_MP
  77 };
  78 
  79 /*
  80  * Module linkage information for the kernel.
  81  */
  82 
  83 static struct modlstrmod modlstrmod = {
  84         &mod_strmodops,
  85         "alt ioctl calls",
  86         &fsw
  87 };
  88 
  89 static struct modlinkage modlinkage = {
  90         MODREV_1, { &modlstrmod, NULL }
  91 };
  92 
  93 int
  94 _init(void)
  95 {
  96         return (mod_install(&modlinkage));
  97 }
  98 
  99 int
 100 _fini(void)
 101 {
 102         return (mod_remove(&modlinkage));
 103 }
 104 
 105 int
 106 _info(struct modinfo *modinfop)
 107 {
 108         return (mod_info(&modlinkage, modinfop));
 109 }
 110 
 111 static int ttcompatopen(queue_t *, dev_t *, int, int, cred_t *);
 112 static int ttcompatclose(queue_t *, int, cred_t *);
 113 static void ttcompatrput(queue_t *, mblk_t *);
 114 static void ttcompatwput(queue_t *, mblk_t *);
 115 
 116 static struct module_info ttycompatmiinfo = {
 117         0,
 118         "ttcompat",
 119         0,
 120         INFPSZ,
 121         2048,
 122         128
 123 };
 124 
 125 static struct qinit ttycompatrinit = {
 126         (int (*)())ttcompatrput,
 127         NULL,
 128         ttcompatopen,
 129         ttcompatclose,
 130         NULL,
 131         &ttycompatmiinfo
 132 };
 133 
 134 static struct module_info ttycompatmoinfo = {
 135         42,
 136         "ttcompat",
 137         0,
 138         INFPSZ,
 139         300,
 140         200
 141 };
 142 
 143 static struct qinit ttycompatwinit = {
 144         (int (*)())ttcompatwput,
 145         NULL,
 146         ttcompatopen,
 147         ttcompatclose,
 148         NULL,
 149         &ttycompatmoinfo
 150 };
 151 
 152 static struct streamtab ttcoinfo = {
 153         &ttycompatrinit,
 154         &ttycompatwinit,
 155         NULL,
 156         NULL
 157 };
 158 
 159 /*
 160  * This is the termios structure that is used to reset terminal settings
 161  * when the underlying device is an instance of zcons.  It came from
 162  * cmd/init/init.c and should be kept in-sync with dflt_termios found therein.
 163  */
 164 static const struct termios base_termios = {
 165         BRKINT|ICRNL|IXON|IMAXBEL,                              /* iflag */
 166         OPOST|ONLCR|TAB3,                                       /* oflag */
 167         CS8|CREAD|B9600,                                        /* cflag */
 168         ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN,     /* lflag */
 169         {   CINTR, CQUIT, CERASE, CKILL, CEOF,                  /* c_cc vals */
 170             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 171 };
 172 
 173 
 174 static void ttcompat_do_ioctl(ttcompat_state_t *, queue_t *, mblk_t *);
 175 static void ttcompat_ioctl_ack(queue_t *, mblk_t *);
 176 static void ttcopyout(queue_t *, mblk_t *);
 177 static void ttcompat_ioctl_nak(queue_t *, mblk_t *);
 178 static void from_compat(compat_state_t *, struct termios *);
 179 static void to_compat(struct termios *, compat_state_t *);
 180 
 181 /*
 182  * Open - get the current modes and translate them to the V7/4BSD equivalent.
 183  */
 184 /*ARGSUSED*/
 185 static int
 186 ttcompatopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
 187 {
 188         ttcompat_state_t *tp;
 189         mblk_t *mp;
 190         mblk_t *datamp;
 191         struct iocblk *iocb;
 192         int error;
 193 
 194         if (q->q_ptr != NULL)  {
 195                 tp = (ttcompat_state_t *)q->q_ptr;
 196                 /* fail open if TIOCEXCL was done and its not privileged */
 197                 if ((tp->t_new_lflags & XCLUDE) &&
 198                     secpolicy_excl_open(crp) != 0) {
 199                         return (EBUSY);
 200                 }
 201                 return (0);             /* already attached */
 202         }
 203         tp = kmem_zalloc(sizeof (ttcompat_state_t), KM_SLEEP);
 204         tp->t_iocpending = NULL;
 205         tp->t_state = 0;
 206         tp->t_iocid = 0;
 207         tp->t_ioccmd = 0;
 208         tp->t_new_lflags = 0;
 209         tp->t_curstate.t_flags = 0;
 210         tp->t_curstate.t_ispeed = B0;
 211         tp->t_curstate.t_ospeed = B0;
 212         tp->t_curstate.t_erase = '\0';
 213         tp->t_curstate.t_kill = '\0';
 214         tp->t_curstate.t_intrc = '\0';
 215         tp->t_curstate.t_quitc = '\0';
 216         tp->t_curstate.t_startc = '\0';
 217         tp->t_curstate.t_stopc = '\0';
 218         tp->t_curstate.t_eofc = '\0';
 219         tp->t_curstate.t_brkc = '\0';
 220         tp->t_curstate.t_suspc = '\0';
 221         tp->t_curstate.t_dsuspc = '\0';
 222         tp->t_curstate.t_rprntc = '\0';
 223         tp->t_curstate.t_flushc = '\0';
 224         tp->t_curstate.t_werasc = '\0';
 225         tp->t_curstate.t_lnextc = '\0';
 226         tp->t_curstate.t_xflags = 0;
 227         tp->t_bufcallid = 0;
 228         tp->t_arg = 0;
 229 
 230         q->q_ptr = tp;
 231         WR(q)->q_ptr = tp;
 232         qprocson(q);
 233 
 234         /*
 235          * Determine if the underlying device is a zcons instance.  If so,
 236          * then issue a termios ioctl to reset the terminal settings.
 237          */
 238         if (getmajor(q->q_stream->sd_vnode->v_rdev) !=
 239             ddi_name_to_major("zcons"))
 240                 return (0);
 241 
 242         /*
 243          * Create the ioctl message.
 244          */
 245         if ((mp = mkiocb(TCSETSF)) == NULL) {
 246                 error = ENOMEM;
 247                 goto common_error;
 248         }
 249         if ((datamp = allocb(sizeof (struct termios), BPRI_HI)) == NULL) {
 250                 freemsg(mp);
 251                 error = ENOMEM;
 252                 goto common_error;
 253         }
 254         iocb = (struct iocblk *)mp->b_rptr;
 255         iocb->ioc_count = sizeof (struct termios);
 256         bcopy(&base_termios, datamp->b_rptr, sizeof (struct termios));
 257         datamp->b_wptr += sizeof (struct termios);
 258         mp->b_cont = datamp;
 259 
 260         /*
 261          * Send the ioctl message on its merry way toward the driver.
 262          * Set some state beforehand so we can properly wait for
 263          * an acknowledgement.
 264          */
 265         tp->t_state |= TS_IOCWAIT | TS_TIOCNAK;
 266         tp->t_iocid = iocb->ioc_id;
 267         tp->t_ioccmd = TCSETSF;
 268         putnext(WR(q), mp);
 269 
 270         /*
 271          * Wait for an acknowledgement.  A NAK is treated as an error.
 272          * The presence of the TS_TIOCNAK flag indicates that a NAK was
 273          * received.
 274          */
 275         while (tp->t_state & TS_IOCWAIT) {
 276                 if (qwait_sig(q) == 0) {
 277                         error = EINTR;
 278                         goto common_error;
 279                 }
 280         }
 281         if (!(tp->t_state & TS_TIOCNAK))
 282                 return (0);
 283         error = ENOTTY;
 284 
 285 common_error:
 286         qprocsoff(q);
 287         kmem_free(tp, sizeof (ttcompat_state_t));
 288         q->q_ptr = NULL;
 289         WR(q)->q_ptr = NULL;
 290         return (error);
 291 }
 292 
 293 /* ARGSUSED1 */
 294 static int
 295 ttcompatclose(queue_t *q, int flag, cred_t *crp)
 296 {
 297         ttcompat_state_t *tp = (ttcompat_state_t *)q->q_ptr;
 298         mblk_t *mp;
 299 
 300         /* Dump the state structure, then unlink it */
 301         qprocsoff(q);
 302         if (tp->t_bufcallid != 0) {
 303                 qunbufcall(q, tp->t_bufcallid);
 304                 tp->t_bufcallid = 0;
 305         }
 306         if ((mp = tp->t_iocpending) != NULL)
 307                 freemsg(mp);
 308         kmem_free(tp, sizeof (ttcompat_state_t));
 309         q->q_ptr = NULL;
 310 
 311         return (0);
 312 }
 313 
 314 /*
 315  * Put procedure for input from driver end of stream (read queue).
 316  * Most messages just get passed to the next guy up; we intercept
 317  * "ioctl" replies, and if it's an "ioctl" whose reply we plan to do
 318  * something with, we do it.
 319  */
 320 static void
 321 ttcompatrput(queue_t *q, mblk_t *mp)
 322 {
 323         switch (mp->b_datap->db_type) {
 324 
 325         case M_IOCACK:
 326                 ttcompat_ioctl_ack(q, mp);
 327                 break;
 328 
 329         case M_IOCNAK:
 330                 ttcompat_ioctl_nak(q, mp);
 331                 break;
 332 
 333         default:
 334                 putnext(q, mp);
 335                 break;
 336         }
 337 }
 338 
 339 /*
 340  * Line discipline output queue put procedure: speeds M_IOCTL
 341  * messages.
 342  */
 343 static void
 344 ttcompatwput(queue_t *q, mblk_t *mp)
 345 {
 346         ttcompat_state_t *tp;
 347         struct copyreq *cqp;
 348         struct copyresp *csp;
 349         struct iocblk *iocbp;
 350 
 351         tp = (ttcompat_state_t *)q->q_ptr;
 352 
 353         /*
 354          * Process some M_IOCTL messages here; pass everything else down.
 355          */
 356         switch (mp->b_datap->db_type) {
 357 
 358         default:
 359                 putnext(q, mp);
 360                 return;
 361 
 362         case M_IOCTL:
 363                 iocbp = (struct iocblk *)mp->b_rptr;
 364 
 365                 switch (iocbp->ioc_cmd) {
 366 
 367                 default:
 368         /* these are ioctls with no arguments or are known to stream head */
 369         /* process them right away */
 370                         ttcompat_do_ioctl(tp, q, mp);
 371                         return;
 372                 case TIOCSETN:
 373                 case TIOCSLTC:
 374                 case TIOCSETC:
 375                 case TIOCLBIS:
 376                 case TIOCLBIC:
 377                 case TIOCLSET:
 378                 case TIOCFLUSH:
 379                         if (iocbp->ioc_count != TRANSPARENT) {
 380                                 putnext(q, mp);
 381                                 return;
 382                         }
 383 
 384                         mp->b_datap->db_type = M_COPYIN;
 385                         cqp = (struct copyreq *)mp->b_rptr;
 386                         cqp->cq_addr = (caddr_t)*(intptr_t *)mp->b_cont->b_rptr;
 387                         switch (iocbp->ioc_cmd) {
 388                                 case TIOCSETN:
 389                                         cqp->cq_size = sizeof (struct sgttyb);
 390                                         break;
 391                                 case TIOCSLTC:
 392                                         cqp->cq_size = sizeof (struct ltchars);
 393                                         break;
 394                                 case TIOCSETC:
 395                                         cqp->cq_size = sizeof (struct tchars);
 396                                         break;
 397                                 case TIOCLBIS:
 398                                 case TIOCLBIC:
 399                                 case TIOCLSET:
 400                                 case TIOCFLUSH:
 401                                         cqp->cq_size = sizeof (int);
 402                                         break;
 403                                 default:
 404                                         break;
 405                         }
 406                         cqp->cq_flag = 0;
 407                         cqp->cq_private = NULL;
 408                         freemsg(mp->b_cont);
 409                         mp->b_cont = NULL;
 410                         mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
 411                         tp->t_ioccmd = iocbp->ioc_cmd;
 412                         tp->t_state |= TS_W_IN;
 413                         qreply(q, mp);
 414                         return;
 415 
 416                 } /* switch ioc_cmd */
 417         case M_IOCDATA:
 418                 csp = (struct copyresp *)mp->b_rptr;
 419 
 420                 switch (csp->cp_cmd) {
 421 
 422                 default:
 423                         putnext(q, mp);
 424                         return;
 425 
 426                 case TIOCSETN:
 427                 case TIOCSLTC:
 428                 case TIOCSETC:
 429                 case TIOCLBIS:
 430                 case TIOCLBIC:
 431                 case TIOCLSET:
 432                 case TIOCFLUSH:
 433                         tp->t_state &= ~TS_W_IN;
 434                         if (csp->cp_rval != 0) {     /* failure */
 435                                 freemsg(mp);
 436                                 return;
 437                         }
 438 
 439                         /* make it look like an ioctl */
 440                         mp->b_datap->db_type = M_IOCTL;
 441                         mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
 442                         iocbp = (struct iocblk *)mp->b_rptr;
 443                         iocbp->ioc_count = MBLKL(mp->b_cont);
 444                         iocbp->ioc_error = 0;
 445                         iocbp->ioc_rval = 0;
 446                         ttcompat_do_ioctl(tp, q, mp);
 447                         return;
 448 
 449                 case TIOCGLTC:
 450                 case TIOCLGET:
 451                 case TIOCGETC:
 452                         tp->t_state &= ~TS_W_OUT;
 453                         if (csp->cp_rval != 0) {     /* failure */
 454                                 freemsg(mp);
 455                                 return;
 456                         }
 457 
 458                         iocbp = (struct iocblk *)mp->b_rptr;
 459                         iocbp->ioc_count = 0;
 460                         iocbp->ioc_error = 0;
 461                         iocbp->ioc_rval = 0;
 462                         mp->b_datap->db_type = M_IOCACK;
 463                         qreply(q, mp);
 464                         return;
 465 
 466                 } /* switch cp_cmd */
 467         } /* end message switch */
 468 }
 469 
 470 /*
 471  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
 472  * the buffer we need.
 473  */
 474 static void
 475 ttcompat_reioctl(void *arg)
 476 {
 477         queue_t *q = arg;
 478         ttcompat_state_t *tp;
 479         mblk_t *mp;
 480 
 481         tp = (ttcompat_state_t *)q->q_ptr;
 482         tp->t_bufcallid = 0;
 483 
 484         if ((mp = tp->t_iocpending) != NULL) {
 485                 tp->t_iocpending = NULL;     /* not pending any more */
 486                 ttcompat_do_ioctl(tp, q, mp);
 487         }
 488 }
 489 
 490 /*
 491  * Handle old-style "ioctl" messages; pass the rest down unmolested.
 492  */
 493 static void
 494 ttcompat_do_ioctl(ttcompat_state_t *tp, queue_t *q, mblk_t *mp)
 495 {
 496         struct iocblk *iocp;
 497         int error;
 498 
 499         /*
 500          * Most of the miocpullup()'s below aren't needed because the
 501          * ioctls in question are actually transparent M_IOCDATA messages
 502          * dummied to look like M_IOCTL messages.  However, for clarity and
 503          * robustness against future changes, we've included them anyway.
 504          */
 505 
 506         iocp = (struct iocblk *)mp->b_rptr;
 507         switch (iocp->ioc_cmd) {
 508 
 509         /*
 510          * "get"-style calls that get translated data from the "termios"
 511          * structure.  Save the existing code and pass it down as a TCGETS.
 512          */
 513         case TIOCGETC:
 514         case TIOCLGET:
 515         case TIOCGLTC:
 516                 if (iocp->ioc_count != TRANSPARENT) {
 517                         miocnak(q, mp, 0, EINVAL);
 518                         return;
 519                 }
 520 
 521                 /*
 522                  * We can get here with t_arg != 0, iff the stream head
 523                  * has for some reason given up on the ioctl in progress.
 524                  * The most likely cause is an interrupted ioctl syscall.
 525                  * We will behave robustly because (given our perimeter)
 526                  * the ttcompat_state_t will get set up for the new ioctl,
 527                  * and when the response we were waiting for appears it
 528                  * will be passed on to the stream head which will discard
 529                  * it as non-current.
 530                  */
 531                 ASSERT(mp->b_cont != NULL);
 532                 tp->t_arg = *(intptr_t *)mp->b_cont->b_rptr;
 533                 /* free the data buffer - it might not be sufficient */
 534                 /* driver will allocate one for termios size */
 535                 freemsg(mp->b_cont);
 536                 mp->b_cont = NULL;
 537                 iocp->ioc_count = 0;
 538                 /* FALLTHRU */
 539         case TIOCGETP:
 540                 goto dogets;
 541 
 542         /*
 543          * "set"-style calls that set translated data into a "termios"
 544          * structure.  Set our idea of the new state from the value
 545          * given to us.  We then have to get the current state, so we
 546          * turn this guy into a TCGETS and pass it down.  When the
 547          * ACK comes back, we modify the state we got back and shove it
 548          * back down as the appropriate type of TCSETS.
 549          */
 550         case TIOCSETP:
 551         case TIOCSETN:
 552                 error = miocpullup(mp, sizeof (struct sgttyb));
 553                 if (error != 0) {
 554                         miocnak(q, mp, 0, error);
 555                         return;
 556                 }
 557                 tp->t_new_sgttyb = *((struct sgttyb *)mp->b_cont->b_rptr);
 558                 goto dogets;
 559 
 560         case TIOCSETC:
 561                 error = miocpullup(mp, sizeof (struct tchars));
 562                 if (error != 0) {
 563                         miocnak(q, mp, 0, error);
 564                         return;
 565                 }
 566                 tp->t_new_tchars = *((struct tchars *)mp->b_cont->b_rptr);
 567                 goto dogets;
 568 
 569         case TIOCSLTC:
 570                 error = miocpullup(mp, sizeof (struct ltchars));
 571                 if (error != 0) {
 572                         miocnak(q, mp, 0, error);
 573                         return;
 574                 }
 575                 tp->t_new_ltchars = *((struct ltchars *)mp->b_cont->b_rptr);
 576                 goto dogets;
 577 
 578         case TIOCLBIS:
 579         case TIOCLBIC:
 580         case TIOCLSET:
 581                 error = miocpullup(mp, sizeof (int));
 582                 if (error != 0) {
 583                         miocnak(q, mp, 0, error);
 584                         return;
 585                 }
 586                 tp->t_new_lflags = *(int *)mp->b_cont->b_rptr;
 587                 goto dogets;
 588 
 589         /*
 590          * "set"-style call that sets a particular bit in a "termios"
 591          * structure.  We then have to get the current state, so we
 592          * turn this guy into a TCGETS and pass it down.  When the
 593          * ACK comes back, we modify the state we got back and shove it
 594          * back down as the appropriate type of TCSETS.
 595          */
 596         case TIOCHPCL:
 597         dogets:
 598                 tp->t_ioccmd = iocp->ioc_cmd;
 599                 tp->t_iocid = iocp->ioc_id;
 600                 tp->t_state |= TS_IOCWAIT;
 601                 iocp->ioc_cmd = TCGETS;
 602                 iocp->ioc_count = 0; /* no data returned unless we say so */
 603                 break;
 604 
 605         /*
 606          * "set"-style call that sets DTR.  Pretend that it was a TIOCMBIS
 607          * with TIOCM_DTR set.
 608          */
 609         case TIOCSDTR: {
 610                 mblk_t *datap;
 611 
 612                 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
 613                         goto allocfailure;
 614                 *(int *)datap->b_wptr = TIOCM_DTR;
 615                 datap->b_wptr += sizeof (int);
 616                 iocp->ioc_cmd = TIOCMBIS;    /* turn it into a TIOCMBIS */
 617                 if (mp->b_cont != NULL)
 618                         freemsg(mp->b_cont);
 619                 mp->b_cont = datap;  /* attach the data */
 620                 iocp->ioc_count = sizeof (int);      /* in case driver checks */
 621                 break;
 622         }
 623 
 624         /*
 625          * "set"-style call that clears DTR.  Pretend that it was a TIOCMBIC
 626          * with TIOCM_DTR set.
 627          */
 628         case TIOCCDTR: {
 629                 mblk_t *datap;
 630 
 631                 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
 632                         goto allocfailure;
 633                 *(int *)datap->b_wptr = TIOCM_DTR;
 634                 datap->b_wptr += sizeof (int);
 635                 iocp->ioc_cmd = TIOCMBIC;    /* turn it into a TIOCMBIC */
 636                 if (mp->b_cont != NULL)
 637                         freemsg(mp->b_cont);
 638                 mp->b_cont = datap;  /* attach the data */
 639                 iocp->ioc_count = sizeof (int);      /* in case driver checks */
 640                 break;
 641         }
 642 
 643         /*
 644          * Translate into the S5 form of TCFLSH.
 645          */
 646         case TIOCFLUSH: {
 647                 int flags;
 648 
 649                 error = miocpullup(mp, sizeof (int));
 650                 if (error != 0) {
 651                         miocnak(q, mp, 0, error);
 652                         return;
 653                 }
 654                 flags = *(int *)mp->b_cont->b_rptr;
 655 
 656                 switch (flags&(FREAD|FWRITE)) {
 657 
 658                 case 0:
 659                 case FREAD|FWRITE:
 660                         flags = 2;      /* flush 'em both */
 661                         break;
 662 
 663                 case FREAD:
 664                         flags = 0;      /* flush read */
 665                         break;
 666 
 667                 case FWRITE:
 668                         flags = 1;      /* flush write */
 669                         break;
 670                 }
 671                 iocp->ioc_cmd = TCFLSH;      /* turn it into a TCFLSH */
 672                 *(int *)mp->b_cont->b_rptr = flags;       /* fiddle the arg */
 673                 break;
 674         }
 675 
 676         /*
 677          * Turn into a TCXONC.
 678          */
 679         case TIOCSTOP: {
 680                 mblk_t *datap;
 681 
 682                 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
 683                         goto allocfailure;
 684                 *(int *)datap->b_wptr = 0;   /* stop */
 685                 datap->b_wptr += sizeof (int);
 686                 iocp->ioc_cmd = TCXONC;      /* turn it into a XONC */
 687                 iocp->ioc_count = sizeof (int);
 688                 if (mp->b_cont != NULL)
 689                         freemsg(mp->b_cont);
 690                 mp->b_cont = datap;  /* attach the data */
 691                 break;
 692         }
 693 
 694         case TIOCSTART: {
 695                 mblk_t *datap;
 696 
 697                 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
 698                         goto allocfailure;
 699                 *(int *)datap->b_wptr = 1;   /* start */
 700                 datap->b_wptr += sizeof (int);
 701                 iocp->ioc_cmd = TCXONC;      /* turn it into a XONC */
 702                 iocp->ioc_count = sizeof (int);
 703                 if (mp->b_cont != NULL)
 704                         freemsg(mp->b_cont);
 705                 mp->b_cont = datap;  /* attach the data */
 706                 break;
 707         }
 708         case TIOCSETD:
 709         case TIOCGETD:
 710         case DIOCSETP:
 711         case DIOCGETP:
 712         case LDOPEN:
 713         case LDCLOSE:
 714         case LDCHG:
 715         case LDSETT:
 716         case LDGETT:
 717                 /*
 718                  * All of these ioctls are just ACK'd, except for
 719                  * TIOCSETD, which must be for line discipline zero.
 720                  */
 721                 mp->b_datap->db_type = M_IOCACK;
 722                 if (iocp->ioc_cmd == TIOCSETD) {
 723                         iocp->ioc_error = miocpullup(mp, sizeof (uchar_t));
 724                         if (iocp->ioc_error == 0 && (*mp->b_cont->b_rptr != 0))
 725                                 mp->b_datap->db_type = M_IOCNAK;
 726                 }
 727 
 728                 iocp->ioc_error = 0;
 729                 iocp->ioc_count = 0;
 730                 iocp->ioc_rval = 0;
 731                 qreply(q, mp);
 732                 return;
 733         case IOCTYPE:
 734                 mp->b_datap->db_type = M_IOCACK;
 735                 iocp->ioc_error = 0;
 736                 iocp->ioc_count = 0;
 737                 iocp->ioc_rval = TIOC;
 738                 qreply(q, mp);
 739                 return;
 740         case TIOCEXCL:
 741                 /* check for binary value of XCLUDE flag ???? */
 742                 tp->t_new_lflags |= XCLUDE;
 743                 mp->b_datap->db_type = M_IOCACK;
 744                 iocp->ioc_error = 0;
 745                 iocp->ioc_count = 0;
 746                 iocp->ioc_rval = 0;
 747                 qreply(q, mp);
 748                 return;
 749         case TIOCNXCL:
 750                 tp->t_new_lflags &= ~XCLUDE;
 751                 mp->b_datap->db_type = M_IOCACK;
 752                 iocp->ioc_error = 0;
 753                 iocp->ioc_count = 0;
 754                 iocp->ioc_rval = 0;
 755                 qreply(q, mp);
 756                 return;
 757         }
 758 
 759         /*
 760          * We don't reply to most calls, we just pass them down,
 761          * possibly after modifying the arguments.
 762          */
 763         putnext(q, mp);
 764         return;
 765 
 766 allocfailure:
 767         /*
 768          * We needed to allocate something to handle this "ioctl", but
 769          * couldn't; save this "ioctl" and arrange to get called back when
 770          * it's more likely that we can get what we need.
 771          * If there's already one being saved, throw it out, since it
 772          * must have timed out.
 773          */
 774         if (tp->t_iocpending != NULL)
 775                 freemsg(tp->t_iocpending);
 776         tp->t_iocpending = mp;       /* hold this ioctl */
 777         if (tp->t_bufcallid != 0)
 778                 qunbufcall(q, tp->t_bufcallid);
 779 
 780         tp->t_bufcallid = qbufcall(q, sizeof (struct iocblk), BPRI_HI,
 781             ttcompat_reioctl, q);
 782 }
 783 
 784 /*
 785  * Called when an M_IOCACK message is seen on the read queue; if this
 786  * is the response we were waiting for, we either:
 787  *    modify the data going up (if the "ioctl" read data); since in all
 788  *    cases, the old-style returned information is smaller than or the same
 789  *    size as the new-style returned information, we just overwrite the old
 790  *    stuff with the new stuff (beware of changing structure sizes, in case
 791  *    you invalidate this)
 792  * or
 793  *    take this data, modify it appropriately, and send it back down (if
 794  *    the "ioctl" wrote data).
 795  * In either case, we cancel the "wait"; the final response to a "write"
 796  * ioctl goes back up to the user.
 797  * If this wasn't the response we were waiting for, just pass it up.
 798  */
 799 static void
 800 ttcompat_ioctl_ack(queue_t *q,  mblk_t *mp)
 801 {
 802         ttcompat_state_t *tp;
 803         struct iocblk *iocp;
 804         mblk_t *datap;
 805 
 806         tp = (ttcompat_state_t *)q->q_ptr;
 807         iocp = (struct iocblk *)mp->b_rptr;
 808 
 809         if (!(tp->t_state&TS_IOCWAIT) || iocp->ioc_id != tp->t_iocid) {
 810                 /*
 811                  * This isn't the reply we're looking for.  Move along.
 812                  */
 813                 putnext(q, mp);
 814                 return;
 815         }
 816 
 817         datap = mp->b_cont;  /* mblk containing data going up */
 818 
 819         switch (tp->t_ioccmd) {
 820 
 821         case TIOCGETP: {
 822                 struct sgttyb *cb;
 823 
 824                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 825                 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
 826                         /* recycle the reply's buffer */
 827                 cb = (struct sgttyb *)datap->b_wptr;
 828                 /*
 829                  * This is used for TIOCGETP handling of sg_ispeed and
 830                  * sg_ospeed.  If the current speed is over 38400 (the
 831                  * sgttyb limit), then we report 38400.  Note that
 832                  * when "compatibility with old releases" is enabled
 833                  * (sgttyb_handling == 0), then t_[io]speed will have
 834                  * garbled nonsense, as in prior releases.  (See
 835                  * to_compat() below).
 836                  */
 837                 cb->sg_ispeed = tp->t_curstate.t_ispeed > B38400 ? B38400 :
 838                     tp->t_curstate.t_ispeed;
 839                 cb->sg_ospeed = tp->t_curstate.t_ospeed > B38400 ? B38400 :
 840                     tp->t_curstate.t_ospeed;
 841                 cb->sg_erase = tp->t_curstate.t_erase;
 842                 cb->sg_kill = tp->t_curstate.t_kill;
 843                 cb->sg_flags = tp->t_curstate.t_flags;
 844                 datap->b_wptr += sizeof (struct sgttyb);
 845                 iocp->ioc_count = sizeof (struct sgttyb);
 846 
 847                 /* you are lucky - stream head knows how to copy you out */
 848 
 849                 tp->t_state &= ~TS_IOCWAIT;      /* we got what we wanted */
 850                 iocp->ioc_rval = 0;
 851                 iocp->ioc_cmd =  tp->t_ioccmd;
 852                 putnext(q, mp);
 853                 return;
 854         }
 855 
 856         case TIOCGETC:
 857                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 858                 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
 859                         /* recycle the reply's buffer */
 860                 bcopy(&tp->t_curstate.t_intrc, datap->b_wptr,
 861                     sizeof (struct tchars));
 862                 datap->b_wptr += sizeof (struct tchars);
 863                 break;
 864 
 865         case TIOCGLTC:
 866                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 867                 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
 868                         /* recycle the reply's buffer */
 869                 bcopy(&tp->t_curstate.t_suspc, datap->b_wptr,
 870                     sizeof (struct ltchars));
 871                 datap->b_wptr += sizeof (struct ltchars);
 872                 break;
 873 
 874         case TIOCLGET:
 875                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 876                 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
 877                         /* recycle the reply's buffer */
 878                 *(int *)datap->b_wptr =
 879                     ((unsigned)tp->t_curstate.t_flags) >> 16;
 880                 datap->b_wptr += sizeof (int);
 881                 break;
 882 
 883         case TIOCSETP:
 884         case TIOCSETN:
 885                 /*
 886                  * Get the current state from the GETS data, and
 887                  * update it.
 888                  */
 889                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 890                 tp->t_curstate.t_erase = tp->t_new_sgttyb.sg_erase;
 891                 tp->t_curstate.t_kill = tp->t_new_sgttyb.sg_kill;
 892                 /*
 893                  * For new-style handling, we ignore requests to set
 894                  * B38400 when the current speed is over B38400.  This
 895                  * means that we change the speed as requested if:
 896                  *      old style (sgttyb_handling == 0) is requested
 897                  *      the requested new speed isn't B38400
 898                  *      the current speed is at or below B38400
 899                  * Note that when old style is requested, both speeds
 900                  * in t_curstate are set to <= B38400 by to_compat, so
 901                  * the first test isn't needed here.
 902                  * Also note that we silently allow the user to set
 903                  * speeds above B38400 through this interface,
 904                  * regardless of the style setting.  This allows
 905                  * greater compatibility with current BSD releases.
 906                  */
 907                 if (tp->t_new_sgttyb.sg_ispeed != B38400 ||
 908                     tp->t_curstate.t_ispeed <= B38400)
 909                         tp->t_curstate.t_ispeed = tp->t_new_sgttyb.sg_ispeed;
 910                 if (tp->t_new_sgttyb.sg_ospeed != B38400 ||
 911                     tp->t_curstate.t_ospeed <= B38400)
 912                         tp->t_curstate.t_ospeed = tp->t_new_sgttyb.sg_ospeed;
 913                 tp->t_curstate.t_flags =
 914                     (tp->t_curstate.t_flags & 0xffff0000) |
 915                     (tp->t_new_sgttyb.sg_flags & 0xffff);
 916 
 917                 /*
 918                  * Replace the data that came up with the updated data.
 919                  */
 920                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
 921 
 922                 /*
 923                  * Send it back down as a TCSETS or TCSETSF.
 924                  */
 925                 iocp->ioc_cmd = (tp->t_ioccmd == TIOCSETP) ? TCSETSF : TCSETS;
 926                 goto senddown;
 927 
 928         case TIOCSETC:
 929                 /*
 930                  * Get the current state from the GETS data, and
 931                  * update it.
 932                  */
 933                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 934                 bcopy(&tp->t_new_tchars,
 935                     &tp->t_curstate.t_intrc, sizeof (struct tchars));
 936 
 937                 /*
 938                  * Replace the data that came up with the updated data.
 939                  */
 940                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
 941 
 942                 /*
 943                  * Send it back down as a TCSETS.
 944                  */
 945                 iocp->ioc_cmd = TCSETS;
 946                 goto senddown;
 947 
 948         case TIOCSLTC:
 949                 /*
 950                  * Get the current state from the GETS data, and
 951                  * update it.
 952                  */
 953                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 954                 bcopy(&tp->t_new_ltchars,
 955                     &tp->t_curstate.t_suspc, sizeof (struct ltchars));
 956 
 957                 /*
 958                  * Replace the data that came up with the updated data.
 959                  */
 960                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
 961 
 962                 /*
 963                  * Send it back down as a TCSETS.
 964                  */
 965                 iocp->ioc_cmd = TCSETS;
 966                 goto senddown;
 967 
 968         case TIOCLBIS:
 969                 /*
 970                  * Get the current state from the GETS data, and
 971                  * update it.
 972                  */
 973                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 974                 tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
 975 
 976                 /*
 977                  * Replace the data that came up with the updated data.
 978                  */
 979                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
 980 
 981                 /*
 982                  * Send it back down as a TCSETS.
 983                  */
 984                 iocp->ioc_cmd = TCSETS;
 985                 goto senddown;
 986 
 987         case TIOCLBIC:
 988                 /*
 989                  * Get the current state from the GETS data, and
 990                  * update it.
 991                  */
 992                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
 993                 tp->t_curstate.t_flags &= ~(tp->t_new_lflags << 16);
 994 
 995                 /*
 996                  * Replace the data that came up with the updated data.
 997                  */
 998                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
 999 
1000                 /*
1001                  * Send it back down as a TCSETS.
1002                  */
1003                 iocp->ioc_cmd = TCSETS;
1004                 goto senddown;
1005 
1006         case TIOCLSET:
1007                 /*
1008                  * Get the current state from the GETS data, and
1009                  * update it.
1010                  */
1011                 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
1012                 tp->t_curstate.t_flags &= 0xffff;
1013                 tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
1014 
1015                 /*
1016                  * Replace the data that came up with the updated data.
1017                  */
1018                 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
1019 
1020                 /*
1021                  * Send it back down as a TCSETS.
1022                  */
1023                 iocp->ioc_cmd = TCSETS;
1024                 goto senddown;
1025 
1026         case TIOCHPCL:
1027                 /*
1028                  * Replace the data that came up with the updated data.
1029                  */
1030                 ((struct termios *)datap->b_rptr)->c_cflag |= HUPCL;
1031 
1032                 /*
1033                  * Send it back down as a TCSETS.
1034                  */
1035                 iocp->ioc_cmd = TCSETS;
1036                 goto senddown;
1037 
1038         case TCSETSF:
1039                 /*
1040                  * We're acknowledging the terminal reset ioctl that we sent
1041                  * when the module was opened.
1042                  */
1043                 tp->t_state &= ~(TS_IOCWAIT | TS_TIOCNAK);
1044                 freemsg(mp);
1045                 return;
1046 
1047         default:
1048                 cmn_err(CE_WARN, "ttcompat: Unexpected ioctl acknowledgment\n");
1049         }
1050 
1051         /*
1052          * All the calls that return something return 0.
1053          */
1054         tp->t_state &= ~TS_IOCWAIT;      /* we got what we wanted */
1055         iocp->ioc_rval = 0;
1056 
1057         /* copy out the data - ioctl transparency */
1058         iocp->ioc_cmd =  tp->t_ioccmd;
1059         ttcopyout(q, mp);
1060         return;
1061 
1062 senddown:
1063         /*
1064          * Send a "get state" reply back down, with suitably-modified
1065          * state, as a "set state" "ioctl".
1066          */
1067         tp->t_state &= ~TS_IOCWAIT;
1068         mp->b_datap->db_type = M_IOCTL;
1069         mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
1070         putnext(WR(q), mp);
1071 }
1072 /* Called from ttcompatrput M_IOCACK processing. */
1073 /* Copies out the data using M_COPYOUT messages */
1074 
1075 static void
1076 ttcopyout(queue_t *q, mblk_t *mp)
1077 {
1078         struct copyreq *cqp;
1079         ttcompat_state_t *tp;
1080 
1081         tp = (ttcompat_state_t *)q->q_ptr;
1082 
1083         mp->b_datap->db_type = M_COPYOUT;
1084         cqp = (struct copyreq *)mp->b_rptr;
1085         cqp->cq_addr = (caddr_t)tp->t_arg; /* retrieve the 3rd argument */
1086         tp->t_arg = 0; /* clear it since we don't need it anymore */
1087         switch (tp->t_ioccmd) {
1088                 case TIOCGLTC:
1089                         cqp->cq_size = sizeof (struct ltchars);
1090                         break;
1091                 case TIOCGETC:
1092                         cqp->cq_size = sizeof (struct tchars);
1093                         break;
1094                 case TIOCLGET:
1095                         cqp->cq_size = sizeof (int);
1096                         break;
1097                 default:
1098                         cmn_err(CE_WARN,
1099                             "ttcompat: Unknown ioctl to copyout\n");
1100                         break;
1101                 }
1102         cqp->cq_flag = 0;
1103         cqp->cq_private = NULL;
1104         tp->t_state |= TS_W_OUT;
1105         putnext(q, mp);
1106 }
1107 
1108 
1109 /*
1110  * Called when an M_IOCNAK message is seen on the read queue; if this is
1111  * the response we were waiting for, cancel the wait.  Pass the reply up;
1112  * if we were waiting for this response, we can't complete the "ioctl" and
1113  * the NAK will tell that to the guy above us.
1114  * If this wasn't the response we were waiting for, just pass it up.
1115  */
1116 static void
1117 ttcompat_ioctl_nak(queue_t *q, mblk_t *mp)
1118 {
1119         ttcompat_state_t *tp;
1120         struct iocblk *iocp;
1121 
1122         iocp = (struct iocblk *)mp->b_rptr;
1123         tp = (ttcompat_state_t *)q->q_ptr;
1124 
1125         if (tp->t_state&TS_IOCWAIT && iocp->ioc_id == tp->t_iocid) {
1126                 tp->t_state &= ~TS_IOCWAIT; /* this call isn't going through */
1127                 tp->t_arg = 0;       /* we may have stashed the 3rd argument */
1128         }
1129         putnext(q, mp);
1130 }
1131 
1132 #define FROM_COMPAT_CHAR(to, from) { if ((to = from) == 0377) to = 0; }
1133 
1134 static void
1135 from_compat(compat_state_t *csp, struct termios *termiosp)
1136 {
1137         termiosp->c_iflag = 0;
1138         termiosp->c_oflag &= (ONLRET|ONOCR);
1139 
1140         termiosp->c_cflag = (termiosp->c_cflag &
1141             (CRTSCTS|CRTSXOFF|PAREXT|LOBLK|HUPCL)) | CREAD;
1142 
1143         if (csp->t_ospeed > CBAUD) {
1144                 termiosp->c_cflag |= ((csp->t_ospeed - CBAUD - 1) & CBAUD) |
1145                     CBAUDEXT;
1146         } else {
1147                 termiosp->c_cflag |= csp->t_ospeed & CBAUD;
1148         }
1149 
1150         if (csp->t_ospeed != csp->t_ispeed) {
1151                 if (csp->t_ispeed > (CIBAUD >> IBSHIFT)) {
1152                         termiosp->c_cflag |= CIBAUDEXT |
1153                             (((csp->t_ispeed - (CIBAUD >> IBSHIFT) - 1) <<
1154                             IBSHIFT) & CIBAUD);
1155                 } else {
1156                         termiosp->c_cflag |= (csp->t_ispeed << IBSHIFT) &
1157                             CIBAUD;
1158                 }
1159                 /* hang up if ispeed=0 */
1160                 if (csp->t_ispeed == 0)
1161                         termiosp->c_cflag &= ~CBAUD & ~CBAUDEXT;
1162         }
1163         if (csp->t_ispeed == B110 || csp->t_xflags & STOPB)
1164                 termiosp->c_cflag |= CSTOPB;
1165         termiosp->c_lflag = ECHOK;
1166         FROM_COMPAT_CHAR(termiosp->c_cc[VERASE], csp->t_erase);
1167         FROM_COMPAT_CHAR(termiosp->c_cc[VKILL], csp->t_kill);
1168         FROM_COMPAT_CHAR(termiosp->c_cc[VINTR], csp->t_intrc);
1169         FROM_COMPAT_CHAR(termiosp->c_cc[VQUIT], csp->t_quitc);
1170         FROM_COMPAT_CHAR(termiosp->c_cc[VSTART], csp->t_startc);
1171         FROM_COMPAT_CHAR(termiosp->c_cc[VSTOP], csp->t_stopc);
1172         termiosp->c_cc[VEOL2] = 0;
1173         FROM_COMPAT_CHAR(termiosp->c_cc[VSUSP], csp->t_suspc);
1174         /* is this useful? */
1175         FROM_COMPAT_CHAR(termiosp->c_cc[VDSUSP], csp->t_dsuspc);
1176         FROM_COMPAT_CHAR(termiosp->c_cc[VREPRINT], csp->t_rprntc);
1177         FROM_COMPAT_CHAR(termiosp->c_cc[VDISCARD], csp->t_flushc);
1178         FROM_COMPAT_CHAR(termiosp->c_cc[VWERASE], csp->t_werasc);
1179         FROM_COMPAT_CHAR(termiosp->c_cc[VLNEXT], csp->t_lnextc);
1180         termiosp->c_cc[VSTATUS] = 0;
1181         if (csp->t_flags & O_TANDEM)
1182                 termiosp->c_iflag |= IXOFF;
1183         if (csp->t_flags & O_LCASE) {
1184                 termiosp->c_iflag |= IUCLC;
1185                 termiosp->c_oflag |= OLCUC;
1186                 termiosp->c_lflag |= XCASE;
1187         }
1188         if (csp->t_flags & O_ECHO)
1189                 termiosp->c_lflag |= ECHO;
1190         if (csp->t_flags & O_CRMOD) {
1191                 termiosp->c_iflag |= ICRNL;
1192                 termiosp->c_oflag |= ONLCR;
1193                 switch (csp->t_flags & O_CRDELAY) {
1194 
1195                 case O_CR1:
1196                         termiosp->c_oflag |= CR2;
1197                         break;
1198 
1199                 case O_CR2:
1200                         termiosp->c_oflag |= CR3;
1201                         break;
1202                 }
1203         } else {
1204                 if ((csp->t_flags & O_NLDELAY) == O_NL1)
1205                         termiosp->c_oflag |= ONLRET|CR1;     /* tty37 */
1206         }
1207         if ((csp->t_flags & O_NLDELAY) == O_NL2)
1208                 termiosp->c_oflag |= NL1;
1209         /*
1210          * When going into RAW mode, the special characters controlled by the
1211          * POSIX IEXTEN bit no longer apply; when leaving, they do.
1212          */
1213         if (csp->t_flags & O_RAW) {
1214                 termiosp->c_cflag |= CS8;
1215                 termiosp->c_iflag &= ~(ICRNL|IUCLC);
1216                 termiosp->c_lflag &= ~(XCASE|IEXTEN);
1217         } else {
1218                 termiosp->c_iflag |= IMAXBEL|BRKINT|IGNPAR;
1219                 if (termiosp->c_cc[VSTOP] != 0 && termiosp->c_cc[VSTART] != 0)
1220                         termiosp->c_iflag |= IXON;
1221                 if (csp->t_flags & O_LITOUT)
1222                         termiosp->c_cflag |= CS8;
1223                 else {
1224                         if (csp->t_flags & O_PASS8)
1225                                 termiosp->c_cflag |= CS8;
1226                                 /* XXX - what about 8 bits plus parity? */
1227                         else {
1228                                 switch (csp->t_flags & (O_EVENP|O_ODDP)) {
1229 
1230                                 case 0:
1231                                         termiosp->c_iflag |= ISTRIP;
1232                                         termiosp->c_cflag |= CS8;
1233                                         break;
1234 
1235                                 case O_EVENP:
1236                                         termiosp->c_iflag |= INPCK|ISTRIP;
1237                                         termiosp->c_cflag |= CS7|PARENB;
1238                                         break;
1239 
1240                                 case O_ODDP:
1241                                         termiosp->c_iflag |= INPCK|ISTRIP;
1242                                         termiosp->c_cflag |= CS7|PARENB|PARODD;
1243                                         break;
1244 
1245                                 case O_EVENP|O_ODDP:
1246                                         termiosp->c_iflag |= ISTRIP;
1247                                         termiosp->c_cflag |= CS7|PARENB;
1248                                         break;
1249                                 }
1250                         }
1251                         if (!(csp->t_xflags & NOPOST))
1252                                 termiosp->c_oflag |= OPOST;
1253                 }
1254                 termiosp->c_lflag |= IEXTEN;
1255                 if (!(csp->t_xflags & NOISIG))
1256                         termiosp->c_lflag |= ISIG;
1257                 if (!(csp->t_flags & O_CBREAK))
1258                         termiosp->c_lflag |= ICANON;
1259                 if (csp->t_flags & O_CTLECH)
1260                         termiosp->c_lflag |= ECHOCTL;
1261         }
1262         switch (csp->t_flags & O_TBDELAY) {
1263 
1264         case O_TAB1:
1265                 termiosp->c_oflag |= TAB1;
1266                 break;
1267 
1268         case O_TAB2:
1269                 termiosp->c_oflag |= TAB2;
1270                 break;
1271 
1272         case O_XTABS:
1273                 termiosp->c_oflag |= TAB3;
1274                 break;
1275         }
1276         if (csp->t_flags & O_VTDELAY)
1277                 termiosp->c_oflag |= FFDLY;
1278         if (csp->t_flags & O_BSDELAY)
1279                 termiosp->c_oflag |= BSDLY;
1280         if (csp->t_flags & O_PRTERA)
1281                 termiosp->c_lflag |= ECHOPRT;
1282         if (csp->t_flags & O_CRTERA)
1283                 termiosp->c_lflag |= ECHOE;
1284         if (csp->t_flags & O_TOSTOP)
1285                 termiosp->c_lflag |= TOSTOP;
1286         if (csp->t_flags & O_FLUSHO)
1287                 termiosp->c_lflag |= FLUSHO;
1288         if (csp->t_flags & O_NOHANG)
1289                 termiosp->c_cflag |= CLOCAL;
1290         if (csp->t_flags & O_CRTKIL)
1291                 termiosp->c_lflag |= ECHOKE;
1292         if (csp->t_flags & O_PENDIN)
1293                 termiosp->c_lflag |= PENDIN;
1294         if (!(csp->t_flags & O_DECCTQ))
1295                 termiosp->c_iflag |= IXANY;
1296         if (csp->t_flags & O_NOFLSH)
1297                 termiosp->c_lflag |= NOFLSH;
1298         if (termiosp->c_lflag & ICANON) {
1299                 FROM_COMPAT_CHAR(termiosp->c_cc[VEOF], csp->t_eofc);
1300                 FROM_COMPAT_CHAR(termiosp->c_cc[VEOL], csp->t_brkc);
1301         } else {
1302                 termiosp->c_cc[VMIN] = 1;
1303                 termiosp->c_cc[VTIME] = 0;
1304         }
1305 }
1306 
1307 #define TO_COMPAT_CHAR(to, from) { if ((to = from) == 0) to = (uchar_t)0377; }
1308 
1309 static void
1310 to_compat(struct termios *termiosp, compat_state_t *csp)
1311 {
1312         csp->t_xflags &= (NOISIG|NOPOST);
1313         csp->t_ospeed = termiosp->c_cflag & CBAUD;
1314         csp->t_ispeed = (termiosp->c_cflag & CIBAUD) >> IBSHIFT;
1315         if (sgttyb_handling > 0) {
1316                 if (termiosp->c_cflag & CBAUDEXT)
1317                         csp->t_ospeed += CBAUD + 1;
1318                 if (termiosp->c_cflag & CIBAUDEXT)
1319                         csp->t_ispeed += (CIBAUD >> IBSHIFT) + 1;
1320         }
1321         if (csp->t_ispeed == 0)
1322                 csp->t_ispeed = csp->t_ospeed;
1323         if ((termiosp->c_cflag & CSTOPB) && csp->t_ispeed != B110)
1324                 csp->t_xflags |= STOPB;
1325         TO_COMPAT_CHAR(csp->t_erase, termiosp->c_cc[VERASE]);
1326         TO_COMPAT_CHAR(csp->t_kill, termiosp->c_cc[VKILL]);
1327         TO_COMPAT_CHAR(csp->t_intrc, termiosp->c_cc[VINTR]);
1328         TO_COMPAT_CHAR(csp->t_quitc, termiosp->c_cc[VQUIT]);
1329         TO_COMPAT_CHAR(csp->t_startc, termiosp->c_cc[VSTART]);
1330         TO_COMPAT_CHAR(csp->t_stopc, termiosp->c_cc[VSTOP]);
1331         TO_COMPAT_CHAR(csp->t_suspc, termiosp->c_cc[VSUSP]);
1332         TO_COMPAT_CHAR(csp->t_dsuspc, termiosp->c_cc[VDSUSP]);
1333         TO_COMPAT_CHAR(csp->t_rprntc, termiosp->c_cc[VREPRINT]);
1334         TO_COMPAT_CHAR(csp->t_flushc, termiosp->c_cc[VDISCARD]);
1335         TO_COMPAT_CHAR(csp->t_werasc, termiosp->c_cc[VWERASE]);
1336         TO_COMPAT_CHAR(csp->t_lnextc, termiosp->c_cc[VLNEXT]);
1337         csp->t_flags &= (O_CTLECH|O_LITOUT|O_PASS8|O_ODDP|O_EVENP);
1338         if (termiosp->c_iflag & IXOFF)
1339                 csp->t_flags |= O_TANDEM;
1340         if (!(termiosp->c_iflag &
1341             (IMAXBEL|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|
1342             INLCR|IGNCR|ICRNL|IUCLC|IXON)) &&
1343             !(termiosp->c_oflag & OPOST) &&
1344             (termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1345             !(termiosp->c_lflag & (ISIG|ICANON|XCASE|IEXTEN)))
1346                 csp->t_flags |= O_RAW;
1347         else {
1348                 if (!(termiosp->c_iflag & IXON)) {
1349                         csp->t_startc = (uchar_t)0377;
1350                         csp->t_stopc = (uchar_t)0377;
1351                 }
1352                 if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1353                     !(termiosp->c_oflag & OPOST))
1354                         csp->t_flags |= O_LITOUT;
1355                 else {
1356                         csp->t_flags &= ~O_LITOUT;
1357                         if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8) {
1358                                 if (!(termiosp->c_iflag & ISTRIP))
1359                                         csp->t_flags |= O_PASS8;
1360                         } else {
1361                                 csp->t_flags &= ~(O_ODDP|O_EVENP|O_PASS8);
1362                                 if (termiosp->c_cflag & PARODD)
1363                                         csp->t_flags |= O_ODDP;
1364                                 else if (termiosp->c_iflag & INPCK)
1365                                         csp->t_flags |= O_EVENP;
1366                                 else
1367                                         csp->t_flags |= O_ODDP|O_EVENP;
1368                         }
1369                         if (!(termiosp->c_oflag & OPOST))
1370                                 csp->t_xflags |= NOPOST;
1371                         else
1372                                 csp->t_xflags &= ~NOPOST;
1373                 }
1374                 if (!(termiosp->c_lflag & ISIG))
1375                         csp->t_xflags |= NOISIG;
1376                 else
1377                         csp->t_xflags &= ~NOISIG;
1378                 if (!(termiosp->c_lflag & ICANON))
1379                         csp->t_flags |= O_CBREAK;
1380                 if (termiosp->c_lflag & ECHOCTL)
1381                         csp->t_flags |= O_CTLECH;
1382                 else
1383                         csp->t_flags &= ~O_CTLECH;
1384         }
1385         if (termiosp->c_oflag & OLCUC)
1386                 csp->t_flags |= O_LCASE;
1387         if (termiosp->c_lflag&ECHO)
1388                 csp->t_flags |= O_ECHO;
1389         if (termiosp->c_oflag & ONLCR) {
1390                 csp->t_flags |= O_CRMOD;
1391                 switch (termiosp->c_oflag & CRDLY) {
1392 
1393                 case CR2:
1394                         csp->t_flags |= O_CR1;
1395                         break;
1396 
1397                 case CR3:
1398                         csp->t_flags |= O_CR2;
1399                         break;
1400                 }
1401         } else {
1402                 if ((termiosp->c_oflag & CR1) &&
1403                     (termiosp->c_oflag & ONLRET))
1404                         csp->t_flags |= O_NL1;       /* tty37 */
1405         }
1406         if ((termiosp->c_oflag & ONLRET) && (termiosp->c_oflag & NL1))
1407                 csp->t_flags |= O_NL2;
1408         switch (termiosp->c_oflag & TABDLY) {
1409 
1410         case TAB1:
1411                 csp->t_flags |= O_TAB1;
1412                 break;
1413 
1414         case TAB2:
1415                 csp->t_flags |= O_TAB2;
1416                 break;
1417 
1418         case XTABS:
1419                 csp->t_flags |= O_XTABS;
1420                 break;
1421         }
1422         if (termiosp->c_oflag & FFDLY)
1423                 csp->t_flags |= O_VTDELAY;
1424         if (termiosp->c_oflag & BSDLY)
1425                 csp->t_flags |= O_BSDELAY;
1426         if (termiosp->c_lflag & ECHOPRT)
1427                 csp->t_flags |= O_PRTERA;
1428         if (termiosp->c_lflag & ECHOE)
1429                 csp->t_flags |= (O_CRTERA|O_CRTBS);
1430         if (termiosp->c_lflag & TOSTOP)
1431                 csp->t_flags |= O_TOSTOP;
1432         if (termiosp->c_lflag & FLUSHO)
1433                 csp->t_flags |= O_FLUSHO;
1434         if (termiosp->c_cflag & CLOCAL)
1435                 csp->t_flags |= O_NOHANG;
1436         if (termiosp->c_lflag & ECHOKE)
1437                 csp->t_flags |= O_CRTKIL;
1438         if (termiosp->c_lflag & PENDIN)
1439                 csp->t_flags |= O_PENDIN;
1440         if (!(termiosp->c_iflag & IXANY))
1441                 csp->t_flags |= O_DECCTQ;
1442         if (termiosp->c_lflag & NOFLSH)
1443                 csp->t_flags |= O_NOFLSH;
1444         if (termiosp->c_lflag & ICANON) {
1445                 TO_COMPAT_CHAR(csp->t_eofc, termiosp->c_cc[VEOF]);
1446                 TO_COMPAT_CHAR(csp->t_brkc, termiosp->c_cc[VEOL]);
1447         } else {
1448                 termiosp->c_cc[VMIN] = 1;
1449                 termiosp->c_cc[VTIME] = 0;
1450         }
1451 }