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