资讯
HOME
资讯
正文内容
u boot nand 嵌入式Linux论坛问题精选以及解决方法
发布时间 : 2024-11-24
作者 : 小编
访问数量 : 23
扫码分享至微信

嵌入式Linux论坛问题精选以及解决方法

天韦东山老师花了半天时间在交流社区答疑,这不小编马上送来热腾腾的9个问题以及解决方法,答案全部出自韦老师一人之手,全是精华。可能只有区区20字,但里面包含了珍贵的经验和情怀。对了,之前发的问题对你有启发吗,或者正好解决了你问题?麻烦给我们反馈一下,还有,只有论坛版主以上级别才能更改问题状态,我们回答问题,问你是否解决,等回复,假设解决了,我们更改问题状态,这样一来一去,沟通成本太大,希望你的问题被回复后能及时通过帖子反馈结果给我们。方便我们及时更改问题状态,给遇到类似相关问题者更好的参考。

1、u-boot烧写根文件系统超出了分区大小咋办?

##### 100ask Bootloader for OpenJTAG #####

[n] Download u-boot to Nand Flash

[o] Download u-boot to Nor Flash

[k] Download Linux kernel uImage

[j] Download root_jffs2 image

[y] Download root_yaffs image

[d] Download to SDRAM & Run

[z] Download zImage into RAM

[g] Boot linux from RAM

[f] Format the Nand Flash

[s] Set the boot parameters

[b] Boot the system

[r] Reboot u-boot

[q] Quit from menu

Enter your selection: y

USB host is connected. Waiting a download.

Length of file is too big : 88564608 > 66183036

NAND erase: device 0 offset 0x260000, size 0xfda0000

答: Length of file is too big : 88564608 > 66183036

这个文件系统并没有超过分区,只是超过了内存大小。

因为我们的UBOOT是先把文件系统下载到内存,再烧写的。

对于这种情况,只有这样:

1.使用NFS启动

2.擦除root分区(当然可以在UBOOT里先擦除ROOT分区)

3.mount -t yaffs /dev/mtdblock3 /mnt

4. 把文件复制到 /mnt上:

a. 事先把根文件系统放在NFS里某个目录,比如mydir里,

b. 执行:cd mydir; cp * -rfd /mnt

注意,必须加-rfd,其中的d表示原来的链接文件继续作为链接文件,不加d的话链接文件就会作为实体文件复制了,占空间太大

2、fops_get与fops_put怎么理解?

答: 内核源码有段:

#define fops_get(fops) \

(((fops) && try_module_get((fops)->owner) ? (fops) : NULL))

#define fops_put(fops) \

do { if (fops) module_put((fops)->owner); } while(0)

一看即知,

fops_get: 如果fops存在,先增加它的使用计数,再返回。

fops_put: 如果fops存在,减小它的使用计数。

3、两次设置堆栈,C语言初始化SDRAM拷贝程序失败了

启动文件:

initSDRAM和copyToSDRAM函数:

main.c:

答:

思路没问题,

第1次设置sp弄错了,NOR启动时,片内内存从0x40000000开始,所以应该这样:

ldr sp, =1024*4

改为:

ldr sp, =1024*4 + 0x40000000

4、不知道为什么开发板启动突然出现这个问题:

答:

贴出烧写内核时的串口信息,这样才能确定问题。

肯定是烧写出了问题:要么没烧成功,要么是uImage超过2M了

5、busybox1.7.0制作yaffs2重启时,出现:

答:

修改busybox下的 .config,添加这2行:

CONFIG_ASH=y

CONFIG_ASH_BASH_COMPAT=y

6、指针和数组的问题

volatile unsigned long*p=(volatile unsigned long *)0x48000000;

不是应该是p=mem_cfg_val[i];

p++;吗

怎么MMU那课讲的是p[i]=mem_cfg_val[i];

答:p=mem_cfg_val[i]; 是错的;

volatile unsigned long*p

p = val ? 必错

应该是 *p = val; 或 p[0] = val

7、想玩一下JZ2440 pwm控制舵机但是找不到引脚

答: JZ2440没引出PWM引脚

8、jz2440怎样挂载SD卡?

512MB的SD卡,插入开发板后用fdisk -l 命令没有东西显示出来,提示如下:

# s3c2440-sdi s3c2440-sdi: running at 0kHz (requested: 0kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 196kHz (requested: 195kHz).

s3c2440-sdi s3c2440-sdi: running at 25000kHz (requested: 25000kHz).

s3c2440-sdi s3c2440-sdi: running at 25000kHz (requested: 25000kHz).

mmcblk0: mmc0:59b4 NCard 7851008KiB

mmcblk0:<7>mmc0: starting CMD18 arg 00000000 flags 00000035

p1

# mount mmc0 mnt/usb/

mount: mounting mmc0 on mnt/usb/ failed: No such file or directory

答:

mount -tvfat /dev/mmcblk0p1 /mnt

「正点原子Linux连载」第三十二章U-Boot启动流程详解(三)

1)实验平台:正点原子Linux开发板

2)摘自《正点原子 I.MX6U嵌入式Linux驱动开发指南

关注官方微信号公众号,获取更多资料:正点原子

32.2.8 board_init_r函数详解

