2019 at Bootlin, a year in review

First of all, the entire team at Bootlin wishes you a Happy New Year, and best wishes for 2020 in your personal and professional life. The beginning of the new year is a good time to look back and see the achievements of the past year, which is why we review the 2019 year in terms of Bootlin news and activity.

Linux kernel contributions

In 2019, we made contributions to Linux 5.0, Linux 5.1, Linux 5.2, Linux 5.3 and Linux 5.4. We contributed a total of 1078 patches to these releases.

Some of the highlights were:

  • The brand new subsystem to support the MIPI I3C bus that we developed from scratch was merged in Linux 5.0. This paved the way to support both I3C controllers and I3C devices in Linux. See our blog post.
  • The Marvell Ethernet controller driver was extended to support packet classification offloading in hardware.
  • A brand new driver in the IIO subsystem for the TI ADS8344 ADC chip.
  • Support for HW-accelerated H264 video decoding was added to the Allwinner VPU driver, as part of our crowd-funded project, see our recent blog post.
  • Numerous improvements in the support of Allwinner, Microchip and Marvell platforms, see our blog posts for each kernel release for more details.

Other contributions

  • We contributed 52 patches to the U-Boot project in 2019: a new network driver the Microsemi Ocelot platform, improvements to the MTD subsystem, fixes to the NXP LPC3250 and Rockchip PX30 platform support.
  • We contributed 314 patches to the Buildroot project, our most significant contribution is support for top-level parallel build, which will land in the upcoming Buildroot 2020.02 release. In addition, Bootlin engineer Thomas Petazzoni remains an active Buildroot co-maintainer: in 2019 he reviewed and merged 2924 patches from other contributors, out of the total of 5503 patches merged throughout the year.
  • We improved our Web-based Elixir code browser. Elixir now indexes the sources of 16 projects, allows to browse various types of include files, has support for project specific HTML post-processing filters, has a new REST API (thanks to Carmeli Tamir), and has many fixed bugs. All this activity corresponds to a round number of commits: 128.

Engineering projects

Of course, most of the contributions described above are driven by the engineering projects we have with our customers worldwide. Here are some of the significant engineering projects we worked on in 2019:

  • Implemented support for MACsec hardware offloading, and support for this functionality for the Microchip VSC8584 Ethernet PHY. We presented this work in detail in a blog post, and submitted 4 iterations in 2019 and hope to see this merged in early 2020.
  • Improved the Intel GMA500 display driver to support page flipping, so that one of our customers can use the Weston Wayland compositor on hardware platforms that use this display controller.
  • Started a project to support the Microchip VSC8572 Ethernet PHY in Linux, used as a pass-through to SFP cages. As part of this project, we already contributed patches to convert the cpsw network driver to phylink, and we will contribute VSC8572 patches once the project has made enough progress.
  • Migrated a complete Buildroot-based BSP for a Danish customer in the healthcare industry: migration to a newer Linux kernel version and a newer Buildroot version for a Microchip AT91SAM9G45 platform and a TI AM335x platform.
  • Migrated a complete Yocto-based BSP for a Belgian customer to a newer mainline version of the Linux kernel for an i.MX6 platform, implemented secure boot and optimized the boot time.
  • For a major US customer, implemented a complete Linux BSP for a custom Xilinx Zynq 7000 platform: upstream U-Boot, upstream Linux and Yocto-based build system. As part of this project, we did a number of kernel contributions and we still have a major kernel contribution pending: a complete DRM driver for the logiCVC display controller.
  • For a German customer in the healthcare industry, continued to support additional hardware features of an i.MX6 platform, and built a complete new BSP for an SAMA5D3-based hardware platform.
  • For a Canadian customer, completely upgraded the Linux BSP for a NXP LPC3250 platform: update to U-Boot upstream, to Linux upstream, and migration to Buildroot as a build system.
  • For an Italian customer, started a brand new BSP for a Rockchip PX30 based platform, which will require a number of improvements and additions to the Linux kernel support for this platform, which we will work on in 2020.
  • For a Belgian customer, migrated a Linux BSP for an OMAP44xx platform with complex audio interfaces to a recent upstream Linux kernel version.
  • For a French customer, implemented and delivered a complete Yocto-based Linux BSP for an i.MX6 platform.
  • For the French company Overkiz, updated, cleaned-up and upstreamed to the Linux kernel the Device Tree files for their Microchip SAMA5 home automation platforms.
  • Implemented support for software-based ECC and external hardware ECC engines in the SPI flash subsystem of the Linux kernel, for one of our customers that manufactures flash chips. This patch series is still under review, but we expect to get it merged in early 2020.
  • Implemented support for dm-verity and SELinux in OpenWRT, for one of our customers. See our blog post.
  • Continued to work on implementing top-level parallel build in Buildroot, which finally got merged at the end of 2019 in upstream Buildroot.
  • Continued to work on improving the support for Microchip ARM processors and Marvell Armada ARM processors in the Linux kernel, in many areas.
  • For a customer building a complex audio product based on an Allwinner system-on-chip, we implemented support for TDM in the Allwinner audio interface driver and used it in conjunction with PDM microphones.
  • Published a long series of blog posts on how to create a Linux system with Buildroot for the STM32MP1 platform: part 1, part 2, part 3, part 4 and part 5. This series will continue in 2020.
  • Completed the work on the Allwinner VPU that was funded by the Kickstarter campaign of early 2018: the Allwinner VPU driver was merged, with MPEG2, H264 and H265 decoding support, see our blog post.
  • Continued to work on the support for the RaspberryPi platforms, making a number of enhancements to the display support and its testing, as we reported in a blog post.

Training

Our training business has seen quite a bit of activity in 2019:

  • We created and published a new training course: Displaying and rendering graphics with Linux. This 2-day course, created by Bootlin engineer Paul Kocialkowski provides a detailed walk-through of Linux graphics: graphics hardware and theory, low-level support in the Linux kernel, support in user-space in Wayland or X.org, OpenGL acceleration, etc. This course is available on-site and we already delivered it to customers in Spain, Portugal and France.
  • Our Embedded Linux and Yocto/OpenEmbedded training courses have been ported to use the STM32MP1 platform for practical labs, and we became a member of the STMicroelectronics Partner Program. The support for STM32MP1 in our training courses is proposed as an alternative to the Microchip SAMA5D3 platform (for the Embedded Linux training) and the BeagleBone Black (for the Yocto training) that we continue to support as well.
  • Thanks to funding from Zuehlke Engineering in Serbia, we expanded our Linux boot time optimization training course to 3 days, adding much more lecture and lab content, and collecting useful benchmarks that we later shared in the Embedded Linux Conference Europe.
  • Overall our publicly available training materials have received 376 commits during the course of 2019, all visible in their GitHub repository.
  • We delivered many of our on-site training courses in France, India, Spain, Serbia, Poland, Finland, Portugal, Nederlands and Austria, and continued to offer our training courses in public sessions in Avignon, France.

Conferences

