/*
** 对u-boot-1.1.6中 main_loop()函数的理解。
** main_loop function in diractory: /common/main.c。
** u-boot启动内核过程:start.S -> start_armboot() -> main_loop() -> run_command() -> do_bootm_linux() ->
theKernel (0, bd->bi_arch_number, bd->bi_boot_params) ,then start kernel core.
*/
/*
** 由于官方发布的源文件是通用版本,使用了很多条件编译,代码看起来很乱。
** 这里根据实际使用情况(默认条件),去掉一些条件编译,使得代码清晰,然后来分析。
*/
void main_loop (void)
{
static char lastcommand[CFG_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
char *s;
int bootdelay;
/*
** 函数getenv从环境变量内存块中查找环境变量值bootdelay,如果存在,就返回在环境变量内存块的地址。
** 如果不存在这个环境变量值,那么返回NULL。
** 调用关系:getenv -> env_get_char -> env_get_char_init -> env_get_char_spec.
*/
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
s = getenv ("bootcmd");
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "
/*
** abortboot()函数是获取串口输入,如果在设定的延时时间bootdelay内没有字符,那么返回0,否则返回1.
** 1.如果返回0,说明没有输入任何命令,那么就执行run_command()函数,这里的s = bootcmd,
** 就是执行引导内核的命令,u-boot的任务就此完成,进入Linux的世界;
** 2.如果返回1,这个if条件不满足,就执行下面的for 死循环,等待串口输入命令,并且执行命令;
*/
if (bootdelay >= 0 && s && !abortboot (bootdelay))
{
run_command (s, 0);
}
/*
* Main Loop for Monitor Command Processing
*/
for (;;) {
len = readline (CFG_PROMPT);
flag = 0; /* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
if (len == -1)
puts ("
else
rc = run_command (lastcommand, flag);
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
}
/*
** Look up variable from environment,return address of storage for that variable,or NULL if not found.
** dir: common/cmd_nvedit.c
*/
char *getenv (char *name)
{
int i, nxt;
WATCHDOG_RESET();
/*
** 在文件dir: common/common.c 中定义:uchar (*env_get_char)(int) = env_get_char_init;
** 所以,env_get_char是一个函数指针,指向了函数env_get_char_init (dir:common.c)。
** 函数env_get_char_init又调用了env_get_char_spec (dir:env_flash.c)。
** 我认为这就是一种封装思维,分文件(cmd_nvedit.c) ->总文件(common.c) -分文件(cmd_flash.c),
** common.c就是一个接口文件,分文件通过这个接口调用别的分文件,而不是直接调用别的分文件。
** 整个函数getenv的思路是:
** 1.先明白环境变量的储存方式:"bootargs=" CONFIG_BOOTARGS "\0" "bootcmd=" CONFIG_BOOTCOMMAND "\0" ......;
** 2.从环境变量分区的开始地址gd->env_addr,按byte by byte的往后查找,先找出第一个环境变量的值,然后用name去比较
环境变量的值,如果匹配,则到下一步,计算环境变量地址值,且退出整个getenv函数。如果不匹配,继续找出下一个
环境变量值,再与name比较,如此循环,直到环境变量内存区的结束地址为止。
** 3.如果环境变量校验错误,使用默认环境变量数组的值,直接使用数组下标查找,这样代码更容易理解些。
*/
for (i=0; env_get_char(i) != '\0'; i=nxt+1)
{
int val;
/*
** 从当前地址开始找到第一个字符\0,即找出了一个环境变量值,然后退出循环。
** 如果一直没有找到,且超过了环境变量最大长度CFG_ENV_SIZE则退出所有for循环返回NULL。
*/
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt)
{
if (nxt >= CFG_ENV_SIZE)
{
return (NULL);
}
}
/*
** dir: common/cmd_nvedit.c
** 如果匹配到了环境变量值,则返回此环境变量值在环境变量内存区的地址偏移量。
*/
if ((val=envmatch((uchar *)name, i)) < 0)
continue;
/*
** dir: common/cmd_nvedit.c
** 匹配环境变化成功,说明我们确实定义了这个环境变量值,计算出此环境变量在环境变量内存区的地址。
*/
return ((char *)env_get_addr(val));
}
return (NULL);
}
static uchar env_get_char_init (int index) //dir:common.c
{
uchar c;
/* if crc was bad, use the default environment */
if (gd->env_valid)
{
c = env_get_char_spec(index);
} else {
c = default_environment[index];
}
return (c);
}
uchar env_get_char_spec (int index) //dir:env_flash.c
{
return ( *((uchar *)(gd->env_addr + index)) );
}