How to add a kernel configuration setting in the Yocto Project

Introduction

In many cases you need to enable a particular feature in the Linux kernel which is not active by default, such as a specialized device driver. A very common scenario are peripherals on an interface such as SPI or I2C that are present on custom baseboards.

This tutorial provides a walkthrough to add a configuration item to the kernel config and package the result up properly in a Yocto build.

It uses I2C-bus connected AT24-style EEPROMs as an example.

Version notes

This tutorial uses scarthgap as the primary target, which is the current LTS release by the Yocto Project. You can find more infomation on releases here. Supported releases for following the tutorial are:

Yocto Project Tutorial applies Maintenance
walnascar (5.0) :test_works: :test_works: development
styhead (5.0) :test_works: :test_works: current stable
scarthgap (5.0) :test_works: :test_works: LTS
nanbield (4.3) :test_works: :test_works: EOL
mickledore (4.2) :test_works: :test_fails: EOL
langdale (4.1) :test_works: :test_fails: EOL
kirkstone (4.0) :test_works: :test_works: LTS

Please note: a failure in the “tutorial applies” column indicates that the instructions do not work without modification. Depending on the combination of release and host Linux distribution, installing other python versions or dependencies might provide a functional state.

Step 0: prepare a meta layer

We will need a meta layer to place our resulting recipe and files in. The rest of this tutorial assumes such a layer is prepared and added to the build already. We will use the name meta-dawg, and presume that the layer is present at the directory one level up from the build directory.

Example layout, top level:

.
├── build
├── meta-dawg
└── poky

Also, we expect the shell to be initialized for the build, and the current working directory to be build.

If this does not reflect your setup, please change the paths and name accordingly.

Step 1: configure the Linux kernel

We need the kernel configuration baseline to add our customizations on top. There’s no need to do a full kernel build which might be time consuming, just invoking the kernel_configme task is enough:

bitbake -c kernel_configme virtual/kernel

With this configuration as it is provided by the current build setup, we start the menuconfig tool so we can interactively modify the kernel config as desired.

bitbake -c menuconfig virtual/kernel

Now we need to find the driver we want to add. In our case, it is the driver for “24C”-style EEPROMs. How do we find out which one it is? Kconfig has a search function.
Type the / character. Then enter the (sub)string “at24”. Bingo! Here it is.

The important part now is to remember the location. Suprisingly - and also confusingly - it is at Device Drivers -> Misc devices -> EEPROM -> I2C EEPROMs / RAMs / ROMs ....

Close the search results view by pressing Enter on the auto-selected Exit button. Then head to the location you just memorized by using the up/down arrows and Enter keys. Once you have arrived at the destination item, you can use the spacebar to trigger the Select function. By pressing it multiple times, it will cycle through these three variants for the selected functionality.

  • < >: disabled and will not be compiled.
  • <M>: enabled and compiled as a loadable module.
  • <*>: enabled and compiled as a built-in module.

Unless the part you are working on is essential for booting the machine - which in our case, it isn’t - going for M is a sensible choice. Having as much as possible factored out into runtime loadable modules means that the kernel gets smaller and has less initializations to go through at boot time, which results in a shortened start up time.

