xref: /freebsd/contrib/tcsh/mi.termios.c (revision d3d381b2b194b4d24853e92eecef55f262688d1a)
1 /* $Header: /p/tcsh/cvsroot/tcsh/mi.termios.c,v 1.5 2006/03/02 18:46:44 christos Exp $ */
2 /* termios.c - fake termios interface using sgtty interface
3  * 	       by Magnus Doell and Bruce Evans.
4  *
5  */
6 #include "sh.h"
7 RCSID("$tcsh: mi.termios.c,v 1.5 2006/03/02 18:46:44 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 
46 static _PROTOTYPE( int tc_to_sg_speed, (speed_t speed) );
47 static _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 
57 speed_t cfgetispeed(termios_p)
58 struct termios *termios_p;
59 {
60     return termios_p->c_ispeed;
61 }
62 
63 speed_t cfgetospeed(termios_p)
64 struct termios *termios_p;
65 {
66     return termios_p->c_ospeed;
67 }
68 
69 speed_t cfsetispeed(termios_p, speed)
70 struct termios *termios_p;
71 speed_t speed;
72 {
73     termios_p->c_ispeed = speed;
74     return 0;
75 }
76 
77 speed_t cfsetospeed(termios_p, speed)
78 struct termios *termios_p;
79 speed_t speed;
80 {
81     termios_p->c_ospeed = speed;
82     return 0;
83 }
84 
85 static speed_t sg_to_tc_speed(speed)
86 int 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 
120 static int tc_to_sg_speed(speed)
121 speed_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 
153 int tcgetattr(filedes, termios_p)
154 int filedes;
155 struct 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 
284 int tcsetattr(filedes, opt_actions, termios_p)
285 int filedes;
286 int opt_actions;
287 struct 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