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