u-boot原理分析第三课-------u-boot命令的实现

原博客:https://blog.csdn.net/xiaokangdream/article/details/79537410

版权声明: https://blog.csdn.net/xiaokangdream/article/details/79537410

    如果我们在串口终端中使用过u-boot,就可以知道,u-boot是可以执行相关命令的。然而,在u-boot里面有很多命令,我们得给这些命令定义一个统一的接口,让所有的命令都封装成同样的格式,这样才便于去使用和管理。那么我们应该怎样去实现这个接口呢?用结构体去实现!

    对于每个命令,肯定有它的标识符,也就是它的名字,输入命令后,执行的过程肯定是调用某个函数,所以它也得有一个函数指针。这就是命令结构体的两大最重要的部分。我们在u-boot目录里面的common目录里,打开main.c这个文件,我们在里面会看到有不少的run_command的命令,我们直接跳转到run_command的定义处,也是在这个函数里面:


   
   
  1. int run_command (const char *cmd, int flag)
  2. {
  3. cmd_tbl_t *cmdtp;
  4. char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */
  5. char *token; /* start of token in cmdbuf */
  6. char *sep; /* end of token (separator) in cmdbuf */
  7. char finaltoken[CFG_CBSIZE];
  8. char *str = cmdbuf;
  9. char *argv[CFG_MAXARGS + 1]; /* NULL terminated */
  10. int argc, inquotes;
  11. int repeatable = 1;
  12. int rc = 0;
  13. #ifdef DEBUG_PARSER
  14. printf ( "[RUN_COMMAND] cmd[%p]=\"", cmd);
  15. puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */
  16. puts ( "\"\n");
  17. #endif
  18. clear_ctrlc(); /* forget any previous Control C */
  19. if (!cmd || !*cmd) {
  20. return -1; /* empty command */
  21. }
  22. if ( strlen(cmd) >= CFG_CBSIZE) {
  23. puts ( "## Command too long!\n");
  24. return -1;
  25. }
  26. strcpy (cmdbuf, cmd);
  27. /* Process separators and check for invalid
  28. * repeatable commands
  29. */
  30. #ifdef DEBUG_PARSER
  31. printf ( "[PROCESS_SEPARATORS] %s\n", cmd);
  32. #endif
  33. while (*str) {
  34. /*
  35. * Find separator, or string end
  36. * Allow simple escape of ';' by writing "\;"
  37. */
  38. for (inquotes = 0, sep = str; *sep; sep++) {
  39. if ((*sep== '\'') &&
  40. (*(sep -1) != '\\'))
  41. inquotes=!inquotes;
  42. if (!inquotes &&
  43. (*sep == ';') && /* separator */
  44. ( sep != str) && /* past string start */
  45. (*(sep -1) != '\\')) /* and NOT escaped */
  46. break;
  47. }
  48. /*
  49. * Limit the token to data between separators
  50. */
  51. token = str;
  52. if (*sep) {
  53. str = sep + 1; /* start of command for next pass */
  54. *sep = '\0';
  55. }
  56. else
  57. str = sep; /* no more commands for next pass */
  58. #ifdef DEBUG_PARSER
  59. printf ( "token: \"%s\"\n", token);
  60. #endif
  61. /* find macros in this token and replace them */
  62. process_macros (token, finaltoken);
  63. /* Extract arguments */
  64. if ((argc = parse_line (finaltoken, argv)) == 0) {
  65. rc = -1; /* no command at all */
  66. continue;
  67. }
  68. /* Look up command in command table */
  69. if ((cmdtp = find_cmd(argv[ 0])) == NULL) {
  70. printf ( "Unknown command '%s' - try 'help'\n", argv[ 0]);
  71. rc = -1; /* give up after bad command */
  72. continue;
  73. }
  74. /* found - check max args */
  75. if (argc > cmdtp->maxargs) {
  76. printf ( "Usage:\n%s\n", cmdtp->usage);
  77. rc = -1;
  78. continue;
  79. }
  80. #if (CONFIG_COMMANDS & CFG_CMD_BOOTD)
  81. /* avoid "bootd" recursion */
  82. if (cmdtp->cmd == do_bootd) {
  83. #ifdef DEBUG_PARSER
  84. printf ( "[%s]\n", finaltoken);
  85. #endif
  86. if (flag & CMD_FLAG_BOOTD) {
  87. puts ( "'bootd' recursion detected\n");
  88. rc = -1;
  89. continue;
  90. } else {
  91. flag |= CMD_FLAG_BOOTD;
  92. }
  93. }
  94. #endif /* CFG_CMD_BOOTD */
  95. /* OK - call function to do the command */
  96. if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
  97. rc = -1;
  98. }
  99. repeatable &= cmdtp->repeatable;
  100. /* Did the user stop this? */
  101. if (had_ctrlc ())
  102. return 0; /* if stopped then not repeatable */
  103. }
  104. return rc ? rc : repeatable;
  105. }

