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