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.
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
The 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.
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
$ 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:
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
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
$ 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
$ 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:
iface lo inet loopback
iface eth0 inet dhcp
iface wlan0 inet dhcp
# 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:
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
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 110Mb/s – 190Mb/s, with several peaks above 200Mb/s reaching 203Mb/s at one time. This is faster than the Raspberry Pi running on wired connection alone which can only achieve the maximum of100Mb/s.
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.