mi.termios.c revision 69408
1/* $Header: /src/pub/tcsh/mi.termios.c,v 1.4 2000/07/04 19:38:14 christos Exp $ */
2/* termios.c - fake termios interface using sgtty interface
3 * 	       by Magnus Doell and Bruce Evans.
4 *
5 */
6#include "sh.h"
7RCSID("$Id: mi.termios.c,v 1.4 2000/07/04 19:38:14 christos Exp $")
8
9#if defined(_MINIX) && !defined(_MINIX_VMD)
10
11
12/* Undefine everything that clashes with sgtty.h. */
13#undef B0
14#undef B50
15#undef B75
16#undef B110
17#undef B134
18#undef B150
19#undef B200
20#undef B300
21#undef B600
22#undef B1200
23#undef B1800
24#undef B2400
25#undef B4800
26#undef B9600
27#undef B19200
28#undef B28800
29#undef B38400
30#undef B57600
31#undef B115200
32/* Do not #undef CRMOD. We want a warning when they differ! */
33#undef ECHO
34/* Do not #undef XTABS. We want a warning when they differ! */
35
36/* Redefine some of the termios.h names just undefined with 'T_' prefixed
37 * to the name.  Don't bother with the low speeds - Minix does not support
38 * them.  Add support for higher speeds (speeds are now easy and don't need
39 * defines because they are not encoded).
40 */
41#define T_ECHO		000001
42
43#include <errno.h>
44#include <sgtty.h>
45
46static _PROTOTYPE( int tc_to_sg_speed, (speed_t speed) );
47static _PROTOTYPE( speed_t sg_to_tc_speed, (int speed) );
48#define B19200   192
49
50/* The speed get/set functions could be macros in the Minix implementation
51 * because there are speed fields in the structure with no fancy packing
52 * and it is not practical to check the values outside the driver.
53 * Where tests are necessary because the driver acts different from what
54 * POSIX requires, they are done in tcsetattr.
55 */
56
57speed_t cfgetispeed(termios_p)
58struct termios *termios_p;
59{
60    return termios_p->c_ispeed;
61}
62
63speed_t cfgetospeed(termios_p)
64struct termios *termios_p;
65{
66    return termios_p->c_ospeed;
67}
68
69speed_t cfsetispeed(termios_p, speed)
70struct termios *termios_p;
71speed_t speed;
72{
73    termios_p->c_ispeed = speed;
74    return 0;
75}
76
77speed_t cfsetospeed(termios_p, speed)
78struct termios *termios_p;
79speed_t speed;
80{
81    termios_p->c_ospeed = speed;
82    return 0;
83}
84
85static speed_t sg_to_tc_speed(speed)
86int speed;
87{
88    /* The speed encodings in sgtty.h and termios.h are different.  Both are
89     * inflexible.  Minix doesn't really support B0 but we map it through
90     * anyway.  It doesn't support B50, B75 or B134.
91     */
92    switch (speed) {
93	case B0: return 0;
94	case B110: return 110;
95	case B200: return 200;
96	case B300: return 300;
97	case B600: return 600;
98	case B1200: return 1200;
99	case B1800: return 1800;
100	case B2400: return 2400;
101	case B4800: return 4800;
102	case B9600: return 9600;
103	case B19200: return 19200;
104#ifdef B28800
105	case B28800: return 28800;
106#endif
107#ifdef B38400
108	case B38400: return 38400;
109#endif
110#ifdef B57600
111	case B57600: return 57600;
112#endif
113#ifdef B115200
114	case B115200: return 115200;
115#endif
116	default: return (speed_t)-1;
117    }
118}
119
120static int tc_to_sg_speed(speed)
121speed_t speed;
122{
123    /* Don't use a switch here in case the compiler is 16-bit and doesn't
124     * properly support longs (speed_t's) in switches.  It turns out the
125     * switch is larger and slower for most compilers anyway!
126     */
127    if (speed == 0) return 0;
128    if (speed == 110) return B110;
129    if (speed == 200) return B200;
130    if (speed == 300) return B300;
131    if (speed == 600) return B600;
132    if (speed == 1200) return B1200;
133    if (speed == 1800) return B1800;
134    if (speed == 2400) return B2400;
135    if (speed == 4800) return B4800;
136    if (speed == 9600) return B9600;
137    if (speed == 19200) return B19200;
138#ifdef B28800
139    if (speed == 28800) return B28800;
140#endif
141#ifdef B38400
142    if (speed == 38400) return B38400;
143#endif
144#ifdef B57600
145    if (speed == 57600) return B57600;
146#endif
147#ifdef B115200
148    if (speed == 115200) return B115200;
149#endif
150    return -1;
151}
152
153int tcgetattr(filedes, termios_p)
154int filedes;
155struct termios *termios_p;
156{
157    struct sgttyb sgbuf;
158    struct tchars tcbuf;
159
160    if (ioctl(filedes, TIOCGETP, &sgbuf) < 0
161	|| ioctl(filedes, TIOCGETC, (struct sgttyb *) &tcbuf) < 0)
162    {
163	return -1;
164    }
165
166    /* Minix input flags:
167     *   BRKINT:  forced off (break is not recognized)
168     *   IGNBRK:  forced on (break is not recognized)
169     *   ICRNL:   set if CRMOD is set and not RAW (CRMOD also controls output)
170     *   IGNCR:   forced off (ignoring cr's is not supported)
171     *   INLCR:   forced off (mapping nl's to cr's is not supported)
172     *   ISTRIP:  forced off (should be off for consoles, on for rs232 no RAW)
173     *   IXOFF:   forced off (rs232 uses CTS instead of XON/XOFF)
174     *   IXON:    forced on if not RAW
175     *   PARMRK:  forced off (no '\377', '\0', X sequence on errors)
176     * ? IGNPAR:  forced off (input with parity/framing errors is kept)
177     * ? INPCK:   forced off (input parity checking is not supported)
178     */
179    termios_p->c_iflag = IGNBRK;
180    if (!(sgbuf.sg_flags & RAW))
181    {
182	termios_p->c_iflag |= IXON;
183	if (sgbuf.sg_flags & CRMOD)
184	{
185	    termios_p->c_iflag |= ICRNL;
186	}
187    }
188
189    /* Minix output flags:
190     *   OPOST:   set if CRMOD or XTABS is set
191     *   XTABS:   copied from sg_flags
192     *   CRMOD:	  copied from sg_flags
193     */
194    termios_p->c_oflag = sgbuf.sg_flags & (CRMOD | XTABS);
195    if (termios_p->c_oflag)
196    {
197	termios_p->c_oflag |= OPOST;
198    }
199
200    /* Minix local flags:
201     *   ECHO:    set if ECHO is set
202     *   ECHOE:   set if ECHO is set (ERASE echoed as error-corecting backspace)
203     *   ECHOK:   set if ECHO is set ('\n' echoed after KILL char)
204     *   ECHONL:  forced off ('\n' not echoed when ECHO isn't set)
205     *   ICANON:  set if neither CBREAK nor RAW
206     *   IEXTEN:  forced off
207     *   ISIG:    set if not RAW
208     *   NOFLSH:  forced off (input/output queues are always flushed)
209     *   TOSTOP:  forced off (no job control)
210     */
211    termios_p->c_lflag = 0;
212    if (sgbuf.sg_flags & ECHO)
213    {
214	termios_p->c_lflag |= T_ECHO | ECHOE | ECHOK;
215    }
216    if (!(sgbuf.sg_flags & RAW))
217    {
218	termios_p->c_lflag |= ISIG;
219	if (!(sgbuf.sg_flags & CBREAK))
220	{
221	    termios_p->c_lflag |= ICANON;
222	}
223    }
224
225    /* Minix control flags:
226     *   CLOCAL:  forced on (ignore modem status lines - not quite right)
227     *   CREAD:   forced on (receiver is always enabled)
228     *   CSIZE:   CS5-CS8 correspond directly to BITS5-BITS8
229     *   CSTOPB:  set for B110 (driver will generate 2 stop-bits than)
230     *   HUPCL:   forced off
231     *   PARENB:  set if EVENP or ODDP is set
232     *   PARODD:  set if ODDP is set
233     */
234    termios_p->c_cflag = CLOCAL | CREAD;
235    switch (sgbuf.sg_flags & BITS8)
236    {
237	case BITS5: termios_p->c_cflag |= CS5; break;
238	case BITS6: termios_p->c_cflag |= CS6; break;
239	case BITS7: termios_p->c_cflag |= CS7; break;
240	case BITS8: termios_p->c_cflag |= CS8; break;
241    }
242    if (sgbuf.sg_flags & ODDP)
243    {
244	termios_p->c_cflag |= PARENB | PARODD;
245    }
246    if (sgbuf.sg_flags & EVENP)
247    {
248	termios_p->c_cflag |= PARENB;
249    }
250    if (sgbuf.sg_ispeed == B110)
251    {
252	termios_p->c_cflag |= CSTOPB;
253    }
254
255    /* Minix may give back different input and output baudrates,
256     * but only the input baudrate is valid for both.
257     * As our termios emulation will fail, if input baudrate differs
258     * from output baudrate, force them to be equal.
259     * Otherwise it would be very suprisingly not to be able to set
260     * the terminal back to the state returned by tcgetattr :).
261     */
262    termios_p->c_ospeed =
263    termios_p->c_ispeed =
264		sg_to_tc_speed((unsigned char) sgbuf.sg_ispeed);
265
266    /* Minix control characters correspond directly except VSUSP and the
267     * important VMIN and VTIME are not really supported.
268     */
269    termios_p->c_cc[VEOF] = tcbuf.t_eofc;
270    termios_p->c_cc[VEOL] = tcbuf.t_brkc;
271    termios_p->c_cc[VERASE] = sgbuf.sg_erase;
272    termios_p->c_cc[VINTR] = tcbuf.t_intrc;
273    termios_p->c_cc[VKILL] = sgbuf.sg_kill;
274    termios_p->c_cc[VQUIT] = tcbuf.t_quitc;
275    termios_p->c_cc[VSTART] = tcbuf.t_startc;
276    termios_p->c_cc[VSTOP] = tcbuf.t_stopc;
277    termios_p->c_cc[VMIN] = 1;
278    termios_p->c_cc[VTIME] = 0;
279    termios_p->c_cc[VSUSP] = 0;
280
281    return 0;
282}
283
284int tcsetattr(filedes, opt_actions, termios_p)
285int filedes;
286int opt_actions;
287struct termios *termios_p;
288{
289    struct sgttyb sgbuf;
290    struct tchars tcbuf;
291    int sgspeed;
292
293    /* Posix 1003.1-1988 page 135 says:
294     * Attempts to set unsupported baud rates shall be ignored, and it is
295     * implementation-defined whether an error is returned by any or all of
296     * cfsetispeed(), cfsetospeed(), or tcsetattr(). This refers both to
297     * changes to baud rates not supported by the hardware, and to changes
298     * setting the input and output baud rates to different values if the
299     * hardware does not support it.
300     * Ignoring means not to change the existing settings, doesn't it?
301     */
302    if ((termios_p->c_ispeed != 0 && termios_p->c_ispeed != termios_p->c_ospeed)
303	|| (sgspeed = tc_to_sg_speed(termios_p->c_ospeed)) < 0)
304    {
305	errno = EINVAL;
306	return -1;
307    }
308
309    sgbuf.sg_ispeed = sgbuf.sg_ospeed = sgspeed;
310    sgbuf.sg_flags = 0;
311
312    /* I don't know what should happen with requests that are not supported by
313     * old Minix drivers and therefore cannot be emulated.
314     * Returning an error may confuse the application (the values aren't really
315     * invalid or unsupported by the hardware, they just couldn't be satisfied
316     * by the driver). Not returning an error might be even worse because the
317     * driver will act different to what the application requires it to act
318     * after sucessfully setting the attributes as specified.
319     * Settings that cannot be emulated fully include:
320     *   c_ospeed != 110 && c_cflag & CSTOPB
321     *   c_ospeed == 110 && ! c_cflag & CSTOPB
322     *   (c_cc[VMIN] != 1 || c_cc[VTIME] != 0) && ! c_lflag & ICANON
323     *   c_lflag & ICANON && ! c_lflag & ISIG
324     * For the moment I just ignore these conflicts.
325     */
326
327    if (termios_p->c_oflag & OPOST)
328    {
329	/* CRMOD isn't Posix and may conflict with ICRNL, which is Posix,
330	 * so we just ignore it.
331	 */
332	if (termios_p->c_oflag & XTABS)
333	{
334		sgbuf.sg_flags |= XTABS;
335	}
336    }
337
338    if (termios_p->c_iflag & ICRNL)
339    {
340	/* We couldn't do it better :-(. */
341	sgbuf.sg_flags |= CRMOD;
342    }
343
344    if (termios_p->c_lflag & T_ECHO)
345    {
346	sgbuf.sg_flags |= ECHO;
347    }
348    if (!(termios_p->c_lflag & ICANON))
349    {
350	if (termios_p->c_lflag & ISIG)
351	{
352	     sgbuf.sg_flags |= CBREAK;
353	}
354	else
355	{
356	     sgbuf.sg_flags |= RAW;
357	}
358    }
359
360    switch (termios_p->c_cflag & CSIZE)
361    {
362	case CS5: sgbuf.sg_flags |= BITS5; break;
363	case CS6: sgbuf.sg_flags |= BITS6; break;
364	case CS7: sgbuf.sg_flags |= BITS7; break;
365	case CS8: sgbuf.sg_flags |= BITS8; break;
366    }
367    if (termios_p->c_cflag & PARENB)
368    {
369	if (termios_p->c_cflag & PARODD)
370	{
371	    sgbuf.sg_flags |= ODDP;
372	}
373	else
374	{
375	    sgbuf.sg_flags |= EVENP;
376	}
377    }
378
379    sgbuf.sg_erase = termios_p->c_cc[VERASE];
380    sgbuf.sg_kill = termios_p->c_cc[VKILL];
381
382    tcbuf.t_intrc = termios_p->c_cc[VINTR];
383    tcbuf.t_quitc = termios_p->c_cc[VQUIT];
384    tcbuf.t_startc = termios_p->c_cc[VSTART];
385    tcbuf.t_stopc = termios_p->c_cc[VSTOP];
386    tcbuf.t_eofc = termios_p->c_cc[VEOF];
387    tcbuf.t_brkc = termios_p->c_cc[VEOL];
388
389    return ioctl(filedes, TIOCSETP, &sgbuf) < 0 &&
390	   ioctl(filedes, TIOCSETC, (struct sgttyb *) &tcbuf) < 0 ?
391		-1 : 0;
392}
393#endif /* _MINIX && !_MINIX_VMD */
394