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 #pragma ident   "%Z%%M% %I%     %E% SMI" /* from S5R4 1.13 */
  32 
  33 /*
  34  * Description:
  35  *
  36  * The PTEM streams module is used as a pseudo driver emulator.  Its purpose
  37  * is to emulate the ioctl() functions of a terminal device driver.
  38  */
  39 
  40 #include <sys/types.h>
  41 #include <sys/param.h>
  42 #include <sys/stream.h>
  43 #include <sys/stropts.h>
  44 #include <sys/strsun.h>
  45 #include <sys/termio.h>
  46 #include <sys/pcb.h>
  47 #include <sys/signal.h>
  48 #include <sys/cred.h>
  49 #include <sys/strtty.h>
  50 #include <sys/errno.h>
  51 #include <sys/cmn_err.h>
  52 #include <sys/jioctl.h>
  53 #include <sys/ptem.h>
  54 #include <sys/ptms.h>
  55 #include <sys/debug.h>
  56 #include <sys/kmem.h>
  57 #include <sys/ddi.h>
  58 #include <sys/sunddi.h>
  59 #include <sys/conf.h>
  60 #include <sys/modctl.h>
  61 
  62 extern struct streamtab pteminfo;
  63 
  64 static struct fmodsw fsw = {
  65         "ptem",
  66         &pteminfo,
  67         D_MTQPAIR | D_MP
  68 };
  69 
  70 static struct modlstrmod modlstrmod = {
  71         &mod_strmodops, "pty hardware emulator", &fsw
  72 };
  73 
  74 static struct modlinkage modlinkage = {
  75         MODREV_1, &modlstrmod, NULL
  76 };
  77 
  78 int
  79 _init()
  80 {
  81         return (mod_install(&modlinkage));
  82 }
  83 
  84 int
  85 _fini()
  86 {
  87         return (mod_remove(&modlinkage));
  88 }
  89 
  90 int
  91 _info(struct modinfo *modinfop)
  92 {
  93         return (mod_info(&modlinkage, modinfop));
  94 }
  95 
  96 /*
  97  * stream data structure definitions
  98  */
  99 static int ptemopen(queue_t *, dev_t  *, int, int, cred_t *);
 100 static int ptemclose(queue_t *, int, cred_t *);
 101 static void ptemrput(queue_t *, mblk_t *);
 102 static void ptemwput(queue_t *, mblk_t *);
 103 static void ptemwsrv(queue_t *);
 104 
 105 static struct module_info ptem_info = {
 106         0xabcd,
 107         "ptem",
 108         0,
 109         512,
 110         512,
 111         128
 112 };
 113 
 114 static struct qinit ptemrinit = {
 115         (int (*)()) ptemrput,
 116         NULL,
 117         ptemopen,
 118         ptemclose,
 119         NULL,
 120         &ptem_info,
 121         NULL
 122 };
 123 
 124 static struct qinit ptemwinit = {
 125         (int (*)()) ptemwput,
 126         (int (*)()) ptemwsrv,
 127         ptemopen,
 128         ptemclose,
 129         nulldev,
 130         &ptem_info,
 131         NULL
 132 };
 133 
 134 struct streamtab pteminfo = {
 135         &ptemrinit,
 136         &ptemwinit,
 137         NULL,
 138         NULL
 139 };
 140 
 141 static void     ptioc(queue_t *, mblk_t *, int);
 142 static int      ptemwmsg(queue_t *, mblk_t *);
 143 
 144 /*
 145  * ptemopen - open routine gets called when the module gets pushed onto the
 146  * stream.
 147  */
 148 /* ARGSUSED */
 149 static int
 150 ptemopen(
 151         queue_t    *q,          /* pointer to the read side queue */
 152         dev_t   *devp,          /* pointer to stream tail's dev */
 153         int     oflag,          /* the user open(2) supplied flags */
 154         int     sflag,          /* open state flag */
 155         cred_t *credp)          /* credentials */
 156 {
 157         struct ptem *ntp;       /* ptem entry for this PTEM module */
 158         mblk_t *mop;            /* an setopts mblk */
 159         struct stroptions *sop;
 160         struct termios *termiosp;
 161         int len;
 162 
 163         if (sflag != MODOPEN)
 164                 return (EINVAL);
 165 
 166         if (q->q_ptr != NULL) {
 167                 /* It's already attached. */
 168                 return (0);
 169         }
 170 
 171         /*
 172          * Allocate state structure.
 173          */
 174         ntp = kmem_alloc(sizeof (*ntp), KM_SLEEP);
 175 
 176         /*
 177          * Allocate a message block, used to pass the zero length message for
 178          * "stty 0".
 179          *
 180          * NOTE: it's better to find out if such a message block can be
 181          *       allocated before it's needed than to not be able to
 182          *       deliver (for possible lack of buffers) when a hang-up
 183          *       occurs.
 184          */
 185         if ((ntp->dack_ptr = allocb(4, BPRI_MED)) == NULL) {
 186                 kmem_free(ntp, sizeof (*ntp));
 187                 return (EAGAIN);
 188         }
 189 
 190         /*
 191          * Initialize an M_SETOPTS message to set up hi/lo water marks on
 192          * stream head read queue and add controlling tty if not set.
 193          */
 194         mop = allocb(sizeof (struct stroptions), BPRI_MED);
 195         if (mop == NULL) {
 196                 freemsg(ntp->dack_ptr);
 197                 kmem_free(ntp, sizeof (*ntp));
 198                 return (EAGAIN);
 199         }
 200         mop->b_datap->db_type = M_SETOPTS;
 201         mop->b_wptr += sizeof (struct stroptions);
 202         sop = (struct stroptions *)mop->b_rptr;
 203         sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
 204         sop->so_hiwat = 512;
 205         sop->so_lowat = 256;
 206 
 207         /*
 208          * Cross-link.
 209          */
 210         ntp->q_ptr = q;
 211         q->q_ptr = ntp;
 212         WR(q)->q_ptr = ntp;
 213 
 214         /*
 215          * Get termios defaults.  These are stored as
 216          * a property in the "options" node.
 217          */
 218         if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 0, "ttymodes",
 219             (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
 220             len == sizeof (struct termios)) {
 221 
 222                 ntp->cflags = termiosp->c_cflag;
 223                 kmem_free(termiosp, len);
 224         } else {
 225                 /*
 226                  * Gack!  Whine about it.
 227                  */
 228                 cmn_err(CE_WARN, "ptem: Couldn't get ttymodes property!");
 229         }
 230         ntp->wsz.ws_row = 0;
 231         ntp->wsz.ws_col = 0;
 232         ntp->wsz.ws_xpixel = 0;
 233         ntp->wsz.ws_ypixel = 0;
 234 
 235         ntp->state = 0;
 236 
 237         /*
 238          * Commit to the open and send the M_SETOPTS off to the stream head.
 239          */
 240         qprocson(q);
 241         putnext(q, mop);
 242 
 243         return (0);
 244 }
 245 
 246 
 247 /*
 248  * ptemclose - This routine gets called when the module gets popped off of the
 249  * stream.
 250  */
 251 /* ARGSUSED */
 252 static int
 253 ptemclose(queue_t *q, int flag, cred_t *credp)
 254 {
 255         struct ptem *ntp;       /* ptem entry for this PTEM module */
 256 
 257         qprocsoff(q);
 258         ntp = (struct ptem *)q->q_ptr;
 259         freemsg(ntp->dack_ptr);
 260         kmem_free(ntp, sizeof (*ntp));
 261         q->q_ptr = WR(q)->q_ptr = NULL;
 262         return (0);
 263 }
 264 
 265 
 266 /*
 267  * ptemrput - Module read queue put procedure.
 268  *
 269  * This is called from the module or driver downstream.
 270  */
 271 static void
 272 ptemrput(queue_t *q, mblk_t *mp)
 273 {
 274         struct iocblk *iocp;    /* M_IOCTL data */
 275         struct copyresp *resp;  /* transparent ioctl response struct */
 276         int error;
 277 
 278         switch (mp->b_datap->db_type) {
 279         case M_DELAY:
 280         case M_READ:
 281                 freemsg(mp);
 282                 break;
 283 
 284         case M_IOCTL:
 285                 iocp = (struct iocblk *)mp->b_rptr;
 286 
 287                 switch (iocp->ioc_cmd) {
 288                 case TCSBRK:
 289                         /*
 290                          * Send a break message upstream.
 291                          *
 292                          * XXX: Shouldn't the argument come into play in
 293                          *      determining whether or not so send an M_BREAK?
 294                          *      It certainly does in the write-side direction.
 295                          */
 296                         error = miocpullup(mp, sizeof (int));
 297                         if (error != 0) {
 298                                 miocnak(q, mp, 0, error);
 299                                 break;
 300                         }
 301                         if (!(*(int *)mp->b_cont->b_rptr)) {
 302                                 if (!putnextctl(q, M_BREAK)) {
 303                                         /*
 304                                          * Send an NAK reply back
 305                                          */
 306                                         miocnak(q, mp, 0, EAGAIN);
 307                                         break;
 308                                 }
 309                         }
 310                         /*
 311                          * ACK it.
 312                          */
 313                         mioc2ack(mp, NULL, 0, 0);
 314                         qreply(q, mp);
 315                         break;
 316 
 317                 case JWINSIZE:
 318                 case TIOCGWINSZ:
 319                 case TIOCSWINSZ:
 320                         ptioc(q, mp, RDSIDE);
 321                         break;
 322 
 323                 case TIOCSIGNAL:
 324                         /*
 325                          * The following subtle logic is due to the fact that
 326                          * `mp' may be in any one of three distinct formats:
 327                          *
 328                          *      1. A transparent M_IOCTL with an intptr_t-sized
 329                          *         payload containing the signal number.
 330                          *
 331                          *      2. An I_STR M_IOCTL with an int-sized payload
 332                          *         containing the signal number.
 333                          *
 334                          *      3. An M_IOCDATA with an int-sized payload
 335                          *         containing the signal number.
 336                          */
 337                         if (iocp->ioc_count == TRANSPARENT) {
 338                                 intptr_t sig = *(intptr_t *)mp->b_cont->b_rptr;
 339 
 340                                 if (sig < 1 || sig >= NSIG) {
 341                                         /*
 342                                          * it's transparent with pointer
 343                                          * to the arg
 344                                          */
 345                                         mcopyin(mp, NULL, sizeof (int), NULL);
 346                                         qreply(q, mp);
 347                                         break;
 348                                 }
 349                         }
 350                         ptioc(q, mp, RDSIDE);
 351                         break;
 352 
 353                 case TIOCREMOTE:
 354                         if (iocp->ioc_count != TRANSPARENT)
 355                                 ptioc(q, mp, RDSIDE);
 356                         else {
 357                                 mcopyin(mp, NULL, sizeof (int), NULL);
 358                                 qreply(q, mp);
 359                         }
 360                         break;
 361 
 362                 default:
 363                         putnext(q, mp);
 364                         break;
 365                 }
 366                 break;
 367 
 368         case M_IOCDATA:
 369                 resp = (struct copyresp *)mp->b_rptr;
 370                 if (resp->cp_rval) {
 371                         /*
 372                          * Just free message on failure.
 373                          */
 374                         freemsg(mp);
 375                         break;
 376                 }
 377 
 378                 /*
 379                  * Only need to copy data for the SET case.
 380                  */
 381                 switch (resp->cp_cmd) {
 382 
 383                 case TIOCSWINSZ:
 384                 case TIOCSIGNAL:
 385                 case TIOCREMOTE:
 386                         ptioc(q, mp, RDSIDE);
 387                         break;
 388 
 389                 case JWINSIZE:
 390                 case TIOCGWINSZ:
 391                         mp->b_datap->db_type = M_IOCACK;
 392                         mioc2ack(mp, NULL, 0, 0);
 393                         qreply(q, mp);
 394                         break;
 395 
 396                 default:
 397                         freemsg(mp);
 398                         break;
 399         }
 400         break;
 401 
 402         case M_IOCACK:
 403         case M_IOCNAK:
 404                 /*
 405                  * We only pass write-side ioctls through to the master that
 406                  * we've already ACKed or NAKed to the stream head.  Thus, we
 407                  * discard ones arriving from below, since they're redundant
 408                  * from the point of view of modules above us.
 409                  */
 410                 freemsg(mp);
 411                 break;
 412 
 413         case M_HANGUP:
 414                 /*
 415                  * clear blocked state.
 416                  */
 417                 {
 418                         struct ptem *ntp = (struct ptem *)q->q_ptr;
 419                         if (ntp->state & OFLOW_CTL) {
 420                                 ntp->state &= ~OFLOW_CTL;
 421                                 qenable(WR(q));
 422                         }
 423                 }
 424         default:
 425                 putnext(q, mp);
 426                 break;
 427         }
 428 }
 429 
 430 
 431 /*
 432  * ptemwput - Module write queue put procedure.
 433  *
 434  * This is called from the module or stream head upstream.
 435  *
 436  * XXX: This routine is quite lazy about handling allocation failures,
 437  *      basically just giving up and reporting failure.  It really ought to
 438  *      set up bufcalls and only fail when it's absolutely necessary.
 439  */
 440 static void
 441 ptemwput(queue_t *q, mblk_t *mp)
 442 {
 443         struct ptem *ntp = (struct ptem *)q->q_ptr;
 444         struct iocblk *iocp;    /* outgoing ioctl structure */
 445         struct copyresp *resp;
 446         unsigned char type = mp->b_datap->db_type;
 447 
 448         if (type >= QPCTL) {
 449                 switch (type) {
 450 
 451                 case M_IOCDATA:
 452                         resp = (struct copyresp *)mp->b_rptr;
 453                         if (resp->cp_rval) {
 454                                 /*
 455                                  * Just free message on failure.
 456                                  */
 457                                 freemsg(mp);
 458                                 break;
 459                         }
 460 
 461                         /*
 462                          * Only need to copy data for the SET case.
 463                          */
 464                         switch (resp->cp_cmd) {
 465 
 466                                 case TIOCSWINSZ:
 467                                         ptioc(q, mp, WRSIDE);
 468                                         break;
 469 
 470                                 case JWINSIZE:
 471                                 case TIOCGWINSZ:
 472                                         mioc2ack(mp, NULL, 0, 0);
 473                                         qreply(q, mp);
 474                                         break;
 475 
 476                                 default:
 477                                         freemsg(mp);
 478                         }
 479                         break;
 480 
 481                 case M_FLUSH:
 482                         if (*mp->b_rptr & FLUSHW) {
 483                             if ((ntp->state & IS_PTSTTY) &&
 484                                         (*mp->b_rptr & FLUSHBAND))
 485                                 flushband(q, *(mp->b_rptr + 1), 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 }