[UBOOT] AM335x 启动流程(u-boot-2011.09)

u-boot-2011.09
am335x启动流程:
1.rom code(详见芯片手册)
2.spl(Secondary Program Loader)
	根据spl/u-boot-spl.lds<===arch/arm/cpu/armv7/omap-common/u-boot-spl.lds:
	arch/arm/cpu/armv7/start.o    (.text)
	_start: 
		b	reset
			bl	save_boot_params://arch/arm/cpu/armv7/ti81xx/lowlevel_init.S
				#ifdef CONFIG_SPL_BUILD
					ldr	r4, =ti81xx_boot_device
					ldr	r5, [r0, #BOOT_DEVICE_OFFSET]//rom启动后,将一个BootParametersStructure的指针保存在r0,其中r0+8保存boot device(详见芯片手册)
					and	r5, r5, #BOOT_DEVICE_MASK//取低八位
					str	r5, [r4]//将boot device保存在变量ti81xx_boot_device
				#endif
					bx	lr
			#ifndef CONFIG_SKIP_LOWLEVEL_INIT
			bl	cpu_init_crit//只有在spl中才执行,uboot中不执行
				bl	lowlevel_init//lowlevel_init.S,设置堆栈,跳转到c函数
						bl	s_init//evm.c
							l2_cache_enable();//cache.S,cp15
							//关看门狗
							pll_init();***
							preloader_console_init();//spl.c
								//#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")//r8寄存器保存gd的地址
								//static gd_t gdata __attribute__ ((section(".data")));
								gd = &gdata;//spl阶段r8=gd=&gdata,位于sram (MLO的data段)
							config_am335x_ddr();
			#endif
			bl	board_init_f//spl.c
				/*
				 * We call relocate_code() with relocation target same as the
				 * CONFIG_SYS_SPL_TEXT_BASE. This will result in relocation getting
				 * skipped. Instead, only .bss initialization will happen. That's
				 * all we need //设置重定向地址为sram基地址,故意跳过relocation,只清bbs段
				 */
				 relocate_code(CONFIG_SPL_STACK, &gdata, CONFIG_SPL_TEXT_BASE);
				 	relocate_code://start.S
						mov	r4, r0	/* save addr_sp */ //栈地址=CONFIG_SPL_STACK
						mov	r5, r1	/* save addr of gd */ //gd地址=&gdata(位于data段)
						mov	r6, r2	/* save addr of destination */ //重定位地址=CONFIG_SPL_TEXT_BASE
					
						/* Set up the stack */
					stack_setup:
						mov	sp, r4
						 /*
					    adr伪指令相对于pc(运行时确定)。adr的反汇编就是基于PC的add sub指令
					    位置无关,用于获得_start的真正运行地址(而不是链接地址)
					    */
						adr	r0, _start// adr伪指令相对于pc(运行时确定),当前PC位于sram,所以r0=CONFIG_SPL_TEXT_BASE
						cmp	r0, r6    // r0=r6 说明在sram中运行
						moveq	r9, #0		/* no relocation. relocation offset(r9) = 0 */ //r9保存重定向地址的偏移地址,spl中不进行重定向,r9=0
						beq	clear_bss		/* skip relocation */  //跳过relocation,以下代码在spl中没有执行。调用clear_bss
						
						/************ skip relocation in spl ***********/
						mov	r1, r6			/* r1 <- scratch for copy_loop*/ //r1=sram_start
						ldr	r3, _image_copy_end_ofs //_image_copy_end_ofs=__image_copy_end - _start
						add	r2, r0, r3		/* r2 <- source end address	    */
						
					copy_loop: //在spl中,这段代码是将自己copy给自己。实际上在spl中这段代码没有被调用,因为clear_bss没有返回
						ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */
						stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */
						cmp	r0, r2			/* until source end address [r2]    */
						blo	copy_loop
						/************ skip relocation in spl ***********/
						
					clear_bss:	
						#ifdef CONFIG_SPL_BUILD
						/* No relocation for SPL */
						ldr	r0, =__bss_start
						/*
						 *u-boot-spl.lds,sdram中的bss = CONFIG_SPL_BSS_START_ADDR = 0x80000000,即sdram起始地址
						 *CONFIG_SPL_BSS_MAX_SIZE = 0x80000,即MLO的BBS段位于sdram 0x80000000-0x80080000(512k),
						 */
						ldr	r1, =__bss_end__
						#endif
						mov	r2, #0x00000000		/* clear			    */
					
					clbss_l:str	r2, [r0]		/* clear loop...		    */
						add	r0, r0, #4
						cmp	r0, r1
						bne	clbss_l
				 	/*
					 * We are done. Do not return, instead branch to second part of board
					 * initialization, now running from RAM.
					 */
					jump_2_ram:
					/*
					 * If I-cache is enabled invalidate it
					 */
					#ifndef CONFIG_SYS_ICACHE_OFF
						mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
						mcr     p15, 0, r0, c7, c10, 4	@ DSB
						mcr     p15, 0, r0, c7, c5, 4	@ ISB
					#endif
						ldr	r0, _board_init_r_ofs//r0=board_init_r - _start(board_init_r偏移地址)
						adr	r1, _start //r1=_start的运行地址
						add	lr, r0, r1 //lr=board_init_r的真正运行地址
						add	lr, lr, r9 //r9保存重定向偏移大小,spl中 r9 = 0
						/* setup parameters for board_init_r */
						mov	r0, r5		/* gd_t */ //r0 = &gdata
						mov	r1, r6		/* dest_addr */ //r6=CONFIG_SPL_TEXT_BASE
						/* jump to it ... */
						mov	pc, lr       //跳转到board_init_r
					
					_board_init_r_ofs:
						.word board_init_r - _start
					     //确定board_init_r和_start差值。如果是spl,board_init_r位于spl.c。如果是uboot,board_init_r位于board.c
	
		void board_init_r(gd_t *id, ulong dummy)//spl.c,这段代码仍然在sram中运行	
				timer_init();
				i2c_init
				spl_board_init();
				boot_device = omap_boot_device() = ti81xx_boot_device;	
				spl_nand_load_image();
				{
					gpmc_init();****
					nand_init();****//nand_spl_simple.c
						board_nand_init//ti81xx_nand.c
					/*use CONFIG_SYS_TEXT_BASE as temporary storage area */
					header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);//临时保存image_header到CONFIG_SYS_TEXT_BASE
					nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,//#define CONFIG_SYS_NAND_U_BOOT_OFFS	0x80000//nand的第5块
						CONFIG_SYS_NAND_PAGE_SIZE, (void *)header);//先读nand flash中uboot分区的第一页数据:image_header(64字节)
					spl_parse_image_header(header);//解析 image_header,结果保存在spl_image.其内容由mkimage的参数决定。
					nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,//再读整个uboot镜像到load_addr(sdram)
						spl_image.size, (void *)spl_image.load_addr);//load_addr=0x80800000(entry_point) - 64(image_header_size) = 807FFFC0
					nand_deselect();
				}
				jump_to_image_no_args();
				{
					typedef void (*image_entry_noargs_t)(void)__attribute__ ((noreturn));
					image_entry_noargs_t image_entry =
							(image_entry_noargs_t) spl_image.entry_point;
				//    image_entry_noargs_t image_entry =
				//               (image_entry_noargs_t) 0x80800000;
					image_entry();
				    /*
				    在反汇编中,无论是使用blx还是ldr,最终从sram跳转到sdram中运行。
				    当使用spl_image.entry_point(运行时确定,保存在bss段),反汇编使用ldr pc,[0x8000000c](位于bss)(u-boot-spl.map)。
				    当使用0x80800000时,反汇编使用blx 0x80800000,超过了32M,具体实现细节由连接器实现
					*/ 
				}
					
		//至此,第二阶段spl结束。PC跳转到0x80800000(CONFIG_SYS_TEXT_BASE)执行u-boot
