xref: /illumos-gate/usr/src/uts/common/io/ttcompat.c (revision 13b70267be56f36cbbc54ef71e13714ad820ad22)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * Module to intercept old V7 and 4BSD "ioctl" calls.
42  */
43 
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/signal.h>
47 #include <sys/file.h>
48 #include <sys/termios.h>
49 #include <sys/ttold.h>
50 #include <sys/cmn_err.h>
51 #include <sys/stream.h>
52 #include <sys/stropts.h>
53 #include <sys/strsubr.h>
54 #include <sys/strsun.h>
55 #include <sys/errno.h>
56 #include <sys/debug.h>
57 #include <sys/ttcompat.h>
58 #include <sys/ddi.h>
59 #include <sys/sunddi.h>
60 #include <sys/kmem.h>
61 #include <sys/policy.h>
62 
63 /*
64  * This is the loadable module wrapper.
65  */
66 #include <sys/conf.h>
67 #include <sys/modctl.h>
68 
69 /* See os/streamio.c */
70 extern int sgttyb_handling;
71 
72 static struct streamtab ttcoinfo;
73 
74 static struct fmodsw fsw = {
75 	"ttcompat",
76 	&ttcoinfo,
77 	D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE
78 };
79 
80 /*
81  * Module linkage information for the kernel.
82  */
83 
84 static struct modlstrmod modlstrmod = {
85 	&mod_strmodops,
86 	"alt ioctl calls",
87 	&fsw
88 };
89 
90 static struct modlinkage modlinkage = {
91 	MODREV_1, &modlstrmod, NULL
92 };
93 
94 int
95 _init(void)
96 {
97 	return (mod_install(&modlinkage));
98 }
99 
100 int
101 _fini(void)
102 {
103 	return (mod_remove(&modlinkage));
104 }
105 
106 int
107 _info(struct modinfo *modinfop)
108 {
109 	return (mod_info(&modlinkage, modinfop));
110 }
111 
112 static int ttcompatopen(queue_t *, dev_t *, int, int, cred_t *);
113 static int ttcompatclose(queue_t *, int, cred_t *);
114 static void ttcompatrput(queue_t *, mblk_t *);
115 static void ttcompatwput(queue_t *, mblk_t *);
116 
117 static struct module_info ttycompatmiinfo = {
118 	0,
119 	"ttcompat",
120 	0,
121 	INFPSZ,
122 	2048,
123 	128
124 };
125 
126 static struct qinit ttycompatrinit = {
127 	(int (*)())ttcompatrput,
128 	NULL,
129 	ttcompatopen,
130 	ttcompatclose,
131 	NULL,
132 	&ttycompatmiinfo
133 };
134 
135 static struct module_info ttycompatmoinfo = {
136 	42,
137 	"ttcompat",
138 	0,
139 	INFPSZ,
140 	300,
141 	200
142 };
143 
144 static struct qinit ttycompatwinit = {
145 	(int (*)())ttcompatwput,
146 	NULL,
147 	ttcompatopen,
148 	ttcompatclose,
149 	NULL,
150 	&ttycompatmoinfo
151 };
152 
153 static struct streamtab ttcoinfo = {
154 	&ttycompatrinit,
155 	&ttycompatwinit,
156 	NULL,
157 	NULL
158 };
159 
160 static void ttcompat_do_ioctl(ttcompat_state_t *, queue_t *, mblk_t *);
161 static void ttcompat_ioctl_ack(queue_t *, mblk_t *);
162 static void ttcopyout(queue_t *, mblk_t *);
163 static void ttcompat_ioctl_nak(queue_t *, mblk_t *);
164 static void from_compat(compat_state_t *, struct termios *);
165 static void to_compat(struct termios *, compat_state_t *);
166 
167 /*
168  * Open - get the current modes and translate them to the V7/4BSD equivalent.
169  */
170 /*ARGSUSED*/
171 static int
172 ttcompatopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
173 {
174 	ttcompat_state_t *tp;
175 
176 	if (q->q_ptr != NULL)  {
177 		tp = (ttcompat_state_t *)q->q_ptr;
178 		/* fail open if TIOCEXCL was done and its not privileged */
179 		if ((tp->t_new_lflags & XCLUDE) &&
180 		    secpolicy_excl_open(crp) != 0) {
181 			return (EBUSY);
182 		}
183 		return (0);		/* already attached */
184 	}
185 	tp = kmem_zalloc(sizeof (ttcompat_state_t), KM_SLEEP);
186 	q->q_ptr = tp;
187 	WR(q)->q_ptr = tp;
188 	qprocson(q);
189 
190 	return (0);
191 }
192 
193 /* ARGSUSED1 */
194 static int
195 ttcompatclose(queue_t *q, int flag, cred_t *crp)
196 {
197 	ttcompat_state_t *tp = (ttcompat_state_t *)q->q_ptr;
198 	mblk_t *mp;
199 
200 	/* Dump the state structure, then unlink it */
201 	qprocsoff(q);
202 	if (tp->t_bufcallid != 0) {
203 		qunbufcall(q, tp->t_bufcallid);
204 		tp->t_bufcallid = 0;
205 	}
206 	if ((mp = tp->t_iocpending) != NULL)
207 		freemsg(mp);
208 	kmem_free(tp, sizeof (ttcompat_state_t));
209 	q->q_ptr = NULL;
210 
211 	return (0);
212 }
213 
214 /*
215  * Put procedure for input from driver end of stream (read queue).
216  * Most messages just get passed to the next guy up; we intercept
217  * "ioctl" replies, and if it's an "ioctl" whose reply we plan to do
218  * something with, we do it.
219  */
220 static void
221 ttcompatrput(queue_t *q, mblk_t *mp)
222 {
223 	switch (mp->b_datap->db_type) {
224 
225 	case M_IOCACK:
226 		ttcompat_ioctl_ack(q, mp);
227 		break;
228 
229 	case M_IOCNAK:
230 		ttcompat_ioctl_nak(q, mp);
231 		break;
232 
233 	default:
234 		putnext(q, mp);
235 		break;
236 	}
237 }
238 
239 /*
240  * Line discipline output queue put procedure: speeds M_IOCTL
241  * messages.
242  */
243 static void
244 ttcompatwput(queue_t *q, mblk_t *mp)
245 {
246 	ttcompat_state_t *tp;
247 	struct copyreq *cqp;
248 	struct copyresp *csp;
249 	struct iocblk *iocbp;
250 
251 	tp = (ttcompat_state_t *)q->q_ptr;
252 
253 	/*
254 	 * Process some M_IOCTL messages here; pass everything else down.
255 	 */
256 	switch (mp->b_datap->db_type) {
257 
258 	default:
259 		putnext(q, mp);
260 		return;
261 
262 	case M_IOCTL:
263 		iocbp = (struct iocblk *)mp->b_rptr;
264 
265 		switch (iocbp->ioc_cmd) {
266 
267 		default:
268 	/* these are ioctls with no arguments or are known to stream head */
269 	/* process them right away */
270 			ttcompat_do_ioctl(tp, q, mp);
271 			return;
272 		case TIOCSETN:
273 		case TIOCSLTC:
274 		case TIOCSETC:
275 		case TIOCLBIS:
276 		case TIOCLBIC:
277 		case TIOCLSET:
278 		case TIOCFLUSH:
279 			if (iocbp->ioc_count != TRANSPARENT) {
280 				putnext(q, mp);
281 				return;
282 			}
283 
284 			mp->b_datap->db_type = M_COPYIN;
285 			cqp = (struct copyreq *)mp->b_rptr;
286 			cqp->cq_addr = (caddr_t)*(intptr_t *)mp->b_cont->b_rptr;
287 			switch (iocbp->ioc_cmd) {
288 				case TIOCSETN:
289 					cqp->cq_size = sizeof (struct sgttyb);
290 					break;
291 				case TIOCSLTC:
292 					cqp->cq_size = sizeof (struct ltchars);
293 					break;
294 				case TIOCSETC:
295 					cqp->cq_size = sizeof (struct tchars);
296 					break;
297 				case TIOCLBIS:
298 				case TIOCLBIC:
299 				case TIOCLSET:
300 				case TIOCFLUSH:
301 					cqp->cq_size = sizeof (int);
302 					break;
303 				default:
304 					break;
305 			}
306 			cqp->cq_flag = 0;
307 			cqp->cq_private = NULL;
308 			freemsg(mp->b_cont);
309 			mp->b_cont = NULL;
310 			mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
311 			tp->t_ioccmd = iocbp->ioc_cmd;
312 			tp->t_state |= TS_W_IN;
313 			qreply(q, mp);
314 			return;
315 
316 		} /* switch ioc_cmd */
317 	case M_IOCDATA:
318 		csp = (struct copyresp *)mp->b_rptr;
319 
320 		switch (csp->cp_cmd) {
321 
322 		default:
323 			putnext(q, mp);
324 			return;
325 
326 		case TIOCSETN:
327 		case TIOCSLTC:
328 		case TIOCSETC:
329 		case TIOCLBIS:
330 		case TIOCLBIC:
331 		case TIOCLSET:
332 		case TIOCFLUSH:
333 			tp->t_state &= ~TS_W_IN;
334 			if (csp->cp_rval != 0) {	/* failure */
335 				freemsg(mp);
336 				return;
337 			}
338 
339 			/* make it look like an ioctl */
340 			mp->b_datap->db_type = M_IOCTL;
341 			mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
342 			iocbp = (struct iocblk *)mp->b_rptr;
343 			iocbp->ioc_count = MBLKL(mp->b_cont);
344 			iocbp->ioc_error = 0;
345 			iocbp->ioc_rval = 0;
346 			ttcompat_do_ioctl(tp, q, mp);
347 			return;
348 
349 		case TIOCGLTC:
350 		case TIOCLGET:
351 		case TIOCGETC:
352 			tp->t_state &= ~TS_W_OUT;
353 			if (csp->cp_rval != 0) {	/* failure */
354 				freemsg(mp);
355 				return;
356 			}
357 
358 			iocbp = (struct iocblk *)mp->b_rptr;
359 			iocbp->ioc_count = 0;
360 			iocbp->ioc_error = 0;
361 			iocbp->ioc_rval = 0;
362 			mp->b_datap->db_type = M_IOCACK;
363 			qreply(q, mp);
364 			return;
365 
366 		} /* switch cp_cmd */
367 	} /* end message switch */
368 }
369 
370 /*
371  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
372  * the buffer we need.
373  */
374 static void
375 ttcompat_reioctl(void *arg)
376 {
377 	queue_t *q = arg;
378 	ttcompat_state_t *tp;
379 	mblk_t *mp;
380 
381 	tp = (ttcompat_state_t *)q->q_ptr;
382 	tp->t_bufcallid = 0;
383 
384 	if ((mp = tp->t_iocpending) != NULL) {
385 		tp->t_iocpending = NULL;	/* not pending any more */
386 		ttcompat_do_ioctl(tp, q, mp);
387 	}
388 }
389 
390 /*
391  * Handle old-style "ioctl" messages; pass the rest down unmolested.
392  */
393 static void
394 ttcompat_do_ioctl(ttcompat_state_t *tp, queue_t *q, mblk_t *mp)
395 {
396 	struct iocblk *iocp;
397 	int error;
398 
399 	/*
400 	 * Most of the miocpullup()'s below aren't needed because the
401 	 * ioctls in question are actually transparent M_IOCDATA messages
402 	 * dummied to look like M_IOCTL messages.  However, for clarity and
403 	 * robustness against future changes, we've included them anyway.
404 	 */
405 
406 	iocp = (struct iocblk *)mp->b_rptr;
407 	switch (iocp->ioc_cmd) {
408 
409 	/*
410 	 * "get"-style calls that get translated data from the "termios"
411 	 * structure.  Save the existing code and pass it down as a TCGETS.
412 	 */
413 	case TIOCGETC:
414 	case TIOCLGET:
415 	case TIOCGLTC:
416 		if (iocp->ioc_count != TRANSPARENT) {
417 			miocnak(q, mp, 0, EINVAL);
418 			return;
419 		}
420 
421 		/*
422 		 * We can get here with t_arg != 0, iff the stream head
423 		 * has for some reason given up on the ioctl in progress.
424 		 * The most likely cause is an interrupted ioctl syscall.
425 		 * We will behave robustly because (given our perimeter)
426 		 * the ttcompat_state_t will get set up for the new ioctl,
427 		 * and when the response we were waiting for appears it
428 		 * will be passed on to the stream head which will discard
429 		 * it as non-current.
430 		 */
431 		ASSERT(mp->b_cont != NULL);
432 		tp->t_arg = *(intptr_t *)mp->b_cont->b_rptr;
433 		/* free the data buffer - it might not be sufficient */
434 		/* driver will allocate one for termios size */
435 		freemsg(mp->b_cont);
436 		mp->b_cont = NULL;
437 		iocp->ioc_count = 0;
438 		/* FALLTHRU */
439 	case TIOCGETP:
440 		goto dogets;
441 
442 	/*
443 	 * "set"-style calls that set translated data into a "termios"
444 	 * structure.  Set our idea of the new state from the value
445 	 * given to us.  We then have to get the current state, so we
446 	 * turn this guy into a TCGETS and pass it down.  When the
447 	 * ACK comes back, we modify the state we got back and shove it
448 	 * back down as the appropriate type of TCSETS.
449 	 */
450 	case TIOCSETP:
451 	case TIOCSETN:
452 		error = miocpullup(mp, sizeof (struct sgttyb));
453 		if (error != 0) {
454 			miocnak(q, mp, 0, error);
455 			return;
456 		}
457 		tp->t_new_sgttyb = *((struct sgttyb *)mp->b_cont->b_rptr);
458 		goto dogets;
459 
460 	case TIOCSETC:
461 		error = miocpullup(mp, sizeof (struct tchars));
462 		if (error != 0) {
463 			miocnak(q, mp, 0, error);
464 			return;
465 		}
466 		tp->t_new_tchars = *((struct tchars *)mp->b_cont->b_rptr);
467 		goto dogets;
468 
469 	case TIOCSLTC:
470 		error = miocpullup(mp, sizeof (struct ltchars));
471 		if (error != 0) {
472 			miocnak(q, mp, 0, error);
473 			return;
474 		}
475 		tp->t_new_ltchars = *((struct ltchars *)mp->b_cont->b_rptr);
476 		goto dogets;
477 
478 	case TIOCLBIS:
479 	case TIOCLBIC:
480 	case TIOCLSET:
481 		error = miocpullup(mp, sizeof (int));
482 		if (error != 0) {
483 			miocnak(q, mp, 0, error);
484 			return;
485 		}
486 		tp->t_new_lflags = *(int *)mp->b_cont->b_rptr;
487 		goto dogets;
488 
489 	/*
490 	 * "set"-style call that sets a particular bit in a "termios"
491 	 * structure.  We then have to get the current state, so we
492 	 * turn this guy into a TCGETS and pass it down.  When the
493 	 * ACK comes back, we modify the state we got back and shove it
494 	 * back down as the appropriate type of TCSETS.
495 	 */
496 	case TIOCHPCL:
497 	dogets:
498 		tp->t_ioccmd = iocp->ioc_cmd;
499 		tp->t_iocid = iocp->ioc_id;
500 		tp->t_state |= TS_IOCWAIT;
501 		iocp->ioc_cmd = TCGETS;
502 		iocp->ioc_count = 0;	/* no data returned unless we say so */
503 		break;
504 
505 	/*
506 	 * "set"-style call that sets DTR.  Pretend that it was a TIOCMBIS
507 	 * with TIOCM_DTR set.
508 	 */
509 	case TIOCSDTR: {
510 		mblk_t *datap;
511 
512 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
513 			goto allocfailure;
514 		*(int *)datap->b_wptr = TIOCM_DTR;
515 		datap->b_wptr += sizeof (int);
516 		iocp->ioc_cmd = TIOCMBIS;	/* turn it into a TIOCMBIS */
517 		if (mp->b_cont != NULL)
518 			freemsg(mp->b_cont);
519 		mp->b_cont = datap;	/* attach the data */
520 		iocp->ioc_count = sizeof (int);	/* in case driver checks */
521 		break;
522 	}
523 
524 	/*
525 	 * "set"-style call that clears DTR.  Pretend that it was a TIOCMBIC
526 	 * with TIOCM_DTR set.
527 	 */
528 	case TIOCCDTR: {
529 		mblk_t *datap;
530 
531 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
532 			goto allocfailure;
533 		*(int *)datap->b_wptr = TIOCM_DTR;
534 		datap->b_wptr += sizeof (int);
535 		iocp->ioc_cmd = TIOCMBIC;	/* turn it into a TIOCMBIC */
536 		if (mp->b_cont != NULL)
537 			freemsg(mp->b_cont);
538 		mp->b_cont = datap;	/* attach the data */
539 		iocp->ioc_count = sizeof (int);	/* in case driver checks */
540 		break;
541 	}
542 
543 	/*
544 	 * Translate into the S5 form of TCFLSH.
545 	 */
546 	case TIOCFLUSH: {
547 		int flags;
548 
549 		error = miocpullup(mp, sizeof (int));
550 		if (error != 0) {
551 			miocnak(q, mp, 0, error);
552 			return;
553 		}
554 		flags = *(int *)mp->b_cont->b_rptr;
555 
556 		switch (flags&(FREAD|FWRITE)) {
557 
558 		case 0:
559 		case FREAD|FWRITE:
560 			flags = 2;	/* flush 'em both */
561 			break;
562 
563 		case FREAD:
564 			flags = 0;	/* flush read */
565 			break;
566 
567 		case FWRITE:
568 			flags = 1;	/* flush write */
569 			break;
570 		}
571 		iocp->ioc_cmd = TCFLSH;	/* turn it into a TCFLSH */
572 		*(int *)mp->b_cont->b_rptr = flags;	/* fiddle the arg */
573 		break;
574 	}
575 
576 	/*
577 	 * Turn into a TCXONC.
578 	 */
579 	case TIOCSTOP: {
580 		mblk_t *datap;
581 
582 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
583 			goto allocfailure;
584 		*(int *)datap->b_wptr = 0;	/* stop */
585 		datap->b_wptr += sizeof (int);
586 		iocp->ioc_cmd = TCXONC;	/* turn it into a XONC */
587 		iocp->ioc_count = sizeof (int);
588 		if (mp->b_cont != NULL)
589 			freemsg(mp->b_cont);
590 		mp->b_cont = datap;	/* attach the data */
591 		break;
592 	}
593 
594 	case TIOCSTART: {
595 		mblk_t *datap;
596 
597 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
598 			goto allocfailure;
599 		*(int *)datap->b_wptr = 1;	/* start */
600 		datap->b_wptr += sizeof (int);
601 		iocp->ioc_cmd = TCXONC;	/* turn it into a XONC */
602 		iocp->ioc_count = sizeof (int);
603 		if (mp->b_cont != NULL)
604 			freemsg(mp->b_cont);
605 		mp->b_cont = datap;	/* attach the data */
606 		break;
607 	}
608 	case TIOCSETD:
609 	case TIOCGETD:
610 	case DIOCSETP:
611 	case DIOCGETP:
612 	case LDOPEN:
613 	case LDCLOSE:
614 	case LDCHG:
615 	case LDSETT:
616 	case LDGETT:
617 		/*
618 		 * All of these ioctls are just ACK'd, except for
619 		 * TIOCSETD, which must be for line discipline zero.
620 		 */
621 		mp->b_datap->db_type = M_IOCACK;
622 		if (iocp->ioc_cmd == TIOCSETD) {
623 			iocp->ioc_error = miocpullup(mp, sizeof (uchar_t));
624 			if (iocp->ioc_error == 0 && (*mp->b_cont->b_rptr != 0))
625 				mp->b_datap->db_type = M_IOCNAK;
626 		}
627 
628 		iocp->ioc_error = 0;
629 		iocp->ioc_count = 0;
630 		iocp->ioc_rval = 0;
631 		qreply(q, mp);
632 		return;
633 	case IOCTYPE:
634 		mp->b_datap->db_type = M_IOCACK;
635 		iocp->ioc_error = 0;
636 		iocp->ioc_count = 0;
637 		iocp->ioc_rval = TIOC;
638 		qreply(q, mp);
639 		return;
640 	case TIOCEXCL:
641 		/* check for binary value of XCLUDE flag ???? */
642 		tp->t_new_lflags |= XCLUDE;
643 		mp->b_datap->db_type = M_IOCACK;
644 		iocp->ioc_error = 0;
645 		iocp->ioc_count = 0;
646 		iocp->ioc_rval = 0;
647 		qreply(q, mp);
648 		return;
649 	case TIOCNXCL:
650 		tp->t_new_lflags &= ~XCLUDE;
651 		mp->b_datap->db_type = M_IOCACK;
652 		iocp->ioc_error = 0;
653 		iocp->ioc_count = 0;
654 		iocp->ioc_rval = 0;
655 		qreply(q, mp);
656 		return;
657 	}
658 
659 	/*
660 	 * We don't reply to most calls, we just pass them down,
661 	 * possibly after modifying the arguments.
662 	 */
663 	putnext(q, mp);
664 	return;
665 
666 allocfailure:
667 	/*
668 	 * We needed to allocate something to handle this "ioctl", but
669 	 * couldn't; save this "ioctl" and arrange to get called back when
670 	 * it's more likely that we can get what we need.
671 	 * If there's already one being saved, throw it out, since it
672 	 * must have timed out.
673 	 */
674 	if (tp->t_iocpending != NULL)
675 		freemsg(tp->t_iocpending);
676 	tp->t_iocpending = mp;	/* hold this ioctl */
677 	if (tp->t_bufcallid != 0)
678 		qunbufcall(q, tp->t_bufcallid);
679 
680 	tp->t_bufcallid = qbufcall(q, sizeof (struct iocblk), BPRI_HI,
681 	    ttcompat_reioctl, q);
682 }
683 
684 /*
685  * Called when an M_IOCACK message is seen on the read queue; if this
686  * is the response we were waiting for, we either:
687  *    modify the data going up (if the "ioctl" read data); since in all
688  *    cases, the old-style returned information is smaller than or the same
689  *    size as the new-style returned information, we just overwrite the old
690  *    stuff with the new stuff (beware of changing structure sizes, in case
691  *    you invalidate this)
692  * or
693  *    take this data, modify it appropriately, and send it back down (if
694  *    the "ioctl" wrote data).
695  * In either case, we cancel the "wait"; the final response to a "write"
696  * ioctl goes back up to the user.
697  * If this wasn't the response we were waiting for, just pass it up.
698  */
699 static void
700 ttcompat_ioctl_ack(queue_t *q, 	mblk_t *mp)
701 {
702 	ttcompat_state_t *tp;
703 	struct iocblk *iocp;
704 	mblk_t *datap;
705 
706 	tp = (ttcompat_state_t *)q->q_ptr;
707 	iocp = (struct iocblk *)mp->b_rptr;
708 
709 	if (!(tp->t_state&TS_IOCWAIT) || iocp->ioc_id != tp->t_iocid) {
710 		/*
711 		 * This isn't the reply we're looking for.  Move along.
712 		 */
713 		putnext(q, mp);
714 		return;
715 	}
716 
717 	datap = mp->b_cont;	/* mblk containing data going up */
718 
719 	switch (tp->t_ioccmd) {
720 
721 	case TIOCGETP: {
722 		struct sgttyb *cb;
723 
724 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
725 		datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
726 			/* recycle the reply's buffer */
727 		cb = (struct sgttyb *)datap->b_wptr;
728 		/*
729 		 * This is used for TIOCGETP handling of sg_ispeed and
730 		 * sg_ospeed.  If the current speed is over 38400 (the
731 		 * sgttyb limit), then we report 38400.  Note that
732 		 * when "compatibility with old releases" is enabled
733 		 * (sgttyb_handling == 0), then t_[io]speed will have
734 		 * garbled nonsense, as in prior releases.  (See
735 		 * to_compat() below).
736 		 */
737 		cb->sg_ispeed = tp->t_curstate.t_ispeed > B38400 ? B38400 :
738 		    tp->t_curstate.t_ispeed;
739 		cb->sg_ospeed = tp->t_curstate.t_ospeed > B38400 ? B38400 :
740 		    tp->t_curstate.t_ospeed;
741 		cb->sg_erase = tp->t_curstate.t_erase;
742 		cb->sg_kill = tp->t_curstate.t_kill;
743 		cb->sg_flags = tp->t_curstate.t_flags;
744 		datap->b_wptr += sizeof (struct sgttyb);
745 		iocp->ioc_count = sizeof (struct sgttyb);
746 
747 		/* you are lucky - stream head knows how to copy you out */
748 
749 		tp->t_state &= ~TS_IOCWAIT;	/* we got what we wanted */
750 		iocp->ioc_rval = 0;
751 		iocp->ioc_cmd =  tp->t_ioccmd;
752 		putnext(q, mp);
753 		return;
754 	}
755 
756 	case TIOCGETC:
757 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
758 		datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
759 			/* recycle the reply's buffer */
760 		bcopy(&tp->t_curstate.t_intrc, datap->b_wptr,
761 		    sizeof (struct tchars));
762 		datap->b_wptr += sizeof (struct tchars);
763 		break;
764 
765 	case TIOCGLTC:
766 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
767 		datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
768 			/* recycle the reply's buffer */
769 		bcopy(&tp->t_curstate.t_suspc, datap->b_wptr,
770 		    sizeof (struct ltchars));
771 		datap->b_wptr += sizeof (struct ltchars);
772 		break;
773 
774 	case TIOCLGET:
775 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
776 		datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
777 			/* recycle the reply's buffer */
778 		*(int *)datap->b_wptr =
779 		    ((unsigned)tp->t_curstate.t_flags) >> 16;
780 		datap->b_wptr += sizeof (int);
781 		break;
782 
783 	case TIOCSETP:
784 	case TIOCSETN:
785 		/*
786 		 * Get the current state from the GETS data, and
787 		 * update it.
788 		 */
789 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
790 		tp->t_curstate.t_erase = tp->t_new_sgttyb.sg_erase;
791 		tp->t_curstate.t_kill = tp->t_new_sgttyb.sg_kill;
792 		/*
793 		 * For new-style handling, we ignore requests to set
794 		 * B38400 when the current speed is over B38400.  This
795 		 * means that we change the speed as requested if:
796 		 *	old style (sgttyb_handling == 0) is requested
797 		 *	the requested new speed isn't B38400
798 		 *	the current speed is at or below B38400
799 		 * Note that when old style is requested, both speeds
800 		 * in t_curstate are set to <= B38400 by to_compat, so
801 		 * the first test isn't needed here.
802 		 * Also note that we silently allow the user to set
803 		 * speeds above B38400 through this interface,
804 		 * regardless of the style setting.  This allows
805 		 * greater compatibility with current BSD releases.
806 		 */
807 		if (tp->t_new_sgttyb.sg_ispeed != B38400 ||
808 		    tp->t_curstate.t_ispeed <= B38400)
809 			tp->t_curstate.t_ispeed = tp->t_new_sgttyb.sg_ispeed;
810 		if (tp->t_new_sgttyb.sg_ospeed != B38400 ||
811 		    tp->t_curstate.t_ospeed <= B38400)
812 			tp->t_curstate.t_ospeed = tp->t_new_sgttyb.sg_ospeed;
813 		tp->t_curstate.t_flags =
814 		    (tp->t_curstate.t_flags & 0xffff0000) |
815 		    (tp->t_new_sgttyb.sg_flags & 0xffff);
816 
817 		/*
818 		 * Replace the data that came up with the updated data.
819 		 */
820 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
821 
822 		/*
823 		 * Send it back down as a TCSETS or TCSETSF.
824 		 */
825 		iocp->ioc_cmd = (tp->t_ioccmd == TIOCSETP) ? TCSETSF : TCSETS;
826 		goto senddown;
827 
828 	case TIOCSETC:
829 		/*
830 		 * Get the current state from the GETS data, and
831 		 * update it.
832 		 */
833 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
834 		bcopy(&tp->t_new_tchars,
835 		    &tp->t_curstate.t_intrc, sizeof (struct tchars));
836 
837 		/*
838 		 * Replace the data that came up with the updated data.
839 		 */
840 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
841 
842 		/*
843 		 * Send it back down as a TCSETS.
844 		 */
845 		iocp->ioc_cmd = TCSETS;
846 		goto senddown;
847 
848 	case TIOCSLTC:
849 		/*
850 		 * Get the current state from the GETS data, and
851 		 * update it.
852 		 */
853 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
854 		bcopy(&tp->t_new_ltchars,
855 		    &tp->t_curstate.t_suspc, sizeof (struct ltchars));
856 
857 		/*
858 		 * Replace the data that came up with the updated data.
859 		 */
860 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
861 
862 		/*
863 		 * Send it back down as a TCSETS.
864 		 */
865 		iocp->ioc_cmd = TCSETS;
866 		goto senddown;
867 
868 	case TIOCLBIS:
869 		/*
870 		 * Get the current state from the GETS data, and
871 		 * update it.
872 		 */
873 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
874 		tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
875 
876 		/*
877 		 * Replace the data that came up with the updated data.
878 		 */
879 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
880 
881 		/*
882 		 * Send it back down as a TCSETS.
883 		 */
884 		iocp->ioc_cmd = TCSETS;
885 		goto senddown;
886 
887 	case TIOCLBIC:
888 		/*
889 		 * Get the current state from the GETS data, and
890 		 * update it.
891 		 */
892 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
893 		tp->t_curstate.t_flags &= ~(tp->t_new_lflags << 16);
894 
895 		/*
896 		 * Replace the data that came up with the updated data.
897 		 */
898 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
899 
900 		/*
901 		 * Send it back down as a TCSETS.
902 		 */
903 		iocp->ioc_cmd = TCSETS;
904 		goto senddown;
905 
906 	case TIOCLSET:
907 		/*
908 		 * Get the current state from the GETS data, and
909 		 * update it.
910 		 */
911 		to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
912 		tp->t_curstate.t_flags &= 0xffff;
913 		tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
914 
915 		/*
916 		 * Replace the data that came up with the updated data.
917 		 */
918 		from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
919 
920 		/*
921 		 * Send it back down as a TCSETS.
922 		 */
923 		iocp->ioc_cmd = TCSETS;
924 		goto senddown;
925 
926 	case TIOCHPCL:
927 		/*
928 		 * Replace the data that came up with the updated data.
929 		 */
930 		((struct termios *)datap->b_rptr)->c_cflag |= HUPCL;
931 
932 		/*
933 		 * Send it back down as a TCSETS.
934 		 */
935 		iocp->ioc_cmd = TCSETS;
936 		goto senddown;
937 
938 	case TCSETSF:
939 		/*
940 		 * We're acknowledging the terminal reset ioctl that we sent
941 		 * when the module was opened.
942 		 */
943 		tp->t_state &= ~(TS_IOCWAIT | TS_TIOCNAK);
944 		freemsg(mp);
945 		return;
946 
947 	default:
948 		cmn_err(CE_WARN, "ttcompat: Unexpected ioctl acknowledgment\n");
949 	}
950 
951 	/*
952 	 * All the calls that return something return 0.
953 	 */
954 	tp->t_state &= ~TS_IOCWAIT;	/* we got what we wanted */
955 	iocp->ioc_rval = 0;
956 
957 	/* copy out the data - ioctl transparency */
958 	iocp->ioc_cmd =  tp->t_ioccmd;
959 	ttcopyout(q, mp);
960 	return;
961 
962 senddown:
963 	/*
964 	 * Send a "get state" reply back down, with suitably-modified
965 	 * state, as a "set state" "ioctl".
966 	 */
967 	tp->t_state &= ~TS_IOCWAIT;
968 	mp->b_datap->db_type = M_IOCTL;
969 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
970 	putnext(WR(q), mp);
971 }
972 /* Called from ttcompatrput M_IOCACK processing. */
973 /* Copies out the data using M_COPYOUT messages */
974 
975 static void
976 ttcopyout(queue_t *q, mblk_t *mp)
977 {
978 	struct copyreq *cqp;
979 	ttcompat_state_t *tp;
980 
981 	tp = (ttcompat_state_t *)q->q_ptr;
982 
983 	mp->b_datap->db_type = M_COPYOUT;
984 	cqp = (struct copyreq *)mp->b_rptr;
985 	cqp->cq_addr = (caddr_t)tp->t_arg; /* retrieve the 3rd argument */
986 	tp->t_arg = 0; /* clear it since we don't need it anymore */
987 	switch (tp->t_ioccmd) {
988 		case TIOCGLTC:
989 			cqp->cq_size = sizeof (struct ltchars);
990 			break;
991 		case TIOCGETC:
992 			cqp->cq_size = sizeof (struct tchars);
993 			break;
994 		case TIOCLGET:
995 			cqp->cq_size = sizeof (int);
996 			break;
997 		default:
998 			cmn_err(CE_WARN,
999 			    "ttcompat: Unknown ioctl to copyout\n");
1000 			break;
1001 		}
1002 	cqp->cq_flag = 0;
1003 	cqp->cq_private = NULL;
1004 	tp->t_state |= TS_W_OUT;
1005 	putnext(q, mp);
1006 }
1007 
1008 
1009 /*
1010  * Called when an M_IOCNAK message is seen on the read queue; if this is
1011  * the response we were waiting for, cancel the wait.  Pass the reply up;
1012  * if we were waiting for this response, we can't complete the "ioctl" and
1013  * the NAK will tell that to the guy above us.
1014  * If this wasn't the response we were waiting for, just pass it up.
1015  */
1016 static void
1017 ttcompat_ioctl_nak(queue_t *q, mblk_t *mp)
1018 {
1019 	ttcompat_state_t *tp;
1020 	struct iocblk *iocp;
1021 
1022 	iocp = (struct iocblk *)mp->b_rptr;
1023 	tp = (ttcompat_state_t *)q->q_ptr;
1024 
1025 	if (tp->t_state&TS_IOCWAIT && iocp->ioc_id == tp->t_iocid) {
1026 		tp->t_state &= ~TS_IOCWAIT; /* this call isn't going through */
1027 		tp->t_arg = 0;	/* we may have stashed the 3rd argument */
1028 	}
1029 	putnext(q, mp);
1030 }
1031 
1032 #define	FROM_COMPAT_CHAR(to, from) { if ((to = from) == 0377) to = 0; }
1033 
1034 static void
1035 from_compat(compat_state_t *csp, struct termios *termiosp)
1036 {
1037 	termiosp->c_iflag = 0;
1038 	termiosp->c_oflag &= (ONLRET|ONOCR);
1039 
1040 	termiosp->c_cflag = (termiosp->c_cflag &
1041 	    (CRTSCTS|CRTSXOFF|PAREXT|LOBLK|HUPCL)) | CREAD;
1042 
1043 	if (csp->t_ospeed > CBAUD) {
1044 		termiosp->c_cflag |= ((csp->t_ospeed - CBAUD - 1) & CBAUD) |
1045 		    CBAUDEXT;
1046 	} else {
1047 		termiosp->c_cflag |= csp->t_ospeed & CBAUD;
1048 	}
1049 
1050 	if (csp->t_ospeed != csp->t_ispeed) {
1051 		if (csp->t_ispeed > (CIBAUD >> IBSHIFT)) {
1052 			termiosp->c_cflag |= CIBAUDEXT |
1053 			    (((csp->t_ispeed - (CIBAUD >> IBSHIFT) - 1) <<
1054 			    IBSHIFT) & CIBAUD);
1055 		} else {
1056 			termiosp->c_cflag |= (csp->t_ispeed << IBSHIFT) &
1057 			    CIBAUD;
1058 		}
1059 		/* hang up if ispeed=0 */
1060 		if (csp->t_ispeed == 0)
1061 			termiosp->c_cflag &= ~CBAUD & ~CBAUDEXT;
1062 	}
1063 	if (csp->t_ispeed == B110 || csp->t_xflags & STOPB)
1064 		termiosp->c_cflag |= CSTOPB;
1065 	termiosp->c_lflag = ECHOK;
1066 	FROM_COMPAT_CHAR(termiosp->c_cc[VERASE], csp->t_erase);
1067 	FROM_COMPAT_CHAR(termiosp->c_cc[VKILL], csp->t_kill);
1068 	FROM_COMPAT_CHAR(termiosp->c_cc[VINTR], csp->t_intrc);
1069 	FROM_COMPAT_CHAR(termiosp->c_cc[VQUIT], csp->t_quitc);
1070 	FROM_COMPAT_CHAR(termiosp->c_cc[VSTART], csp->t_startc);
1071 	FROM_COMPAT_CHAR(termiosp->c_cc[VSTOP], csp->t_stopc);
1072 	termiosp->c_cc[VEOL2] = 0;
1073 	FROM_COMPAT_CHAR(termiosp->c_cc[VSUSP], csp->t_suspc);
1074 	/* is this useful? */
1075 	FROM_COMPAT_CHAR(termiosp->c_cc[VDSUSP], csp->t_dsuspc);
1076 	FROM_COMPAT_CHAR(termiosp->c_cc[VREPRINT], csp->t_rprntc);
1077 	FROM_COMPAT_CHAR(termiosp->c_cc[VDISCARD], csp->t_flushc);
1078 	FROM_COMPAT_CHAR(termiosp->c_cc[VWERASE], csp->t_werasc);
1079 	FROM_COMPAT_CHAR(termiosp->c_cc[VLNEXT], csp->t_lnextc);
1080 	termiosp->c_cc[VSTATUS] = 0;
1081 	if (csp->t_flags & O_TANDEM)
1082 		termiosp->c_iflag |= IXOFF;
1083 	if (csp->t_flags & O_LCASE) {
1084 		termiosp->c_iflag |= IUCLC;
1085 		termiosp->c_oflag |= OLCUC;
1086 		termiosp->c_lflag |= XCASE;
1087 	}
1088 	if (csp->t_flags & O_ECHO)
1089 		termiosp->c_lflag |= ECHO;
1090 	if (csp->t_flags & O_CRMOD) {
1091 		termiosp->c_iflag |= ICRNL;
1092 		termiosp->c_oflag |= ONLCR;
1093 		switch (csp->t_flags & O_CRDELAY) {
1094 
1095 		case O_CR1:
1096 			termiosp->c_oflag |= CR2;
1097 			break;
1098 
1099 		case O_CR2:
1100 			termiosp->c_oflag |= CR3;
1101 			break;
1102 		}
1103 	} else {
1104 		if ((csp->t_flags & O_NLDELAY) == O_NL1)
1105 			termiosp->c_oflag |= ONLRET|CR1;	/* tty37 */
1106 	}
1107 	if ((csp->t_flags & O_NLDELAY) == O_NL2)
1108 		termiosp->c_oflag |= NL1;
1109 	/*
1110 	 * When going into RAW mode, the special characters controlled by the
1111 	 * POSIX IEXTEN bit no longer apply; when leaving, they do.
1112 	 */
1113 	if (csp->t_flags & O_RAW) {
1114 		termiosp->c_cflag |= CS8;
1115 		termiosp->c_iflag &= ~(ICRNL|IUCLC);
1116 		termiosp->c_lflag &= ~(XCASE|IEXTEN);
1117 	} else {
1118 		termiosp->c_iflag |= IMAXBEL|BRKINT|IGNPAR;
1119 		if (termiosp->c_cc[VSTOP] != 0 && termiosp->c_cc[VSTART] != 0)
1120 			termiosp->c_iflag |= IXON;
1121 		if (csp->t_flags & O_LITOUT)
1122 			termiosp->c_cflag |= CS8;
1123 		else {
1124 			if (csp->t_flags & O_PASS8)
1125 				termiosp->c_cflag |= CS8;
1126 				/* XXX - what about 8 bits plus parity? */
1127 			else {
1128 				switch (csp->t_flags & (O_EVENP|O_ODDP)) {
1129 
1130 				case 0:
1131 					termiosp->c_iflag |= ISTRIP;
1132 					termiosp->c_cflag |= CS8;
1133 					break;
1134 
1135 				case O_EVENP:
1136 					termiosp->c_iflag |= INPCK|ISTRIP;
1137 					termiosp->c_cflag |= CS7|PARENB;
1138 					break;
1139 
1140 				case O_ODDP:
1141 					termiosp->c_iflag |= INPCK|ISTRIP;
1142 					termiosp->c_cflag |= CS7|PARENB|PARODD;
1143 					break;
1144 
1145 				case O_EVENP|O_ODDP:
1146 					termiosp->c_iflag |= ISTRIP;
1147 					termiosp->c_cflag |= CS7|PARENB;
1148 					break;
1149 				}
1150 			}
1151 			if (!(csp->t_xflags & NOPOST))
1152 				termiosp->c_oflag |= OPOST;
1153 		}
1154 		termiosp->c_lflag |= IEXTEN;
1155 		if (!(csp->t_xflags & NOISIG))
1156 			termiosp->c_lflag |= ISIG;
1157 		if (!(csp->t_flags & O_CBREAK))
1158 			termiosp->c_lflag |= ICANON;
1159 		if (csp->t_flags & O_CTLECH)
1160 			termiosp->c_lflag |= ECHOCTL;
1161 	}
1162 	switch (csp->t_flags & O_TBDELAY) {
1163 
1164 	case O_TAB1:
1165 		termiosp->c_oflag |= TAB1;
1166 		break;
1167 
1168 	case O_TAB2:
1169 		termiosp->c_oflag |= TAB2;
1170 		break;
1171 
1172 	case O_XTABS:
1173 		termiosp->c_oflag |= TAB3;
1174 		break;
1175 	}
1176 	if (csp->t_flags & O_VTDELAY)
1177 		termiosp->c_oflag |= FFDLY;
1178 	if (csp->t_flags & O_BSDELAY)
1179 		termiosp->c_oflag |= BSDLY;
1180 	if (csp->t_flags & O_PRTERA)
1181 		termiosp->c_lflag |= ECHOPRT;
1182 	if (csp->t_flags & O_CRTERA)
1183 		termiosp->c_lflag |= ECHOE;
1184 	if (csp->t_flags & O_TOSTOP)
1185 		termiosp->c_lflag |= TOSTOP;
1186 	if (csp->t_flags & O_FLUSHO)
1187 		termiosp->c_lflag |= FLUSHO;
1188 	if (csp->t_flags & O_NOHANG)
1189 		termiosp->c_cflag |= CLOCAL;
1190 	if (csp->t_flags & O_CRTKIL)
1191 		termiosp->c_lflag |= ECHOKE;
1192 	if (csp->t_flags & O_PENDIN)
1193 		termiosp->c_lflag |= PENDIN;
1194 	if (!(csp->t_flags & O_DECCTQ))
1195 		termiosp->c_iflag |= IXANY;
1196 	if (csp->t_flags & O_NOFLSH)
1197 		termiosp->c_lflag |= NOFLSH;
1198 	if (termiosp->c_lflag & ICANON) {
1199 		FROM_COMPAT_CHAR(termiosp->c_cc[VEOF], csp->t_eofc);
1200 		FROM_COMPAT_CHAR(termiosp->c_cc[VEOL], csp->t_brkc);
1201 	} else {
1202 		termiosp->c_cc[VMIN] = 1;
1203 		termiosp->c_cc[VTIME] = 0;
1204 	}
1205 }
1206 
1207 #define	TO_COMPAT_CHAR(to, from) { if ((to = from) == 0) to = (uchar_t)0377; }
1208 
1209 static void
1210 to_compat(struct termios *termiosp, compat_state_t *csp)
1211 {
1212 	csp->t_xflags &= (NOISIG|NOPOST);
1213 	csp->t_ospeed = termiosp->c_cflag & CBAUD;
1214 	csp->t_ispeed = (termiosp->c_cflag & CIBAUD) >> IBSHIFT;
1215 	if (sgttyb_handling > 0) {
1216 		if (termiosp->c_cflag & CBAUDEXT)
1217 			csp->t_ospeed += CBAUD + 1;
1218 		if (termiosp->c_cflag & CIBAUDEXT)
1219 			csp->t_ispeed += (CIBAUD >> IBSHIFT) + 1;
1220 	}
1221 	if (csp->t_ispeed == 0)
1222 		csp->t_ispeed = csp->t_ospeed;
1223 	if ((termiosp->c_cflag & CSTOPB) && csp->t_ispeed != B110)
1224 		csp->t_xflags |= STOPB;
1225 	TO_COMPAT_CHAR(csp->t_erase, termiosp->c_cc[VERASE]);
1226 	TO_COMPAT_CHAR(csp->t_kill, termiosp->c_cc[VKILL]);
1227 	TO_COMPAT_CHAR(csp->t_intrc, termiosp->c_cc[VINTR]);
1228 	TO_COMPAT_CHAR(csp->t_quitc, termiosp->c_cc[VQUIT]);
1229 	TO_COMPAT_CHAR(csp->t_startc, termiosp->c_cc[VSTART]);
1230 	TO_COMPAT_CHAR(csp->t_stopc, termiosp->c_cc[VSTOP]);
1231 	TO_COMPAT_CHAR(csp->t_suspc, termiosp->c_cc[VSUSP]);
1232 	TO_COMPAT_CHAR(csp->t_dsuspc, termiosp->c_cc[VDSUSP]);
1233 	TO_COMPAT_CHAR(csp->t_rprntc, termiosp->c_cc[VREPRINT]);
1234 	TO_COMPAT_CHAR(csp->t_flushc, termiosp->c_cc[VDISCARD]);
1235 	TO_COMPAT_CHAR(csp->t_werasc, termiosp->c_cc[VWERASE]);
1236 	TO_COMPAT_CHAR(csp->t_lnextc, termiosp->c_cc[VLNEXT]);
1237 	csp->t_flags &= (O_CTLECH|O_LITOUT|O_PASS8|O_ODDP|O_EVENP);
1238 	if (termiosp->c_iflag & IXOFF)
1239 		csp->t_flags |= O_TANDEM;
1240 	if (!(termiosp->c_iflag &
1241 	    (IMAXBEL|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|
1242 	    INLCR|IGNCR|ICRNL|IUCLC|IXON)) &&
1243 	    !(termiosp->c_oflag & OPOST) &&
1244 	    (termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1245 	    !(termiosp->c_lflag & (ISIG|ICANON|XCASE|IEXTEN)))
1246 		csp->t_flags |= O_RAW;
1247 	else {
1248 		if (!(termiosp->c_iflag & IXON)) {
1249 			csp->t_startc = (uchar_t)0377;
1250 			csp->t_stopc = (uchar_t)0377;
1251 		}
1252 		if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1253 		    !(termiosp->c_oflag & OPOST))
1254 			csp->t_flags |= O_LITOUT;
1255 		else {
1256 			csp->t_flags &= ~O_LITOUT;
1257 			if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8) {
1258 				if (!(termiosp->c_iflag & ISTRIP))
1259 					csp->t_flags |= O_PASS8;
1260 			} else {
1261 				csp->t_flags &= ~(O_ODDP|O_EVENP|O_PASS8);
1262 				if (termiosp->c_cflag & PARODD)
1263 					csp->t_flags |= O_ODDP;
1264 				else if (termiosp->c_iflag & INPCK)
1265 					csp->t_flags |= O_EVENP;
1266 				else
1267 					csp->t_flags |= O_ODDP|O_EVENP;
1268 			}
1269 			if (!(termiosp->c_oflag & OPOST))
1270 				csp->t_xflags |= NOPOST;
1271 			else
1272 				csp->t_xflags &= ~NOPOST;
1273 		}
1274 		if (!(termiosp->c_lflag & ISIG))
1275 			csp->t_xflags |= NOISIG;
1276 		else
1277 			csp->t_xflags &= ~NOISIG;
1278 		if (!(termiosp->c_lflag & ICANON))
1279 			csp->t_flags |= O_CBREAK;
1280 		if (termiosp->c_lflag & ECHOCTL)
1281 			csp->t_flags |= O_CTLECH;
1282 		else
1283 			csp->t_flags &= ~O_CTLECH;
1284 	}
1285 	if (termiosp->c_oflag & OLCUC)
1286 		csp->t_flags |= O_LCASE;
1287 	if (termiosp->c_lflag&ECHO)
1288 		csp->t_flags |= O_ECHO;
1289 	if (termiosp->c_oflag & ONLCR) {
1290 		csp->t_flags |= O_CRMOD;
1291 		switch (termiosp->c_oflag & CRDLY) {
1292 
1293 		case CR2:
1294 			csp->t_flags |= O_CR1;
1295 			break;
1296 
1297 		case CR3:
1298 			csp->t_flags |= O_CR2;
1299 			break;
1300 		}
1301 	} else {
1302 		if ((termiosp->c_oflag & CR1) &&
1303 		    (termiosp->c_oflag & ONLRET))
1304 			csp->t_flags |= O_NL1;	/* tty37 */
1305 	}
1306 	if ((termiosp->c_oflag & ONLRET) && (termiosp->c_oflag & NL1))
1307 		csp->t_flags |= O_NL2;
1308 	switch (termiosp->c_oflag & TABDLY) {
1309 
1310 	case TAB1:
1311 		csp->t_flags |= O_TAB1;
1312 		break;
1313 
1314 	case TAB2:
1315 		csp->t_flags |= O_TAB2;
1316 		break;
1317 
1318 	case XTABS:
1319 		csp->t_flags |= O_XTABS;
1320 		break;
1321 	}
1322 	if (termiosp->c_oflag & FFDLY)
1323 		csp->t_flags |= O_VTDELAY;
1324 	if (termiosp->c_oflag & BSDLY)
1325 		csp->t_flags |= O_BSDELAY;
1326 	if (termiosp->c_lflag & ECHOPRT)
1327 		csp->t_flags |= O_PRTERA;
1328 	if (termiosp->c_lflag & ECHOE)
1329 		csp->t_flags |= (O_CRTERA|O_CRTBS);
1330 	if (termiosp->c_lflag & TOSTOP)
1331 		csp->t_flags |= O_TOSTOP;
1332 	if (termiosp->c_lflag & FLUSHO)
1333 		csp->t_flags |= O_FLUSHO;
1334 	if (termiosp->c_cflag & CLOCAL)
1335 		csp->t_flags |= O_NOHANG;
1336 	if (termiosp->c_lflag & ECHOKE)
1337 		csp->t_flags |= O_CRTKIL;
1338 	if (termiosp->c_lflag & PENDIN)
1339 		csp->t_flags |= O_PENDIN;
1340 	if (!(termiosp->c_iflag & IXANY))
1341 		csp->t_flags |= O_DECCTQ;
1342 	if (termiosp->c_lflag & NOFLSH)
1343 		csp->t_flags |= O_NOFLSH;
1344 	if (termiosp->c_lflag & ICANON) {
1345 		TO_COMPAT_CHAR(csp->t_eofc, termiosp->c_cc[VEOF]);
1346 		TO_COMPAT_CHAR(csp->t_brkc, termiosp->c_cc[VEOL]);
1347 	} else {
1348 		termiosp->c_cc[VMIN] = 1;
1349 		termiosp->c_cc[VTIME] = 0;
1350 	}
1351 }
1352