MicroZed WiFi – Petalinux

Home  >>  Bootseeds Home  >>  Bootseeds Projects  >>  MicroZed WiFi – Petalinux

Synopsis

This tutorial describes steps to get a USB WiFi dongle working with the Avnet MicroZed 7010 board under Petalinux. It is assumed that readers are somewhat familiar with Xilinx Vivado and Petalinux environments and that the 2015.4 version of both have been installed and setup properly. It is also assumed that you have downloaded and extracted the Avnet MicroZed board definition files (see link below under “Prerequisites”) into the recommended location in the README file at the top level archive. That aside, we will attempt to make this tutorial as step-by-step as possible.

We’ll post a link to the source for this project as time permits.

Prerequisites

The following software versions were used for this implementation:

The following hardware is used for this implementation:

Readers should also refer to the following document(s):

We get started by creating a logical directory structure to contain the various components for this implementation:

We have used this basic structure on numerous Vivado projects and have found it to lend itself well to configuration management. The key directories are:

  • zed/hw/viv_prj – this will contain the Vivado Zynq platform design
  • zed/sw/peta_prj – this will contain the Petalinux project (NOTE: this directory will be generated during a step later in the tutorial)
  • zed/sw/wifi_cc – this is where we will cross compile the required WiFi components

Create New Vivado Project

You must first create a Vivado base Zynq system to deploy your WiFi enabled Linux project on. If you have not already created your Zynq processing platform for the MicroZed in Vivado or if you already know how to do this then you can skip ahead to the section Create Petalinux Project. Otherwise go through the Zed WiFi Base Platform Setup Tutorial first.

Create Petalinux Project

This section describes how to setup the Petalinux project and dependencies for a WiFi enabled MicroZed. First cd to the directory “zed/sw” so that you can run the Petalinux project creation script.

Step 1:

Create the Petalinux project:
petalinux-create -t project -n peta_prj --template zynq

    -t specifies the type as project
    -n specifies the name for the project
    –template zynq specifies that we are targeting a zynq platform

Now cd into the newly created “peta_prj” directory. All petalinux commands relating to this project must be executed from within this directory. The Petalinux build environment for the project is setup to use this directory as its root location.

Step 2:

Now import the hardware description from the base system that was exported during the final step of the Zed WiFi Base Platform Setup Tutorial
petalinux-config --get-hw-description ../../hw/export

Step 3:

If this is the first time that petalinux-config is run, or if something in the hardware config will change the underlying .config file, then the configuration menu will open and you will need to set things up per your platform requirements. If you decide you want to change the configuration at anytime just run “petalinux-config” and the config menu will run again.

Step 4:

On this system we’ll be using the on board QSPI Flash to hold the FPGA bit definition file, the u-boot image, the boot-env, and the kernel. You could alternatively do any number of configurations where components are stored in QSPI or on the SD card. But in a deployment scenario if we can fit the kernel in the QSPI and reserve the SD card for non-volatile user file system storage; that is the ideal scenario.

To set this up you will need to select the primary flash and the flash target for a number of the menus. First go to the “Flash Settings” menu:

If the primary flash specifies something other than the QSPI, select that menu and change the primary flash to the QSPI.

Step 5:

Now go into the “Advanced Bootable Settings” menu to configure the boot settings.

Step 6:

Select the “boot image settings” submenu and set the boot storage device.

Since this project will boot from the QSPI we will select “primary flash” as our bootable image storage location.

Step 7:

Exit up one level in the menu structure.

Now select the “kernel image settings” submenu and set the kernel storage device to “primary flash” as well.

Step 8:

Finally, if you need to make changes to the image packaging configuration you can do this under the “Image Packaging Configuration” menu under the top-level menu.

Here we’ll change the location that the built images get written to for using tftpboot on the target to load images as we do our development.

Since Bootseeds does a lot of development on both CentOS and Ubuntu hosts, we’ve found it useful to standardize the default location of the tftp server share directory. We have found that CentOS is much more picky about moving this location than Ubuntu, so we’ll use CentOS’s default location of “/var/lib/tftpboot” in place of Ubuntu’s “/tftpboot”. (NOTE: If you use “/tftpboot” as your tftp directory then just skip this step.)

At this point we could build linux and load it onto the MicroZed target and boot into a system that doesn’t do very much. You may want to take the time to do that now as a sanity check that everything that you’ve done to date is correct for your base system.

Since the goal of this project is to get WiFi working on the MicroZed, we’re going to forgo explanation on how to package and install the image files on the target until the section Package, Boot and Connect WiFi.

Cross Compiling WiFi Components

In this step you will cross compile the components and dependencies required to get wifi up and running on the MicroZed.

NOTE: We have found that this step is far easier using Buildroot since by default it pulls from the latest source and handles the setup of the cross compile process for you. We will cover setting up wifi under build root on another page.

Now cd into the directory “zed/sw/wifi_cc”. Here we’ll make two directories, one for downloading and building the source (/build), and the other to serve as a mock file-system target for the cross-compile output (/bin). Create a directory called “build” and another called “bin”.

It is important to note that you must perform the cross compile in a directory somewhere under your home directory. If you attempt to do it in a location that requires root privileges, such as “/usr/bin” (which is the default location for most of the Makefiles in this tutorial) the steps here will ultimately fail.

For the cross compile step it is convenient to set an environment variable to the target build output directory, as this will be fed as a parameter to the numerous build steps and saves you a lot of typing. For this tutorial we’ll call it ‘X’.
export X=$HOME/repos/zed/sw/wifi_cc/bin
Alternatively you could place this line in your .bashrc to ensure that it exists for the duration of your cross compile in case you open other terminals. Now cd into the $X directory to test your environment variable. If that worked you can cd to the parallel directory “build”. Here you will download the following packages (NOTE: At the time of this writing these are the latest package versions, in general you should pull the latest versions):

  • libnl-3.2.25 – required by iw
  • wget http://www.infradead.org/~tgr/libnl/files/libnl-3.2.25.tar.gz

  • iw-4.3
  • wget https://www.kernel.org/pub/software/network/iw/iw-4.3.tar.gz

  • libnl-1.1.4 – required by wpa_supplicant
  • wget http://www.infradead.org/~tgr/libnl/files/libnl-1.1.4.tar.gz

  • openssl-1.0.2g – required by wpa_supplicant
  • wget http://www.openssl.org/source/openssl-1.0.2g.tar.gz

  • wpa_supplicant-2.5
  • wget http://hostap.epitest.fi/releases/wpa_supplicant-2.5.tar.gz

These are the minimum required packages to get WiFi support built under Petalinux. Now you can unpackage each of the archives using:
tar -zxf <package name>
We’ll now proceed to cross compile these packages for the xlnx-4.0 Xilinx Linux distribution. The default optimization flags for these packages is generally -g -O2, which means they will not be size optimized. Since we are assuming that they will work, and we’ve configured the Petalinux base configuration to try to stuff everything that is not a user filesystem into the on-board QSPI flash, where possible we will optimize for size using the -Os flag. Most of the Makefiles here will not accept the CFLAGS parameter at the command line since they use the ‘=’ assignment rather than ‘?=’ or ‘+=’. Therefore, if you run out of room in your on board flash, then you should consider editing the individual Makefiles to change the optimization flag to -Os.

Step 1: libnl-3.x

In order to build iw we must first cross-compile the netlink (libnl) library into a mock target filesystem. This is accomplished by running the configure script with the host variable set to our xlnx-4.0 cross compiler and with the output prefix set to the bin directory that we’ve setup for the cross compile output. This action is typically handled by a build and packaging utility such as Yocto or Buildroot, but can be done manually at the command line as well.

cd libnl-3.2.25
./configure --host=arm-xilinx-linux-gnueabi --prefix=$X
make
make install
cd include
make install
cd ../../

Step 2: iw

To build the iw package, we must point the PKG_CONFIG make variable to the location of our previous libnl-3.x build. You can see this as a requirement if you look at the README for iw. Additionally we’ll set the cross compiler “CC” flag to the Xilinx tool-chain and set the CFLAGS to optimize for size.

cd iw-4.3
PKG_CONFIG_PATH=$X/lib/pkgconfig CC=arm-xilinx-linux-gnueabi-gcc CFLAGS=-Os make
cd ..

Step 3: libnl-1.x

We’re not sure why, but the wpa_supplicant is dependent on the deprecated version of libnl-1.x (at least it builds cleanly against it). The documentation for libnl clearly states that software should no longer depend on the libnl-1.x component however, the latest version of the wpa_supplicant builds better against 1.x than the latest version. Therefore we must build it into our mock target in order to cross compile the wpa_supplicant.

cd libnl-1.1.4
./configure --host=arm-xilinx-linux-gnueabi --prefix=$X
make
make install
cd ..

Step 4: openssl

The wpa_supplicant also depends on the openssl library to provide the secure socket layer.

cd openssl-1.0.2g
export ARCH=arm
export CROSS_COMPILE=arm-xilinx-linux-gnueabi-
./Configure linux-generic32 --prefix=$X
make
make install
cd ..

Step 5: wpa_supplicant

Now it is time to build the wpa_supplicant. There are numerous features and configuration options that alter the capabilities of the wpa_supplicant. Prior to cross-compiling it, we’ll need to make some configuration changes to the build configuration. To accomplish this, copy the example build configuration file “defconfig” to “.config” and edit it:

cd wpa_supplicant-2.5/wpa_supplicant
cp defconfig .config
gedit .config

A word of caution about this config file; small inconsistencies in the configuration can lead to significant problems with compile-time dependencies as well as improper functionality.

How you configure the rest of the file is entirely up to your needs for your WiFi project. You can setup to build in support for client only or client and simple host protocols and/or numerous other options. In the case of the MicroZed configuration, we uncommented the following options mostly to allow flexibility in our WiFi communications with the MicroZed:

CONFIG_LIBNL32=y
CONFIG_WPS=y
CONFIG_WPS_ER=y
CONFIG_WPS_NFC=y
CONFIG_IEEE80211N=y
CONFIG_IEEE80211AC=y
CONFIG_HS20=y
CONFIG_AP=y
CONFIG_P2P=y
CONFIG_TDLS=y
CONFIG_AUTOSCAN_PERIODIC=y

Also uncomment and set the CFLAGS and LIBS variables to use our directory structure.

CFLAGS += -I$X/include/ -I$X/include/libnl3
LIBS += -L$X/lib

Finally before you build the wpa_supplicant you need to add the dynamic loader library to the LDFLAGS in the Makefile, otherewise you will get a lot of undefined symbol errors during the compile. Open the Makefile and add the following line after the CFLAGS line:

LDFLAGS += -ldl -s

Now build the wpa_supplicant with a few arguments. Note, since we reference $X in the .config file we must pass $X to the build:

export PKG_CONFIG_PATH=$X/lib/pkgconfig
CFLAGS+=-Os X=$X CC=arm-xilinx-linux-gnueabi-gcc make

Update Rootfs

Now that we have cross compiled the WiFi components, we need to install them into the target rootfs. There are numerous ways that this can be done. Since Petalinux offers a decently flexible project generation facility we are going to take advantage of that infrastructure to easily build the components into rootfs.

Step 1: Create a Petalinux Component

Now cd into the “peta_prj” directory. Here run the command to generate a Petalinux library project component:
petalinux-create -t libs -n xwifi --enable
This will create a new component under the “peta_prj/components/libs” directory called “xwifi” and automatically create a Kconfig entry and enable it for you.

Step 2: Copy the WiFi Build Products

Now copy the cross-compiled build products to the newly created component directory:
export XWIFI=~/repos/zed/sw/peta_prj/components/libs/xwifi
cd ~/repos/zed/sw/wifi_cc/
cp /build/iw-4.3/iw $XWIFI
cp /build/wpa_supplicant-2.5/wpa_supplicant/wpa_supplicant $XWIFI
cp /build/wpa_supplicant-2.5/wpa_supplicant/wpa_passphrase $XWIFI
cp /build/wpa_supplicant-2.5/wpa_supplicant/wpa_supplicant_conf.sh $XWIFI
cp /bin/lib/libnl-3.so.200 $XWIFI
cp /bin/lib/libnl-genl-3.so.200 $XWIFI
cp /bin/lib/libnl.so.1$XWIFI
cd $XWIFI

Step 3: Create your wpa_supplicant configuration

This step can be done at run-time if you prefer (for example you may not want to statically encode the SSID and password), however since this design is assuming a deployment scenario we will provide a default wpa_supplicant configuration file. Within the XWIFI library directory create and edit a file called “wpa_supplicant.conf”

network={
 ssid="MY-NETWORK-SSID"
 key_mgmt=WPA-PSK
 proto=RSN
 pairwise=CCMP TKIP
 psk="MY-WIFI-PASSWORD"
}

Obviously you should replace “MY-NETWORK-SSID” with whatever your network SSID is, and “MY-WIFI-PASSWORD” with whatever your password is.

Step 4: Create a New Network Descriptor File

In order for linux to know that it needs to configure the wlan0 interface, a new network “interface” descriptor needs to be created and installed into the rootfs. This file can be configured in many ways. For this project we’re going to add entries for loopback, eth0 and wlan0. Create this file within the “libs/xwifi” project. Here is what the final product looks like:

# /etc/network/interfaces

# loopback interface
auto lo
iface lo inet loopback

# wired ethernet interface
auto eth0
iface eth0 inet dhcp

# wifi interface
allow-hotplug wlan0
auto wlan0
iface wlan0 inet dhcp
# wpa secured (requires supplicant)
pre-up wpa_supplicant -Dnl80211,wext -B -i wlan0 -c /etc/wpa_supplicant.conf
pre-down killall -q wpa_supplicant

Step 5: Modify the Petalinux Component to Install Into Rootfs

Now setup the component by deleting the auto-generated source code and editing the Makefile:
cd xwifi
rm *.c
rm *.h
gedit Makefile

Now gut the makefile and repurpose it to copy the WiFi components to the target rootfs:

ifndef PETALINUX
$(error "Error: PETALINUX environment variable not set.  Change to the root of your PetaLinux install, and source the settings.sh file")
endif

include libs.common.mk

all: install

install:
	$(TARGETINST) interfaces /etc/network/interfaces
	$(TARGETINST) libnl-genl-3.so.200 /lib
	$(TARGETINST) libnl-3.so.200 /lib
	$(TARGETINST) iw /sbin
	$(TARGETINST) libnl.so.1 /lib
	$(TARGETINST) wpa_passphrase /sbin
	$(TARGETINST) wpa_supplicant /sbin
	$(TARGETINST) wpa_supplicant_conf.sh /sbin
	$(TARGETINST) wpa_supplicant.conf /etc

clean:
	rm libnl* iw wpa_passprase wpa_supplicant*

Download Device Firmware Files

We tried a number of USB WiFi dongles that are popular among the small ARM embedded systems, but the one that we had the best luck with was the “Comfast CF-WU815N”. We also had success with the “Edimax EW-7811Un” however, with the Edimax device there was a significant amount of patching involved and in the end it was outperformed on the network by the Comfast part.

By default the xlnx-4.0 kernel does not include the firmware files for the CF-WU815N, or many other USB WiFi components. In this step we’ll pull in the latest that the Linux kernel repositories have to offer in terms of firmware.

Step 1: Find the Correct Firmware Requirement for your Hardware

This will require some internet searching. Find the firware name for your USB WiFi component and make sure that it shows up during the repository clone in the next step. If it doesn’t you may want to look for a different device.

Step 2: Clone the Firmware Repository

One thing that we have found useful in working with the quarterly releases of the “xlnx-” kernel is to upgrade firmware repositories, at each cycle, to the “firmware” directory for the latest stable Linux kernel repository. This is likely to have everything that you will need in terms of existing firmware. In our case we are using petalinux-v2015.4 installed in /opt/Petalinux.

cd /opt/Petalinux/petalinux-v2015.4-final/components/linux-kernel/xlnx-4.0
git clone http://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git ext_firmware
ls ext_firmware

Now you should see a lot of firmware files including the rt2870.bin firmware required by the CU-WU815N. Chances are if you are using a different WiFi USB dongle, if it is supported under Linux, it’s firmware will now be available. Later when we configure the kernel, we will point it to this directory to look for the required firmware products.

Add Device-tree Descriptor

The USB wifi dongle will require a device tree entry in order to load and configure the usb phy to control the WiFi dongle. We’ve found that the best way to understand how to properly write an entry for a particular device is to search the kernel source code for the driver code that uses the entry. That is as far as we’ll go regarding explanation of the device tree entry since it is an entire topic in-and-of-itself. Here is the final result which you should place in a file called “system-top.dts” within “zed/sw/peta_prj/subsystems/linux/configs/device-tree”:

/dts-v1/;
/include/ "system-conf.dtsi"
/ {
	usb_phy0: usb_phy@0{
		compatible = "ulpi-phy";
		#phy-cells = <0>;
		reg = <0xe0002000 0x1000>;
		view-port=<0x0170>;
		drv-vbus;
	};
};

&usb0 {
	status = "okay";
	usb-phy = <&usb_phy0>;
	dr_mode = "host";
};

Configure and Build Petalinux

OK, we’re finally ready to configure and build the kernel. Now we must set and configure a number of kernel parameters using the kconfig menu system… There are so many options within this menu that instead of going through and explaining every configured or not-configured option we’ll just post our resulting config file output. You should open this file and use a diff-tool such as meld against your version. There are some required changes though, and those will be covered here.

Step 1: Launch Kernel Config

Change directories to the zed/sw/peta_prj directory and run the following command:
petalinux-config -c kernel

Step 2: Disable IPv6

If IPv6 is enabled it ends up being selected as the default for the WiFi module, and then it will not work. The IPv6 setting is found under “Networking Support/Networking options/TCP/IP networking”. Make sure it is disabled.
Disable IPv6

Step 3: Configure Wireless 80211

80211 support is located in “Networking support/Wireless”. Setup the Wireless settings as shown below:
80211 Wireless Settings

Step 4: Configure USB

USB options are located in “Device Drivers/USB support”. There are a large number of USB options and many of them are hidden until you enable the correct dependencies. Take care when setting the USB configuration options because disabling a particular feature in one location can remove already selected options from another menu.

Start by selecting “Support for Host-side USB”, then enable at a minimum “USB 2.0 OTG FSM implementation” which will force “OTG support” to enable. There are other useful options such as “USB announce new devices” and “Enable USB perist by default” which you can enable but are not required:

USB Initial Settings

Next enable “EHCI HCD (USB 2.0) support” and the sub-item “Root Hub Transaction Translator”:

EHCI Root Hub Support

This is required to make available the “ChipIdea Highspeed Dual Role Controller” and the host controller sub option. Make sure to enable both:

Enable ChipIdea Highspeed Dual Role Controller

Finally, the device tree is expecting to find a ULPI Physical Layer, so that must be enabled as well, go to the “USB Physical Layer Drivers” sub-menu:

USB Phy Level

Then select the Generic ULPI Phy:

Generic ULPI Phyisical Layer

Step 5: Configure Wireless LAN

Next the WLAN support must be configured. Navigate to “Device Drivers/Network device support/Wireless LAN” and enable the menu:

WLAN Submenu

If you plan on using your Zynq SoC as a wireless access point as apposed to just connecting to an existing network, you must enable HostAP mode (which also should have been configured in to the earlier cross compile of the WiFI libraries). It doesn’t hurt anything from a functional standpoint to add them in, so you may choose to include the option “IEEE 802.11 for Host AP”.

Since we tested a number of WiFi USB dongles, we included a few different WLAN support drivers; namely the Ralink and Realtek drivers specific to the USB dongles that were tested. If you are trying a different USB device you may have to dig around for the specific chipset support.

Enable “Ralink driver support” and “Realtek rtlwifi family of devices”.

WLAN Chipset Support

In the Ralink menu we chose the following chipset support:

Ralink Chipsets

In the Realtek menu we chose the following chipset support:

Realtek Chipsets

Additionally the chipset that proved to be most reliable had its support located within staging drivers located in “Device Drivers/Staging Drivers”:

Staging Drivers

We enabled the Realtek RTL8188EU Wireless LAN NIC driver:

Realtek Staging Chipset Support

Step 6: Configure External Firmware

This is the final step before building the Kernel and booting with WiFi enabled. Most (if not all) USB WiFi dongles require some firmware to be loaded at boot to support the device. This may be to allow for updates post product release, or maybe it is just how things were done… who knows. Anyway, Petalinux has a nice and probably underutilized feature that has proven valuable to us on other projects as well. There is a configuration option that allows users to specify an external location to look for additional firmware support.

Before showing that option, recall that in the above step Download Device Firmware Files we pulled down the mainline source firmware repository. Now we’re going to make use of that repository. Under the kernel configuration menu navigate to “Device Drivers/Generic Driver Options” and select the option to Include in-kernel firmware blobs in kernel binary”. Set the “Firmware blobs root directory” to point to the location where the firmware repository was pulled. Then set the “External firmware blobs to build” option paths to include the firmware packages that apply to the chipsets that we are using. These files are space delimited and are relative to the xlnx-4.0/ location within the Petalinux install directory.

External Firmware

Now exit and save the configuration changes

Step 7: Build the Target Image

Simply type “petalinux-build”, hit enter, and wait for the image to be built. If everything was executed properly you’ll end up with the build products located within the directory “/zed/sw/peta_prj/images/linux/”.
petalinux-build

Package, Boot and Connect WiFi

There are a number of ways to package the bootable image. However in this tutorial the plan is to flash everything into the QSPI. We’ll first generate a programming file that can be programmed via JTAG that will allow us to boot into U-Boot. Then we’ll utilize U-Boot to program a FIT image of our built linux image.

Step 1: Package BOOT.BIN

Execute the following command to generate the BOOT.BIN programming file:
petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/zed_wifi_wrapper.bit --u-boot --force

When the script completes you should have a BOOT.BIN file that can be programmed into the QSPI using the Vivado hardware manager.

Step 2: Setup the Hardware

Connect up the host to the MicroZed using a JTAG pod, a micro-usb cable an Ethernet cable and the Comfast USB WiFi dongle. Also verify that the JT1, JT2, & JT3 are set to the QSPI mode specified in the MicroZed Hardware User Guide.

Step 3: Programming with Vivado Hardware Manager

Launch Vivado and choose “Open Hardware Manager” at the startup screen. When the hardware manager opens select the “Open target” link at the top left where the hardware status is displayed and select “Auto connect” from the drop down menu:

JTAG Chain Auto Connect

The QSPI part used on the MicroZed is the Spansion S25FL128SAGBH1200, it is single 4-bit 1.8V configuration. Right click on the Zynq part in the component tree, it is labeled “xc7z010_1”. From the drop down menu select “Add Configuration Memory Device…”:

Add Configuration Memory Device

In the configuration memory dialog set the “Manufacturer” filter to “Spansion” and the “Density (Mb)” filter to 128. Select the part “s25fl128s-1.8v-qspi-x4-single” from the list:

Spansion QSPI

Click “OK” if you are prompted whether or not you want to program the memory now select “OK” which will bring up the configuration file chooser dialog. Use the browser to navigate and select the BOOT.BIN file that was just created:

BOOT.BIN Configuration File Selected for Programming

Make sure that at a minimum “Erase” and “Program” are selected, though “Verify” is a good option to read back and compare what was programmed vs. what is on disk locally. Also select “Configuration File Only” for address range as this will speed up the process a bit.

Click “Apply” then click “OK” and wait for programming to complete. When programming is complete you will get a prompt stating the status of the programming operation. Hopefully it was successful. Now power cycle the MicroZed.

Step 4: Programming the FIT Image Using U-Boot

Open up a serial terminal program such as Screen or Putty and set the baud rate to 115200. When the system powers up view the serial console and you’ll see the U-Boot prompt. The U-Boot environment should be setup properly to interact with your host over the network based on the configuration steps performed in Create Petalinux Project. However if you need to view the environment variables you can use the command “printenv” and you can modify them with “setenv” and make them persist with “saveenv”.

To install the linux kernel image using the FIT image you can simply run:

run update_kernel

Be sure to wait for programming to complete before doing anything else. When programming is complete the U-Boot prompt will return. Now you can boot the kernel by executing the “bootm” command, but I like to do a full reset to make sure the kernel can boot from scratch. Press the reset button and if everything went well with the tutorial the system will boot, and your Comfast WiFi dongle should configure during boot and establish itself within your network:

MicroZed Booted with WiFi

Now if you run ifconfig you should see a wlan0 interface. This is how you reference it in code when writing your applications. Treat it like another ethernet port, but be aware that the USB WiFi module is lossy, so you may want to stick with TCP/IP as opposed to UDP for example.

Good luck!