3.u-boot
	根据u-boot.lds<===arch/arm/cpu/armv7/u-boot.lds:
	arch/arm/cpu/armv7/start.o (.text)//u-boot入口函数与spl相同,都是start.S中的_start
	_start: 
		b	reset
			bl	save_boot_params:
					bx	lr//直接返回
			/* Set stackpointer in internal RAM to call board_init_f */
			call_board_init_f:
				ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
				bic	sp, sp, #7 /* 8-byte alignment for ABI compliance //8字节对齐*/
				ldr	r0,=0x00000000
				bl	board_init_f//board.c
				
					//#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")//r8寄存器保存gd的地址
					/* Pointer is writable since we allocated a register for it */
    				//r8=gd=CONFIG_SYS_INIT_SP_ADDR(位于sram尾部128字节),并且8字节对齐。
					gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
					
					memset((void *)gd, 0, sizeof(gd_t));//说明uboot阶段的gd和spl没有关系
					
					for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
						if ((*init_fnc_ptr)() != 0)//遍历执行init_sequence数组中的函数,在这些函数中给gd的成员赋值
						{
								env_init,		/* initialize environment */
						}
					}
					
					/*
				    gd->mon_len = _bss_end_ofs;
				    整个u-boot镜像的大小,从_start到_bss_end(包括bss,不包括image_header)
				    每次修改代码后编译都不同
				    */
					gd->mon_len = _bss_end_ofs;//__bss_end__ - _start
					
					//dram_init:gd->ram_size = PHYS_DRAM_1_SIZE;
				    //addr=CONFIG_SYS_SDRAM_BASE + PHYS_DRAM_1_SIZE=0xa0000000,sdram尾部
					addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
					
					#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
					/* reserve TLB table */
					addr -= (4096 * 4);//addr=0xa0000000 - 16k = 0x9FFFC000
					/* round down to next 64 kB limit */
					addr &= ~(0x10000 - 1);//64k对齐,addr=0x9FFFC000 & ~ 0xFFFF=0x9FFF0000
					gd->tlb_addr = addr;
					#endif
					
					addr &= ~(4096 - 1);// 4k对齐,addr=0x9FFF0000
					
					/*
					 * reserve memory for U-Boot code, data & bss
					 * round down to next 4 kB limit
					 */
					addr -= gd->mon_len;//addr=0x9FFF0000-0x4B6D70 = 0x9FB39290
					addr &= ~(4096 - 1);// 4k对齐 addr = 0x9FB39000,uboot重定向地址addr
					
					/*
					 * reserve memory for malloc() arena
					 */
					addr_sp = addr - TOTAL_MALLOC_LEN;//addr_sp=0x9FB39000 - 1048k = 0x9FA33000
					/*
					 * (permanently) allocate a Board Info struct
					 * and a permanent copy of the "global" data
					 * 在sdram分配一个gd_t:id,用于拷贝gd(sram),最后在将id赋值给gd,之后的gd也指向0x9FA32F70
					 */
					addr_sp -= sizeof (bd_t);//addr_sp=0x9FA33000 - 24 = 0x9FA32FE8
					bd = (bd_t *) addr_sp;//bd = 0x9FA32FE8
					gd->bd = bd;
				
				    //sizeof (gd_t)=120 ?? 计算为116
					addr_sp -= sizeof (gd_t);//addr_sp=0x9FA32FE8-120=0x9FA32F70
					id = (gd_t *) addr_sp;//id=0x9FA32F70
					
					/* leave 3 words for abort-stack    */
					addr_sp -= 12;//addr_sp=0x9FA32F70-12=0x9FA32F64
					
					/* 8-byte alignment for ABI compliance */
					addr_sp &= ~0x07;//add_sp=0x9FA32F60
					
					gd->bd->bi_baudrate = gd->baudrate;//init_baudrate
					/* Ram ist board specific, so move it to board code ... */
					dram_init_banksize();
					display_dram_config();	/* and display it */
				
					gd->relocaddr = addr;//addr=0x9FB39000,uboot重定向地址addr
					gd->start_addr_sp = addr_sp;//addr_sp=0x9FA32F60
					gd->reloc_off = addr - _TEXT_BASE;//gd->reloc_off=0x9FB39000-CONFIG_SYS_TEXT_BASE=0x9FB39000-0x80800000=1F339000
					memcpy(id, (void *)gd, sizeof(gd_t));//将gd(sram:CONFIG_SYS_INIT_SP_ADDR)内容付给id(sdram:0x9FA32F70)
				
					relocate_code(addr_sp, id, addr);//addr_sp=0x9FA32F60;id=0x9FA32F70;addr=0x9FB39000
					
    relocate_code://start.S
    	relocate_code:
			mov	r4, r0	/* save addr_sp */ //栈地址=0x9FA32F60
			mov	r5, r1	/* save addr of gd */ //id地址=0x9FA32F70
			mov	r6, r2	/* save addr of destination */ //重定位地址=0x9FB39000
		
			/* Set up the stack						    */
		stack_setup:
			mov	sp, r4
		
		    /*
		    adr伪指令相对于pc(运行时确定)。adr的反汇编就是基于PC的add sub指令
		    位置无关,用于获得_start的真正运行地址(而不是链接地址)
		    */
			adr	r0, _start	//r0 = 0x80800000
			cmp	r0, r6    // r0!=r6 需要重定向
			
			/**** 不执行 ****/
			moveq	r9, #0		/* no relocation. relocation offset(r9) = 0 */
			beq	clear_bss		/* skip relocation */
			/**** 不执行 ****/
		
			/************ relocation  ***********/
			mov	r1, r6			/* r1 <- scratch for copy_loop*/ //r1=0x9FB39000 重定向地址
			ldr	r3, _image_copy_end_ofs //_image_copy_end_ofs=__image_copy_end(u-boot.lds) - _start;表示需要copy的uboot镜像大小=0x64854(反汇编)  
			add	r2, r0, r3		/* r2 <- source end address */ //r2=0x80800000+0x64854=0x80864854
		
		copy_loop: //将uboot(从_start到___image_copy_end)从地址r0到地址r2 copy到 地址r1
			ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */
			stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */
			cmp	r0, r2			/* until source end address [r2]    */
			blo	copy_loop
    	
    		/*
    		
    		ldr	pc, _undefined_instruction//位置无关,_undefined_instruction基于PC计算
    		_undefined_instruction: .word undefined_instruction//位置相关,undefined_instruction编译期间确定
    		
    		Disassembly of section .text:
    		80800020 <_undefined_instruction>:
    	 	80800020:       808001a0        .word   0x808001a0
    	 	
    	 	808001a0 <undefined_instruction>:
    		808001a0:       e51fd154        ldr     sp, [pc, #-340] ; 80800054 <IRQ_STACK_START_IN>

    		Disassembly of section .rel.dyn
    		808648dc:       80800020        addhi   r0, r0, r0, lsr #32
    		
    		修复代码重定向之后某些位置相关的代码不能执行问题。
    		uboot 在ld时,指定 -pie,生成的uboot会包含_rel_dyn段,
    		_rel_dyn段(808648dc)的作用就是保存“保存函数(text)、常量(rodata)绝对地址(808001a0:undefined_instruction)”的地址(80800020)
    		下面的代码就是遍历重定位的uboot的_rel_dyn段(还有dynsym段),
    		将_rel_dyn段(808648dc)中的每一个地址(80800020)所指向的地址(808001a0)加上relocation offset
    		80800020:       808001a0        .word   0x808001a0
    		变为
    		80800020:       808001a0+offset        .word   0x808001a0
    		*/
	    	#ifndef CONFIG_SPL_BUILD//uboot
			/*
			 * fix .rel.dyn relocations
			 */
			ldr	r0, _TEXT_BASE		/* r0 <- Text base */ //r0=0x80800000
			sub	r9, r6, r0		/* r9 <- relocation offset */ //r9=0x9FB39000 - 0x80800000=1F339000
			ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */ //r10=0x6d104(反汇编)
			add	r10, r10, r0		/* r10 <- sym table in FLASH */ //r10=0x8086D104,_dynsym_start在sdram中的真实地址
			ldr	r2, _rel_dyn_start_ofs	/* r2 <- rel dyn start ofs */ //r2=0x64854(反汇编)
			add	r2, r2, r0		/* r2 <- rel dyn start in FLASH */ //r2=0x80864854,_rel_dyn_start在sdram中的真实地址
			ldr	r3, _rel_dyn_end_ofs	/* r3 <- rel dyn end ofs */ //r3=0x6d104(反汇编)
			add	r3, r3, r0		/* r3 <- rel dyn end in FLASH */ //r3=0x8086D104,_rel_dyn_end在sdram中的真实地址=_dynsym_start(u-boot.lds)
		fixloop:
			ldr	r0, [r2]		/* r0 <- location to fix up, IN FLASH! */
			add	r0, r0, r9		/* r0 <- location to fix up in RAM */
			ldr	r1, [r2, #4]
			and	r7, r1, #0xff
			cmp	r7, #23			/* relative fixup? */
			beq	fixrel
			cmp	r7, #2			/* absolute fixup? */
			beq	fixabs
			/* ignore unknown type of fixup */
			b	fixnext
		fixabs:
			/* absolute fix: set location to (offset) symbol value */
			mov	r1, r1, LSR #4		/* r1 <- symbol index in .dynsym */
			add	r1, r10, r1		/* r1 <- address of symbol in table */
			ldr	r1, [r1, #4]		/* r1 <- symbol value */
			add	r1, r1, r9		/* r1 <- relocated sym addr */
			b	fixnext
		fixrel:
			/* relative fix: increase location by offset */
			ldr	r1, [r0]
			add	r1, r1, r9
		fixnext:
			str	r1, [r0]
			add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */
			cmp	r2, r3
			blo	fixloop
			b	clear_bss
		_rel_dyn_start_ofs:
			.word __rel_dyn_start - _start
		_rel_dyn_end_ofs:
			.word __rel_dyn_end - _start
		_dynsym_start_ofs:
			.word __dynsym_start - _start
		
		#endif	/* #ifndef CONFIG_SPL_BUILD */
		
		clear_bss:
			ldr	r0, _bss_start_ofs//r0=0x64854
			ldr	r1, _bss_end_ofs//r1=0x4b6d70
			mov	r4, r6			/* reloc addr */ //r4=0x9FB39000 (重定向地址)
			add	r0, r0, r4      //r0=9FB9D854 (重定向后的bbs_start)
			add	r1, r1, r4      //r1=9FFEFD70 (重定向后的bbs_end)
		
			mov	r2, #0x00000000		/* clear			    */
		
		clbss_l:str	r2, [r0]		/* clear loop...		    */
			add	r0, r0, #4
			cmp	r0, r1
			bne	clbss_l
		
		/*
		 * We are done. Do not return, instead branch to second part of board
		 * initialization, now running from RAM.
		 */
		jump_2_ram:
			ldr	r0, _board_init_r_ofs//r0=0x01a04(反汇编)
			adr	r1, _start //r1=0x80800000(adr基于pc的位置无关码,如果pc=当前链接地址,那么_start=_TEXT_BASE)
			add	lr, r0, r1 //lr=0x01a04 + 0x80800000 = 80801A04
			add	lr, lr, r9 //(r9 保存着 relocation offset;spl中 r9 = 0;uboot中 r9=1F339000)
			//lr=80801A04+1F339000=1F33AA04(重定向后board_init_r的地址)
		
			/* setup parameters for board_init_r */
			mov	r0, r5		/* gd_t */ //r0 = id地址 =0x9FA32F70
			mov	r1, r6		/* dest_addr */ //r6=重定位地址=0x9FB39000
			/* jump to it ... */
			mov	pc, lr       //跳转到1F33AA04(重定向后board_init_r的地址) 绝对跳转
	
	board_init_r//board.c
	{
		gd = id;//r8=gd=id=0x9FA32F70
		bd = gd->bd;//bd=gd->bd=0x9FA32FE8
		
		board_init();//evm.c
		{
			gpmc_init();
			board_evm_init();
			{
				gd->bd->bi_arch_number = MACH_TYPE_TIAM335EVM;//3589
				/* address of boot parameters */
				gd->bd->bi_boot_params = PHYS_DRAM_1 + 0x100;//0x80000100
			}
		}
		nand_init();//nand.c
		{
			nand_init_chip
			{
				int board_nand_init(struct nand_chip *nand);//初始化nand_chip
				{
					ti81xx_nand_switch_ecc(NAND_ECC_HW, 2);
					{
						__ti81xx_nand_switch_ecc
						{
							nand->options |= NAND_OWN_BUFFERS;
							ti81xx_hwecc_init_bch(nand, NAND_ECC_READ);
						}		
						nand_scan_tail(mtd);
							//初始化阶段,在这里调用的nand_scan_tail本质上没有作用,直接返回(因为在ti81xx_hwecc_init_bch设置了NAND_OWN_BUFFERS)						
						nand->options &= ~NAND_OWN_BUFFERS;
					}
				}
				nand_scan
				{
					nand_scan_ident
					{
						nand_set_defaults
							chip->cmdfunc = nand_command;//这时还是小页的nand_command
						nand_get_flash_type
						{
							chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//READID
							*maf_id = chip->read_byte(mtd);//1st MID
							*dev_id = chip->read_byte(mtd);//2nd PID
							nand_flash_detect_onfi//不支持onfi,直接返回0;
							{
								chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);//READID,ONFI
								read_byte //4个周期 O、N、F、I
								chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
								chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));//读nand_onfi_params结构体
							}
							nand_flash_detect_non_onfi//readid 3rd/4th个周期
							

							chip->erase_cmd = single_erase_cmd;
							if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
								chip->cmdfunc = nand_command_lp;//改为大页的nand_command_lp
						}
					}
					nand_scan_tail
				}
			}
		}		
		/* initialize environment */
		env_relocate();//初始化环境变量
		...
		...
		main_loop();
		{
			#if//超时,启动内核
			s = getenv ("bootcmd");
			run_command (s, 0);
			
			#else//没有超时,进入命令行模式
			run_command("menu", 0);
			for (;;) {//uboot命令行模式的死循环
				len = readline (CONFIG_SYS_PROMPT);//读命令行字符串到console_buffer
				{
					return readline_into_buffer(prompt, console_buffer);
				}
				flag = 0;
				if (len > 0)//命令行敲入命令
				{
					strcpy (lastcommand, console_buffer);
					if(strcmp(get_cmd,"00")==0 ... )如果是自定义的菜单命令
					{
						run_menu_command(get_cmd);
							run_command(load_cmd, 0);
					}
				}else if (len == 0)//直接回车,重复上个命令
				{
					
					flag |= CMD_FLAG_REPEAT;
				}
				/*
				 * 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)
				 */
				rc = run_command (lastcommand, flag);
				{
					if (!cmd || !*cmd) {
						return -1;	/* empty command */
					}
					process_macros (token, finaltoken);//处理宏定义
					{
						envval = getenv (envname);
					}
					/* Extract arguments */
					argc = parse_line (finaltoken, argv);//解析参数 argv argc
					
					/* Look up command in command table */
					cmdtp = find_cmd(argv[0]);//argv[0]=命令的名字
					{
						int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
						return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
					}
					
					(cmdtp->cmd) (cmdtp, flag, argc, argv);//执行函数
					
					repeatable &= cmdtp->repeatable;
					//run_command执行没有错误rc=0,返回repeatable
				    //run_command执行有错误rc=-1,返回rc=-1
					return rc ? rc : repeatable;
				}
				/*
				命令执行失败或者命令成功执行但不能重复执行,清除lastcommand;
				下次循环如果直接回车,执行到run_command时直接返回-1;
				*/
				if (rc <= 0) {
				/* invalid command or not repeatable, forget it */
					lastcommand[0] = 0;
				} else (rc == 1)
				/*
				命令执行成功并且可重复执行,
				lastcommand保持为这次成功执行的可重复的命令;
				下次循环如果直接回车,执行到run_command时和上次循环相同,实现了命令的重复执行;
				*/
			}
			
		}
	}				


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值