unified MX27, MX25, MX37 trees
[karo-tx-redboot.git] / packages / io / serial / v2_0 / src / common / tty.c
1 //==========================================================================
2 //
3 //      io/serial/common/tty.c
4 //
5 //      TTY (terminal-like interface) 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 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):   gthomas
44 // Contributors:  gthomas
45 // Date:        1999-02-04
46 // Purpose:     Device driver for tty I/O, layered on top of serial I/O
47 // Description: 
48 //
49 //####DESCRIPTIONEND####
50 //
51 //==========================================================================
52
53 #include <pkgconf/io.h>
54 #include <pkgconf/io_serial.h>
55 #ifdef CYGPKG_IO_SERIAL_TTY
56 #include <cyg/io/io.h>
57 #include <cyg/io/devtab.h>
58 #include <cyg/io/ttyio.h>
59 #include <cyg/infra/diag.h>
60
61 static bool tty_init(struct cyg_devtab_entry *tab);
62 static Cyg_ErrNo tty_lookup(struct cyg_devtab_entry **tab, 
63                                struct cyg_devtab_entry *sub_tab,
64                                const char *name);
65 static Cyg_ErrNo tty_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len);
66 static Cyg_ErrNo tty_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len);
67 static cyg_bool  tty_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info);
68 static Cyg_ErrNo tty_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len);
69 static Cyg_ErrNo tty_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len);
70
71 struct tty_private_info {
72     cyg_tty_info_t     dev_info;
73     cyg_io_handle_t dev_handle;
74 };
75
76 static DEVIO_TABLE(tty_devio,
77                    tty_write,
78                    tty_read,
79                    tty_select,
80                    tty_get_config,
81                    tty_set_config
82     );
83
84 #ifdef CYGPKG_IO_SERIAL_TTY_TTYDIAG
85 static struct tty_private_info tty_private_info_diag;
86 DEVTAB_ENTRY(tty_io_diag, 
87 //             "/dev/console",       
88 //             CYGDAT_IO_SERIAL_TTY_CONSOLE,   // Based on driver for this device
89              "/dev/ttydiag",
90              "/dev/haldiag",
91              &tty_devio, 
92              tty_init, 
93              tty_lookup,      // Execute this when device is being looked up
94              &tty_private_info_diag);
95 #endif
96
97 #ifdef CYGPKG_IO_SERIAL_TTY_TTY0
98 static struct tty_private_info tty_private_info0;
99 DEVTAB_ENTRY(tty_io0, 
100              "/dev/tty0",
101              CYGDAT_IO_SERIAL_TTY_TTY0_DEV,
102              &tty_devio, 
103              tty_init, 
104              tty_lookup,      // Execute this when device is being looked up
105              &tty_private_info0);
106 #endif
107
108 #ifdef CYGPKG_IO_SERIAL_TTY_TTY1
109 static struct tty_private_info tty_private_info1;
110 DEVTAB_ENTRY(tty_io1, 
111              "/dev/tty1", 
112              CYGDAT_IO_SERIAL_TTY_TTY1_DEV,
113              &tty_devio, 
114              tty_init, 
115              tty_lookup,      // Execute this when device is being looked up
116              &tty_private_info1);
117 #endif
118
119 #ifdef CYGPKG_IO_SERIAL_TTY_TTY2
120 static struct tty_private_info tty_private_info2;
121 DEVTAB_ENTRY(tty_io2, 
122              "/dev/tty2", 
123              CYGDAT_IO_SERIAL_TTY_TTY2_DEV,
124              &tty_devio, 
125              tty_init, 
126              tty_lookup,      // Execute this when device is being looked up
127              &tty_private_info2);
128 #endif
129
130 #ifdef CYGPKG_IO_SERIAL_TTY_TTY3
131 static struct tty_private_info tty_private_info3;
132 DEVTAB_ENTRY(tty_io3, 
133              "/dev/tty3", 
134              CYGDAT_IO_SERIAL_TTY_TTY3_DEV,
135              &tty_devio, 
136              tty_init, 
137              tty_lookup,      // Execute this when device is being looked up
138              &tty_private_info3);
139 #endif
140
141 static bool 
142 tty_init(struct cyg_devtab_entry *tab)
143 {
144     struct tty_private_info *priv = (struct tty_private_info *)tab->priv;
145 #ifdef CYGDBG_IO_INIT
146     diag_printf("Init tty channel: %p\n", tab);
147 #endif
148     priv->dev_info.tty_out_flags = CYG_TTY_OUT_FLAGS_DEFAULT;
149     priv->dev_info.tty_in_flags = CYG_TTY_IN_FLAGS_DEFAULT;
150     return true;
151 }
152
153 static Cyg_ErrNo 
154 tty_lookup(struct cyg_devtab_entry **tab, 
155            struct cyg_devtab_entry *sub_tab,
156            const char *name)
157 {
158     cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
159     struct tty_private_info *priv = (struct tty_private_info *)(*tab)->priv;
160 #if 0
161     cyg_int32 len;
162 #endif
163     priv->dev_handle = chan;
164 #if 0
165     len = sizeof(cyg_serial_info_t);
166     // Initialize configuration
167     cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG, 
168                       (void *)&priv->dev_info.serial_config, &len);
169 #endif
170     return ENOERR;
171 }
172
173 #define BUFSIZE 64
174
175 static Cyg_ErrNo 
176 tty_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
177 {
178     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
179     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
180     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
181     cyg_uint32 size;
182     cyg_int32 bytes_successful, actually_written;
183     cyg_uint8 xbuf[BUFSIZE];
184     cyg_uint8 c;
185     cyg_uint8 *buf = (cyg_uint8 *)_buf;
186     Cyg_ErrNo res = -EBADF;
187     // assert(chan)
188     size = 0;
189     bytes_successful = 0;
190     actually_written = 0;
191     while (bytes_successful++ < *len) {
192         c = *buf++;
193         if ((c == '\n') &&
194             (priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF)) {
195             xbuf[size++] = '\r';
196         }
197         xbuf[size++] = c;
198         // Always leave room for possible CR/LF expansion
199         if ((size >= (BUFSIZE-1)) ||
200             (bytes_successful == *len)) {
201             res = cyg_io_write(chan, xbuf, &size);
202             if (res != ENOERR) {
203                 *len = actually_written;
204                 return res;
205             }
206             actually_written += size;
207             size = 0;
208         }
209     }
210     return res;
211 }
212
213 static Cyg_ErrNo 
214 tty_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
215 {
216     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
217     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
218     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
219     cyg_uint32 clen;
220     cyg_int32 size;
221     Cyg_ErrNo res;
222     cyg_uint8 c;
223     cyg_uint8 *buf = (cyg_uint8 *)_buf;
224     // assert(chan)
225     size = 0;
226     while (size < *len) {
227         clen = 1;
228         res = cyg_io_read(chan, &c, &clen);
229         if (res != ENOERR) {
230             *len = size;
231             return res;
232         }
233         buf[size++] = c;
234         if ((priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_BINARY) == 0) {
235             switch (c) {
236             case '\b':    /* drop through */
237             case 0x7f:
238                 size -= 2;  // erase one character + 'backspace' char
239                 if (size < 0) {
240                     size = 0;
241                 } else if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
242                     clen = 3;
243                     cyg_io_write(chan, "\b \b", &clen);
244                 }
245                 break;
246             case '\r':
247                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CRLF) {
248                     /* Don't do anything because a '\n' will come next */
249                     break;
250                 }
251                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CR) {
252                     c = '\n';  // Map CR -> LF
253                 }
254                 /* drop through */
255             case '\n':
256                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
257                     if (priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF) {
258                         clen = 2;
259                         cyg_io_write(chan, "\r\n", &clen);
260                     } else {
261                         clen = 1;
262                         cyg_io_write(chan, &c, &clen);
263                     }
264                 }
265                 buf[size-1] = c;
266                 *len = size;
267                 return ENOERR;
268             default:
269                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
270                     clen = 1;
271                     cyg_io_write(chan, &c, &clen);
272                 }
273                 break;
274             }
275         }
276     }
277     *len = size;
278     return ENOERR;
279 }
280
281 static cyg_bool
282 tty_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
283 {
284     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
285     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
286     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;    
287
288     // Just pass it on to next driver level
289     return cyg_io_select( chan, which, info );
290 }
291
292 static Cyg_ErrNo 
293 tty_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len)
294 {
295     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
296     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
297     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
298     Cyg_ErrNo res = ENOERR;
299 #if 0
300     cyg_int32 current_len;
301 #endif
302     // assert(chan)
303     switch (key) {
304     case CYG_IO_GET_CONFIG_TTY_INFO:
305         if (*len < sizeof(cyg_tty_info_t)) {
306             return -EINVAL;
307         }
308 #if 0
309         current_len = sizeof(cyg_serial_info_t);
310         res = cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG, 
311                                 (void *)&priv->dev_info.serial_config, &current_len);
312         if (res != ENOERR) {
313             return res;
314         }
315 #endif
316         *(cyg_tty_info_t *)buf = priv->dev_info;
317         *len = sizeof(cyg_tty_info_t);
318         break;
319     default:  // Assume this is a 'serial' driver control
320         res = cyg_io_get_config(chan, key, buf, len);
321     }
322     return res;
323 }
324
325 static Cyg_ErrNo 
326 tty_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
327 {
328     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
329     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
330     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
331 #if 0
332     cyg_int32 current_len;
333 #endif
334     Cyg_ErrNo res = ENOERR;
335     // assert(chan)
336     switch (key) {
337     case CYG_IO_SET_CONFIG_TTY_INFO:
338         if (*len != sizeof(cyg_tty_info_t)) {
339             return -EINVAL;
340         }
341         priv->dev_info = *(cyg_tty_info_t *)buf;
342         break;
343     default: // Pass on to serial driver
344         res = cyg_io_set_config(chan, key, buf, len);
345     }
346     return res;
347 }
348 #endif // CYGPKG_IO_SERIAL_TTY