As usual in 2019, we attended and participated to a number of conferences:

  • Our networking experts Antoine Ténart and Maxime Chevallier attended the Netdev 0x13 conference in Prague.
  • Bootlin engineer Alexandre Belloni attended the SiFive Tech Symposium about the RISC-V architecture, in Grenoble.
  • A significant part of our engineering team attended the Linux Plumbers conference in Lisbon.
  • Bootlin engineer Grégory Clement attended the Kernel Recipes conference in Paris.
  • Our display/video expert Paul Kocialkowski attended the X.org Developers Conference, in Montreal.
  • And of course, we attended the Embedded Linux Conference Europe 2019. We gave 5 talks and 2 tutorials at this event (see our blog post) and shared our selection of talks.
  • We participated to the Capitole du Libre conference in Toulouse, where Michael Opdenacker gave a talk on Embedded Linux from scratch in 40 minutes (on RISC-V).
  • Participated to the Buildroot Developers Meeting throughout the year: after FOSDEM 2019 and before ELCE 2019. See our report from the FOSDEM meeting.
  • Recruiting

    • An additional engineer joined our team in Lyon: Kamel Bouhara. Kamel is now working with our senior engineers Grégory Clement and Alexandre Belloni in this office.
    • During the summer 2019, Victor Huesca joined Bootlin as an intern in Toulouse, and worked on improving the tooling used for the maintenance of the Buildroot project. All the work done by Victor is now used by the Buildroot community, see our blog post for more details.
    • An additional engineer will join our Toulouse office at the end of January 2020, and 3 interns will also join Bootlin during the first half of 2020, working on improving the Elixir Cross Referencer, and contributing to Linux and U-Boot.
    • We still have positions opened for Embedded Linux and kernel engineers, see job offer.

Linux 5.4 released, Bootlin contributions inside

LinuxThis time around, we’re quite late to the party, but Linux 5.4 was indeed released a number of weeks ago, and once again, Bootlin contributed a number of patches to this Linux kernel release. As usual, the most useful source of information to learn about the major features brought by Linux 5.4 are the LWN articles (part 1, part 2) and the KernelNewbies Wiki.

With a total of 143 patches contributed to this release, Bootlin is the 17th contributing company by number of commits acccording to the Linux Kernel Patch Statistic.

Here are the highlights of our contributions:

  • Antoine Ténart contributed support for IEEE 1588 Precision Time Protocol (PTP) to the Microsemi Ocelot Ethernet switch driver, which Bootlin developed and upstreamed in 2018 (see our blog post)
  • In the MTD subsystem, a number of contributions to the spi-nor support, written originally by Boris Brezillon, made their way upstream.
  • In the support of Microchip (formerly Atmel) platforms, Kamel Bouhara, who joined Bootlin in September 2019, sees his first kernel contribution merged as a Bootlin engineer: dropping useless support for platform_data from the Atmel PWM driver.
  • In the support of Allwinner platforms
    • Maxime Ripard contributed a brand new driver for the Allwinner A10 camera interface driver, a driver that we started at Bootlin for the CHIP platform back in the days, and that we finished more recently.
    • Maxime Ripard contributed a significant number of improvements to the sun4i-i2s audio interface driver, especially TDM support, which was developed as part of a customer project at Bootlin.
    • Maxime Ripard also contributed numerous enhancements to Allwinner platform Device Tree files, especially in the area of using YAML schemas.
  • In the support for Marvell platforms
    • Grégory Clement added cpufreq support to the Marvell Armada 7K/8K platform by extending some of its clock drivers.
    • Miquèl Raynal contributed improvements to the Marvell CP110 COMPHY driver, which is used to control SERDES lanes on the Marvell Armada 7K/8K platforms, and added the description of the SERDES lanes used by various IP blocks in those processors.
  • Alexandre Belloni, as the RTC subsystem maintainer, did a number of fixes and improvements in several RTC drivers (mainly pcf2123 and pcf8563)
  • For the LPC3250 platform, for which Bootlin delivered a modern BSP to a customer last year, Alexandre Belloni fixed an issue in the lpc_eth network driver, which was preventing the system from booting if the network had been initialized by the bootloader.

In addition to being contributors, some Bootlin engineers are also maintainers of various parts of the Linux kernel, and as such review and merge code from other contributors:

  • As the RTC subsystem maintainer and Microchip platform co-maintainer, Alexandre Belloni merged 47 patches from other contributors
  • As the MTD subsystem co-maintainer, Miquèl Raynal merged 33 patches from other contributors
  • As the Marvell platform co-maintainer, Grégory Clement merged 11 patches from other contributors

Here are the details of all our contributions to Linux 5.4:

Security contributions to OpenWrt: dm-verity and SELinux

OpenWrtWhile Bootlin is largely known for its expertise with the Buildroot and Yocto/OpenEmbedded build systems, we do also work with other build systems for customer projects. Specifically, in 2019, we have worked for one of our customers on extending OpenWrt to add support for two security features: dm-verity and SELinux, which we have contributed to upstream OpenWrt. In this blog post, we provide some details about those features, and how they are integrated in OpenWrt.

dm-verity support

dm-verity is a device mapper target that allows to create a block device on top of an existing block device, with a transparent integrity checking in-between. Provided a tree of per-block hashes that is generated offline, dm-verity will verify at run-time that all the data read from the underlying block device matches the hashes that are provided. This allows to guarantee that the data has not been modified, as a root hash must be passed from a trusted source when setting up the dm-verity block device at boot time. If any bit in the storage has been modified, the verification of the hashes all the way up to the root hash will fail, and the I/O operation on the block of data being read from storage will be rejected. Therefore, dm-verity is typically used as part of a secure boot strategy, which allows the root hash to be passed by the bootloader to the kernel, where the bootloader and kernel themselves are verified by other means. Also, due to the nature of the integrity verification, dm-verity provides a read-only block device, and will therefore only work with read-only filesystems.

dm-verity was also presented in our Secure Boot from A to Z talk the Embedded Linux Conference 2018, from slide 28.

dm-verity in "Secure Boot from A to Z"

We implemented an integration of this mechanism in OpenWrt, and contributed a first version back in March, and we just sent a second version in November.

In essence, our integration consists in:

  1. Packaging the different tools that are needed to generate at build time the tree of hashes corresponding to a given filesystem image. The important package here is cryptsetup (patch 05/12), but it requires packaging a few dependencies: libjson-c (patch 04/12), popt (patch 03/12), lvm2 (patch 02/12) and libaio (patch 01/12)
  2. Extending the mechanism of OpenWrt to generate FIT images for the kernel so that it can include a U-Boot script (patch 06/12 and patch 08/12). Indeed, we have chosen to embed the root hash information inside the FIT image, as FIT image can be signed and verified by the bootloader before booting, ensuring that the root hash is part of the trusted information.
  3. Extending the squashfs filesystem image generation logic so that a dm-verity-capable image can optionally be generated (patch 09/12). If this is the case, then the squashfs image itself is concatenated with the tree of hashes, and a U-Boot script containing the details of the dm-verity image is generated. This includes the important root hash information.
  4. Backporting to the 4.14 and 4.19 Linux kernels currently supported by OpenWrt the DM_INIT mechanism that is in upstream Linux since 5.1, and which allows to setup a device mapper target at boot time using the dm-mod.create= kernel argument (patch 10/12). This allows to have the root filesystem on a device mapper block device, without the need for an initramfs to setup the device mapper target.
  5. Showing with the example of the Marvell Armada XP GP platform how to enable this mechanism on a specific hardware platform already supported by OpenWrt (patch 11/12 and patch 12/12).

For more details, you can read the cover letter of the patch series.

SELinux support

SELinux logoSELinux is a Linux security module that implements Mandatory Access Control and that is generally pretty infamously known in the Linux user community for being difficult to use and configure. However, it is widely used in security-sensitive systems, including embedded systems and as such, makes sense to see supported in OpenWrt. For example, SELinux is already supported in the Yocto/OpenEmbedded ecosystem through the meta-selinux layer, and in the Buildroot project since 2014, contributed by Collins Aerospace.

In short, the basic principle of SELinux is that important objects in the system (files, processes, etc.) are associated to a security context. Then, a policy defines which operations are allowed, depending on the security context of who is doing the operation and on what the operation takes place. This policy is compiled into a binary policy, which is loaded into the kernel early at boot time, and then enforced by the kernel during the system life. Of course, around this, SELinux provides a wide range of tools and libraries to manipulate the policy, build the policy, debug policy violations, and more.

