1> main_loop  common/main.c

/****************************************************************************/

void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
static char lastcommand[CONFIG_SYS_CBSIZE] = { , };
int len;
int rc = ;
int flag;
#endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
char *s;
int bootdelay;
#endif
#ifdef CONFIG_PREBOOT
char *p;
#endif
#ifdef CONFIG_BOOTCOUNT_LIMIT
unsigned long bootcount = ;
unsigned long bootlimit = ;
char *bcs;
char bcs_set[];
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO)
ulong bmp = ; /* default bitmap */
extern int trab_vfd (ulong bitmap); #ifdef CONFIG_MODEM_SUPPORT
if (do_mdm_init)
bmp = ; /* alternate bitmap */
#endif
trab_vfd (bmp);
#endif /* CONFIG_VFD && VFD_TEST_LOGO */ #ifdef CONFIG_BOOTCOUNT_LIMIT
bootcount = bootcount_load();
bootcount++;
bootcount_store (bootcount);
sprintf (bcs_set, "%lu", bootcount);
setenv ("bootcount", bcs_set);
bcs = getenv ("bootlimit");
bootlimit = bcs ? simple_strtoul (bcs, NULL, ) : ;
#endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_MODEM_SUPPORT
debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init);
if (do_mdm_init) {
char *str = strdup(getenv("mdm_cmd"));
setenv ("preboot", str); /* set or delete definition */
if (str != NULL)
free (str);
mdm_init(); /* wait for modem connection */
}
#endif /* CONFIG_MODEM_SUPPORT */ #ifdef CONFIG_VERSION_VARIABLE
{
extern char version_string[]; setenv ("ver", version_string); /* set version variable */
}
#endif /* CONFIG_VERSION_VARIABLE */ #ifdef CONFIG_SYS_HUSH_PARSER
u_boot_hush_start ();
#endif #if defined(CONFIG_HUSH_INIT_VAR)
hush_init_var ();
#endif #ifdef CONFIG_AUTO_COMPLETE
install_auto_complete(); //安装自动补全的函数,分析如下
#endif #ifdef CONFIG_PREBOOT
if ((p = getenv ("preboot")) != NULL) {
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (p, );
# else
parse_string_outer(p, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
}
#endif /* CONFIG_PREBOOT */ #if defined(CONFIG_UPDATE_TFTP)
update_tftp ();
#endif /* CONFIG_UPDATE_TFTP */ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, ) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); # ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_POST
if (gd->flags & GD_FLG_POSTFAIL) {
s = getenv("failbootcmd");
}
else
#endif /* CONFIG_POST */
#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd"); //获取引导命令。分析见下面。 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); if (bootdelay >= && s && !abortboot (bootdelay)) {
//如果延时大于等于零,并且没有在延时过程中接收到按键,则引导内核。abortboot函数的分析见下面。
# ifdef CONFIG_AUTOBOOT_KEYED
int prev = disable_ctrlc(); /* disable Control C checking */
# endif # ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );//运行引导内核的命令。这个命令是在配置头文件中定义的。run_command的分析在下面。
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif # ifdef CONFIG_AUTOBOOT_KEYED
disable_ctrlc(prev); /* restore Control C checking */
# endif
} # ifdef CONFIG_MENUKEY
if (menukey == CONFIG_MENUKEY) {
s = getenv("menucmd");
if (s) {
# ifndef CONFIG_SYS_HUSH_PARSER
run_command (s, );
# else
parse_string_outer(s, FLAG_PARSE_SEMICOLON |
FLAG_EXIT_FROM_LOOP);
# endif
}
}
#endif /* CONFIG_MENUKEY */
#endif /* CONFIG_BOOTDELAY */ #ifdef CONFIG_AMIGAONEG3SE
{
extern void video_banner(void);
video_banner();
}
#endif /*
* Main Loop for Monitor Command Processing
*/
#ifdef CONFIG_SYS_HUSH_PARSER
parse_file_outer();
/* This point is never reached */
for (;;);
#else
for (;;) {
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= ) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT); //CONFIG_SYS_PROMPT的意思是回显字符,一般是“>”。这是由配置头文件定义的
flag = ; /* assume no special flags for now */
if (len > )
strcpy (lastcommand, console_buffer); //保存输入的数据。
else if (len == )
flag |= CMD_FLAG_REPEAT; ;//如果输入数据为零,则重复执行上次的命令,如果上次输入的是一个命令的话
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, , , NULL);
# else
return; /* retry autoboot */
# endif
}
#endif if (len == -)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag); //执行命令 if (rc <= ) {//执行失败,则清空记录
/* invalid command or not repeatable, forget it */
lastcommand[] = ;
}
}
#endif /*CONFIG_SYS_HUSH_PARSER*/
}

2> 自动补全

