U-Boot源码阅读与分析 001:U-Boot代码风格和编码规范

U-Boot编码风格指南:结构体、注释与测试实践
本文介绍了U-Boot的编码风格规范,包括遵循Linux内核编码、使用结构体处理I/O访问、遵循特定的头文件包含顺序和文件命名规则,以及推荐的文档编写和测试方法。

本文大部分内容翻译自U-Boot官方文档
https://u-boot.readthedocs.io/en/latest/develop/codingstyle.html


前言

在正式进入U-Boot源码的阅读和编写之前,需要了解其编码规范。U-Boot的编码规范借鉴了Linux的很多编码规范,这也使得Linux内核开发者可以更容易上手U-Boot的开发,也更方便进行代码管理。本文对U-Boot编码风格进行简要的介绍,希望在阅读完本文之后你将会更加轻松地进行U-Boot的源码阅读和开发。


U-Boot 编码风格

所有为U-Boot开源项目贡献的代码需要符合如下的编码风格,除非是从其他项目中移植的几乎没有进行修改的代码。

  • 所有 U-Boot 代码需要符合 Linux 内核代码规范以及 Lindent script

    • 网络文件中多行注释格式的例外仅适用于Linux,不适用于U-Boot,只有从Linux中未经更改地复制的大段代码才能保留该注释格式。
  • 使用patman提交补丁(tools/patman/patman -H 获取完整说明),并在提交中添加标记,方便对其进行检查。

  • 如果不使用patman,则需要运行 scripts/checkpatch.pl,更多信息请阅读官方文档中的checkpatch部分。注意这些工作需要在发送邮件之前完成!

  • 源自其他项目的源文件(例如MTD子系统或BusyBox项目的hush shell代码)在经过仔细审查后可以不受这些规则的约束。对于这些文件,可以保留原始的编码风格,以便于后续迁移到这些源的较新版本。

  • 请同时关注如下格式规则:

    • 删除每一行末尾的空格

    • 使用 TAB 字符而不是空格完成缩进和垂直对齐

      • 例外:Python 需要使用 4 空格缩进
    • 所有源文件在行末需要采用 Unix 格式而非 DOS 或 Windows 格式。

    • 在源文件中的空行不要连续多于两行

    • 在源文件末尾不要添加空行

    • 使用 git config --global color.diff auto 可以将上述空格问题可视化

    • 在Emacs中可以使用 =M-x whitespace-global-mode= 获取视觉反馈,使用 =M-x whitespace-cleanup= 可以自动调整空白格式 ™

已提交的不符合上述要求的新代码或补丁会被拒绝,并要求重新修改格式。

U-Boot 文档编写

U-Boot 采用 Linux内核kernel-doc 注释风格,这也是多行注释编码风格规则唯一的例外。尽管并非强制,编写文档是强烈推荐的。

使用结构体用于I/O访问

U-Boot通常使用C语言中的结构体来映射I/O区域中的寄存器,而不是采用单独定义基地址和偏移量。原因如下:

  • 采用单独定义的方法将寄存器位置(偏移量)与寄存器类型分离,意味着开发人员必须确保每次类型访问都是正确的,而struct结构体会是由编译器检查,确保寄存器数据类型可靠;

  • 采用单独定义的方法需要定义所有偏移量的具体值,这更容易出错;

  • 采用结构体的方法允许对我们写入寄存器的值进行更好的编译时健全性检查。

不使用C语言结构体的情况:

  • 同一驱动程序需要支持不同硬件版本时,寄存器以不同的偏移量出现;

  • 驱动程序只使用寄存器的一小部分,不值得占用大量存储空间定义一个结构体来覆盖所有寄存器;

  • 寄存器的结构体较为复杂,可能有嵌入的子结构,难以确定特定的寄存器偏移量;

  • 如果我们允许更多的运行时检测哪些驱动程序适合我们运行的驱动程序,那么这可能需要在内核模型中完成。

请使用check_member()宏验证您的结构体是否为预期大小,或者特定的成员是否出现在正确的偏移处。