The SELinux support in OpenWrt comes in two parts: a number of additional packages for various libraries and applications, and some integration work in OpenWrt. We will cover both in the next sections. It is worth mentioning that our work does not provide a SELinux policy specifically modified or adjusted for OpenWrt: we simply use the SELinux reference policy, which users will have to tune to their needs.

SELinux-related packages

Getting SELinux to work required a number of new packages to be added in OpenWrt. Those packages were contributed to the community-maintained package feed at https://github.com/openwrt/packages/. They were initially submitted through the mailing list, and then submitted as a pull request.

In short, it contains:

  • libsepol, the binary policy manipulation library.
  • libselinux, the runtime SELinux library that provides interfaces to ELinux-aware applications.
  • audit, which contains the user space utilities for storing and searching the audit records generated by the audit subsystem of the Linux kernel.
  • libcap-ng, which allows to use the POSIX capabilities, and is needed by policycoreutils.
  • policycoreutils, which is the set of core SELinux utilities such as sestatus, secon, setfiles, load_policy and more.
  • libsemanage, which is the policy management library.
  • checkpolicy, which is the SELinux policy compiler.
  • refpolicy, which is the SELinux reference policy.
  • selinux-python, a number of SELinux tools written in Python, especially audit2allow for policy debugging.

SELinux integration

Our second patch series, for OpenWrt itself, allows to build a SELinux-enabled system thanks to the following changes:

  • Allow to build Busybox with SELinux support, so that all the Busybox applets that support SELinux specific options such as -Z can be built with libselinux (patch 1/7)
  • Add support in OpenWrt’s init application, called procd, for loading the SELinux policy at boot time (patch 2/7). This patch has been submitted separately for integration into the procd project.
  • Add support for building a new host tool called fakeroot (patch 3/7).
  • Add support for building squashfs images with extended attributes generated by SELinux setfiles tool (patch 4/7). This is why fakeroot is needed: writing those extended attributes that store SELinux security contexts require root access, so we run the entire process within a fakeroot environment. This also requires building the squashfs tools with extended attributes support (patch 7/7).
  • Add new options to enable in the Linux kernel support for SELinux and SquashFS with extended attributes (patch 5/7 and patch 6/7).

Conclusion

Integrating those two security features in OpenWrt required numerous changes in the build system, and the corresponding patches are still under review by the OpenWrt community. We hope to see these features merged in 2020.

Back from ELCE 2019: our talks videos, slides, and more!

With 8 engineers participating to the Embedded Linux Conference Europe, almost the entire Bootlin engineering team took part to the conference. As usual, we not only attended the event, but also contributed by giving a total of 5 talks and 2 tutorials, for which we’re happy to share below the videos and slides. Also, as part of this conference, Bootlin CTO Thomas Petazzoni received an award for his contribution to the conference.

Buildroot, what’s new ?

Talk given by Thomas Petazzoni, slides in PDF and slides source code.

Timing boot time reduction techniques

Talk given by Michael Opdenacker, slides in PDF, slides source code.

Integrating hardware-accelerated video decoding with the display stack

Talk given by Paul Kocialkowski, slides in PDF, slides source code.

RTC subsystem, recent changes and where it is heading

Talk given by Alexandre Belloni, slides in PDF, slides source code.

Flash subsystems status update

Talk given by Miquèl Raynal (from Bootlin) and Richard Weinberger (from sigma star gmbh), slides in PDF, slides source code.

Offloading network traffic classification to hardware

Talk given by Maxime Chevallier, slides in PDF, slides source code.

Introduction to Linux Kernel driver programming

Tutorial given by Michael Opdenacker, slides in PDF, slides source document. The video is not yet available, but should be published in the future.

Introduction to the Buildroot embedded Linux build system

Tutorial given by Thomas Petazzoni, slides in PDF, slides source code. The video is not yet available, but should be published in the future.

Award to Thomas Petazzoni

During the traditional closing game of the conference, we were really happy to have Bootlin’s CTO Thomas Petazzoni called on stage, to receive from the hands of Tim Bird, an award for his continuous 11 year participation to the conference, with 24 presentations given, one keynote and for the past two years, participation to the conference program committee. We are honored and proud by this recognition of Thomas contribution to the conference.

Thomas Petazzoni receives ELCE conference award

Thomas Petazzoni receives ELCE conference award

Thomas Petazzoni receives ELCE conference award

Yocto Project training course available on STM32MP1 platform

Back in May 2019, we announced the availability of our Embedded Linux system development course on the STMicroelectronics STM32MP1 platform, in addition to the already supported Microchip SAMA5D3 Xplained board.

In the context of our partnership with STMicroelectronics, we are now happy to announce the availability of our Yocto Project and OpenEmbedded development training also on the STM32MP1 platform for the practical labs. We now support either the BeagleBoneBlack Wireless or the STMicroelectronics STM32MP1 platforms for this training course.

The complete training materials are available: detailed agenda, slides and practical labs. The complete source code of the training materials is also available in our Github repository.

Bootlin Yocto course on STM32MP1
Slides of Bootlin’s Yocto course for the STM32MP1

This will hopefully help customers around the world to get started with using Yocto on the STM32MP1 system-on-chip. The Yocto experts at Bootlin are available to deliver this 3-day course anywhere in the world, at your location. The first edition of this new variant of the course is going to be given this week to one of our customers in Spain. Contact us if you’re interested by having this course organized at your location!

Publication of Linux graphics training materials

Back in June 2019, we announced the availability of a new training course, Displaying and rendering graphics with Linux. At the time of this announcement, the training materials were not available though.

Since then, Bootlin engineer Paul Kocialkowski has been very busy preparing those training materials, and has successfully delivered the first edition of this course to one of our customers in Spain early September. After taking the time to polish those training materials following this first course, we are now very happy to publish and share this 200+ slides deck, covering a wide range of graphics related topics:

  • Image and color representation
  • Basic drawing
  • Basic and advanced operations
  • Hardware aspects overview
  • Hardware for display
  • Hardware for rendering
  • Memory aspects
  • Performance aspects
  • Software aspects overview
  • Kernel components in Linux
  • Userspace components with Linux

See also the detailed agenda of this training course. The LaTeX source code for all our training materials, including this graphics training, is available in a Git repository. It is worth mentioning that this training only consists of slides and demos, and does not include practical labs done by the participants, in order to keep the training logistics manageable and the duration reasonably short (2 days).

Here are a few slides showing various aspects of this training course:

Graphics training

Graphics training

Graphics training

Graphics training

Graphics training

By publishing this training materials right after our first course, and under the Creative Commons CC-BY-SA license, Bootlin sticks to its commitment of publishing all its training materials under a free documentation license, to better spread the knowledge in the entire embedded Linux community.

We are available to deliver this Displaying and rendering graphics with Linux course anywhere in the world, at your location. Contact us for more details.

Linux 5.3 released, Bootlin contributions inside