第32.2.5小节讲解了board_init_f函数,在此函数里面会调用一系列的函数来初始化一些外设和gd的成员变量。但是board_init_f并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数board_init_r来完成的,board_init_r函数定义在文件common/board_r.c中,代码如下:

示例代码32.2.8.1 board_r.c代码段

991void board_init_r( gd_t * new_gd, ulong dest_addr)

992{

993 #ifdef CONFIG_NEEDS_MANUAL_RELOC

994int i;

995 #endif

996

997 #ifdef CONFIG_AVR32

998 mmu_init_r( dest_addr);

999 #endif

1000

1001 #if! defined( CONFIG_X86)&&! defined( CONFIG_ARM)&&! defined( CONFIG_ARM64)

1002 gd = new_gd;

1003 #endif

1004

1005 #ifdef CONFIG_NEEDS_MANUAL_RELOC

1006for( i = 0; i < ARRAY_SIZE( init_sequence_r); i++)

1007 init_sequence_r[ i]+= gd-> reloc_off;

1008 #endif

1009

1010if( initcall_run_list( init_sequence_r))

1011 hang();

1012

1013/* NOTREACHED - run_main_loop() does not return */

1014 hang();

1015}

第1010行调用initcall_run_list函数来执行初始化序列init_sequence_r,init_sequence_r是一个函数集合,init_sequence_r也定义在文件common/board_r.c中,由于init_sequence_f的内容比较长,里面有大量的条件编译代码,这里为了缩小篇幅,将条件编译部分删除掉了,去掉条件编译以后的init_sequence_r定义如下:

示例代码32.2.8.2 board_r.c代码段

1 init_fnc_t init_sequence_r[]={

2 initr_trace,

3 initr_reloc,

4 initr_caches,

5 initr_reloc_global_data,

6 initr_barrier,

7 initr_malloc,

8 initr_console_record,

9 bootstage_relocate,

10 initr_bootstage,

11 board_init, /* Setup chipselects */

12 stdio_init_tables,

13 initr_serial,

14 initr_announce,

15 INIT_FUNC_WATCHDOG_RESET

16 INIT_FUNC_WATCHDOG_RESET

17 INIT_FUNC_WATCHDOG_RESET

18 power_init_board,

19 initr_flash,

20 INIT_FUNC_WATCHDOG_RESET

21 initr_nand,

22 initr_mmc,

23 initr_env,

24 INIT_FUNC_WATCHDOG_RESET

25 initr_secondary_cpu,

26 INIT_FUNC_WATCHDOG_RESET

27 stdio_add_devices,

28 initr_jumptable,

29 console_init_r, /* fully init console as a device */

30 INIT_FUNC_WATCHDOG_RESET

31 interrupt_init,

32 initr_enable_interrupts,

33 initr_ethaddr,

34 board_late_init,

35 INIT_FUNC_WATCHDOG_RESET

36 INIT_FUNC_WATCHDOG_RESET

37 INIT_FUNC_WATCHDOG_RESET

38 initr_net,

39 INIT_FUNC_WATCHDOG_RESET

40 run_main_loop,

41};

第2行,initr_trace函数,如果定义了宏CONFIG_TRACE的话就会调用函数trace_init,初始化和调试跟踪有关的内容。

第3行,initr_reloc函数用于设置gd->flags,标记重定位完成。

第4行,initr_caches函数用于初始化cache,使能cache。

第5行,initr_reloc_global_data函数,初始化重定位后gd的一些成员变量。

第6行,initr_barrier函数,I.MX6ULL未用到。

第7行,initr_malloc函数,初始化malloc。

第8行,initr_console_record函数,初始化控制台相关的内容,I.MX6ULL未用到,空函数。

第9行,bootstage_relocate函数,启动状态重定位。

第10行,initr_bootstage函数,初始化bootstage什么的。

第11行,board_init函数,板级初始化,包括74XX芯片,I2C、FEC、USB和QSPI等。这里执行的是mx6ull_alientek_emmc.c文件中的board_init函数。

第12行,stdio_init_tables函数,stdio相关初始化。

第13行,initr_serial函数,初始化串口。

第14行,initr_announce函数,与调试有关,通知已经在RAM中运行。

第18行,power_init_board函数,初始化电源芯片,正点原子的I.MX6ULL开发板没有用到。

第19行,initr_flash函数,对于I.MX6ULL而言,没有定义宏CONFIG_SYS_NO_FLASH的话函数initr_flash才有效。但是mx6_common.h中定义了宏CONFIG_SYS_NO_FLASH,所以此函数无效。

第21行,initr_nand函数,初始化NAND,如果使用NAND版本核心板的话就会初始化NAND。

第22行,initr_mmc函数,初始化EMMC,如果使用EMMC版本核心板的话就会初始化EMMC,串口输出如图32.2.8.1所示信息:

图32.2.8.1 EMMC信息输出

从图32.2.8.1可以看出,此时有两个EMCM设备,FSL_SDHC:0和FSL_SDHC:1。

第23行,initr_env函数,初始化环境变量。

第25行,initr_secondary_cpu函数,初始化其他CPU核,I.MX6ULL只有一个核,因此此函数没用。

第27行,stdio_add_devices函数,各种输入输出设备的初始化,如LCD driver,I.MX6ULL使用drv_video_init函数初始化LCD。会输出如图32.2.8.2所示信息:

图32.2.8.2 LCD信息

