Break the 100Mb/s barrier – Turbo Boost the Raspberry Pi 2 with 802.11ac

In the ideal world, any USB devices you plug in to your Raspberry Pi will work instantly, somebody somewhere has it sussed out for you and kindly bundled the correct drivers with the distribution. In the real world, this doesn’t happen, of all the drivers available, none of them work for that very device you happen to own!  This is what happened to me recently with one of my 802.11ac WiFi dongle, a cheap no-brand device, (and this equivalent), purchased for use with my notebook running Windows 7. Something in my head tells me that I should plug this WiFi dongle into my new Raspberry Pi 2 so that I can run it wirelessly at 780Mb/s.802.11ac-02

No price for guessing what happened when I plug this dongle into my Pi 2. I wasn’t disappointed though, because I didn’t expect the kernel to have the correct driver for this WiFi dongle and I most likely need to find the driver source code for this device which I need to compile against the kernel I’m running. dmesg at the terminal got me this:

[ 3.477600] usb 1-1.2: new high-speed USB device number 4 using dwc_otg
[ 3.608268] usb 1-1.2: New USB device found, idVendor=0bda, idProduct=8812
[ 3.619239] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 3.630578] usb 1-1.2: Product: 802.11n NIC
[ 3.638993] usb 1-1.2: Manufacturer: Realtek
[ 3.648025] usb 1-1.2: SerialNumber: 123456

P1050371The Kernel reported that the device is manufactured by Realtek with the Vendor ID=0bda, Product ID=8812. Opening up the case of the dongle revealed the chip used is a Realtek RTL8812AU. A quick search on the Internet return one very promising result. A nice chap put up the driver source at GitHub for use with his DLink DWA-171 WiFi dongle, which also had the Realtek RTL8812AU chip inside. His fork of the Realtek driver already has the option in the Makefile for compiling the diver for use with Raspberry Pis.

Before you compile the driver, you need to have a copy of the Linux kernel source. So I cloned a copy of kernel source for my current Raspbian distribution:

$ git clone –depth=1 git://github.com/raspberrypi/linux

My current kernel version is 3.18.7 and the version I pulled from the github at the time of writing this is version 3.18.11. I hope the minor revision won’t affect the driver compilation. I also added any missing dependencies in case it’s not already installed:

$ sudo apt-get install bc make screen ncurses-dev

I then create the necessary symbolic links in the modules directory

$ cd /lib/modules/`uname -r`
$ sudo ln -s /opt/src/linux build
$ sudo ln -s /opt/src/linux source

My kernel source is under /opt/src directory, if you’re following this, change the path to where you downloaded your kernel source.

I then clone a copy of the Realtek RTL8812AU code:

$ git clone git://github.com/gnab/rtl8812au

Using the editor, I modified the Makefile so that the code will target for the Raspberry Pi’s architecture:

$ nano Makefile.

Change CONFIG_PLATFORM_I386_PC to ‘n’, and CONFIG_PLATFORM_ARM_RPI to ‘y’ :

CONFIG_PLATFORM_I386_PC = n
:
:
CONFIG_PLATFORM_ARM_RPI = y

Now compile the driver by typing make at the command prompt.

make

The driver should compiled ok without error, but I get a lot of warnings. On my Pi 2, it took about 6.5mins to compile the driver. Once done, I install the kernel module into its appropriate place:

$ sudo make install

Fingers crossed, I plug my dongle in and…….. :

[ 8957.207074] usb 1-1.2: new high-speed USB device number 8 using dwc_otg
[ 8957.308022] usb 1-1.2: New USB device found, idVendor=0bda, idProduct=8812
[ 8957.308047] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 8957.308064] usb 1-1.2: Product: 802.11n NIC
[ 8957.308081] usb 1-1.2: Manufacturer: Realtek
[ 8957.308097] usb 1-1.2: SerialNumber: 123456
[ 8957.332038] 8812au: disagrees about version of symbol module_layout

Oops, It didn’t work! What’s going on? Is it because the minor version different of the kernel? Or is it something else? After some extensive searches on the Internet, I found out that the data structures layout in the Linux kernel varies not only from version to version but also depending on the compilation options. As a consequence, when you compile a kernel module, you need to have not only the header files from the kernel source, but also some files that are generated during the kernel compilation. Therefore, having the kernel source isn’t enough, I need to recompile the kernel first before I can compile the driver modules.

