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