U-Boot source code reading and analysis 001: U-Boot code style and coding standards

Most of the content of this article is translated from U-Boot official documentation
https://u-boot.readthedocs.io/en/latest/develop/codingstyle.html

Article directory

  • Preface
  • U-Boot coding style
  • U-Boot documentation writing
  • Use structures for I/O access
  • Header file inclusion order
  • file name
  • Comments on functions and structures
  • Device driver modelDM
  • other
  • test

Foreword

Before officially entering the reading and writing of U-Boot source code, you need to understand its coding specifications. U-Boot’s coding standards draw on many of Linux’s coding standards, which also makes it easier for Linux kernel developers to get started with U-Boot development and code management. This article provides a brief introduction to U-Boot coding style. I hope that after reading this article, you will be able to read and develop U-Boot source code more easily.

U-Boot coding style

All code contributed to the U-Boot open source project needs to conform to the following coding style, unless it is code ported from other projects with little modification.

  • All U-Boot code needs to comply with Linux kernel code specifications and Lindent script

    • The exception to the multiline comment format in network files only applies to Linux, not U-Boot, and only large sections of code copied unaltered from Linux can retain the comment format.
  • Use patman to submit patches (tools/patman/patman -H for complete instructions) and add tags to the submission to make it easier to review.

  • If you are not using patman, you need to run scripts/checkpatch.pl. For more information, please read the checkpatch section in the official documentation. Note that these tasks need to be completed before sending the email!

  • Source files originating from other projects (such as the MTD subsystem or the BusyBox project’s hush shell code) may be exempt from these rules after careful review. For these files, the original coding style can be preserved to facilitate subsequent migration to newer versions of these sources.

  • Please also pay attention to the following format rules:

    • Remove spaces at the end of each line

    • Complete indentation and vertical alignment using TAB characters instead of spaces

      • Exception: Python requires 4 spaces indentation
    • All source files need to be in Unix format at the end of the line rather than DOS or Windows format.

    • Do not have more than two consecutive blank lines in the source file.

    • Do not add blank lines at the end of source files

    • Use git config --global color.diff auto to visualize the above space problem

    • In Emacs, you can use =M-x whitespace-global-mode= to get visual feedback, and use =M-x whitespace-cleanup= to automatically adjust the whitespace format?

Submitted new code or patches that do not meet the above requirements will be rejected and required to be re-formatted.

U-Boot document writing

U-Boot adopts the Linux kernel kernel-doc comment style, which is the only exception to the multi-line comment coding style rules. Although not mandatory, writing documentation is highly recommended.

Use structures for I/O access

U-Boot usually uses structures in the C language to map registers in the I/O area, rather than defining base addresses and offsets separately. Here’s why:

  • Using a separately defined method to separate the register location (offset) from the register type means that the developer must ensure that each type access is correct, and the struct structure is checked by the compiler to ensure that the register data type is reliable;

  • Using a separately defined approach requires defining specific values for all offsets, which is more error-prone;

  • Taking the struct approach allows for better compile-time sanity checking of the values we write to registers.

Without using C language structure:

  • When the same driver needs to support different hardware versions, the registers appear at different offsets;

  • The driver only uses a small part of the registers, and it is not worth occupying a lot of storage space to define a structure to cover all registers;

  • The structure of the register is relatively complex and may have embedded substructures, making it difficult to determine a specific register offset;

  • This may need to be done in the kernel model if we allow more runtime detection of which drivers are suitable for the ones we run.

Please use the check_member() macro to verify that your structure is the expected size or that a specific member appears at the correct offset.

Header file inclusion order

You should follow the following header file inclusion order in U-Boot. The common.h header file should always come first, followed by other header files, then header files with directories, and finally local header files:

 #include <common.h>
   #include <bootstage.h>
   #include <dm.h>
   #include <others.h>
   #include <asm/...> /* Header file with directory */
   #include <arm/arch/...>
   #include <dm/device_compat/.h>
   #include <linux/...>
   #include "local.h" /* Local header file */

In that order, the contained content is sorted. It’s important to include common.h first because it provides basic functionality used by most files, such as CONFIG options. For files that need to be compiled for the host (such as tools), you need to use #ifndef use_HOSTCC to avoid including common.h, as it contains a lot of internal U-Boot stuff. See common/image.c for an example. If your file uses the driver dm model, include in the C file. Do not include dm.h in the header file. Try using forward declarations (e.g. struct udevice).

File name

When naming .c and .h files, try using underscores instead of hyphens, unless you want the files to stand out (for example, driver dm model uclass should be named xxx-uclass.h). Avoid using capital letters when naming, and keep names short.

Comments on functions and structures

Non-basic functions should have comments describing their functionality. If it is a function that references other files, please put the comment in the header file. If it’s a static function, put it in a C file.

If the function returns an error, you need to write a list of error return values. If you only return the return value of the called function, you do not need to write a comment. For details, see Kernel Function Document.

Device driver model DM

When declaring a new device, try to use struct udevice *dev or dev as the variable name:

 struct udevice *dev;

Use the variable ret as the return value:

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

Considering using log_ret() or log_msg_ret() to return a value, the device’s return parameter should be suffixed with p at the end:

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

There are many standard variable names that need to be used in the driver, such as:

  • Private data of dev_get_priv() function struct xxx_priv and priv
  • Platform parameter information of dev_get_platdata() function struct xxx_plat and plat
    For example:
 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);

Others

Some small things:

  • Add a blank line before the last return in a function, unless it is the only line in the function:
 struct udevice *pci_get_controller(struct udevice *dev)
   {<!-- -->
      while (device_is_on_pci_bus(dev))
         dev = dev->parent;
   
      return dev;
   }

Test

Please add test programs to the code, and modify or add test programs while changing the code. Run the test with the following command:

 make check
   make qcheck (skip some tests)

The Python test program is provided in the test/py/tests directory. For detailed information, see the documentation in test/py. If possible, try to write test programs in C language. For example, if run_command() and ut_check_console_line() could be called directly, rather than using Python to pipe the commands to U-Boot, then tests that check for commands would be faster (10-100x or more faster).

Test scripts in the root directory of the U-Boot code tree can be run on all supported CI systems (GitLab, Azure).