For any serious Linux users, soon or later, you will need to get your hand dirty learning how to compiling the kernel from scratch. If you want extra features that are not available on the stocked kernel from the distribution, you’ll need to enable those features and re-compile the kernel to make those features available. There are at least two ways to go about compiling the kernel for your Raspberry Pi, cross compile on a PC and compile the kernel directly on the Raspberry Pi.

I elected to do the latter not just because I’m insane, but I also want to see how much better this new Raspberry Pi 2, with the new quad-core processor, performs. On the old Raspberry Pi 1 with 512MB running @700MHz, I was told that it can take over 12 hours to compile the kernel with default settings. The new Raspberry Pi 2 has more RAM, and claimed to run six times faster, I really want to see how long it will take to compile the kernel. So to get started, I grep the original configuration from the kernel I’m running. Goto the root of the kernel source:

$ cd /opt/src/linux
$ zcat /proc/config.gz > .config

The compilation is going to take a while, even if you’re working directly on the Raspberry Pi and not via a ssh session, you don’t really want to tie up the terminal session for hours on end. You may, once in while, want to check on the progress to see how much CPU the compilation is using. I suggest the use of screen so that you can detach the session and re-attach the session any time later:

At the root of the kernel source
$ screen
$ time make

The first time you invoke screen, it will present you with the introductory text, hit space or return to clear it and you’re now in a screen session. Anything you do here will be preserved after you detached and terminated the terminal session. If you want to come back later on, you can re-attach the screen session and it’ll return you to where you left off. Of course, if you cut the power to the Pi, everything will be lost forever!

I know the compilation is going to take a long time, I use time to invoke make so that I can get some idea how long the compilation will take.  With the kernel version 3.8.11, I need to answer a few questions because there are new modules/features in the new kernel that the original configuration file did not answer, such as the PiTFT LCD support, so I just tell make to compile it as modules. Once the compilation process kicked off, I can detach the screen session and free up the terminal session using:

<CTRL> – a  d

That is, you press and hold the control key while you hit the “a” key, release both keys and hit “d” to detach the screen session. You can re-attach the screen session any time by issuing the following command:

screen -r

This will re-attach the screen session you’ve detached last time. If you have more than 1 screen sessions, screen will present you with the list of available sessions and ask you which session you want to re-attach.

Because I had the screen session detached, I have a free terminal session for me to check on the status of the system during compilation. I used top to see how hard the system is working. If you hit ‘1’ while you’re in top, top will present you with the statistic of each CPU instead of the average of all CPUs, from there you can see the quad-core CPU in action.  One thing I noticed from the CPU stats is that each CPU seems take turns to work, at times I see one CPU is at 0% idle while the other 3 are 99%-100% idle, that means the the system is not sharing workload across all CPUs! At the end of compilation, I get the following on screen:

real    237m12.198s
user   217m35.220s
sys      10m53.710s

The compilation took less than 4 hours to complete, surprisingly quick! After the main kernel is compiled, I went on to compile and install the modules followed by copying the new kernel to the /boot directory:

make modules
sudo make modules_install
sudo cp arch/arm/boot/Image   /boot/kernel-new.img

I didn’t bother measuring the time taken to compile the modules, it should only take a few minutes. Instead of over-writing the old kernel, I named the new kernel kernel-new.img so that I can revert back to use the old kernel should the new kernel failed for any reason. To tell the Pi to use the new kernel on next boot, I added a new entry to /boot/config.txt:

$ sudo nano /boot/config.txt

Append this to the end

kernel=kernel-new.img

Save the file and reboot. Voilà, the system should now be running on the new kernel, to verify this:

uname -a
Linux raspi2a 3.18.11-v7+ #1 SMP PREEMPT Sat Apr 18 18:00:30 BST 2015 armv7l GNU/Linux
or
cat /proc/version
Linux version 3.18.11-v7+ (pi@raspi2a) (gcc version 4.6.3 (Debian 4.6.3-14+rpi1) ) #1 SMP PREEMPT Sat Apr 18 18:00:30 BST 2015

With the new kernel compiled and running on my Pi 2. I can now go back to create the WiFi driver by compiling it against the current running kernel.

Go back to the driver directory, do a clean up and start a new compilation:

cd /opt/src/rtl8812au
make clean
make
sudo make install

Now for the moment of truth! I plug the dongle in and……Eureka it’s working, When I do a dmesg I get the followings together with pages of other messages:

[ 1265.975557] usb 1-1.2: new high-speed USB device number 5 using dwc_otg
[ 1266.076511] usb 1-1.2: New USB device found, idVendor=0bda, idProduct=8812
[ 1266.076538] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 1266.076554] usb 1-1.2: Product: 802.11n NIC
[ 1266.076571] usb 1-1.2: Manufacturer: Realtek
[ 1266.076587] usb 1-1.2: SerialNumber: 123456
[ 1266.122505] RTL871X: rtl8812au driver version=v4.2.2_7502.20130517
[ 1266.122705] RTL871X:
:
:
[ 1266.123102] RTL871X: USB_SPEED_HIGH
[ 1266.123405] RTL871X: CHIP TYPE: RTL8812
[ 1266.123451] RTL871X: register rtw_netdev_ops to netdev_ops
[ 1266.123780] RTL871X: ReadChipVersion8812A SYS_CFG(0xF0)=0x04411137
[ 1266.123970] RTL871X: Chip Version Info: CHIP_8812_Normal_Chip_TSMC_C_CUT_2T2R_RomVer(0)
[ 1266.123984] RTL871X: RF_Type is 2!!
[ 1266.124000] RTL871X: _ConfigChipOutEP_8812 OutEpQueueSel(0x07), OutEpNumber(3)
[ 1266.124012] RTL871X: ====> ReadAdapterInfo8812AU
[ 1266.124179] RTL871X: Boot from EFUSE, Autoload OK !
[ 1266.391100] RTL871X: EEPROM ID=0x8129
[ 1266.391120] RTL871X: VID = 0x0BDA, PID = 0x8812
[ 1266.391129] RTL871X: Customer ID: 0x00, SubCustomer ID: 0xCD
:
:

There are pages of messages in the syslog regarding the discovery of the device’s particulars such as mac address, eeprom, revision of firmware download, device registration etc. etc.

Most importantly, when I query the available network interfaces using ifconfig, I can see the wlan0 listed there too. To verify if the interface is working and can be connected to my router, I configure the /etc/network/interface with the SSID and passkey for my router, my /etc/network/interfaces file looks like this:

auto lo

iface lo inet loopback
iface eth0 inet dhcp

allow-hotplug wlan0
iface wlan0 inet dhcp

wpa-ssid <YOUR-ACCESS-POINT-SSID>
wpa-psk <YOUR-ACCESS-POINT-PASSKEY>

# wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
iface default inet dhcp

You notice that I didn’t bother with the wpa_supplicant.conf file as I found the above setting is easier and less troublesome. I setup the wireless interface to connect to my 802.11ac router. With a connection established, the output from iwconfig looks like this:

iwconfig
wlan0 IEEE 802.11AC ESSID:”AP-SSID” Nickname:”<WIFI@REALTEK>”
Mode:Managed Frequency:5.3 GHz Access Point: UU:VV:WW:XX:YY:ZZ
Bit Rate:780 Mb/s Sensitivity:0/0
Retry:off RTS thr:off Fragment thr:off
Power Management:off
Link Quality=100/100 Signal level=73/100 Noise level=0/100
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:0 Invalid misc:0 Missed beacon:0

lo no wireless extensions.

eth0 no wireless extensions.

I now have an 802.11ac wireless connection running @780Mb/s on my Raspberry Pi 2. However, the bottleneck in now in the USB2 connection which has the max throughput of 480Mb/s! I guess the main advantage of this is that my wireless connection is now on the 5GHz band, which is less crowded than the 2.4GHz band that everyone else is using in my immediate vicinity.

Update Dec 2015 :

A reader left me a comment questioning the throughput of this wireless connection. He suggested to collect some test data on the throughput using iperf which can provide some indication on how well the Raspberry Pi 2 is doing over the wireless connection.

My initial test was done on a mixed 10/100/1000Mb network where the 802.11ac access point(AP) is sitting on the far end of the building and my Linux workstation is connected to a chain of mixed 100/1000Mb switches. The initial test results were not very promising, I can only achieve the throughput between 58.9Mb/s – 78.2Mb/s.

