xref: /freebsd/contrib/tcsh/mi.termios.c (revision 78373a5faa3bab46d247c10a7059e484bc634504)
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 
44 static _PROTOTYPE( int tc_to_sg_speed, (speed_t speed) );
45 static _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 
55 speed_t cfgetispeed(termios_p)
56 struct termios *termios_p;
57 {
58     return termios_p->c_ispeed;
59 }
60 
61 speed_t cfgetospeed(termios_p)
62 struct termios *termios_p;
63 {
64     return termios_p->c_ospeed;
65 }
66 
67 speed_t cfsetispeed(termios_p, speed)
68 struct termios *termios_p;
69 speed_t speed;
70 {
71     termios_p->c_ispeed = speed;
72     return 0;
73 }
74 
75 speed_t cfsetospeed(termios_p, speed)
76 struct termios *termios_p;
77 speed_t speed;
78 {
79     termios_p->c_ospeed = speed;
80     return 0;
81 }
82 
83 static speed_t sg_to_tc_speed(speed)
84 int 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 
118 static int tc_to_sg_speed(speed)
119 speed_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 
151 int tcgetattr(filedes, termios_p)
152 int filedes;
153 struct 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 
282 int tcsetattr(filedes, opt_actions, termios_p)
283 int filedes;
284 int opt_actions;
285 struct 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