Penguin from Mylène Josserand
Drawing from Mylène Josserand, based on a picture from Samuel Blanc (https://commons.wikimedia.org/wiki/File:Manchot_royal_-_King_Penguin.jpg)
The 5.3 version of the Linux kernel was released recently. As usual, we recommend our readers to look at the LWN coverage for this release merge window: part 1 and part 2. Together with the KernelNewbies page, these articles give a very nice overview of the major features and improvements of this new release.

For this release, Bootlin is the 16th contributing company by number of commits, with 143 patches merged in the Linux kernel. Our significant contributions this time were:

  • Support for Allwinner processors
    • The support for H264 video decoding, from Maxime Ripard, was finally merged in the cedrus VPU driver that we have developed thanks to the funding of our Kickstarter campaign last year. The last missing piece is H265 video decoding, which we have submitted several times and we hope to get merged soon.
  • Support for Marvell platforms
    • Antoine Ténart contributed a number of bug fixes and updates to the inside-secure crypto driver, which is used for the cryptographic hardware accelerator found on Marvell Armada 3700 and Marvell Armada 7K/8K.
    • Maxime Chevallier contributed many improvements to the mvpp2 network driver, used on the Marvell Armada 375 and Armada 7K/8K systems. His patches improve the traffic classification offloading capabilities, a topic he will present in detail at the next Embedded Linux Conference Europe.
    • Miquèl Raynal added PHY support for the PCIe Armada 8K driver, and adjusted a few things in the Marvell Armada 7K/8K Device Tree files.
  • Support for Microchip MPU (formerly Atmel) platforms
    • Alexandre Belloni converted the remaining SoCs (SAM9x5, SAM9G45, SAM9RL and SAMA5D3) to the new slow clock controller bindings.
    • Antoine Ténart contributed a few small improvements to the macb driver, for the Cadence network controller used on Microchip platforms.
  • Maxime Ripard contributed numerous YAML Device Tree schemas, to help the effort of converting many Device Tree bindings to the new YAML format, which can be used to validate Device Trees against their bindings.
  • Maxime Ripard contributed numerous patches to the core DRM subsystem: a complete rewrite of the command line parser that parses the DRM-related options of the kernel command line, and support for new options. This was done as part of an effort to make sure the upstream Linux kernel can support all the possible options that the downstream RaspberryPi kernel+firmware combination provides to configure the display.
  • Paul Kocialkowski contributed a few improvements to the RaspberryPi vc4 display controller driver, related to buffer allocation.

Also, several of Bootlin engineers are also kernel maintainers, so they review and merge patches from other contributors:

  • Miquèl Raynal as the NAND subsystem maintainer and MTD subsystem co-maintainer, reviewed and merged 51 patches from other contributors
  • Maxime Ripard as the Allwinner platform co-maintainer, reviewed and merged 38 patches from other contributors
  • Alexandre Belloni as the RTC maintainer and Microchip platform co-maintainer, reviewed and merged 36 patches from other contributors
  • Grégory Clement as the Marvell EBU platform co-maintainer, reviewed and merged 9 patches from other contributors

Here is the details of all our contributions, patch by patch:

Building a Linux system for the STM32MP1: developing a Qt5 graphical application

After showing how to build a minimal Linux system for the STM32MP157 platform, how to connect and use an I2C based pressure/temperature/humidity sensor and how to integrate Qt5 in our system, how to set up a development environment to write our own Qt5 application, we are finally going to write our Qt5 application.

List of articles in this series:

  1. Building a Linux system for the STM32MP1: basic system
  2. Building a Linux system for the STM32MP1: connecting an I2C sensor
  3. Building a Linux system for the STM32MP1: enabling Qt5 for graphical applications
  4. Building a Linux system for the STM32MP1: setting up a Qt5 application development environment
  5. Building a Linux system for the STM32MP1: developing a Qt5 graphical application
  6. Building a Linux system for the STM32MP1: implementing factory flashing
  7. Building a Linux system for the STM32MP1: remote firmware updates

Disclaimer

Before we get started in this blog post, it is important to mention that it is not meant to be a full introduction to programming applications with Qt5. This would require much more than a blog post, and the Qt web site has extensive documentation.

Also, we want to make it clear that Bootlin’s core expertise is in low-level embedded Linux development, not in Qt application development. Therefore, our example application may not show the best practices in terms of Qt development. We welcome comments and suggestions from our readers to improve the example used in this blog post.

Reading sensor data

As we’ve seen in a previous article, the sensor data is available by reading the following files:

  • /sys/bus/iio/devices/iio:device2/in_temp_input for the temperature
  • /sys/bus/iio/devices/iio:device2/in_pressure_input for the pressure
  • /sys/bus/iio/devices/iio:device2/in_humidityrelative_input for the humidity

So what we will do is writing a new class called DataProvider, which will read those files once per second, and emit a signal with the 3 values every second. Slots and signals is a fundamental mechanism in Qt, which allows to connect emitters of events to receivers for those events. In our case, the DataProvider class will emit a signal when new sensor values are read, while another class in charge of the graphical UI will receive those signals.

At this step, we don’t yet have a graphical UI, so we’ll simply add a few debugging messages in the DataProvider to make sure it works as expected.

Let’s start by adding a data-provider.h file to our project:

#ifndef DATA_PROVIDER_H
#define DATA_PROVIDER_H

#include <QtCore/QTimer>

class DataProvider: public QObject
{
    Q_OBJECT

public:
    DataProvider();

private slots:
    void handleTimer();

signals:
    void valueChanged(float temp, float pressure, float humidity);

private:
    QTimer timer;
};

#endif /* DATA_PROVIDER_H */

It creates a very simple class than inherits from QObject, with:

  • A constructor
  • A private slot handleTimer which will be used internally by the class QTimer’s instance to notify that a timer has expired. This is what will allow us to poll the sensor values every second.
  • A valueChanged signal, which will be emitted by the class every time new sensor values are available.

Then, the implementation of this class in data-provider.cpp is fairly straight-forward:

#include <QtCore/QFile>
#include <QDebug>
#include "data-provider.h"

DataProvider::DataProvider()
{
    QObject::connect(&timer, &QTimer::timeout,
		     this, &DataProvider::handleTimer);
    timer.setInterval(1000);
    timer.start();
}

void DataProvider::handleTimer()
{
    QFile temp_f("/sys/bus/iio/devices/iio:device2/in_temp_input");
    QFile pressure_f("/sys/bus/iio/devices/iio:device2/in_pressure_input");
    QFile humidity_f("/sys/bus/iio/devices/iio:device2/in_humidityrelative_input");

    if (!temp_f.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    if (!pressure_f.open(QIODevice::ReadOnly | QIODevice::Text))
        return;
    if (!humidity_f.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    float temp = QString(temp_f.readAll()).toDouble() / 1000;
    float pressure = QString(pressure_f.readAll()).toDouble() * 10;
    float humidity = QString(humidity_f.readAll()).toDouble() / 1000;

    qDebug() << "Temperature: " << temp << "Pressure: " << pressure << "Humidity: " << humidity;

    emit valueChanged(temp, pressure, humidity);
}

The constructor of the class connects the QTimer::timeout signal of the QTimer to this class handlerTimer slot, sets the timer interval to 1000 milliseconds, and starts the timer. This is what will ensure the handleTimer method gets called every second.

In the handleTimer method, we open the 3 files in sysfs, read their value and convert them to meaningful units: the temperature in Celcius, the pressure in hPA, and the humidity in percent. We then print a debugging message and emit the signal with the three values.

With this in place, we need to make sure those two files are properly taken into account by our project, by changing the .pro file as follows:

QT += widgets
SOURCES = main.cpp data-provider.cpp
HEADERS = data-provider.h
INSTALLS += target
target.path = /usr/bin

The data-provider.cpp file was added to SOURCES, while data-provider.h was added to the new HEADERS.

Now, we just need to change main.cpp to instantiate one DataProvider object:

#include <QApplication>
#include <QPushButton&ht;
#include "data-provider.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    QPushButton hello("Hello world!");
    DataProvider dp;
    hello.resize(100,30);
    hello.show();
    return app.exec();
}

With this, you can now build and run the application, and you should see every second the debugging message showing the temperature, pressure and humidity values:

# qt-sensor-demo -platform linuxfb
Temperature:  28.12  Pressure:  1003.08  Humidity:  32.235
Temperature:  28.12  Pressure:  1003.07  Humidity:  32.246
Temperature:  28.12  Pressure:  1003.06  Humidity:  32.256
Temperature:  28.12  Pressure:  1003.08  Humidity:  32.267

Displaying sensor data

We now want to display the sensor data. For this, we'll create a UI with two panels, one to display the numeric value of the temperature, humidity and pressure, and another panel with a chart of the temperature. At the bottom of the screen, two buttons Values and Chart will allow to switch between both panels.

So, we'll create a Window class to encapsulate the overall window layout and behavior, and a Values class providing the widget showing the 3 values. We'll leave the chart implementation to the next section. To help you follow the code in this section, here is a diagram that shows the different widgets and how they will be grouped together in our user interface:

Qt Sensor demo UI

Let's start by implementing the Values widget, which will be used to show the 3 numeric values, one below each other. The values.h file will look like this:

#ifndef VALUES_H
#define VALUES_H

#include <QWidget>

class QLabel;

class Values : public QWidget
{
    Q_OBJECT

public:
    Values();

public slots:
    void handleValueChanged(float temp, float pressure, float humidity);

private:
    QLabel *temperature_v;
    QLabel *pressure_v;
    QLabel *humidity_v;
};

#endif /* VALUES_H */

So it has a simple constructor, a slot to be notified of new values available, and 3 text labels to display the 3 values. The implementation in values.cpp is:

// SPDX-License-Identifier: MIT
#include <QtWidgets>
#include "values.h"

Values::Values()
{
    QVBoxLayout *layout = new QVBoxLayout;

    QLabel *temperature_l = new QLabel(tr("Temperature (°C)"));
    QLabel *pressure_l = new QLabel(tr("Pressure (hPa)"));
    QLabel *humidity_l = new QLabel(tr("Humidity (%)"));

    temperature_v = new QLabel();
    pressure_v = new QLabel();
    humidity_v = new QLabel();

    QFont f = temperature_v->font();
    f.setPointSize(28);
    f.setBold(true);
    temperature_v->setFont(f);
    pressure_v->setFont(f);
    humidity_v->setFont(f);
    temperature_v->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    pressure_v->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    humidity_v->setAlignment(Qt::AlignRight | Qt::AlignVCenter);

    layout->addWidget(temperature_l);
    layout->addWidget(temperature_v);
    layout->addWidget(pressure_l);
    layout->addWidget(pressure_v);
    layout->addWidget(humidity_l);
    layout->addWidget(humidity_v);

    setLayout(layout);
}

void Values::handleValueChanged(float temp, float pressure, float humidity)
{
    temperature_v->setText(QString::number(temp, 'f', 2));
    pressure_v->setText(QString::number(pressure, 'f', 1));
    humidity_v->setText(QString::number(humidity, 'f', 1));
}

The constructor creates 3 text labels for the legends ("Temperature (°C)", "Pressure (hPA)" and "Humidity (%)"), then instantiates the 3 text labels for the values themselves. It sets up the font and text alignment properties for those labels, and then adds all widgets in a QVBoxLayout so that they all appear vertically below each other.

The handleValueChanged slot simply updates the text labels contents with the new sensor values, doing the proper text formatting on the way.

With the Values class implemented, we can now implement the main Window class. The window.h will contain:

#ifndef WINDOW_H
#define WINDOW_H

#include <QWidget>

class Values;

class Window : public QWidget
{
    Q_OBJECT

public slots:
    void handleValueChanged(float temp, float pressure, float humidity);

public:
    Window();

private:
    Values *values;
};

#endif

Beyond a simple constructor, it has a slot to receive new sensor values, and a reference to a Values widget instance.

The implementation in window.cpp is as follows:

#include <QtWidgets>

#include "window.h"
#include "values.h"

Window::Window()
{
    values = new Values;
    QVBoxLayout *layout = new QVBoxLayout;
    QHBoxLayout *buttons = new QHBoxLayout;

    QPushButton *values_button = new QPushButton("Values");
    QPushButton *chart_button = new QPushButton("Chart");

    buttons->addWidget(values_button);
    buttons->addWidget(chart_button);

    layout->addWidget(values);
    layout->addLayout(buttons);

    setLayout(layout);

    setWindowTitle(tr("Sensors"));
}

void Window::handleValueChanged(float temp, float pressure, float humidity)
{
    values->handleValueChanged(temp, pressure, humidity);
}

The constructor creates a horizontal layout QHBoxLayout with two buttons: Values and Chart. Those will be used in the next section to switch between the Values panel and the Chart panel. For now, they don't do anything.

Then, the constructor adds the Value widget, and the horizontal layout box with the buttons into a vertical box layout, assigns the main window layout and defines the window title.

The handleValueChanged slot implementation just forwards the call to the Values::handleValueChanged method.

Now, obviously main.cpp needs to be changed: instead of creating a button, we'll create our window, and do a bit of additional setup:

#include <QApplication>
#include "window.h"
#include "data-provider.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    DataProvider dp;
    Window window;

    QObject::connect(&dp, &DataProvider::valueChanged,
		     &window, &Window::handleValueChanged);

    window.setFixedSize(480, 800);
    window.setStyleSheet("background-color: white;");
    window.show();
    return app.exec();
}

So, not only we create the Window, but more importantly, we connect the valueChanged signal of DataProvider to the handleValueChanged slot of Window. We define the window size (which is fixed, to match the STM32MP15 Discovery board panel) and set the background color of the application.

Obviously, the qt-sensor-demo.pro file needs to be adjusted to build our new files. It now looks like this:

QT += widgets
SOURCES = main.cpp data-provider.cpp window.cpp values.cpp
HEADERS = data-provider.h window.h values.h
INSTALLS += target
target.path = /usr/bin

With this done, we can run the Qt5 application on our target, and see:

Qt5 application showing the I2C sensor data

Graphing the temperature

The final part of developing our application is to implement a graph showing the evolution of temperature over time. For this, we are going to use the very convenient Qt Charts module, which is available in a separate Qt module from the base of Qt.

To implement the graph widget itself, we'll create a new Chart class:

#ifndef CHART_H
#define CHART_H

#include <QtCharts/QChart>

QT_CHARTS_BEGIN_NAMESPACE
class QSplineSeries;
class QValueAxis;
QT_CHARTS_END_NAMESPACE

QT_CHARTS_USE_NAMESPACE

class Chart: public QChart
{
    Q_OBJECT

public:
    Chart(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);

public slots:
    void handleValueChanged(float temp, float pressure, float humidity);

private:
    QSplineSeries *m_series;
    QStringList m_titles;
    QValueAxis *m_axisX;
    QValueAxis *m_axisY;
    int xpos;
};

#endif /* CHART_H */

This class inherits from the QChart class provided by Qt. It provides a constructor and destructor, a slot that allows to receive notification of new sensor values, and it has a number of private variables to manage the chart itself.

Let's go through the implementation of this class now:

#include "chart.h"
#include <QtCharts/QAbstractAxis>
#include <QtCharts/QSplineSeries>
#include <QtCharts/QValueAxis>

Chart::Chart(QGraphicsItem *parent, Qt::WindowFlags wFlags):
    QChart(QChart::ChartTypeCartesian, parent, wFlags),
    m_series(0),
    m_axisX(new QValueAxis()),
    m_axisY(new QValueAxis()),
    xpos(0)
{
    m_series = new QSplineSeries(this);
    QPen pen(Qt::red);
    pen.setWidth(2);
    m_series->setPen(pen);
    m_series->append(xpos, 30);

    addSeries(m_series);

    addAxis(m_axisX,Qt::AlignBottom);
    addAxis(m_axisY,Qt::AlignLeft);
    m_series->attachAxis(m_axisX);
    m_series->attachAxis(m_axisY);
    m_axisX->setTickCount(5);
    m_axisX->setRange(0, 60);
    m_axisY->setRange(0, 50);

    QFont f = m_axisX->labelsFont();
    f.setPointSize(8);
    m_axisX->setLabelsFont(f);
    m_axisY->setLabelsFont(f);

    setMargins(QMargins(0,0,0,0));

    setTitle("Temperature (°C)");
    legend()->hide();
}

void Chart::handleValueChanged(float temp, float pressure, float humidity)
{
    Q_UNUSED(pressure);
    Q_UNUSED(humidity);
    m_series->append(xpos, temp);
    xpos++;
    if (xpos >= 60)
      scroll(plotArea().width() / 60, 0);
}

The constructor simply sets up the QChart we inherit from: defining the axis, their range, the pen width and color, etc. On the X axis (time), we are going to show 60 measurements, and since our handleValueChanged slot is going to be called every second, it means our graph will show the last 60 seconds of temperature measurement. On the Y axis (temperature), we can show temperatures from 0°C to 50°C. Of course, this is all very hardcoded in this example, for simplicity.

The handleValueChanged slot appends the new temperature value to the graph, and then updates the area displayed by the graph so that always the last 60 seconds are visible.

Now, we need to integrate this to our existing Window class, so that we can display the chart, and switch between the numeric values and the chart. First, we need to do some changes in window.h, and below we'll show only the diff to make the differences very clear:

diff --git a/window.h b/window.h
index 3d63d38..05d1f39 100644
--- a/window.h
+++ b/window.h
@@ -3,8 +3,12 @@
 #define WINDOW_H
 
 #include <QWidget>
+#include <QtCharts/QChartView>
+
+QT_CHARTS_USE_NAMESPACE
 
 class Values;
+class Chart;
 
 class Window : public QWidget
 {
@@ -13,11 +17,17 @@ class Window : public QWidget
 public slots:
     void handleValueChanged(float temp, float pressure, float humidity);
 
+private slots:
+    void chartButtonClicked();
+    void valuesButtonClicked();
+
 public:
     Window();
 
 private:
     Values *values;
+    QChartView *chartView;
+    Chart *chart;
 };
 
 #endif

So, we're defining two private slots that will be used for the two buttons that allow to switch between the numeric values and the chart, and then we add two variables, one for the chart itself, and one for the QChartView (which basically renders the graph into a widget).

Then, in window.cpp, we do the following changes:

diff --git a/window.cpp b/window.cpp
index aba2862..d654964 100644
--- a/window.cpp
+++ b/window.cpp
@@ -3,28 +3,54 @@
 
 #include "window.h"
 #include "values.h"
+#include "chart.h"
 
 Window::Window()
 {
     values = new Values;
+    chart = new Chart;
     QVBoxLayout *layout = new QVBoxLayout;
     QHBoxLayout *buttons = new QHBoxLayout;
 
     QPushButton *values_button = new QPushButton("Values");
     QPushButton *chart_button = new QPushButton("Chart");
 
+    QObject::connect(chart_button, &QPushButton::clicked,
+                    this, &Window::chartButtonClicked);
+    QObject::connect(values_button, &QPushButton::clicked,
+                    this, &Window::valuesButtonClicked);
+
     buttons->addWidget(values_button);
     buttons->addWidget(chart_button);
 
+    chartView = new QChartView(chart);
+    chartView->setRenderHint(QPainter::Antialiasing);
+
     layout->addWidget(values);
+    layout->addWidget(chartView);
     layout->addLayout(buttons);
 
     setLayout(layout);
 
+    chartView->hide();
+
     setWindowTitle(tr("Sensors"));
 }
 
 void Window::handleValueChanged(float temp, float pressure, float humidity)
 {
     values->handleValueChanged(temp, pressure, humidity);
+    chart->handleValueChanged(temp, pressure, humidity);
+}
+
+void Window::chartButtonClicked()
+{
+    values->hide();
+    chartView->show();
+}
+
+void Window::valuesButtonClicked()
+{
+    values->show();
+    chartView->hide();
 }

So, in the constructor we are connecting the clicked signals of the two buttons to their respective slots. We create the Chart object, and then the QChartView to render the graph. We add the latter as an additional widget in the QVBoxLayout, and we hide it.

The existing handleValueChanged slot is modified to also update the Chart object with the new sensor values.

Finally, the new chartButtonClicked and valuesButtonClicked slots implement the logic that is executed when the buttons are pressed. We simply hide or show the appropriate widget to display either the numeric values or the chart. There is probably a nicer way to achieve this in Qt, but this was good enough for our example.

Now that the source code is in place, we of course need to adjust the build logic in qt-sensor-demo.pro:

--- a/qt-sensor-demo.pro
+++ b/qt-sensor-demo.pro
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: MIT
-QT += widgets
-SOURCES = main.cpp data-provider.cpp window.cpp values.cpp
-HEADERS = data-provider.h window.h values.h
+QT += widgets charts
+SOURCES = main.cpp data-provider.cpp window.cpp values.cpp chart.cpp
+HEADERS = data-provider.h window.h values.h chart.h
 INSTALLS += target
 target.path = /usr/bin

Besides the obvious addition of the chart.cpp and chart.h file, the other important addition is charts to the QT variable. This tells qmake that our application is using the Qt Charts, and that we therefore need to link against the appropriate libraries.

Building the application

At this point, if you try to build the application, it will fail because QtCharts has not been built as part of our Buildroot configuration. In order to address this, run Buildroot's make menuconfig, enable the BR2_PACKAGE_QT5CHARTS option (in Target packages -> Graphic libraries and applications -> Qt5 -> qt5charts).

Then, run the Buildroot build with make, and reflash the resulting SD card image.

Now, you can build again your application, either with Qt Creator if you've been using Qt Creator, or manually. If you build it manually, you'll have to run qmake again to regenerate the Makefile, and then build with make.

When you run the application on the target, the GUI will display the same numeric values as before, but now if you press the Chart button, it will show something like:

Qt5 application with chart

Adjusting the Buildroot package

We have for now been building this application manually, but as explained in our previous blog post, we really want Buildroot to be able to build our complete system, including our application. For this reason, we had created a qt-sensor-demo package, which gets our application source code, configures it with qmake, builds it and installs it.

However, with the new use of Qt Charts, our qt-sensor-demo package needs a few adjustements:

  • The Config.in file needs an additional select BR2_PACKAGE_QT5CHARTS, to make sure Qt Charts are enabled in the Buildroot configuration
  • The qt-sensor-demo.mk file needs an additional qt5charts in the QT_SENSOR_DEMO_DEPENDENCIES variable to make sure the qt5charts package gets built before qt-sensor-demo

With this in place, you can run:

make qt-sensor-demo-rebuild
make

And you have an SD card image that includes our application!

Starting the application automatically at boot time

The next and almost final step for this blog post is to get our application automatically started at boot time. We can simply add a small shell script on the target in /etc/init.d/: the default Buildroot configuration for the init system will execute all scripts named Ssomething in /etc/init.d/. We'll add a file named package/qt-sensor-demo/S99qt-sensor-demo with these contents:

#!/bin/sh

DAEMON="qt-sensor-demo"
DAEMON_ARGS="-platform linuxfb"
PIDFILE="/var/run/qt-sensor-demo.pid"

start() {
	printf 'Starting %s: ' "$DAEMON"
	start-stop-daemon -b -m -S -q -p "$PIDFILE" -x "/usr/bin/$DAEMON" -- $DAEMON_ARGS
	status=$?
	if [ "$status" -eq 0 ]; then
		echo "OK"
	else
		echo "FAIL"
	fi
	return "$status"
}

stop () {
	printf 'Stopping %s: ' "$DAEMON"
	start-stop-daemon -K -q -p "$PIDFILE"
	status=$?
	if [ "$status" -eq 0 ]; then
		rm -f "$PIDFILE"
		echo "OK"
	else
		echo "FAIL"
	fi
	return "$status"
}

restart () {
	stop
	sleep 1
	start
}

case "$1" in
        start|stop|restart)
		"$1";;
	reload)
		# Restart, since there is no true "reload" feature.
		restart;;
        *)
                echo "Usage: $0 {start|stop|restart|reload}"
                exit 1
