u-boot的命令行函数 run_command ()分析

❏【u-boot 分析】 专栏收录该内容
10 篇文章 1 订阅

一、run_command () 函数分析

/****************************************************************************
 * returns: (返回参数  1,0,-1)
 *	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:入口字符串命令 flag:参数
{
	cmd_tbl_t *cmdtp;
	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd	*///cmdbuf:用来备份的
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
	char finaltoken[CONFIG_SYS_CBSIZE];
	char *str = cmdbuf;		//*str指向备份的入口命令
	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
	int argc, inquotes;
	int repeatable = 1;
	int rc = 0;

#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 */

//先对字符串命令cmd的有效性进行检测,判断该命令是否为空
	if (!cmd || !*cmd) {
		return -1;	/* empty command */
	}
	
//判断字符串命令的长度是否在CFG_CBSIZE(256)范围之内
	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
		puts ("## Command too long!\n");
		return -1;
	}
	
//备份入口命令cmd到cmdbuf
	strcpy (cmdbuf, cmd);

	/* Process separators and check for invalid
	 * repeatable commands
	 */

#ifdef DEBUG_PARSER
	printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif

//str指向cmdbuf备份命令,循环判断字符串命令有几个
/*因为uboot允许一次输入多个命令,以下就是分析 是否有多个命令,*/ 
/* 比如在uboot界面输入"print;md.w 0"回车后,先打印出环境参数后,后打印地址0里存放的数据。*/ 
	while (*str) {

		/*
		 * Find separator, or string end
		 * Allow simple escape of ';' by writing "\;"
		 */
		for (inquotes = 0, sep = str; *sep; sep++) {	//sep指向当前命令str的开头,一直for寻找当前命令结尾处
			if ((*sep=='\'') &&		//对"\"解析,分割成多个命令 
			    (*(sep-1) != '\\'))
				inquotes=!inquotes;

			if (!inquotes &&
			    (*sep == ';') &&	/* separator*/	 //判断当前等于";"  
			    ( sep != str) &&	/* past string start*/  //且当前指向的位置不是命令的开头, 
			    (*(sep-1) != '\\'))	/* and NOT escaped	*///停止本次for循环,sep指向当前这个命令结尾处 
				break;
		}

		/*
		 * Limit the token to data between separators
		 */
		token = str;	//token指向当前命令的开头 
		if (*sep) {	//将当前";"分割符处替换成'\0'空字符 
			str = sep + 1;	/* start of command for next pass *///str命令指向下个命令,若下个命令为空,退出while (*str)循环. 
			*sep = '\0';//将当前命令结尾";"处替换成'\0'空字符 
		}
		else
			str = sep;	/* no more commands for next pass *///如果没有命令了,就指向当前命令底部 
#ifdef DEBUG_PARSER
		printf ("token: \"%s\"\n", token);
#endif

//token=当前命令开头,将当前命令中的宏替换掉,例如命令"nand write .yaffs 30000000 0X00260000 $(kernelsize)":
//其中$(kernelsize)就是宏,这里将替换成文件大小长度
		/* find macros in this token and replace them */
		process_macros (token, finaltoken);

//argc等于参数的个数。
//parse_line函数:解析当前命令用argv数组保存并返回当前命令参数个数,例如"md.w 0"->argv[0]=“md.w”(保存命令), argv[1]=“0”(保存参数)
		/* Extract arguments */
		if ((argc = parse_line (finaltoken, argv)) == 0) {
			rc = -1;	/* no command at all */
			continue;
		}
		
/* find_cmd(argv[0])) :查找.u_boot_cmd段中是否有这个命令argv[0]名字,若有的话返回这个命令的结构体,否则返回NULL。*/
/* cmdtp: 指向argv[0]命令名字的结构体. */
		/* Look up command in command table */
		if ((cmdtp = find_cmd(argv[0])) == NULL) {
			printf ("Unknown command '%s' - try 'help'\n", argv[0]);	//输出提示,未找到命令
			rc = -1;	/* give up after bad command */
			continue;
		}
		
		/*其中find_cmd()返回值和cmdtp都是一个cmd_tbl_s型结构体,其中成员如下所示:
		struct cmd_tbl_s {
		char	*name;	  //命令的名字
		int    maxargs;    //命令后带的最大参数个数
		int    repeatable;	  //定义命令是否可重复,例如:在uboot界面输入"md.w 0"打印后,再次敲回车键继续运行该命令.
		
		int    (*cmd)(struct cmd_tbl_s *, int, int, char *[]); //函数指针,用于命令执行时需要调用什么函数
		char	*usage;    // 该命令所对应得较短的使用说明,例如输入“help”,每行命令后面都跟着较短的使用说明 
#ifdef    CFG_LONGHELP
		char	*help;	  // 该命令所对应得较详细的使用说明,例如输入“help md”,会打印出该命令详细的使用说明 
#endif 
		};
		*/

		/* found - check max args */
		if (argc > cmdtp->maxargs) {	//检查当前命令的参数个数argc是否在最大参数个数范围内
			cmd_usage(cmdtp);
			rc = -1;
			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 = -1;
				continue;
			} else {
				flag |= CMD_FLAG_BOOTD;
			}
		}
