Integrating support for custom QEMU peripherals in Yocto
- June 9, 2024
In the previous posts a custom memory-mapped peripheral was introduced, together with the Linux device driver for it and a userspace application which can be used to interact with it. Besides the memory-mapped peripheral, a custom-i2c-peripheral was also described.
The functionality and integration of the custom components were demonstrated using a SD card image with Ubuntu root filesystem.
In this post the steps for integrating the custom components into the Yocto build environment will be described. The following items will be covered
- Creating a new Yocto MACHINE description
- Updating Linux kernel recipes
- Adding mmsens-app recipe
- Adding i2csens-app recipe
- Building and testing image
- Summary
NOTE: The Yocto version has been bumped from
kirkstone
toscarthgap
(the latest LTS version) compared to the original Yocto post.
Related to the change of Yocto version, the command to initialize the repo environment has changed to
repo init -u https://github.com/straxy/qemu-sunxi-yocto -m default.xml -b scarthgap
From the end-user standpoint, there are no other changes needed, and steps from the previous post can be used as is.
From the developer standpoint, the changes were made in several locations in order to use scarthgap
- on the new
scarthgap
branch inqemu-sunxi-yocto
- the manifest file has been updated to use
scarthgap
for all layers meta-arm
(new dependency ofmeta-sunxi
) has been added to the manifest- bblayers template has new
meta-arm
andmeta-arm-toolchain
layers
- the manifest file has been updated to use
- on the new
scarthgap
branch inmeta-mistra
- the
layer.conf
has been updated to be compatible withscarthgap
- the systemd network interface file has been changed to
end0
frometh0
- the
Creating a new Yocto MACHINE description
The MACHINE
description in Yocto is used to configure different aspects of BSP (Board Support Package) for a certain
board. Some of the items that can be configured are
- architecture compilation options,
- preferences for virtual packages provided by different modules (bootloader, kernel, etc.),
- device tree file to be used,
- default image formats that will be built,
- SD card layout,
- and many others.
The information about the MACHINE
that is selected can also influence the way other packages are configured or handled
during different steps of the build process, by using the
overrides mechanism.
For instance, in order to add extra file for package
mypackage
only forMACHINE=mymachine
, aSRC_URI
would be added tomypackage.bb
withSRC_URI:append:mymachine="extrafile"
.
Since some of the configuration should be shared between the different boards (MACHINE
s), then the MACHINEOVERRIDES
is used to define shared overrides. For example, all sunxi machines have sunxi
added to the MACHINEOVERRIDES
variable, so every override defined for sunxi
applies to all of those MACHINE
s.
The MACHINE
descriptions are stored in <machine_name>.conf
files in the conf/machine
directory of a layer, and
<machine_name>
is automatically added to the MACHINEOVERRIDES
variable.
In the original Yocto post the MACHINE=cubieboard
description was used. That
description comes from the meta-sunxi
layer and supports the original Cubieboard.
However, since we have modified the memory map of the Allwinner-A10 and added the new I2C component, it would be good to
create a new Yocto MACHINE
description, so Cubieboard MACHINE
can still be used with the original board and QEMU
implementation.
A new MACHINE
description in Yocto is created by adding a new cubieboard-ng.conf
file in the conf/machine
directory
require conf/machine/include/sun4i.inc
MACHINEOVERRIDES =. "cubieboard:"
KERNEL_DEVICETREE = "sun4i-a10-cubieboard.dtb"
UBOOT_MACHINE = "Cubieboard_config"
SUNXI_FEX_FILE = "sys_config/a10/cubieboard.fex"
The description is based on the cubieboard.conf
description for Cubieboard, but also updates the MACHINEOVERRIDES
so
every configuration that is used for the “original” Cubieboard also applies to this one. The items that will be specific
for our custom board will be overridden with cubieboard-ng
.
Updating Linux kernel recipes
The kernel configuration for the modified Cubieboard should include the description of the memory-mapped peripheral in the Device Tree description, as well as the device driver for that peripheral.
Device tree patch
The Device tree should be updated for the memory-mapped peripheral, so it will be detected at boot time.
Since the device tree exists in the Linux kernel repository, a patch has been made to add the changes to it for the memory-mapped peripheral. The changes are the ones described in the device driver post
The patch itself is placed inside the recipes-kernel/linux/linux-mainline
directory in the
meta-mistra
layer, and integrated into Yocto using the SRC_URI
variable in the linux-mainline_%.bbappend
recipe
SRC_URI:append:cubieboard-ng = "\
file://sun4i-a10.dtsi-Update-for-custom-memory-mapped-device.patch \
"
By using the cubieboard-ng
override, the patch will only be applied when the cubieboard-ng
machine is used.
Device driver recipe
In order to add the mmsens driver to Yocto a new recipe has to be created. The template (skeleton) exists at
poky/meta-skeleton/recipes-kernel/hello-mod
.
The recipe for the module should look like
SUMMARY = "Memory-mapped QEMU sensor driver"
DESCRIPTION = "${SUMMARY}"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"
inherit module
SRC_URI = "git://github.com/straxy/mmsens-drv.git;protocol=https;branch=main"
SRCREV = "${AUTOREV}"
S = "${WORKDIR}/git"
# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.
RPROVIDES:${PN} += "kernel-module-mmsens-drv"
This module also needs to be added to the image recipe so it will be included in the output root filesystem image
IMAGE_INSTALL:append:cubieboard-ng = "\
kernel-module-mmsens-drv \
"
Adding mmsens-app recipe
Similarly to the kernel module, the mmsens application can be added to the Yocto build system by adding a new recipe for
it. The recipe is located in recipes-apps/mmsens-app/mmsens-app_1.0.bb
.
The recipe inherits cmake
which indicates to bitbake that CMake is used as the build system. That way the bitbake will
use the standard CMake build steps to make the executable and copy it to the final root filesystem.
DESCRIPTION = "Memory-mapped sensor userspace application"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://LICENSE;md5=e983ef1fada174da9affb3753a18936f"
SRC_URI = "git://github.com/straxy/mmsens-app.git;protocol=https;branch=main"
SRCREV = "${AUTOREV}"
S = "${WORKDIR}/git"
inherit cmake
The only other thing needed is to add mmsens-app
to IMAGE_INSTALL
variable in the image recipe, and the application
will be built and included in the final output image.
Adding i2csens-app recipe
The steps for adding the i2csens application are the same as adding the mmsens application to the Yocto build system.
The recipe file is located in recipes-apps/i2csens-app/i2csens-app_1.0.bb
, and the biggest difference in the recipe
content is the git repository address.
The recipe also has to be added to the IMAGE_INSTALL
variable, so after adding all three described recipes, the final
change to the mistra-image.bb
should look like
IMAGE_INSTALL:append:cubieboard-ng = "\
kernel-module-mmsens-drv \
mmsens-app \
i2csens-app \
"
Building and testing image
Building a Yocto image with the new MACHINE
follows the same process as in the
original Yocto post. The only difference is that the new
MACHINE=cubieboard-ng
is used, so the build invocation should be
source setup-environment build_fb
DISTRO=mistra-framebuffer MACHINE=cubieboard-ng bitbake mistra-image
One big difference is that with the scarthgap
release the .sunxi-sdimg
is not built anymore, and instead of it the
.wic.gz
is build by default.
In order to make an sd.img
which can be used with the QEMU, the following steps should be taken (NOTE: assumed that
qemu-img
is in the $PATH
and that commands are executed from within the build_fb
directory)
qemu-img create sd.img 1G
gunzip -c ${PWD}/tmp/deploy/images/cubieboard-ng/mistra-image-cubieboard-ng.wic.gz | dd of=sd.img bs=1M iflag=fullblock oflag=direct conv=fsync
qemu-img resize sd.img 1G
After the SD card is prepared, the QEMU can be started using
# Run QEMU with SD card and networking
qemu-system-arm -M cubieboard -m 1G \
-drive file=sd.img,format=raw,if=sd \
-net nic -net tap,ifname=qemu-tap0,script=no \
-nographic
The first thing that can be noticed is that the kernel module is loaded automatically
[ 27.406924] mmsensdrv: loading out-of-tree module taints kernel.
[ 27.499134] Hello, world!
Both mmsens-app
and i2csens-app
are present in the $PATH
, so they can be invoked just by
mmsens-app /sys/class/mmsens/mmsens0
Hello World!
[ 129.749436] Interrupt received
Initial 0001
[ 130.749875] Interrupt received
0002
[ 131.748683] Interrupt received
0003
[ 132.748993] Interrupt received
0004
^C[ 133.749004] Interrupt received
0005
and
i2csens-app /dev/i2c-1
Measured 25
Measured 25
Measured 15
Measured 25
Measured 23.5
^CMeasured 24
Summary
In this blog post a new Yocto MACHINE
has been added for the customized Cubieboard. A new MACHINE
helps isolate the
changes related only to the customized Cubieboard, so the same environment can still be used to build images for the
original Cubieboard.
The recipes for the driver and userspace applications for the custom QEMU peripherals are also added, so they will be
included with every image built for the new MACHINE
.
One more thing that is covered is the steps that were taken to bump the Yocto version that is used to the latest LTS
version, scarthgap
. It was easy to do the migration since the meta-mistra
layer did not have many contents or
dependencies, but in real applications the changes might also be needed for the kernel and bootloader support, since
with the new Yocto version the U-Boot and Linux versions have been updated.