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 void ptemrput(queue_t *, mblk_t *);
 101 static void ptemwput(queue_t *, mblk_t *);
 102 static void 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         (int (*)()) ptemrput,
 115         NULL,
 116         ptemopen,
 117         ptemclose,
 118         NULL,
 119         &ptem_info,
 120         NULL
 121 };
 122 
 123 static struct qinit ptemwinit = {
 124         (int (*)()) ptemwput,
 125         (int (*)()) 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 void
 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         default:
 424                 putnext(q, mp);
 425                 break;
 426         }
 427 }
 428 
 429 
 430 /*
 431  * ptemwput - Module write queue put procedure.
 432  *
 433  * This is called from the module or stream head upstream.
 434  *
 435  * XXX: This routine is quite lazy about handling allocation failures,
 436  *      basically just giving up and reporting failure.  It really ought to
 437  *      set up bufcalls and only fail when it's absolutely necessary.
 438  */
 439 static void
 440 ptemwput(queue_t *q, mblk_t *mp)
 441 {
 442         struct ptem *ntp = (struct ptem *)q->q_ptr;
 443         struct iocblk *iocp;    /* outgoing ioctl structure */
 444         struct copyresp *resp;
 445         unsigned char type = mp->b_datap->db_type;
 446 
 447         if (type >= QPCTL) {
 448                 switch (type) {
 449 
 450                 case M_IOCDATA:
 451                         resp = (struct copyresp *)mp->b_rptr;
 452                         if (resp->cp_rval) {
 453                                 /*
 454                                  * Just free message on failure.
 455                                  */
 456                                 freemsg(mp);
 457                                 break;
 458                         }
 459 
 460                         /*
 461                          * Only need to copy data for the SET case.
 462                          */
 463                         switch (resp->cp_cmd) {
 464 
 465                                 case TIOCSWINSZ:
 466                                         ptioc(q, mp, WRSIDE);
 467                                         break;
 468 
 469                                 case JWINSIZE:
 470                                 case TIOCGWINSZ:
 471                                         mioc2ack(mp, NULL, 0, 0);
 472                                         qreply(q, mp);
 473                                         break;
 474 
 475                                 default:
 476                                         freemsg(mp);
 477                         }
 478                         break;
 479 
 480                 case M_FLUSH:
 481                         if (*mp->b_rptr & FLUSHW) {
 482                                 if ((ntp->state & IS_PTSTTY) &&
 483                                     (*mp->b_rptr & FLUSHBAND))
 484                                         flushband(q, *(mp->b_rptr + 1),
 485                                             FLUSHDATA);
 486                                 else
 487                                         flushq(q, FLUSHDATA);
 488                         }
 489                         putnext(q, mp);
 490                         break;
 491 
 492                 case M_READ:
 493                         freemsg(mp);
 494                         break;
 495 
 496                 case M_STOP:
 497                         /*
 498                          * Set the output flow control state.
 499                          */
 500                         ntp->state |= OFLOW_CTL;
 501                         putnext(q, mp);
 502                         break;
 503 
 504                 case M_START:
 505                         /*
 506                          * Relieve the output flow control state.
 507                          */
 508                         ntp->state &= ~OFLOW_CTL;
 509                         putnext(q, mp);
 510                         qenable(q);
 511                         break;
 512                 default:
 513                         putnext(q, mp);
 514                         break;
 515                 }
 516                 return;
 517         }
 518         /*
 519          * If our queue is nonempty or flow control persists
 520          * downstream or module in stopped state, queue this message.
 521          */
 522         if (q->q_first != NULL || !bcanputnext(q, mp->b_band)) {
 523                 /*
 524                  * Exception: ioctls, except for those defined to
 525                  * take effect after output has drained, should be
 526                  * processed immediately.
 527                  */
 528                 switch (type) {
 529 
 530                 case M_IOCTL:
 531                         iocp = (struct iocblk *)mp->b_rptr;
 532                         switch (iocp->ioc_cmd) {
 533                         /*
 534                          * Queue these.
 535                          */
 536                         case TCSETSW:
 537                         case TCSETSF:
 538                         case TCSETAW:
 539                         case TCSETAF:
 540                         case TCSBRK:
 541                                 break;
 542 
 543                         /*
 544                          * Handle all others immediately.
 545                          */
 546                         default:
 547                                 (void) ptemwmsg(q, mp);
 548                                 return;
 549                         }
 550                         break;
 551 
 552                 case M_DELAY: /* tty delays not supported */
 553                         freemsg(mp);
 554                         return;
 555 
 556                 case M_DATA:
 557                         if ((mp->b_wptr - mp->b_rptr) < 0) {
 558                                 /*
 559                                  * Free all bad length messages.
 560                                  */
 561                                 freemsg(mp);
 562                                 return;
 563                         } else if ((mp->b_wptr - mp->b_rptr) == 0) {
 564                                 if (!(ntp->state & IS_PTSTTY)) {
 565                                         freemsg(mp);
 566                                         return;
 567                                 }
 568                         }
 569                 }
 570                 (void) putq(q, mp);
 571                 return;
 572         }
 573         /*
 574          * fast path into ptemwmsg to dispose of mp.
 575          */
 576         if (!ptemwmsg(q, mp))
 577                 (void) putq(q, mp);
 578 }
 579 
 580 /*
 581  * ptem write queue service procedure.
 582  */
 583 static void
 584 ptemwsrv(queue_t *q)
 585 {
 586         mblk_t *mp;
 587 
 588         while ((mp = getq(q)) != NULL) {
 589                 if (!bcanputnext(q, mp->b_band) || !ptemwmsg(q, mp)) {
 590                         (void) putbq(q, mp);
 591                         break;
 592                 }
 593         }
 594 }
 595 
 596 
 597 /*
 598  * This routine is called from both ptemwput and ptemwsrv to do the
 599  * actual work of dealing with mp.  ptmewput will have already
 600  * dealt with high priority messages.
 601  *
 602  * Return 1 if the message was processed completely and 0 if not.
 603  */
 604 static int
 605 ptemwmsg(queue_t *q, mblk_t *mp)
 606 {
 607         struct ptem *ntp = (struct ptem *)q->q_ptr;
 608         struct iocblk *iocp;    /* outgoing ioctl structure */
 609         struct termio *termiop;
 610         struct termios *termiosp;
 611         mblk_t *dack_ptr;               /* disconnect message ACK block */
 612         mblk_t *pckt_msgp;              /* message sent to the PCKT module */
 613         mblk_t *dp;                     /* ioctl reply data */
 614         tcflag_t cflags;
 615         int error;
 616 
 617         switch (mp->b_datap->db_type) {
 618 
 619         case M_IOCTL:
 620                 /*
 621                  * Note:  for each "set" type operation a copy
 622                  * of the M_IOCTL message is made and passed
 623                  * downstream.  Eventually the PCKT module, if
 624                  * it has been pushed, should pick up this message.
 625                  * If the PCKT module has not been pushed the master
 626                  * side stream head will free it.
 627                  */
 628                 iocp = (struct iocblk *)mp->b_rptr;
 629                 switch (iocp->ioc_cmd) {
 630 
 631                 case TCSETAF:
 632                 case TCSETSF:
 633                         /*
 634                          * Flush the read queue.
 635                          */
 636                         if (putnextctl1(q, M_FLUSH, FLUSHR) == 0) {
 637                                 miocnak(q, mp, 0, EAGAIN);
 638                                 break;
 639                         }
 640                         /* FALLTHROUGH */
 641 
 642                 case TCSETA:
 643                 case TCSETAW:
 644                 case TCSETS:
 645                 case TCSETSW:
 646 
 647                         switch (iocp->ioc_cmd) {
 648                         case TCSETAF:
 649                         case TCSETA:
 650                         case TCSETAW:
 651                                 error = miocpullup(mp, sizeof (struct termio));
 652                                 if (error != 0) {
 653                                         miocnak(q, mp, 0, error);
 654                                         goto out;
 655                                 }
 656                                 cflags = ((struct termio *)
 657                                     mp->b_cont->b_rptr)->c_cflag;
 658                                 ntp->cflags =
 659                                     (ntp->cflags & 0xffff0000 | cflags);
 660                                 break;
 661 
 662                         case TCSETSF:
 663                         case TCSETS:
 664                         case TCSETSW:
 665                                 error = miocpullup(mp, sizeof (struct termios));
 666                                 if (error != 0) {
 667                                         miocnak(q, mp, 0, error);
 668                                         goto out;
 669                                 }
 670                                 cflags = ((struct termios *)
 671                                     mp->b_cont->b_rptr)->c_cflag;
 672                                 ntp->cflags = cflags;
 673                                 break;
 674                         }
 675 
 676                         if ((cflags & CBAUD) == B0) {
 677                                 /*
 678                                  * Hang-up: Send a zero length message.
 679                                  */
 680                                 dack_ptr = ntp->dack_ptr;
 681 
 682                                 if (dack_ptr) {
 683                                         ntp->dack_ptr = NULL;
 684                                         /*
 685                                          * Send a zero length message
 686                                          * downstream.
 687                                          */
 688                                         putnext(q, dack_ptr);
 689                                 }
 690                         } else {
 691                                 /*
 692                                  * Make a copy of this message and pass it on
 693                                  * to the PCKT module.
 694                                  */
 695                                 if ((pckt_msgp = copymsg(mp)) == NULL) {
 696                                         miocnak(q, mp, 0, EAGAIN);
 697                                         break;
 698                                 }
 699                                 putnext(q, pckt_msgp);
 700                         }
 701                         /*
 702                          * Send ACK upstream.
 703                          */
 704                         mioc2ack(mp, NULL, 0, 0);
 705                         qreply(q, mp);
 706 out:
 707                         break;
 708 
 709                 case TCGETA:
 710                         dp = allocb(sizeof (struct termio), BPRI_MED);
 711                         if (dp == NULL) {
 712                                 miocnak(q, mp, 0, EAGAIN);
 713                                 break;
 714                         }
 715                         termiop = (struct termio *)dp->b_rptr;
 716                         termiop->c_cflag = (ushort_t)ntp->cflags;
 717                         mioc2ack(mp, dp, sizeof (struct termio), 0);
 718                         qreply(q, mp);
 719                         break;
 720 
 721                 case TCGETS:
 722                         dp = allocb(sizeof (struct termios), BPRI_MED);
 723                         if (dp == NULL) {
 724                                 miocnak(q, mp, 0, EAGAIN);
 725                                 break;
 726                         }
 727                         termiosp = (struct termios *)dp->b_rptr;
 728                         termiosp->c_cflag = ntp->cflags;
 729                         mioc2ack(mp, dp, sizeof (struct termios), 0);
 730                         qreply(q, mp);
 731                         break;
 732 
 733                 case TCSBRK:
 734                         error = miocpullup(mp, sizeof (int));
 735                         if (error != 0) {
 736                                 miocnak(q, mp, 0, error);
 737                                 break;
 738                         }
 739 
 740                         /*
 741                          * Need a copy of this message to pass it on to
 742                          * the PCKT module.
 743                          */
 744                         if ((pckt_msgp = copymsg(mp)) == NULL) {
 745                                 miocnak(q, mp, 0, EAGAIN);
 746                                 break;
 747                         }
 748                         /*
 749                          * Send a copy of the M_IOCTL to the PCKT module.
 750                          */
 751                         putnext(q, pckt_msgp);
 752 
 753                         /*
 754                          * TCSBRK meaningful if data part of message is 0
 755                          * cf. termio(7).
 756                          */
 757                         if (!(*(int *)mp->b_cont->b_rptr))
 758                                 (void) putnextctl(q, M_BREAK);
 759                         /*
 760                          * ACK the ioctl.
 761                          */
 762                         mioc2ack(mp, NULL, 0, 0);
 763                         qreply(q, mp);
 764                         break;
 765 
 766                 case JWINSIZE:
 767                 case TIOCGWINSZ:
 768                 case TIOCSWINSZ:
 769                         ptioc(q, mp, WRSIDE);
 770                         break;
 771 
 772                 case TIOCSTI:
 773                         /*
 774                          * Simulate typing of a character at the terminal.  In
 775                          * all cases, we acknowledge the ioctl and pass a copy
 776                          * of it along for the PCKT module to encapsulate.  If
 777                          * not in remote mode, we also process the ioctl
 778                          * itself, looping the character given as its argument
 779                          * back around to the read side.
 780                          */
 781 
 782                         /*
 783                          * Need a copy of this message to pass on to the PCKT
 784                          * module.
 785                          */
 786                         if ((pckt_msgp = copymsg(mp)) == NULL) {
 787                                 miocnak(q, mp, 0, EAGAIN);
 788                                 break;
 789                         }
 790                         if ((ntp->state & REMOTEMODE) == 0) {
 791                                 mblk_t *bp;
 792 
 793                                 error = miocpullup(mp, sizeof (char));
 794                                 if (error != 0) {
 795                                         freemsg(pckt_msgp);
 796                                         miocnak(q, mp, 0, error);
 797                                         break;
 798                                 }
 799 
 800                                 /*
 801                                  * The permission checking has already been
 802                                  * done at the stream head, since it has to be
 803                                  * done in the context of the process doing
 804                                  * the call.
 805                                  */
 806                                 if ((bp = allocb(1, BPRI_MED)) == NULL) {
 807                                         freemsg(pckt_msgp);
 808                                         miocnak(q, mp, 0, EAGAIN);
 809                                         break;
 810                                 }
 811                                 /*
 812                                  * XXX: Is EAGAIN really the right response to
 813                                  *      flow control blockage?
 814                                  */
 815                                 if (!bcanputnext(RD(q), mp->b_band)) {
 816                                         freemsg(bp);
 817                                         freemsg(pckt_msgp);
 818                                         miocnak(q, mp, 0, EAGAIN);
 819                                         break;
 820                                 }
 821                                 *bp->b_wptr++ = *mp->b_cont->b_rptr;
 822                                 qreply(q, bp);
 823                         }
 824 
 825                         putnext(q, pckt_msgp);
 826                         mioc2ack(mp, NULL, 0, 0);
 827                         qreply(q, mp);
 828                         break;
 829 
 830                 case PTSSTTY:
 831                         if (ntp->state & IS_PTSTTY) {
 832                                 miocnak(q, mp, 0, EEXIST);
 833                         } else {
 834                                 ntp->state |= IS_PTSTTY;
 835                                 mioc2ack(mp, NULL, 0, 0);
 836                                 qreply(q, mp);
 837                         }
 838                         break;
 839 
 840                 default:
 841                         /*
 842                          * End of the line.  The slave driver doesn't see any
 843                          * ioctls that we don't explicitly pass along to it.
 844                          */
 845                         miocnak(q, mp, 0, EINVAL);
 846                         break;
 847                 }
 848                 break;
 849 
 850         case M_DELAY: /* tty delays not supported */
 851                 freemsg(mp);
 852                 break;
 853 
 854         case M_DATA:
 855                 if ((mp->b_wptr - mp->b_rptr) < 0) {
 856                         /*
 857                          * Free all bad length messages.
 858                          */
 859                         freemsg(mp);
 860                         break;
 861                 } else if ((mp->b_wptr - mp->b_rptr) == 0) {
 862                         if (!(ntp->state & IS_PTSTTY)) {
 863                                 freemsg(mp);
 864                                 break;
 865                         }
 866                 }
 867                 if (ntp->state & OFLOW_CTL)
 868                         return (0);
 869 
 870         default:
 871                 putnext(q, mp);
 872                 break;
 873 
 874         }
 875 
 876         return (1);
 877 }
 878 
 879 /*
 880  * Message must be of type M_IOCTL or M_IOCDATA for this routine to be called.
 881  */
 882 static void
 883 ptioc(queue_t *q, mblk_t *mp, int qside)
 884 {
 885         struct ptem *tp;
 886         struct iocblk *iocp;
 887         struct winsize *wb;
 888         struct jwinsize *jwb;
 889         mblk_t *tmp;
 890         mblk_t *pckt_msgp;      /* message sent to the PCKT module */
 891         int error;
 892 
 893         iocp = (struct iocblk *)mp->b_rptr;
 894         tp = (struct ptem *)q->q_ptr;
 895 
 896         switch (iocp->ioc_cmd) {
 897 
 898         case JWINSIZE:
 899                 /*
 900                  * For compatibility:  If all zeros, NAK the message for dumb
 901                  * terminals.
 902                  */
 903                 if ((tp->wsz.ws_row == 0) && (tp->wsz.ws_col == 0) &&
 904                     (tp->wsz.ws_xpixel == 0) && (tp->wsz.ws_ypixel == 0)) {
 905                         miocnak(q, mp, 0, EINVAL);
 906                         return;
 907                 }
 908 
 909                 tmp = allocb(sizeof (struct jwinsize), BPRI_MED);
 910                 if (tmp == NULL) {
 911                         miocnak(q, mp, 0, EAGAIN);
 912                         return;
 913                 }
 914 
 915                 if (iocp->ioc_count == TRANSPARENT)
 916                         mcopyout(mp, NULL, sizeof (struct jwinsize), NULL, tmp);
 917                 else
 918                         mioc2ack(mp, tmp, sizeof (struct jwinsize), 0);
 919 
 920                 jwb = (struct jwinsize *)mp->b_cont->b_rptr;
 921                 jwb->bytesx = tp->wsz.ws_col;
 922                 jwb->bytesy = tp->wsz.ws_row;
 923                 jwb->bitsx = tp->wsz.ws_xpixel;
 924                 jwb->bitsy = tp->wsz.ws_ypixel;
 925 
 926                 qreply(q, mp);
 927                 return;
 928 
 929         case TIOCGWINSZ:
 930                 /*
 931                  * If all zeros NAK the message for dumb terminals.
 932                  */
 933                 if ((tp->wsz.ws_row == 0) && (tp->wsz.ws_col == 0) &&
 934                     (tp->wsz.ws_xpixel == 0) && (tp->wsz.ws_ypixel == 0)) {
 935                         miocnak(q, mp, 0, EINVAL);
 936                         return;
 937                 }
 938 
 939                 tmp = allocb(sizeof (struct winsize), BPRI_MED);
 940                 if (tmp == NULL) {
 941                         miocnak(q, mp, 0, EAGAIN);
 942                         return;
 943                 }
 944 
 945                 mioc2ack(mp, tmp, sizeof (struct winsize), 0);
 946 
 947                 wb = (struct winsize *)mp->b_cont->b_rptr;
 948                 wb->ws_row = tp->wsz.ws_row;
 949                 wb->ws_col = tp->wsz.ws_col;
 950                 wb->ws_xpixel = tp->wsz.ws_xpixel;
 951                 wb->ws_ypixel = tp->wsz.ws_ypixel;
 952 
 953                 qreply(q, mp);
 954                 return;
 955 
 956         case TIOCSWINSZ:
 957                 error = miocpullup(mp, sizeof (struct winsize));
 958                 if (error != 0) {
 959                         miocnak(q, mp, 0, error);
 960                         return;
 961                 }
 962 
 963                 wb = (struct winsize *)mp->b_cont->b_rptr;
 964                 /*
 965                  * Send a SIGWINCH signal if the row/col information has
 966                  * changed.
 967                  */
 968                 if ((tp->wsz.ws_row != wb->ws_row) ||
 969                     (tp->wsz.ws_col != wb->ws_col) ||
 970                     (tp->wsz.ws_xpixel != wb->ws_xpixel) ||
 971                     (tp->wsz.ws_ypixel != wb->ws_xpixel)) {
 972                         /*
 973                          * SIGWINCH is always sent upstream.
 974                          */
 975                         if (qside == WRSIDE)
 976                                 (void) putnextctl1(RD(q), M_SIG, SIGWINCH);
 977                         else if (qside == RDSIDE)
 978                                 (void) putnextctl1(q, M_SIG, SIGWINCH);
 979                         /*
 980                          * Message may have come in as an M_IOCDATA; pass it
 981                          * to the master side as an M_IOCTL.
 982                          */
 983                         mp->b_datap->db_type = M_IOCTL;
 984                         if (qside == WRSIDE) {
 985                                 /*
 986                                  * Need a copy of this message to pass on to
 987                                  * the PCKT module, only if the M_IOCTL
 988                                  * orginated from the slave side.
 989                                  */
 990                                 if ((pckt_msgp = copymsg(mp)) == NULL) {
 991                                         miocnak(q, mp, 0, EAGAIN);
 992                                         return;
 993                                 }
 994                                 putnext(q, pckt_msgp);
 995                         }
 996                         tp->wsz.ws_row = wb->ws_row;
 997                         tp->wsz.ws_col = wb->ws_col;
 998                         tp->wsz.ws_xpixel = wb->ws_xpixel;
 999                         tp->wsz.ws_ypixel = wb->ws_ypixel;
1000                 }
1001 
1002                 mioc2ack(mp, NULL, 0, 0);
1003                 qreply(q, mp);
1004                 return;
1005 
1006         case TIOCSIGNAL: {
1007                 /*
1008                  * This ioctl can emanate from the master side in remote
1009                  * mode only.
1010                  */
1011                 int     sig;
1012 
1013                 if (DB_TYPE(mp) == M_IOCTL && iocp->ioc_count != TRANSPARENT) {
1014                         error = miocpullup(mp, sizeof (int));
1015                         if (error != 0) {
1016                                 miocnak(q, mp, 0, error);
1017                                 return;
1018                         }
1019                 }
1020 
1021                 if (DB_TYPE(mp) == M_IOCDATA || iocp->ioc_count != TRANSPARENT)
1022                         sig = *(int *)mp->b_cont->b_rptr;
1023                 else
1024                         sig = (int)*(intptr_t *)mp->b_cont->b_rptr;
1025 
1026                 if (sig < 1 || sig >= NSIG) {
1027                         miocnak(q, mp, 0, EINVAL);
1028                         return;
1029                 }
1030 
1031                 /*
1032                  * Send an M_PCSIG message up the slave's read side and
1033                  * respond back to the master with an ACK or NAK as
1034                  * appropriate.
1035                  */
1036                 if (putnextctl1(q, M_PCSIG, sig) == 0) {
1037                         miocnak(q, mp, 0, EAGAIN);
1038                         return;
1039                 }
1040 
1041                 mioc2ack(mp, NULL, 0, 0);
1042                 qreply(q, mp);
1043                 return;
1044         }
1045 
1046         case TIOCREMOTE: {
1047                 int     onoff;
1048                 mblk_t  *mctlp;
1049 
1050                 if (DB_TYPE(mp) == M_IOCTL) {
1051                         error = miocpullup(mp, sizeof (int));
1052                         if (error != 0) {
1053                                 miocnak(q, mp, 0, error);
1054                                 return;
1055                         }
1056                 }
1057 
1058                 onoff = *(int *)mp->b_cont->b_rptr;
1059 
1060                 /*
1061                  * Send M_CTL up using the iocblk format.
1062                  */
1063                 mctlp = mkiocb(onoff ? MC_NO_CANON : MC_DO_CANON);
1064                 if (mctlp == NULL) {
1065                         miocnak(q, mp, 0, EAGAIN);
1066                         return;
1067                 }
1068                 mctlp->b_datap->db_type = M_CTL;
1069                 putnext(q, mctlp);
1070 
1071                 /*
1072                  * ACK the ioctl.
1073                  */
1074                 mioc2ack(mp, NULL, 0, 0);
1075                 qreply(q, mp);
1076 
1077                 /*
1078                  * Record state change.
1079                  */
1080                 if (onoff)
1081                         tp->state |= REMOTEMODE;
1082                 else
1083                         tp->state &= ~REMOTEMODE;
1084                 return;
1085         }
1086 
1087         default:
1088                 putnext(q, mp);
1089                 return;
1090         }
1091 }