esac

This is the canonical init script used in Buildroot to start system daemons and services, and is modeled after the one in package/busybox/S01syslogd. It uses the start-stop-daemon program to start our application in the background.

Then, to get this init script installed, we need to adjust package/qt-sensor-demo/qt-sensor-demo.mk with the following additional lines:

define QT_SENSOR_DEMO_INSTALL_INIT_SYSV
        $(INSTALL) -D -m 755 package/qt-sensor-demo/S99qt-sensor-demo \
                $(TARGET_DIR)/etc/init.d/S99qt-sensor-demo
endef

This ensures that the init script gets installed in /etc/init.d/S99qt-sensor-demo as part of the build process of our qt-sensor-demo package. Note that an init script works fine if you're using the Busybox init implementation or the sysvinit init implementation (we're using the default Buildroot setup here, which uses the Busybox init implementation). If you want to use systemd as an init implementation, then a different setup is necessary.

With this done, you simply need to reinstall the application and regenerate the SD card image

$ make qt-sensor-demo-reinstall
$ make

You can now test your SD card image on you board, and you should see the application being started automatically, with the following messages at boot time

Starting dropbear sshd: OK
Starting qt-sensor-demo: OK

Welcome to Buildroot

Avoid unnecessary logging on the display panel

In our current setup, the kernel messages are being sent to both the serial port and the framebuffer console, which means they appear on the display panel. This is not very pretty, and we would like the display to remain black until the application starts, while keeping the kernel messages on the serial port for debugging purposes. Also, we would like the framebuffer console text cursor to not be displayed, to really have a fully black screen. To achieve this we will add two arguments on the Linux kernel command line:

  • console=ttySTM0,115200, which will tell the Linux kernel to only use the serial port as the console, and not all registered consoles, which would include the framebuffer console. This option will make sure the kernel messages are not displayed on the screen.
  • vt.global_cursor_default=0, which will tell the Linux kernel to not display any cursor on the framebuffer console.

