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