uboot源码分析-uboot命令体系

导读:本文以S5PV210为例,从uboot源代码角度分析uboot命令体系架构,并举例说明如何在uboot代码中添加自己的命令。为了展示方便,文章中的代码有进行删减,主要删除了无效的宏定义,可能有误删。

1、uboot命令存储

1.1 uboot命令结构体

每个uboot命令都对应一个结构体,添加一个命令就要添加一个结构体,并初始化结构体。该结构体主要包含命令的名字、命令最大可接受参数个数、命令执行函数和帮助信息等,具体定义如下:

struct cmd_tbl_s {
	char *name;		/* 命令名字 */
	int	 maxargs;	/* 命令最大接收参数个数	*/
	int	 repeatable;	/* 是否允许按回车重复执行	*/
	int  (*cmd)(struct cmd_tbl_s *, int, int, char *[]);  //命令执行函数
	char  *usage;		/* 短帮助信息字符串	*/
	char  *help;		/* 长帮助信息字符串	*/
};

typedef struct cmd_tbl_s	cmd_tbl_t;

1.2 uboot命令段声明

uboot命令都申明成cmd_tbl_t结构体,这些结构体都会放在u_boot_cmd段中,链接脚本中关于u_boot_cmd声明如下:
在这里插入图片描述
注:__u_boot_cmd_start 和 __u_boot_cmd_end为命令结构体起始地址和结束地址,被find_cmd函数调用,方便函数查找uboot支持的命令。

2、uboot如何解析命令

2.1 main_loop主循环