So, to add those options, we simply modify board/stmicroelectronics/stm32mp157-dk/overlay/boot/extlinux/extlinux.conf in Buildroot as follows:

label stm32mp15-buildroot
  kernel /boot/zImage
  devicetree /boot/stm32mp157c-dk2.dtb
  append root=/dev/mmcblk0p4 rootwait console=ttySTM0,115200 vt.global_cursor_default=0

Of course, rebuild the SD card image with make, reflash and test the result on your STM32MP1 platform.

Conclusion

In this blog post, we have seen how to write a real (but admittedly very simple) Qt application, how to make it read and display sensor data, and how to integrate this application so that it gets started at boot time.

You can find the Buildroot changes corresponding to this blog post in the 2019.02/stm32mp157-dk-blog-5 branch of our repository. The qt-sensor-demo application code can be found in the blog-5 branch of this application Git repository.

Stay tuned for our next blog post about factory flashing and OTA update!

Bootlin contributes Linux DRM driver for LogicBricks logiCVC-ML IP

LogicBricks is a vendor of numerous IP blocks, ranging from display controllers, audio controllers, 3D accelerators and many other specialized IP blocks. Most of these IP blocks are designed to work with the Xilinx Zynq 7000 system-on-chip, which includes an FPGA area. And indeed, because the Zynq 7000 does not have a display controller, one of Bootlin customers has selected the LogicBricks logiCVC-ML IP to provide display support for their Zynq 7000 design.