#endif

		/* OK - call function to do the command */
		/*(cmdtp->cmd) (cmdtp, flag, argc, argv) 这句是传递参数,并执行命令*/
		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {//cmdtp:当前命令结构体,判断cmdtp->cmd执行命令有没有函数
			rc = -1;	
		}

		repeatable &= cmdtp->repeatable;	//设置命令重复执行标志

		/* Did the user stop this? */
		if (had_ctrlc ())	//检查是否有ctrl+c按键按下,如果有的话,取消当前命令执行。
			return -1;	/* if stopped then not repeatable */
	}

	return rc ? rc : repeatable;
}

进入find_cmd()函数如下:

cmd_tbl_t *find_cmd (const char *cmd)
{
	int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
	return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
}

进入find_cmd_tbl()函数如下:

/***************************************************************************
 * find command table entry for a command
 */
cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
	cmd_tbl_t *cmdtp;  //定义一个cmd_tbl_t 类型的指针变量
	cmd_tbl_t *cmdtp_temp = table;	/*Init value */  //定义一个cmd_tbl_t 类型的指针变量(用于返回匹配结果),并且初始化为指向第一个cmd_tbl_t 结构体
	const char *p;		//p指针变量,临时存储‘.’符号在cmd中的位置,计算命令长度用
	int len;	//存储命令长度
	int n_found = 0; //简写指令匹配次数计数用

	/*
	 * Some commands allow length modifiers (like "cp.b");
	 * compare command name only until first dot.
	 */
 //查找'.'这个字符,用来获得当前字符串命令长度len	 
	len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);  //p - cmd ,p指针地址减cmd的指针地址
	
//下边的for循环就是在命令结构体存储区中,从前到后依次查找是否有匹配的命令
	for (cmdtp = table;		//指向第一个cmd_tbl_t结构体
	     cmdtp != table + table_len;	//内存中的cmd_tbl_t结构体个数
	     cmdtp++) {
		if (strncmp (cmd, cmdtp->name, len) == 0) {	//查找到当前命令(这里匹配的是按你输入命令的长度,匹配成功后还有比较你输入的命令是否是完整命令)
			if (len == strlen (cmdtp->name))	//检查是否是完整命令
				return cmdtp;	/* full match */ //完全匹配,返回匹配结果

			cmdtp_temp = cmdtp;	/* abbreviated command ? */
			n_found++; //简写指令匹配成功的次数
		}
	}
	if (n_found == 1) {			/* exactly one match */ //如果简写命令只有一个匹配,那么说明就是要找的命令
		return cmdtp_temp;	//返回结果
	}

	return NULL;	/* not found or ambiguous command */ //否则未知指令
}

二、命令定义分析,分析命令是怎么定义出来的:

例如:"boodcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0 "中bootm命令(定义过程,如何定义的)分析:

2.1“bootm 0x30007FC0” 是使用bootm命令,参数为0x30007FC0, 该命令位于Cmd_bootm.C
先搜索bootm命令,位于./common/Cmd_bootm.C (命令文件都存在common文件里,Cmd_bootm.C就是定义bootm命令的文件)
进入./common/Cmd_bootm.C:

其中执行bootm这个命令时所对应的函数就是:

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);

这个do_bootm函数等于bootm命令里的宏U_BOOT_CMD->cmd
do_bootm()成员:

cmd_tbl_t *cmdtp :  指向命令段里的bootm命令结构体
flag   :      参数
argc   :      参数个数,例如"bootm 0x30007FC0",那么argc=2。
argv   :      存参数的数组,共argc个.例如"bootm 0x30007FC0",那么argv[0]="bootm",argv[1]="0x30007FC0".

