Initial revision
[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 static const cc_t c_cc_init[ NCCS ] = { 
162     0x04,     /* EOF == ^D */
163     0,        /* EOL */
164     0x08,     /* ERASE = BS ; NB DEL=0x7f */
165     0x03,     /* INTR = ^C */
166     0x15,     /* KILL = ^U */
167     0,        /* MIN = 0 */
168     0x1c,     /* QUIT = ^\ */
169     0x1a,     /* SUSP = ^Z ; NB ignored in this impl - no job control */
170     0,        /* TIME = 0 */
171 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_SOFTWARE
172     CYGDAT_IO_SERIAL_FLOW_CONTROL_XON_CHAR,
173     CYGDAT_IO_SERIAL_FLOW_CONTROL_XOFF_CHAR,
174 #else
175     17,
176     19,
177 #endif
178 };
179
180 // map eCos bitrates to POSIX bitrates.
181 static speed_t ecosbaud2posixbaud[] = {
182     0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B2400, B3600,
183     B4800, B7200, B9600, B14400, B19200, B38400, B57600, B115200, B230400 };
184
185 // map POSIX bitrates to eCos bitrates.
186 static cyg_serial_baud_rate_t posixbaud2ecosbaud[] = {
187     0, CYGNUM_SERIAL_BAUD_50, CYGNUM_SERIAL_BAUD_75, CYGNUM_SERIAL_BAUD_110,
188     CYGNUM_SERIAL_BAUD_134_5, CYGNUM_SERIAL_BAUD_150, CYGNUM_SERIAL_BAUD_200,
189     CYGNUM_SERIAL_BAUD_300, CYGNUM_SERIAL_BAUD_600, CYGNUM_SERIAL_BAUD_1200,
190     CYGNUM_SERIAL_BAUD_1800, CYGNUM_SERIAL_BAUD_2400, CYGNUM_SERIAL_BAUD_3600,
191     CYGNUM_SERIAL_BAUD_4800, CYGNUM_SERIAL_BAUD_7200, CYGNUM_SERIAL_BAUD_9600,
192     CYGNUM_SERIAL_BAUD_14400, CYGNUM_SERIAL_BAUD_19200,
193     CYGNUM_SERIAL_BAUD_38400, CYGNUM_SERIAL_BAUD_57600,
194     CYGNUM_SERIAL_BAUD_115200, CYGNUM_SERIAL_BAUD_230400 };
195
196
197 //==========================================================================
198 // FUNCTIONS
199
200 static __inline__ speed_t
201 map_ecosbaud_to_posixbaud( cyg_serial_baud_rate_t ebaud )
202 {
203     if ( ebaud > (sizeof(ecosbaud2posixbaud) / sizeof(speed_t)) )
204         return 0;
205     return ecosbaud2posixbaud[ ebaud ];
206 }
207
208 static __inline__ cyg_serial_baud_rate_t
209 map_posixbaud_to_ecosbaud( speed_t pbaud )
210 {
211     if ( pbaud > (sizeof(posixbaud2ecosbaud)/sizeof(cyg_serial_baud_rate_t)) )
212         return 0;
213     return posixbaud2ecosbaud[ pbaud ];
214 }
215
216 //==========================================================================
217 // real_termios_init is used to initialize the termios structure. This is
218 // called at lookup time, and not from termios_init() because it needs
219 // to query the serial device which may not be set up yet at that point
220 // in termios_init()
221
222 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
223 # define C_IFLAG_INIT (ICRNL|IGNBRK|BRKINT)
224 #else
225 # define C_IFLAG_INIT (ICRNL|IGNBRK)
226 #endif
227 #define C_OFLAG_INIT (ONLCR)
228 #define C_CFLAG_INIT (CREAD)
229 #define C_LFLAG_INIT (ECHO|ECHOE|ECHOK|ICANON)
230
231 static Cyg_ErrNo
232 real_termios_init( struct termios_private_info *priv )
233 {
234     Cyg_ErrNo res;
235     struct termios *t;
236     cyg_serial_info_t dev_conf;
237     cyg_serial_buf_info_t dev_buf_conf;
238     cyg_uint32 len = sizeof( dev_conf );
239
240     CYG_REPORT_FUNCTYPE("returning %d");
241     CYG_REPORT_FUNCARG1XV( priv );
242     CYG_CHECK_DATA_PTRC( priv );
243
244     t = &priv->termios;
245
246     // Get info from driver
247     res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
248                              &dev_conf, &len );
249     if ( ENOERR == res ) {
250         len = sizeof( dev_buf_conf );
251         res = cyg_io_get_config( priv->dev_handle,
252                                  CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
253                                  &dev_buf_conf, &len );
254     }
255
256     if ( ENOERR != res ) {
257         CYG_REPORT_RETVAL( res );
258         return res;
259     }
260     
261     // we only support symmetric baud rates
262     t->c_ispeed = t->c_ospeed = map_ecosbaud_to_posixbaud( dev_conf.baud );
263     t->c_iflag = C_IFLAG_INIT;
264     t->c_oflag = C_OFLAG_INIT;
265     t->c_cflag = C_CFLAG_INIT;
266     t->c_lflag = C_LFLAG_INIT;
267     memcpy( t->c_cc, c_cc_init, sizeof( t->c_cc ) );
268     
269     switch ( dev_conf.parity ) {
270     case CYGNUM_SERIAL_PARITY_NONE:
271         t->c_iflag |= IGNPAR;
272         break;
273     case CYGNUM_SERIAL_PARITY_ODD:
274         t->c_cflag |= PARODD;
275         // DROPTHROUGH
276     case CYGNUM_SERIAL_PARITY_EVEN:
277         t->c_iflag |= PARENB;
278         break;
279     default:
280         CYG_FAIL( "Unsupported default parity" );
281         break;
282     }
283
284     switch( dev_conf.word_length ) {
285     case CYGNUM_SERIAL_WORD_LENGTH_5:
286         t->c_cflag |= CS5;
287         break;        
288     case CYGNUM_SERIAL_WORD_LENGTH_6:
289         t->c_cflag |= CS6;
290         break;
291     case CYGNUM_SERIAL_WORD_LENGTH_7:
292         t->c_cflag |= CS7;
293         break;
294     case CYGNUM_SERIAL_WORD_LENGTH_8:
295         t->c_cflag |= CS8;
296         break;
297     default:
298         CYG_FAIL( "Unsupported word length" );
299         break;
300     }
301
302     switch ( dev_conf.stop ) {
303     case CYGNUM_SERIAL_STOP_1:
304         // Don't need to do anything
305         break;
306     case CYGNUM_SERIAL_STOP_2:
307         t->c_cflag |= CSTOPB;
308         break;
309     default:
310         CYG_FAIL( "Unsupported number of stop bits" );
311         break;
312     }
313
314     switch ( dev_conf.flags ) {
315     case CYGNUM_SERIAL_FLOW_RTSCTS_RX:
316         t->c_cflag |= CRTSCTS;
317         // drop through
318     case CYGNUM_SERIAL_FLOW_XONXOFF_RX:
319         t->c_iflag |= IXOFF;
320         break;
321     case CYGNUM_SERIAL_FLOW_RTSCTS_TX:
322         t->c_cflag |= CRTSCTS;
323         // drop through
324     case CYGNUM_SERIAL_FLOW_XONXOFF_TX:
325         t->c_iflag |= IXON;
326         break;
327     default:
328         // Ignore flags we don't grok
329         break;
330     }
331
332     return ENOERR;
333 } // real_termios_init()
334
335 //==========================================================================
336 // set_attr() actually enacts the termios config. We come in here with
337 // the mutex in priv locked
338 //
339 // Possible deviation from standard: POSIX says we should enact which ever
340 // bits we can and only return EINVAL when none of them can be performed
341 // Rather than tracking whether *none* of them worked, instead we just
342 // always claim success. At the very least, setting c_cc is never to
343 // fail so I'm not sure if this is really non-standard or not!
344
345 static Cyg_ErrNo
346 set_attr( struct termios *t, struct termios_private_info *priv )
347 {
348     Cyg_ErrNo res = ENOERR;
349     cyg_serial_info_t dev_conf, new_dev_conf;
350     cyg_uint32 len = sizeof( dev_conf );
351     cc_t *tempcc = &priv->termios.c_cc[0];
352     struct termios *ptermios = &priv->termios;
353     
354     // Get info from driver
355     res = cyg_io_get_config( priv->dev_handle, CYG_IO_GET_CONFIG_SERIAL_INFO,
356                              &dev_conf, &len );
357
358     if ( ENOERR != res )
359         return res;
360         
361     // We need to set up each facet of config to change one by one because
362     // POSIX says we should try and change as much of the config as possible
363     // This is tedious and has to be done by steam :-(
364
365     if ( t->c_ospeed != ptermios->c_ospeed ) {
366         new_dev_conf = dev_conf;
367         new_dev_conf.baud = map_posixbaud_to_ecosbaud( t->c_ospeed );
368         if ( 0 != new_dev_conf.baud ) {
369             len = sizeof( new_dev_conf );
370             res = cyg_io_set_config( priv->dev_handle,
371                                      CYG_IO_SET_CONFIG_SERIAL_INFO,
372                                      &new_dev_conf, &len );
373             if ( ENOERR == res ) {
374                 // It worked, so update dev_conf to reflect the new state
375                 dev_conf.baud = new_dev_conf.baud;
376                 // and termios
377                 ptermios->c_ispeed = t->c_ospeed;
378                 ptermios->c_ospeed = t->c_ospeed;
379             }
380         }
381     }
382
383     if ( (t->c_cflag & CSTOPB) != (ptermios->c_cflag & CSTOPB) ) {
384         new_dev_conf = dev_conf;
385         if ( t->c_cflag & CSTOPB )
386             new_dev_conf.stop = CYGNUM_SERIAL_STOP_2;
387         else
388             new_dev_conf.stop = CYGNUM_SERIAL_STOP_1;
389         
390         len = sizeof( new_dev_conf );
391         res = cyg_io_set_config( priv->dev_handle,
392                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
393                                  &new_dev_conf, &len );
394         if ( ENOERR == res ) {
395             // It worked, so update dev_conf to reflect the new state
396             dev_conf.stop = new_dev_conf.stop;
397             // and termios
398             ptermios->c_cflag &= ~CSTOPB;
399             ptermios->c_cflag |= t->c_cflag & CSTOPB;
400         }
401     }
402
403     if ( ((t->c_cflag & PARENB) != (ptermios->c_cflag & PARENB)) ||
404          ((t->c_cflag & PARODD) != (ptermios->c_cflag & PARODD)) ) {
405         new_dev_conf = dev_conf;
406         if ( t->c_cflag & PARENB )
407             if ( t->c_cflag & PARODD )
408                 new_dev_conf.parity = CYGNUM_SERIAL_PARITY_ODD;
409             else
410                 new_dev_conf.parity = CYGNUM_SERIAL_PARITY_EVEN;
411         else
412             new_dev_conf.parity = CYGNUM_SERIAL_PARITY_NONE;
413         
414         len = sizeof( new_dev_conf );
415         res = cyg_io_set_config( priv->dev_handle,
416                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
417                                  &new_dev_conf, &len );
418         if ( ENOERR == res ) {
419             // It worked, so update dev_conf to reflect the new state
420             dev_conf.parity = new_dev_conf.parity;
421             // and termios
422             ptermios->c_cflag &= ~(PARENB|PARODD);
423             ptermios->c_cflag |= t->c_cflag & (PARENB|PARODD);
424         }
425     }
426
427     if ( (t->c_cflag & CSIZE) != (ptermios->c_cflag & CSIZE) ) {
428         new_dev_conf = dev_conf;
429         switch ( t->c_cflag & CSIZE ) {
430         case CS5:
431             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_5;
432             break;
433         case CS6:
434             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_6;
435             break;
436         case CS7:
437             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_7;
438             break;
439         case CS8:
440             new_dev_conf.word_length = CYGNUM_SERIAL_WORD_LENGTH_8;
441             break;
442         }
443         
444         len = sizeof( new_dev_conf );
445         res = cyg_io_set_config( priv->dev_handle,
446                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
447                                  &new_dev_conf, &len );
448         if ( ENOERR == res ) {
449             // It worked, so update dev_conf to reflect the new state
450             dev_conf.word_length = new_dev_conf.word_length;
451             // and termios
452             ptermios->c_cflag &= ~CSIZE;
453             ptermios->c_cflag |= t->c_cflag & CSIZE;
454         }
455     }
456
457     if ( (t->c_cflag & IXOFF) != (ptermios->c_cflag & IXOFF) ) {
458         new_dev_conf = dev_conf;
459         new_dev_conf.flags &=
460             ~(CYGNUM_SERIAL_FLOW_XONXOFF_RX|CYGNUM_SERIAL_FLOW_RTSCTS_RX);
461         if ( t->c_cflag & IXOFF )
462             if ( t->c_cflag & CRTSCTS)
463                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_RX;
464             else
465                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_RX;
466         else
467             new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
468         
469         len = sizeof( new_dev_conf );
470         res = cyg_io_set_config( priv->dev_handle,
471                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
472                                  &new_dev_conf, &len );
473         if ( ENOERR == res ) {
474             // It worked, so update dev_conf to reflect the new state
475             dev_conf.flags = new_dev_conf.flags;
476             // and termios
477             ptermios->c_cflag &= ~(IXOFF|CRTSCTS);
478             ptermios->c_cflag |= t->c_cflag & (IXOFF|CRTSCTS);
479         }
480     }
481
482     if ( (t->c_cflag & IXON) != (ptermios->c_cflag & IXON) ) {
483         new_dev_conf = dev_conf;
484         new_dev_conf.flags &=
485             ~(CYGNUM_SERIAL_FLOW_XONXOFF_TX|CYGNUM_SERIAL_FLOW_RTSCTS_TX);
486         if ( t->c_cflag & IXON )
487             if ( t->c_cflag & CRTSCTS)
488                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_RTSCTS_TX;
489             else
490                 new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_XONXOFF_TX;
491         else
492             new_dev_conf.flags |= CYGNUM_SERIAL_FLOW_NONE;
493         
494         len = sizeof( new_dev_conf );
495         res = cyg_io_set_config( priv->dev_handle,
496                                  CYG_IO_SET_CONFIG_SERIAL_INFO,
497                                  &new_dev_conf, &len );
498         if ( ENOERR == res ) {
499             // It worked, so update dev_conf to reflect the new state
500             dev_conf.flags = new_dev_conf.flags;
501             // and termios
502             ptermios->c_cflag &= ~(IXON|CRTSCTS);
503             ptermios->c_cflag |= t->c_cflag & (IXON|CRTSCTS);
504         }
505     }
506
507     // Input/Output processing flags can just be set as we grok them all
508     // with few exceptions (see lflags below)
509     ptermios->c_iflag &= ~(BRKINT|ICRNL|IGNBRK|IGNCR|IGNPAR|INLCR|INPCK|
510                            ISTRIP|PARMRK);
511     ptermios->c_iflag |= t->c_iflag & (
512 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
513                                        BRKINT|
514 #endif
515                                        ICRNL|IGNBRK|IGNCR|IGNPAR|
516                                        INLCR|INPCK|ISTRIP|PARMRK );
517     
518     ptermios->c_oflag &= ~(OPOST|ONLCR);
519     ptermios->c_oflag |= t->c_oflag & (OPOST|ONLCR);
520
521     ptermios->c_cflag &= ~(CLOCAL|CREAD|HUPCL);
522     ptermios->c_cflag |= t->c_cflag & (CLOCAL|CREAD|HUPCL);
523
524     ptermios->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|
525                                  IEXTEN|ISIG|NOFLSH|TOSTOP);
526     // Note we don't support IEXTEN nor TOSTOP so we don't set them
527     ptermios->c_lflag |= t->c_lflag & (ECHO|ECHOE|ECHOK|ECHONL|ICANON|
528 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
529                                        ISIG|
530 #endif
531                                        NOFLSH);
532
533     // control characters. We don't support changing of VSTART, VSTOP,
534     // VTIME or VSUSP though
535     tempcc[VEOF]   = t->c_cc[VEOF];
536     tempcc[VEOL]   = t->c_cc[VEOL];
537     tempcc[VERASE] = t->c_cc[VERASE];
538     tempcc[VINTR]  = t->c_cc[VINTR];
539     tempcc[VKILL]  = t->c_cc[VKILL];
540     tempcc[VMIN]   = t->c_cc[VMIN];
541     tempcc[VQUIT]  = t->c_cc[VQUIT];
542         
543     return res;
544 }
545
546 //==========================================================================
547
548 static bool 
549 termios_init(struct cyg_devtab_entry *tab)
550 {
551     // can't initialize the termios structure because we can't
552     // query the serial driver yet. Wait until lookup time.
553
554     return true;
555 } // termios_init()
556
557 //==========================================================================
558
559 static Cyg_ErrNo 
560 termios_lookup(struct cyg_devtab_entry **tab, 
561            struct cyg_devtab_entry *sub_tab,
562            const char *name)
563 {
564     cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
565     struct termios_private_info *priv =
566         (struct termios_private_info *)(*tab)->priv;
567     Cyg_ErrNo err = ENOERR;
568     
569     if ( !priv->init ) {
570         cyg_drv_mutex_lock( &priv->lock );
571         if ( !priv->init ) {  // retest as we may have been pre-empted
572             priv->dev_handle = chan;
573             err = real_termios_init( priv );
574         }
575         cyg_drv_mutex_unlock( &priv->lock );
576     }
577     return err;
578 }
579
580 //==========================================================================
581
582 #define WRITE_BUFSIZE 100 // FIXME: ->CDL
583 // #define MAX_CANON 64  FIXME: relevance?
584
585
586 static Cyg_ErrNo 
587 termios_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
588 {
589     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
590     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
591     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
592     cyg_int32 xbufsize, input_bytes_read;
593     cyg_uint8 xbuf[WRITE_BUFSIZE];
594     cyg_uint8 *buf = (cyg_uint8 *)_buf;
595     Cyg_ErrNo res;
596
597     xbufsize = input_bytes_read = 0;
598     while (input_bytes_read++ < *len) {
599         if ( (*buf == '\n') && (priv->termios.c_oflag & (OPOST|ONLCR)) ) {
600             xbuf[xbufsize++] = '\r';
601         }
602         xbuf[xbufsize++] = *buf;
603         if ((xbufsize >= (WRITE_BUFSIZE-1)) || (input_bytes_read == *len) ||
604             (*buf == '\n'))
605         {
606             cyg_uint32 size = xbufsize;
607             res = cyg_io_write(chan, xbuf, &size);
608             if (res != ENOERR) {
609                 *len = input_bytes_read - (xbufsize - size);
610                 return res;
611             }
612             xbufsize = 0;
613         }
614         buf++;
615     }
616     // Everything sent, so *len is correct.
617     return ENOERR;
618 }
619
620
621 //==========================================================================
622
623 static Cyg_ErrNo 
624 termios_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
625 {
626     cyg_devtab_entry_t *dt = (cyg_devtab_entry_t *)handle;
627     struct termios_private_info *priv = (struct termios_private_info *)dt->priv;
628     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
629     struct termios *t = &priv->termios;
630     cyg_uint32 clen;
631     cyg_uint32 size;
632     Cyg_ErrNo res;
633     cyg_uint8 c;
634     cyg_uint8 *buf = (cyg_uint8 *)_buf;
635     cyg_bool discardc; // should c be discarded (not read, not printed)
636     cyg_bool returnnow = false; // return back to user after this char
637     
638     // if receiver off
639     if (0 == (t->c_cflag & CREAD) ) {
640         *len = 0;
641         return -EINVAL;
642     }
643
644     size = 0;
645     if ( 0 == (t->c_lflag & ICANON) ) {
646         // In non-canonical mode we return the min of *len and the
647         // number of bytes available
648         // So we query the driver for how many bytes are available - this
649         // guarantees we won't block
650         cyg_serial_buf_info_t dev_buf_conf;
651         cyg_uint32 dbc_len = sizeof( dev_buf_conf );
652         res = cyg_io_get_config( chan,
653                                  CYG_IO_GET_CONFIG_SERIAL_BUFFER_INFO,
654                                  &dev_buf_conf, &dbc_len );
655         CYG_ASSERT( res == ENOERR, "Query buffer status failed!" );
656         if (dev_buf_conf.rx_count > 0) {
657             // Adjust length to be max characters currently available
658             *len = *len < dev_buf_conf.rx_count ? *len : dev_buf_conf.rx_count;
659         } else if (t->c_cc[VMIN] == 0) {
660             // No chars available - don't block
661             *len = 0;
662             return ENOERR;
663         }
664     } // if
665
666     while (!returnnow && size < *len) {
667         clen = 1;
668         discardc = false;
669         res = cyg_io_read(chan, &c, &clen);
670         if (res != ENOERR) {
671             *len = size;
672             return res;
673         }
674
675         // lock to prevent termios getting corrupted while we read from it
676         cyg_drv_mutex_lock( &priv->lock );
677
678         if ( t->c_iflag & ISTRIP )
679             c &= 0x7f;
680
681         // canonical mode: erase, kill, and newline processing
682         if ( t->c_lflag & ICANON ) {
683             if ( t->c_cc[ VERASE ] == c ) {
684                 discardc = true;
685                 // erase on display?
686                 if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOE) ) {
687                     cyg_uint8 erasebuf[3];
688                     erasebuf[0] = erasebuf[2] = t->c_cc[ VERASE ];
689                     erasebuf[1] = ' ';
690                     clen = sizeof(erasebuf);
691                     // FIXME: what about error or non-blocking?
692                     cyg_io_write(chan, erasebuf, &clen);
693                 }
694                 if ( size )
695                     size--;
696             } // if
697             else if ( t->c_cc[ VKILL ] == c ) {
698                 // kill line on display?
699                 if ( (t->c_lflag & ECHO) && (t->c_lflag & ECHOK) ) {
700
701                     // we could try and be more efficient here and 
702                     // output a stream of erases, and then a stream
703                     // of spaces and then more erases. But this is poor
704                     // because on a slow terminal the user will see characters
705                     // delete from the middle forward in chunks!
706                     // But at least we try and chunk up sets of writes
707                     cyg_uint8 erasebuf[30];
708                     cyg_uint8 erasechunks;
709                     cyg_uint8 i;
710
711                     erasechunks = size < (sizeof(erasebuf)/3) ? 
712                         size : (sizeof(erasebuf)/3);
713
714                     for (i=0; i<erasechunks; i++) {
715                         erasebuf[i*3] = erasebuf[i*3+2] = t->c_cc[ VERASE ];
716                         erasebuf[i*3+1] = ' ';
717                     }
718
719                     while( size ) {
720                         cyg_uint8 j;
721
722                         j = size < (sizeof(erasebuf)/3) ? 
723                             size : (sizeof(erasebuf)/3);
724                         clen = (j*3);
725                         // FIXME: what about error or non-blocking?
726                         cyg_io_write( chan, erasebuf, &clen );
727                         size -= j;
728                     }
729                 } else
730                     size = 0;
731                 discardc = true;
732             } // else if
733             // CR
734             else if ( '\r' == c ) {
735                 if ( t->c_iflag & IGNCR )
736                     discardc = true;
737                 else if ( t->c_iflag & ICRNL )
738                     c = '\n';
739             }
740             // newlines or similar.
741             // Note: not an else if to catch CRNL conversion
742             if ( (t->c_cc[ VEOF ] == c) || (t->c_cc[ VEOL ] == c) ||
743                  ('\n' == c) ) {
744                 if ( t->c_cc[ VEOF ] == c )
745                      discardc = true;
746                 if ( t->c_lflag & ECHONL ) { // don't check ECHO in this case
747                     clen = 1;
748                     // FIXME: what about error or non-blocking?
749                     // FIXME: what if INLCR is set?
750                     cyg_io_write( chan, "\n", &clen );
751                 }
752                 if ( t->c_iflag & INLCR )
753                     c = '\r';
754                 returnnow = true; // FIXME: true even for INLCR?
755             } // else if
756         } // if 
757
758 #ifdef CYGSEM_IO_SERIAL_TERMIOS_USE_SIGNALS
759         if ( (t->c_lflag & ISIG) && (t->c_cc[ VINTR ] == c) ) {
760             discardc = true;
761             if ( 0 == (t->c_lflag & NOFLSH) )
762                 size = 0;
763             // raise could be a non-local jump - we should unlock mutex
764             cyg_drv_mutex_unlock( &priv->lock ); 
765
766             // FIXME: what if raise returns != 0?
767             raise( SIGINT );
768             cyg_drv_mutex_lock( &priv->lock ); 
769         } 
770
771         if ( (t->c_lflag & ISIG) && (t->c_cc[ VQUIT ] == 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( SIGQUIT );
780             cyg_drv_mutex_lock( &priv->lock ); 
781         } 
782 #endif
783         if (!discardc) {
784             buf[size++] = c;
785             if ( t->c_lflag & ECHO ) {
786                 clen = 1;
787                 // FIXME: what about error or non-blocking?
788                 termios_write( handle, &c, &clen );
789             }
790         }
791
792         if ( (t->c_lflag & ICANON) == 0 ) {
793             // Check to see if read has been satisfied
794             if ( t->c_cc[ VMIN ] && (size >= t->c_cc[ VMIN ]) )
795                 returnnow = true;
796         }
797         cyg_drv_mutex_unlock( &priv->lock );
798     } // while
799
800     *len = size;
801     return ENOERR;
802 }
803
804
805 //==========================================================================
806
807 static cyg_bool
808 termios_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
809 {
810     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
811     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
812     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;    
813
814     // Just pass it on to next driver level
815     return cyg_io_select( chan, which, info );
816 }
817
818
819 //==========================================================================
820
821 static Cyg_ErrNo 
822 termios_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf,
823                    cyg_uint32 *len)
824 {
825     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
826     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
827     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
828     Cyg_ErrNo res = ENOERR;
829
830     switch (key) {
831     case CYG_IO_GET_CONFIG_TERMIOS:
832         {
833             if ( *len < sizeof(struct termios) ) {
834                 return -EINVAL;
835             }
836             cyg_drv_mutex_lock( &priv->lock );
837             *(struct termios *)buf = priv->termios;
838             cyg_drv_mutex_unlock( &priv->lock );
839             *len = sizeof(struct termios);
840         }
841         break;
842     default:  // Assume this is a 'serial' driver control
843         res = cyg_io_get_config(chan, key, buf, len);
844     } // switch
845     return res;
846 }
847
848
849 //==========================================================================
850
851
852 static Cyg_ErrNo 
853 termios_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
854 {
855     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
856     struct termios_private_info *priv = (struct termios_private_info *)t->priv;
857     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
858     Cyg_ErrNo res = ENOERR;
859
860     switch (key) {
861     case CYG_IO_SET_CONFIG_TERMIOS:
862         {
863             setattr_struct *attr = (setattr_struct *)buf;
864             int optact = attr->optact;
865
866             if ( *len != sizeof( *attr ) ) {
867                 return -EINVAL;
868             }
869
870             CYG_ASSERT( (optact == TCSAFLUSH) || (optact == TCSADRAIN) ||
871                         (optact == TCSANOW), "Invalid optact" );
872                 
873             cyg_drv_mutex_lock( &priv->lock );
874     
875             if ( ( TCSAFLUSH == optact ) ||
876                  ( TCSADRAIN == optact ) ) {
877                 res = cyg_io_get_config( chan,
878                                          CYG_IO_GET_CONFIG_SERIAL_OUTPUT_DRAIN,
879                                          NULL, NULL );
880                 CYG_ASSERT( ENOERR == res, "Drain request failed" );
881             }
882             if ( TCSAFLUSH == optact ) {
883                 res = cyg_io_get_config( chan,
884                                          CYG_IO_GET_CONFIG_SERIAL_INPUT_FLUSH,
885                                          NULL, NULL );
886                 CYG_ASSERT( ENOERR == res, "Flush request failed" );
887             }
888                 
889             res = set_attr( attr->termios_p, priv );
890             cyg_drv_mutex_unlock( &priv->lock );
891             return res;
892         }
893     default: // Pass on to serial driver
894         res = cyg_io_set_config(chan, key, buf, len);
895     }
896     return res;
897 }
898
899
900 //==========================================================================
901
902 #endif // ifdef CYGPKG_IO_SERIAL_TERMIOS
903
904 // EOF termiostty.c