在main_loop函数最后有一个死循环,该循环就是命令行的读取和解析,工作流程如下:

  1. 读取命令:调用readline函数,该函数循环读取用户输入的字符,将输入的字符串缓存到全局变量console_buff中,当用户输入了回车就退出,并返回读到的长度。
  2. 读取结果判断:对返回值判断,大于0表示读取正常,将console_buffer赋值给lastcommand;等于0表示用户按下ctrl+C终止输入;
  3. 执行命令:调用run_command执行用户输入的命令
	#define CFG_PROMPT              "x210 # "   /* Monitor Command Prompt       */
	static char lastcommand[256] = { 0, };
	for (;;) {
		len = readline(CFG_PROMPT);  //读取用户输入到全局变量console_buffer,遇到\n或\r就返回

		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy(lastcommand, console_buffer); //将用户输入的命令放到lastcommand
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;

		if (len == -1)  //按下了ctrl + C
			puts("<INTERRUPT>\n");
		else
			rc = run_command(lastcommand, flag);  //执行命令

		if (rc <= 0) {
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
	}

2.2 run_command执行命令

run_command首先校验cmd,然后处理\ ; ( {等特殊字符,再将cmd中的命令提取出来,再调用find_cmd查找命令,最后对查找到的命令尽心校验,最后执行命令。

int run_command (const char *cmd, int flag)
{
	cmd_tbl_t *cmdtp;
	char cmdbuf[CFG_CBSIZE];	/* working copy of cmd		*/
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
	char finaltoken[CFG_CBSIZE];
	char *str = cmdbuf;
	char *argv[CFG_MAXARGS + 1];	/* NULL terminated	*/
	int argc, inquotes;
	int repeatable = 1;
	int rc = 0;

	clear_ctrlc();		/* forget any previous Control C */

	if (!cmd || !*cmd) {  //命令行非空判断
		return -1;	/* empty command */
	}

	if (strlen(cmd) >= CFG_CBSIZE) {  //命令行太长无法处理
		puts ("## Command too long!\n");
		return -1;
	}
	strcpy(cmdbuf, cmd);

	/* Process separators and check for invalid, repeatable commands */
	while (*str) { //处理命令
		/* Find separator, or string end
		 * Allow simple escape of ';' by writing "\;" */
		for (inquotes = 0, sep = str; *sep; sep++) { //处理 \ ;等字符
			if ((*sep=='\'') &&
			    (*(sep-1) != '\\'))
				inquotes=!inquotes;

			if (!inquotes &&
			    (*sep == ';') &&	/* separator		*/
			    ( sep != str) &&	/* past string start	*/
			    (*(sep-1) != '\\'))	/* and NOT escaped	*/
				break;
		}

		/* Limit the token to data between separators*/
		token = str;
		if (*sep) {
			str = sep + 1;	/* start of command for next pass */
			*sep = '\0';
		}
		else
			str = sep;	/* no more commands for next pass */

		/* find macros in this token and replace them */
		process_macros(token, finaltoken);  //已经输入了\ ( {等字符,换行继续等待输入

		/* Extract arguments */
		if ((argc = parse_line(finaltoken, argv)) == 0) { //将命令提取出来,放到argv里
			rc = -1;	/* no command at all */
			continue;
		}

		/* 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;
		}

		/* found - check max args */
		if (argc > cmdtp->maxargs) {
			printf("Usage:\n%s\n", cmdtp->usage);
			rc = -1;
			continue;
		}
		
		/* avoid "bootd" recursion */
		if (cmdtp->cmd == do_bootd) {

			if (flag & CMD_FLAG_BOOTD) {
				puts("'bootd' recursion detected\n");
				rc = -1;
				continue;
			} else {
				flag |= CMD_FLAG_BOOTD;
			}
		}

		/* OK - call function to do the command */
		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {  //执行命令
			rc = -1;
		}

		repeatable &= cmdtp->repeatable;

		/* Did the user stop this? */
		if (had_ctrlc())
			return -1;	/* if stopped then not repeatable */
	}

	return rc ? rc : repeatable;
}

2.3 find_comd查找命令

在run_command函数中会调用find_comd函数查找命令,该函数会去链接脚本申明的uboot命令空间查找,如果有匹配上的就返回该命令的结构体指针,如果没有就返回空,同时该函数支持用户输入简写(简写必须唯一匹配uboot命令,否则返回空)。

cmd_tbl_t *find_cmd (const char *cmd)
{
	cmd_tbl_t *cmdtp;
	cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; //链接脚本中指定的uboot命令结构体起始地址
	const char *p;
	int len;
	int n_found = 0;

	/* Some commands allow length modifiers (like "cp.b");
	 * compare command name only until first dot. */
	len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);  //处理带.的命令

	for (cmdtp = &__u_boot_cmd_start;  //uboot命令结构体起始地址,在链接脚本中定义
	     cmdtp != &__u_boot_cmd_end;   //uboot命令结构体结束地址
	     cmdtp++) {
		if (strncmp (cmd, cmdtp->name, len) == 0) {
			if (len == strlen(cmdtp->name)) //字符串前len字节内容相等且长度相当
				return cmdtp;	//找到了命令,返回命令结构体指针

			cmdtp_temp = cmdtp;	  //字符串前len个字节相等,但长度不相等,输入可能是简写
			n_found++;
		}
	}
	if (n_found == 1) {     //输入命令为简写,只有一个匹配才返回
		return cmdtp_temp;
	}

	return NULL;	//没找到,返回空
}

3 、uboot如何定义一个新命令

  1. U_BOOT_CMD宏定义
    U_BOOT_CMD就是定义cmd_tbl_t结构体,调用一个U_BOOT_CMD就声明一个uboot命令结构体,并将该结构体放入u_boot_cmd段中。
//声明段属性,将uboot命令数组都放到.u_boot_cmd段中(u_boot_cmd在链接脚本中申明)
#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

#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}
  1. U_BOOT_CMD宏展开举例
    U_BOOT_CMD通过##name避免重复定义,例如在4.1中U_BOOT_CMD宏展开结果为:
cmd_tbl_t __u_boot_cmd_mycmd\
__attribute__ ((unused,section (".u_boot_cmd")))= \
{mycmd, 3, 1, do_mycmd, "Short help of mycmd\n", "Long help of mycmd\n"}

4、如何添加自己定义的uboot命令

4.1 添加C文件

在common/文件夹下新建mycmd.c,然后添加以下代码内容(声明命令执行函数,定义命令并初始化)

#include <common.h>
#include <command.h>
int do_mycmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) //命令执行函数
{
	printf("This is my command\n");
	for(int i = 0; i < argc; i++)
	{
		printf("argv[%d] = %s\n", i, argv++);  //打印传参
	}
	return 0;
}

U_BOOT_CMD(   //定义命令并初始化
	mycmd,	3,		1,	do_mycmd,  //命令名字,最大接收参数,允许重复执行,指定命令执行函数
	"Short help of mycmd\n",       //帮助信息
	"Long help of mycmd\n"         //自动补全函数省略
);

4.2 修改Makefile

在/common/Makefile文件中添加下面一行代码,让新添加的C文件被编译进来。然后回到主目录make distclean,然后配置编译就能使用自己定义的uboot命令了。

COBJS-y += mycmd.o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值