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