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.gitDownload ODROID BSP layer:
$ git clone -b warrior git://github.com/akuster/meta-odroidDownload openembedded layer:
$ git clone -b warrior https://github.com/openembedded/meta-openembedded.gitOnce you have downloaded all the sources into the yocto-odroid directory, please make sure the directory structure looks like Figure 1.
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-envThe 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.confDo 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-opensshsuch 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-satoThe 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.9296Then 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=progressPlease 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”.
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) cleanPlease 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-buildSo, 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-buildCommand to compile and clean the above Kernel Module:
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- cleanOnce 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:
Then insert the module by executing:
$ insmod gpio-toggle.koThe 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