浅析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) |