它在U_BOOT_CMD宏里,是因为每个命令都是通过U_BOOT_CMD宏定义调用的,如下:

U_BOOT_CMD(                            //U_BOOT_CMD宏里有bootm成员,CFG_MAXARGS成员等
bootm,    CFG_MAXARGS,    1,    do_bootm,                 // do_bootm是一个函数名
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n" //usage成员,较短的帮助说明
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n" 
"\ta bd_info struct will be passed instead\n" //help成员,详细的帮助说明
#endif 
);

2.2 再来看看U_BOOT_CMD宏是怎么定义的,宏U_BOOT_CMD在./include/command.h定义,如下所示:

#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}

这里定义了全局变量:U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)等于"cmd_tbl_t _u_boot_cmd##name Struct_Section={#name, maxargs, rep, cmd, usage, help}"

2.3 其中U_BOOT_CMD宏各个参数意义如下:

cmd_tbl_t: 定义__u_boot_cmd_bootm的类型为cmd_tbl_t结构体
##name:    指向name,其中name指向第2.1节里面U_BOOT_CMD宏里的第一个成员                       bootm.
Struct_Section :     保存命令的段位置参数,./include/Command.h中定义, “#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd"))),section表示强制将 段属性设为.u_boot_cmd命令段
#name:     U_BOOT_CMD宏里第一个成员,命令名字,等于"bootm"
maxargs:  最大参数个数,等于CFG_MAXARGS
rep:        [repeat]是否支持重复,等于1,表示支持
cmd:          执行命令后对应的函数指针,执行命令时就会使用该指针	,2.1节里,
usage:保存字符串,用于较短的帮助说明,等于上面代码里"bootm - boot application                     image from  memory\n"
help:用于详细的帮助说明,等于U_BOOT_CMD宏里usage成员后剩下的几行字符串  (它们之间没有加逗号,所以那几行字符串都是连接在一起的).

2.4所以对于bootm命令,最终扩展开为: cmd_tbl_t __u_boot_cmd_bootm attribute ((unused,section (".u_boot_cmd"))) /
={bootm, CFG_MAXARGS, 1, do_bootm, “字符串1”,"“字符串2”}

所有uboot中命令定义都是通过 U_BOOT_CMD宏 保存在.u_boot_cmd段中,通过run_command()函数调用.
接下来学习怎么仿照bootm命令来制作hello命令。

三、u-boot自定义添加命令

1、添加命令

1.u-boot的命令格式:

U_BOOT_CMD(name,maxargs,repeatable,command,”usage”,“help”)

name:命令的名字,不是一个字符串;

maxargs:最大的参数个数;

repeatable:命令是可重复的;

command:对应的函数指针

2.在uboot/common目录下,随便找一个cmd_xxx.c文件,将cmd_xxx.c文件拷贝一下,并重新命名为cmd_hello.c

cp cmd_xxx.c cmd_hello.c

3.进入到cmd_hello.c中,修改

a:修改宏U_BOOT_CMD

U_BOOT_CMD宏参数有6个:

第一个参数:添加的命令的名字

第二个参数:添加的命令最多有几个参数(注意,假如你设置的参数个数是3,而实际的参数个数是4,那么执行命令会输出帮助信息的)

第三个参数:是否重复(1重复,0不重复)(即按下Enter键的时候,自动执行上次的命令)

第四个参数:执行函数,即运行了命令具体做啥会在这个函数中体现出来

第五个参数:帮助信息(short)

第六个参数:帮助信息(long)

b:定义执行函数

执行函数格式:int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

然后在该函数中添加你想要做的事,比如打印一行信息

int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
      printf(“hello world\n”);
      return 0;  
    }

c:uboot中添加命令基本操作已经做完,但是还差一步,就是将该命令添加进uboot中,

之前的操作虽然添加了一个cmd_hello.c文件,但是还没有将该文件放进Uboot的代码中,

所以,我们需要在uboot下common文件的Makfile中添加一行:

COBJS-y += cmd_hello.o

#include<command.h>
#include<common.h>
#ifdef CONFIG_CMD_HELLO
int do_hello(cmd_tbl_t *cmdtp,int flag,int argc,char *argv)
{
        printf("my test \n");
        return 0;
}
U_BOOT_CMD(
hello,1,0,do_hello,"usage:test\n","help:test\n"
);
#endif

 
  • 1
    点赞
  • 0
    评论
  • 7
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值