第28行,initr_jumptable函数,初始化跳转表。

第29行,console_init_r函数,控制台初始化,初始化完成以后此函数会调用stdio_print_current_devices函数来打印出当前的控制台设备,如图32.2.8.3所示:

图32.2.8.3 控制台信息

第31行,interrupt_init函数,初始化中断。

第32行,initr_enable_interrupts函数,使能中断。

第33行,initr_ethaddr函数,初始化网络地址,也就是获取MAC地址。读取环境变量“ethaddr”的值。

第34行,board_late_init函数,板子后续初始化,此函数定义在文件mx6ull_alientek_emmc.c中,如果环境变量存储在EMMC或者SD卡中的话此函数会调用board_late_mmc_env_init函数初始化EMMC/SD。会切换到正在时候用的emmc设备,代码如图32.2.8.4所示:

图32.2.8.4 board_late_mmc_env_init函数

图32.2.8.4中的第46行和第47行就是运行“mmcdevxx”命令,用于切换到正在使用的EMMC设备,串口输出信息如图32.2.8.5所示:

图32.2.8.5 切换mmc设备

第38行,initr_net函数,初始化网络设备,函数调用顺序为:initr_net->eth_initialize->board_eth_init(),串口输出如图32.2.8.6所示信息:

图32.2.8.6 网络信息输出

第40行,run_main_loop行,主循环,处理命令。

32.2.9 run_main_loop函数详解

uboot启动以后会进入3秒倒计时,如果在3秒倒计时结束之前按下按下回车键,那么就会进入uboot的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动Linux内核,这个功能就是由run_main_loop函数来完成的。run_main_loop函数定义在文件common/board_r.c中,函数内容如下:

示例代码32.2.9.1 board_r.c文件代码段

753staticint run_main_loop( void)

754{

755 #ifdef CONFIG_SANDBOX

756 sandbox_main_loop_init();

757 #endif

758/* main_loop() can return to retry autoboot, if so just run it again */

759 for(;;)

760 main_loop();

761 return 0;

762}

第7行和第8行是个死循环,“for(;;)”和“while(1)”功能一样,死循环里面就一个main_loop函数,main_loop函数定义在文件common/main.c里面,代码如下:

示例代码32.2.9.2 main.c文件代码段

43/* We come here after U-Boot is initialised and ready to process commands */

44void main_loop( void)

