unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / io / serial / v2_0 / src / common / termiostty.c
1 //==========================================================================
2 //
3 //      termiostty.c
4 //
5 //      POSIX Termios compatible TTY I/O driver
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2003 Jonathan Larmour
13 // Copyright (C) 2003 Gary Thomas
14 //
15 // eCos is free software; you can redistribute it and/or modify it under
16 // the terms of the GNU General Public License as published by the Free
17 // Software Foundation; either version 2 or (at your option) any later version.
18 //
19 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
20 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
22 // for more details.
23 //
24 // You should have received a copy of the GNU General Public License along
25 // with eCos; if not, write to the Free Software Foundation, Inc.,
26 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 //
28 // As a special exception, if other files instantiate templates or use macros
29 // or inline functions from this file, or you compile this file and link it
30 // with other works to produce a work based on this file, this file does not
31 // by itself cause the resulting work to be covered by the GNU General Public
32 // License. However the source code for this file must still be made available
33 // in accordance with section (3) of the GNU General Public License.
34 //
35 // This exception does not invalidate any other reasons why a work based on
36 // this file might be covered by the GNU General Public License.
37 //
38 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
39 // at http://sources.redhat.com/ecos/ecos-license/
40 // -------------------------------------------
41 //####ECOSGPLCOPYRIGHTEND####
42 //==========================================================================
43 //#####DESCRIPTIONBEGIN####
44 //
45 // Author(s):    jlarmour
46 // Contributors: gthomas
47 // Date:         2000-07-22
48 // Purpose:      Device driver for termios emulation tty I/O, layered on
49 //               top of serial I/O
50 // Description:  TODO: Add OPOST support for 80x25 (configurable) windows
51 //               TODO: Support _POSIX_VDISABLE
52 //
53 //####DESCRIPTIONEND####
54 //
55 //==========================================================================
56
57 // CONFIGURATION
58
59 #include <pkgconf/io_serial.h>
60
61 #ifdef CYGPKG_IO_SERIAL_TERMIOS
62
63 // INCLUDES
64
65 #include <cyg/infra/cyg_type.h>    // Common types 
66 #include <cyg/infra/cyg_ass.h>     // Assertion support
67 #include <cyg/infra/cyg_trac.h>    // Tracing support
68 #include <cyg/io/io.h>
69 #include <cyg/io/devtab.h>
70 #include <cyg/io/serialio.h>       // public serial API
71 #include <termios.h>               // Termios header
72 #include <cyg/hal/drv_api.h>
73 #include <stdlib.h>                // malloc
74 #include <string.h>
75 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
76 # include <signal.h>
77 #endif
78
79 //==========================================================================
80 // FUNCTION PROTOTYPES
81
82 static bool
83 termios_init(struct cyg_devtab_entry *tab);
84
85 static Cyg_ErrNo
86 termios_lookup(struct cyg_devtab_entry **tab, 
87                struct cyg_devtab_entry *sub_tab,
88                const char *name);
89 static Cyg_ErrNo
90 termios_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len);
91 static Cyg_ErrNo
92 termios_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len);
93 static cyg_bool
94 termios_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info);
95 static Cyg_ErrNo 
96 termios_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf,
97                    cyg_uint32 *len);
98 static Cyg_ErrNo 
99 termios_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf,
100                    cyg_uint32 *len);
101
102 //==========================================================================
103 // TYPE DEFINITIONS
104
105 struct termios_private_info {
106     struct termios  termios;
107     cyg_io_handle_t dev_handle;
108     cyg_drv_mutex_t lock;
109     cyg_bool        init;
110 };
111
112 typedef struct {
113     struct termios *termios_p;
114     int optact;
115 } setattr_struct;
116
117
118 //==========================================================================
119 // STATIC OBJECTS
120
121 static DEVIO_TABLE(termios_devio,
122                    termios_write,
123                    termios_read,
124                    termios_select,
125                    termios_get_config,
126                    termios_set_config);
127
128 #ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS0
129 static struct termios_private_info termios_private_info0;
130 DEVTAB_ENTRY(termios_io0, 
131              "/dev/termios0",
132              CYGDAT_IO_SERIAL_TERMIOS_TERMIOS0_DEV,
133              &termios_devio,
134              termios_init, 
135              termios_lookup,
136              &termios_private_info0);
137 #endif
138
139 #ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS1
140 static struct termios_private_info termios_private_info1;
141 DEVTAB_ENTRY(termios_io1, 
142              "/dev/termios1", 
143              CYGDAT_IO_SERIAL_TERMIOS_TERMIOS1_DEV,
144              &termios_devio, 
145              termios_init, 
146              termios_lookup,
147              &termios_private_info1);
148 #endif
149
150 #ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS2
151 static struct termios_private_info termios_private_info2;
152 DEVTAB_ENTRY(termios_io2, 
153              "/dev/termios2", 
154              CYGDAT_IO_SERIAL_TERMIOS_TERMIOS2_DEV,
155              &termios_devio, 
156              termios_init, 
157              termios_lookup,
158              &termios_private_info2);
159 #endif
160
161 #ifdef CYGPKG_IO_SERIAL_TERMIOS_TERMIOS3
162 static struct termios_private_info termios_private_info3;
163 DEVTAB_ENTRY(termios_io3, 
164              "/dev/termios3", 
165              CYGDAT_IO_SERIAL_TERMIOS_TERMIOS3_DEV,
166              &termios_devio, 
167              termios_init, 
168              termios_lookup,
169              &termios_private_info3);
170 #endif
171
172 static const cc_t c_cc_init[ NCCS ] = { 
173     0x04,     /* EOF == ^D */
174     0,        /* EOL */
175     0x08,     /* ERASE = BS ; NB DEL=0x7f */
176     0x03,     /* INTR = ^C */
177     0x15,     /* KILL = ^U */
178     0,        /* MIN = 0 */
179     0x1c,     /* QUIT = ^\ */
180     0x1a,     /* SUSP = ^Z ; NB ignored in this impl - no job control */
181     0,        /* TIME = 0 */
182 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
183     CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR,
184     CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR,
185 #else
186     17,
187     19,
188 #endif
189 };
190
191 // map eCos bitrates to POSIX bitrates.
192 static speed_t ecosbaud2posixbaud[] = {
193     0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B3600,
194     B4800, B7200, B9600, B14400, B19200, B38400, B57600, B115200, B230400 };
195
196 // map POSIX bitrates to eCos bitrates.
197 static cyg_serial_baud_rate_t posixbaud2ecosbaud[] = {
198     0, CYGNUM_SERIAL_BAUD_50, CYGNUM_SERIAL_BAUD_75, CYGNUM_SERIAL_BAUD_110,
199     CYGNUM_SERIAL_BAUD_134_5, CYGNUM_SERIAL_BAUD_150, CYGNUM_SERIAL_BAUD_200,
200     CYGNUM_SERIAL_BAUD_300, CYGNUM_SERIAL_BAUD_600, CYGNUM_SERIAL_BAUD_1200,
201     CYGNUM_SERIAL_BAUD_1800, CYGNUM_SERIAL_BAUD_2400, CYGNUM_SERIAL_BAUD_3600,
202     CYGNUM_SERIAL_BAUD_4800, CYGNUM_SERIAL_BAUD_7200, CYGNUM_SERIAL_BAUD_9600,
203     CYGNUM_SERIAL_BAUD_14400, CYGNUM_SERIAL_BAUD_19200,
204     CYGNUM_SERIAL_BAUD_38400, CYGNUM_SERIAL_BAUD_57600,
205     CYGNUM_SERIAL_BAUD_115200, CYGNUM_SERIAL_BAUD_230400 };
206
207
208 //==========================================================================
209 // FUNCTIONS
210
211 static __inline__ speed_t
212 map_ecosbaud_to_posixbaud( cyg_serial_baud_rate_t ebaud )
213 {
214     if ( ebaud > (sizeof(ecosbaud2posixbaud) / sizeof(speed_t)) )
215         return 0;
216     return ecosbaud2posixbaud[ ebaud ];
217 }
218
219 static __inline__ cyg_serial_baud_rate_t
220 map_posixbaud_to_ecosbaud( speed_t pbaud )
221 {
222     if ( pbaud > (sizeof(posixbaud2ecosbaud)/sizeof(cyg_serial_baud_rate_t)) )
223         return 0;
224     return posixbaud2ecosbaud[ pbaud ];
225 }
226
227 //==========================================================================
228 // real_termios_init is used to initialize the termios structure. This is
229 // called at lookup time, and not from termios_init() because it needs
230 // to query the serial device which may not be set up yet at that point
231 // in termios_init()
232
233 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
234 # define C_IFLAG_INIT (ICRNL|IGNBRK|BRKINT)
235 #else
236 # define C_IFLAG_INIT (ICRNL|IGNBRK)
237 #endif
238 #define C_OFLAG_INIT (ONLCR)
239 #define C_CFLAG_INIT (CREAD)
240 #define C_LFLAG_INIT (ECHO|ECHOE|ECHOK|ICANON)
241
242 static Cyg_ErrNo
243 real_termios_init( struct termios_private_info *priv )
244 {
245     Cyg_ErrNo res;
246     struct termios *t;
247     cyg_serial_info_t dev_conf;
248     cyg_serial_buf_info_t dev_buf_conf;
249     cyg_uint32 len = sizeof( dev_conf );
250
251     CYG_REPORT_FUNCTYPE("returning %d");
252     CYG_REPORT_FUNCARG1XV( priv );
253     CYG_CHECK_DATA_PTRC( priv );
254
255     t = &priv->termios;
256
257     // Get info from driver
258     res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
259                              &dev_conf, &len );
260     if ( ENOERR == res ) {
261         len = sizeof( dev_buf_conf );
262         res = cyg_io_get_config( priv->dev_handle,
263                                  CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
264                                  &dev_buf_conf, &len );
265     }
266
267     if ( ENOERR != res ) {
268         CYG_REPORT_RETVAL( res );
269         return res;
270     }
271     
272     // we only support symmetric baud rates
273     t->c_ispeed = t->c_ospeed = map_ecosbaud_to_posixbaud( dev_conf.baud );
274     t->c_iflag = C_IFLAG_INIT;
275     t->c_oflag = C_OFLAG_INIT;
276     t->c_cflag = C_CFLAG_INIT;
277     t->c_lflag = C_LFLAG_INIT;
278     memcpy( t->c_cc, c_cc_init, sizeof( t->c_cc ) );
279     
280     switch ( dev_conf.parity ) {
281     case CYGNUM_SERIAL_PARITY_NONE:
282         t->c_iflag |= IGNPAR;
283         break;
284     case CYGNUM_SERIAL_PARITY_ODD:
285         t->c_cflag |= PARODD;
286         // DROPTHROUGH
287     case CYGNUM_SERIAL_PARITY_EVEN:
288         t->c_iflag |= PARENB;
289         break;
290     default:
291         CYG_FAIL( "Unsupported default parity" );
292         break;
293     }
294
295     switch( dev_conf.word_length ) {
296     case CYGNUM_SERIAL_WORD_LENGTH_5:
297         t->c_cflag |= CS5;
298         break;        
299     case CYGNUM_SERIAL_WORD_LENGTH_6:
300         t->c_cflag |= CS6;
301         break;
302     case CYGNUM_SERIAL_WORD_LENGTH_7:
303         t->c_cflag |= CS7;
304         break;
305     case CYGNUM_SERIAL_WORD_LENGTH_8:
306         t->c_cflag |= CS8;
307         break;
308     default:
309         CYG_FAIL( "Unsupported word length" );
310         break;
311     }
312
313     switch ( dev_conf.stop ) {
314     case CYGNUM_SERIAL_STOP_1:
315         // Don't need to do anything
316         break;
317     case CYGNUM_SERIAL_STOP_2:
318         t->c_cflag |= CSTOPB;
319         break;
320     default:
321         CYG_FAIL( "Unsupported number of stop bits" );
322         break;
323     }
324
325     switch ( dev_conf.flags ) {
326     case CYGNUM_SERIAL_FLOW_RTSCTS_RX:
327         t->c_cflag |= CRTSCTS;
328         // drop through
329     case CYGNUM_SERIAL_FLOW_XONXOFF_RX:
330         t->c_iflag |= IXOFF;
331         break;
332     case CYGNUM_SERIAL_FLOW_RTSCTS_TX:
333         t->c_cflag |= CRTSCTS;
334         // drop through
335     case CYGNUM_SERIAL_FLOW_XONXOFF_TX:
336         t->c_iflag |= IXON;
337         break;
338     default:
339         // Ignore flags we don't grok
340         break;
341     }
342
343     return ENOERR;
344 } // real_termios_init()
345
346 //==========================================================================
347 // set_attr() actually enacts the termios config. We come in here with
348 // the mutex in priv locked
349 //
350 // Possible deviation from standard: POSIX says we should enact which ever
351 // bits we can and only return EINVAL when none of them can be performed
352 // Rather than tracking whether *none* of them worked, instead we just
353 // always claim success. At the very least, setting c_cc is never to
354 // fail so I'm not sure if this is really non-standard or not!
355
356 static Cyg_ErrNo
357 set_attr( struct termios *t, struct termios_private_info *priv )
358 {
359     Cyg_ErrNo res = ENOERR;
360     cyg_serial_info_t dev_conf, new_dev_conf;
361     cyg_uint32 len = sizeof( dev_conf );
362     cc_t *tempcc = &priv->termios.c_cc[0];
363     struct termios *ptermios = &priv->termios;
364     
365     // Get info from driver
366     res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
367                              &dev_conf, &len );
368
369     if ( ENOERR != res )
370         return res;
371         
372     // We need to set up each facet of config to change one by one because
373     // POSIX says we should try and change as much of the config as possible
374     // This is tedious and has to be done by steam :-(
375
376     if ( t->c_ospeed != ptermios->c_ospeed ) {
377         new_dev_conf = dev_conf;
378         new_dev_conf.baud = map_posixbaud_to_ecosbaud( t->c_ospeed );
379         if ( 0 != new_dev_conf.baud ) {
380             len = sizeof( new_dev_conf );
381             res = cyg_io_set_config( priv->dev_handle,
382                                      CYG_IO_SET_CONFIG_SERIAL_INFO,
383                                      &new_dev_conf, &len );
384             if ( ENOERR == res ) {
385                 // It worked, so update dev_conf to reflect the new state
386                 dev_conf.baud = new_dev_conf.baud;
387                 // and termios
388                 ptermios->c_ispeed = t->c_ospeed;
389                 ptermios->c_ospeed = t->c_ospeed;
390             }
391         }
392     }
393
394     if ( (t->c_cflag & CSTOPB) != (ptermios->c_cflag & CSTOPB) ) {
395         new_dev_conf = dev_conf;
396         if ( t->c_cflag & CSTOPB )
397             new_dev_conf.stop = CYGNUM_SERIAL_STOP_2;
398         else
399             new_dev_conf.stop = CYGNUM_SERIAL_STOP_1;
400         
401         len = sizeof( new_dev_conf );
402         res = cyg_io_set_config( priv->dev_handle,
403                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
404                                  &new_dev_conf, &len );
405         if ( ENOERR == res ) {
406             // It worked, so update dev_conf to reflect the new state
407             dev_conf.stop = new_dev_conf.stop;
408             // and termios
409             ptermios->c_cflag &= ~CSTOPB;
410             ptermios->c_cflag |= t->c_cflag & CSTOPB;
411         }
412     }
413
414     if ( ((t->c_cflag & PARENB) != (ptermios->c_cflag & PARENB)) ||
415          ((t->c_cflag & PARODD) != (ptermios->c_cflag & PARODD)) ) {
416         new_dev_conf = dev_conf;
417         if ( t->c_cflag & PARENB )
418             if ( t->c_cflag & PARODD )
419                 new_dev_conf.parity = CYGNUM_SERIAL_PARITY_ODD;
420             else
421                 new_dev_conf.parity = CYGNUM_SERIAL_PARITY_EVEN;
422         else
423             new_dev_conf.parity = CYGNUM_SERIAL_PARITY_NONE;
424         
425         len = sizeof( new_dev_conf );
426         res = cyg_io_set_config( priv->dev_handle,
427                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
428                                  &new_dev_conf, &len );
429         if ( ENOERR == res ) {
430             // It worked, so update dev_conf to reflect the new state
431             dev_conf.parity = new_dev_conf.parity;
432             // and termios
433             ptermios->c_cflag &= ~(PARENB|PARODD);
434             ptermios->c_cflag |= t->c_cflag & (PARENB|PARODD);
435         }
436     }
437
438     if ( (t->c_cflag & CSIZE) != (ptermios->c_cflag & CSIZE) ) {
439         new_dev_conf = dev_conf;
440         switch ( t->c_cflag & CSIZE ) {
441         case CS5:
442             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_5;
443             break;
444         case CS6:
445             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_6;
446             break;
447         case CS7:
448             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_7;
449             break;
450         case CS8:
451             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_8;
452             break;
453         }
454         
455         len = sizeof( new_dev_conf );
456         res = cyg_io_set_config( priv->dev_handle,
457                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
458                                  &new_dev_conf, &len );
459         if ( ENOERR == res ) {
460             // It worked, so update dev_conf to reflect the new state
461             dev_conf.word_length = new_dev_conf.word_length;
462             // and termios
463             ptermios->c_cflag &= ~CSIZE;
464             ptermios->c_cflag |= t->c_cflag & CSIZE;
465         }
466     }
467
468     if ( (t->c_cflag & IXOFF) != (ptermios->c_cflag & IXOFF) ) {
469         new_dev_conf = dev_conf;
470         new_dev_conf.flags &=
471             ~(CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_RTSCTS_RX);
472         if ( t->c_cflag & IXOFF )
473             if ( t->c_cflag & CRTSCTS)
474                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_RX;
475             else
476                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_RX;
477         else
478             new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
479         
480         len = sizeof( new_dev_conf );
481         res = cyg_io_set_config( priv->dev_handle,
482                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
483                                  &new_dev_conf, &len );
484         if ( ENOERR == res ) {
485             // It worked, so update dev_conf to reflect the new state
486             dev_conf.flags = new_dev_conf.flags;
487             // and termios
488             ptermios->c_cflag &= ~(IXOFF|CRTSCTS);
489             ptermios->c_cflag |= t->c_cflag & (IXOFF|CRTSCTS);
490         }
491     }
492
493     if ( (t->c_cflag & IXON) != (ptermios->c_cflag & IXON) ) {
494         new_dev_conf = dev_conf;
495         new_dev_conf.flags &=
496             ~(CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_RTSCTS_TX);
497         if ( t->c_cflag & IXON )
498             if ( t->c_cflag & CRTSCTS)
499                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_TX;
500             else
501                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_TX;
502         else
503             new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
504         
505         len = sizeof( new_dev_conf );
506         res = cyg_io_set_config( priv->dev_handle,
507                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
508                                  &new_dev_conf, &len );
509         if ( ENOERR == res ) {
510             // It worked, so update dev_conf to reflect the new state
511             dev_conf.flags = new_dev_conf.flags;
512             // and termios
513             ptermios->c_cflag &= ~(IXON|CRTSCTS);
514             ptermios->c_cflag |= t->c_cflag & (IXON|CRTSCTS);
515         }
516     }
517
518     // Input/Output processing flags can just be set as we grok them all
519     // with few exceptions (see lflags below)
520     ptermios->c_iflag &= ~(BRKINT|ICRNL|IGNBRK|IGNCR|IGNPAR|INLCR|INPCK|
521                            ISTRIP|PARMRK);
522     ptermios->c_iflag |= t->c_iflag & (
523 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
524                                        BRKINT|
525 #endif
526                                        ICRNL|IGNBRK|IGNCR|IGNPAR|
527                                        INLCR|INPCK|ISTRIP|PARMRK );
528     
529     ptermios->c_oflag &= ~(OPOST|ONLCR);
530     ptermios->c_oflag |= t->c_oflag & (OPOST|ONLCR);
531
532     ptermios->c_cflag &= ~(CLOCAL|CREAD|HUPCL);
533     ptermios->c_cflag |= t->c_cflag & (CLOCAL|CREAD|HUPCL);
534
535     ptermios->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|
536                                  IEXTEN|ISIG|NOFLSH|TOSTOP);
537     // Note we don't support IEXTEN nor TOSTOP so we don't set them
538     ptermios->c_lflag |= t->c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ICANON|
539 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
540                                        ISIG|
541 #endif
542                                        NOFLSH);
543
544     // control characters. We don't support changing of VSTART, VSTOP,
545     // VTIME or VSUSP though
546     tempcc[VEOF]   = t->c_cc[VEOF];
547     tempcc[VEOL]   = t->c_cc[VEOL];
548     tempcc[VERASE] = t->c_cc[VERASE];
549     tempcc[VINTR]  = t->c_cc[VINTR];
550     tempcc[VKILL]  = t->c_cc[VKILL];
551     tempcc[VMIN]   = t->c_cc[VMIN];
552     tempcc[VQUIT]  = t->c_cc[VQUIT];
553         
554     return res;
555 }
556
557 //==========================================================================
558
559 static bool 
560 termios_init(struct cyg_devtab_entry *tab)
561 {
562     // can't initialize the termios structure because we can't
563     // query the serial driver yet. Wait until lookup time.
564
565     return true;
566 } // termios_init()
567
568 //==========================================================================
569
570 static Cyg_ErrNo 
571 termios_lookup(struct cyg_devtab_entry **tab, 
572            struct cyg_devtab_entry *sub_tab,
573            const char *name)
574 {
575     cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
576     struct termios_private_info *priv =
577         (struct termios_private_info *)(*tab)->priv;
578     Cyg_ErrNo err = ENOERR;
579     
580     if ( !priv->init ) {
581         cyg_drv_mutex_lock( &priv->lock );
582         if ( !priv->init ) {  // retest as we may have been pre-empted
583             priv->init = true;
584             priv->dev_handle = chan;
585             err = real_termios_init( priv );
586         }
587         cyg_drv_mutex_unlock( &priv->lock );
588     }
589     return err;
590 }
591
592 //==========================================================================
593
594 #define WRITE_BUFSIZE 100 // FIXME: ->CDL
595 // #define MAX_CANON 64  FIXME: relevance?
596
597
598 static Cyg_ErrNo 
599 termios_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
600 {
601     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
602     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
603     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
604     cyg_int32 xbufsize, input_bytes_read;
605     cyg_uint8 xbuf[WRITE_BUFSIZE];
606     cyg_uint8 *buf = (cyg_uint8 *)_buf;
607     Cyg_ErrNo res;
608
609     xbufsize = input_bytes_read = 0;
610     while (input_bytes_read++ < *len) {
611         if ( (*buf == '\n') && (priv->termios.c_oflag & (OPOST|ONLCR)) ) {
612             xbuf[xbufsize++] = '\r';
613         }
614         xbuf[xbufsize++] = *buf;
615         if ((xbufsize >= (WRITE_BUFSIZE-1)) || (input_bytes_read == *len) ||
616             (*buf == '\n'))
617         {
618             cyg_uint32 size = xbufsize;
619             res = cyg_io_write(chan, xbuf, &size);
620             if (res != ENOERR) {
621                 *len = input_bytes_read - (xbufsize - size);
622                 return res;
623             }
624             xbufsize = 0;
625         }
626         buf++;
627     }
628     // Everything sent, so *len is correct.
629     return ENOERR;
630 }
631
632
633 //==========================================================================
634
635 static Cyg_ErrNo 
636 termios_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
637 {
638     cyg_devtab_entry_t *dt = (cyg_devtab_entry_t *)handle;
639     struct termios_private_info *priv = (struct termios_private_info *)dt->priv;
640     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
641     struct termios *t = &priv->termios;
642     cyg_uint32 clen;
643     cyg_uint32 size;
644     Cyg_ErrNo res;
645     cyg_uint8 c;
646     cyg_uint8 *buf = (cyg_uint8 *)_buf;
647     cyg_bool discardc; // should c be discarded (not read, not printed)
648     cyg_bool returnnow = false; // return back to user after this char
649     
650     // if receiver off
651     if (0 == (t->c_cflag & CREAD) ) {
652         *len = 0;
653         return -EINVAL;
654     }
655
656     size = 0;
657     if ( 0 == (t->c_lflag & ICANON) ) {
658         // In non-canonical mode we return the min of *len and the
659         // number of bytes available
660         // So we query the driver for how many bytes are available - this
661         // guarantees we won't block
662         cyg_serial_buf_info_t dev_buf_conf;
663         cyg_uint32 dbc_len = sizeof( dev_buf_conf );
664         res = cyg_io_get_config( chan,
665                                  CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
666                                  &dev_buf_conf, &dbc_len );
667         CYG_ASSERT( res == ENOERR, "Query buffer status failed!" );
668         if (dev_buf_conf.rx_count > 0) {
669             // Adjust length to be max characters currently available
670             *len = *len < dev_buf_conf.rx_count ? *len : dev_buf_conf.rx_count;
671         } else if (t->c_cc[VMIN] == 0) {
672             // No chars available - don't block
673             *len = 0;
674             return ENOERR;
675         }
676     } // if
677
678     while (!returnnow && size < *len) {
679         clen = 1;
680         discardc = false;
681         res = cyg_io_read(chan, &c, &clen);
682         if (res != ENOERR) {
683             *len = size;
684             return res;
685         }
686
687         // lock to prevent termios getting corrupted while we read from it
688         cyg_drv_mutex_lock( &priv->lock );
689
690         if ( t->c_iflag & ISTRIP )
691             c &= 0x7f;
692
693         // canonical mode: erase, kill, and newline processing
694         if ( t->c_lflag & ICANON ) {
695             if ( t->c_cc[ VERASE ] == c ) {
696                 discardc = true;
697                 // erase on display?
698                 if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOE) ) {
699                     cyg_uint8 erasebuf[3];
700                     erasebuf[0] = erasebuf[2] = t->c_cc[ VERASE ];
701                     erasebuf[1] = ' ';
702                     clen = sizeof(erasebuf);
703                     // FIXME: what about error or non-blocking?
704                     cyg_io_write(chan, erasebuf, &clen);
705                 }
706                 if ( size )
707                     size--;
708             } // if
709             else if ( t->c_cc[ VKILL ] == c ) {
710                 // kill line on display?
711                 if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOK) ) {
712
713                     // we could try and be more efficient here and 
714                     // output a stream of erases, and then a stream
715                     // of spaces and then more erases. But this is poor
716                     // because on a slow terminal the user will see characters
717                     // delete from the middle forward in chunks!
718                     // But at least we try and chunk up sets of writes
719                     cyg_uint8 erasebuf[30];
720                     cyg_uint8 erasechunks;
721                     cyg_uint8 i;
722
723                     erasechunks = size < (sizeof(erasebuf)/3) ? 
724                         size : (sizeof(erasebuf)/3);
725
726                     for (i=0; i<erasechunks; i++) {
727                         erasebuf[i*3] = erasebuf[i*3+2] = t->c_cc[ VERASE ];
728                         erasebuf[i*3+1] = ' ';
729                     }
730
731                     while( size ) {
732                         cyg_uint8 j;
733
734                         j = size < (sizeof(erasebuf)/3) ? 
735                             size : (sizeof(erasebuf)/3);
736                         clen = (j*3);
737                         // FIXME: what about error or non-blocking?
738                         cyg_io_write( chan, erasebuf, &clen );
739                         size -= j;
740                     }
741                 } else
742                     size = 0;
743                 discardc = true;
744             } // else if
745             // CR
746             else if ( '\r' == c ) {
747                 if ( t->c_iflag & IGNCR )
748                     discardc = true;
749                 else if ( t->c_iflag & ICRNL )
750                     c = '\n';
751             }
752             // newlines or similar.
753             // Note: not an else if to catch CRNL conversion
754             if ( (t->c_cc[ VEOF ] == c) || (t->c_cc[ VEOL ] == c) ||
755                  ('\n' == c) ) {
756                 if ( t->c_cc[ VEOF ] == c )
757                      discardc = true;
758                 if ( t->c_lflag & ECHONL ) { // don't check ECHO in this case
759                     clen = 1;
760                     // FIXME: what about error or non-blocking?
761                     // FIXME: what if INLCR is set?
762                     cyg_io_write( chan, "\n", &clen );
763                 }
764                 if ( t->c_iflag & INLCR )
765                     c = '\r';
766                 returnnow = true; // FIXME: true even for INLCR?
767             } // else if
768         } // if 
769
770 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
771         if ( (t->c_lflag & ISIG) && (t->c_cc[ VINTR ] == c) ) {
772             discardc = true;
773             if ( 0 == (t->c_lflag & NOFLSH) )
774                 size = 0;
775             // raise could be a non-local jump - we should unlock mutex
776             cyg_drv_mutex_unlock( &priv->lock ); 
777
778             // FIXME: what if raise returns != 0?
779             raise( SIGINT );
780             cyg_drv_mutex_lock( &priv->lock ); 
781         } 
782
783         if ( (t->c_lflag & ISIG) && (t->c_cc[ VQUIT ] == c) ) {
784             discardc = true;
785             if ( 0 == (t->c_lflag & NOFLSH) )
786                 size = 0;
787             // raise could be a non-local jump - we should unlock mutex
788             cyg_drv_mutex_unlock( &priv->lock ); 
789
790             // FIXME: what if raise returns != 0?
791             raise( SIGQUIT );
792             cyg_drv_mutex_lock( &priv->lock ); 
793         } 
794 #endif
795         if (!discardc) {
796             buf[size++] = c;
797             if ( t->c_lflag & ECHO ) {
798                 clen = 1;
799                 // FIXME: what about error or non-blocking?
800                 termios_write( handle, &c, &clen );
801             }
802         }
803
804         if ( (t->c_lflag & ICANON) == 0 ) {
805             // Check to see if read has been satisfied
806             if ( t->c_cc[ VMIN ] && (size >= t->c_cc[ VMIN ]) )
807                 returnnow = true;
808         }
809         cyg_drv_mutex_unlock( &priv->lock );
810     } // while
811
812     *len = size;
813     return ENOERR;
814 }
815
816
817 //==========================================================================
818
819 static cyg_bool
820 termios_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
821 {
822     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
823     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
824     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;    
825
826     // Just pass it on to next driver level
827     return cyg_io_select( chan, which, info );
828 }
829
830
831 //==========================================================================
832
833 static Cyg_ErrNo 
834 termios_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf,
835                    cyg_uint32 *len)
836 {
837     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
838     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
839     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
840     Cyg_ErrNo res = ENOERR;
841
842     switch (key) {
843     case CYG_IO_GET_CONFIG_TERMIOS:
844         {
845             if ( *len < sizeof(struct termios) ) {
846                 return -EINVAL;
847             }
848             cyg_drv_mutex_lock( &priv->lock );
849             *(struct termios *)buf = priv->termios;
850             cyg_drv_mutex_unlock( &priv->lock );
851             *len = sizeof(struct termios);
852         }
853         break;
854     default:  // Assume this is a 'serial' driver control
855         res = cyg_io_get_config(chan, key, buf, len);
856     } // switch
857     return res;
858 }
859
860
861 //==========================================================================
862
863
864 static Cyg_ErrNo 
865 termios_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
866 {
867     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
868     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
869     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
870     Cyg_ErrNo res = ENOERR;
871
872     switch (key) {
873     case CYG_IO_SET_CONFIG_TERMIOS:
874         {
875             setattr_struct *attr = (setattr_struct *)buf;
876             int optact = attr->optact;
877
878             if ( *len != sizeof( *attr ) ) {
879                 return -EINVAL;
880             }
881
882             CYG_ASSERT( (optact == TCSAFLUSH) || (optact == TCSADRAIN) ||
883                         (optact == TCSANOW), "Invalid optact" );
884                 
885             cyg_drv_mutex_lock( &priv->lock );
886     
887             if ( ( TCSAFLUSH == optact ) ||
888                  ( TCSADRAIN == optact ) ) {
889                 res = cyg_io_get_config( chan,
890                                          CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN,
891                                          NULL, NULL );
892                 CYG_ASSERT( ENOERR == res, "Drain request failed" );
893             }
894             if ( TCSAFLUSH == optact ) {
895                 res = cyg_io_get_config( chan,
896                                          CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH,
897                                          NULL, NULL );
898                 CYG_ASSERT( ENOERR == res, "Flush request failed" );
899             }
900                 
901             res = set_attr( attr->termios_p, priv );
902             cyg_drv_mutex_unlock( &priv->lock );
903             return res;
904         }
905     default: // Pass on to serial driver
906         res = cyg_io_set_config(chan, key, buf, len);
907     }
908     return res;
909 }
910
911
912 //==========================================================================
913
914 #endif // ifdef CYGPKG_IO_SERIAL_TERMIOS
915
916 // EOF termiostty.c