int var_complete(int argc, char *argv[], char last_char, int maxv, char *cmdv[])
{
static char tmp_buf[];
int space; space = last_char == '\0' || last_char == ' ' || last_char == '\t'; if (space && argc == )
return env_complete("", maxv, cmdv, sizeof(tmp_buf), tmp_buf); if (!space && argc == )
return env_complete(argv[], maxv, cmdv, sizeof(tmp_buf), tmp_buf); return ;
} static void install_auto_complete_handler(const char *cmd,
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]))
{
cmd_tbl_t *cmdtp; cmdtp = find_cmd(cmd);
if (cmdtp == NULL)
return; cmdtp->complete = complete; //命令结构体的complete指针指向传入的函数。
} void install_auto_complete(void)
{
#if defined(CONFIG_CMD_EDITENV)
install_auto_complete_handler("editenv", var_complete);
#endif
install_auto_complete_handler("printenv", var_complete);
install_auto_complete_handler("setenv", var_complete);
#if defined(CONFIG_CMD_RUN)
install_auto_complete_handler("run", var_complete);
#endif
}

可以看到将editenv、printenv、setenv和run的自动补全函数安装为 var_complete。var_complete的功能是根据给出的前缀字符串,找出所有前缀相同的命令。

每个命令在内存中用一个cmd_tbl_t 表示。include/command.h

struct cmd_tbl_s {
char *name; /* 命令名,输入的就是它 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 允许自动重发,也就是在按下空格键之后执行最后一条命令。 */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 实现命令的参数 */
char *usage; /* 短的提示信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 详细的帮助信息。 */
#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
}; typedef struct cmd_tbl_s cmd_tbl_t; extern cmd_tbl_t __u_boot_cmd_start;
extern cmd_tbl_t __u_boot_cmd_end; #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} #define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) /
{#name, maxargs, rep, cmd, usage, help} /*uboot中的命令使用U_BOOT_CMD这个宏声明来注册进系统,链接脚本会把所有的cmd_tbl_t结构体放在相邻的地方。
链接脚本中的一些内容如下: */
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
/*可见,__u_boot_cmd_start 和__u_boot_cmd_end 分别对应命令结构体在内存中开始和结束的地址。 */

3> abortboot函数的分析 abortboot是uboot在引导期间的延时函数。期间可以按键进入uboot的命令行。common/main.c

static __inline__ int abortboot(int bootdelay)
{
int abort = ; #ifdef CONFIG_MENUPROMPT
printf(CONFIG_MENUPROMPT);
#else
printf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK
//如果定义了这个宏,即使定义延时为0,也会检查一次是否有按键按下。
//只要在这里执行之前按键,还是能进入uboot的命令行。
/*
* Check if key already pressed
* Don't check if bootdelay < 0
*/
if (bootdelay >= ) {
if (tstc()) { // 测试是否有按键按下
(void) getc(); //接收按键值
puts ("\b\b\b 0");
abort = ; //修改标记,停止自动引导
}
}
#endif while ((bootdelay > ) && (!abort)) {
//如果延时大于零并且停止标记没有赋值则进入延时循环,直到延时完或者接收到了按 键
int i; --bootdelay;
/* delay 100 * 10ms */
//每秒中测试按键100次,之后延时10ms。
for (i=; !abort && i<; ++i) {
if (tstc()) { /* we got a key press */
abort = ; //修改标记,停止自动引导
bootdelay = ; //延时归零
# ifdef CONFIG_MENUKEY
menukey = getc();
# else
(void) getc(); /* 获取按键*/
# endif
break;
}
udelay(); //延时10000us,也就是10ms
} printf("\b\b\b%2d ", bootdelay);//打印当前剩余时间
//可以看到uboot延时的单位是秒,如果想提高延时的精度,
//比如想进行10ms级的延时,将udelay(10000)改为udelay(100)就可以了 。 } putc('\n'); #ifdef CONFIG_SILENT_CONSOLE
if (abort)
gd->flags &= ~GD_FLG_SILENT;
#endif return abort;//返回结果:1-停止引导,进入命令行; 0-引导内核。
}
# endif /* CONFIG_AUTOBOOT_KEYED */
#endif /* CONFIG_BOOTDELAY >= 0 */

4> 引导命令

/****************************************************************************
* 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 CONFIG_SYS_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[CONFIG_SYS_CBSIZE]; /* working copy of cmd */
char *token; /* start of token in cmdbuf */
char *sep; /* end of token (separator) in cmdbuf */
char finaltoken[CONFIG_SYS_CBSIZE];
char *str = cmdbuf;
char *argv[CONFIG_SYS_MAXARGS + ]; /* NULL terminated */
int argc, inquotes;
int repeatable = ;
int rc = ; #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 -; /* 空命令 */
} if (strlen(cmd) >= CONFIG_SYS_CBSIZE) { //命令太长
puts ("## Command too long!\n");
return -;
} strcpy (cmdbuf, cmd);//将命令拷贝到临时命令缓冲cmdbuf /* 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 = , sep = str; *sep; sep++) { //寻找分割符或者命令尾部。相邻的句子之间是用;隔开的。每次处理一句命令
if ((*sep=='\'') &&
(*(sep-) != '\\'))
inquotes=!inquotes; if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-) != '\\')) /* and NOT escaped */
break;
} /*
* Limit the token to data between separators
*/
token = str; //token指向命令的开头
if (*sep) { //如果是分隔符的话,将分隔符替换为空字符
str = sep + ; /* str指向下一句的开头*/
*sep = '\0';
}
else
str = sep; /* 如果没有其它命令了,str指向命令尾部 */
#ifdef DEBUG_PARSER
printf ("token: \"%s\"\n", token);
#endif /* find macros in this token and replace them */
process_macros (token, finaltoken); //将token指向的命令中的宏替换掉,如把$(kernelsize)替换成内核的大小 /* Extract arguments */
if ((argc = parse_line (finaltoken, argv)) == ) {
//将每一个参数用‘/0’隔开,argv中的每一个指针指向一个参数的起始地址。
//返回值为参数的个数
rc = -; /* no command at all */
continue;
} /* Look up command in command table */
if ((cmdtp = find_cmd(argv[])) == NULL) {
//第一个参数就是要运行的命令,首先在命令表中找到它的命令结构体的指针
printf ("Unknown command '%s' - try 'help'\n", argv[]);
rc = -; /* give up after bad command */
continue;
} /* found - check max args */
if (argc > cmdtp->maxargs) {//检查参数个数是否过多
cmd_usage(cmdtp);
rc = -;
continue;
} #if defined(CONFIG_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 = -;
continue;
} else {
flag |= CMD_FLAG_BOOTD;
}
}
#endif /* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != ) {//调用命令执行函数。这是最重要的一步。
rc = -;
} repeatable &= cmdtp->repeatable;;//设置命令自动重复执行的标志。也就是只按下enter键是否可以执行最近执行的命令 . /* Did the user stop this? */
if (had_ctrlc ())
//检查是否有ctrl+c按键按下,如果有,结束当前命令。
//本函数并没有从中断接收字符,接收ctrl+c的是一些执行命令的函数。
return -; /* if stopped then not repeatable */
} return rc ? rc : repeatable;
}

