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