Author Archives: Hal Martin

About Hal Martin

In my free time I like experiment with hardware and embedded systems. Here I write about personal projects and random adventures into firmware land.

Arch Linux and SDIO WiFi on a Bay Trail tablet

tl;dr If you just came to download the bootable USB stick filesystem to boot your tablet, click here.

You will need to format a USB key (minimum 1GB) with a VFAT/FAT32 filesystem with the label ARCH_201512, unzip the contents of the file to the USB key, and read the section marked Grub near the bottom of this post to boot! It shouldn’t require Linux to set up the USB key.

I highly recommend you make a backup of the tablet before you proceed to install Linux. The easiest/fastest/laziest way I have found is to use dd and pigz to make a block for block backup of the internal EMMC onto an ext4 formatted microSD card (as the archive will exceed the 4GB limit of VFAT).


So, you have a Bay Trail based tablet, in my case a Dell Venue 8 Pro (model 3845), and you want to install Linux on it. Chances are pretty good that your tablet will use SDIO for WiFi, and this means that you will start the installer and quickly realize you have no WiFi. Bummer. Hope you’ve got a USB to Ethernet adapter with you, and a USB OTG hub with 3 ports.

Or, you could compile a custom kernel with patches for the SDIO WiFi chipset, put it into the Arch Linux installer, and then have glorious WiFi for your installation.

I chose the second option, because USB ethernet adapters are slow. And now I will tell you how I did it, so you too can do it too.

First: you need to have a computer which can build a normal Linux kernel. I run Arch Linux also on my laptop, so just install the development tools and you can start:

$ sudo pacman -S base-devel arch-install-scripts squashfs-tools

Go download the latest stable Linux kernel from kernel.org, I used the following: https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.3.2.tar.xz

Then you need to download the source code for the rtl8723bs WiFi chipset module (it is not in mainline yet):
https://github.com/hadess/rtl8723bs

Decompress the Linux source you downloaded earlier:

$ tar -xf linux-4.3.2.tar.xz

And decompress the rtl8723bs driver you downloaded earlier:

$ unzip rtl8723bs-master.zip

Don’t forget to apply the patches from the rtl8723bs driver:

$ cd linux-4.3.2
linux-4.3.2 ~$ patch -p1 < ../rtl8723bs-master/patches/0001-PM-QoS-Add-pm_qos_cancel_request_lazy-that-doesn-t-s.patch 
patching file include/linux/pm_qos.h
patching file kernel/power/qos.c
linux-4.3.2 ~$ patch -p1 < ../rtl8723bs-master/patches/0001-mmc-sdhci-get-runtime-pm-when-sdio-irq-is-enabled.patch    
patching file drivers/mmc/host/sdhci.c
Hunk #1 succeeded at 1731 (offset -13 lines).
Hunk #2 succeeded at 1743 (offset -13 lines).
linux-4.3.2 ~$ patch -p1 < ../rtl8723bs-master/patches/0002-mmc-sdhci-Support-maximum-DMA-latency-request-via-PM.patch 
patching file drivers/mmc/host/sdhci.c
Hunk #2 succeeded at 1402 (offset 2 lines).
Hunk #3 succeeded at 1427 (offset 2 lines).
Hunk #4 succeeded at 2206 (offset 2 lines).
Hunk #5 succeeded at 2279 (offset 2 lines).
Hunk #6 succeeded at 2911 (offset 2 lines).
Hunk #7 succeeded at 3407 (offset 2 lines).
Hunk #8 succeeded at 3472 (offset 2 lines).
Hunk #9 succeeded at 3529 (offset 2 lines).
patching file drivers/mmc/host/sdhci.h
Hunk #2 succeeded at 428 (offset 5 lines).
linux-4.3.2 ~$ patch -p1 < ../rtl8723bs-master/patches/0003-mmc-sdhci-acpi-Fix-device-hang-on-Intel-BayTrail.patch     
patching file drivers/mmc/host/sdhci-acpi.c
linux-4.3.2 ~$ patch -p1 < ../rtl8723bs-master/patches/0004-mmc-sdhci-pci-Fix-device-hang-on-Intel-BayTrail.patch  
patching file drivers/mmc/host/sdhci-pci.c

If any of the patches fail to apply, do not proceed with building the kernel, you will not build a working kernel with SDIO WiFi support.

Moving right along, I stole the stock Arch Linux configuration from the 2015.12 installer ISO and ran make oldconfig to bring it up to date on Linux 4.3.2.