A few days later, I gather up some equipments to setup a proper test network with all devices connected to a single Gb switch, e.g.  the 802.11ac AP and the Linux workstation with Gb Ethernet card both connected to the same Gb switch. With the 802.11ac dongle attached to the Raspberry Pi 2, I ran the same iperf tests as before and I can now achieve the throughout between 100Mb/s – 115Mb/s, with several peaks at 120Mb/s. This is almost as fast as running on wired connection. However, the test conditions may vary depending on your environment!

In case people are wondering what other dongles will work with this driver. I recently purchased this TRENDnet TEW-805UB dongle, it’s cheaper and it has the same Realtek RTL8812AU chipset, it works with this driver without any issues.

10 thoughts on “Break the 100Mb/s barrier – Turbo Boost the Raspberry Pi 2 with 802.11ac

  1. Hi and congrats on the achievement!

    I have checked the link to the WiFi dongle that you have used. It does not say that it supports 802.11ac. It only says 802.11b/g/n.

    Are you sure you’re using 802.11ac?

    Like

    1. Looks like they no longer sell the same version of dongle I bought from them. The version I used is shown in the guide with a much longer profile than that show in the link provided. Here is another link for your reference. Thanks for pointing this out and I’ve updated the guide to avoid further confusions.

      Disregard the type of dongle profiles, it should match the chip-set I used, I wrote the guide based on the Realtek RTL8812AU chip-set. As you can see from the output of iwconfig, the bitrate is at 780 Mb/s and it is communicating on the 5GHz band.

      Like

  2. Get I this WLAN stick by Amzon.de?

    The manufacturer is unknown. Therefore, I do not know what I should look after.

    Thank you for your Answer.

    Like

  3. Excellent article. I would love to try this.

    Did you run iperf? I have tried 150Mbps wifi dongle, ethernet wire at 22Mbps, and 150Mbps external AP using RPi 2 B. The nominal throughput for all three connections over many iperf runs is 1.69Mbps. It peaks around 3.16 on a wired line. Are you actually getting higher throughput?

    Like

  4. Sorry if this is a duplicate. I thought my comment was posted yesterday.

    Excellent article. I want to try this, but it seems like a lot of work.

    Have you run iperf to determine the actual upload and download speeds?

    I tried all combinations of an RPi B+ and an RPi 2 B using RT5370 wifi dongle, external AP and hardwired ethernet connection, and raspbian. The nominal maximum download speed was 1.69 Mbps, with a one time peak below 4Mbps. Often the speed drops below 1Mbps. To me it seems like there is some type of USB rate limiting occurring on wifi.

    My son teaches high school science in a disadvantaged neighborhood. I am trying to build RPi computers, so his students can do research. However, the download speeds are just too slow.

    Any performance numbers would be greatly appreciated. Thanks.

    Like

    1. Sorry for the late reply Jeff. I have not previously ran any iperf on my RPi-2B wired or wireless. However, just to make this complete, I quickly ran a few tests prior to writing this reply.

      With just the wire connection, my performance is around 93.5Mb/s – 94.9Mb/s on my mixed 100M/1000M network. With just the Realtek RTL8812AU based WiFi dongle connected and a reported bit rate of 780 Mb/s as listed in iwconfig, I get readings between 58.9Mb/s to 78.2Mb/s, depending on how and where I position the RPi-2B. I believe I’ve done the test correctly with my Linux host running as iperf server and my RPi-2B running the iperf test against the Linux host. I hope my quick and simple tests can be of any help to you. Let me know if I have not done the test correctly or if you want me to perform more tests in other different ways.

      UPDATE: I performed another test with everything moved onto a single Giga-bit network, e.g. the RPi-2B with 802.11ac dongle, the 802.11ac WiFi AP, and a Linux workstation with 1Gb network card all connected to a Gb network switch. I can achieve throughput between 100Mb/s to 120Mb/s, that is almost as fast as than running the RPi-2B on wire, which can only achieve 100Mb/s!

      Like

    1. No, It’s only USB2.0 not USB3.0. The Raspberry Pi doesn’t yet support USB3.0, so the bottleneck is on the USB sub-system even though the WiFi device is a USB3.0 device. Another advantage with this device is that I can make use of the 5GHz spectrum.

      Like

Leave a comment