Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15530417
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类:

2009-07-05 17:07:10

浅析redboot如何格式化serial输入和script脚本是怎么被读出来的

  redboot的交互过程类似于linux中的shell,redboot中设计的交互快捷键,i
完全参照了linux下terminal中的快捷键:

        ctrl+a 将光标移动到输入的所有数据的开头
        ctrl+e 将光标移动到输入的所有数据的末尾
        ctrl+b 将光标向后移动
        ctrl+f 将光标向前移动
        ctrl+d 删除光标所在字符
        ctrl+k 将光标之后的所有内容删除掉
        还有一个快捷键redboot没有实现,即:ctrl+u,它是将光标之前的所有数据清除
        ctrl+p 查询前一个历史命令
        ctrl+n 查询下一个历史命令

来看看源码实现[luther.gliethttp]:
void
cyg_start(void)
{
    ......
    static char line[CYGPKG_REDBOOT_MAX_CMD_LINE];
    ......
    unsigned char *hold_script = script;
    script = (unsigned char *)0; // 下面的_rb_gets()函数将因为script为0,而跳过读取script操作[luther.gliethttp]
    res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
    if (res == _GETS_CTRLC) { // 如果用户从serial串口输入了ctrl+c组合
        script = (unsigned char *)0;  // Disable script
    } else {
        script = hold_script;  // Re-enable script,这样再次执行_rb_gets()时,就会读取script脚本,然后执行它了[luther.gliethtp]
    }
    ......
}

int
_rb_gets(char *buf, int buflen, int timeout)
{
    *buf = '\0';  // Empty buffer
    return _rb_gets_preloaded(buf, buflen, timeout);
}

// 如下对_rb_gets_preloaded返回值含义进行了描述[luthe.rligehtttp]
// Read a line of input from the user
// Return:
//        _GETS_OK: 'n' valid characters received
//       _GETS_GDB: '$' (GDB lead-in)
//   _GETS_TIMEOUT: No input before timeout
//     _GETS_CTRLC: ^C typed
//
int
_rb_gets_preloaded(char *buf, int buflen, int timeout)
{
    char *ip = buf;   // Insertion point
    char *eol = buf;  // End of line
    char c;
    bool res = false;
    static char last_ch = '\0';
    int _timeout;
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0 // 我们定义该值为16
//
// Command line history support
//    ^P - Select previous line from history
//    ^N - Select next line from history
//    ^A - Move insertion [cursor] to start of line
//    ^E - Move cursor to end of line
//    ^B - Move cursor back [previous character]
//    ^F - Move cursor forward [next character]
//
// 我们定义CYGNUM_REDBOOT_CMD_LINE_EDITING该值为16
#define _CL_NUM_LINES CYGNUM_REDBOOT_CMD_LINE_EDITING       // Number of lines to keep
    // 保持16行数据,类似history
    // CYGPKG_REDBOOT_MAX_CMD_LINE我们定义为256字节[luther.gliethttp]
    static char _cl_lines[_CL_NUM_LINES][CYGPKG_REDBOOT_MAX_CMD_LINE];
    // 循环缓冲区指针
    static int  _cl_index = -1;      // Last known command line
    static int  _cl_max_index = -1;  // Last command in buffers
    int _index = _cl_index;  // Last saved line
    char *xp;
#endif

    // Display current buffer data
    while (*eol) {
        // 先drain空buf中的内容,如果有的话[luther.gliethttp]
        mon_write_char(*eol++);
    }
    ip = eol; // insert point插入点指针

    while (true) {
#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
        // 我们定义了该宏
        if (getc_script(&c)) // 从script脚本中读取一个字节数据,同时script++,源码见后
            do_idle(false); // 执行定义的idle函数,源码见后
        else
#endif
        if ((timeout > 0) && (eol == buf)) { // 所以如果我们只是输入了几个字符串在minicom中,而并不按回车,那么程序将
        // 永远停留在下面的mon_read_char(&c);上[luther.gliethttp]
#define MIN_TIMEOUT 50
            _timeout = timeout > MIN_TIMEOUT ? MIN_TIMEOUT : timeout;
            mon_set_read_char_timeout(_timeout);
            while (timeout > 0) {
                // 最大超时_timeout,等待serial串口数据到来[luther.gliethttp]
                res = mon_read_char_with_timeout(&c);
                if (res) {
                    // Got a character
                    // 从serail读到一个字符
                    do_idle(false); // 再次尝试执行idle函数
                    break;
                }
                timeout -= _timeout;
            }
            if (res == false) {
                do_idle(true);
                return _GETS_TIMEOUT;  // Input timed out
            }
        } else {
            mon_read_char(&c); // 读一个字符
        }
        *eol = '\0';
        switch (c) {
#define CTRL(c) ((c)&0x1F)
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
        case CTRL('P'):
            // Fetch the previous line into the buffer
            if (_index >= 0) {
                // Erase the previous line [crude]
                // 发送'\b'让minicom向后退格一个字符,然后退格
                while (ip != buf) {
                // 删除上面mon_write_char(*eol++);发送出去的字符,如果发送了的话.
                    mon_write_char('\b');
                    mon_write_char(' '); // 发送' '覆盖原有数据,所以这样看上去minicom就删除了一个字符[luther.gliethttp]
                    mon_write_char('\b');
                    ip--;
                }
                // 现在ip等于buf了
                strcpy(buf, _cl_lines[_index]); // 将前一个cmd从_cl_lines[]取出来
                while (*ip) {
                    mon_write_char(*ip++); // 将命令通过serial发送给minicom.
                }
                eol = ip; // eol等于当前插入缓冲区指针ip
                // Move to previous line
                _index--; // 调整到前一个命令行历史记录
                if (_index < 0) {
                    _index = _cl_max_index; // 循环到最大处,不保证此处有有效命令
                }
            } else {
                // 发送bell铃声数据,minicom会让pc的bios发声[luther.gliethttp]
                mon_write_char(0x07);  // Audible bell on most devices
            }
            break;
        case CTRL('N'):
            // Fetch the next line into the buffer
            // 操作和上面的'P'操作一致
            if (_index >= 0) {
                if (++_index > _cl_max_index) _index = 0;
                // Erase the previous line [crude]
                while (ip != buf) {
                    mon_write_char('\b');
                    mon_write_char(' ');
                    mon_write_char('\b');
                    ip--;
                }
                strcpy(buf, _cl_lines[_index]);
                while (*ip) {
                    mon_write_char(*ip++);
                }
                eol = ip;
            } else {
                mon_write_char(0x07);  // Audible bell on most devices
            }
            break;
        case CTRL('B'):
            // Move insertion point backwards
            // 向后移动光标,ip当前insert point减减,eof不变[luther.gliethttp]
            if (ip != buf) {
                mon_write_char('\b');
                ip--;
            }
            break;
        case CTRL('F'):
            // Move insertion point forwards
            // 如果当前ip还没有到eol,那么前移,即,再在此处输出一次同样的字符[luther.gliethttp]
            if (ip != eol) {
                mon_write_char(*ip++);
            }
            break;
        case CTRL('E'):
            // Move insertion point to end of line
            // 原理同上
            while (ip != eol) {
                mon_write_char(*ip++);
            }
            break;
        case CTRL('A'):
            // Move insertion point to beginning of line
            // 原理同上
            if (ip != buf) {
                xp = eol; // 这里是个bug,应该改为xp = ip;才正常[luther.gliethttp]
                while (xp-- != buf) {
                    mon_write_char('\b');
                }
            }
            ip = buf;
            break;
        case CTRL('K'):
            // 从当前位置一直删除到结尾字符
            // Kill to the end of line
            if (ip != eol) {
                xp = ip;
                while (xp++ != eol) {
                    // 从当前插入指针所在出ip开始输出' '空格,一直到eol
                    // 这样原有的数据都因为' '而看上去像是被删除了似的[luther.gliethttp]
                    mon_write_char(' ');
                }
                while (--xp != ip) {
                    mon_write_char('\b'); // 退格在退到原来的ip处
                }
                eol = ip; // 因为当前ip后的数据已经全部清空,所以当前ip就是eol.
            }
            break;
        case CTRL('D'):
            // Erase the character under the cursor
            if (ip != eol) {
                xp = ip;
                eol--; // 彻底删除当前ip所指字符,所以eol少1.
                while (xp != eol) {
                    *xp = *(xp+1);
                    mon_write_char(*xp++);
                }
                mon_write_char(' ');  // Erases last character
                mon_write_char('\b');
                while (xp-- != ip) {
                    mon_write_char('\b');
                }
            }
            break;
#endif // CYGNUM_REDBOOT_CMD_LINE_EDITING
        case CTRL('C'): // ^C
            // Abort current input
            diag_printf("^C\n");
            *buf = '\0';  // Nothing useful in buffer
            return _GETS_CTRLC; // ok,收到了ctrl+c组合.
        case '\n':
        case '\r':
            // If previous character was the "other" end-of-line, ignore this one
            if (((c == '\n') && (last_ch == '\r')) ||
                ((c == '\r') && (last_ch == '\n'))) {
                // 如果前一个是与其配对的另外一个字符,那么退出[luther.gliethttp]
                c = '\0'; // 追加'\0'字符串结尾符号
                break;
            }
            // End of line
            if (console_echo) {
                    // 发送回车换行给console,这样console也才能出现回车换行[luther.gliethttp]
                    mon_write_char('\r');
                    mon_write_char('\n');
            }
            last_ch = c; // 记录当前输入
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
            if (cmd_history && (buf != eol)) {
                // Save current line - only when enabled
                // 允许作history时,才执行如下操作,将当前serial获得的命令行,存入_cl_lines[]历史记录缓冲区.
                if (++_cl_index == _CL_NUM_LINES) _cl_index = 0;
                if (_cl_index > _cl_max_index) _cl_max_index = _cl_index;
                strcpy(_cl_lines[_cl_index], buf);
            }
#endif
            return _GETS_OK;
        case '\b':
        case 0x7F:  // DEL
            if (ip != buf) {
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
                if (ip != eol) {
                    ip--;
                    mon_write_char('\b');
                    xp = ip;
                    while (xp != (eol-1)) {
                        *xp = *(xp+1);
                        mon_write_char(*xp++);
                    }
                    mon_write_char(' ');  // Erases last character
                    mon_write_char('\b');
                    while (xp-- != ip) {
                        mon_write_char('\b');
                    }
                } else {
                    // 如果当前ip就在结尾,那么直接删除最后1个显示的字符就可以了[luther.gliethttp]
                    if (console_echo) {
                        mon_write_char('\b');
                        mon_write_char(' ');
                        mon_write_char('\b');
                    }
                    ip--;
                }
                eol--; // 结尾少1
#else
                if (console_echo) {
                    mon_write_char('\b');
                    mon_write_char(' ');
                    mon_write_char('\b');
                }
                ip--;
                eol--;
#endif
            }
            break;
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
        case '+': // fall through
        case '$':
            if (ip == buf || last_ch != '\\') // '\'为转义字符,如果打算输入$或者+符号的话.
            {
                // Give up and try GDB protocol
                ungetDebugChar(c);  // Push back character so stubs will see it
                return _GETS_GDB;
            }
            if (last_ch == '\\') {
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
                if (ip == eol) {
                    // 将前一个转义符号删除
                    // Just save \$ as $
                    eol = --ip;
                } else {
                    // 退格到'\'显示位置
                    mon_write_char('\b');
                    *--ip = c; // 将buf中的'\'替换为$或者+
                    // 然后输出$或者+符号,覆盖'\'[luther.gliethttp].
                    mon_write_char(c);
                    break;
                }
#else
                ip--;  // Save \$ as $
#endif
            }
            // else fall through
#endif
        default:
        // 如果串口serail发送过来的数据不是上面的控制字符,
        // 那么执行下面的普通字符处理流程.
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
            // If the insertion point is not at the end of line, make space for it
            if (ip != eol) {
                xp = eol;
                *++eol = '\0'; // 末尾追加'\0'字符串结尾[luther.gliethttp]
                while (xp != ip) {
                    *xp = *(xp-1); // 将ip后面的所有字符串向后移动一个字节空间,这样ip就可以插入新输入的字符了
                    xp--;
                }
            }
#endif
            if (console_echo) {
                mon_write_char(c); // 显示该字符
            }
            if (ip == eol) {
                // Advance both pointers
                *ip++ = c; // 如果ip就在eol结尾,那么直接送入insert pointer缓冲区即可[luther.gliethttp]
                eol = ip;
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
            } else {
                // Just insert the character
                *ip++ = c; // ip不在结尾,那么调整显示到pc串口软件上的内容[luther.gliethttp]
                xp = ip;
                while (xp != eol) {
                    mon_write_char(*xp++);
                }
                while (xp-- != ip) {
                    mon_write_char('\b');
                }
#endif
            }
        }
        last_ch = c;
        if (ip == buf + buflen - 1) { // Buffer full
            *ip = '\0'; // 缓冲区满了,其实应该检查eol才对,因为处在非结尾的ip,必定eol先到达buf[]结尾[luther.gliethttp]
            return buflen;
        }
    }
}

#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
#define __STRINGIFY(x) #x
#define _STRINGIFY(x) __STRINGIFY(x)
#define _STARTUP_STR _STRINGIFY(CYG_HAL_STARTUP) "}"
//
// Read a character from script.
// Return true if script character found, false if not.
//
static int
getc_script(char *cp)
{
    static bool newline = true;
    bool skip;
// 我的script脚本内容一共包含3行,如下:
// fis load ramdisk
// fis load zImage
// exec -r 0x800000 -s 0x600000

    while (script && *script) {
    if (newline && *script == '{') {
        // 对于我的script脚本来说,永远不会执行到这里
        skip = false;
        ++script;

        // skip if it isn't for this startup type
        // script是否等于"ROMRAM}",我的ep9312配置redboot为ROMRAM启动方式[luther.gliethttp]
        // 所以我的上面的script脚本内容也可以写成
        // {ROMRAM}fis load ramdisk
        // 所以我们的script可以写成
        // fis load ramdisk
        // {ROMRAM}fis load zImage
        // {ROMRAM}fis list
        // {ROM}fconfig
        // exec -r 0x800000 -s 0x600000

        if (strncmp(script, _STARTUP_STR, strlen(_STARTUP_STR)))
        skip = true; // 如果不等与"{ROMRAM}",那么跳过
        
        // skip past "{...}"
        while (*script && *script++ != '}') // 跳过{ }之间的所有内容,可能是RAM或者ROM启动需要的特有参数[luther.gliethttp]
        ;

        // skip script line if neccessary
        if (skip) {
        // 既然是跳,那么就完全跳过1行吧[luther.gliethttp]
        while (*script && *script++ != '\n')
            ;
        } else
        newline = false;

    } else {
        *cp = *script++; // 读取1个字符,然后script指针加加[luther.gliethttp]
        if (*cp == '\n') {
              newline = true; // 下一个字符就是新行了'\r\n'或者linux中直接一个'\n'[luther.gliethttp]
            } else {
              newline = false;
            }
        return true;
    }
    }
    return false;
}
#endif

void
do_idle(bool is_idle)
{
    struct idle_tab_entry *idle_entry;

    for (idle_entry = __RedBoot_IDLE_TAB__;
         idle_entry != &__RedBoot_IDLE_TAB_END__;  idle_entry++) {
        (*idle_entry->fun)(is_idle);
    }
}
阅读(1749) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~