Here is a copy of the .config which you will want to use. The .config is inside the zip file, just move the zip file to the linux-4.3.2 directory and unzip.

Verify that everything is cool with the .config file you decompressed (if you use a newer kernel this will prompt you to answer questions about new features supported which are not in the config file):

linux-4.3.2 ~$ make oldconfig
scripts/kconfig/conf  --oldconfig Kconfig
#
# configuration written to .config
#
linux-4.3.2 ~$ make -j 9

Now wait a really long time. I will never understand why Arch Linux includes kernel modules for USB webcams in their text-only installer media…

Now, while this is happening, download the latest Arch Linux live installation media, because we’re going to open it up and replace the kernel and squashfs:
https://www.archlinux.org/download/

I followed the excellent Arch Wiki instructions to remaster the install ISO:
https://wiki.archlinux.org/index.php/Remastering_the_Install_ISO

Mount the ISO somewhere:

$ mkdir /tmp/archlinux-iso
$ sudo mount -o loop archlinux-2015.12.01-dual.iso /tmp/archlinux-iso

Since I have 16GB of RAM, I just do everything in /tmp because it’s a ramdisk and faster than an SSD:

$ cp /tmp/archlinux-iso/arch/x86_64/airootfs.sfs /tmp/
$ cd /tmp/
$ unsquashfs airootfs.sfs

Now, hopefully by now your kernel has finished building and we can install it to the recently unsquashed install ISO:

linux-4.3.2 ~$ sudo make INSTALL_MOD_PATH=/tmp/squashfs-root modules_install
linux-4.3.2 ~$ sudo cp arch/x86/boot/bzImage /tmp/squashfs-root/boot/vmlinuz

This will install our kernel modules to the squashfs-root folder. Feel free to delete the modules from the previous kernel version if you want to save space (for me this was 4.2.5-1-ARCH):

$ sudo rm -rf /tmp/squashfs-root/lib/modules/4.2.5-1-ARCH/

Now, we need to build the rtl8723bs module:

$ cd rtl8723bs-master
rtl8723bs-master ~$ make KSRC=~/linux-4.3.2 KVER=4.3.2-ARCH
  (output omitted for brevity)
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/hmartin/rtl8723bs-master/r8723bs.mod.o
  LD [M]  /home/hmartin/rtl8723bs-master/r8723bs.ko
make[1]: Leaving directory '/home/hmartin/linux-4.3.2'
rtl8723bs-master ~$ sudo cp r8723bs.ko /tmp/squashfs-root/lib/modules/4.3.2-ARCH/kernel/drivers/net/wireless/
rtl8723bs-master ~$ sudo chmod 0644 /tmp/squashfs-root/lib/modules/4.3.2-ARCH/kernel/drivers/net/wireless/r8723bs.ko
rtl8723bs-master ~$ sudo cp -n rtl8723bs_nic.bin /tmp/squashfs-root/lib/firmware/rtlwifi/rtl8723bs_nic.bin
rtl8723bs-master ~$ sudo cp -n rtl8723bs_wowlan.bin /tmp/squashfs-root/lib/firmware/rtlwifi/rtl8723bs_wowlan.bin

Okay, now we need to chroot into the decompressed squashfs filesystem to create an initrd. We need to modify /etc/mkinitcpio.conf in the squashfs root so we can generate an initrd with the correct modules and options, otherwise your tablet won’t boot with the new kernel:

$ sudo arch-chroot /tmp/squashfs-root
(chroot) $ depmod -a 4.3.2-ARCH
(chroot) $ vi /etc/mkinitcpio.conf
- MODULES=""
+ MODULES="r8723bs"
- HOOKS="base udev autodetect modconf block filesystems keyboard fsck"
+ HOOKS="base udev memdisk archiso_shutdown archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs archiso_k
ms block pcmcia filesystems keyboard"
- #COMPRESSION="xz"
+ COMPRESSION="xz"

Earlier we installed the 4.3.2-ARCH kernel modules, and also copied the kernel to /boot/ within the decompressed squashfs filesystem. Now we are going to use the modules, the vmlinuz kernel in /tmp/squashfs-root/boot/, and the above modifications to the /etc/mkinitcpio.conf file to generate a new initrd which we will call archiso.img:

(chroot) $ mkinitcpio -k /boot/vmlinuz -c /etc/mkinitcpio.conf -g /boot/archiso.img -k 4.3.2-ARCH
==> Starting build: 4.3.2-ARCH
  -> Running build hook: [base]
  -> Running build hook: [udev]
  -> Running build hook: [memdisk]
  -> Running build hook: [archiso_shutdown]
  -> Running build hook: [archiso]
  -> Running build hook: [archiso_loop_mnt]
  -> Running build hook: [archiso_pxe_common]