45{

46 constchar* s;

47

48 bootstage_mark_name( BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

49

50 #ifndef CONFIG_SYS_GENERIC_BOARD

51 puts( "Warning: Your board does not use generic board. Please read\n");

52 puts( "doc/README.generic-board and take action. Boards not\n");

53 puts( "upgraded by the late 2014 may break or be removed.\n");

54 #endif

55

56 #ifdef CONFIG_VERSION_VARIABLE

57 setenv( "ver", version_string); /* set version variable */

58 #endif /* CONFIG_VERSION_VARIABLE */

59

60 cli_init();

61

62 run_preboot_environment_command();

63

64 #if defined( CONFIG_UPDATE_TFTP)

65 update_tftp( 0UL,NULL,NULL);

66 #endif /* CONFIG_UPDATE_TFTP */

67

68 s = bootdelay_process();

69 if( cli_process_fdt(& s))

70 cli_secure_boot_cmd( s);

71

72 autoboot_command( s);

73

74 cli_loop();

75}

第48行,调用bootstage_mark_name函数,打印出启动进度。

第57行,如果定义了宏CONFIG_VERSION_VARIABLE的话就会执行函数setenv,设置换将变量ver的值为version_string,也就是设置版本号环境变量。version_string定义在文件cmd/version.c中,定义如下:

const char __weak version_string[] = U_BOOT_VERSION_STRING;

U_BOOT_VERSION_STRING是个宏,定义在文件include/version.h,如下:

#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \

U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING

U_BOOT_VERSION 定义在文件include/generated/version_autogenerated.h中,文件version_autogenerated.h内如如下:

示例代码32.2.9.4 version_autogenerated.h文件代码

1 #define PLAIN_VERSION "2016.03"

2 #define U_BOOT_VERSION "U-Boot " PLAIN_VERSION

3 #define CC_VERSION_STRING "arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4"

4 #define LD_VERSION_STRING "GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git"

可以看出,U_BOOT_VERSION为“U-boot 2016.03”,

U_BOOT_DATE、U_BOOT_TIME和U_BOOT_TZ这定义在文件include/generated/timestamp_autogenerated.h中,如下所示:

示例代码32.2.9.5 timestamp_autogenerated.h文件代码

1 #define U_BOOT_DATE "Apr 25 2019"

2 #define U_BOOT_TIME "21:10:53"

3 #define U_BOOT_TZ "+0800"

4 #define U_BOOT_DMI_DATE "04/25/2019"

宏CONFIG_IDENT_STRING为空,所以U_BOOT_VERSION_STRING为“U-Boot 2016.03 (Apr 25 2019 - 21:10:53 +0800)”,进入uboot命令模式,输入命令“version”查看版本号,如图32.2.9.1所示:

图32.2.9.1 版本查询

图32.2.9.1中的第一行就是uboot版本号,和我们分析的一致。

接着回到示例代码32.2.9.2中,第60行,cli_init函数,跟命令初始化有关,初始化hushshell相关的变量。

第62行,run_preboot_environment_command函数,获取环境变量perboot的内容,perboot是一些预启动命令,一般不使用这个环境变量。

第68行,bootdelay_process函数,此函数会读取环境变量bootdelay和bootcmd的内容,然后将bootdelay的值赋值给全局变量stored_bootdelay,返回值为环境变量bootcmd的值。

第69行,如果定义了CONFIG_OF_CONTROL的话函数cli_process_fdt就会实现,如果没有定义CONFIG_OF_CONTROL的话函数cli_process_fdt直接返回一个false。在本uboot中没有定义CONFIG_OF_CONTROL,因此cli_process_fdt函数返回值为false。

第72行,autoboot_command函数,此函数就是检查倒计时是否结束?倒计时结束之前有没有被打断?此函数定义在文件common/autoboot.c中,内容如下:

示例代码32.2.9.5 auboboot.c文件代码段

380void autoboot_command( constchar* s)

381{

382 debug( "### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

383

384if( stored_bootdelay !=- 1&& s &&! abortboot( stored_bootdelay)){

385 #if defined( CONFIG_AUTOBOOT_KEYED)&&! defined( CONFIG_AUTOBOOT_KEYED_CTRLC)

386int prev = disable_ctrlc( 1); /* disable Control C checking */

387 #endif

388

389 run_command_list( s,- 1, 0);

390

391 #if defined( CONFIG_AUTOBOOT_KEYED)&&! defined( CONFIG_AUTOBOOT_KEYED_CTRLC)

392 disable_ctrlc( prev); /* restore Control C checking */

393 #endif

394}

395

396 #ifdef CONFIG_MENUKEY

397if( menukey == CONFIG_MENUKEY){

398 s = getenv( "menucmd");

399if( s)

400 run_command_list( s,- 1, 0);

401}

402 #endif /* CONFIG_MENUKEY */

403}

可以看出,autoboot_command函数里面有很多条件编译,条件编译一多就不利于我们阅读程序(所以正点原子的例程基本是不用条件编译的,就是为了方便大家阅读源码)!宏CONFIG_AUTOBOOT_KEYED、CONFIG_AUTOBOOT_KEYED_CTRLC和CONFIG_MENUKEY这三个宏在I.MX6ULL里面没有定义,所以讲示例代码32.2.9.5进行精简,得到如下代码:

示例代码32.2.9.6 autoboot_command函数精简版本

1void autoboot_command( constchar* s)

2{

3if( stored_bootdelay !=- 1&& s &&! abortboot( stored_bootdelay)){

4 run_command_list( s,- 1, 0);

5}

6}

当一下三条全部成立的话,就会执行函数run_command_list。

①、stored_bootdelay不等于-1。

②、s不为空。

③、函数abortboot返回值为0。

stored_bootdelay等于环境变量bootdelay的值;s是环境变量bootcmd的值,一般不为空,因此前两个成立,就剩下了函数abortboot的返回值,abortboot函数也定义在文件common/autoboot.c中,内容如下:

示例代码32.2.9.7 abortboot函数

283staticint abortboot( int bootdelay)

284{

285 #ifdef CONFIG_AUTOBOOT_KEYED

286return abortboot_keyed( bootdelay);

287 #else

288return abortboot_normal( bootdelay);

289 #endif

290}

因为宏CONFIG_AUTOBOOT_KEYE未定义,因此执行函数abortboot_normal,好吧,绕来绕去的!接着来看函数abortboot_normal,此函数也定义在文件common/autoboot.c中,内容如下:

示例代码32.2.9.8 abortboot_normal函数

225staticint abortboot_normal( int bootdelay)

226{

227int abort = 0;

228unsignedlong ts;

229

230 #ifdef CONFIG_MENUPROMPT

231 printf( CONFIG_MENUPROMPT);

232 #else

233if( bootdelay >= 0)

234 printf( "Hit any key to stop autoboot: %2d ", bootdelay);

235 #endif

236

237 #if defined CONFIG_ZERO_BOOTDELAY_CHECK

238/*

239 * Check if key already pressed

240 * Don't check if bootdelay < 0

241 */

242if( bootdelay >= 0){

243if( tstc()){ /* we got a key press */

244( void) getc(); /* consume input */

245 puts( "\b\b\b 0");

246 abort = 1; /* don't auto boot */

247}

248}

249 #endif

250

251while(( bootdelay > 0)&&(! abort)){

252-- bootdelay;

253/* delay 1000 ms */

254 ts = get_timer( 0);

255do{

256if( tstc()){ /* we got a key press */

257 abort = 1; /* don't auto boot */

258 bootdelay = 0; /* no more delay */

259 # ifdef CONFIG_MENUKEY

260 menukey = getc();

261 # else

262( void) getc(); /* consume input */

263 # endif

264break;

265}

266 udelay( 10000);

267}while(! abort && get_timer( ts)< 1000);

268

269 printf( "\b\b\b%2d ", bootdelay);

270}

271

272 putc( '\n');

273

274 #ifdef CONFIG_SILENT_CONSOLE

275if( abort)

276 gd-> flags &=~ GD_FLG_SILENT;

277 #endif

278

279return abort;

280}

函数abortboot_normal同样很多条件编译,删除掉条件编译相关代码后abortboot_normal函数内容如下:

示例代码32.2.9.9 abortboot_normal函数精简

1staticint abortboot_normal( int bootdelay)

2{

3 int abort = 0;

4 unsignedlong ts;

5

6 if( bootdelay >= 0)

7 printf( "Hit any key to stop autoboot: %2d ", bootdelay);

8

9 while(( bootdelay > 0)&&(! abort)){

10 -- bootdelay;

11 /* delay 1000 ms */

12 ts = get_timer( 0);

13 do{

14 if( tstc()){ /* we got a key press */

15 abort = 1; /* don't auto boot */

16 bootdelay = 0; /* no more delay */

17 ( void) getc(); /* consume input */

18 break;

19 }

20 udelay( 10000);

21 }while(! abort && get_timer( ts)< 1000);

22

23 printf( "\b\b\b%2d ", bootdelay);

24 }

25 putc( '\n');

26 return abort;

27}

第3行的变量abort是函数abortboot_normal的返回值,默认值为0。

第7行通过串口输出“Hit any key to stop autoboot”字样,如图32.2.9.2所示:

图32.2.9.2 倒计时

第9~21行就是倒计时的具体实现。

第14行判断键盘是否有按下,也就是是否打断了倒计时,如果键盘按下的话就执行相应的分支。比如设置abort为1,设置bootdelay为0等,最后跳出倒计时循环。

第26行,返回abort的值,如果倒计时自然结束,没有被打断abort就为0,否则的话abort的值就为1。

回到示例代码32.2.9.6的autoboot_command函数中,如果倒计时自然结束那么就执行函数run_command_list,此函数会执行参数s指定的一系列命令,也就是环境变量bootcmd的命令,bootcmd里面保存着默认的启动命令,因此linux内核启动!这个就是uboot中倒计时结束以后自动启动linux内核的原理。如果倒计时结束之前按下了键盘上的按键,那么run_command_list函数就不会执行,相当于autoboot_command是个空函数。

回到“遥远”的示例代码32.2.9.2中的main_loop函数中,如果倒计时结束之前按下按键,那么就会执行第74行的cli_loop函数,这个就是命令处理函数,负责接收好处理输入的命令。

32.2.10 cli_loop函数详解

cli_loop函数是uboot的命令行处理函数,我们在uboot中输入各种命令,进行各种操作就是有cli_loop来处理的,此函数定义在文件common/cli.c中,函数内容如下:

示例代码32.2.10.1 cli.c文件代码段

202void cli_loop( void)

203{

204 #ifdef CONFIG_SYS_HUSH_PARSER

205 parse_file_outer();

206/* This point is never reached */

207for(;;);

208 #else

209 cli_simple_loop();

210 #endif /*CONFIG_SYS_HUSH_PARSER*/

211}

在文件include/configs/mx6_common.h中有定义宏CONFIG_SYS_HUSH_PARSER,而正点原子的I.MX6ULL开发板配置头文件mx6ullevk.h里面会引用mx_common.h这个头文件,因此宏CONFIG_SYS_HUSH_PARSER有定义。

第205行调用函数parse_file_outer。

第207行是个死循环,永远不会执行到这里。

函数parse_file_outer定义在文件common/cli_hush.c中,去掉条件编译内容以后的函数内容如下:

示例代码32.2.10.2 parse_file_outer函数精简

1int parse_file_outer( void)

2{

3 int rcode;

4 struct in_str input;

5

6 setup_file_in_str(& input);

7 rcode = parse_stream_outer(& input, FLAG_PARSE_SEMICOLON);

8 return rcode;

9}

第3行调用函数setup_file_in_str初始化变量input的成员变量。

第4行调用函数parse_stream_outer,这个函数就是hushshell的命令解释器,负责接收命令行输入,然后解析并执行相应的命令,函数parse_stream_outer定义在文件common/cli_hush.c中,精简版的函数内容如下:

示例代码32.2.10.3 parse_stream_outer函数精简

1staticint parse_stream_outer( struct in_str * inp, int flag)

2{

3struct p_context ctx;

4 o_string temp= NULL_O_STRING;

5int rcode;

6int code = 1;

7do{

8......

9 rcode = parse_stream(& temp,& ctx, inp,

10 flag & FLAG_CONT_ON_NEWLINE ?- 1: '\n');

11......

12if( rcode != 1&& ctx. old_flag == 0){

13......

14 run_list( ctx. list_head);

15......

16}else{

17......

18}

19 b_free(& temp);

20/* loop on syntax errors, return on EOF */

21}while( rcode !=- 1&&!( flag & FLAG_EXIT_FROM_LOOP)&&

22( inp-> peek != static_peek || b_peek( inp)));

23return 0;

24}

第7~21行中的do-while循环就是处理输入命令的。

第9行调用函数parse_stream进行命令解析。

第14行调用调用run_list函数来执行解析出来的命令。

函数run_list会经过一系列的函数调用,最终通过调用cmd_process函数来处理命令,过程如下:

示例代码32.2.10.4 run_list执行流程

1staticint run_list( struct pipe * pi)

2{

3 int rcode= 0;

4

5 rcode = run_list_real( pi);

6 ......

7 return rcode;

8}

9

10staticint run_list_real( struct pipe * pi)

11{

12 char* save_name =NULL;

13 ......

14 int if_code= 0, next_if_code= 0;

15 ......

16 rcode = run_pipe_real( pi);

17 ......

18 return rcode;

19}

20

21staticint run_pipe_real( struct pipe * pi)

22{

23 int i;

24

25 int nextin;

26 int flag = do_repeat ? CMD_FLAG_REPEAT : 0;

27 struct child_prog * child;

28 char* p;

29 ......

30 if( pi-> num_progs == 1) child =&( pi-> progs[ 0]);

31 ......

32 return rcode;

33 }elseif( pi-> num_progs == 1&& pi-> progs[ 0]. argv !=NULL){

34 ......

35 /* Process the command */

36 return cmd_process( flag, child-> argc, child-> argv,

37 & flag_repeat,NULL);

38 }

39

40 return- 1;

41}

第5行,run_list调用run_list_real函数。

第16行,run_list_real函数调用run_pipe_real函数。

第36行,run_pipe_real函数调用cmd_process函数。

最终通过函数cmd_process来处理命令,接下来就是分析cmd_process函数。

32.2.11cmd_process函数详解

在学习cmd_process之前先看一下uboot中命令是如何定义的。uboot使用宏U_BOOT_CMD来定义命令,宏U_BOOT_CMD定义在文件include/command.h中,定义如下:

示例代码32.2.11.1 U_BOOT_CMD宏定义

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \

U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

可以看出U_BOOT_CMD是U_BOOT_CMD_COMPLETE的特例,将U_BOOT_CMD_COMPLETE的最后一个参数设置成NULL就是U_BOOT_CMD。宏U_BOOT_CMD_COMPLETE如下:

示例代码32.2.11.2 U_BOOT_CMD_COMPLETE宏定义

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \

ll_entry_declare(cmd_tbl_t, _name, cmd) = \

U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \

_usage, _help, _comp);

