总览
本文基于 u-boot-1.1.6, 使用jz2440开发板. 若要使用最新的u-boot版本见: u-boot官网 u-boot下载
hello world
- 新建文件
./common/cmd_hello.c
, 按照其他 cmd_XXX 文件内容, 依样画葫芦即可.
- 打开文件
./common/Makefile
, 在 COBJS = ...
一行, 加入 cmd_hello.o
即可.
- Linux主机下,
make
指令重新编译 u-boot, 并生成 u-boot.bin
文件
- 烧录并执行, 测试新指令即可, 譬如在uboot命令行下, 输入
hello DRA&PHO
.
#include <common.h> #include <command.h>
int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { int i;
printf ("hello world!, %d\n", argc); for (i=0; i<argc; i++) { printf("argv[%d]: %s\n", i, argv[i]); } }
U_BOOT_CMD( hello, CFG_MAXARGS, 1, do_hello, "hello - print hello world and arguments\n", "[arg [arg ...]]\n - print hello and arguments\n" "\ttest purpose, learn to write uboot command\n" );
|
源码分析
我们用倒推法, 从关键的 U_BOOT_CMD
宏定义开始分析.
找到其宏定义所在的文件 ./include/command.h
39 struct cmd_tbl_s { 40 char *name; 41 int maxargs; 42 int repeatable; 44 int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); 45 char *usage; 47 char *help; 53 }; 55 typedef struct cmd_tbl_s cmd_tbl_t;
57 extern cmd_tbl_t __u_boot_cmd_start; 58 extern cmd_tbl_t __u_boot_cmd_end;
93 #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
97 #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ 98 cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
cmd_tbl_t __u_boot_cmd_hello __attribute__ ((unused,section (".u_boot_cmd"))) = { hello, CFG_MAXARGS, 1, do_hello, "hello - print hello world and arguments\n", "..." }
|
清楚了指令的结构体存放方式后, 需要考虑uboot是如何识别输入的指令, 并执行其指定的函数 do_XXX
这个文件位于 ./common/command.c
346 cmd_tbl_t *find_cmd (const char *cmd) { 360 for (cmdtp = &__u_boot_cmd_start; 361 cmdtp != &__u_boot_cmd_end; 362 cmdtp++) { 363 if (strncmp (cmd, cmdtp->name, len) == 0) { 364 if (len == strlen (cmdtp->name)) 365 return cmdtp; 367 cmdtp_temp = cmdtp; 368 n_found++; 369 } 370 } 371 if (n_found == 1) { 372 return cmdtp_temp; 373 } 376 }
|
此时, 通过查找 find_cmd 函数, 发现被多次调用, 其中一条路径是指令自动完成, 此处忽略.
可以发现它也被 ./common/main.c
的 run_command
调用了
301 void main_loop (void) { 488 for (;;) { 497 len = readline (CFG_PROMPT); 501 strcpy (lastcommand, console_buffer); 521 rc = run_command (lastcommand, flag); 527 } 529 }
1280 int run_command (const char *cmd, int flag) { 1361 if ((cmdtp = find_cmd(argv[0])) == NULL) {...} 1391 if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {...} 1403 }
|
至此, 指令部分的实现分析完成. 再倒过来总结一下:
- main_loop 中, 终端等待用户输入指令, 譬如 “hello”
- run_command 先查找指令是否存在, 调用find_cmd
- find_cmd 会在 “.u_boot_cmd” 段内查找指令是否存在
- 因此, 增减指令很简单, 只有两个关键点:
- 使用
U_BOOT_CMD
宏定义, 定义好指令结构, 编译器会自动存放进”.u_boot_cmd” 段
- 实现指令函数. 习惯上将其命名为 “do_XXX”, 如 “do_hello”.
- 指令存在的话, 执行指令函数, 即通过 (cmdtp->cmd) (cmdtp, flag, argc, argv) 的形式调用 do_hello
- 执行完成后, 继续死循环等待下一条输入
原创于 DRA&PHO