==> WARNING: Possibly missing firmware for module: liquidio
  -> Running build hook: [archiso_pxe_nbd]
  -> Running build hook: [archiso_pxe_http]
  -> Running build hook: [archiso_pxe_nfs]
  -> Running build hook: [archiso_kms]
  -> Running build hook: [block]
==> WARNING: Possibly missing firmware for module: wd719x
==> WARNING: Possibly missing firmware for module: aic94xx
  -> Running build hook: [pcmcia]
  -> Running build hook: [filesystems]
  -> Running build hook: [keyboard]
==> Generating module dependencies
==> Creating xz-compressed initcpio image: /boot/archiso.img
==> Image generation successful

Pack the contents of squashfs-root back into a squashfs image:

/tmp ~$ mksquashfs squashfs-root airootfs.sfs

Okay, now it’s time to create the USB boot media. You will need at least a 1GB USB key for this, and you will lose all the data current on the USB key.

If your stick was previously formatted with a FAT32 partition, skip this step:

$ sudo fdisk /dev/sdX

Welcome to fdisk (util-linux 2.27.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xfa02f14c.

Command (m for help): o
Created a new DOS disklabel with disk identifier 0xf1b89f31.

Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-2097151, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-2097151, default 2097151): 

Created a new partition 1 of type 'Linux' and of size 1023 MiB.

Command (m for help): t
Selected partition 1
Partition type (type L to list all types): c
Changed type of partition 'Linux' to 'W95 FAT32 (LBA)'.

Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.

Now format and mount the USB key, this will erase all data on the USB key:

$ sudo mkfs.vfat -n ARCH_201512 /dev/sdX1
$ mkdir /tmp/archlinux-usb
$ sudo mount /dev/sdX1 /tmp/archlinux-usb

Copy the contents of the Arch installation ISO you mounted earlier to the USB key:

$ sudo cp -R /tmp/archlinux-iso/* /tmp/archlinux-usb/

Now, we need to replace the kernel, initrd, and squashfs filesystem on the USB key with the ones we made:

$ sudo cp /tmp/squashfs-root/boot/vmlinuz /tmp/archlinux-usb/arch/boot/x86_64/vmlinuz
$ sudo cp /tmp/squashfs-root/boot/archiso.img /tmp/archlinux-usb/arch/boot/x86_64/archiso.img
$ sudo cp /tmp/airootfs.sfs /tmp/archlinux-usb/arch/x86_64/airootfs.sfs
$ cd /tmp
/tmp ~$ echo $(md5sum airootfs.sfs) | sudo tee /tmp/archlinux-usb/arch/x86_64/airootfs.md5

Feel free to delete the i686 squashfs, since we did not compile an i686 kernel:

$ sudo rm /tmp/archlinux-usb/arch/i686/airootfs.*

If you’re building the boot media yourself, you will also need to put bootia32.efi in /tmp/archlinux-usb/EFI/boot/bootia32.efi since Bay Trail tablets only have 32-bit UEFI (the CPU is 64-bit). Download bootia32.efi here.


In summary:

  1. We downloaded Linux kernel from kernel.org
  2. We downloaded the rtl8723bs driver from GitHub
  3. We applied the patches required for SDIO from rtl8723bs to the kernel source
  4. We compiled the kernel and modules using the default Arch Linux .config file
  5. We decompressed the squashfs filesystem present on the Arch Linux ISO
  6. We installed the kernel modules compiled earlier
  7. We compiled and installed the r8723bs kernel module in the decompressed squashfs filesystem
  8. We used chroot to run depmod and generate a new initrd using mkinitcpio inside the decompressed squashfs filesystem
  9. (optional) We deleted old kernel modules from the decompressed squashfs filesystem
  10. We recompressed the squashfs filesystem
  11. We formatted our USB installation media
  12. We copied the unmodified Arch Linux ISO contents to the USB installation media
  13. We replaced vmlinuz, initrd (archiso.img). and the x86_64 compressed squashfs filesystem on the USB installation media
  14. We installed bootia32.efi on the USB installation media

Grub

There is an issue with the install media which I haven’t bothered to diagnose. Grub will not display the normal boot menu, so you have to type in the commands manually. You need a keyboard anyway to configure WiFi and start SSH, so you might as well get one out now…

set root=hd0,msdos1
linux /arch/boot/x86_64/vmlinuz archisobasedir=arch archisolabel=ARCH_201512 nomodeset
initrd /arch/boot/x86_64/archiso.img
boot

Wireless

If all goes well, you will have a booted tablet with a wlan0 device. Follow the Arch instructions to configure wireless.

Or, create /etc/wpa_supplicant/MyNetwork.conf with your network details:

ctrl_interface=/var/run/wpa_supplicant
update_config=1
country=US

network={
  ssid="MyNetwork"
  psk="Staple Horse Battery XKCD"
}

Up the interface with wpa_supplicant:

$ wpa_supplicant -Dnl80211 -iwlan0 -c/etc/wpa_supplicant/MyNetwork.conf

If all goes well, wpa_supplicant will find and connect to your network, but you still won’t have an IP address, so switch to another TTY (e.g. ctrl+alt+F2) and run dhclient to get an IP address:

$ dhclient wlan0

Set a root password and start SSH:

$ passwd
$ systemctl start sshd

Find the IP address of your tablet:

$ ip addr

Now you should be able to SSH to your tablet from another computer, and complete the installation (I have censored my MAC addresses):
venue_8_pro_archiso_wlan-clean


Notes: I haven’t actually installed Arch Linux on my Dell Venue 8 Pro (3845) yet. I need to use it over the holidays and want it to work. I will try to post a follow up in the next few months about my experience installing and using Arch Linux on it.

Also, I did this and wrote the post in one afternoon. Usually when I post something here, I work on it for several days and then sit on the draft in case there are any mistakes. However, since I am leaving for Christmas vacation shortly, I wanted to get this out quickly so people could read it over the holidays. There may be errors or omissions in the article which prevent it from working exactly as written. If I discover any errors, I will update the article to correct them.

Build and package your own software for OpenWRT

Today I am going to discuss how to build and package your own software for OpenWRT.

When I say “your own software” in this case I am referring to a C program which you want to cross-compile for the target SoC and install using the opkg package manager included in OpenWRT.

The program I wrote is a little more complicated than your standard “Hello World” application. Here’s what I wanted to do:
1) use libconfig to read a configuration file in /etc/config/ and then perform actions based on the configuration described in this file
2) use sqlite3 to create a database
3) write some meaningful data to the database

Here’s the program flow:
1) Open /etc/config/example-sqlite and read the values into variables
2) Open (or create) a new SQLite3 database file at the location defined in the above configuration file
3) Determine if the SQLite file is initialized with the target table we want to write to, and if not, create the table
4) Write the system load average to the database
5) Quit

To recap, this program is different from “Hello World” in the following ways:
1) It must read and understand a configuration file in libconfig syntax; this requires linking against the libconfig library, which we must tell opkg is a dependency
2) It must create or open an SQLite 3 database; this requires linking against the sqlite3 library, which we must tell opkg is a depenedency
3) It must perform some useful operations on this SQLite file

Let’s start with compiling the C file on your native architecture. Sure, you can just use cc/gcc from bash, but this isn’t any good to OpenWRT SDK, which expects that each package will have a makefile which can be used to compile the software.

load2sqlite.c

#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <libconfig.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char *argv[]) {
// ...

Most importantly above, we are including sqlite3.h for SQLite support, libconfig.h, and sys/stat.h, fcntl.h,errno.h to check if the SQLite3 database file exists or not.

You can compile this by hand quite easily, just by doing:
cc load2sqlite.c -lsqlite3 -lconfig -o load2sqlite

Okay, but how do we make this ready for OpenWRT SDK? By writing a makefile!

makefile

PROFILE = -O2 -s
CFLAGS = $(PROFILE)
LDFLAGS = -lsqlite3 -lconfig

all: main

# build it
main:
	$(CC) $(CFLAGS) load2sqlite.c $(LDFLAGS) -o load2sqlite

# clean it
clean:
	rm load2sqlite

Okay, so now if you type make in the directory, magically you will end up with an executable called load2sqlite!

But, this is a native binary, and it’s somewhat unlikely that your OpenWRT device is on the same architecture.

load2sqlite: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9661b88e92b553d0556cbeeafccf04d2526c770f, stripped

If you run it, you’ll see that it looks for the sqlite database file, can’t find it, and so initalizes a new one with the “readings” table.

[hmartin@localhost src]$ ./load2sqlite 
Database file /tmp/sqlite3.db does not exist
Initialized database with readings table
[hmartin@localhost src]$ echo "select * from readings;" | sqlite3 /tmp/sqlite3.db 
2015-10-28 22:48:42|0.57|0.56|0.57

And if you run it again, without removing the SQLite3 file that was created, you’ll see this output:

[hmartin@localhost src]$ ./load2sqlite
SQLite database opened
Found readings table
[hmartin@localhost src]$ echo "select * from readings;" | sqlite3 /tmp/sqlite3.db 
2015-10-28 22:48:42|0.57|0.56|0.57
2015-10-28 22:49:00|0.47|0.54|0.57

Before we proceed further, I want to show you the directory structure so you have an idea of where we just were when we did this compilation. We are currently in the the src directory.

load2sqlite/
|-- Makefile
|-- README
`-- src
    |-- load2sqlite.c
    |-- load2sqlite.conf
    `-- makefile

Now let’s move up to the load2sqlite directory and work on the OpenWRT Makefile (seen above).

Here is the complete file, and then we will discuss it section by section:
Makefile

#
# Copyright (C) 2006-2015 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

include $(TOPDIR)/rules.mk

PKG_NAME:=load2sqlite
PKG_VERSION:=1.0.1
PKG_RELEASE:=5
PKG_MAINTAINER:=Hal Martin 
PKG_LICENSE:=GPL-2
PKG_CONFIG_DEPENDS:=libsqlite3 libconfig

include $(INCLUDE_DIR)/package.mk

PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)

TARGET_LDFLAGS+= \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib/libconfig/lib \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib/sqlite/lib

define Package/load2sqlite
  SECTION:=utils
  CATEGORY:=Utilities
  DEPENDS:=+libsqlite3 +libconfig
  TITLE:=SQLite example program, creates or opens a user defined SQLite database
  URL:=https://github.com/halmartin/load2sqlite
  MENU:=1
endef

define Package/load2sqlite/description
 Example SQLite is a sample program built using libsqlite3 and libconfig
 which creates or opens a user-defined SQLite3 database and performs some
 simple verification checks on the file to ensure that the target table (readings)
 exists, and if not creates the table, then inserts a row with the current system
 time, and the load (1 minute, 5 minute, 15 minute).
endef

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Configure
endef

define Build/Compile
	$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef

define Package/load2sqlite/install
	$(INSTALL_DIR) $(1)/bin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/load2sqlite $(1)/bin/
	$(INSTALL_DIR) $(1)/etc/config
	$(INSTALL_CONF) $(PKG_BUILD_DIR)/load2sqlite.conf $(1)/etc/config/load2sqlite
endef

$(eval $(call BuildPackage,load2sqlite))

If you clone the OpenWRT source and take a look at basically any package, you’ll see a Makefile that looks similar to the one above.

Let’s look at the package information:

PKG_NAME:=load2sqlite
PKG_VERSION:=1.0.1
PKG_RELEASE:=5
PKG_MAINTAINER:=Hal Martin 
PKG_LICENSE:=GPL-2

Here is where we define core details of our package, such as the name (e.g. what opkg will know it as), the version (useful for upgrading later), maintainer, and license.

TARGET_LDFLAGS+= \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib/libconfig/lib \
  -Wl,-rpath-link=$(STAGING_DIR)/usr/lib/sqlite/lib

Since we want to build a program which links against external libraries, we must also tell the compiler where to find the header files for these libraries, so that the linking process does not fail during compilation. Above you can see that we are linking to libconfig and sqlite libraries.

define Package/load2sqlite
  SECTION:=utils
  CATEGORY:=Utilities
  DEPENDS:=+libsqlite3 +libconfig
  TITLE:=SQLite example program, creates or opens a user defined SQLite database
  URL:=https://github.com/halmartin/load2sqlite
  MENU:=1
endef

This is where you define the package for the OpenWRT build system and declare things like dependencies, and the description that will be present when you run menuconfig (which is how you will select your package to be built as part of an image).

Without declaring dependencies, you may find that you can build, package, and install your software, but it won’t run! So, by declaring the dependencies (packages which provide the libraries we link against) we ensure that when we type opkg install load2sqlite and libconfig and libsqlite3 are not installed, opkg knows to go and install them before installing our program. Now we can safely run the program because all the required libraries are installed on the device!

define Build/Prepare
	mkdir -p $(PKG_BUILD_DIR)
	$(CP) ./src/* $(PKG_BUILD_DIR)/
endef

define Build/Configure
endef

Since our utility is quite simple, as *NIX software goes, the preparation steps are to create the build directory and copy the source from the source directory to the build directory. Since there is nothing to configure in our sample program, the configure step is empty (otherwise the OpenWRT build system will attempt to configure the package and fail because we haven’t bothered to implement this).

define Build/Compile
	$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef

define Package/load2sqlite/install
	$(INSTALL_DIR) $(1)/bin
	$(INSTALL_BIN) $(PKG_BUILD_DIR)/load2sqlite $(1)/bin/
	$(INSTALL_DIR) $(1)/etc/config
	$(INSTALL_CONF) $(PKG_BUILD_DIR)/load2sqlite.conf $(1)/etc/config/load2sqlite
endef

Finally, compile and install the software. As you can see above, I didn’t include an install directive in the makefile of the application, it is instead done manually within the OpenWRT Makefile. This is your choice, since I was designing this program specifically to run on OpenWRT, I saw no need to incorporate the installation steps in the makefile of the program.

And, finally:

$(eval $(call BuildPackage,load2sqlite))

This line is required for OpenWRT to build the package. Forget this line, and you will sit there wondering why your package is not being built!


Okay, now we have prepared our software to be built for OpenWRT. It would be stupid of me to get this far and not tell you how to compile it using the OpenWRT toolchain!

Following the excellent OpenWRT documentation, we need to set up a buildroot.

Install the dependencies (instructions for Debian/Ubuntu):

sudo apt-get install git-core build-essential libssl-dev libncurses5-dev unzip subversion mercurial

Clone the OpenWRT Chaos Calmer release:

git clone git://git.openwrt.org/15.05/openwrt.git

I find that the stock OpenWRT repository is a bit light on some of the software I like to have on my routers, so I take step 3 and install the additional feeds:

cd openwrt
./scripts/feeds update -a
./scripts/feeds install -a

Follow step 4 to ensure you have all the required dependencies installed on your host system!

make defconfig
make prereq
# don't forget to copy load2sqlite to package/utils/ before running this step, or the package won't appear in the menu!
make menuconfig

If everything has gone well thus far (e.g. no errors in the OpenWRT Makefile, and you put load2sqlite in package/utils/ then you should see the following in your menuconfig:

menuconfig_load2sqlite

menuconfig_load2sqlite_desc

Now I already have an official OpenWRT build installed on my router, so I don’t need to build an entire image, just the package I want to install. To do this, we must first build the cross compilation toolchain required to compile for a different CPU architecture.

Warning: the OpenWRT buildroot is around 6GB on disk, so ensure you have the necessary space before starting!

make tools/install
# this will take a while the first time
make toolchain/install
# this will also take a while the first time

When we have the tools and toolchain compiled, we can compile our package:

make package/load2sqlite/compile

This will create an ipkg file in bin/ramips/packages/base/load2sqlite_1.0.1-5_ramips_24kec.ipk which we need to copy to our router to install:

scp bin/ramips/packages/base/load2sqlite_1.0.1-5_ramips_24kec.ipk [email protected]:/tmp/
# scp completes
ssh [email protected]
[email protected]'s password:

BusyBox v1.23.2 (2015-07-25 03:03:02 CEST) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 CHAOS CALMER (15.05, r46767)
 -----------------------------------------------------
  * 1 1/2 oz Gin            Shake with a glassful
  * 1/4 oz Triple Sec       of broken ice and pour
  * 3/4 oz Lime Juice       unstrained into a goblet.
  * 1 1/2 oz Orange Juice
  * 1 tsp. Grenadine Syrup
 -----------------------------------------------------
root@OpenWrt:~# opkg install /tmp/load2sqlite_1.0.1-5_ramips_24kec.ipk 
Installing load2sqlite (1.0.1-4) to root...
Installing libsqlite3 (3081101-1) to root...
Downloading http://downloads.openwrt.org/chaos_calmer/15.05/ramips/mt7620/packages/packages/libsqlite3_3081101-1_ramips_24kec.ipk.
Installing libpthread (0.9.33.2-1) to root...
Downloading http://downloads.openwrt.org/chaos_calmer/15.05/ramips/mt7620/packages/base/libpthread_0.9.33.2-1_ramips_24kec.ipk.
Installing libconfig (1.4.9-1) to root...
Downloading http://downloads.openwrt.org/chaos_calmer/15.05/ramips/mt7620/packages/base/libconfig_1.4.9-1_ramips_24kec.ipk.
Configuring libpthread.
Configuring libconfig.
Configuring libsqlite3.
Configuring load2sqlite.

Now that our package is installed, we can test it!

root@OpenWrt:~# /bin/load2sqlite 
Database file /tmp/sqlite3.db does not exist
Initialized database with readings table

If you install sqlite3-cli we can inspect the row added to the file:

root@OpenWrt:~# opkg install sqlite3-cli
root@OpenWrt:~# echo "select * from readings;" | sqlite3 /tmp/sqlite3.db 
2015-10-31 20:47:33|0.76|0.4|0.25

Since this is just an example program, it is one-shot (e.g. not a daemon). If you really do want to track the load of our OpenWRT router, just add /bin/load2sqlite to crontab (e.g. every hour) and you’ll have this tracking info in the SQLite database.

If you run it multiple times, you get another row added to the file each time the program is run:

root@OpenWrt:~# /bin/load2sqlite 
SQLite database opened
Found readings table
root@OpenWrt:~# echo "select * from readings;" | sqlite3 /tmp/sqlite3.db 
2015-10-31 20:47:33|0.76|0.4|0.25
2015-10-31 20:53:43|0.02|0.2|0.22
2015-10-31 21:23:19|0.08|0.04|0.05

Note that by default the file is saved to /tmp/, which on OpenWRT is a ramdisk. This means that the file will be lost when you reboot, or if you leave it running unattended for too long, the file size will grow to the point where the ramdisk will consume all available memory and the router will crash. For this reason, I suggest you modify the configuration file /etc/config/load2sqlite to point to non-volatile storage (such as a USB stick).


Source code: https://github.com/halmartin/load2sqlite


Why write another OpenWRT software guide?

Well, while I was looking for resources on how to build and package software for OpenWRT, I ran into a lot of posts about people compiling simple “Hello World” programs for OpenWRT, but for my particular use case, I wanted to utilize multiple libraries in my program, and I couldn’t find any good instructions on how to compile a program with linked libraries for OpenWRT.

Disclaimer: I’m not a C expert, so maybe there are some headers there which are not strictly necessary, but it works for me and the executable size is quite small.

If you wish to further reduce the size of your executable, you can tell the compiler to strip it of the symbol table and relocation information. Do this by appending -s to the PROFILE line in the makefile. When I did this on my laptop, the output went from 9.9KB to 7.0KB, or a savings of 30%

I have tested this on Chaos Calmer (15.05), and I expect the instructions would also work on Barrier Breaker (14.07) however I didn’t try this, so I cannot say certainly that it will work.

Building Linux 4.1 for the Banana Pi

This post is a follow up to my original post Building BananaPi LeMaker Kernel.

If you’re just looking for a vanilla Debian or Ubuntu image for your Banana Pi that utilizes a Linux kernel newer than 3.4.xxx, then stop reading and go to this page maintained by Igor Pečovnik. He provides pre-built Debian and Ubuntu images for a variety of Banana Pi boards.

If you want to manually build an image, he has put the build scripts he uses up on his GitHub repository. While I tried to do everything manually starting from my last post, I ended up building a kernel that would not boot. So I shamelessly stole the kernel configuration from Igor, and the resulting kernel boots.

The GMAC driver which required so much patching for the 3.4 kernel was mainlined in 3.17. As such, these instructions should work for any kernel newer than 3.17. I am building 4.1.3 in my script.

Here is the Jenkins/bash script to build the kernel, modules, and boot goodness you need (a direct link to the .sh file is at the end of the post):

if [ ! -d "linux-4.1.3" ]; then
wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.3.tar.xz
tar -Jxvf linux-4.1.3.tar.xz
fi
cd linux-4.1.3
wget https://watchmysys.com/blog/wp-content/uploads/2015/07/banana-pi-linux-4.1.3-config.txt -O .config
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LOADADDR=0x40008000 zImage dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=output modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=output modules_install
mkdir -p output/boot/
cp arch/arm/boot/zImage output/boot/
cp arch/arm/boot/dts/sun7i-a20-bananapi.dtb output/boot/
cat > output/boot/boot.cmd < output/boot/uEnv.txt << EOF
fatload mmc 0 0x46000000 zImage
fatload mmc 0 0x49000000 sun7i-a20-bananapi.dtb
setenv bootargs console=ttyS0,115200 [earlyprintk] root=/dev/mmcblk0p2 rootwait panic=10 rootfstype=ext4 rw ${extra}
bootz 0x46000000 - 0x49000000
EOF
mkimage -C none -A arm -T script -d output/boot/boot.cmd output/boot/boot.scr
cd ..
if [ ! -d "u-boot-2015.04" ]; then
wget ftp://ftp.denx.de/pub/u-boot/u-boot-2015.04.tar.bz2
tar -jxvf u-boot-2015.04.tar.bz2
fi
cd u-boot-2015.04
make -s CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- Bananapi_defconfig
make -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
cp u-boot-sunxi-with-spl.bin ../linux-4.1.3/output/boot/
cd ../linux-4.1.3/
cat > output/boot/uEnv.txt << EOF
fatload mmc 0 0x46000000 zImage
fatload mmc 0 0x49000000 sun7i-a20-bananapi.dtb
setenv bootargs console=ttyS0,115200 [earlyprintk] root=/dev/mmcblk0p2 rootwait panic=10 rootfstype=ext4 rw ${extra}
bootz 0x46000000 - 0x49000000
EOF
tar -C output -cjvf ../linux-bananapi-4.1.3.tar.bz2 boot/ lib/

Here is what the partition layout of my SDHC card:

root@bpi:~# fdisk -l /dev/mmcblk0

Disk /dev/mmcblk0: 7948 MB, 7948206080 bytes
4 heads, 16 sectors/track, 242560 cylinders, total 15523840 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

        Device Boot      Start         End      Blocks   Id  System
/dev/mmcblk0p1   *        2048      133119       65536    c  W95 FAT32 (LBA)
/dev/mmcblk0p2          133120    15523839     7695360   83  Linux

Here is the contents of the boot partition (mmcblk0p1, vfat):

root@bpi:~# ls /boot
boot.cmd  boot.scr  sun7i-a20-bananapi.dtb  uEnv.txt  zImage

boot.cmd

fatload mmc 0 0x46000000 zImage
fatload mmc 0 0x49000000 sun7i-a20-bananapi.dtb
setenv bootargs console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait panic=10 
bootm 0x46000000 - 0x49000000

uEnv.txt

fatload mmc 0 0x46000000 zImage
fatload mmc 0 0x49000000 sun7i-a20-bananapi.dtb
setenv bootargs console=ttyS0,115200 [earlyprintk] root=/dev/mmcblk0p2 rootwait panic=10 rootfstype=ext4 rw ${extra}
bootz 0x46000000 - 0x49000000

You will need to update u-boot on the SD card to v2015.04. If you use the script I provide above, this file is in boot/u-boot-sunxi-with-spl.bin. You need to write it to the SD card using dd:

dd if=boot/u-boot-sunxi-with-spl.bin of=/dev/mmcblk0 bs=1024 seek=8

More notes:
I was unable to build this kernel running Debian 7 (Wheezy) because binutils is too old. Unfortunately the official repositories do not have a newer version available for Wheezy (slash) I was too lazy to look for a repository that might have a newer version. As such, I upgraded by Jenkins build box to Debian 8 (Jessie) to build this kernel.

Additionally, I had to upgrade from a 1GB SD Card in my Banana Pi to an 8GB SDHC Card because the new u-boot does not seem to like small (non-SDHC) cards.

Banana Pi info:

root@bpi:~# free -m
             total       used       free     shared    buffers     cached
Mem:           996         72        923          0          4         19
-/+ buffers/cache:         48        948
Swap:            0          0          0
root@bpi:~# uname -a
Linux bpi 4.1.3-bananapi #2 SMP Sun Jul 26 15:41:54 CEST 2015 armv7l GNU/Linux
root@bpi:~# lsmod
Module                  Size  Used by
root@bpi:~#

Speed test of the network interface:

# Laptop with Gigabit wired connection, saving to /tmp/ ramdisk
[derp@laptop ~]$ nc -lp 5000 | dd of=/tmp/zerofile
# Banana Pi
root@bpi:~# dd if=/dev/zero bs=1M count=2000 | nc laptop 5000
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB) copied, 73.5536 s, 28.5 MB/s

Yes, it is a Gigabit link, but not the fastest. It does seem to be quite stable, and since I am not using my Banana Pi for a bandwidth intensive purpose, this speed is fine for me.

The build script does everything with relative paths, and can be run as a normal user. The output is a tar.bz2 archive containing u-boot binary, boot folder, and kernel modules. You will need sudo/root to install the u-boot bin file with dd as described above.

Download kernel .config: here
Download build script: here
Download u-boot 2015.04: here (SHA1SUM: 8bf4f738ba8aa18ab5d45fca324587f0749f7c10)
Download tar archive with u-boot, kernel, and modules: here (SHA1SUM: d94a8da66ce6d77a1ceb4569740cadf2c8c67e72)