source: SVN/cambria/redboot/packages/redboot/current/src/io.c @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 21.4 KB
Line 
1//==========================================================================
2//
3//      io.c
4//
5//      RedBoot I/O support
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, 2003 Red Hat, Inc.
12// Copyright (C) 2002, 2003 Gary Thomas
13//
14// eCos is free software; you can redistribute it and/or modify it under
15// the terms of the GNU General Public License as published by the Free
16// Software Foundation; either version 2 or (at your option) any later version.
17//
18// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19// WARRANTY; without even the implied warranty of MERCHANTABILITY or
20// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21// for more details.
22//
23// You should have received a copy of the GNU General Public License along
24// with eCos; if not, write to the Free Software Foundation, Inc.,
25// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26//
27// As a special exception, if other files instantiate templates or use macros
28// or inline functions from this file, or you compile this file and link it
29// with other works to produce a work based on this file, this file does not
30// by itself cause the resulting work to be covered by the GNU General Public
31// License. However the source code for this file must still be made available
32// in accordance with section (3) of the GNU General Public License.
33//
34// This exception does not invalidate any other reasons why a work based on
35// this file might be covered by the GNU General Public License.
36//
37// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38// at http://sources.redhat.com/ecos/ecos-license/
39// -------------------------------------------
40//####ECOSGPLCOPYRIGHTEND####
41//==========================================================================
42//#####DESCRIPTIONBEGIN####
43//
44// Author(s):    gthomas
45// Contributors: gthomas,hmt,jlarmour
46// Date:         2000-07-14
47// Purpose:     
48// Description: 
49//             
50// This code is part of RedBoot (tm).
51//
52//####DESCRIPTIONEND####
53//
54//==========================================================================
55
56#include "redboot.h"
57
58#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
59// GDB interface functions
60extern void ungetDebugChar(char c);
61#endif
62
63static void
64do_channel(int argc, char *argv[]);
65
66#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
67RedBoot_cmd("channel", 
68            "Display/switch console channel", 
69            "[-1|<channel number>]",
70            do_channel
71    );
72#else
73RedBoot_cmd("channel", 
74            "Display/switch console channel", 
75            "[<channel number>]",
76            do_channel
77    );
78#endif
79
80static void
81do_channel(int argc, char *argv[])
82{
83    int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
84
85    if (argc == 2) { 
86#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
87        if (strcmp( argv[1], "-1") == 0) {
88            console_selected = false;
89            console_echo = true;
90        } else 
91#endif
92        {
93            unsigned long chan;
94            if ( !parse_num( argv[1], &chan, NULL, NULL) ) {
95                diag_printf("** Error: invalid channel '%s'\n", argv[1]);
96            } else {
97                if (chan < CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
98                    CYGACC_CALL_IF_SET_CONSOLE_COMM(chan);
99                    CYGACC_CALL_IF_SET_DEBUG_COMM(chan);
100                    if (chan != cur)
101                        console_echo = true;
102                }
103                else {
104                    diag_printf("**Error: bad channel number '%s'\n", argv[1]);
105                }
106            }
107        }
108    }
109    /* else display */ 
110    else {
111        diag_printf("Current console channel id: ");
112#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
113        if (!console_selected)
114            diag_printf("-1\n");
115        else
116#endif
117            diag_printf("%d\n", cur);
118    }
119}
120
121void 
122mon_write_char(char c)
123{
124    hal_virtual_comm_table_t *__chan;
125
126#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
127    if (!console_selected) {
128        int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
129        int i;
130        // Send output to all channels
131        for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++) {
132            CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
133            __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
134            CYGACC_COMM_IF_PUTC(*__chan, c);
135        }
136        CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
137    } else 
138#endif
139    {
140        __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
141        if (__chan)
142            CYGACC_COMM_IF_PUTC(*__chan, c);
143        else {
144            __chan = CYGACC_CALL_IF_DEBUG_PROCS();
145            CYGACC_COMM_IF_PUTC(*__chan, c);
146        }
147    }
148}
149
150static void 
151mon_read_char(char *c)
152{
153    hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
154   
155    if (__chan)
156        *c = CYGACC_COMM_IF_GETC(*__chan);
157    else {
158        __chan = CYGACC_CALL_IF_DEBUG_PROCS();
159        *c = CYGACC_COMM_IF_GETC(*__chan);
160    }
161}
162
163#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
164static int _mon_timeout;
165#endif
166
167static bool
168mon_read_char_with_timeout(char *c)
169{
170    bool res = false;
171    hal_virtual_comm_table_t *__chan;
172
173#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
174    if (!console_selected) {
175        int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
176        int i, j, tot;
177        // Try input from all channels
178        tot = 0;
179        while (tot < _mon_timeout) {
180            for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++, tot++) {
181                CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
182                __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
183                res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
184                if (res) {
185                    // Input available on this channel, make it be the console
186                    if (*c != '\0') {
187                        // Don't chose this unless real data have arrived
188                        console_selected = true;
189                        CYGACC_CALL_IF_SET_DEBUG_COMM(i);
190                        // Disable interrupts on all channels but this one
191                        for (j = 0;  j < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  j++) {
192                            if (i != j) {
193                                CYGACC_CALL_IF_SET_CONSOLE_COMM(j);
194                                __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
195                                CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_IRQ_DISABLE);
196                            }
197                        }
198                        CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
199                        return res;
200                    }
201                }
202            }
203        }
204        CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);       
205    } else 
206#endif
207    {
208        __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
209        if (__chan)
210            res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
211        else {
212            __chan = CYGACC_CALL_IF_DEBUG_PROCS();
213            res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
214        }
215    }
216    return res;
217}
218
219static void
220mon_set_read_char_timeout(int ms)
221{
222    hal_virtual_comm_table_t *__chan;
223
224#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
225    if (!console_selected) {
226        int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
227        int i;
228        // Set timeout to minimum on each channel; total amounts to desired value
229        _mon_timeout = ms;
230        ms = 1;
231        for (i = 0;  i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS;  i++) {
232            CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
233            if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
234                CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
235            }
236        }
237        CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);       
238    } else 
239#endif
240    {
241        if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
242            CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
243        }
244        if ((__chan = CYGACC_CALL_IF_DEBUG_PROCS()) != 0) {
245            CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
246        }
247    }
248}
249
250//
251// Test for ^C on the console.  CAUTION! discards all console input
252//
253bool
254_rb_break(int timeout)
255{
256    char c;
257    mon_set_read_char_timeout(timeout);
258    if (mon_read_char_with_timeout(&c)) {
259        if (c == 0x03) {  // Test for ^C
260            return true;
261        }
262    }
263    return false;
264}
265
266#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
267#define __STRINGIFY(x) #x
268#define _STRINGIFY(x) __STRINGIFY(x)
269#define _STARTUP_STR _STRINGIFY(CYG_HAL_STARTUP) "}"
270
271//
272// Read a character from script.
273// Return true if script character found, false if not.
274//
275static int
276getc_script(char *cp)
277{
278    static bool newline = true;
279    bool skip;
280
281    while (script && *script) {
282        if (newline && *script == '{') {
283            skip = false;
284            ++script;
285
286            // skip if it isn't for this startup type
287            if (strncmp(script, _STARTUP_STR, strlen(_STARTUP_STR)))
288                skip = true;
289
290            // skip past "{...}"
291            while (*script && *script++ != '}')
292                ;
293
294            // skip script line if neccessary
295            if (skip) {
296                while (*script && *script++ != '\n')
297                    ;
298            } else
299                newline = false;
300
301        } else {
302            *cp = *script++;
303            if (*cp == '\n') {
304              newline = true;
305            } else {
306              newline = false;
307            }
308            return true;
309        }
310    }
311    return false;
312}
313#endif
314
315#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
316#define _CL_NUM_LINES CYGNUM_REDBOOT_CMD_LINE_EDITING       // Number of lines to keep
317static char _cl_lines[_CL_NUM_LINES][CYGPKG_REDBOOT_MAX_CMD_LINE];
318static int  _cl_index = -1;      // Last known command line
319static int  _cl_max_index = -1;  // Last command in buffers
320
321#ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
322static void expand_history(char *);
323#endif
324#endif
325
326//
327// Read a line of input from the user
328// Return:
329//        _GETS_OK: 'n' valid characters received
330//       _GETS_GDB: '$' (GDB lead-in)
331//   _GETS_TIMEOUT: No input before timeout
332//     _GETS_CTRLC: ^C typed
333//
334// if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
335//   Command line history support
336//    ^P - Select previous line from history
337//    ^N - Select next line from history
338//    ^A - Move insertion [cursor] to start of line
339//    ^E - Move cursor to end of line
340//    ^B - Move cursor back [previous character]
341//    ^F - Move cursor forward [next character]
342//
343int
344_rb_gets_preloaded(char *buf, int buflen, int timeout)
345{
346    char *ip = buf;   // Insertion point
347    char *eol = buf;  // End of line
348    char c;
349    bool res = false;
350    static char last_ch = '\0';
351    int _timeout;
352#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
353    int _index = _cl_index;  // Last saved line
354    char *xp;
355#endif
356
357    // Display current buffer data
358    while (*eol) {
359        mon_write_char(*eol++);
360    }
361    ip = eol;
362
363    while (true) {
364#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
365        if (getc_script(&c))
366            do_idle(false);
367        else
368#endif
369        if ((timeout > 0) && (eol == buf)) {
370#define MIN_TIMEOUT 50
371            _timeout = timeout > MIN_TIMEOUT ? MIN_TIMEOUT : timeout;
372            mon_set_read_char_timeout(_timeout);
373            while (timeout > 0) {
374                res = mon_read_char_with_timeout(&c);
375                if (res) {
376                    // Got a character
377                    do_idle(false);
378                    break;
379                }
380                timeout -= _timeout;
381            }
382            if (res == false) {
383                do_idle(true);
384                return _GETS_TIMEOUT;  // Input timed out
385            }
386        } else {
387            mon_read_char(&c);
388        }
389        *eol = '\0';
390        switch (c) {
391#define CTRL(c) ((c)&0x1F)
392#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
393        case CTRL('P'):
394            // Fetch the previous line into the buffer
395            if (_index >= 0) {
396                // Erase the previous line [crude]
397                while (ip != buf) {
398                    mon_write_char('\b');
399                    mon_write_char(' ');
400                    mon_write_char('\b');
401                    ip--;
402                }
403                strcpy(buf, _cl_lines[_index]);
404                while (*ip) {
405                    mon_write_char(*ip++);
406                }
407                eol = ip;
408                // Move to previous line
409                _index--;
410                if (_index < 0) {
411                    _index = _cl_max_index;
412                }
413            } else {
414                mon_write_char(0x07);  // Audible bell on most devices
415            }
416            break;
417        case CTRL('N'):
418            // Fetch the next line into the buffer
419            if (_index >= 0) {
420                if (++_index > _cl_max_index) _index = 0;
421                // Erase the previous line [crude]
422                while (ip != buf) {
423                    mon_write_char('\b');
424                    mon_write_char(' ');
425                    mon_write_char('\b');
426                    ip--;
427                }
428                strcpy(buf, _cl_lines[_index]);
429                while (*ip) {
430                    mon_write_char(*ip++);
431                }
432                eol = ip;
433            } else {
434                mon_write_char(0x07);  // Audible bell on most devices
435            }
436            break;
437        case CTRL('B'): 
438            // Move insertion point backwards
439            if (ip != buf) {
440                mon_write_char('\b');
441                ip--;
442            }
443            break;
444        case CTRL('F'):
445            // Move insertion point forwards
446            if (ip != eol) {
447                mon_write_char(*ip++);
448            }
449            break;
450        case CTRL('E'):
451            // Move insertion point to end of line
452            while (ip != eol) {
453                mon_write_char(*ip++);
454            }
455            break;
456        case CTRL('A'):
457            // Move insertion point to beginning of line
458            if (ip != buf) {
459                xp = ip;
460                while (xp-- != buf) {
461                    mon_write_char('\b');
462                }
463            }
464            ip = buf;
465            break;
466        case CTRL('K'):
467            // Kill to the end of line
468            if (ip != eol) {
469                xp = ip;
470                while (xp++ != eol) {
471                    mon_write_char(' ');
472                }
473                while (--xp != ip) {
474                    mon_write_char('\b');
475                }
476                eol = ip;
477            }
478            break;
479        case CTRL('D'):
480            // Erase the character under the cursor
481            if (ip != eol) {
482                xp = ip;
483                eol--;
484                while (xp != eol) {
485                    *xp = *(xp+1);
486                    mon_write_char(*xp++);
487                }
488                mon_write_char(' ');  // Erases last character
489                mon_write_char('\b');
490                while (xp-- != ip) {
491                    mon_write_char('\b');
492                }
493            }
494            break;
495#endif // CYGNUM_REDBOOT_CMD_LINE_EDITING
496        case CTRL('C'): // ^C
497            // Abort current input
498            diag_printf("^C\n");
499            *buf = '\0';  // Nothing useful in buffer
500            return _GETS_CTRLC;
501        case '\n':
502        case '\r':
503            // If previous character was the "other" end-of-line, ignore this one
504            if (((c == '\n') && (last_ch == '\r')) ||
505                ((c == '\r') && (last_ch == '\n'))) {
506                c = '\0';
507                break;
508            }
509            // End of line
510            if (console_echo) {
511                mon_write_char('\r');
512                mon_write_char('\n');
513            }
514            last_ch = c;
515#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
516            if (cmd_history) {
517                // History handling - only when enabled
518#ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
519                expand_history(buf);
520#endif
521                if (*buf != '\0') {
522                    if (++_cl_index == _CL_NUM_LINES) _cl_index = 0;
523                    if (_cl_index > _cl_max_index) _cl_max_index = _cl_index;
524                    strcpy(_cl_lines[_cl_index], buf);
525                }
526            }
527#endif
528            return _GETS_OK;
529        case '\b':
530        case 0x7F:  // DEL
531            if (ip != buf) {
532#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
533                if (ip != eol) {
534                    ip--;
535                    mon_write_char('\b');
536                    xp = ip;
537                    while (xp != (eol-1)) {
538                        *xp = *(xp+1);
539                        mon_write_char(*xp++);
540                    }
541                    mon_write_char(' ');  // Erases last character
542                    mon_write_char('\b');
543                    while (xp-- != ip) {
544                        mon_write_char('\b');
545                    }
546                } else {
547                    if (console_echo) {
548                        mon_write_char('\b');
549                        mon_write_char(' ');
550                        mon_write_char('\b');
551                    }
552                    ip--;
553                }
554                eol--;
555#else
556                if (console_echo) {
557                    mon_write_char('\b');
558                    mon_write_char(' ');
559                    mon_write_char('\b');
560                }
561                ip--;
562                eol--;
563#endif
564            }
565            break;
566#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
567        case '+': // fall through
568        case '$':
569            if (ip == buf || last_ch != '\\')
570            {
571                // Give up and try GDB protocol
572                ungetDebugChar(c);  // Push back character so stubs will see it
573                return _GETS_GDB;
574            }
575            if (last_ch == '\\') {
576#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
577                if (ip == eol) {
578                    // Just save \$ as $
579                    eol = --ip;
580                } else {
581                    mon_write_char('\b');
582                    *--ip = c;
583                    mon_write_char(c);
584                    break;
585                }
586#else
587                ip--;  // Save \$ as $
588#endif
589            }
590            // else fall through
591#endif
592        default:
593#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
594            // If the insertion point is not at the end of line, make space for it
595            if (ip != eol) {
596                xp = eol;
597                *++eol = '\0';
598                while (xp != ip) {
599                    *xp = *(xp-1);
600                    xp--;
601                }
602            }
603#endif
604            if (console_echo) {
605                mon_write_char(c);
606            }
607            if (ip == eol) {
608                // Advance both pointers
609                *ip++ = c;
610                eol = ip;
611#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
612            } else {
613                // Just insert the character
614                *ip++ = c;
615                xp = ip;
616                while (xp != eol) {
617                    mon_write_char(*xp++);
618                }
619                while (xp-- != ip) {
620                    mon_write_char('\b');
621                }
622#endif
623            }
624        }
625        last_ch = c;
626        if (ip == buf + buflen - 1) { // Buffer full
627            *ip = '\0';
628            return buflen;
629        }
630    }
631}
632
633int
634_rb_gets(char *buf, int buflen, int timeout)
635{
636    *buf = '\0';  // Empty buffer
637    return _rb_gets_preloaded(buf, buflen, timeout);
638}
639
640static bool
641_verify_action(int timeout, char *fmt, va_list ap)
642{
643    char ans[8];
644    int ret;
645#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
646    // Don't ask if we're executing a script
647    if (script && *script)
648        return 1;
649#endif
650
651    diag_vprintf(fmt, ap);
652    diag_printf(" - continue (y/n)? ");
653    if ((ret = _rb_gets(ans, sizeof(ans), timeout)) > 0) {
654        return ((ans[0] == 'y') || (ans[0] == 'Y'));
655    } else {
656        if (ret == _GETS_TIMEOUT) {
657            diag_printf(" ** Timed out!\n");
658        }
659        return 0;  // Timed out or ^C
660    }
661}
662
663bool
664verify_action(char *fmt, ...)
665{
666    va_list ap;
667
668    va_start(ap, fmt);
669    return _verify_action(0, fmt, ap);
670}
671
672bool
673verify_action_with_timeout(int timeout, char *fmt, ...)
674{
675    va_list ap;
676
677    va_start(ap, fmt);
678    return _verify_action(timeout, fmt, ap);
679}
680
681
682#ifdef CYGBLD_REDBOOT_CMD_LINE_HISTORY
683// parse for history index number. Return number or -1 if not
684// an index number.
685static int
686parse_history_index(char *s)
687{
688    int val = 0;
689
690    while ('0' <= *s && *s <= '9')
691        val = (val * 10) + (*s++ - '0');
692
693    if (*s)
694        return -1;
695
696    return val;
697}
698
699// Check input line to see if it needs history expansion. If so,
700// try to find matching command and replace buffer as appropriate.
701static void
702expand_history(char *buf)
703{
704    int ncmds = _cl_max_index + 1;
705    int i, index, len;
706
707    if (buf[0] != '!' || buf[1] == '\0')
708        return;
709
710    if (ncmds > 0) {
711        if (!strcmp(buf, "!!")) {
712            strcpy(buf, _cl_lines[_cl_index]);
713            return;
714        }
715        if ((index = parse_history_index(buf + 1)) >= 0) {
716            if (index <= _cl_max_index) {
717                strcpy(buf, _cl_lines[index]);
718                return;
719            }
720        }
721        len = strlen(buf + 1);
722        for (i = 0, index = _cl_index; i < ncmds; i++) {
723            if (!strncmp(_cl_lines[index], buf+1, len)) {
724                strcpy(buf, _cl_lines[index]);
725                return;
726            }
727            if (--index < 0)
728                index = _cl_max_index;
729        }
730    }
731
732    diag_printf("%s: event not found\n", buf);
733    *buf = '\0';
734}
735
736static void
737do_history(int argc, char *argv[])
738{
739    int ncmds = _cl_max_index + 1;
740    int i, index;
741
742    if (_cl_index == _cl_max_index) {
743        // history has not wrapped around
744        for (i = 0; i < ncmds; i++)
745            diag_printf("%3d %s\n", i, _cl_lines[i]);
746    } else {
747        for (i = 0, index = _cl_index + 1; i < ncmds; i++) {
748            diag_printf("%3d %s\n", i, _cl_lines[index++]);
749            if (index > _cl_max_index)
750                index = 0;
751        }
752    }
753}
754
755RedBoot_cmd("history", 
756            "Display command history", 
757            "",
758            do_history
759    );
760#endif  // CYGBLD_REDBOOT_CMD_LINE_HISTORY
Note: See TracBrowser for help on using the repository browser.