Yocto on the ODROID-C2: Using Yocto with Kernel 5.0

The Yocto Project (YP) is an open source collaborative project that helps developers create custom Linux-based systems regardless of the hardware architecture. Yocto is not an embedded Linux distribution, but it instead creates a custom one for you. The project provides a flexible set of tools and a space where embedded developers worldwide can share technologies, software stacks, configurations, and best practices that can be used to create tailored Linux images for embedded and IOT devices; or, anywhere a customized Linux OS is needed. This article describes the basic steps and procedures for building a custom ODROID-C2 Linux image using Linux-5.0.

Prerequisites for Host System Setup

A Linux based build system is required for Yocto project, it supports most of the major Linux desktop and server distributions. A list of all supported Linux distribution can be found at: https://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#detailed-supported-distros. The Yocto project needs certain packages to be installed on the host machine prior to starting a custom Linux system build for target machine. The list of host packages and tools required for a yocto build can be found at: https://www.yoctoproject.org/docs/current/brief-yoctoprojectqs/brief-yoctoprojectqs.html#brief-compatible-distro. For debian based systems, the following packages are required to be installed:

$ sudo apt-get install gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath socat cpio python python3 python3-pip python3-pexpect xz-utils debianutils iputils-ping

Steps for Building a Custom Linux Image for Odroid-C2

Note: The steps below have been tested on Ubuntu 16.04 host system. After performing the host system setup (i.e., installing all the required packages) the next step is to get the source of the yocto project build system. As we are going to use yocto for building our custom image, we need yocto’s reference distribution. Poky is a reference distribution of the Yocto Project. It contains the OpenEmbedded Build System, BitBake and OpenEmbedded Core, as well as a set of metadata to get you started building your own distro. The core layer provides all the common pieces and additional layers change the software stack as needed.

Getting Sources

The following instructions are based on upstream warrior branch. Warrior branch has support for building Linux Kernel 5.0. Download poky reference distribution for yocto.

$ mkdir yocto-odroid
$ cd yocto-odroid
$ git clone -b warrior git://git.yoctoproject.org/poky.git
Download ODROID BSP layer:
$ git clone -b warrior git://github.com/akuster/meta-odroid
Download openembedded layer:
$ git clone -b warrior https://github.com/openembedded/meta-openembedded.git
Once you have downloaded all the sources into the yocto-odroid directory, please make sure the directory structure looks like Figure 1.

Figure 1 – Directory Structure of Yocto build setup

Starting A Build

Once you have all the sources and directory structure as shown in Figure 1, then the build can be started by the following steps: Initialize the build setup:

$ source poky/oe-init-build-env
The above command will create a build directory and move you into that directory. We now have a workspace where we can build an emulator and reference board based images -- e.g. qemuarm. To make an image compatible with our machine (i.e., ODROID-C2) we need to add the ODROID-C2 BSP layer and meta-openembedded layer into the workspace.
$ bitbake-layers add-layer ../meta-odroid/
$ bitbake-layers add-layer ../meta-openembedded/meta-oe/
$ bitbake-layers add-layer ../meta-openembedded/meta-python/
$ bitbake-layers add-layer ../meta-openembedded/meta-networking/
Next, we need to modify the configuration file located in the conf directory inside the build directory. Open the local.conf file located in conf/local.conf using your favourite text editor.
$vi conf/local.conf
Do the following modifications in local.conf file: Search for
MACHINE ?? = “qemux86”
Comment it by putting and # before it or replace it with
MACHINE ?? = “odroid-c2”
Find and Comment the following lines by putting a # before them:
PACKAGECONFIG_append_pn-qemu-system-native = " sdl"
PACKAGECONFIG_append_pn-nativesdk-qemu = " sdl"
Search for the line
EXTRA_IMAGE_FEATURES ?= "debug-tweaks"
append the following after “debug-tweaks”
ssh-server-openssh
such that the line should look as:
EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"
Now, copy the below lines and paste them at the end of local.conf:
PACKAGECONFIG_remove_pn-xserver-xorg = "glamor"
IMAGE_FEATURES_append = " x11 "
DISTRO_FEATURES_append = " opengl x11"
DISTRO_FEATURES_remove = "wayland"

