给uboot添加命令和命令菜单

一、uboot命令添加过程

$U-boot命令的解析

在uboot main_loop中执行一个命令,将调用函数 run_command()执行命令

int run_command (const char *cmd, int flag)
{
	cmd_tbl_t *cmdtp;
	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
	char finaltoken[CONFIG_SYS_CBSIZE];
	char *str = cmdbuf;
	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 */

	if (!cmd || !*cmd) {
		return -1;	/* empty command */
	}

	if (strlen(cmd) >= CONFIG_SYS_CBSIZE) {
		puts ("## Command too long!\n");
		return -1;
	}

	strcpy (cmdbuf, cmd);

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

#ifdef DEBUG_PARSER
	printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
	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 == ';') &&	/* ;是两个命令的分隔		*/
			    ( 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 */
#ifdef DEBUG_PARSER
		printf ("token: \"%s\"\n", token);
#endif

		/* find macros in this token and replace them */
		process_macros (token, finaltoken); //处理环境宏

		/* 解析命令的参数 */
		if ((argc = parse_line (finaltoken, argv)) == 0) {
			rc = -1;	/* no command at all */
			continue;
		}

		/* Look up command in command table */
		/*argv[0]是命令的名字*/
		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) {
			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 */
		/*调用命令对应的函数*/
		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;
}

这段代码的作用是匹配是否存在我们输入的命令(argv[0])

/* Look up command in command table */
		/*argv[0]是命令的名字*/
		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()函数定义如下:

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);
}


cmd_tbl_t *find_cmd_tbl (const char *cmd, cmd_tbl_t *table, int table_len)
{
	cmd_tbl_t *cmdtp;
	cmd_tbl_t *cmdtp_temp = table;	/*Init value */
	const char *p;
	int len;
	int n_found = 0;

	if (!cmd)
		return NULL;
	/*
	 * 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 = table;
	     cmdtp != table + table_len;
	     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 */
}
__u_boot_cmd_start与__u_boot_cmd_end 位于u-boot.lds中:

	 __u_boot_cmd_start = .;
	 .u_boot_cmd : { *(.u_boot_cmd) }
	 __u_boot_cmd_end = .;

find_cmd()做的其实就是遍历u-boot.lds,从 .u_boot_cmd段中找出是否存在argv[0]这个命令.

而我们的命名是如何存入.u_boot_cmd这个段的呢?其实他是通过下面的过程.

U-Boot的每一个命令都是通过U_BOOT_CMD宏定义的。这个宏在include/command.h头文件中定义,每一个命令定义一个cmd_tbl_t结构体。

/*命令宏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.cmd_tbl_t 命令相关的结构体,定义如下:

	typedef struct cmd_tbl_s	cmd_tbl_t;

	struct cmd_tbl_s {
		char		*name;		/* Command Name			*/
		int		maxargs;	/* maximum number of arguments	*/
		int		repeatable;	/* autorepeat allowed?		*/
						/* Implementation function	*/
		int		(*cmd)(struct cmd_tbl_s *, int, int, char * const []);
		char		*usage;		/* Usage message	(short)	*/
	#ifdef	CONFIG_SYS_LONGHELP
		char		*help;		/* Help  message	(long)	*/
	#endif
	#ifdef CONFIG_AUTO_COMPLETE
		/* do auto completion on the arguments */
		int		(*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
	#endif
	};

2.##name = #name :#是字符串操作符,_##为字符串连接符

3.Struct_Section 这是定义一个结构的属性,将其放在.u_boot_cmd这个段当中(common.h),相当于.data/.bss这些段

	#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd"))) 

4.cmd_tbl_t 这个结构拥有一个属性,即Struct_section.这个的作用是所有的命令将被集中到.u_boot_cmd这个段中.[位于uboot.lds]

$实践-添加一个U-boot命令

在common目录下touch cmd_hello.c, vim cmd_hello.c

#include <common.h>
#include <command.h>