头文件包含顺序

您应该在U-Boot中遵循如下的头文件包含顺序。common.h头文件应该始终放在首位,然后依次是其他头文件,然后是带有目录的头文件,最后是本地头文件:

   #include <common.h>
   #include <bootstage.h>
   #include <dm.h>
   #include <others.h>
   #include <asm/...>    /* 带有目录的头文件 */
   #include <arm/arch/...>
   #include <dm/device_compat/.h>
   #include <linux/...>
   #include "local.h"    /* 本地头文件 */

在该顺序中,对包含的内容进行排序。首先包含common.h很重要,因为它提供了大多数文件使用的基本功能,例如CONFIG选项。对于需要为主机编译的文件(例如工具),您需要使用#ifndef use_HOSTCC来避免包含common.h,因为它包含了许多内部U-Boot内容。有关示例,请参见common/image.c。如果您的文件使用驱动程序dm模型,请在C文件中包含<dm.h>。不要在头文件中包含dm.h。请尝试使用前向声明(例如struct udevice)。

文件名

在命名.c和.h文件时,请尝试使用下划线而不是连字符,除非您希望文件突出(例如,驱动程序dm模型uclass应命名为xxx-uclass.h)。命名时应避免使用大写字母,并保持较短的名称。

函数和结构体的注释

非基本的函数应该编写注释来描述它的功能。如果是引用其他文件的函数,请将注释放在头文件中。如果是一个静态函数,请将它放在C文件中。

如果函数返回错误,需要写明错误返回值的列表,若仅仅返回调用的函数的返回值则可以不写注释,详见 内核函数文档

设备驱动模型DM

当声明一个新设备时,尽量使用struct udevice *devdev 作为变量名:

   struct udevice *dev;

使用变量ret 作为返回值:

   struct udevice *dev;
   int ret;
   
   ret = uclass_first_device_err(UCLASS_ACPI_PMC, &dev);
   if (ret)
       return log_msg_ret("pmc", dev);

考虑到使用log_ret()或者log_msg_ret() 来返回一个值,设备的返回参数应在末尾加上后缀 p

   int dm_pci_find_class(uint find_class, int index, struct udevice **devp)
   {
   ...
          *devp = dev;
   ...   
          return 0;
   }

在驱动中还有很多需要使用的标准变量名称,如:

  • dev_get_priv()函数的私有数据 struct xxx_privpriv
  • dev_get_platdata()函数的平台参数信息 struct xxx_platplat
    例如:
   struct simple_bus_plat {
      u32 base;
      u32 size;
      u32 target;
   };
   
   /* Davinci MMC board definitions */
   struct davinci_mmc_priv {
   struct davinci_mmc_regs *reg_base; /* Register base address */
   uint input_clk; /* Input clock to MMC controller */
   struct gpio_desc cd_gpio; /* Card Detect GPIO */
   struct gpio_desc wp_gpio; /* Write Protect GPIO */
 };
   
 struct rcar_gpio_priv *priv = dev_get_priv(dev);

 struct pl01x_serial_platdata *plat = dev_get_platdata(dev);

其他

一些小的事项:

  • 在函数的最后一个 return 之前增加一个空行,除非它是函数的唯一一行:
   struct udevice *pci_get_controller(struct udevice *dev)
   {
      while (device_is_on_pci_bus(dev))
         dev = dev->parent;
   
      return dev;
   }

测试

请在代码中增加测试程序,并在更改代码的同时修改或增加测试程序。用如下命令运行测试:

   make check
   make qcheck   (跳过一些测试)

test/py/tests目录中提供了Python测试程序,具体信息详见test/py 中的文档。如果可以的话,尽量用C语言编写测试程序。例如,如果可以直接调用run_command()和ut_check_console_line(),而不是使用Python通过管道将命令发送到U-Boot,那么检查命令的测试将更快(10-100倍或更快)。

在U-Boot代码树的根目录中的测试脚本在所有支持的CI系统(GitLab、Azure)上均可运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值