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