tty_compat.c revision 130840
1/*-
2 * Copyright (c) 1982, 1986, 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *	@(#)tty_compat.c	8.1 (Berkeley) 6/10/93
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/kern/tty_compat.c 130840 2004-06-21 12:28:56Z phk $");
34
35#include "opt_compat.h"
36
37/*
38 * mapping routines for old line discipline (yuck)
39 */
40#if defined(COMPAT_43)
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/ioctl_compat.h>
45#include <sys/tty.h>
46#include <sys/kernel.h>
47#include <sys/sysctl.h>
48
49static int ttcompatgetflags(struct tty *tp);
50static void ttcompatsetflags(struct tty *tp, struct termios *t);
51static void ttcompatsetlflags(struct tty *tp, struct termios *t);
52static int ttcompatspeedtab(int speed, struct speedtab *table);
53
54static int ttydebug = 0;
55SYSCTL_INT(_debug, OID_AUTO, ttydebug, CTLFLAG_RW, &ttydebug, 0, "");
56
57static struct speedtab compatspeeds[] = {
58#define MAX_SPEED	17
59	{ 115200, 17 },
60	{ 57600, 16 },
61	{ 38400, 15 },
62	{ 19200, 14 },
63	{ 9600,	13 },
64	{ 4800,	12 },
65	{ 2400,	11 },
66	{ 1800,	10 },
67	{ 1200,	9 },
68	{ 600,	8 },
69	{ 300,	7 },
70	{ 200,	6 },
71	{ 150,	5 },
72	{ 134,	4 },
73	{ 110,	3 },
74	{ 75,	2 },
75	{ 50,	1 },
76	{ 0,	0 },
77	{ -1,	-1 },
78};
79static int compatspcodes[] = {
80	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
81	1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200,
82};
83
84static int
85ttcompatspeedtab(int speed, struct speedtab *table)
86{
87	if (speed == 0)
88		return (0); /* hangup */
89	for ( ; table->sp_speed > 0; table++)
90		if (table->sp_speed <= speed) /* nearest one, rounded down */
91			return (table->sp_code);
92	return (1); /* 50, min and not hangup */
93}
94
95int
96ttsetcompat(struct tty *tp, u_long *com, caddr_t data, struct termios *term)
97{
98	switch (*com) {
99	case TIOCSETP:
100	case TIOCSETN: {
101		struct sgttyb *sg = (struct sgttyb *)data;
102		int speed;
103
104		if ((speed = sg->sg_ispeed) > MAX_SPEED || speed < 0)
105			return(EINVAL);
106		else if (speed != ttcompatspeedtab(tp->t_ispeed, compatspeeds))
107			term->c_ispeed = compatspcodes[speed];
108		else
109			term->c_ispeed = tp->t_ispeed;
110		if ((speed = sg->sg_ospeed) > MAX_SPEED || speed < 0)
111			return(EINVAL);
112		else if (speed != ttcompatspeedtab(tp->t_ospeed, compatspeeds))
113			term->c_ospeed = compatspcodes[speed];
114		else
115			term->c_ospeed = tp->t_ospeed;
116		term->c_cc[VERASE] = sg->sg_erase;
117		term->c_cc[VKILL] = sg->sg_kill;
118		tp->t_flags = (tp->t_flags&0xffff0000) | (sg->sg_flags&0xffff);
119		ttcompatsetflags(tp, term);
120		*com = (*com == TIOCSETP) ? TIOCSETAF : TIOCSETA;
121		break;
122	}
123	case TIOCSETC: {
124		struct tchars *tc = (struct tchars *)data;
125		cc_t *cc;
126
127		cc = term->c_cc;
128		cc[VINTR] = tc->t_intrc;
129		cc[VQUIT] = tc->t_quitc;
130		cc[VSTART] = tc->t_startc;
131		cc[VSTOP] = tc->t_stopc;
132		cc[VEOF] = tc->t_eofc;
133		cc[VEOL] = tc->t_brkc;
134		if (tc->t_brkc == (char)_POSIX_VDISABLE)
135			cc[VEOL2] = _POSIX_VDISABLE;
136		*com = TIOCSETA;
137		break;
138	}
139	case TIOCSLTC: {
140		struct ltchars *ltc = (struct ltchars *)data;
141		cc_t *cc;
142
143		cc = term->c_cc;
144		cc[VSUSP] = ltc->t_suspc;
145		cc[VDSUSP] = ltc->t_dsuspc;
146		cc[VREPRINT] = ltc->t_rprntc;
147		cc[VDISCARD] = ltc->t_flushc;
148		cc[VWERASE] = ltc->t_werasc;
149		cc[VLNEXT] = ltc->t_lnextc;
150		*com = TIOCSETA;
151		break;
152	}
153	case TIOCLBIS:
154	case TIOCLBIC:
155	case TIOCLSET:
156		if (*com == TIOCLSET)
157			tp->t_flags = (tp->t_flags&0xffff) | *(int *)data<<16;
158		else {
159			tp->t_flags =
160			 (ttcompatgetflags(tp)&0xffff0000)|(tp->t_flags&0xffff);
161			if (*com == TIOCLBIS)
162				tp->t_flags |= *(int *)data<<16;
163			else
164				tp->t_flags &= ~(*(int *)data<<16);
165		}
166		ttcompatsetlflags(tp, term);
167		*com = TIOCSETA;
168		break;
169	}
170	return 0;
171}
172
173/*ARGSUSED*/
174int
175ttcompat(struct tty *tp, u_long com, caddr_t data, int flag)
176{
177	switch (com) {
178	case TIOCSETP:
179	case TIOCSETN:
180	case TIOCSETC:
181	case TIOCSLTC:
182	case TIOCLBIS:
183	case TIOCLBIC:
184	case TIOCLSET: {
185		struct termios term;
186		int error;
187
188		term = tp->t_termios;
189		if ((error = ttsetcompat(tp, &com, data, &term)) != 0)
190			return error;
191		return ttioctl(tp, com, &term, flag);
192	}
193	case TIOCGETP: {
194		struct sgttyb *sg = (struct sgttyb *)data;
195		cc_t *cc = tp->t_cc;
196
197		sg->sg_ospeed = ttcompatspeedtab(tp->t_ospeed, compatspeeds);
198		if (tp->t_ispeed == 0)
199			sg->sg_ispeed = sg->sg_ospeed;
200		else
201			sg->sg_ispeed = ttcompatspeedtab(tp->t_ispeed, compatspeeds);
202		sg->sg_erase = cc[VERASE];
203		sg->sg_kill = cc[VKILL];
204		sg->sg_flags = tp->t_flags = ttcompatgetflags(tp);
205		break;
206	}
207	case TIOCGETC: {
208		struct tchars *tc = (struct tchars *)data;
209		cc_t *cc = tp->t_cc;
210
211		tc->t_intrc = cc[VINTR];
212		tc->t_quitc = cc[VQUIT];
213		tc->t_startc = cc[VSTART];
214		tc->t_stopc = cc[VSTOP];
215		tc->t_eofc = cc[VEOF];
216		tc->t_brkc = cc[VEOL];
217		break;
218	}
219	case TIOCGLTC: {
220		struct ltchars *ltc = (struct ltchars *)data;
221		cc_t *cc = tp->t_cc;
222
223		ltc->t_suspc = cc[VSUSP];
224		ltc->t_dsuspc = cc[VDSUSP];
225		ltc->t_rprntc = cc[VREPRINT];
226		ltc->t_flushc = cc[VDISCARD];
227		ltc->t_werasc = cc[VWERASE];
228		ltc->t_lnextc = cc[VLNEXT];
229		break;
230	}
231	case TIOCLGET:
232		tp->t_flags =
233		 (ttcompatgetflags(tp) & 0xffff0000UL)
234		   | (tp->t_flags & 0xffff);
235		*(int *)data = tp->t_flags>>16;
236		if (ttydebug)
237			printf("CLGET: returning %x\n", *(int *)data);
238		break;
239
240	case OTIOCGETD:
241		*(int *)data = tp->t_line ? tp->t_line : 2;
242		break;
243
244	case OTIOCSETD: {
245		int ldisczero = 0;
246
247		return (ttioctl(tp, TIOCSETD,
248			*(int *)data == 2 ? (caddr_t)&ldisczero : data, flag));
249	    }
250
251	case OTIOCCONS:
252		*(int *)data = 1;
253		return (ttioctl(tp, TIOCCONS, data, flag));
254
255	default:
256		return (ENOIOCTL);
257	}
258	return (0);
259}
260
261static int
262ttcompatgetflags(struct tty *tp)
263{
264	tcflag_t iflag	= tp->t_iflag;
265	tcflag_t lflag	= tp->t_lflag;
266	tcflag_t oflag	= tp->t_oflag;
267	tcflag_t cflag	= tp->t_cflag;
268	int flags = 0;
269
270	if (iflag&IXOFF)
271		flags |= TANDEM;
272	if (iflag&ICRNL || oflag&ONLCR)
273		flags |= CRMOD;
274	if ((cflag&CSIZE) == CS8) {
275		flags |= PASS8;
276		if (iflag&ISTRIP)
277			flags |= ANYP;
278	}
279	else if (cflag&PARENB) {
280		if (iflag&INPCK) {
281			if (cflag&PARODD)
282				flags |= ODDP;
283			else
284				flags |= EVENP;
285		} else
286			flags |= EVENP | ODDP;
287	}
288
289	if ((lflag&ICANON) == 0) {
290		/* fudge */
291		if (iflag&(INPCK|ISTRIP|IXON) || lflag&(IEXTEN|ISIG)
292		    || (cflag&(CSIZE|PARENB)) != CS8)
293			flags |= CBREAK;
294		else
295			flags |= RAW;
296	}
297	if (!(flags&RAW) && !(oflag&OPOST) && (cflag&(CSIZE|PARENB)) == CS8)
298		flags |= LITOUT;
299	if (cflag&MDMBUF)
300		flags |= MDMBUF;
301	if ((cflag&HUPCL) == 0)
302		flags |= NOHANG;
303	if (oflag&OXTABS)
304		flags |= XTABS;
305	if (lflag&ECHOE)
306		flags |= CRTERA|CRTBS;
307	if (lflag&ECHOKE)
308		flags |= CRTKIL|CRTBS;
309	if (lflag&ECHOPRT)
310		flags |= PRTERA;
311	if (lflag&ECHOCTL)
312		flags |= CTLECH;
313	if ((iflag&IXANY) == 0)
314		flags |= DECCTQ;
315	flags |= lflag&(ECHO|TOSTOP|FLUSHO|PENDIN|NOFLSH);
316	if (ttydebug)
317		printf("getflags: %x\n", flags);
318	return (flags);
319}
320
321static void
322ttcompatsetflags(struct tty *tp, struct termios *t)
323{
324	int flags = tp->t_flags;
325	tcflag_t iflag	= t->c_iflag;
326	tcflag_t oflag	= t->c_oflag;
327	tcflag_t lflag	= t->c_lflag;
328	tcflag_t cflag	= t->c_cflag;
329
330	if (flags & RAW) {
331		iflag = IGNBRK;
332		lflag &= ~(ECHOCTL|ISIG|ICANON|IEXTEN);
333	} else {
334		iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR);
335		iflag |= BRKINT|IXON|IMAXBEL;
336		lflag |= ISIG|IEXTEN|ECHOCTL;	/* XXX was echoctl on ? */
337		if (flags & XTABS)
338			oflag |= OXTABS;
339		else
340			oflag &= ~OXTABS;
341		if (flags & CBREAK)
342			lflag &= ~ICANON;
343		else
344			lflag |= ICANON;
345		if (flags&CRMOD) {
346			iflag |= ICRNL;
347			oflag |= ONLCR;
348		} else {
349			iflag &= ~ICRNL;
350			oflag &= ~ONLCR;
351		}
352	}
353	if (flags&ECHO)
354		lflag |= ECHO;
355	else
356		lflag &= ~ECHO;
357
358	cflag &= ~(CSIZE|PARENB);
359	if (flags&(RAW|LITOUT|PASS8)) {
360		cflag |= CS8;
361		if (!(flags&(RAW|PASS8))
362		    || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP))
363			iflag |= ISTRIP;
364		else
365			iflag &= ~ISTRIP;
366		if (flags&(RAW|LITOUT))
367			oflag &= ~OPOST;
368		else
369			oflag |= OPOST;
370	} else {
371		cflag |= CS7|PARENB;
372		iflag |= ISTRIP;
373		oflag |= OPOST;
374	}
375	/* XXX don't set INPCK if RAW or PASS8? */
376	if ((flags&(EVENP|ODDP)) == EVENP) {
377		iflag |= INPCK;
378		cflag &= ~PARODD;
379	} else if ((flags&(EVENP|ODDP)) == ODDP) {
380		iflag |= INPCK;
381		cflag |= PARODD;
382	} else
383		iflag &= ~INPCK;
384	if (flags&TANDEM)
385		iflag |= IXOFF;
386	else
387		iflag &= ~IXOFF;
388	if ((flags&DECCTQ) == 0)
389		iflag |= IXANY;
390	else
391		iflag &= ~IXANY;
392	t->c_iflag = iflag;
393	t->c_oflag = oflag;
394	t->c_lflag = lflag;
395	t->c_cflag = cflag;
396}
397
398static void
399ttcompatsetlflags(struct tty *tp, struct termios *t)
400{
401	int flags = tp->t_flags;
402	tcflag_t iflag	= t->c_iflag;
403	tcflag_t oflag	= t->c_oflag;
404	tcflag_t lflag	= t->c_lflag;
405	tcflag_t cflag	= t->c_cflag;
406
407	iflag &= ~(PARMRK|IGNPAR|IGNCR|INLCR);
408	if (flags&CRTERA)
409		lflag |= ECHOE;
410	else
411		lflag &= ~ECHOE;
412	if (flags&CRTKIL)
413		lflag |= ECHOKE;
414	else
415		lflag &= ~ECHOKE;
416	if (flags&PRTERA)
417		lflag |= ECHOPRT;
418	else
419		lflag &= ~ECHOPRT;
420	if (flags&CTLECH)
421		lflag |= ECHOCTL;
422	else
423		lflag &= ~ECHOCTL;
424	if (flags&TANDEM)
425		iflag |= IXOFF;
426	else
427		iflag &= ~IXOFF;
428	if ((flags&DECCTQ) == 0)
429		iflag |= IXANY;
430	else
431		iflag &= ~IXANY;
432	if (flags & MDMBUF)
433		cflag |= MDMBUF;
434	else
435		cflag &= ~MDMBUF;
436	if (flags&NOHANG)
437		cflag &= ~HUPCL;
438	else
439		cflag |= HUPCL;
440	lflag &= ~(TOSTOP|FLUSHO|PENDIN|NOFLSH);
441	lflag |= flags&(TOSTOP|FLUSHO|PENDIN|NOFLSH);
442
443	/*
444	 * The next if-else statement is copied from above so don't bother
445	 * checking it separately.  We could avoid fiddlling with the
446	 * character size if the mode is already RAW or if neither the
447	 * LITOUT bit or the PASS8 bit is being changed, but the delta of
448	 * the change is not available here and skipping the RAW case would
449	 * make the code different from above.
450	 */
451	cflag &= ~(CSIZE|PARENB);
452	if (flags&(RAW|LITOUT|PASS8)) {
453		cflag |= CS8;
454		if (!(flags&(RAW|PASS8))
455		    || (flags&(RAW|PASS8|ANYP)) == (PASS8|ANYP))
456			iflag |= ISTRIP;
457		else
458			iflag &= ~ISTRIP;
459		if (flags&(RAW|LITOUT))
460			oflag &= ~OPOST;
461		else
462			oflag |= OPOST;
463	} else {
464		cflag |= CS7|PARENB;
465		iflag |= ISTRIP;
466		oflag |= OPOST;
467	}
468	t->c_iflag = iflag;
469	t->c_oflag = oflag;
470	t->c_lflag = lflag;
471	t->c_cflag = cflag;
472}
473#endif	/* COMPAT_43 */
474