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