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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  23 /*        All Rights Reserved   */
  24 
  25 
  26 /*
  27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  28  * Use is subject to license terms.
  29  */
  30 
  31 /*
  32  * Description:
  33  *
  34  * The PTEM streams module is used as a pseudo driver emulator.  Its purpose
  35  * is to emulate the ioctl() functions of a terminal device driver.
  36  */
  37 
  38 #include <sys/types.h>
  39 #include <sys/param.h>
  40 #include <sys/stream.h>
  41 #include <sys/stropts.h>
  42 #include <sys/strsun.h>
  43 #include <sys/termio.h>
  44 #include <sys/pcb.h>
  45 #include <sys/signal.h>
  46 #include <sys/cred.h>
  47 #include <sys/strtty.h>
  48 #include <sys/errno.h>
  49 #include <sys/cmn_err.h>
  50 #include <sys/jioctl.h>
  51 #include <sys/ptem.h>
  52 #include <sys/ptms.h>
  53 #include <sys/debug.h>
  54 #include <sys/kmem.h>
  55 #include <sys/ddi.h>
  56 #include <sys/sunddi.h>
  57 #include <sys/conf.h>
  58 #include <sys/modctl.h>
  59 
  60 extern struct streamtab pteminfo;
  61 
  62 static struct fmodsw fsw = {
  63         "ptem",
  64         &pteminfo,
  65         D_MTQPAIR | D_MP
  66 };
  67 
  68 static struct modlstrmod modlstrmod = {
  69         &mod_strmodops, "pty hardware emulator", &fsw
  70 };
  71 
  72 static struct modlinkage modlinkage = {
  73         MODREV_1, { &modlstrmod, NULL }
  74 };
  75 
  76 int
  77 _init()
  78 {
  79         return (mod_install(&modlinkage));
  80 }
  81 
  82 int
  83 _fini()
  84 {
  85         return (mod_remove(&modlinkage));
  86 }
  87 
  88 int
  89 _info(struct modinfo *modinfop)
  90 {
  91         return (mod_info(&modlinkage, modinfop));
  92 }
  93 
  94 /*
  95  * stream data structure definitions
  96  */
  97 static int ptemopen(queue_t *, dev_t  *, int, int, cred_t *);
  98 static int ptemclose(queue_t *, int, cred_t *);
  99 static void ptemrput(queue_t *, mblk_t *);
 100 static void ptemwput(queue_t *, mblk_t *);
 101 static void ptemwsrv(queue_t *);
 102 
 103 static struct module_info ptem_info = {
 104         0xabcd,
 105         "ptem",
 106         0,
 107         512,
 108         512,
 109         128
 110 };
 111 
 112 static struct qinit ptemrinit = {
 113         (int (*)()) ptemrput,
 114         NULL,
 115         ptemopen,
 116         ptemclose,
 117         NULL,
 118         &ptem_info,
 119         NULL
 120 };
 121 
 122 static struct qinit ptemwinit = {
 123         (int (*)()) ptemwput,
 124         (int (*)()) ptemwsrv,
 125         ptemopen,
 126         ptemclose,
 127         nulldev,
 128         &ptem_info,
 129         NULL
 130 };
 131 
 132 struct streamtab pteminfo = {
 133         &ptemrinit,
 134         &ptemwinit,
 135         NULL,
 136         NULL
 137 };
 138 
 139 static void     ptioc(queue_t *, mblk_t *, int);
 140 static int      ptemwmsg(queue_t *, mblk_t *);
 141 
 142 /*
 143  * ptemopen - open routine gets called when the module gets pushed onto the
 144  * stream.
 145  */
 146 /* ARGSUSED */
 147 static int
 148 ptemopen(
 149         queue_t    *q,          /* pointer to the read side queue */
 150         dev_t   *devp,          /* pointer to stream tail's dev */
 151         int     oflag,          /* the user open(2) supplied flags */
 152         int     sflag,          /* open state flag */
 153         cred_t *credp)          /* credentials */
 154 {
 155         struct ptem *ntp;       /* ptem entry for this PTEM module */
 156         mblk_t *mop;            /* an setopts mblk */
 157         struct stroptions *sop;
 158         struct termios *termiosp;
 159         int len;
 160 
 161         if (sflag != MODOPEN)
 162                 return (EINVAL);
 163 
 164         if (q->q_ptr != NULL) {
 165                 /* It's already attached. */
 166                 return (0);
 167         }
 168 
 169         /*
 170          * Allocate state structure.
 171          */
 172         ntp = kmem_alloc(sizeof (*ntp), KM_SLEEP);
 173 
 174         /*
 175          * Allocate a message block, used to pass the zero length message for
 176          * "stty 0".
 177          *
 178          * NOTE: it's better to find out if such a message block can be
 179          *       allocated before it's needed than to not be able to
 180          *       deliver (for possible lack of buffers) when a hang-up
 181          *       occurs.
 182          */
 183         if ((ntp->dack_ptr = allocb(4, BPRI_MED)) == NULL) {
 184                 kmem_free(ntp, sizeof (*ntp));
 185                 return (EAGAIN);
 186         }
 187 
 188         /*
 189          * Initialize an M_SETOPTS message to set up hi/lo water marks on
 190          * stream head read queue and add controlling tty if not set.
 191          */
 192         mop = allocb(sizeof (struct stroptions), BPRI_MED);
 193         if (mop == NULL) {
 194                 freemsg(ntp->dack_ptr);
 195                 kmem_free(ntp, sizeof (*ntp));
 196                 return (EAGAIN);
 197         }
 198         mop->b_datap->db_type = M_SETOPTS;
 199         mop->b_wptr += sizeof (struct stroptions);
 200         sop = (struct stroptions *)mop->b_rptr;
 201         sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
 202         sop->so_hiwat = 512;
 203         sop->so_lowat = 256;
 204 
 205         /*
 206          * Cross-link.
 207          */
 208         ntp->q_ptr = q;
 209         q->q_ptr = ntp;
 210         WR(q)->q_ptr = ntp;
 211 
 212         /*
 213          * Get termios defaults.  These are stored as
 214          * a property in the "options" node.
 215          */
 216         if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 0, "ttymodes",
 217             (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
 218             len == sizeof (struct termios)) {
 219 
 220                 ntp->cflags = termiosp->c_cflag;
 221                 kmem_free(termiosp, len);
 222         } else {
 223                 /*
 224                  * Gack!  Whine about it.
 225                  */
 226                 cmn_err(CE_WARN, "ptem: Couldn't get ttymodes property!");
 227         }
 228         ntp->wsz.ws_row = 0;
 229         ntp->wsz.ws_col = 0;
 230         ntp->wsz.ws_xpixel = 0;
 231         ntp->wsz.ws_ypixel = 0;
 232 
 233         ntp->state = 0;
 234 
 235         /*
 236          * Commit to the open and send the M_SETOPTS off to the stream head.
 237          */
 238         qprocson(q);
 239         putnext(q, mop);
 240 
 241         return (0);
 242 }
 243 
 244 
 245 /*
 246  * ptemclose - This routine gets called when the module gets popped off of the
 247  * stream.
 248  */
 249 /* ARGSUSED */
 250 static int
 251 ptemclose(queue_t *q, int flag, cred_t *credp)
 252 {
 253         struct ptem *ntp;       /* ptem entry for this PTEM module */
 254 
 255         qprocsoff(q);
 256         ntp = (struct ptem *)q->q_ptr;
 257         freemsg(ntp->dack_ptr);
 258         kmem_free(ntp, sizeof (*ntp));
 259         q->q_ptr = WR(q)->q_ptr = NULL;
 260         return (0);
 261 }
 262 
 263 
 264 /*
 265  * ptemrput - Module read queue put procedure.
 266  *
 267  * This is called from the module or driver downstream.
 268  */
 269 static void
 270 ptemrput(queue_t *q, mblk_t *mp)
 271 {
 272         struct iocblk *iocp;    /* M_IOCTL data */
 273         struct copyresp *resp;  /* transparent ioctl response struct */
 274         int error;
 275 
 276         switch (mp->b_datap->db_type) {
 277         case M_DELAY:
 278         case M_READ:
 279                 freemsg(mp);
 280                 break;
 281 
 282         case M_IOCTL:
 283                 iocp = (struct iocblk *)mp->b_rptr;
 284 
 285                 switch (iocp->ioc_cmd) {
 286                 case TCSBRK:
 287                         /*
 288                          * Send a break message upstream.
 289                          *
 290                          * XXX: Shouldn't the argument come into play in
 291                          *      determining whether or not so send an M_BREAK?
 292                          *      It certainly does in the write-side direction.
 293                          */
 294                         error = miocpullup(mp, sizeof (int));
 295                         if (error != 0) {
 296                                 miocnak(q, mp, 0, error);
 297                                 break;
 298                         }
 299                         if (!(*(int *)mp->b_cont->b_rptr)) {
 300                                 if (!putnextctl(q, M_BREAK)) {
 301                                         /*
 302                                          * Send an NAK reply back
 303                                          */
 304                                         miocnak(q, mp, 0, EAGAIN);
 305                                         break;
 306                                 }
 307                         }
 308                         /*
 309                          * ACK it.
 310                          */
 311                         mioc2ack(mp, NULL, 0, 0);
 312                         qreply(q, mp);
 313                         break;
 314 
 315                 case JWINSIZE:
 316                 case TIOCGWINSZ:
 317                 case TIOCSWINSZ:
 318                         ptioc(q, mp, RDSIDE);
 319                         break;
 320 
 321                 case TIOCSIGNAL:
 322                         /*
 323                          * The following subtle logic is due to the fact that
 324                          * `mp' may be in any one of three distinct formats:
 325                          *
 326                          *      1. A transparent M_IOCTL with an intptr_t-sized
 327                          *         payload containing the signal number.
 328                          *
 329                          *      2. An I_STR M_IOCTL with an int-sized payload
 330                          *         containing the signal number.
 331                          *
 332                          *      3. An M_IOCDATA with an int-sized payload
 333                          *         containing the signal number.
 334                          */
 335                         if (iocp->ioc_count == TRANSPARENT) {
 336                                 intptr_t sig = *(intptr_t *)mp->b_cont->b_rptr;
 337 
 338                                 if (sig < 1 || sig >= NSIG) {
 339                                         /*
 340                                          * it's transparent with pointer
 341                                          * to the arg
 342                                          */
 343                                         mcopyin(mp, NULL, sizeof (int), NULL);
 344                                         qreply(q, mp);
 345                                         break;
 346                                 }
 347                         }
 348                         ptioc(q, mp, RDSIDE);
 349                         break;
 350 
 351                 case TIOCREMOTE:
 352                         if (iocp->ioc_count != TRANSPARENT)
 353                                 ptioc(q, mp, RDSIDE);
 354                         else {
 355                                 mcopyin(mp, NULL, sizeof (int), NULL);
 356                                 qreply(q, mp);
 357                         }
 358                         break;
 359 
 360                 default:
 361                         putnext(q, mp);
 362                         break;
 363                 }
 364                 break;
 365 
 366         case M_IOCDATA:
 367                 resp = (struct copyresp *)mp->b_rptr;
 368                 if (resp->cp_rval) {
 369                         /*
 370                          * Just free message on failure.
 371                          */
 372                         freemsg(mp);
 373                         break;
 374                 }
 375 
 376                 /*
 377                  * Only need to copy data for the SET case.
 378                  */
 379                 switch (resp->cp_cmd) {
 380 
 381                 case TIOCSWINSZ:
 382                 case TIOCSIGNAL:
 383                 case TIOCREMOTE:
 384                         ptioc(q, mp, RDSIDE);
 385                         break;
 386 
 387                 case JWINSIZE:
 388                 case TIOCGWINSZ:
 389                         mp->b_datap->db_type = M_IOCACK;
 390                         mioc2ack(mp, NULL, 0, 0);
 391                         qreply(q, mp);
 392                         break;
 393 
 394                 default:
 395                         freemsg(mp);
 396                         break;
 397         }
 398         break;
 399 
 400         case M_IOCACK:
 401         case M_IOCNAK:
 402                 /*
 403                  * We only pass write-side ioctls through to the master that
 404                  * we've already ACKed or NAKed to the stream head.  Thus, we
 405                  * discard ones arriving from below, since they're redundant
 406                  * from the point of view of modules above us.
 407                  */
 408                 freemsg(mp);
 409                 break;
 410 
 411         case M_HANGUP:
 412                 /*
 413                  * clear blocked state.
 414                  */
 415                 {
 416                         struct ptem *ntp = (struct ptem *)q->q_ptr;
 417                         if (ntp->state & OFLOW_CTL) {
 418                                 ntp->state &= ~OFLOW_CTL;
 419                                 qenable(WR(q));
 420                         }
 421                 }
 422         default:
 423                 putnext(q, mp);
 424                 break;
 425         }
 426 }
 427 
 428 
 429 /*
 430  * ptemwput - Module write queue put procedure.
 431  *
 432  * This is called from the module or stream head upstream.
 433  *
 434  * XXX: This routine is quite lazy about handling allocation failures,
 435  *      basically just giving up and reporting failure.  It really ought to
 436  *      set up bufcalls and only fail when it's absolutely necessary.
 437  */
 438 static void
 439 ptemwput(queue_t *q, mblk_t *mp)
 440 {
 441         struct ptem *ntp = (struct ptem *)q->q_ptr;
 442         struct iocblk *iocp;    /* outgoing ioctl structure */
 443         struct copyresp *resp;
 444         unsigned char type = mp->b_datap->db_type;
 445 
 446         if (type >= QPCTL) {
 447                 switch (type) {
 448 
 449                 case M_IOCDATA:
 450                         resp = (struct copyresp *)mp->b_rptr;
 451                         if (resp->cp_rval) {
 452                                 /*
 453                                  * Just free message on failure.
 454                                  */
 455                                 freemsg(mp);
 456                                 break;
 457                         }
 458 
 459                         /*
 460                          * Only need to copy data for the SET case.
 461                          */
 462                         switch (resp->cp_cmd) {
 463 
 464                                 case TIOCSWINSZ:
 465                                         ptioc(q, mp, WRSIDE);
 466                                         break;
 467 
 468                                 case JWINSIZE:
 469                                 case TIOCGWINSZ:
 470                                         mioc2ack(mp, NULL, 0, 0);
 471                                         qreply(q, mp);
 472                                         break;
 473 
 474                                 default:
 475                                         freemsg(mp);
 476                         }
 477                         break;
 478 
 479                 case M_FLUSH:
 480                         if (*mp->b_rptr & FLUSHW) {
 481                             if ((ntp->state & IS_PTSTTY) &&
 482                                         (*mp->b_rptr & FLUSHBAND))
 483                                 flushband(q, *(mp->b_rptr + 1), FLUSHDATA);
 484                             else
 485                                 flushq(q, FLUSHDATA);
 486                         }
 487                         putnext(q, mp);
 488                         break;
 489 
 490                 case M_READ:
 491                         freemsg(mp);
 492                         break;
 493 
 494                 case M_STOP:
 495                         /*
 496                          * Set the output flow control state.
 497                          */
 498                         ntp->state |= OFLOW_CTL;
 499                         putnext(q, mp);
 500                         break;
 501 
 502                 case M_START:
 503                         /*
 504                          * Relieve the output flow control state.
 505                          */
 506                         ntp->state &= ~OFLOW_CTL;
 507                         putnext(q, mp);
 508                         qenable(q);
 509                         break;
 510                 default:
 511                         putnext(q, mp);
 512                         break;
 513                 }
 514                 return;
 515         }
 516         /*
 517          * If our queue is nonempty or flow control persists
 518          * downstream or module in stopped state, queue this message.
 519          */
 520         if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
 521                 /*
 522                  * Exception: ioctls, except for those defined to
 523                  * take effect after output has drained, should be
 524                  * processed immediately.
 525                  */
 526                 switch (type) {
 527 
 528                 case M_IOCTL:
 529                         iocp = (struct iocblk *)mp->b_rptr;
 530                         switch (iocp->ioc_cmd) {
 531                         /*
 532                          * Queue these.
 533                          */
 534                         case TCSETSW:
 535                         case TCSETSF:
 536                         case TCSETAW:
 537                         case TCSETAF:
 538                         case TCSBRK:
 539                                 break;
 540 
 541                         /*
 542                          * Handle all others immediately.
 543                          */
 544                         default:
 545                                 (void) ptemwmsg(q, mp);
 546                                 return;
 547                         }
 548                         break;
 549 
 550                 case M_DELAY: /* tty delays not supported */
 551                         freemsg(mp);
 552                         return;
 553 
 554                 case M_DATA:
 555                         if ((mp->b_wptr - mp->b_rptr) < 0) {
 556                                 /*
 557                                  * Free all bad length messages.
 558                                  */
 559                                 freemsg(mp);
 560                                 return;
 561                         } else if ((mp->b_wptr - mp->b_rptr) == 0) {
 562                                 if (!(ntp->state & IS_PTSTTY)) {
 563                                         freemsg(mp);
 564                                         return;
 565                                 }
 566                         }
 567                 }
 568                 (void) putq(q, mp);
 569                 return;
 570         }
 571         /*
 572          * fast path into ptemwmsg to dispose of mp.
 573          */
 574         if (!ptemwmsg(q, mp))
 575                 (void) putq(q, mp);
 576 }
 577 
 578 /*
 579  * ptem write queue service procedure.
 580  */
 581 static void
 582 ptemwsrv(queue_t *q)
 583 {
 584         mblk_t *mp;
 585 
 586         while ((mp = getq(q)) != NULL) {
 587                 if (!bcanputnext(q, mp->b_band) || !ptemwmsg(q, mp)) {
 588                         (void) putbq(q, mp);
 589                         break;
 590                 }
 591         }
 592 }
 593 
 594 
 595 /*
 596  * This routine is called from both ptemwput and ptemwsrv to do the
 597  * actual work of dealing with mp.  ptmewput will have already
 598  * dealt with high priority messages.
 599  *
 600  * Return 1 if the message was processed completely and 0 if not.
 601  */
 602 static int
 603 ptemwmsg(queue_t *q, mblk_t *mp)
 604 {
 605         struct ptem *ntp = (struct ptem *)q->q_ptr;
 606         struct iocblk *iocp;    /* outgoing ioctl structure */
 607         struct termio *termiop;
 608         struct termios *termiosp;
 609         mblk_t *dack_ptr;               /* disconnect message ACK block */
 610         mblk_t *pckt_msgp;              /* message sent to the PCKT module */
 611         mblk_t *dp;                     /* ioctl reply data */
 612         tcflag_t cflags;
 613         int error;
 614 
 615         switch (mp->b_datap->db_type) {
 616 
 617         case M_IOCTL:
 618                 /*
 619                  * Note:  for each "set" type operation a copy
 620                  * of the M_IOCTL message is made and passed
 621                  * downstream.  Eventually the PCKT module, if
 622                  * it has been pushed, should pick up this message.
 623                  * If the PCKT module has not been pushed the master
 624                  * side stream head will free it.
 625                  */
 626                 iocp = (struct iocblk *)mp->b_rptr;
 627                 switch (iocp->ioc_cmd) {
 628 
 629                 case TCSETAF:
 630                 case TCSETSF:
 631                         /*
 632                          * Flush the read queue.
 633                          */
 634                         if (putnextctl1(q, M_FLUSH, FLUSHR) == 0) {
 635                                 miocnak(q, mp, 0, EAGAIN);
 636                                 break;
 637                         }
 638                         /* FALLTHROUGH */
 639 
 640                 case TCSETA:
 641                 case TCSETAW:
 642                 case TCSETS:
 643                 case TCSETSW:
 644 
 645                         switch (iocp->ioc_cmd) {
 646                         case TCSETAF:
 647                         case TCSETA:
 648                         case TCSETAW:
 649                                 error = miocpullup(mp, sizeof (struct termio));
 650                                 if (error != 0) {
 651                                         miocnak(q, mp, 0, error);
 652                                         goto out;
 653                                 }
 654                                 cflags = ((struct termio *)
 655                                     mp->b_cont->b_rptr)->c_cflag;
 656                                 ntp->cflags =
 657                                     (ntp->cflags & 0xffff0000 | cflags);
 658                                 break;
 659 
 660                         case TCSETSF:
 661                         case TCSETS:
 662                         case TCSETSW:
 663                                 error = miocpullup(mp, sizeof (struct termios));
 664                                 if (error != 0) {
 665                                         miocnak(q, mp, 0, error);
 666                                         goto out;
 667                                 }
 668                                 cflags = ((struct termios *)
 669                                     mp->b_cont->b_rptr)->c_cflag;
 670                                 ntp->cflags = cflags;
 671                                 break;
 672                         }
 673 
 674                         if ((cflags & CBAUD) == B0) {
 675                                 /*
 676                                  * Hang-up: Send a zero length message.
 677                                  */
 678                                 dack_ptr = ntp->dack_ptr;
 679 
 680                                 if (dack_ptr) {
 681                                         ntp->dack_ptr = NULL;
 682                                         /*
 683                                          * Send a zero length message
 684                                          * downstream.
 685                                          */
 686                                         putnext(q, dack_ptr);
 687                                 }
 688                         } else {
 689                                 /*
 690                                  * Make a copy of this message and pass it on
 691                                  * to the PCKT module.
 692                                  */
 693                                 if ((pckt_msgp = copymsg(mp)) == NULL) {
 694                                         miocnak(q, mp, 0, EAGAIN);
 695                                         break;
 696                                 }
 697                                 putnext(q, pckt_msgp);
 698                         }
 699                         /*
 700                          * Send ACK upstream.
 701                          */
 702                         mioc2ack(mp, NULL, 0, 0);
 703                         qreply(q, mp);
 704 out:
 705                         break;
 706 
 707                 case TCGETA:
 708                         dp = allocb(sizeof (struct termio), BPRI_MED);
 709                         if (dp == NULL) {
 710                                 miocnak(q, mp, 0, EAGAIN);
 711                                 break;
 712                         }
 713                         termiop = (struct termio *)dp->b_rptr;
 714                         termiop->c_cflag = (ushort_t)ntp->cflags;
 715                         mioc2ack(mp, dp, sizeof (struct termio), 0);
 716                         qreply(q, mp);
 717                         break;
 718 
 719                 case TCGETS:
 720                         dp = allocb(sizeof (struct termios), BPRI_MED);
 721                         if (dp == NULL) {
 722                                 miocnak(q, mp, 0, EAGAIN);
 723                                 break;
 724                         }
 725                         termiosp = (struct termios *)dp->b_rptr;
 726                         termiosp->c_cflag = ntp->cflags;
 727                         mioc2ack(mp, dp, sizeof (struct termios), 0);
 728                         qreply(q, mp);
 729                         break;
 730 
 731                 case TCSBRK:
 732                         error = miocpullup(mp, sizeof (int));
 733                         if (error != 0) {
 734                                 miocnak(q, mp, 0, error);
 735                                 break;
 736                         }
 737 
 738                         /*
 739                          * Need a copy of this message to pass it on to
 740                          * the PCKT module.
 741                          */
 742                         if ((pckt_msgp = copymsg(mp)) == NULL) {
 743                                 miocnak(q, mp, 0, EAGAIN);
 744                                 break;
 745                         }
 746                         /*
 747                          * Send a copy of the M_IOCTL to the PCKT module.
 748                          */
 749                         putnext(q, pckt_msgp);
 750 
 751                         /*
 752                          * TCSBRK meaningful if data part of message is 0
 753                          * cf. termio(7).
 754                          */
 755                         if (!(*(int *)mp->b_cont->b_rptr))
 756                                 (void) putnextctl(q, M_BREAK);
 757                         /*
 758                          * ACK the ioctl.
 759                          */
 760                         mioc2ack(mp, NULL, 0, 0);
 761                         qreply(q, mp);
 762                         break;
 763 
 764                 case JWINSIZE:
 765                 case TIOCGWINSZ:
 766                 case TIOCSWINSZ:
 767                         ptioc(q, mp, WRSIDE);
 768                         break;
 769 
 770                 case TIOCSTI:
 771                         /*
 772                          * Simulate typing of a character at the terminal.  In
 773                          * all cases, we acknowledge the ioctl and pass a copy
 774                          * of it along for the PCKT module to encapsulate.  If
 775                          * not in remote mode, we also process the ioctl
 776                          * itself, looping the character given as its argument
 777                          * back around to the read side.
 778                          */
 779 
 780                         /*
 781                          * Need a copy of this message to pass on to the PCKT
 782                          * module.
 783                          */
 784                         if ((pckt_msgp = copymsg(mp)) == NULL) {
 785                                 miocnak(q, mp, 0, EAGAIN);
 786                                 break;
 787                         }
 788                         if ((ntp->state & REMOTEMODE) == 0) {
 789                                 mblk_t *bp;
 790 
 791                                 error = miocpullup(mp, sizeof (char));
 792                                 if (error != 0) {
 793                                         freemsg(pckt_msgp);
 794                                         miocnak(q, mp, 0, error);
 795                                         break;
 796                                 }
 797 
 798                                 /*
 799                                  * The permission checking has already been
 800                                  * done at the stream head, since it has to be
 801                                  * done in the context of the process doing
 802                                  * the call.
 803                                  */
 804                                 if ((bp = allocb(1, BPRI_MED)) == NULL) {
 805                                         freemsg(pckt_msgp);
 806                                         miocnak(q, mp, 0, EAGAIN);
 807                                         break;
 808                                 }
 809                                 /*
 810                                  * XXX: Is EAGAIN really the right response to
 811                                  *      flow control blockage?
 812                                  */
 813                                 if (!bcanputnext(RD(q), mp->b_band)) {
 814                                         freemsg(bp);
 815                                         freemsg(pckt_msgp);
 816                                         miocnak(q, mp, 0, EAGAIN);
 817                                         break;
 818                                 }
 819                                 *bp->b_wptr++ = *mp->b_cont->b_rptr;
 820                                 qreply(q, bp);
 821                         }
 822 
 823                         putnext(q, pckt_msgp);
 824                         mioc2ack(mp, NULL, 0, 0);
 825                         qreply(q, mp);
 826                         break;
 827 
 828                 case PTSSTTY:
 829                         if (ntp->state & IS_PTSTTY) {
 830                                 miocnak(q, mp, 0, EEXIST);
 831                         } else {
 832                                 ntp->state |= IS_PTSTTY;
 833                                 mioc2ack(mp, NULL, 0, 0);
 834                                 qreply(q, mp);
 835                         }
 836                         break;
 837 
 838                 default:
 839                         /*
 840                          * End of the line.  The slave driver doesn't see any
 841                          * ioctls that we don't explicitly pass along to it.
 842                          */
 843                         miocnak(q, mp, 0, EINVAL);
 844                         break;
 845                 }
 846                 break;
 847 
 848         case M_DELAY: /* tty delays not supported */
 849                 freemsg(mp);
 850                 break;
 851 
 852         case M_DATA:
 853                 if ((mp->b_wptr - mp->b_rptr) < 0) {
 854                         /*
 855                          * Free all bad length messages.
 856                          */
 857                         freemsg(mp);
 858                         break;
 859                 } else if ((mp->b_wptr - mp->b_rptr) == 0) {
 860                         if (!(ntp->state & IS_PTSTTY)) {
 861                                 freemsg(mp);
 862                                 break;
 863                         }
 864                 }
 865                 if (ntp->state & OFLOW_CTL)
 866                         return (0);
 867 
 868         default:
 869                 putnext(q, mp);
 870                 break;
 871 
 872         }
 873 
 874         return (1);
 875 }
 876 
 877 /*
 878  * Message must be of type M_IOCTL or M_IOCDATA for this routine to be called.
 879  */
 880 static void
 881 ptioc(queue_t *q, mblk_t *mp, int qside)
 882 {
 883         struct ptem *tp;
 884         struct iocblk *iocp;
 885         struct winsize *wb;
 886         struct jwinsize *jwb;
 887         mblk_t *tmp;
 888         mblk_t *pckt_msgp;      /* message sent to the PCKT module */
 889         int error;
 890 
 891         iocp = (struct iocblk *)mp->b_rptr;
 892         tp = (struct ptem *)q->q_ptr;
 893 
 894         switch (iocp->ioc_cmd) {
 895 
 896         case JWINSIZE:
 897                 /*
 898                  * For compatibility:  If all zeros, NAK the message for dumb
 899                  * terminals.
 900                  */
 901                 if ((tp->wsz.ws_row == 0) && (tp->wsz.ws_col == 0) &&
 902                     (tp->wsz.ws_xpixel == 0) && (tp->wsz.ws_ypixel == 0)) {
 903                         miocnak(q, mp, 0, EINVAL);
 904                         return;
 905                 }
 906 
 907                 tmp = allocb(sizeof (struct jwinsize), BPRI_MED);
 908                 if (tmp == NULL) {
 909                         miocnak(q, mp, 0, EAGAIN);
 910                         return;
 911                 }
 912 
 913                 if (iocp->ioc_count == TRANSPARENT)
 914                         mcopyout(mp, NULL, sizeof (struct jwinsize), NULL, tmp);
 915                 else
 916                         mioc2ack(mp, tmp, sizeof (struct jwinsize), 0);
 917 
 918                 jwb = (struct jwinsize *)mp->b_cont->b_rptr;
 919                 jwb->bytesx = tp->wsz.ws_col;
 920                 jwb->bytesy = tp->wsz.ws_row;
 921                 jwb->bitsx = tp->wsz.ws_xpixel;
 922                 jwb->bitsy = tp->wsz.ws_ypixel;
 923 
 924                 qreply(q, mp);
 925                 return;
 926 
 927         case TIOCGWINSZ:
 928                 /*
 929                  * If all zeros NAK the message for dumb terminals.
 930                  */
 931                 if ((tp->wsz.ws_row == 0) && (tp->wsz.ws_col == 0) &&
 932                     (tp->wsz.ws_xpixel == 0) && (tp->wsz.ws_ypixel == 0)) {
 933                         miocnak(q, mp, 0, EINVAL);
 934                         return;
 935                 }
 936 
 937                 tmp = allocb(sizeof (struct winsize), BPRI_MED);
 938                 if (tmp == NULL) {
 939                         miocnak(q, mp, 0, EAGAIN);
 940                         return;
 941                 }
 942 
 943                 mioc2ack(mp, tmp, sizeof (struct winsize), 0);
 944 
 945                 wb = (struct winsize *)mp->b_cont->b_rptr;
 946                 wb->ws_row = tp->wsz.ws_row;
 947                 wb->ws_col = tp->wsz.ws_col;
 948                 wb->ws_xpixel = tp->wsz.ws_xpixel;
 949                 wb->ws_ypixel = tp->wsz.ws_ypixel;
 950 
 951                 qreply(q, mp);
 952                 return;
 953 
 954         case TIOCSWINSZ:
 955                 error = miocpullup(mp, sizeof (struct winsize));
 956                 if (error != 0) {
 957                         miocnak(q, mp, 0, error);
 958                         return;
 959                 }
 960 
 961                 wb = (struct winsize *)mp->b_cont->b_rptr;
 962                 /*
 963                  * Send a SIGWINCH signal if the row/col information has
 964                  * changed.
 965                  */
 966                 if ((tp->wsz.ws_row != wb->ws_row) ||
 967                     (tp->wsz.ws_col != wb->ws_col) ||
 968                     (tp->wsz.ws_xpixel != wb->ws_xpixel) ||
 969                     (tp->wsz.ws_ypixel != wb->ws_xpixel)) {
 970                         /*
 971                          * SIGWINCH is always sent upstream.
 972                          */
 973                         if (qside == WRSIDE)
 974                                 (void) putnextctl1(RD(q), M_SIG, SIGWINCH);
 975                         else if (qside == RDSIDE)
 976                                 (void) putnextctl1(q, M_SIG, SIGWINCH);
 977                         /*
 978                          * Message may have come in as an M_IOCDATA; pass it
 979                          * to the master side as an M_IOCTL.
 980                          */
 981                         mp->b_datap->db_type = M_IOCTL;
 982                         if (qside == WRSIDE) {
 983                                 /*
 984                                  * Need a copy of this message to pass on to
 985                                  * the PCKT module, only if the M_IOCTL
 986                                  * orginated from the slave side.
 987                                  */
 988                                 if ((pckt_msgp = copymsg(mp)) == NULL) {
 989                                         miocnak(q, mp, 0, EAGAIN);
 990                                         return;
 991                                 }
 992                                 putnext(q, pckt_msgp);
 993                         }
 994                         tp->wsz.ws_row = wb->ws_row;
 995                         tp->wsz.ws_col = wb->ws_col;
 996                         tp->wsz.ws_xpixel = wb->ws_xpixel;
 997                         tp->wsz.ws_ypixel = wb->ws_ypixel;
 998                 }
 999 
1000                 mioc2ack(mp, NULL, 0, 0);
1001                 qreply(q, mp);
1002                 return;
1003 
1004         case TIOCSIGNAL: {
1005                 /*
1006                  * This ioctl can emanate from the master side in remote
1007                  * mode only.
1008                  */
1009                 int     sig;
1010 
1011                 if (DB_TYPE(mp) == M_IOCTL && iocp->ioc_count != TRANSPARENT) {
1012                         error = miocpullup(mp, sizeof (int));
1013                         if (error != 0) {
1014                                 miocnak(q, mp, 0, error);
1015                                 return;
1016                         }
1017                 }
1018 
1019                 if (DB_TYPE(mp) == M_IOCDATA || iocp->ioc_count != TRANSPARENT)
1020                         sig = *(int *)mp->b_cont->b_rptr;
1021                 else
1022                         sig = (int)*(intptr_t *)mp->b_cont->b_rptr;
1023 
1024                 if (sig < 1 || sig >= NSIG) {
1025                         miocnak(q, mp, 0, EINVAL);
1026                         return;
1027                 }
1028 
1029                 /*
1030                  * Send an M_PCSIG message up the slave's read side and
1031                  * respond back to the master with an ACK or NAK as
1032                  * appropriate.
1033                  */
1034                 if (putnextctl1(q, M_PCSIG, sig) == 0) {
1035                         miocnak(q, mp, 0, EAGAIN);
1036                         return;
1037                 }
1038 
1039                 mioc2ack(mp, NULL, 0, 0);
1040                 qreply(q, mp);
1041                 return;
1042             }
1043 
1044         case TIOCREMOTE: {
1045                 int     onoff;
1046                 mblk_t  *mctlp;
1047 
1048                 if (DB_TYPE(mp) == M_IOCTL) {
1049                         error = miocpullup(mp, sizeof (int));
1050                         if (error != 0) {
1051                                 miocnak(q, mp, 0, error);
1052                                 return;
1053                         }
1054                 }
1055 
1056                 onoff = *(int *)mp->b_cont->b_rptr;
1057 
1058                 /*
1059                  * Send M_CTL up using the iocblk format.
1060                  */
1061                 mctlp = mkiocb(onoff ? MC_NO_CANON : MC_DO_CANON);
1062                 if (mctlp == NULL) {
1063                         miocnak(q, mp, 0, EAGAIN);
1064                         return;
1065                 }
1066                 mctlp->b_datap->db_type = M_CTL;
1067                 putnext(q, mctlp);
1068 
1069                 /*
1070                  * ACK the ioctl.
1071                  */
1072                 mioc2ack(mp, NULL, 0, 0);
1073                 qreply(q, mp);
1074 
1075                 /*
1076                  * Record state change.
1077                  */
1078                 if (onoff)
1079                         tp->state |= REMOTEMODE;
1080                 else
1081                         tp->state &= ~REMOTEMODE;
1082                 return;
1083             }
1084 
1085         default:
1086                 putnext(q, mp);
1087                 return;
1088         }
1089 }