PREFERRED_PROVIDER_virtual/libgl = "mesa-gl"
PREFERRED_PROVIDER_virtual/libgles2 = "mali"
PREFERRED_PROVIDER_virtual/libgles1 = "mali"
PREFERRED_PROVIDER_virtual/egl = "mali"
PREFERRED_PROVIDER_virtual/mesa = "mesa"

IMAGE_INSTALL_append = "libgcc libgcc-dev libstdc++ libstdc++-dev libstdc++-staticdev \
        autoconf automake ccache chkconfig glib-networking \
        packagegroup-core-buildessential pkgconfig  \
        boost cmake zlib glib-2.0 \
        rng-tools \
        logrotate \
        lrzsz \
        watchdog \
        util-linux \
        pciutils \
        usbutils \
"
IMAGE_ROOTFS_EXTRA_SPACE ="2097152"
INHERIT += "extrausers"
EXTRA_USERS_PARAMS = "usermod -P root root; "
Now save and close the local.conf file. The workspace is now ready to start a build. There are several types of target images that can be built using yocto for various use cases. Here, a graphical image based on X11 and matchbox is built. Execute the following command to kick start a build:
$ bitbake core-image-sato
The build will take some time depending upon the processing power of the host machine and your Internet connection speed. It may take from 30 minutes to several hours.

Steps to speed up the build process can be found at: https://www.yoctoproject.org/docs/2.7/dev-manual/dev-manual.html#speeding-up-a-build If, during the build, any error related to “Timer Expired” occurs, for example:

aclocal: error: cannot open /home/gaurav/Downloads/yocto-odroid/build/tmp/work/aarch64-poky-linux/alsa-plugins/1.1.8-r0/recipe-sysroot/usr/share/aclocal/ax_check_mysqlr.m4: Timer expired
autoreconf: aclocal failed with exit status: 1
ERROR: autoreconf execution failed.
WARNING: exit code 1 from a shell command.
ERROR: Function failed: do_configure (log file is located at /home/gaurav/Downloads/yocto-odroid/build/tmp/work/aarch64-poky-linux/alsa-plugins/1.1.8-r0/temp/log.do_configure.9191)
ERROR: Logfile of failure stored in: /home/gaurav/Downloads/yocto-odroid/build/tmp/work/aarch64-poky-linux/mpg123/1.25.10-r0/temp/log.do_configure.9296
Then just simply clean the sstate cache and start the build again from the target image using:
$ bitbake core-image-sato -c cleansstate
$ bitbake core-image-sato

Creating a Bootable SD Card

When the build is successful, the target images can be found in the "tmp/deploy/images/odroid-c2" directory. Shell command line tools like dd can be used to create a bootable SD card. The user needs to be cautious while using this tool. If the wrong device is chosen, it can overwrite a hard disk belonging to the build host. Execute following command in order to write the image file to your SD card.

$ cd tmp/deploy/images/odroid-c2
$ xzcat core-image-sato-odroid-c2.wic.xz | sudo dd of=/dev/sdX bs=4M iflag=fullblock oflag=direct conv=fsync status=progress
Please make sure that sdX points to the right device (i.e., the mounted SD card). You can confirm and find the target SD card by executing this command:
$ dmesg|tail
[19001.706817] mmc0: new high speed SDHC card at address e624
[19001.707251] mmcblk0: mmc0:e624 SU08G 7.40 GiB 
[19001.718156] mmcblk0:
In the above case, the mounted SD card is “mmcblk0”, on some host machines it can also come up as “sdb” or “sdc”.

Figure 2 – ODROID-C2 Running Yocto Project Sato Image

Toggling GPIO in Kernel Space

There are several tutorials and sample code for accessing GPIO. Almost all of them are based on the legacy integer based gpio access. The below sample code shows how to toggle gpio using a new descriptor based gpio access. The code shown below may not be a proper way to access gpio in kernel space as it’s just an example. Based on the below code a character driver can be written to create a node entry in /dev such as /dev/gpio-test and then that node can be used to send commands from userspace to toggle the gpio using kernel space code.

