xref: /xnu-10002.81.5/bsd/kern/tty_compat.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1 /*
2  * Copyright (c) 2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1997 Apple Computer, Inc. All Rights Reserved */
29 /*-
30  * Copyright (c) 1982, 1986, 1991, 1993
31  *      The Regents of the University of California.  All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  * 3. All advertising materials mentioning features or use of this software
42  *    must display the following acknowledgement:
43  *      This product includes software developed by the University of
44  *      California, Berkeley and its contributors.
45  * 4. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  *
61  *      @(#)tty_compat.c        8.1 (Berkeley) 6/10/93
62  */
63 
64 /*
65  * Compatibility routines for BSD 4.3 tty ioctl() commands
66  *
67  * The only function externalized from this file is ttcompat() and it is
68  * externalized as private extern to prevent exporting of the symbol when
69  * KEXTs link against the kernel.
70  *
71  * Locks:	All functions in this file assume that the tty_lock()
72  *		is held on the tty structure before these functions are
73  *		called.
74  */
75 
76 #include <sys/param.h>
77 #include <sys/systm.h>
78 #include <sys/ioctl.h>
79 #include <sys/proc_internal.h>
80 #include <sys/tty.h>
81 #include <sys/termios.h>
82 #include <sys/file_internal.h>
83 #include <sys/conf.h>
84 #include <sys/kernel.h>
85 #include <sys/sysctl.h>
86 #include <sys/syslog.h>
87 
88 static int ttcompatgetflags(struct tty *tp);
89 static void ttcompatsetflags(struct tty *tp, struct termios *t);
90 static void ttcompatsetlflags(struct tty *tp, struct termios *t);
91 static unsigned int ttcompatspeedtab(speed_t speed, struct speedtab *table);
92 
93 /*
94  * These two tables encode baud rate to speed code and speed code to
95  * baud rate information.  They are a mapping between the <sys/termios.h>
96  * baud rate constants and the <sys/ttydev.h> baud rate constants.  We
97  * cannot use those constants directly here because they occupy the same
98  * name space.
99  */
100 static struct speedtab compatspeeds[] = {
101 #define MAX_SPEED 17
102 	{ .sp_speed = 115200, .sp_code = 17 },
103 	{ .sp_speed = 57600, .sp_code = 16 },
104 	{ .sp_speed = 38400, .sp_code = 15 },
105 	{ .sp_speed = 19200, .sp_code = 14 },
106 	{ .sp_speed = 9600, .sp_code = 13 },
107 	{ .sp_speed = 4800, .sp_code = 12 },
108 	{ .sp_speed = 2400, .sp_code = 11 },
109 	{ .sp_speed = 1800, .sp_code = 10 },
110 	{ .sp_speed = 1200, .sp_code = 9 },
111 	{ .sp_speed = 600, .sp_code = 8 },
112 	{ .sp_speed = 300, .sp_code = 7 },
113 	{ .sp_speed = 200, .sp_code = 6 },
114 	{ .sp_speed = 150, .sp_code = 5 },
115 	{ .sp_speed = 134, .sp_code = 4 },
116 	{ .sp_speed = 110, .sp_code = 3 },
117 	{ .sp_speed = 75, .sp_code = 2 },
118 	{ .sp_speed = 50, .sp_code = 1 },
119 	{ .sp_speed = 0, .sp_code = 0 },
120 	{ .sp_speed = -1, .sp_code = -1 },
121 };
122 static int compatspcodes[] = {
123 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
124 	1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
125 };
126 
127 /*
128  * ttcompatspeedtab
129  *
130  * Description:	Given a baud rate value as a speed_t, and a speed table,
131  *		convert the baud rate to a speed code integer, according to the
132  *		contents of the table.  This effectively changes termios.h
133  *		baud rate values into ttydev.h baud rate codes.
134  *
135  * Parameters:	speed_t speed  Baud rate
136  *		struct speedtab *table Baud rate table to speed code table
137  *
138  * Returns:	1              B50 speed code; returned if we can
139  *                         not find an answer in the table.
140  *          0              If a 0 was requested in order to
141  *                         trigger a hangup (250ms of line
142  *                         silence, per Bell 103C standard).
143  *          [2, MAX_SPEED] A speed code matching the requested
144  *                         baud rate (potentially rounded down,
145  *                         if there is no exact match).
146  *
147  * Notes:	This function is used for TIOCGETP, TIOCSETP, and TIOCSETN.
148  */
149 static unsigned int
ttcompatspeedtab(speed_t speed,struct speedtab * table)150 ttcompatspeedtab(speed_t speed, struct speedtab *table)
151 {
152 	if (speed == 0) {
153 		return 0; /* hangup */
154 	}
155 	for (; table->sp_speed > 0; table++) {
156 		if (table->sp_speed <= speed) { /* nearest one, rounded down */
157 			return (unsigned int)table->sp_code;
158 		}
159 	}
160 	return 1; /* 50, min and not hangup */
161 }
162 
163 
164 /*
165  * ttsetcompat
166  *
167  * Description:	Convert backward compatability set command arguments as
168  *		follows:
169  *
170  *		TIOCSETP	->	TIOSETAF
171  *		TIOCSETN	->	TIOCSETA
172  *		TIOCSETC	->	TIOCSETA
173  *		TIOCSLTC	->	TIOCSETA
174  *		TIOCLBIS	->	TIOCSETA
175  *		TIOCLBIC	->	TIOCSETA
176  *		TIOCLSET	->	TIOCSETA
177  *
178  *	The converted command argument and potentially modified 'term'
179  *	argument are returned to ttcompat(), which will then call
180  *	ttioctl_locked(), if this function returns successfully.
181  *
182  * Parameters	struct tty *tp		The tty on which the operation is
183  *					being performed.
184  *		u_long *com		A pointer to the terminal input/output
185  *					command being requested; its contents
186  *					will be modified per the table above,
187  *					on a non-error return.
188  *		caddr_t data		Command specific parameter data; this
189  *					data is read but not modified.
190  *		struct termios *term	A local stack termios structure from
191  *					ttcompat(), whose contents are to be
192  *					modified based on *com and *data.
193  *
194  * Returns:	EINVAL			An input speed or output speed is
195  *					outside the allowable range for a
196  *					TIOCSETP or TIOCSETN command.
197  *		0			All other cases return 0.
198  *
199  * Notes:	This function may modify the contents of the tp->t_flags
200  *		field in a successful call to TIOCSETP, TIOCSETN, TIOCLBIS,
201  *		TIOCLBIC, or TIOCLSET.
202  *
203  *		All other tp fields will remain unmodifed, since the struct
204  *		termios is a local stack copy from ttcompat(), and not the
205  *		real thing.  A subsequent call to ttioctl_locked() in
206  *		ttcompat(), however, may result in subsequent changes.
207  *
208  * WARNING:	This compatibility code is not 64/32 clean; it will only
209  *		work for 32 bit processes on 32 bit kernels or 64 bit
210  *		processes on 64 bit kernels.  We are not addressing this
211  *		due to <rdar://6904053>.
212  */
213 static int
ttsetcompat(struct tty * tp,u_long * com,caddr_t data,struct termios * term)214 ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term)
215 {
216 	switch (*com) {
217 	case TIOCSETP:
218 	/*
219 	 * Wait for all characters queued for output to drain, then
220 	 * Discard all characters queued for input, and then set
221 	 * the input and output speeds and device flags, per the
222 	 * contents of the struct sgttyb that 'data' points to.
223 	 */
224 	case TIOCSETN:
225 		/*
226 		 * Same as TIOCSETP, but the output is not drained, and any
227 		 * pending input is not discarded.
228 		 */
229 	{
230 		__IGNORE_WCASTALIGN(struct sgttyb *sg = (struct sgttyb *)data);
231 		if (sg->sg_ispeed < 0) {
232 			return EINVAL;
233 		}
234 		unsigned int ispeed = (unsigned int)sg->sg_ispeed;
235 		if (ispeed > MAX_SPEED) {
236 			return EINVAL;
237 		}
238 		if (ispeed != ttcompatspeedtab(tp->t_ispeed, compatspeeds)) {
239 			term->c_ispeed = compatspcodes[ispeed];
240 		} else {
241 			term->c_ispeed = tp->t_ispeed;
242 		}
243 
244 		/*
245 		 * Can't error out at the beginning due to potential for
246 		 * backwards-incompatibility.  For instance:
247 		 *
248 		 * struct sgttyb sg; // uninitialized
249 		 * sg.sg_ispeed = SOME_VALID_VALUE;
250 		 *
251 		 * Should still set the input speed.
252 		 */
253 		if (sg->sg_ospeed < 0) {
254 			return EINVAL;
255 		}
256 		unsigned int ospeed = (unsigned int)sg->sg_ospeed;
257 		if (ospeed > MAX_SPEED) {
258 			return EINVAL;
259 		}
260 		if (ospeed != ttcompatspeedtab(tp->t_ospeed, compatspeeds)) {
261 			term->c_ospeed = compatspcodes[ospeed];
262 		} else {
263 			term->c_ospeed = tp->t_ospeed;
264 		}
265 
266 		term->c_cc[VERASE] = sg->sg_erase;
267 		term->c_cc[VKILL] = sg->sg_kill;
268 		tp->t_flags = (tp->t_flags & 0xffff0000) | (sg->sg_flags & 0xffff);
269 		ttcompatsetflags(tp, term);
270 		*com = (*com == TIOCSETP) ? TIOCSETAF : TIOCSETA;
271 		break;
272 	}
273 	case TIOCSETC:
274 		/*
275 		 * Set the terminal control characters per the contents of
276 		 * the struct tchars that 'data' points to.
277 		 */
278 	{
279 		__IGNORE_WCASTALIGN(struct tchars *tc = (struct tchars *)data);
280 		cc_t *cc;
281 
282 		cc = term->c_cc;
283 		cc[VINTR] = tc->t_intrc;
284 		cc[VQUIT] = tc->t_quitc;
285 		cc[VSTART] = tc->t_startc;
286 		cc[VSTOP] = tc->t_stopc;
287 		cc[VEOF] = tc->t_eofc;
288 		cc[VEOL] = tc->t_brkc;
289 		if (tc->t_brkc == -1) {
290 			cc[VEOL2] = _POSIX_VDISABLE;
291 		}
292 		*com = TIOCSETA;
293 		break;
294 	}
295 	case TIOCSLTC:
296 		/*
297 		 * Set the terminal control characters per the contents of
298 		 * the struct ltchars that 'data' points to.
299 		 */
300 	{
301 		__IGNORE_WCASTALIGN(struct ltchars *ltc = (struct ltchars *)data);
302 		cc_t *cc;
303 
304 		cc = term->c_cc;
305 		cc[VSUSP] = ltc->t_suspc;
306 		cc[VDSUSP] = ltc->t_dsuspc;
307 		cc[VREPRINT] = ltc->t_rprntc;
308 		cc[VDISCARD] = ltc->t_flushc;
309 		cc[VWERASE] = ltc->t_werasc;
310 		cc[VLNEXT] = ltc->t_lnextc;
311 		*com = TIOCSETA;
312 		break;
313 	}
314 	case TIOCLBIS:
315 	/*
316 	 * Set the bits in the terminal state local flags word
317 	 * (16 bits) for the terminal to the current bits OR
318 	 * those in the 16 bit value pointed to by 'data'.
319 	 */
320 	case TIOCLBIC:
321 	/*
322 	 * Clear the bits in the terminal state local flags word
323 	 * for the terminal to the current bits AND those bits NOT
324 	 * in the 16 bit value pointed to by 'data'.
325 	 */
326 	case TIOCLSET:
327 		/*
328 		 * Set the terminal state local flags word to exactly those
329 		 * bits that correspond to the 16 bit value pointed to by
330 		 * 'data'.
331 		 */
332 	{
333 		__IGNORE_WCASTALIGN(int set = *(int *)data);
334 		if (*com == TIOCLSET) {
335 			tp->t_flags = (tp->t_flags & 0xffff) | set << 16;
336 		} else {
337 			tp->t_flags =
338 			    (ttcompatgetflags(tp) & 0xffff0000) | (tp->t_flags & 0xffff);
339 			if (*com == TIOCLBIS) {
340 				tp->t_flags |= set << 16;
341 			} else {
342 				tp->t_flags &= ~(set << 16);
343 			}
344 		}
345 		ttcompatsetlflags(tp, term);
346 		*com = TIOCSETA;
347 		break;
348 	}
349 	}
350 	return 0;
351 }
352 
353 /*
354  * ttcompat
355  *
356  * Description:	For 'set' commands, convert the command and arguments as
357  *		necessary, and call ttioctl_locked(), returning the result
358  *		as our result; for 'get' commands, obtain the requested data
359  *		from the appropriate source, and return it in the expected
360  *		format.  If the command is not recognized, return EINVAL.
361  *
362  * Parameters	struct tty *tp		The tty on which the operation is
363  *					being performed.
364  *		u_long com		The terminal input/output command
365  *					being requested.
366  *		caddr_t	data		The pointer to the user data argument
367  *					provided with the command.
368  *		int flag		The file open flags (e.g. FREAD).
369  *		struct proc *p		The current process pointer for the
370  *					operation.
371  *
372  * Returns:	0			Most 'get' operations can't fail, and
373  *					therefore return this.
374  *		ENOTTY			TIOCGSID may return this when you
375  *					attempt to get the session ID for a
376  *					terminal with no associated session,
377  *					or for which there is a session, but
378  *					no session leader.
379  *		ENOTTY			If the command cannot be handled at
380  *					this layer, this will be returned.
381  *		*			Any value returned by ttioctl_locked(),
382  *					if a set command is requested.
383  *
384  * Notes:	The process pointer may be a proxy on whose behalf we are
385  *		operating, so it is not safe to simply use current_process()
386  *		instead.
387  */
388 /*ARGSUSED*/
389 __private_extern__ int
ttcompat(struct tty * tp,u_long com,caddr_t data,int flag,struct proc * p)390 ttcompat(struct tty *tp, u_long com, caddr_t data, int flag, struct proc *p)
391 {
392 	switch (com) {
393 	case TIOCSETP:
394 	case TIOCSETN:
395 	case TIOCSETC:
396 	case TIOCSLTC:
397 	case TIOCLBIS:
398 	case TIOCLBIC:
399 	case TIOCLSET:
400 		/*
401 		 * See ttsetcompat() for a full description of these command
402 		 * values and their meanings.
403 		 */
404 	{
405 		struct termios term;
406 		int error;
407 
408 		term = tp->t_termios;
409 		if ((error = ttsetcompat(tp, &com, data, &term)) != 0) {
410 			return error;
411 		}
412 		return ttioctl_locked(tp, com, (caddr_t) &term, flag, p);
413 	}
414 	case TIOCGETP:
415 		/*
416 		 * Get the current input and output speeds, and device
417 		 * flags, into the structure pointed to by 'data'.
418 		 */
419 	{
420 		__IGNORE_WCASTALIGN(struct sgttyb *sg = (struct sgttyb *)data);
421 		cc_t *cc = tp->t_cc;
422 
423 		static_assert(MAX_SPEED <= CHAR_MAX, "maximum speed fits in a char");
424 		sg->sg_ospeed = (char)ttcompatspeedtab(tp->t_ospeed, compatspeeds);
425 		if (tp->t_ispeed == 0) {
426 			sg->sg_ispeed = sg->sg_ospeed;
427 		} else {
428 			sg->sg_ispeed = (char)ttcompatspeedtab(tp->t_ispeed, compatspeeds);
429 		}
430 		sg->sg_erase = cc[VERASE];
431 		sg->sg_kill = cc[VKILL];
432 		tp->t_flags = ttcompatgetflags(tp);
433 		sg->sg_flags = (short)tp->t_flags;
434 		break;
435 	}
436 	case TIOCGETC:
437 		/*
438 		 * Get the terminal control characters into the struct
439 		 * tchars that 'data' points to.
440 		 */
441 	{
442 		struct tchars *tc = (struct tchars *)data;
443 		cc_t *cc = tp->t_cc;
444 
445 		tc->t_intrc = cc[VINTR];
446 		tc->t_quitc = cc[VQUIT];
447 		tc->t_startc = cc[VSTART];
448 		tc->t_stopc = cc[VSTOP];
449 		tc->t_eofc = cc[VEOF];
450 		tc->t_brkc = cc[VEOL];
451 		break;
452 	}
453 	case TIOCGLTC:
454 		/*
455 		 * Get the terminal control characters into the struct
456 		 * ltchars that 'data' points to.
457 		 */
458 	{
459 		struct ltchars *ltc = (struct ltchars *)data;
460 		cc_t *cc = tp->t_cc;
461 
462 		ltc->t_suspc = cc[VSUSP];
463 		ltc->t_dsuspc = cc[VDSUSP];
464 		ltc->t_rprntc = cc[VREPRINT];
465 		ltc->t_flushc = cc[VDISCARD];
466 		ltc->t_werasc = cc[VWERASE];
467 		ltc->t_lnextc = cc[VLNEXT];
468 		break;
469 	}
470 	case TIOCLGET:
471 		/*
472 		 * Get the terminal state local flags word into the 16 bit
473 		 * value pointed to by 'data'.
474 		 */
475 		tp->t_flags =
476 		    (ttcompatgetflags(tp) & 0xffff0000UL)
477 		    | (tp->t_flags & 0xffff);
478 		*(int *)data = tp->t_flags >> 16;
479 		break;
480 
481 	case OTIOCGETD:
482 		/*
483 		 * Get the current line discipline into the int pointed to
484 		 * by 'data'.
485 		 */
486 		*(int *)data = tp->t_line ? tp->t_line : 2;
487 		break;
488 
489 	case OTIOCSETD:
490 		/*
491 		 * Set the current line discipline based on the value of the
492 		 * int pointed to by 'data'.
493 		 */
494 	{
495 		int ldisczero = 0;
496 
497 		return ttioctl_locked(tp, TIOCSETD,
498 		           *(int *)data == 2 ? (caddr_t)&ldisczero : data, flag, p);
499 	}
500 
501 	case OTIOCCONS:
502 		/*
503 		 * Become the console device.
504 		 */
505 		*(int *)data = 1;
506 		return ttioctl_locked(tp, TIOCCONS, data, flag, p);
507 
508 	case TIOCGSID:
509 		/*
510 		 * Get the current session ID (controlling process' PID).
511 		 */
512 		if (tp->t_session == NULL) {
513 			return ENOTTY;
514 		}
515 
516 		if (tp->t_session->s_leader == NULL) {
517 			return ENOTTY;
518 		}
519 
520 		*(int *) data =  proc_getpid(tp->t_session->s_leader);
521 		break;
522 
523 	default:
524 		/*
525 		 * This ioctl is not handled at this layer.
526 		 */
527 		return ENOTTY;
528 	}
529 
530 	/*
531 	 * Successful 'get' operation.
532 	 */
533 	return 0;
534 }
535 
536 /*
537  * ttcompatgetflags
538  *
539  * Description:	Get the terminal state local flags, device flags, and current
540  *		speed code for the device (all 32 bits are returned).
541  *
542  * Parameters	struct tty *tp		The tty on which the operation is
543  *					being performed.
544  *
545  * Returns:	*			Integer value corresponding to the
546  *					current terminal state local flags
547  *					word.
548  *
549  * Notes:	Caller is responsible for breaking these bits back out into
550  *		separate 16 bit filelds, if that's what was actually desired.
551  */
552 static int
ttcompatgetflags(struct tty * tp)553 ttcompatgetflags(struct tty *tp)
554 {
555 	tcflag_t iflag  = tp->t_iflag;
556 	tcflag_t lflag  = tp->t_lflag;
557 	tcflag_t oflag  = tp->t_oflag;
558 	tcflag_t cflag  = tp->t_cflag;
559 	int flags = 0;
560 
561 	if (iflag & IXOFF) {
562 		flags |= TANDEM;
563 	}
564 	if (iflag & ICRNL || oflag & ONLCR) {
565 		flags |= CRMOD;
566 	}
567 	if ((cflag & CSIZE) == CS8) {
568 		flags |= PASS8;
569 		if (iflag & ISTRIP) {
570 			flags |= ANYP;
571 		}
572 	} else if (cflag & PARENB) {
573 		if (iflag & INPCK) {
574 			if (cflag & PARODD) {
575 				flags |= ODDP;
576 			} else {
577 				flags |= EVENP;
578 			}
579 		} else {
580 			flags |= EVENP | ODDP;
581 		}
582 	}
583 
584 	if ((lflag & ICANON) == 0) {
585 		/* fudge */
586 		if (iflag & (INPCK | ISTRIP | IXON) || lflag & (IEXTEN | ISIG)
587 		    || (cflag & (CSIZE | PARENB)) != CS8) {
588 			flags |= CBREAK;
589 		} else {
590 			flags |= RAW;
591 		}
592 	}
593 	if (!(flags & RAW) && !(oflag & OPOST) && (cflag & (CSIZE | PARENB)) == CS8) {
594 		flags |= LITOUT;
595 	}
596 	if (cflag & MDMBUF) {
597 		flags |= MDMBUF;
598 	}
599 	if ((cflag & HUPCL) == 0) {
600 		flags |= NOHANG;
601 	}
602 	if (oflag & OXTABS) {
603 		flags |= XTABS;
604 	}
605 	if (lflag & ECHOE) {
606 		flags |= CRTERA | CRTBS;
607 	}
608 	if (lflag & ECHOKE) {
609 		flags |= CRTKIL | CRTBS;
610 	}
611 	if (lflag & ECHOPRT) {
612 		flags |= PRTERA;
613 	}
614 	if (lflag & ECHOCTL) {
615 		flags |= CTLECH;
616 	}
617 	if ((iflag & IXANY) == 0) {
618 		flags |= DECCTQ;
619 	}
620 	flags |= lflag & (ECHO | TOSTOP | FLUSHO | PENDIN | NOFLSH);
621 	return flags;
622 }
623 
624 /*
625  * ttcompatsetflags
626  *
627  * Description:	Given a set of compatability flags, convert the compatability
628  *		flags in the terminal flags fields into canonical flags in the
629  *		provided termios struct.
630  *
631  * Parameters:	struct tty *tp		The tty on which the operation is
632  *					being performed.
633  *		struct termios *t	The termios structure into which to
634  *					return the converted flags.
635  *
636  * Returns:	void			(implicit: *t, modified)
637  */
638 static void
ttcompatsetflags(struct tty * tp,struct termios * t)639 ttcompatsetflags(struct tty *tp, struct termios *t)
640 {
641 	int flags = tp->t_flags;
642 	tcflag_t iflag  = t->c_iflag;
643 	tcflag_t oflag  = t->c_oflag;
644 	tcflag_t lflag  = t->c_lflag;
645 	tcflag_t cflag  = t->c_cflag;
646 
647 	if (flags & RAW) {
648 		iflag = IGNBRK;
649 		lflag &= ~(ECHOCTL | ISIG | ICANON | IEXTEN);
650 	} else {
651 		iflag &= ~(PARMRK | IGNPAR | IGNCR | INLCR);
652 		iflag |= BRKINT | IXON | IMAXBEL;
653 		lflag |= ISIG | IEXTEN | ECHOCTL;   /* XXX was echoctl on ? */
654 		if (flags & XTABS) {
655 			oflag |= OXTABS;
656 		} else {
657 			oflag &= ~OXTABS;
658 		}
659 		if (flags & CBREAK) {
660 			lflag &= ~ICANON;
661 		} else {
662 			lflag |= ICANON;
663 		}
664 		if (flags & CRMOD) {
665 			iflag |= ICRNL;
666 			oflag |= ONLCR;
667 		} else {
668 			iflag &= ~ICRNL;
669 			oflag &= ~ONLCR;
670 		}
671 	}
672 	if (flags & ECHO) {
673 		lflag |= ECHO;
674 	} else {
675 		lflag &= ~ECHO;
676 	}
677 
678 	cflag &= ~(CSIZE | PARENB);
679 	if (flags & (RAW | LITOUT | PASS8)) {
680 		cflag |= CS8;
681 		if (!(flags & (RAW | PASS8))
682 		    || (flags & (RAW | PASS8 | ANYP)) == (PASS8 | ANYP)) {
683 			iflag |= ISTRIP;
684 		} else {
685 			iflag &= ~ISTRIP;
686 		}
687 		if (flags & (RAW | LITOUT)) {
688 			oflag &= ~OPOST;
689 		} else {
690 			oflag |= OPOST;
691 		}
692 	} else {
693 		cflag |= CS7 | PARENB;
694 		iflag |= ISTRIP;
695 		oflag |= OPOST;
696 	}
697 	/* XXX don't set INPCK if RAW or PASS8? */
698 	if ((flags & (EVENP | ODDP)) == EVENP) {
699 		iflag |= INPCK;
700 		cflag &= ~PARODD;
701 	} else if ((flags & (EVENP | ODDP)) == ODDP) {
702 		iflag |= INPCK;
703 		cflag |= PARODD;
704 	} else {
705 		iflag &= ~INPCK;
706 	}
707 	if (flags & TANDEM) {
708 		iflag |= IXOFF;
709 	} else {
710 		iflag &= ~IXOFF;
711 	}
712 	if ((flags & DECCTQ) == 0) {
713 		iflag |= IXANY;
714 	} else {
715 		iflag &= ~IXANY;
716 	}
717 	t->c_iflag = iflag;
718 	t->c_oflag = oflag;
719 	t->c_lflag = lflag;
720 	t->c_cflag = cflag;
721 }
722 
723 /*
724  * ttcompatsetlflags
725  *
726  * Description:	Given a set of compatability terminal state local flags,
727  *		convert the compatability flags in the terminal flags
728  *		fields into canonical flags in the provided termios struct.
729  *
730  * Parameters:	struct tty *tp		The tty on which the operation is
731  *					being performed.
732  *		struct termios *t	The termios structure into which to
733  *					return the converted local flags.
734  *
735  * Returns:	void			(implicit: *t, modified)
736  */
737 static void
ttcompatsetlflags(struct tty * tp,struct termios * t)738 ttcompatsetlflags(struct tty *tp, struct termios *t)
739 {
740 	int flags = tp->t_flags;
741 	tcflag_t iflag  = t->c_iflag;
742 	tcflag_t oflag  = t->c_oflag;
743 	tcflag_t lflag  = t->c_lflag;
744 	tcflag_t cflag  = t->c_cflag;
745 
746 	iflag &= ~(PARMRK | IGNPAR | IGNCR | INLCR);
747 	if (flags & CRTERA) {
748 		lflag |= ECHOE;
749 	} else {
750 		lflag &= ~ECHOE;
751 	}
752 	if (flags & CRTKIL) {
753 		lflag |= ECHOKE;
754 	} else {
755 		lflag &= ~ECHOKE;
756 	}
757 	if (flags & PRTERA) {
758 		lflag |= ECHOPRT;
759 	} else {
760 		lflag &= ~ECHOPRT;
761 	}
762 	if (flags & CTLECH) {
763 		lflag |= ECHOCTL;
764 	} else {
765 		lflag &= ~ECHOCTL;
766 	}
767 	if (flags & TANDEM) {
768 		iflag |= IXOFF;
769 	} else {
770 		iflag &= ~IXOFF;
771 	}
772 	if ((flags & DECCTQ) == 0) {
773 		iflag |= IXANY;
774 	} else {
775 		iflag &= ~IXANY;
776 	}
777 	if (flags & MDMBUF) {
778 		cflag |= MDMBUF;
779 	} else {
780 		cflag &= ~MDMBUF;
781 	}
782 	if (flags & NOHANG) {
783 		cflag &= ~HUPCL;
784 	} else {
785 		cflag |= HUPCL;
786 	}
787 	lflag &= ~(TOSTOP | FLUSHO | PENDIN | NOFLSH);
788 	lflag |= flags & (TOSTOP | FLUSHO | PENDIN | NOFLSH);
789 
790 	/*
791 	 * The next if-else statement is copied from above so don't bother
792 	 * checking it separately.  We could avoid fiddlling with the
793 	 * character size if the mode is already RAW or if neither the
794 	 * LITOUT bit or the PASS8 bit is being changed, but the delta of
795 	 * the change is not available here and skipping the RAW case would
796 	 * make the code different from above.
797 	 */
798 	cflag &= ~(CSIZE | PARENB);
799 	if (flags & (RAW | LITOUT | PASS8)) {
800 		cflag |= CS8;
801 		if (!(flags & (RAW | PASS8))
802 		    || (flags & (RAW | PASS8 | ANYP)) == (PASS8 | ANYP)) {
803 			iflag |= ISTRIP;
804 		} else {
805 			iflag &= ~ISTRIP;
806 		}
807 		if (flags & (RAW | LITOUT)) {
808 			oflag &= ~OPOST;
809 		} else {
810 			oflag |= OPOST;
811 		}
812 	} else {
813 		cflag |= CS7 | PARENB;
814 		iflag |= ISTRIP;
815 		oflag |= OPOST;
816 	}
817 	t->c_iflag = iflag;
818 	t->c_oflag = oflag;
819 	t->c_lflag = lflag;
820 	t->c_cflag = cflag;
821 }
822