宏U_BOOT_CMD_COMPLETE又用到了ll_entry_declare和U_BOOT_CMD_MKENT_COMPLETE。ll_entry_declar定义在文件include/linker_lists.h中,定义如下:

示例代码32.2.11.3 ll_entry_declare宏定义

#define ll_entry_declare(_type, _name, _list) \

_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \

__attribute__((unused, \

section(".u_boot_list_2_"#_list"_2_"#_name)))

_type为cmd_tbl_t,因此ll_entry_declare就是定义了一个cmd_tbl_t变量,这里用到了C语言中的“##”连接符符。其中的“##_list”表示用_list的值来替换,“##_name”就是用_name的值来替换。

宏U_BOOT_CMD_MKENT_COMPLETE定义在文件include/command.h中,内容如下:

示例代码32.2.11.4 U_BOOT_CMD_MKENT_COMPLETE宏定义

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \

_usage, _help, _comp) \

{ #_name, _maxargs, _rep, _cmd, _usage, \

_CMD_HELP(_help) _CMD_COMPLETE(_comp) }

上述代码中的“#”表示将_name传递过来的值字符串化,U_BOOT_CMD_MKENT_COMPLETE又用到了宏_CMD_HELP和_CMD_COMPLETE,这两个宏的定义如下:

示例代码32.2.11.5 _CMD_HELP和_CMD_COMPLETE宏定义

1 #ifdef CONFIG_AUTO_COMPLETE

2 # define _CMD_COMPLETE( x) x,

3 #else

4 # define _CMD_COMPLETE( x)

5 #endif

6 #ifdef CONFIG_SYS_LONGHELP

7 # define _CMD_HELP( x) x,

8 #else

9 # define _CMD_HELP( x)

10 #endif

可以看出,如果定义了宏CONFIG_AUTO_COMPLETE和CONFIG_SYS_LONGHELP的话,_CMD_COMPLETE和_CMD_HELP就是取自身的值,然后在加上一个‘,’。CONFIG_AUTO_COMPLETE和CONFIG_SYS_LONGHELP这两个宏有定义在文件mx6_common.h中。

U_BOOT_CMD宏的流程我们已经清楚了(一个U_BOOT_CMD宏就如此的绕来绕去的!),我们就以一个具体的命令为例,来看一下U_BOOT_CMD经过展开以后究竟是个什么模样的。以命令dhcp为例,dhcp命令定义如下:

示例代码32.2.11.6 dhcp命令宏定义

U_BOOT_CMD(

dhcp, 3, 1, do_dhcp,

"boot image via network using DHCP/TFTP protocol",

"[loadAddress] [[hostIPaddr:]bootfilename]"

);

将其展开,结果如下:

示例代码32.2.11.7 dhcp命令展开

U_BOOT_CMD(

dhcp, 3, 1, do_dhcp,

"boot image via network using DHCP/TFTP protocol",

"[loadAddress] [[hostIPaddr:]bootfilename]"

);

1、将U_BOOT_CMD展开后为:

U_BOOT_CMD_COMPLETE( dhcp, 3, 1, do_dhcp,

"boot image via network using DHCP/TFTP protocol",

"[loadAddress] [[hostIPaddr:]bootfilename]",

NULL)

2、将U_BOOT_CMD_COMPLETE展开后为:

ll_entry_declare( cmd_tbl_t, dhcp, cmd)= \

U_BOOT_CMD_MKENT_COMPLETE( dhcp, 3, 1, do_dhcp, \

"boot image via network using DHCP/TFTP protocol", \

"[loadAddress] [[hostIPaddr:]bootfilename]", \

NULL);

3、将ll_entry_declare和U_BOOT_CMD_MKENT_COMPLETE展开后为:

cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned( 4) \

__attribute__(( unused, section(. u_boot_list_2_cmd_2_dhcp))) \

{ "dhcp", 3, 1, do_dhcp, \

"boot image via network using DHCP/TFTP protocol", \

"[loadAddress] [[hostIPaddr:]bootfilename]", \

NULL}

从示例代码32.2.11.7可以看出,dhcp命令最终展开结果为:

示例代码32.2.11.8 dhcp命令最终结果

1 cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned( 4) \

2 __attribute__(( unused, section(. u_boot_list_2_cmd_2_dhcp))) \

3{ "dhcp", 3, 1, do_dhcp, \

4"boot image via network using DHCP/TFTP protocol", \

5"[loadAddress] [[hostIPaddr:]bootfilename]", \

6NULL}

第1行定义了一个cmd_tbl_t类型的变量,变量名为_u_boot_list_2_cmd_2_dhcp,此变量4字节对齐。

第2行,使用__attribute__关键字设置变量_u_boot_list_2_cmd_2_dhcp存储在.u_boot_list_2_cmd_2_dhcp段中。u-boot.lds链接脚本中有一个名为“.u_boot_list”的段,所有.u_boot_list开头的段都存放到.u_boot.list中,如图32.2.11.1所示:

图32.2.11.1 u-boot.lds中的.u_boot_list段

因此,第2行就是设置变量_u_boot_list_2_cmd_2_dhcp的存储位置。

第3~6行,cmd_tbl_t是个结构体,因此第3-6行是初始化cmd_tbl_t这个结构体的各个成员变量。cmd_tbl_t结构体定义在文件include/command.h中,内容如下:

示例代码32.2.11.9 cmd_tbl_t结构体

30struct cmd_tbl_s {

31 char * name; /* Command Name */

32 int maxargs; /* maximum number of arguments */

33 int repeatable; /* autorepeat allowed? */

34 /* Implementation function */

35 int(* cmd)( struct cmd_tbl_s *, int, int, char* const[]);

36 char* usage; /* Usage message (short) */

37 #ifdef CONFIG_SYS_LONGHELP

38 char* help; /* Help message (long) */

39 #endif

40 #ifdef CONFIG_AUTO_COMPLETE

41 /* do auto completion on the arguments */

42 int(* complete)( int argc, char* const argv[], char last_char, int maxv, char* cmdv[]);

43 #endif

44};

45

46typedef struct cmd_tbl_s cmd_tbl_t;

结合实例代码32.2.11.8,可以得出变量_u_boot_list_2_cmd_2_dhcp的各个成员的值如下所示:

_u_boot_list_2_cmd_2_dhcp.name = "dhcp"

_u_boot_list_2_cmd_2_dhcp.maxargs = 3

_u_boot_list_2_cmd_2_dhcp.repeatable = 1

_u_boot_list_2_cmd_2_dhcp.cmd = do_dhcp

_u_boot_list_2_cmd_2_dhcp.usage = "boot image via network using DHCP/TFTP protocol"

_u_boot_list_2_cmd_2_dhcp.help = "[loadAddress] [[hostIPaddr:]bootfilename]"

_u_boot_list_2_cmd_2_dhcp.complete = NULL

当我们在uboot的命令行中输入“dhcp”这个命令的时候,最终执行的是do_dhcp这个函数。总结一下,uboot中使用U_BOOT_CMD来定义一个命令,最终的目的就是为了定义一个cmd_tbl_t类型的变量,并初始化这个变量的各个成员。uboot中的每个命令都存储在.u_boot_list段中,每个命令都有一个名为do_xxx(xxx为具体的命令名)的函数,这个do_xxx函数就是具体的命令处理函数。

了解了uboot中命令的组成以后,再来看一下cmd_process函数的处理过程,cmd_process函数定义在文件common/command.c中,函数内容如下:

示例代码32.2.11.10 command.c文件代码段

500enum command_ret_t cmd_process( int flag, int argc,

501char* const argv[], int* repeatable, ulong * ticks)

502{

503enum command_ret_t rc = CMD_RET_SUCCESS;

504 cmd_tbl_t * cmdtp;

505

506/* Look up command in command table */

507 cmdtp = find_cmd( argv[ 0]);

508if( cmdtp ==NULL){

509 printf( "Unknown command '%s' - try 'help'\n", argv[ 0]);

510return 1;

511}

512

513/* found - check max args */

514if( argc > cmdtp-> maxargs)

515 rc = CMD_RET_USAGE;

516

517 #if defined( CONFIG_CMD_BOOTD)

518/* avoid "bootd" recursion */

519elseif( cmdtp-> cmd == do_bootd){

520if( flag & CMD_FLAG_BOOTD){

521 puts( "'bootd' recursion detected\n");

522 rc = CMD_RET_FAILURE;

523}else{

524 flag |= CMD_FLAG_BOOTD;

525}

526}

527 #endif

528

529/* If OK so far, then do the command */

530if(! rc){

531if( ticks)

532* ticks = get_timer( 0);

533 rc = cmd_call( cmdtp, flag, argc, argv);

534if( ticks)

535* ticks = get_timer(* ticks);

536* repeatable &= cmdtp-> repeatable;

537}

538if( rc == CMD_RET_USAGE)

539 rc = cmd_usage( cmdtp);

540return rc;

541}

第507行,调用函数find_cmd在命令表中找到指定的命令,find_cmd函数内容如下:

示例代码32.2.11.10 command.c文件代码段

118 cmd_tbl_t * find_cmd( constchar* cmd)

119{

120 cmd_tbl_t * start = ll_entry_start( cmd_tbl_t, cmd);

121constint len = ll_entry_count( cmd_tbl_t, cmd);

122return find_cmd_tbl( cmd, start, len);

123}

参数cmd就是所查找的命令名字,uboot中的命令表其实就是cmd_tbl_t结构体数组,通过函数ll_entry_start得到数组的第一个元素,也就是命令表起始地址。通过函数ll_entry_count得到数组长度,也就是命令表的长度。最终通过函数find_cmd_tbl在命令表中找到所需的命令,每个命令都有一个name成员,所以将参数cmd与命令表中每个成员的name字段都对比一下,如果相等的话就说明找到了这个命令,找到以后就返回这个命令。

回到示例代码32.2.11.10的cmd_process函数中,找到命令以后肯定就要执行这个命令了,第533行调用函数cmd_call来执行具体的命令,cmd_call函数内容如下:

示例代码32.2.11.11 command.c文件代码段

490staticint cmd_call( cmd_tbl_t * cmdtp, int flag, int argc, char* const argv[])

491{

492int result;

493

494 result =( cmdtp-> cmd)( cmdtp, flag, argc, argv);

495if( result)

496 debug( "Command failed, result=%d\n", result);

497return result;

498}

在前面的分析中我们知道,cmd_tbl_t的cmd成员就是具体的命令处理函数,所以第494行调用cmdtp的cmd成员来处理具体的命令,返回值为命令的执行结果。

cmd_process中会检测cmd_tbl的返回值,如果返回值为CMD_RET_USAGE的话就会调用cmd_usage函数输出命令的用法,其实就是输出cmd_tbl_t的usage成员变量。

相关问答

电脑开机出现dislkerror pressanykeytorestart rebootandsel...

buhanrui电脑开机出现“diskerrorpressanykeytorestartrebootandselectproperbootdevice”,其含义:当前磁盘错误,...

to enter setup menu f11 to enter boot menu 0078-ZOL问答

开机时按Delete键进入BIOS找到这两项1、Loadbiosdefaults2、Loadsetupdefaults一些主板只有一项的!是Load开头就行了!找到它按回车输入Y再...

u - boot 与bootloader的区别是什么啊?

bootloader是启动加载的意思,就是启动嵌入式系统等启动操作系统的软件的统称,bootloader是统称,u-boot是其中的一种。就像台式机所说的“操作系统”和“window...

uboot 是什么,在linux中干嘛用的?

u-boot是一种普遍用于嵌入式系统中的Bootloader,Bootloader是在操作系统运行之前执行的一小段程序,通过它,我们可以初始化硬件设备、建立内存空间的映射表,从...

我的电脑出现BOOTMGR is missing并且不可以进Bios和不能U盘...

设置光驱启动,把VISTA安装盘放入光驱,按提示修复电脑即可具体步骤参见:www.mydigitallife.info/2007/08/23/bootmgr-is-missing-during-v...

【发u的英文单词都有哪些?就是像goodfoot这类有oo组合的,要...

[回答]英语上字母组合oo的发音都发[u]音.字母组合oo发短音/u/的单词有:classroom,bedroom,good,foot,stood(understood),wood,wool,...

bsp和 uboot 的区别?

bsp指板级支持包,是介于主板硬件和操作系统之间的一层,应该说是属于操作系统的一部分,主要目的是为了支持操作系统。而uboot是一种普遍用于嵌入式系统中的Boot...

【favorite和favourite有什么不同?】作业帮

[回答]这是美式英语和英式英语的区别,“老”英语里的our新大陆的美国人觉得麻烦,就去掉了“u”,其他的如“荣誉”在美语里是honor,英国人写作honour,英美...

开机按F12弹出 BOOT MENU,里头的USB——FDD、USB——ZIP、USB...

一般选USB——HDD即可从U盘启动。USB-HDD:硬盘仿真模式USB-HDD+:增强的USB-HDD模式USB-ZIP:大容量软盘仿真模式USB-ZIP+:增强的USB-ZIP模式USB...

电脑没法进入系统,开机后写“disk boot failure,inse-ZOL问答

完整的英文提示是:DISKBOOTFAILURE,INSERTSYSEMDISKANDPRESSENTER,意思是:硬盘引导失败,请插入系统启动盘并按回车键。1.出现这个提示信息的原...

 娃娃鱼养殖  青岛大学路小学 
王经理: 180-0000-0000(微信同号)
10086@qq.com
北京海淀区西三旗街道国际大厦08A座
©2024  上海羊羽卓进出口贸易有限公司  版权所有.All Rights Reserved.  |  程序由Z-BlogPHP强力驱动
网站首页
电话咨询
微信号

QQ

在线咨询真诚为您提供专业解答服务

热线

188-0000-0000
专属服务热线

微信

二维码扫一扫微信交流
顶部