int do_hello(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
{
	int i;
	printf("hello world!\n");
	for(i = 0;i<argc;i++)
	{
		printf("the argv[%d] is :%s\n",i, argv[i]);
	}
	return 0;

}

U_BOOT_CMD(
	hello,	CONFIG_SYS_MAXARGS,	1,	do_hello,
	"hello,a command test",
	"print detailed usage of 'hello'"
);

打开common目录下的Makefile,vim Makefile,加下面句子:

	 COBJS-y += image.o
	 COBJS-y += s_record.o
	 COBJS-$(CONFIG_SERIAL_MULTI) += serial.o
	 COBJS-y += xyzModem.o
    +COBJS-y += cmd_hello.o

cd 到uboot根目录重新编译就ok了!

二、uboot命令添加menu

uboot menu 可以当成一个普通命令文件.而在其中调用了许多命令集.

#include <common.h>
#include <command.h>
#include <nand.h>

#ifdef CONFIG_CMD_MENU



extern char console_buffer[];
extern int readline (const char *const prompt);



#define USE_USB_DOWN		1


void main_menu_usage(char menu_type)
{

	printf("\r\n#####	 Boot for Smart210 Main Menu	#####\r\n");

	if( menu_type == USE_USB_DOWN)
	{
		printf("#####    Smart210 USB download mode     #####\r\n\n");
	}
	printf("[1] Download U-boot to Nand Flash\r\n");
	printf("[2] Download Linux Kernel (uImage.bin) to Nand Flash\r\n");
	printf("[3] Download YAFFS image (root.bin) to Nand Flash\r\n");
	printf("[4] Download Program to SDRAM and Run it\r\n");
	printf("[5] Boot the system\r\n");
	printf("[6] Format the Nand Flash\r\n");
	printf("[q] Return main Menu \r\n");
	printf("Enter your selection: ");
}






void menu_shell(void)
{
	char keyselect;
	char cmd_buf[200];

	while (1)
	{
		main_menu_usage(USE_USB_DOWN);
		keyselect = getc();
		printf("%c\n", keyselect);
		switch (keyselect)
		{
			case '1':
			{
				printf("\n");
				printf("Download the uboot into Nand flash by DNW\n");
				strcpy(cmd_buf, "dnw 0x20000000; nand erase 0x0 0x80000; nand write 0x20000000 0x0 0x80000");

				run_command(cmd_buf, 0);
				break;
			}
			
			

			case '2':
			{
				printf("\n");
				printf("Download the kernel into Nand flash by DNW\n");
				strcpy(cmd_buf, "dnw 0x20000000; nand erase 0x300000 0x500000; nand write 0x20000000 0x300000 0x500000");

				run_command(cmd_buf, 0);
				break;
			}


			case '3':
			{
//#ifdef CONFIG_MTD_DEVICE
//				strcpy(cmd_buf, "dnw 0x20000000; nand erase root; nand write.yaffs 0x20000000 root $(filesize)");
//#else
				strcpy(cmd_buf, "dnw 0x20000000; nand erase 0xe00000 0xF8D0000; nand write.yaffs 0x20000000 0xe00000 $(filesize)");
//#endif /* CONFIG_MTD_DEVICE */
				run_command(cmd_buf, 0);
				break;
			}

			case '4':
			{
				char addr_buff[12];
				printf("Enter download address:(eg: 0x20000000)\n");
				readline(NULL);
				strcpy(addr_buff,console_buffer);
				sprintf(cmd_buf, "dnw %s;go %s", addr_buff, addr_buff);
				run_command(cmd_buf, 0);
				break;
			}

			case '5':
			{

				printf("Start Linux ...\n");

				printf("Boot the linux (YAFFS2)\n");
				strcpy(cmd_buf, "setenv bootargs noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0,115200 rootfstype=yaffs2 mem=512M");
				run_command(cmd_buf, 0);

				strcpy(cmd_buf, "setenv bootcmd 'nand read 0x21000000 0x300000 0x500000;bootm 0x21000000';save");

				run_command(cmd_buf, 0);
				break;
			}

			case '6':
			{
				strcpy(cmd_buf, "nand erase.chip ");
				run_command(cmd_buf, 0);
				break;
			}

			
#ifdef CONFIG_SMART210
			case 'Q':
			case 'q':
			{
				return;	
				break;
			}
#endif
		}
				
	}
}

int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	menu_shell();
	return 0;
}

U_BOOT_CMD(
	menu,	3,	0,	do_menu,
	"display a menu, to select the items to do something",
	"\n"
	"\tdisplay a menu, to select the items to do something"
);

#endif

这里用的比较多的是下面句子:
strcpy(cmd_buf, “dnw 0x20000000; nand erase 0x0 0x80000; nand write 0x20000000 0x0 0x80000”); run_command(cmd_buf, 0);

这个句子很简单,调用strcpy把”dnw 0x20000000; nand erase 0x0 0x80000; nand write 0x20000000 0x0 0x80000”的内容复制到cmd_buf中,然后调用run_command();实践上就执行了dnw 0x20000000; nand erase 0x0 0x80000; nand write 0x20000000 0x0 0x80000 这些命令,这与uboot的命令行模式相同的.