Once you are finished with adjusting, save the kernel configuration by moving with the arrow keys to the Save item, select and confirm the .config destination with ´Enter. Then close the configuration tool using the Exit` item.

Step 2: extract the configuration fragment

Yocto has a very powerful mechanism to extend a more generic kernel configuration with smaller additions, which it calls “fragments”. This allows for easier maintenance as it is not required to always re-configure the kernel and manage the resulting full configuration upon a new kernel release, but only keep the few modified additional lines under version control.

Extract the fragment which corresponds to our newly enabled driver:

bitbake -c diffconfig virtual/kernel

This command creates a configuration fragment based on our modifications, and tells us where to find it:

# example output
Config fragment has been dumped into:
 /home/mender/builds/bbb/build/tmp/work/beaglebone_yocto-poky-linux-gnueabi/linux-yocto/6.6.21+git/fragment.cfg

We can verify if the fragment reflects our intended change by looking at the file, like

cat /home/mender/builds/bbb/build/tmp/work/beaglebone_yocto-poky-linux-gnueabi/linux-yocto/6.6.21+git/fragment.cfg

and yes, it is exactly our wanted EEPROM driver:

CONFIG_EEPROM_AT24=m

Step 3: create a recipe

The canonical organization for such recipes to extend the kernel configureion in Yocto is $LAYER/recipes-kernel/linux/$RECIPE.bbapend. We want to append to the kernel recipe which is already active, so lets look again at the path at which the configuration fragment was placed. It refers to version 6.6.21+git, and one directory up it tells us the recipes name: linux-yocto.

So lets create the directory structure:

mkdir -p ../meta-dawg/recipes-kernel/linux

The best practice to place additional files is a directory next to the recipe in question, named like the BPN (short for Base Package Name) of the recipe. Create that one too:

mkdir -p ../meta-dawg/recipes-kernel/linux/linux-yocto

So lets move the configuration fragment into place and choose a somewhat meaningful name:

mv tmp/work/beaglebone_yocto-poky-linux-gnueabi/linux-yocto/6.6.21+git/fragment.cfg ../meta-dawg/recipes-kernel/linux/linux-yocto/at24.cfg

The last thing we need to glue everything together finally is the recipe itself. As we’re sticking to the canonical best practices, it is surprisingly short and simple.

cat << 'EOF' > ../meta-dawg/recipes-kernel/linux/linux-yocto_%.bbappend
FILESEXTRAPATHS:prepend := "${THISDIR}/${BPN}:"
SRC_URI += "file://at24.cfg"
EOF

Note that we named the recipe linux-yocto_%.bbappendwhich uses the % version wildcard. This means it will apply to all versions of the recipe being built.

In a production setting you might want to tie this to a specific version or COMPATIBLE_MACHINE.

Thats it, we’re done! Lets do some checks:

bitbake-layers show-appends | grep linux-yocto

includes a line with meta-dawg/recipes-kernel/linux/linux-yocto_%.bbappend, so our .bbappend is functional.

bitbake -c kernel_configcheck -f virtual/kernel

runs the kernel configuration as during a Yocto build, and checks if all expected CONFIG_* items are set in the resulting config file

And finally:

grep AT24 $(bitbake-getvar --value -r virtual/kernel B)/.config

This uses the bitbake-getvar tool to get the kernels build directory, and as the default filename is .config, we can just grep it for our desired item.

Step 4 (only if required): add kernel module to image

If you are using a very minimal image recipe, then kernel modules might not be included by default. A general catch-all strategy is to add

IMAGE_INSTALL:append = " kernel-modules "

to your image, or as a quick workaround for testing, you can also drop it into local.conf.

In cases where you are more concerned with storage, you can add specific kernel modules only to your build. To get a list of the available ones, build the kernel first and then inspect the package list:

bitbake virtual/kernel
oe-pkgdata-util list-pkgs | grep kernel-module

This prints the list of all kernel modules which are available as separate packages. It is dependent on the actual kernel configuration, as only modules that are built are packaged accordingly.

If you take a closer look, you can spot our module:

kernel-module-at24-6.6.21-yocto-standard

So instead installing all modules, we can only pull in the one we just configured:

IMAGE_INSTALL:append = " kernel-module-at24 "

Conclusion

That’s it! Build and run your new kernel and enjoy your customization!

We’ve chosen a relatively simple and contained driver here which does not provike conflicts, and in most embedded situation will not have additional dependencies: the only thing it really needs is support for the I2C bus, and that is usually enabled.

More in depth documentation can be found in the Yocto Project Linux Kernel Development Manual


If this tutorial was useful to you, please press like, or leave a thank you note to the contributor who put valuable time into this and made it available to you. It will be much appreciated!