U-Boot 启动过程和源码分析(第二阶段)-main_loop分析的更多相关文章

  1. [Spring Boot] Spring Boot启动过程源码分析

    关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Spring-boot 1.5.6)的角度来看看Spring Boot的启动过程到底是怎么样的,为何以往纷繁复杂的配置到 ...

  2. Spring Boot启动过程源码分析--转

    https://blog.csdn.net/dm_vincent/article/details/76735888 关于Spring Boot,已经有很多介绍其如何使用的文章了,本文从源代码(基于Sp ...

  3. Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...

  4. Spring Boot启动过程(七):Connector初始化

    Connector实例的创建已经在Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动中提到了: Connector是LifecycleMBeanBase的子类,先是设置L ...

  5. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  6. Spring Boot启动过程(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).pring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真.. ...

  7. Spring Boot启动过程(一)

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

  8. 转:Spring Boot启动过程

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringAp ...

  9. Spring Boot启动过程(二)

    书接上篇 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractAppl ...

随机推荐

  1. <转载>C++命名空间

    原文http://blog.csdn.net/liufei_learning/article/details/5391334 一. 为什么需要命名空间(问题提出) 命名空间是ANSIC++引入的可以由 ...

  2. JUnit4基础 使用JUnit4进行单元测试

    JUnit 4全面引入了Annotation来执行我们编写的测试. 关于JUnit 3的使用可以参见:http://www.cnblogs.com/mengdd/archive/2013/03/26/ ...

  3. Find the maximum(规律,大数)

    Find the maximum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) ...

  4. 我的第一个Servlet

    学了一个学期JEE,明天就要考试了. 在3月份自己開始准备去努力的复习考研的高数还有英语等学科. 结果到如今才发现,虽说是考的计算机(本专业的)可是考研和技不可兼得. 想想自己没准备考研的时候的每天大 ...

  5. LinkedList的分析(转)

    一.源码解析 1. LinkedList类定义. public class LinkedList<E> extends AbstractSequentialList<E> im ...

  6. 2016 ACM/ICPC Asia Regional Shenyang Online

    I:QSC and Master 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5900 题意: 给出n对数keyi,vali表示当前这对数的键值和权值 ...

  7. (转)实例详解CSS中position的fixed属性使用

    关于fixed属性,在什么情况下需要用,怎么用,首先,我们应该先了解下fixed属性的说明:fixed总是以body为定位时的对象,总是根据浏览器的窗口来进行元素的定位,通过"left&qu ...

  8. BackgroundWorker 后台进程控制窗体label、richtextbook内容刷新

    昨天写了一个从文章中提取关键词的程序,写完处理的逻辑后又花了好几个小时在用户友好性上.加了几个progressBar,有显示总进度的.有显示分布进度的..因为程序要跑好几个小时才能执行好,只加个总进度 ...

  9. 常用的方法,读取XML节点并赋值给List集合

    一.前言 很多时候也可以直接在XML文件中配置好节点,在程序需要用到的时候,修改XML文件并不需要重新编译,这里是在极光推送中拿出来的一部分代码.代码简单,大家直接看例子吧. 二.实现过程 1.新创建 ...

  10. 三大主流ETL工具选型

    ETL(extract, transform and load)产品乍看起来似乎并不起眼,单就此项技术本身而言,几乎也没什么特别深奥之处,但是在实际项目中,却常常在这个环节耗费太多的人力,而在后续的维 ...