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 - 317
- 318
#if defined(CONFIG_VFD) && defined(VFD_TEST_LOGO) - 319
ulong bmp = 0; - 320
extern int trab_vfd (ulong bitmap); - 321
- 322
#ifdef CONFIG_MODEM_SUPPORT - 323
if (do_mdm_init) - 324
bmp = 1; - 325
#endif - 326
trab_vfd (bmp); - 327
#endif - 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
第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=%d\n", do_mdm_init); - 341
if (do_mdm_init) { // 判断是否需要初始化Modem - 342
char *str = strdup(getenv("mdm_cmd")); // 获取Modem参数 - 343
setenv ("preboot", str); - 344
if (str != NULL) - 345
free (str); - 346
mdm_init(); // 初始化Modem - 347
} - 348
#endif
(3)接下来设置U-Boot的版本号,初始化命令自动完成功能等。代码如下:
- 350
#ifdef CONFIG_VERSION_VARIABLE - 351
{ - 352
extern char version_string[]; - 353
- 354
setenv ("ver", version_string); -
// 设置版本号 - 355
} - 356
#endif - 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); -
// 关闭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); -
// 恢复Ctrl+C组合键 - 381
# endif - 382
} - 383
#endif
程序第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, NULL, 10) : CONFIG_BOOTDELAY; -
// 启动延迟 - 388
- 389
debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); - 390
- 391
# ifdef CONFIG_BOOT_RETRY_TIME - 392
init_cmd_timeout (); // 初始化命令行超时机制 - 393
# endif - 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 - 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); -
// 关闭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); -
// 打开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 - 437
#endif - 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
- 449
#ifdef CFG_HUSH_PARSER - 450
parse_file_outer(); - 451
- 452
for (;;); - 453
#else - 454
for (;;) { // 进入命令行循环 - 455
#ifdef CONFIG_BOOT_RETRY_TIME - 456
if (rc >= 0) { - 457
- 460
reset_cmd_timeout(); // 设置命令行超时 - 461
} - 462
#endif - 463
len = readline (CFG_PROMPT); // 读取命令 - 464
- 465
flag = 0; - 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
- 474
puts ("\nTimed out waiting for command\n"); - 475
# ifdef CONFIG_RESET_TO_RETRY - 476
- 477
do_reset (NULL, 0, 0, NULL); - 478
# else - 479
return; - 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
- 491
lastcommand[0] = 0; - 492
} - 493
} // dead loop - 494
#endif - 495
}
U-BOOT的功能设计 基本就在这里