logiCVC-ML

LogiBricks provide one driver based on the framebuffer subsystem and another one based on the DRM subsystem, but none of these drivers are in the upstream Linux kernel. Bootlin engineer Paul Kocialkowski worked on a clean DRM driver for this IP block, and submitted the first version to the upstream Linux kernel. We already received some useful comments on the Device Tree binding for this IP block, which is pretty elaborate due to the number of aspects/features that can be tuned at IP synthesis time, and we will of course take into account those comments and send new iterations of the patch series until it gets merged.

In the e-mail containing the driver patch itself, Paul gives a summary of the IP features that are supported and tested, and those that re either untested or unsupported:

Introduces a driver for the LogiCVC display controller, a programmable
logic controller optimized for use in Xilinx Zynq-7000 SoCs and other
Xilinx FPGAs. The controller is mostly configured at logic synthesis
time so only a subset of configuration is left for the driver to
handle.

The following features are implemented and tested:
- LVDS 4-bit interface;
- RGB565 pixel formats;
- Multiple layers and hardware composition;
- Layer-wide alpha mode;

The following features are implemented but untested:
- Other RGB pixel formats;
- Layer framebuffer configuration for version 4;
- Lowest-layer used as background color;
- Per-pixel alpha mode.

The following features are not implemented:
- YUV pixel formats;
- DVI, LVDS 3-bit, ITU656 and camera link interfaces;
- External parallel input for layer;
- Color-keying;
- LUT-based alpha modes.

Additional implementation-specific notes:
- Panels are only enabled after the first page flip to avoid flashing a
  white screen.
- Depth used in context of the LogiCVC driver only counts color components
  to match the definition of the synthesis parameters.

Support is implemented for both version 3 and 4 of the controller.

With version 3, framebuffers are stored in a dedicated contiguous
memory area, with a base address hardcoded for each layer. This requires
using a dedicated CMA pool registered at the base address and tweaking a
few offset-related registers to try to use any buffer allocated from
the pool. This is done on a best-effort basis to have the hardware cope
with the DRM framebuffer allocation model and there is no guarantee
that each buffer allocated by GEM CMA can be used for any layer.
In particular, buffers allocated below the base address for a layer are
guaranteed not to be configurable for that layer. See the implementation of
logicvc_layer_buffer_find_setup for specifics.

Version 4 allows configuring each buffer address directly, which
guarantees that any buffer can be configured.

Improvements to Buildroot maintenance tooling

From mid-April to end of August, Victor Huesca, a student from the University of Toulouse joined Bootlin’s team for a 3.5 months internship. His internship was focused on the Buildroot project, and Victor’s mission was to improve various aspect of the tooling around Buildroot to help in the maintenance of this build system. In this blog post, we will present the different improvements and features implemented by Victor during his internship. This internship was funded by Bootlin, and entirely focused on contributing to the Buildroot project.

Notifications of new upstream versions of packages

Buildroot has over 2400 packages for a wide variety of software components, and it is a challenge to keep all of those packages updated with the latest releases from the upstream developers. Buildroot has a nice statistics page with all its packages, and in early 2019, your author added support for querying the release-monitoring.org service to find the latest upstream version of each package. This allowed our statistics page to show the current version in Buildroot and the latest version available upstream for all Builroot packages.

Victor improved this by implementing e-mail notifications to Buildroot developers about their package having new upstream releases available. Indeed, Buildroot has a DEVELOPERS file which associates the name and e-mail of Buildroot contributors/developers with the packages they take care of. So, what Victor did is:

  • Extend the pkg-stats script, which generates the statistics page, to not only generate a HTML output, but also a JSON output. A JSON output is obviously a lot more usable by other tools. Victor also improved the efficiency of this script in several ways, especially by parallelizing the requests made to release-monitoring.org.
  • Extend the daily-mail script, which until now was only sending autobuild results, to also send notifications about packages that are not up to date with their latest upstream version

The notifications are sent only once a week, both individually to the developers, and globally on the mailing list. They are grouped in a single e-mail with the existing autobuild results notifications, to minimize the amount of e-mails received by developers. You can see an example of such a notification in this e-mail, with a small excerpt below:

Packages having a newer version
===============================

             name              | found by |        link to release-monitoring.org        |   version    |   upstream   | orph?