我们看到,在这个函数的最开始处,定义了一个cmd_tbl_t *cmdtp;这样的东西,它的数据类型叫作 cmd_tbl_t,那这是个什么类型呢?这里我先不讲,我们继续往下看,看到while这里:


   
   
  1. while (*str) {
  2. /*
  3. * Find separator, or string end
  4. * Allow simple escape of ';' by writing "\;"
  5. */
  6. for (inquotes = 0, sep = str; *sep; sep++) {
  7. if ((*sep== '\'') &&
  8. (*(sep -1) != '\\'))
  9. inquotes=!inquotes;
  10. if (!inquotes &&
  11. (*sep == ';') && /* separator */
  12. ( sep != str) && /* past string start */
  13. (*(sep -1) != '\\')) /* and NOT escaped */
  14. break;
  15. }

这是解析串口输入的命令,将命令一个一个提取出来,如果有两个命令的话,会有";"进行隔开。


我们继续往下看:


   
   
  1. /* Extract arguments */
  2. if ((argc = parse_line (finaltoken, argv)) == 0) {
  3. rc = -1; /* no command at all */
  4. continue;
  5. }
  6. /* Look up command in command table */
  7. if ((cmdtp = find_cmd(argv[ 0])) == NULL) {
  8. printf ( "Unknown command '%s' - try 'help'\n", argv[ 0]);
  9. rc = -1; /* give up after bad command */
  10. continue;
  11. }

这里的话是对参数进行解析,调用了parse_line,最后会返回一个值,保存在argc中,argc也就是参数的个数。我们继续看:


   
   
  1. /* Look up command in command table */
  2. if ((cmdtp = find_cmd(argv[ 0])) == NULL) {
  3. printf ( "Unknown command '%s' - try 'help'\n", argv[ 0]);
  4. rc = -1; /* give up after bad command */
  5. continue;
  6. }

这里我们看到这句英文:Look up command in command table:在命令表中查询命令,也就是这段代码的解释。我们可以看到这里它调用了find_cmd函数,函数的返回值由cmdtp接受,也就是我们之前所看到的cmd_tbl_t *cmdtp;

到这里,我们不难得知,cmd_tbl_t就是我们想找的定义命令的结构体(统一接口)。

接下来,我们跳到cmd_tbl_t的定义中(在include文件夹里的command.h里面):


   
   
  1. struct cmd_tbl_s {
  2. char *name; /* Command Name */
  3. int maxargs; /* maximum number of arguments */
  4. int repeatable; /* autorepeat allowed? */
  5. /* Implementation function */
  6. int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
  7. char *usage; /* Usage message (short) */
  8. #ifdef CFG_LONGHELP
  9. char *help; /* Help message (long) */
  10. #endif
  11. #ifdef CONFIG_AUTO_COMPLETE
  12. /* do auto completion on the arguments */
  13. int (*complete)( int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
  14. #endif
  15. };

我们可以看到,在这个结构体里面,有name变量,也就是command的名称;第二个参数时它最大的参数个数;第三个参数则是表示该命令是否可以重复执行,如果可以重复,则第二次直接按回车即可;第四个参数则是一个指针数组,也就是执行这个命令后应该调用的函数;第五个参数是它的短信息;第六个参数时它的长信息;第七个参数是自动补全,当你输入这个命令的一部分的时候,会自动地进行补全。

    以上就是这个命令结构体的所有内容了。我们回到之前的代码,就是在main.c的函数那里:


   
   
  1. if ((cmdtp = find_cmd(argv[ 0])) == NULL) {
  2. printf ( "Unknown command '%s' - try 'help'\n", argv[ 0]);
  3. rc = -1; /* give up after bad command */
  4. continue;
  5. }
我们看到,它是通过find_cmd这个函数来找到这个命令结构体的。其中这个函数传入了一个参数:argv[0],这个应该是我们命令的名字,所以它是应该是根据命令的名字,也就是标识符来找到相应的命令结构体的。为了验证我们的猜想,我们进入find_cmd这个函数看一下(common目录下的command.c函数里),

   
   
  1. cmd_tbl_t *find_cmd ( const char *cmd)
  2. {
  3. cmd_tbl_t *cmdtp;
  4. cmd_tbl_t *cmdtp_temp = &__u_boot_cmd_start; /*Init value */
  5. const char *p;
  6. int len;
  7. int n_found = 0;
  8. /*
  9. * Some commands allow length modifiers (like "cp.b");
  10. * compare command name only until first dot.
  11. */
  12. len = ((p = strchr(cmd, '.')) == NULL) ? strlen (cmd) : (p - cmd);
  13. for (cmdtp = &__u_boot_cmd_start;
  14. cmdtp != &__u_boot_cmd_end;
  15. cmdtp++) {
  16. if ( strncmp (cmd, cmdtp->name, len) == 0) {
  17. if (len == strlen (cmdtp->name))
  18. return cmdtp; /* full match */
  19. cmdtp_temp = cmdtp; /* abbreviated command ? */
  20. n_found++;
  21. }
  22. }
  23. if (n_found == 1) { /* exactly one match */
  24. return cmdtp_temp;
  25. }
  26. return NULL; /* not found or ambiguous command */
  27. }

我们看到,在函数的开始定义了两个命令结构体 ,接着是先获取命令的长度。接下来,是一条for循环:


   
   
  1. for (cmdtp = &__u_boot_cmd_start;
  2. cmdtp != &__u_boot_cmd_end;
  3. cmdtp++) {
  4. if ( strncmp (cmd, cmdtp->name, len) == 0) {
  5. if (len == strlen (cmdtp->name))
  6. return cmdtp; /* full match */
  7. cmdtp_temp = cmdtp; /* abbreviated command ? */
  8. n_found++;
  9. }
  10. }

我们看到,这里有两个参数,__u_boot_cmd_start、__u_boot_cmd_end,这两个参数我们并没有在这个文件里找到他们的定义。其实,这两个参数是在链接脚本里。

我们可以看到,在链接脚本里面,__u_boot_cmd_start和__u_boot_cmd_end的地址是在(Global Offset Table段之后)。中间有一句话,.u_boot_cmd : {*(.u_boot_cmd) } ,这句话意思就是把命令的结构体放在这个地址段里面。这两句for的意思是在放置命令结构体的地址段里面循环,从里面取出一个命令结构体,对比一下名字,是否匹配,匹配的话就返回(当然,后面还有一些其它的判断,比如是否是简写)。接下来,我们再看一下.u_boot_cmd的定义(在command.h)里:


   
   
  1. #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
  2. #ifdef CFG_LONGHELP
  3. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
  4. cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
  5. #else /* no long help info */
  6. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
  7. cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
  8. #endif /* CFG_LONGHELP */
  9. #endif /* __COMMAND_H */

我们先来看到第一句:

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

这句是一个宏定义,替换成的是一个属性指定,也就是关键字__attribute__,指明它可能是unused(可能是未使用的)的,而且被指定属性的对象是放在.u_boot_cmd这个段里面的。我们接着看下面的代码:


   
   
  1. #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
  2. cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
这段代码是定义了
 CFG_LONGHELP
   
   

这个是应该是一个带参宏,我们把一个例子替换进来,比如bootm命令(在该目录下即可找到):


   
   
  1. cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd")))= {
  2. “bootm”,
  3. CFG_MAXARGS,
  4. 1, do_bootm,
  5. "bootm - boot application image from memory\n",
  6. "[addr [arg ...]]\n - boot application image stored in memory\n"
  7. "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
  8. "\t'arg' can be the address of an initrd image\n"
  9. #ifdef CONFIG_OF_FLAT_TREE
  10. "\tWhen booting a Linux kernel which requires a flat device-tree\n"
  11. "\ta third argument is required which is the address of the of the\n"
  12. "\tdevice-tree blob. To boot that kernel without an initrd image,\n"
  13. "\tuse a '-' for the second argument. If you do not pass a third\n"
  14. "\ta bd_info struct will be passed instead\n"
  15. }

我们可以看到,其实这就是一个命令结构体的定义,名字叫做:__u_boot_cmd_bootm,并对它进行了所有成员的初始化,且这个结构体是放在.u_boot_cmd这个段里面的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值