]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/serial/v2_0/src/common/tty.c
Initial revision
[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 static bool 
131 tty_init(struct cyg_devtab_entry *tab)
132 {
133     struct tty_private_info *priv = (struct tty_private_info *)tab->priv;
134 #ifdef CYGDBG_IO_INIT
135     diag_printf("Init tty channel: %x\n", tab);
136 #endif
137     priv->dev_info.tty_out_flags = CYG_TTY_OUT_FLAGS_DEFAULT;
138     priv->dev_info.tty_in_flags = CYG_TTY_IN_FLAGS_DEFAULT;
139     return true;
140 }
141
142 static Cyg_ErrNo 
143 tty_lookup(struct cyg_devtab_entry **tab, 
144            struct cyg_devtab_entry *sub_tab,
145            const char *name)
146 {
147     cyg_io_handle_t chan = (cyg_io_handle_t)sub_tab;
148     struct tty_private_info *priv = (struct tty_private_info *)(*tab)->priv;
149 #if 0
150     cyg_int32 len;
151 #endif
152     priv->dev_handle = chan;
153 #if 0
154     len = sizeof(cyg_serial_info_t);
155     // Initialize configuration
156     cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG, 
157                       (void *)&priv->dev_info.serial_config, &len);
158 #endif
159     return ENOERR;
160 }
161
162 #define BUFSIZE 64
163
164 static Cyg_ErrNo 
165 tty_write(cyg_io_handle_t handle, const void *_buf, cyg_uint32 *len)
166 {
167     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
168     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
169     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
170     cyg_uint32 size;
171     cyg_int32 bytes_successful, actually_written;
172     cyg_uint8 xbuf[BUFSIZE];
173     cyg_uint8 c;
174     cyg_uint8 *buf = (cyg_uint8 *)_buf;
175     Cyg_ErrNo res = -EBADF;
176     // assert(chan)
177     size = 0;
178     bytes_successful = 0;
179     actually_written = 0;
180     while (bytes_successful++ < *len) {
181         c = *buf++;
182         if ((c == '\n') &&
183             (priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF)) {
184             xbuf[size++] = '\r';
185         }
186         xbuf[size++] = c;
187         // Always leave room for possible CR/LF expansion
188         if ((size >= (BUFSIZE-1)) ||
189             (bytes_successful == *len)) {
190             res = cyg_io_write(chan, xbuf, &size);
191             if (res != ENOERR) {
192                 *len = actually_written;
193                 return res;
194             }
195             actually_written += size;
196             size = 0;
197         }
198     }
199     return res;
200 }
201
202 static Cyg_ErrNo 
203 tty_read(cyg_io_handle_t handle, void *_buf, cyg_uint32 *len)
204 {
205     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
206     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
207     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
208     cyg_uint32 clen;
209     cyg_int32 size;
210     Cyg_ErrNo res;
211     cyg_uint8 c;
212     cyg_uint8 *buf = (cyg_uint8 *)_buf;
213     // assert(chan)
214     size = 0;
215     while (size < *len) {
216         clen = 1;
217         res = cyg_io_read(chan, &c, &clen);
218         if (res != ENOERR) {
219             *len = size;
220             return res;
221         }
222         buf[size++] = c;
223         if ((priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_BINARY) == 0) {
224             switch (c) {
225             case '\b':    /* drop through */
226             case 0x7f:
227                 size -= 2;  // erase one character + 'backspace' char
228                 if (size < 0) {
229                     size = 0;
230                 } else if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
231                     clen = 3;
232                     cyg_io_write(chan, "\b \b", &clen);
233                 }
234                 break;
235             case '\r':
236                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CRLF) {
237                     /* Don't do anything because a '\n' will come next */
238                     break;
239                 }
240                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_CR) {
241                     c = '\n';  // Map CR -> LF
242                 }
243                 /* drop through */
244             case '\n':
245                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
246                     if (priv->dev_info.tty_out_flags & CYG_TTY_OUT_FLAGS_CRLF) {
247                         clen = 2;
248                         cyg_io_write(chan, "\r\n", &clen);
249                     } else {
250                         clen = 1;
251                         cyg_io_write(chan, &c, &clen);
252                     }
253                 }
254                 buf[size-1] = c;
255                 *len = size;
256                 return ENOERR;
257             default:
258                 if (priv->dev_info.tty_in_flags & CYG_TTY_IN_FLAGS_ECHO) {
259                     clen = 1;
260                     cyg_io_write(chan, &c, &clen);
261                 }
262                 break;
263             }
264         }
265     }
266     *len = size;
267     return ENOERR;
268 }
269
270 static cyg_bool
271 tty_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info)
272 {
273     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
274     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
275     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;    
276
277     // Just pass it on to next driver level
278     return cyg_io_select( chan, which, info );
279 }
280
281 static Cyg_ErrNo 
282 tty_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len)
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     Cyg_ErrNo res = ENOERR;
288 #if 0
289     cyg_int32 current_len;
290 #endif
291     // assert(chan)
292     switch (key) {
293     case CYG_IO_GET_CONFIG_TTY_INFO:
294         if (*len < sizeof(cyg_tty_info_t)) {
295             return -EINVAL;
296         }
297 #if 0
298         current_len = sizeof(cyg_serial_info_t);
299         res = cyg_io_get_config(chan, CYG_SERIAL_GET_CONFIG, 
300                                 (void *)&priv->dev_info.serial_config, &current_len);
301         if (res != ENOERR) {
302             return res;
303         }
304 #endif
305         *(cyg_tty_info_t *)buf = priv->dev_info;
306         *len = sizeof(cyg_tty_info_t);
307         break;
308     default:  // Assume this is a 'serial' driver control
309         res = cyg_io_get_config(chan, key, buf, len);
310     }
311     return res;
312 }
313
314 static Cyg_ErrNo 
315 tty_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len)
316 {
317     cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle;
318     struct tty_private_info *priv = (struct tty_private_info *)t->priv;
319     cyg_io_handle_t chan = (cyg_io_handle_t)priv->dev_handle;
320 #if 0
321     cyg_int32 current_len;
322 #endif
323     Cyg_ErrNo res = ENOERR;
324     // assert(chan)
325     switch (key) {
326     case CYG_IO_SET_CONFIG_TTY_INFO:
327         if (*len != sizeof(cyg_tty_info_t)) {
328             return -EINVAL;
329         }
330         priv->dev_info = *(cyg_tty_info_t *)buf;
331         break;
332     default: // Pass on to serial driver
333         res = cyg_io_set_config(chan, key, buf, len);
334     }
335     return res;
336 }
337 #endif // CYGPKG_IO_SERIAL_TTY