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 */
70extern int sgttyb_handling;
71
72static struct streamtab ttcoinfo;
73
74static 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
84static struct modlstrmod modlstrmod = {
85	&mod_strmodops,
86	"alt ioctl calls",
87	&fsw
88};
89
90static struct modlinkage modlinkage = {
91	MODREV_1, &modlstrmod, NULL
92};
93
94int
95_init(void)
96{
97	return (mod_install(&modlinkage));
98}
99
100int
101_fini(void)
102{
103	return (mod_remove(&modlinkage));
104}
105
106int
107_info(struct modinfo *modinfop)
108{
109	return (mod_info(&modlinkage, modinfop));
110}
111
112static int ttcompatopen(queue_t *, dev_t *, int, int, cred_t *);
113static int ttcompatclose(queue_t *, int, cred_t *);
114static int ttcompatrput(queue_t *, mblk_t *);
115static int ttcompatwput(queue_t *, mblk_t *);
116
117static struct module_info ttycompatmiinfo = {
118	0,
119	"ttcompat",
120	0,
121	INFPSZ,
122	2048,
123	128
124};
125
126static struct qinit ttycompatrinit = {
127	ttcompatrput,
128	NULL,
129	ttcompatopen,
130	ttcompatclose,
131	NULL,
132	&ttycompatmiinfo
133};
134
135static struct module_info ttycompatmoinfo = {
136	42,
137	"ttcompat",
138	0,
139	INFPSZ,
140	300,
141	200
142};
143
144static struct qinit ttycompatwinit = {
145	ttcompatwput,
146	NULL,
147	ttcompatopen,
148	ttcompatclose,
149	NULL,
150	&ttycompatmoinfo
151};
152
153static struct streamtab ttcoinfo = {
154	&ttycompatrinit,
155	&ttycompatwinit,
156	NULL,
157	NULL
158};
159
160static void ttcompat_do_ioctl(ttcompat_state_t *, queue_t *, mblk_t *);
161static void ttcompat_ioctl_ack(queue_t *, mblk_t *);
162static void ttcopyout(queue_t *, mblk_t *);
163static void ttcompat_ioctl_nak(queue_t *, mblk_t *);
164static void from_compat(compat_state_t *, struct termios *);
165static 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*/
171static int
172ttcompatopen(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 */
194static int
195ttcompatclose(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 */
220static int
221ttcompatrput(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 */
244static int
245ttcompatwput(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 */
376static void
377ttcompat_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 */
395static void
396ttcompat_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
668allocfailure:
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 */
701static void
702ttcompat_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
964senddown:
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
977static void
978ttcopyout(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 */
1018static void
1019ttcompat_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
1036static void
1037from_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
1211static void
1212to_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