/*File: gpio-toggle.c*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio/driver.h>
#include <dt-bindings/gpio/meson-gxbb-gpio.h>

struct gpio_chip *chip;

static int chip_match_name(struct gpio_chip *chip, void *data)
{
  printk(KERN_INFO "Label: %s", chip->label);
  printk(KERN_INFO "Name: %s", chip->parent->init_name);
  printk(KERN_INFO "OF Node Full Name: %s", chip->of_node->full_name);
  return !strcmp(chip->label, data);
}

int gpio_test_init(void)
{
  int err = 0;

  printk(KERN_DEBUG "Init Called\n");
  chip = gpiochip_find("periphs-banks", chip_match_name);
  if (!chip) {
    printk(KERN_ERR "Cannot Find A GPIO Chip");
    return -ENODEV;
  }
  printk(KERN_DEBUG "Got valid GPIO Chip Total num gpios %d\n",
      chip->ngpio);

  err = chip->get(chip, GPIOX_11);
  printk(KERN_INFO "Before Setting Value %d\n", err);

  err = chip->direction_output(chip, GPIOX_11, 1);
  if (err < 0) { printk(KERN_DEBUG "Error Setting GPIO Direction %d", err); } chip->set(chip, GPIOX_11, 1);

  err = chip->get(chip, GPIOX_11);
  printk(KERN_INFO "After Setting Value %d\n", err);

  mdelay(2000);

  chip->set(chip, GPIOX_11, 0);

  err = chip->get(chip, GPIOX_11);
  printk(KERN_INFO "After Clearing Value %d\n", err);
  return 0;
}

void gpio_test_exit(void)
{
  printk(KERN_DEBUG "Exiting....\n");
}
module_init( gpio_test_init);
module_exit( gpio_test_exit);
MODULE_LICENSE("GPL");
Below is the Makefile to compile the above Kernel Module:
obj-m += gpio-toggle.o

KSRC = </path/to/pre-compiled/kernel-source>

EXTRA_CFLAGS = -I$(KSRC)/drivers/pinctrl/meson
EXTRA_CFLAGS += -I$(KSRC)/drivers/

CFLAGS_gpio-toggle.o := -DDEBUG

all:
  make -C $(KSRC) M=$(PWD) modules
clean:
  make -C $(KSRC) M=$(PWD) clean
Please note that in the above Makefile, the variable KSRC needs to be set so it points to the location/directory where the pre-compiled Linux 5.0 kernel is located. The yocto build system places the compiled linux kernel in:
build/tmp/work/odroid_c2-poky-linux/linux-stable/5.0.6+gitAUTOINC+172634f02e_machine-r0/linux-odroid_c2-standard-build/
The absolute path of the pre-compiled Linux Kernel 5.0 in our case is:
/home/gaurav/Downloads/yocto-odroid/build/tmp/work/odroid_c2-poky-linux/linux-stable/5.0.6+gitAUTOINC+172634f02e_machine-r0/linux-odroid_c2-standard-build
So, the KSRC variable would become:
KSRC = /home/gaurav/Downloads/yocto-odroid/build/tmp/work/odroid_c2-poky-linux/linux-stable/5.0.6+gitAUTOINC+172634f02e_machine-r0/linux-odroid_c2-standard-build
Command to compile and clean the above Kernel Module:
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- clean
Once the above kernel module is compiled successfully, it should produce a .ko file. In our case, it will be gpio-test.ko.

If the ODROID board is connected to a network, transfer the file either using “scp.” Else, if the board is not connected, then minicom’s Xmodem file transfer utility can be used to transfer a file to the target machine. NOTE: the Xmode receive utility package is compiled and installed on our target machine as a part of Yocto build. Connect a LED to pin 13 of 40-pin header J2, as shown below:

Figure 3 – Led Connection on OdroidC2 Board

Then insert the module by executing:

$ insmod gpio-toggle.ko
The LED connected to GPIOX_11 i.e. pin 13 of J2 header on Odroid C2 should turn ON and turn OFF once.

Be the first to comment

Leave a Reply