OpenWrt is described as a Linux distribution for embedded devices.
Based on the Linux kernel, this distribution is primarily used on devices to route network traffic due to the fact that it is born because the Linksys released the source code of the firmware for their WRT54G series of wireless routers under the GNU/GPL license (that's why, the WRT into the name). Then, other chipsets, manufacturers, and device types have been included in turning the initial project into a valid and rock-solid software product.
OpenWrt's main components are the Linux kernel, the uClibc (or musl) C library, and BusyBox. All components have been optimized for size in order to fit into very small memory devices (bare but functional OpenWrt footprint is around 4 MB!). This distribution is known as the best distribution for embedded networking devices.
The distribution has its building system based on a (modified) Buildroot system that automates the building process, thanks to a set of makefiles and patches. The main tool used to manage the distribution is make.
Note
More information on the OpenWrt distribution can be retrieved from the project's home page at: https://openwrt.org .
In the upcoming sections, we will build a minimal image from scratch, and then, we'll show you how you can add some included packages and how to add a new (and simple) package in order to expand the distribution.
Using the default configuration
To install the base system for our SAMA5D3 Xplained board, we can use the OpenWrt default configuration we will show here. However, as the first step, we need to download the sources. This can be done with the git command as follows:
Then move into the just created openwrt directory and execute the configuration menu as below:
$ cd openwrt$ make menuconfig
It may happen that the command ends with an error:
Build dependency: Please install zlib. (Missing libz.so or zlib.h)Build dependency: Please install the openssl library (with development headers)Build dependency: Please install GNU 'awk'Build dependency: Please install the Subversion client/home/giometti/A5D3/openwrt/include/prereq.mk:12: recipe for target 'prereq' failedPrerequisite check failed. Use FORCE=1 to override./home/giometti/A5D3/openwrt/include/toplevel.mk:140: recipe for target 'staging_dir/host/.prereq-build' failedmake: *** [staging_dir/host/.prereq-build] Error 1
In this case, we have to manually add all missing dependencies to be able to compile our new OpenWrt distribution, so in the preceding error, our host PC tells us that several packages are missing. Then, we have to install them using the following command:
How we can deduce the missing packages' names from the output of the preceding configuration command is not a an act of magic, but we used the package management tools of the Ubuntu/Debian OS described at Chapter 2 , Managing the System Console, in Packages management section.
Then, we can relaunch the command and, if all packages are in place, we should get a configuration menu similar to the one we got during the kernel configuration in Chapter 1 , Installing the Developing System, in SAMA5D3 Xplained section. Now, we must select our SAMA5D3 Xplained board by setting Atmel AT91 in the Target System entry, SAMA5D3 (Cortex-A5) for the Subtarget entry, and Atmel AT91SAMA5D3XPLAINED in Target Profile, as shown in the following screenshot:
Before starting the compilation, we need to do a little patch at the OpenWrt sources. In fact, by default, the system will generate a single file for both the SAMA5D3 Xplained's kernel image and the DTB configuration file (the DTB file is actually appended to the kernel image), but since we want two separate files, in order to flash them into their matching MTD partitions, we must apply the following patch:
Now, we are ready, so let's launch the compilation using the following make command:
$ make make[1] world make[2] toolchain/install make[3] -C toolchain/gdb prepare make[3] -C toolchain/gdb compile make[3] -C toolchain/gdb install...
Note
The compilation is very time consuming, so you should consider to take your time to have your preferred coffee! If we got some error, we can use the following command line to enable all compilation messages and force just one task to see what caused the error:
$ make -j1 V=s
In any case, we can use only the V=s settings to normally compile the system, but enabling all messages in order to see what's happening.
When the compilation has finished, we should get the following messages:
Now, we can see the compilation results under the bin/at91/ directory as shown here:
$ cd bin/at91/$ lsmd5sumsopenwrt-at91-sama5d3-AT91SAMA5D3XPLAINED-rootfs.tar.gzopenwrt-at91-sama5d3-root.ext4openwrt-at91-sama5d3-root.jffs2-128kopenwrt-at91-sama5d3-root.jffs2-64kopenwrt-at91-sama5d3-root.ubiopenwrt-at91-sama5d3-root.ubifsopenwrt-at91-sama5d3-sama5-oftree.dtbopenwrt-at91-sama5d3-sama5-uImageopenwrt-at91-sama5d3-uImageopenwrt-at91-sama5d3-zImagepackagessha256sums
The files we have to move to the SAMA5D3 Xplained are:
file openwrt-at91-sama5d3-zImage - the kernel,
file openwrt-at91-sama5d3-sama5-oftree.dtb - the DTB and
file openwrt-at91-sama5d3-root.ubi - the rootfs.
The following command will copy all these files into a dedicated directory of our embedded board:
Note that the nand directory must be already present into the SAMA5D3 Xplained root user's home directory.
Now, we have to compile the bootloader. The OpenWrt has the possibility to do it for us, but this option seems disabled for our board! Then, keep calm and remember that we already compiled the SAMA5D3 Xplained's bootloader into Chapter 1 , Installing the Developing System, in SAMA5D3 Xplained section. We can now redo the same steps, but this time, with two major differences:
We have to use the sama5d3_xplained_nandflash_defconfig target in order to compile our U-Boot image for the NAND flash.
We have to write the result image into the flash itself.
Let's see one step at time. First of all, we have to go into the directory we used to download the U-Boot's source code and reconfigure it for the NAND flash:
$ cd A5D3/u-boot$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sama5d3_xplained_nandflash_defconfig## configuration written to .config#
Then, we have to re-compile it with the usual command:
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
Again, the two bootloaders image files boot.bin and u-boot.img (the board has two bootloaders remember? See Chapter 1 , Installing the Developing System, in SAMA5D3 Xplained section) are created, but this time, they are suitable to be used on the NAND instead of on the microSD. So, let's place them on the SAMA5D3 Xplained into the dedicated directory as we did earlier:
$ scp boot.bin u-boot.img root@192.168.8.2:nand/
Now, under the /root/nand directory on the SAMA5D3 Xplained, we should have all the needed files, and then, we have only to write them into the NAND flash to have a running OpenWrt system. However, before continuing, you should notice that we used the kernel and the rootfs from OpenWrt while the bootloaders have been generated outside OpenWrt! This fact can lead to some misconfigurations that can produce an unbootable system. The problem is about the flash partitioning. In fact, we must be sure that we write all data into the correct place. Let's see how.
Since we will use our Debian OS to set up the OpenWrt image files, we must check the current partitioning. This can be done with the following command:
The preceding output states a NAND partitioning as reported into the following table:
In this scenario, we must be sure that:
U-Boot will load the kernel and the DTB file at the right positions.
the kernel has a compatible setting, that is, the rootfs must be placed into a partition from offset 0x00800000 and 248 MB length at maximum.
These settings must be done into the U-Boot, so we have to stop it the first time we try to execute our OpenWrt to check the current U-Boot's configuration. On the other hand, we are quite sure that boot.bin will safely load u-boot.img due to the fact that they derive from the same compilation and that the former has the correct settings to do it.
Tip
You can verify it by looking at the CONFIG_SYS_NAND_U_BOOT_OFFS value in the include/configs/sama5d3_xplained.h file in the U-Boot's repository.
OK, let's start by flashing the bootloaders, erasing the mtd2 partition, just to be sure to work with a void-saved environment:
And the last step is the rootfs image. However, this time, we cannot use the nandwrite utility due to the fact that it doesn't properly format the flash partition for UBIFS. To do this, we have to use the ubiformat as reported here:
Now, we have to stop the system with the halt command, and then, we must remove the microSD and press the reset button (see Chapter 1 , Installing the Developing System, in SAMA5D3 Xplained section). If everything works well, we should see the following messages on the serial console:
RomBOOTU-Boot SPL 2016.03-dirty (Jun 15 2016 - 16:19:44)Trying to boot from NANDU-Boot 2016.03-dirty (Jun 15 2016 - 16:19:44 +0200)CPU: SAMA5D36Crystal frequency: 12 MHzCPU clock : 528 MHzMaster clock : 132 MHzDRAM: 256 MiBNAND: 256 MiBMMC: mci: 0*** Warning - bad CRC, using default environmentIn: serialOut: serialErr: serialNet: gmac0Error: gmac0 address not set., macb0Error: macb0 address not set.Hit any key to stop autoboot: 1
We have to be quick and stop the autoboot by pressing a key, and then, we can show the U-Boot environment:
The relevant settings here are in the bootcmd and bootargs variables. The first variable defines the commands to load the kernel and the DTB file, and they are correct, while bootargs defines a slightly different settings regarding the UBIFS settings (ubi.mtd) and the flash partitioning for the kernel (mtdparts), so they must be fixed up. Recalling the preceding table, the correct values per the mtdparts and ubi.mtd settings are shown here:
OK! The rootfs has been correctly mounted, so it's time to wait for the console login message:
...[ 3.250000] init: - preinit -/etc/preinit: .: line 1: can't open '/lib/at91.sh'[ 3.400000] procd: - early -[ 4.070000] procd: - ubus -[ 5.100000] procd: - init -Please press Enter to activate this console.
We got it! Now, if we strike the Enter key, we get the following message:
BusyBox v1.23.2 (2016-06-15 13:48:20 CEST) built-in shell (ash) _______ ________ __| |.-----.-----.-----.| | | |.----.| |_| - || _ | -__| || | | || _|| _||_______|| __|_____|__|__||________||__| |____| |__| W I R E L E S S F R E E D O M-----------------------------------------------------CHAOS CALMER (Chaos Calmer, r49378)-----------------------------------------------------* 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:/#
Before ending the paragraph, let me show you the actual flash memory occupation:
As you can easily verify using the just created OpenWrt distribution, its base system is very small and it's also quite useless. That's why we will show you how you can add a (quasi) LAMP system. In fact, we will install a lighttpd web server (that's why, the quasi word) with PHP support and a MySQL server.
To do our job, we should consider that the OpenWrt supports the feeds. They are external repositories based on git repositories useful to add additional packages without touching the main distribution. The feeds are managed by the feeds command placed in the scripts directory. So, first of all, we have to update all feeds' repositories with this command:
$ ./scripts/feeds update -a
Then, we can use the next command to search for the package holding the lighttpd web server:
$ ./scripts/feeds search lighttpdSearch results in feed 'packages':lighttpd A flexible and lightweight web server...lighttpd-mod-cgi CGI modulelighttpd-mod-cml Cache Meta Language modulelighttpd-mod-compress Compress output modulelighttpd-mod-evasive Evasive modulelighttpd-mod-evhost Exnhanced Virtual-Hosting modulelighttpd-mod-expire Expire modulelighttpd-mod-extforward Extract client modulelighttpd-mod-fastcgi FastCGI module...
Then, to install it (belong other useful modules, we can use the following command:
Now, we know the trick, so for MySQL, the commands are as follows:
$ ./scripts/feeds search mysqlSearch results in feed 'packages':freeradius2-mod-sql-mysql MySQL modulelibdbd-mysql MySQL database server driver for libdbilibmysqlclient MySQL client librarylibmysqlclient-r MySQL client library threadsafelibzdb A thread-safe multi database connection poo l librarylighttpd-mod-mysql_vhost Mysql virtual hosting moduleluasql-mysql Lua SQL binding for MySQLmysql-server MySQL Serverphp5-mod-mysql MySQL shared modulephp5-mod-mysqli MySQL Improved Extension shared modulephp5-mod-pdo-mysql PDO driver for MySQL shared module...$ ./scripts/feeds install libmysqlclient libmysqlclient-r mysql-server
Now, we have to enable the compilation of these new packages, and in order to do it, we have to re-execute the make menuconfig command. Then, when the configuration menu appears, we have to choose the entry Network and then Web Servers/Proxies and then enable the lighttpd entry. Hit the Enter key on that entry to enter into the lighttpd menu where we have to select lighttpd-mod-cgi and lighttpd-mod-fastcgi as shown in the following screenshot:
Tip
Note that instead of the Linux configuration menu you saw into Chapter 1 , Installing the Developing System, Linux kernel for SAMA5D3 Xplained, this menu has a slightly different meaning for packages selection (even if they look like the same). In the Linux menu, a kernel component is selected as built-in with an * character, while we select it as module with an M. In this menu, using an M character, we select the program for compilation and packaging only. In other words, we get the program's package in the bin/at91/packages directory only, while using *, we select the program to be placed in the final rootfs image too.
Then, to enable the PHP support, we must go back to the main menu, then select the Languages entry, and then enter into the PHP entry. Then, we have to enable the PHP language support as built-in, and we must enable the PHP plugins for CGI and MySQL, as shown in the following screenshot:
The last settings are for MySQL, so let's go back to the main menu and select the Utilities entry, then database, and just enable the mysql-server entry as built-in.
That's all! Now, we have to re-execute the make command and wait for the end of the compilation. When finished, we'll get a new kernel and a new image to be flashed as shown earlier. So, we have to restart our Debian on the SAMA5D3 Xplained, and then erase and flash the kernel and rootfs filesystem's partitions using the following commands:
Now, just restart the system, and the new OpenWrt image should start as before, This time, we can see that it's slightly bigger (even if is still really small, less than 9MB):
To get access to the internal web server, we have to set up the networking settings. The default configuration can be retrieved by looking at the /etc/config/network file:
root@OpenWrt:/# cat /etc/config/networkconfig interface loopback option ifname lo option proto static option ipaddr 127.0.0.1 option netmask 255.0.0.0config interface lan option ifname eth0 option type noneoption proto staticoption ipaddr 192.168.1.1 option netmask 255.255.255.0config interface debug option ifname usb0 option type none option proto static option ipaddr 172.18.0.18 option netmask 255.255.255.0
We see that only one Ethernet device is configured and also the usb0 device is present, but with a different configuration than our Debian. We can choose several solutions. However we decided to set up the eth0 network device with DHCP, so the relative new settings are shown here:
config interface lanoption ifname eth0option type none option proto dhcp
When all our networking settings are in place, we have to restart the networking systems using the following command:
root@OpenWrt:/# /etc/init.d/network restart[ 1225.880000] macb f0028000.ethernet eth0: link down[ 1227.490000] IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready[ 1227.880000] macb f0028000.ethernet eth0: link up (100/Full)[ 1227.880000] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
Great, now, we can see our new IP address with the usual ifconfig command:
Our web server seems OK. However, you should notice that the default root directory is /www rather than /var/www as for Debian. Then, we have to verify the CGI support configuration, which is placed in the /etc/lighttpd/conf.d/30-cgi.conf file:
root@OpenWrt:/# cat /etc/lighttpd/conf.d/30-cgi.conf########################################################################### CGI modules## ---------------#### http://www.lighttpd.net/documentation/cgi.html##server.modules += ( "mod_cgi" )#### Plain old CGI handling#### For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini.##cgi.assign = ( ".pl" => "/usr/bin/perl", ".cgi" => "/usr/bin/perl", ".rb" => "/usr/bin/ruby", ".erb" => "/usr/bin/eruby", ".py" => "/usr/bin/python" )...
Nope, this time, we need to apply the following patch since the .php extension is missing in the cgi.assign array:
After the modifications are in place, let's restart the server:
root@OpenWrt:/# /etc/init.d/lighttpd restart
Then, we have to point our web browser at the IP address of our SAMA5D3 Xplained board, and we should see something similar to the following screenshot:
Now, it's time to verify the MySQL, and we can try the usual command line to log in as MySQL's root user:
root@OpenWrt:/# mysql -u rootERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld.sock' (2)
Tip
The preceding command and the following ones are typical commands used to manage the MySQL from the command line. You can refer to Chapter 4, Quick Programming with Scripts and System Daemons, MySQL or to the online documentation of The MySQL Command-Line Tool at: http://dev.mysql.com/doc/refman/5.7/en/mysql.html to get more information on these commands.
The database is not running, so let's try to restart the daemon to see what's wrong:
root@OpenWrt:/# /etc/init.d/mysqld start/etc/init.d/mysqld: Error: datadir '/mnt/data/mysql/' in /etc/my.cnf doesn't exist
This is a typical error due the fact that, by default, MySQL takes as datadir the /mnt/data/mysql/ directory, which is usually mounted on a separate filesystem (usually, a microSD storage). No problem. We can change the configuration in the /etc/my.cnf file, so we can take a look at that file where we notice the following warnings:
[client]
port = 3306
socket = /var/run/mysqld.sock
[mysqld]
user = root
socket = /var/run/mysqld.sock
port = 3306
basedir = /usr
############ Don't put this on the NAND #############
# Figure out where I are going to put the databases
# And run mysql_install_db --force
datadir = /mnt/data/mysql/
######### This should also not go on the NAND #######
tmpdir = /mnt/data/tmp/
...
This is because the system heavily uses these directories for disk I/O activity, but since our system has nothing other than NAND, we have no choices (usually, these directories are pointing at an external filesystem on microSD or USB key. However, don't forget that also those devices are based on NAND flashes!). So, a reasonable solution for these directories' settings can be as follows:
datadir = /var/data/mysql/
tmpdir = /tmp/
Then, as suggested earlier, we have to execute mysql_install_db as shown here:
root@OpenWrt:/# mysql_install_db --forceInstalling MySQL system tables...OKFilling help tables...OKTo start mysqld at boot time you have to copysupport-files/mysql.server to the right place for your systemPLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !To do so, start the server, then issue the following commands:/usr/bin/mysqladmin -u root password 'new-password'/usr/bin/mysqladmin -u root -h OpenWrt password 'new-password'Alternatively you can run:/usr/bin/mysql_secure_installationwhich will also give you the option of removing the testdatabases and anonymous user created by default. This isstrongly recommended for production servers.See the manual for more instructions.You can start the MySQL daemon with:cd /usr ; /usr/bin/mysqld_safe &You can test the MySQL daemon with mysql-test-run.plcd /usr/mysql-test ; perl mysql-test-run.plPlease report any problems with the /usr/scripts/mysqlbug script!
Now, the starting command that failed before should now work without any errors:
root@OpenWrt:/# /etc/init.d/mysqld start
Then, we can retry the root login as we did earlier:
root@OpenWrt:/# mysql -u rootWelcome to the MySQL monitor. Commands end with ; or g.Your MySQL connection id is 1Server version: 5.1.73 Source distributionCopyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.mysql>
OK, everything is working, so let's try a little demo with our new (quasi) LAMP system. However, before starting, it is better to add a password for MySQL's root user. So, use the quit command to exit from the previous tools and execute the following command:
In the preceding command, we used the myroot password, but of course, you can choose whatever you want in order to fit your needs.
Since now, to log in to MySQL from the command line, we must use the following command and then insert the new password when asked for:
root@OpenWrt:/# mysql -u root -p
Now, we have to copy the chapter_05/phpdemo/demo_init.sh and chapter_05/phpdemo/demo_set.sh files from the book's example code repository to the /root directory on our SAMA5D3 Xplained:
Then, still from the same repository, the chapter_05/phpdemo/demo_dump.php file in the /www directory:
$ scp demo_dump.php root@192.168.32.51:/www/
Tip
It may happen that the scp command will not accept any root password and then refuse to copy the files. In this case, we have to reset the root's password using the passwd command:
root@OpenWrt:/# passwd Changing password for root Enter the new password (minimum of 5, maximum of 8 characters) Please use a combination of upper and lower case le tters and numbers. New password: Re-enter new password: passwd: password changed.
Then, redo the command.
Then, we can set up the demo database using the demo_init.sh command:
root@OpenWrt:~# ./demo_init.shWarning, all data will be dropped!!! [CTRL-C to stop or ENTER to continue]Enter password:
Of course we have to enter here the MySQL root's password we set up before. Once the database has been set up we have only to add data to it and the command to do it is shown below:
root@OpenWrt:~# ./demo_set.sh temp 21.5
Tip
These commands are quite similar to the ones used in Chapter 4 , Quick Programming with Scripts and System Daemons, in MySQL in Bash section, so you can refer to those pages to know how the commands are functioning.
Now, to display the data, we have only to point our web browser to the demo_dump.php file at SAMA5D3 Xplained's IP address, and the result is reported in the next screenshot:
Of course, if we add new data and then reload the page, we'll get different results:
root@OpenWrt:~# ./demo_set.sh lamp on
Tip
You should take a look at the demo_dump.php file since it's quite similar to the my_dump.php file presented in Chapter 4 , Quick Programming with Scripts and System Daemons, in MySQL in PHP section, where we used the standard PHP's mysql API. However, in demo_dump.php, we used the newest mysqli API, which is going to supersede the old one in few releases.
Adding a custom package
As the last example on OpenWrt, let's see how we can add a new package to our new system. We'll use the famous Hello World example, and we'll see how we can add it into our current distribution and how we can install it on our running system.
The trick is to create a new feed named applications where we can put all our new programs. So, let's create a new directory named helloworld within applications as shown here:
$ cd A5D3/$ mkdir -p applications/helloworld$ cd applications/helloworld/
Then, we have to create Makefile where we will define our new applications to be added to OpenWrt:
include $(TOPDIR)/rules.mk
# Define package's name, version, release and the default package's
# build directory.
PKG_NAME:=helloworld
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
# Define package's section and category inside the OpenWRT system.
# These information are used to manage the package and to display
# it inside the comfiguration menu
define Package/$(PKG_NAME)
SECTION:=apps
CATEGORY:=Applications
TITLE:=The Hello World program
MAINTAINER:=Rodolfo Giometti <giometti@hce-engineering.com>
endef
# Define package's description (long version)
define Package/$(PKG_NAME)/description
This package holds a program that display the "hello world" message
endef
# Set up the build directory in order to be use by the compilation
# stage.
# Our data are not downloaded from a remote site by we have them
# already into the "src" directory, so let's copy them accordingly
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
# Define the package's installation steps after the compilation
# stage has done
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(CP) $(PKG_BUILD_DIR)/$(PKG_NAME) $(1)/usr/bin
endef
# The OpenWRT's main entry
$(eval $(call BuildPackage,$(PKG_NAME)))
Note
The preceding code can be found in the chapter_05/openwrt-helloworld/Makefile file in the book's example code repository.
The content of the file is quite self-explicative. However, the reader should notice that everything is defined using several make macros. The OpenWrt system already has some default macros for the main building steps: downloading, compilation, installation, and so on. Our job is to integrate these default macros with our own ones in order to do the correct steps to build the new package inside the distribution.
Note that the -f option argument can be necessary if a package with the same name is already present.
Now, if we execute the make menuconfig command again, a new entry labeled Applications should be present in the configuration menu. Just select it and then enable our new helloworld program as shown in the following screenshot:
Great! Now, we can use the make command again to compile our new package or, just to avoid a long wait, we can use these commands to compile the program and then install it respectively:
$ make package/helloworld/compilemake[1] package/helloworld/compilemake[2] -C package/libs/toolchain compilemake[2] -C /home/giometti/A5D3/applications/helloworld compile$ make package/helloworld/installmake[1] package/helloworld/installmake[2] -C /home/giometti/A5D3/applications/helloworld install
We can now reflash the system, but we can just install the new package into our running system! In fact, we should find a new package file relative to our Hello World program in the bin/at91/packages directory as follows:
$ tree bin/at91/packages/applicationsbin/at91/packages/applications
\-- helloworld_1.0.0-1_at91.ipk0 directories, 1 file
So, let's move it into our SAMA5D3 Xplained with the usual scp: