This post explains how to cross-compile the Linux kernel and Backport driver sources against ARM platform and loading the binaries to the target. The end users who are interested in bringing up their target with linux kernel with wlan drivers might find this blog helpful.

 

What is Backporting in Linux?

The backports is a separate Linux project. "Backporting" aims to backport current Linux upstream device drivers for use with older kernels. Refer Linux wiki page for more documentation. The Linux backports project is a set of patches/scripts/sources that allows us to generate a backport package.

This blog post picks up Cypress's FMAC backport package and compiles them against ARM platform.

 

Why we need Backports ?

The objective is to provide a central mechanism for backporting the device drivers of any subsystem, thereby enabling both users and developers to always focus on upstream Linux kernel development. This means that one can stay in old kernel version and still can use the latest drivers from mainline so as to support the latest features.

Note that the backport shall have limitation on the minimum kernel version running on the target. For instance, the backport driver source with version v4.12 need atleast linux kernel version v3.0+ for backporting to work fine. Check the Cypress's readme files in backport sources for the related information.

 

Setup the toolchain for cross-compiling the linux kernel(against ARM) and device drivers to the target platform.

This blog has following sections.

  • Setting up cross toolchain for ARM

  • Build Linux kernel
  • Build kernel driver module
  • Download or build device tree for the target
  • Load the drivers to the target

 

Contact the Cypress customer support to gain access to the Cypress's FMAC package (Backport driver sources).

 

Toolchain setup

     $ sudo apt-get install libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi libncurses5-dev gcc-arm-linux-gnueabi u-boot-tools lzop

 

The Linux kernel image and driver modules shall be built separately. Below is the example using iMX6UL platform running linux kernel 4.1.15 interfacing 43430 WLAN chip. The instructions provided here should be the same for iMX6SX platform.

 

The high level goal of the following "Build Linux Kernel" section is to generate a kernel image with CONFIG_CFG80211 and CONFIG_BCMDHD being configured in different way as mentioned below, while all others steps are standard steps to build an iMX image.

 

Build Linux Kernel

1. Get the Linux kernel source from freescale git repository and extract the source.

    Git clone/download the correct linux kernel source by referring the git tag from Freescale git - refs/tags/rel_imx_4.1.15_2.1.0_ga

    $ tar zxvf linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.gz

2. Set up build environment and kernel configuration

    $ cd linux-imx-rel_imx_4.1.15_2.1.0_ga

    Make sure to run following commands in full privileged mode, as "distclean" would require admin permission to clear all the last configured files. The "distclean" takes care of deleting all the configs and files that need to be deleted for clean build.

    $ sudo -s

    $ make distclean

    Configure the kernel against iMX platform. Both iMX6UL and iMX6SX platforms use imx_v7_defconfig.

    The kernel configuration will be written to ".config" file.

    $ make ARCH=arm imx_v7_defconfig

3. Now, edit .config and build cfg80211 as module (as shown below). The CONFIG_BCMDHD is not needed as part of kernel, as FMAC driver is used for WLAN configuration in this case.  The CONFIG_CFG80211=m will let kernel know that this is a driver module and it won't get compiled with the kernel build. The driver modules has to be built explicitly.

      CONFIG_CFG80211=m

      CONFIG_BCMDHD=n

4. Build the Linux kernel image(uImage). Pass ARCH=arm and CROSS_COMPILE=arm-linux-gnueabi-  so as to cross-compile against ARM platform using the toolchain "arm-linux-gnueabi-". The LOADADDR is a platform dependent value and this indicates the start address of kernel image. Check the Board files from vendor and set accordingly. The address 0x10008000 is the LOADADDR for iMX6UL platform. (Note: LOADADDR=0x80800000 for iMX6SX platform)

    $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage LOADADDR=0x10008000

5. The kernel image is available in the following folder after successful build.

    Path to kernel image: arch/arm/boot/zImage

 

Please refer to the attached file (kernel_logs) which gives some help commands to cross check the build arguments set correctly for toolchain before build.

 

Build kernel driver module

Get the Cypress's backport driver sources available from Cypress FMAC package: Cypress Linux FMAC (Orga)

1. Download and untar the linux backports package

      $ tar zxvf cypress-backports-*.tar.gz

      $ cd *backports/

2. (Native) compile local tools and generate .config. Export the MY_KERNEL to point to the target linux kernel to compile the drivers against it.

      $ MY_KERNEL=<4.1.15 kernel path>

    Generate the .config using "defconfig-brcmfmac" which will set all necessary configurations for FMAC driver. The driver configuration will be written to ".config" file.

      $ make KLIB=$MY_KERNEL KLIB_BUILD=$MY_KERNEL defconfig-brcmfmac

3. Cross compile the kernel modules. This command will start the build and generate the driver modules. As mentioned in above section, build the FMAC driver modules explicitly by passing "modules" with the make command.

      $ make KLIB=$MY_KERNEL KLIB_BUILD=$MY_KERNEL ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  modules

4. The following files are generated after successful build.

      compat/compat.ko

      net/wireless/cfg80211.ko

      drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko

      drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko

 

Build Device tree -- (Skip this section if .dtb file for target is already available)

Get the Cypress's device tree available from Cypress FMAC package: Cypress Linux FMAC (Orga)

1. Untar the Cypress devicetree package.

      $ tar zxvf cypress-devicetree-*.tar.gz

2. Find your board's dtb file, for example

      cypress-devicetree/iMX6SX/4.1.15/imx6sx-sdb-btwifi-fmac.dtb

      (Note: If your board's dtb is not available in the cypress devicetree package, please refer to the available dts/dtsi files and create them for your board, then compile them for the dtb file. iMX dts files are located in linux-imx/arch/arm/boot/dts/ folder of the Linux kernel tree. Below command compiles a dts file)

      $ make ARCH=arm <devicetree name>.dtb

 

Find attached to this post "fmac_wl_dtb", the DTBs for iMX6UL and iMX6SX platforms.

 

Load the DTB, Kernel image, WLAN firmware and drivers

1. Copy the zImage and dtb files to the target board. Set the build args TARGET_IP with the IP address of the target board. Make sure the target board has internet access via wired/wireless connectivity and reachable to the build machine. Use "scp" to transfer the files from build machine to target.

      $ TARGET_IP=<target board IP>

      $ scp <dtb file> root@$TARGET_IP:/run/media/<mmcblk1p1>/cy.dtb (check mmcblk* on target using "df" command)

      $ scp <zImage file> root@$TARGET_IP:/run/media/<mmcblk1p1>/zImage_cy (check available space on target using "df -h ." at /run/media/mmcblk*. Delete/Backup the old kernel and replace with the new one, if there is no sufficient memory to copy).

2. Copy firmware files to the target board and rename the filename as per chip name(Example: brcmfmac43430-sdio.bin where 43430 is chip name). Create "/lib/firmware/brcm" directory if not already present on target.

      $ tar zxvf cypress-firmware*.tar.gz

      $ scp <firmware.bin> root@$TARGET_IP:/lib/firmware/brcm/brcmfmac<43430>-sdio.bin

3. Copy your nvram file (from board vendor) to the target board and rename it as per chip name (Example: brcmfmac43430-sdio.txt)

      $ scp <nvram file.txt> root@$TARGET_IP:/lib/firmware/brcm/brcmfmac<43430>-sdio.txt

4. Extract the attached "fmac_wl_dtb" and find the "wl_fmac_imx" user utility and copy to the target. This user-space utility is used to configure WLAN chip, associate to SSID, perform scan and get scan results, etc.

      $ scp wl_fmac_imx root@$TARGET_IP:/bin/.

5. Copy Cypress kernel modules which was compiled in previous steps (compat.ko, cfg80211.ko, brcmfmac.ko, and brcmutil.ko ) to the target.

      $ scp <module files> root@$TARGET_IP:/lib/modules/<current_kernel_dir>

6. Reboot the target. Press ctrl-c to enter u-boot and configure it to load with new kernel image. Update the board environment variables to use the correct kernel image and device-tree file. Save and reset the target.

U-Boot command:

      => env print image fdt_file

      => setenv image zImage_cy

      => setenv fdt_file cy.dtb

      => saveenv

      => env print image fdt_file

    Before "reset", connect the wifi dongle to the target.

      => reset

7. Check the updated kernel version using "uname -a". Boot up the target board with the above zImage and insmod the following driver modules.

      $ insmod /lib/modules/<kernel_dir>/compat.ko

      $ insmod /lib/modules/<kernel_dir>/cfg80211.ko

      $ insmod /lib/modules/<kernel_dir>/brcmutil.ko

      $ insmod /lib/modules/<kernel_dir>/brcmfmac.ko

8. Check the new wlan interface "wlan0" created in "ifconfig" menu.

      $ ifconfig

eth0      Link encap:Ethernet  HWaddr 00:04:9F:04:0C:EE

          UP BROADCAST MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

..

..

wlan0    Link encap:Ethernet  HWaddr 00:90:4C:2A:0A:00

          inet6 addr: fe80::290:4cff:fe2a:a00/64 Scope:Link

          UP BROADCAST MULTICAST  MTU:1500  Metric:1

          RX packets:0 errors:0 dropped:0 overruns:0 frame:0

          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:0 (0.0 B)  TX bytes:652 (652.0 B)

9. Check the kernel logs by "dmesg". Check the FMAC firmware version and the logs showing "wlan0" which is the new interface created in "ifconfig".

      $ dmesg | tail

brcmfmac: brcmf_c_preinit_dcmds: Firmware version = wl0: Nov 14 2017 18:47:34 version 13.10.271 (TOB) (r674299 WLTEST) FWID 01-c46a1839

brcmfmac: brcmf_c_preinit_dcmds: CLM version = API: 18.2 Data: 9.10.0 Compiler: 1.36.1 ClmImport: 1.34.1 Creation: 2017-11-14 18:45:42

IPv6: ADDRCONF(NETDEV_UP): wlan0: link is not ready

10. Give full permission for "wl" utility since this will be used for any wlan configuration or getting wlan status.

      $ chmod +x /bin/wl_fmac_imx

11. End users prefer to use wpa_supplicant and wpa_cli commands to configure WLAN. The supplicant uses a global configuration file. The following example is based on a minimal configuration file which let supplicant to join a SSID with open network. Refer to: WPA_Supplicant page for more details on advanced supplicant configuration.

  • Copy and paste the below configuration on /etc/wpa_supplicant file.

ctrl_interface=/var/run/wpa_supplicant

ctrl_interface_group=0

update_config=1

 

network={

ssid="XX"

key_mgmt=NONE

}

  • Start the supplicant to run in background. Basically the below command takes driver(nl80211), interface(wlan0), configuration file(/etc/wpa_supplicant).

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

  • Run wpa_cli and look for connection status.

$ wpa_cli

wpa_cli v2.5

Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors

This software may be distributed under the terms of the BSD license.

See README for more details.

Selected interface 'wlan0'

Interactive mode

> status

bssid=xx:xx:xx:xx:xx:xx

freq=2417

ssid=XX

id=0

mode=station

pairwise_cipher=NONE

group_cipher=NONE

key_mgmt=NONE

wpa_state=COMPLETED

ip_address=x.x.x.x

address=xx:xx:xx:xx:xx:xx

uuid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

> scan_results

bssid / frequency / signal level / flags / ssid

xx:xx:xx:xx:xx:xx       2417    -69     [ESS]   XX

> disconnect

OK

<3>CTRL-EVENT-DISCONNECTED bssid=xx:xx:xx:xx:xx:xx reason=3 locally_generated=1

> status

wpa_state=DISCONNECTED

ip_address=x.x.x.x

address=xx:xx:xx:xx:xx:xx

uuid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

> reconnect

OK

<3>CTRL-EVENT-SCAN-STARTED

<3>CTRL-EVENT-SCAN-RESULTS

<3>WPS-AP-AVAILABLE

<3>Trying to associate with SSID 'XX'

<3>Associated with xx:xx:xx:xx:xx:xx

<3>CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed [id=0 id_str=]

12. Obtain IP address from access point.

      $ udhcpc -i wlan0

      $ ifconfig

..

..

wlan0    Link encap:Ethernet  HWaddr 00:90:4C:2A:0A:00

          inet addr:192.168.43.249  Bcast:192.168.43.255  Mask:255.255.255.0

          UP BROADCAST MULTICAST  MTU:1500  Metric:1

          RX packets:3 errors:0 dropped:0 overruns:0 frame:0

          TX packets:12 errors:0 dropped:0 overruns:0 carrier:0

          collisions:0 txqueuelen:1000

          RX bytes:702 (702.0 B)  TX bytes:1772 (1.7 KiB)

 

(Note: the DHCP client could be different for different platforms)