uboot中 main_loop 函数的详细分析
函数位置:common/main.c
函数功能:在标准转入设备中接受命令行,然后分析,查找,执行
温习:Uboot在硬件初始化完成后将进入main_loop()函数,main_loop()函数将进入一个无限循环,当用户输入命令后,首先将调用run_command()命令进行处理,在run_command()函数中,将调用find_cmd()函数把用户从终端输入的命令进行比较,当find_cmd()返回值不为0时证明系统支持用户输入的命令,在对命令进行检验后最后将调用命令处理函数。
#endif
/*中间很多代码都不在smdk2410编译范围中,所以删了减少空间*/
} //end main_loop
分析main_loop中三个重要函数
1. readline函数
位置:common/main.c
功能:提示用户输入,然后一直等待用户输入,指到用户输入完成, 然后获取用户数据,并存入全局变量console_buffer中
/*
* Prompt for input and read a line.
* If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
* time out when time goes past endtime (timebase time in ticks).
* Return: number of read characters
* -1 if break
* -2 if timed out
*/
int readline (const char *const prompt)
{
#ifdef CONFIG_CMDLINE_EDITING /*sdmk2410没定义*/
char *p = console_buffer;
unsigned int len=MAX_CMDBUF_SIZE;
int rc;
static int initted = 0;
if (!initted) {
hist_init();
initted = 1;
}
puts (prompt);
rc = cread_line(p, &len);
return rc < 0 ? rc : len;
#else
char *p = console_buffer;
int n = 0; /* buffer index */
int plen = 0; /* prompt length */
int col; /* output column cnt */
char c;
/* print prompt */
if (prompt) { //smdk2410中传入的是 "SMDK2410 # "
plen = strlen (prompt);
puts (prompt); //输出提示符
}
col = plen;
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME /*sdmk2410没定义*/
while (!tstc()) { /* while no incoming data */
if (retry_time >= 0 && get_ticks() > endtime)
return (-2); /* timed out */
}
#endif
WATCHDOG_RESET(); /* Trigger watchdog, if needed */
#ifdef CONFIG_SHOW_ACTIVITY /*sdmk2410没定义*/
while (!tstc()) {
extern void show_activity(int arg);
show_activity(0);
}
#endif
c = getc(); /*从串口获取用户输入的命令*/
/*
* Special character handling
*/
/*下面这个switch就是处理不同的字符了*/
switch (c) {
case '\r': /* Enter */
case '\n':
*p = '\0';
puts ("\r\n");
return (p - console_buffer); /*输入结束*/
case '\0': /* nul 忽略,继续*/
continue;
case 0x03: /* ^C - break linux下的ctrl+c功能 */
console_buffer[0] = '\0'; /* discard input */
return (-1);
case 0x15: /* ^U - erase line 删除*/
while (col > plen) {
puts (erase_seq);
--col;
}
p = console_buffer;
n = 0;
continue;
case 0x17: /* ^W - erase word 删除 */
p=delete_char(console_buffer, p, &col, &n, plen);
while ((n > 0) && (*p != ' ')) {
p=delete_char(console_buffer, p, &col, &n, plen);
}
continue;
case 0x08: /* ^H - backspace */
case 0x7F: /* DEL - backspace */
p=delete_char(console_buffer, p, &col, &n, plen);
continue;
default: /*获取常规字符*/
/*
* Must be a normal character then
*/
if (n < CFG_CBSIZE-2) {
if (c == '\t') { /* expand TABs */
#ifdef CONFIG_AUTO_COMPLETE
/* if auto completion triggered just continue 自动补全功能*/
*p = '\0';
if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
p = console_buffer + n; /* reset */
continue;
}
#endif
puts (tab_seq+(col&07));
col += 8 - (col&07);
} else {
++col; /* echo input */
putc (c);
}
*p++ = c; /*把字符保存在buffer中*/
++n;
} else { /* Buffer full */
putc ('\a');
}
}
}
#endif /* CONFIG_CMDLINE_EDITING */
}
2. run_command
位置:common/main.c
功能:在命令table中查找匹配的命令名称,得到对应命令结构体变量指针,以解析得到的参数调用其处理函数执行命令
/****************************************************************************
* returns:
* 1 - command executed, repeatable
* 0 - command executed but not repeatable, interrupted commands are
* always considered not repeatable
* -1 - not executed (unrecognized, bootd recursion or too many args)
* (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
* considered unrecognized)
*
* WARNING:
*
* We must create a temporary copy of the command since the command we get
* may be the result from getenv(), which returns a pointer directly to
* the environment data, which may change magicly when the command we run
* creates or modifies environment variables (like "bootp" does).
*/
int run_command (const char *cmd, int flag)
{
cmd_tbl_t *cmdtp;
char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CFG_CBSIZE];
char *str = cmdbuf;
char *argv[CFG_MAXARGS + 1]; /* NULL terminated */
int argc, inquotes;
int repeatable = 1;
int rc = 0;
#ifdef DEBUG_PARSER
printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
puts ("\"\n");
#endif
clear_ctrlc(); /* forget any previous Control C */
if (!cmd || !*cmd) {
return -1; /* empty command */
}
if (strlen(cmd) >= CFG_CBSIZE) {
puts ("## Command too long!\n");
return -1;
}
strcpy (cmdbuf, cmd);
/* Process separators and check for invalid
* repeatable commands
*/
#ifdef DEBUG_PARSER
printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
/*
* Limit the token to data between separators
*/
token = str;
if (*sep) {
str = sep + 1; /* start of command for next pass */
*sep = '\0';
}
else
str = sep; /* no more commands for next pass */
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif
/* find macros in this token and replace them */
process_macros (token, finaltoken);
/* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == 0) {
rc = -1; /* no command at all */
continue;
}
/* Look up command in command table */
if ((cmdtp = find_cmd(argv[0])) == NULL) {
printf ("Unknown command '%s' - try 'help'\n", argv[0]);
rc = -1; /* give up after bad command */
continue;
}
/* found - check max args */
if (argc > cmdtp->maxargs) {
printf ("Usage:\n%s\n", cmdtp->usage);
rc = -1;
continue;
}
#if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
/* avoid "bootd" recursion */
if (cmdtp->cmd == do_bootd) {
#ifdef DEBUG_PARSER
printf ("[%s]\n", finaltoken);
#endif
if (flag & CMD_FLAG_BOOTD) {
puts ("'bootd' recursion detected\n");
rc = -1;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif /* CFG_CMD_BOOTD */
/* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}
repeatable &= cmdtp->repeatable;
/* Did the user stop this? */
if (had_ctrlc ())
return 0; /* if stopped then not repeatable */
}
return rc ? rc : repeatable;
}
3. abortboot函数的分析
功能: abortboot是uboot在引导期间的延时函数。期间可以按键进入uboot的命令行。
位置:common/main.c
static __inline__ int abortboot(int bootdelay)
{
int abort = 0;
printf("Hit any key to stop autoboot: %2d ", bootdelay);
#if defined CONFIG_ZERO_BOOTDELAY_CHECK //如果定义了这个宏,即使定义延时为0,也会检查一次是否有按键按下。只要在这里执行之前按键,还是能进入uboot的命令行。
if (bootdelay >= 0) {
if (tstc()) { /* we got a key press */ 测试是否有按键按下
(void) getc(); /* consume input */接收按键值
puts ("/b/b/b 0");
abort = 1; /* don't auto boot */修改标记,停止自动引导
}
}
#endif
while ((bootdelay > 0) && (!abort)) { //如果延时大于零并且停止标记没有赋值则进入延时循环,直到延时完或者接收到了按 键
int i;
--bootdelay;
/* delay 100 * 10ms */ 每秒中测试按键100次,之后延时10ms。
for (i=0; !abort && i<100; ++i) {
if (tstc()) { /* we got a key press */
abort = 1; /* don't auto boot */*/修改标记,停止自动引导
bootdelay = 0; /* no more delay */延时归零
(void) getc(); /* consume input */获取按键
break;
}
udelay(10000);//延时10000us,也就是10ms
}
printf("/b/b/b%2d ", bootdelay);//打印当前剩余时间
}
putc('/n');
return abort;//返回结果:1-停止引导,进入命令行; 0-引导内核。
}
可以看到uboot延时的单位是秒,如果想提高延时的精度,比如想进行10ms级的延时,将udelay(10000)改为udelay(100)就可以了 。
关于命令的知识点
命令结构体: 在include/command.h中定义如下
/*
* Monitor Command Table
*/
struct cmd_tbl_s {
char *name; /* Command Name 命令名 */
int maxargs; /* maximum number of arguments 最大参数个数 */
int repeatable; /* autorepeat allowed? 是否自动重复 */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* Usage message (short) 简短的帮助信息 */
#ifdef CFG_LONGHELP
char *help; /* Help message (long) 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
小提示:内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/smdk2410.h中定义了CFG_LONGHELP宏,
则在U-Boot中使用help命令查看某个命令的帮助信息时将显示 usage和help字段的内容,否则就只显示usage字段的内容。
习惯上通用命令源代码放在common目录下,并且习惯以“cmd_<命令名>.c”为文件名。
接着看如何定义命令:
//定义section属性的结构体,编译的时候会单独生成一个名为.u_boot_cmd的section段,而凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,
并且即使该变量没有在代码中显式的使用编译器也不产生警告信息)
在 include/command.h 中有定义 #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
1. 每个命令就用下面这个宏来定义,并加进系统命令表中
#ifdef CFG_LONGHELP
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。它定义一个command变量,并把它放入".u_boot_cmd"节中*/
#else /* no long help info */
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
#endif /* CFG_LONGHELP */
定义命令结构体变量,如:
U_BOOT_CMD(
dcache, 2, 1, do_dcache,
"dcache - enable or disable data cache/n",
"[on, off]/n"
" - enable or disable data (writethrough) cache/n"
);
其实就是定义了一个cmd_tbl_t类型的结构体变量,这个结构体变量名为__u_boot_cmd_dcache。
其中变量的五个域初始化为括号的内容。分别指明了命令名,参数个数,重复数,执行命令的函数,命令提示。
每个命令都对应这样一个变量,同时这个结构体变量的section属性为.u_boot_cmd.也就是说每个变量编译结束
在目标文件中都会有一个.u_boot_cmd的section.一个section是连接时的一个输入段,如.text,.bss,.data等都是section名。
最后由链接程序把所有的.u_boot_cmd段连接在一起,这样就组成了一个命令结构体数组。
u-boot.lds中相应脚本如下:
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
可以看到所有的命令结构体变量集中在__u_boot_cmd_start开始到__u_boot_cmd_end结束的连续地址范围内,
这样形成一个cmd_tbl_t类型的数组,run_command函数就是在这个数组中查找命令的。
2. 实现命令处理函数。
命令处理函数的格式:
void function (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
3. 将自己添加的命令文件添加到编译过程中
仿照uboot的格式: CFG_CMD_* 命令选项位标志。在include/cmd_confdefs.h 中定义。
每个板子的配置文件(如include/config/smdk2410.h)中都可以定义u-boot
需要的命令,如果要添加一个命令,必须添加相应的命令选项。如下:
#define CONFIG_COMMANDS /
(CONFIG_CMD_DFL | /
CFG_CMD_CACHE | /
/*CFG_CMD_NAND |*/ /
/*CFG_CMD_EEPROM |*/ /
/*CFG_CMD_I2C |*/ /
/*CFG_CMD_USB |*/ /
CFG_CMD_REGINFO | /
CFG_CMD_DATE | /
CFG_CMD_ELF)
定义这个选项主要是为了编译命令需要的源文件,大部分命令都在common文件夹下对应一个源文件
cmd_*.c ,如cmd_cache.c实现cache命令。 文件开头就有一行编译条件:
#if(CONFIG_COMMANDS&CFG_CMD_CACHE)
也就是说,如果配置头文件中CONFIG_COMMANDS不添加相应命令的选项,这里就不会被编译。
我感觉这里还是要再makefile文件中添加.o文件名
也可以直接将自己添加的文件,无条件编译
做法: 在common/Makefile中加入如下代码:
COBJS = main.o ACEX1K.o altera.o bedbug.o circbuf.o \
cmd_ace.o cmd_autoscript.o cmd_<命令名>.o \
重新编译下载U-Boot就可以使用添加的命令
至此命令的知识点基本完了