-------------------------------+----------+----------------------------------------------+--------------+--------------+-------
                           4th |  DISTRO  | https://release-monitoring.org/project/20471 | 3.62.5       | 3625         |     
                        acpica |  DISTRO  | https://release-monitoring.org/project/00018 | 20190703     | 20190816     |     
                         acpid |  DISTRO  | https://release-monitoring.org/project/00019 | 2.0.30       | 2.0.32       | ORPH
                       acsccid |  DISTRO  | https://release-monitoring.org/project/15661 | 1.1.4        | 1.1.7        |     
            adwaita-icon-theme |  DISTRO  | https://release-monitoring.org/project/13117 | 3.22.0       | 3.33.92      |     
                       aespipe |  GUESS   | https://release-monitoring.org/project/21320 | 2.4d         | 2.4e         | ORPH
                 android-tools |  GUESS   | https://release-monitoring.org/project/13989 | 4.2.2+git... | 10.0.0_r2    |     
                      argparse |  GUESS   | https://release-monitoring.org/project/02100 | 0.7.0-1      | 1.0.10       |     
                         argus |  GUESS   | https://release-monitoring.org/project/00107 | 3.0.8        | 3.0.8.2      | ORPH
                     armadillo |  GUESS   | https://release-monitoring.org/project/07006 | 7.900.1      | 9.700.2      |     
                   at-spi2-atk |  GUESS   | https://release-monitoring.org/project/07840 | 2.26.2       | 2.33.92      |     
                  at-spi2-core |  GUESS   | https://release-monitoring.org/project/07841 | 2.28.0       | 2.33.92      |     
                         atkmm |  GUESS   | https://release-monitoring.org/project/07962 | 2.24.2       | 2.29.1       |     
                      automake |  DISTRO  | https://release-monitoring.org/project/00144 | 1.15.1       | 1.16.1       | ORPH
[...]

As part of this work, Victor also improved the matching of versions between the Buildroot package versions and the upstream versions. Indeed, for many packages, Buildroot used to use the full Git tag name as the version (for example v1.3), while release-monitoring.org removes any prefix and keeps only 1.3.

As of today, not all Buildroot packages match with a project known by release-monitoring.org, either because release-monitoring.org doesn’t know the project, or because the name is slightly different, but we are improving this progressively (the name mismatch can be handled by creating a mapping on release-monitoring.org, thanks to the concept of distribution they have).

The work of Victor has already proven to be very useful: a number of infrequent contributors suddenly started taking care of the packages they had contributed a long time ago and perhaps forgotten since then, which is very good.

Notifications of defconfig and runtime test failures

Buildroot provides a number of defconfig files, which are example Buildroot configuration for a wide range of hardware platforms (Raspberry Pi, BeagleBone, Qemu emulated machines, NXP or Microchip evaluation boards, and more). These defconfigs offers a very simple way for users to get a minimal Buildroot system up and running on those hardware platforms, making them a great starting point. Of course, to make them useful, they have to build properly, and we regularly build them using Gitlab CI to ensure they continue to build.

Buildroot also has runtime tests, which were initially introduced in the project by your author back in 2017. Those runtime tests are test cases that will each build a specific well-defined Buildroot configuration, boot it under Qemu, and verify that everything works properly. For example, the filesystem test cases will each make a Buildroot build with a specific filesystem image format selected, and boot the result under Qemu, to make sure that the filesystem image is correct and working. We also have a significant number of test cases for Perl or Python modules, which simply build the Perl or Python interpreter with a collection of modules, boot under Qemu, and verify that those modules can be loaded/imported. Just like the defconfigs, these runtime tests are already tested on a regular basis using Gitlab CI, to detect and fix any regression.

However, the results of those tests in Gitlab CI (and especially failures) were not notified to the Buildroot community in a meaningful way. This is where Victor filled in the gap, by adding the appropriate notifications.

He further extended the daily-mail script so that using the Gitlab CI API, the latest Gitlab CI pipelines for the Buildroot project are retrieved, the defconfig and runtime test failures are identified, and the appropriate Buildroot developers and contributors are notified. Indeed, just like packages are referenced in Buildroot’s DEVELOPERS file, the defconfigs and runtime tests are also referenced. The daily-mail script will notify individual developers about the defconfig and runtime tests they take care of, and it will also globally notify the mailing list about all defconfig and runtime test failures.

An example output, visible in this notification e-mail is:


Detail of defconfig failures
----------------------------

                        defconfig |                        link to the job                        | orph?
----------------------------------+---------------------------------------------------------------+------
                amarula_a64_relic |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126211  |      
               arcturus_ucls1012a |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126214  |      
                        bananapro |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126246  |      
                   beaglebone_qt5 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126249  | ORPH 
        engicam_imx6qdl_icore_qt5 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126255  |      
                 imx6-sabresd_qt5 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126282  |      
                       imx6ulpico |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126286  |      
                        imx7dpico |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126288  |      
                    licheepi_zero |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126292  |      
               linksprite_pcduino |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126293  |      
                    orangepi_lite |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126321  |      
                   orangepi_lite2 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126320  |      
                 orangepi_pc_plus |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126326  |      
                    orangepi_zero |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126331  |      
              orangepi_zero_plus2 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126332  |      
                   pc_x86_64_bios |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126334  |      
                    pc_x86_64_efi |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126335  |      
               raspberrypi3_qt5we |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126382  | ORPH 
                            warp7 |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126413  |      
                        warpboard |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126414  | ORPH 


Detail of runtime-test failures
-------------------------------

             runtime-test |                        link to the job                        | orph?
--------------------------+---------------------------------------------------------------+------
...ystemSystemdRoIfupdown |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126471  | ORPH 
...ystemSystemdRoNetworkd |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126473  | ORPH 
...nitSystemSystemdRwFull |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126475  | ORPH 
...ystemSystemdRwIfupdown |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126476  | ORPH 
...ystemSystemdRwNetworkd |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126477  | ORPH 
             TestSyslogNg |  https://gitlab.com/buildroot.org/buildroot/-/jobs/289126581  | ORPH 

Overall, thanks to Victor’s work, a single e-mail now reports autobuilder failures, the need to update packages to a newer upstream versions, defconfig build failures and runtime tests failures. This is a really good improvement in the tooling of the Buildroot community!

Buildroot autobuilder search capabilities

Buildroot provides over 2400 packages, and many of them have configurable features and optional dependencies. This creates a massive amount of possible configuration combinations, making it impossible to test all of them. To make sure as many Buildroot configurations build properly, the project has been running for many years the Buildroot autobuilders. A number of build machines build random Buildroot configurations 24/7, and report their results to autobuild.buildroot.org. This helps tremendously the Buildroot developers and maintainers to detect the problematic packages and configurations.

For a long time, the autobuild.buildroot.org allowed to filter build results by architecture, C library, failing package, and a few other criterias. Such filtering is very often useful to understand when a package started failing to build, and in which situations it fails to build.

autobuild.buildroot.org was also collecting in a database all the configuration symbols (the BR2_something symbols) for every Buildroot configuration that was built. However, the size of this database made any query excessively long, so we were not able to make use of it so far. This was annoying because it would sometimes be useful to ask could you tell me which configuration had BR2_PACKAGE_STRACE=y and built successfully ?.

That’s where Victor jumped in:

  • He improved the database by adding the appropriate indexes and found a reasonably efficient way to query the database when configuration symbols are involved
  • He added filtering per configuration symbol, which can be done using GET arguments on the main autobuild.buildroot.org page: http://autobuild.buildroot.org/?status=OK&symbols[BR2_PACKAGE_STRACE]=y will show the builds that had BR2_PACKAGE_STRACE=y and that ended successfully. Multiple symbols[name]=value arguments can be passed.
  • Since writing such queries by hand was a bit cumbersome, Victor also added a new search page.
Autobuild search page
Autobuild search page

This work will be very useful in the future to analyze build failures and understand better in which situations they are happening.

Conclusion

Victor’s internship has been very productive in improving the tooling used by the Buildroot community to maintain the project. All the work done by Victor has been merged, is in production, and is already showing some useful results.