main_loop()函数做的都是与具体平台无关的工作,主要包括初始化启动次数限制机制、设置软件版本号、打印启动信息、解析命令等。
(1)设置启动次数有关参数。在进入main_loop()函数后,首先是根据配置加载已经保留的启动次数,并且根据配置判断是否超过启动次数。代码如下:
- 295?void?main_loop?(void) ?
- 296?{ ?
- 297?#ifndef?CFG_HUSH_PARSER ?
- 298???static?char?lastcommand[CFG_CBSIZE]?=?{?0,?}; ?
- 299???int?len; ?
-
300???int?rc?=?1; ?
- 301???int?flag; ?
- 302?#endif ?
- 303? ?
-
304?#if?defined(CONFIG_BOOTDELAY)?&&?(CONFIG_BOOTDELAY?>=?0) ?
- 305???char?*s; ?
- 306???int?bootdelay; ?
- 307?#endif ?
- 308?#ifdef?CONFIG_PREBOOT ?
- 309???char?*p; ?
- 310?#endif ?
- 311?#ifdef?CONFIG_BOOTCOUNT_LIMIT ?
-
312???unsigned?long?bootcount?=?0; ?
-
313???unsigned?long?bootlimit?=?0; ?
- 314???char?*bcs; ?
- 315???char?bcs_set[16]; ?
- 316?#endif?/*?CONFIG_BOOTCOUNT_LIMIT?*/ ?
- 317? ?
- 318?#if?defined(CONFIG_VFD)?&&?defined(VFD_TEST_LOGO) ?
-
319???ulong?bmp?=?0;????/*?default?bitmap?*/ ?
- 320???extern?int?trab_vfd?(ulong?bitmap); ?
- 321? ?
- 322?#ifdef?CONFIG_MODEM_SUPPORT ?
- 323???if?(do_mdm_init) ?
-
324?????bmp?=?1;??/*?alternate?bitmap?*/ ?
- 325?#endif ?
- 326???trab_vfd?(bmp); ?
- 327?#endif??/*?CONFIG_VFD?&&?VFD_TEST_LOGO?*/ ?
- 328? ?
- 329?#ifdef?CONFIG_BOOTCOUNT_LIMIT ?
-
330???bootcount?=?bootcount_load();?????????//?加载保存的启动次数 ?
- 331???bootcount++;??????????????????????????//?启动次数加1 ?
- 332???bootcount_store?(bootcount);??????????//?更新启动次数 ?
- 333???sprintf?(bcs_set,?"%lu",?bootcount);??//?打印启动次数 ?
- 334???setenv?("bootcount",?bcs_set); ?
-
335???bcs?=?getenv?("bootlimit"); ?
-
336???bootlimit?=?bcs???simple_strtoul?(bcs,?NULL,?10)?:?0; ?
- ????????????????????????????????????????????//?转换启动次数字符串为UINT类型 ?
- 337?#endif?/*?CONFIG_BOOTCOUNT_LIMIT?*/?
第329~337行是启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。
(2)程序第339~348行是Modem功能。如果系统中有Modem,打开该功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用,代码如下:
- 339?#ifdef?CONFIG_MODEM_SUPPORT ?
-
340???debug?("DEBUG:?main_loop:???do_mdm_init=%dn",?do_mdm_init); ?
- 341???if?(do_mdm_init)?{???????????????????????????//?判断是否需要初始化Modem ?
-
342?????char?*str?=?strdup(getenv("mdm_cmd"));?????//?获取Modem参数 ?
- 343?????setenv?("preboot",?str);??/*?set?or?delete?definition?*/ ?
- 344?????if?(str?!=?NULL) ?
- 345???????free?(str); ?
- 346?????mdm_init();?/*?wait?for?modem?connection?*/?//?初始化Modem ?
- 347???} ?
- 348?#endif??/*?CONFIG_MODEM_SUPPORT?*/?
(3)接下来设置U-Boot的版本号,初始化命令自动完成功能等。代码如下:
- 350?#ifdef?CONFIG_VERSION_VARIABLE ?
- 351???{ ?
- 352?????extern?char?version_string[]; ?
- 353? ?
- 354?????setenv?("ver",?version_string);??/*?set?version?variable?*/? ?
- ????????????????????????????????????????????????//?设置版本号 ?
- 355???} ?
- 356?#endif?/*?CONFIG_VERSION_VARIABLE?*/ ?
- 357? ?
- 358?#ifdef?CFG_HUSH_PARSER ?
- 359???u_boot_hush_start?();?????????????????????//?初始化Hash功能 ?
- 360?#endif ?
- 361? ?
- 362?#ifdef?CONFIG_AUTO_COMPLETE ?
- 363???install_auto_complete();??????????????????//?初始化命令自动完成功能 ?
- 364?#endif ?
- 365? ?
- 366?#ifdef?CONFIG_PREBOOT ?
-
367???if?((p?=?getenv?("preboot"))?!=?NULL)?{ ?
- 368?#?ifdef?CONFIG_AUTOBOOT_KEYED ?
-
369?????int?prev?=?disable_ctrlc(1);??/*?disable?Control?C?checking?*/ ?
- ????????????????????????????????????????????????//?关闭Crtl+C组合键 ?
- 370?#?endif ?
- 371? ?
- 372?#?ifndef?CFG_HUSH_PARSER ?
- 373?????run_command?(p,?0);?//?运行Boot参数 ?
- 374?#?else ?
- 375?????parse_string_outer(p,?FLAG_PARSE_SEMICOLON?| ?
- 376?????????????FLAG_EXIT_FROM_LOOP); ?
- 377?#?endif ?
- 378? ?
- 379?#?ifdef?CONFIG_AUTOBOOT_KEYED ?
- 380?????disable_ctrlc(prev);??/*?restore?Control?C?checking?*/ ?
- ????????????????????????????????????????????????//?恢复Ctrl+C组合键 ?
- 381?#?endif ?
- 382???} ?
- 383?#endif?/*?CONFIG_PREBOOT?*/?
程序第350~356行是动态版本号功能支持代码,version_string变量是在其他文件定义的一个字符串变量,当用户改变U-Boot版本的时候会更新该变量。打开动态版本支持功能后,U-Boot在启动的时候会显示最新的版本号。
程序第363行设置命令行自动完成功能,该功能与Linux的shell类似,当用户输入一部分命令后,可以通过按下键盘上的Tab键补全命令的剩余部分。main_loop()函数不同的功能使用宏开关控制不仅能提高代码模块化,更主要的是针对嵌入式系统Flash存储器大小设计的。在嵌入式系统上,不同的系统Flash存储空间不同。对于一些Flash空间比较紧张的设备来说,通过宏开关关闭一些不是特别必要的功能如命令行自动完成,可以减小U-Boot编译后的文件大小。
(4)在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。代码如下:
- 385?#if?defined(CONFIG_BOOTDELAY)?&&?(CONFIG_BOOTDELAY?>=?0) ?
-
386???s?=?getenv?("bootdelay"); ?
-
387???bootdelay?=?s???(int)simple_strtol(s,?10)?:?CONFIG_BOOTDELAY; ?
- ????????????????????????????????????????????????????????//?启动延迟 ?
- 388? ?
-
389???debug?("###?main_loop?entered:?bootdelay=%dnn",?bootdelay); ?
- 390? ?
- 391?#?ifdef?CONFIG_BOOT_RETRY_TIME ?
- 392???init_cmd_timeout?();??????//?初始化命令行超时机制 ?
- 393?#?endif?/*?CONFIG_BOOT_RETRY_TIME?*/ ?
- 394? ?
- 395?#ifdef?CONFIG_BOOTCOUNT_LIMIT ?
-
396???if?(bootlimit?&&?(bootcount?>?bootlimit))?{?//?检查是否超出启动次数限制 ?
- 397?????printf?("Warning:?Bootlimit?(%u)?exceeded.?Using?altbootcmd.n",?
- 398?????????????(unsigned)bootlimit); ?
-
399?????s?=?getenv?("altbootcmd"); ?
- 400???} ?
- 401???else ?
- 402?#endif?/*?CONFIG_BOOTCOUNT_LIMIT?*/ ?
-
403?????s?=?getenv?("bootcmd");?//?获取启动命令参数 ?
- 404? ?
-
405???debug?("###?main_loop:?bootcmd="%s"n",?s???s?:?"<UNDEFINED>"); ?
- 406? ?
-
407???if?(bootdelay?>=?0?&&?s?&&?!abortboot?(bootdelay))?{?? ?
- ????????????????????????????????????????????????????//检查是否支持启动延迟功能 ?
- 408?#?ifdef?CONFIG_AUTOBOOT_KEYED ?
-
409?????int?prev?=?disable_ctrlc(1);??/*?disable?Control?C?checking?*/?? ?
- ????????????????????????????????????????????????????//?关闭Ctrl+C组合键 ?
- 410?#?endif ?
- 411? ?
- 412?#?ifndef?CFG_HUSH_PARSER ?
- 413?????run_command?(s,?0);?????//?运行启动命令行 ?
- 414?#?else ?
- 415?????parse_string_outer(s,?FLAG_PARSE_SEMICOLON?| ?
- 416?????????????FLAG_EXIT_FROM_LOOP); ?
- 417?#?endif ?
- 418? ?
- 419?#?ifdef?CONFIG_AUTOBOOT_KEYED ?
- 420?????disable_ctrlc(prev);??/*?restore?Control?C?checking?*/ ?
- ????????????????????????????????????????????????????//?打开Ctrl+C组合键 ?
- 421?#?endif ?
- 422???} ?
- 423? ?
- 424?#?ifdef?CONFIG_MENUKEY ?
-
425???if?(menukey?==?CONFIG_MENUKEY)?{??//?检查是否支持菜单键 ?
-
426???????s?=?getenv("menucmd"); ?
- 427???????if?(s)?{ ?
- 428?#?ifndef?CFG_HUSH_PARSER ?
- 429?????run_command?(s,?0); ?
- 430?#?else ?
- 431?????parse_string_outer(s,?FLAG_PARSE_SEMICOLON?| ?
- 432?????????????FLAG_EXIT_FROM_LOOP); ?
- 433?#?endif ?
- 434???????} ?
- 435???} ?
- 436?#endif?/*?CONFIG_MENUKEY?*/ ?
- 437?#endif??/*?CONFIG_BOOTDELAY?*/ ?
- 438? ?
- 439?#ifdef?CONFIG_AMIGAONEG3SE ?
- 440???{ ?
- 441???????extern?void?video_banner(void); ?
- 442???????video_banner();???????????????//?打印启动图标 ?
- 443???} ?
- 444?#endif?
(5)在各功能设置完毕后,程序第454行进入一个for死循环,该循环不断使用readline()函数(第463行)从控制台(一般是串口)读取用户的输入,然后解析。有关如何解析命令请参考U-Boot代码中run_command()函数的定义,本书不再赘述。代码如下:
- 446???/* ?
- 447????*?Main?Loop?for?Monitor?Command?Processing ?
- 448????*/ ?
- 449?#ifdef?CFG_HUSH_PARSER ?
- 450???parse_file_outer(); ?
- 451???/*?This?point?is?never?reached?*/ ?
- 452???for?(;;); ?
- 453?#else ?
- 454???for?(;;)?{????????????????????????//?进入命令行循环 ?
- 455?#ifdef?CONFIG_BOOT_RETRY_TIME ?
-
456?????if?(rc?>=?0)?{ ?
- 457???????/*?Saw?enough?of?a?valid?command?to ?
- 458????????*?restart?the?timeout. ?
- 459????????*/ ?
- 460???????reset_cmd_timeout();??????????//?设置命令行超时 ?
- 461?????} ?
- 462?#endif ?
-
463?????len?=?readline?(CFG_PROMPT);????//?读取命令 ?
- 464? ?
-
465?????flag?=?0;?/*?assume?no?special?flags?for?now?*/ ?
-
466?????if?(len?>?0) ?
- 467???????strcpy?(lastcommand,?console_buffer); ?
-
468?????else?if?(len?==?0) ?
- 469???????flag?|=?CMD_FLAG_REPEAT; ?
- 470?#ifdef?CONFIG_BOOT_RETRY_TIME ?
-
471?????else?if?(len?==?-2)?{ ?
- 472???????/*?-2?means?timed?out,?retry?autoboot ?
- 473????????*/ ?
- 474???????puts?("nTimed?out?waiting?for?commandn"); ?
- 475?#?ifdef?CONFIG_RESET_TO_RETRY ?
- 476???????/*?Reinit?board?to?run?initialization?code?again?*/ ?
- 477???????do_reset?(NULL,?0,?NULL); ?
- 478?#?else ?
- 479???????return;???/*?retry?autoboot?*/ ?
- 480?#?endif ?
- 481?????} ?
- 482?#endif ?
- 483? ?
-
484?????if?(len?==?-1) ?
-
485???????puts?("<INTERRUPT>n"); ?
- 486?????else ?
-
487???????rc?=?run_command?(lastcommand,?flag);?//?运行命令 ?
- 488? ?
-
489?????if?(rc?<=?0)?{ ?
- 490???????/*?invalid?command?or?not?repeatable,?forget?it?*/ ?
- 491???????lastcommand[0]?=?0; ?
- 492?????} ?
- 493???} ?
- 494?#endif?/*CFG_HUSH_PARSER*/ ?
- 495?}