Monday, December 29, 2014

Wifi on the BeagleBone Black with Systemd

EDIT 2015-01-01: What I thought was the last word in getting wifi going on the Beaglebone Black was missing one key piece of information.  Read this addendum, or you're screwed.

Unlike most of my posts on this blog, the title of this post is neither clever nor funny.  I'm feeling serious today.  Why?  I have a Rev B BeagleBone Black (BBB) and I have a love / hate relationship with it.  And I suspect I'm not the only one.
It's Not Me - It's You
I love that it is a hell of a lot more powerful than the Raspberry Pi in all aspects except probably for its graphics: it has a faster processor, more RAM, onboard flash, more I/O, and the incredibly powerful PRUs.  The new Raspberry Pi B+ certainly helps level the field with more I/O than the original B and four sweet USB ports, but for raw power the award still goes to the 'Bone.

On the other hand, I hate that the 'Bone can be such a bastard to get working to its full potential.  One problem is the Imagination Technologies SGX530 graphics core.  This company is open-source hostile, and their closed-source graphics binary blob works only with certain kernels.  All attempts to reverse-engineer an SGX driver have unfortunately failed.  The Raspberry Pi has both more powerful graphics hardware and Broadcom is (amazingly) supporting the development of an open source driver.  Incidentally, the upcoming Beagleboard-X15 will also have an SGX graphics core in it, and they will not be getting my money for this reason alone. Huge win for the Pi here.

The other problem I've had is with getting wifi going.  One dongle was crashing the kernel.  I wasn't the only one having problems, either.  I spent a ridiculous amount of time on this in the fall with no luck.  I thought the best way to fix the problem was to just drive over the 'Bone with my car and get a Pi.

But I'm stubborn.  I've got some time over the Christmas break and I thought I'd give it one more shot.  Amazingly, I seem to have something working using the Jessie snapshot I got from here named BBB-eMMC-flasher-debian-jessie-lxqt-armhf-2014-12-19-2gb.img.xz and running kernel 3.14.26-ti-r42.  I also learned a bit about systemd along the way.  Systemd is fairly new to Debian, and that fact makes a lot of the wireless tutorials out there obsolete.  So how did I get wifi working on the Beaglebone Black with systemd?  Read on, McDuff...

First of all, get the latest Jessie snapshot from the link above and flash it to the eMMC (flash) memory on the 'Bone. I'm not going to go into the details of doing that, because lots of other people have. That is what Google is for.

OK, so you've flashed the 'Bone with the Jessie image and want to get wireless networking going. I'm going under the assumption that you have console access to the 'Bone, either via a USB to Serial connection or SSH over a hard-wired connection. No fancy Graphical User Interface stuff for me.

Next thing to know is that a lot of wifi dongles need some kind of firmware loaded into them.  My TP-Link TL-WN722N already has its firmware on the image linked above (from the ath9k_htc package) so no need to download anything in this case just yet.  Other dongles like those based on the ZD1211 or RaLink chipsets will need you to install a firmware package with the old "apt-get update; apt-get install... " dance.  Here, the dmesg command and good ole' Google are your friend, helping you to figure out what you need.

Now you've got a 'Bone with a dongle plugged in and its firmware loading up.  After that, the thing that trips people up is that recent kernels "soft block" (disable) things like wifi and bluetooth by default. A program called rfkill is necessary to enable them. rfkill is part of the image above (and because I politely asked Robert C. Nelson to put it in) so you should be good to go without having to download something over a hardwired ethernet connection first.

Let's kick things off by checking to see if the wifi is blocked, and enable it if it is.
root@beaglebone:~# rfkill list wifi
0: phy0: Wireless LAN
Soft blocked: yes
Hard blocked: no
root@beaglebone:~# rfkill unblock wifi
root@beaglebone:~# rfkill list wifi
0: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
Good start. "Soft blocked" changed from "yes" to "no" after we unblocked it.

Next thing we need to do is bring the wifi interface up using the ip command.  Note a couple things.  First, Blogger hates me.  The square brackets below are supposed to be angle brackets, but the Blogger editor gets confused when you switch back and forth between "Compose" mode and "HTML" mode (and yes, I tried coding in the HTML entities and no, that didn't work).

Before getting into that, how do we know the name of our wireless interface?  Assuming your dongle is plugged in, do...
root@beaglebone:~# ip link
1: lo: [LOOPBACK,UP,LOWER_UP] mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: [NO-CARRIER,BROADCAST,MULTICAST,UP] mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
3: can0: [NOARP,ECHO] mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
link/can
4: can1: [NOARP,ECHO] mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
link/can
5: wlan1: [BROADCAST,MULTICAST] mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
There we go.  My dongle is wlan1 and I'll be using that in the examples that follow.  I'll also strip out other interfaces in the output going forward.  Numbers I'd rather you didn't see have been replaced by "xx".

Now let's bring the wlan1 interface up and check its status.
root@beaglebone:~# ip link set wlan1 up
root@beaglebone:~# ip link
5: wlan1: [ NO-CARRIER,BROADCAST,MULTICAST,UP ] mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
See how the status changes from [ BROADCAST,MULTICAST ] to [ NO-CARRIER,BROADCAST, MULTICAST,UP ] once the link is up?

The next thing we'll do is associate the dongle to a wireless router.  This example is for the connection  to my router that is wide open.  If yours is not open or not WEP encrypted, you'll need to do something different like bringing wpa_supplicant to the party (or just skip ahead to the automatic stuff below).  My router connection is named Abby_Slow, so you'll have to substitute the name of your access point below.  The really important thing to note in the following is that older command line tools like iwconfig have been deprecated and will probably not work anymore.  The new tool in town is called iw.  We will first use iw query the status of the link, and then make a connection.
root@beaglebone:~# iw wlan1 link
Not connected.
root@beaglebone:~# iw wlan1 connect Abby_Slow
root@beaglebone:~# iw wlan1 link
Connected to xx:xx:xx:xx:xx:xx (on wlan1)
SSID: Abby_Slow
freq: 2437
RX: 10694 bytes (75 packets)
TX: 2029 bytes (13 packets)
signal: -72 dBm
tx bitrate: 15.0 MBit/s MCS 0 40MHz short GI

bss flags:      short-preamble short-slot-time
dtim period:    0
beacon int:     100
So now we have a link, but that isn't going to get us as far as we need to go...
root@beaglebone:~# ping www.google.com
ping: unknown host www.google.com
We need to use something called dhclient to get a DHCP address, like so...
root@beaglebone:~# dhclient wlan1
root@beaglebone:~# ping www.google.com
PING www.google.com (216.58.216.228) 56(84) bytes of data.
64 bytes from ord31s22-in-f4.1e100.net (216.58.216.228): icmp_seq=1 ttl=50 time=75.2 ms
^C
--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 75.251/75.251/75.251/0.000 ms
root@beaglebone:~#
The sweet, sweet ping of success.  There is just one more thing before we go automatic on this problem.  Some dongles have issues going into power saving mode.  Let's not take our chances and disable this.  Inspired by this post, create a file called /etc/udev/rules.d/wifi_power_save.rules and add this one long line to it (no line breaks no matter what it looks like below, please).
ACTION=="add", SUBSYSTEM=="net", KERNEL=="wlan*" RUN+="/usr/sbin/iw dev %k set power_save off"
Here is how to check it the next time you boot the board:
root@beaglebone:~# iwconfig wlan1
wlan1 IEEE 802.11bg ESSID:"Abby_Slow"
Mode:Managed Frequency:2.437 GHz Access Point: xx:xx:xx:xx:xx:xx
Bit Rate=5.5 Mb/s Tx-Power=20 dBm
Retry short limit:7 RTS thr:off Fragment thr:off
Encryption key:off
Power Management:off
Link Quality=20/100 Signal level=20/100
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:59 Invalid misc:48 Missed beacon:0
Now obviously you are not going to want to go through this whole process every time you boot the board.  This stuff needs to be automated.  But how?  One thing you could do is trawl the interweb and try to understand how systemd-networkd works.  Then, working from the sparse set of examples on the web, you could manually create some text files against all odds that whatever you come up with is actually going to work.  You could do that, because that is what I tried to do to start with.  I created a 20-dhcp.network file in /etc/systemd/network that should have automatically worked but didn't.  Be smart.  Don't do that.  Do this:
root@beaglebone:~# apt-get update
root@beaglebone:~# apt-get install network-manager
root@beaglebone:~# nmtui
nmtui is the NetworkManager Text User Interface bundled into the NetworkManager package and it is very easy to use.  Ugly, but easy to use.

Ugly, But Works Beautifully
You'll hear a lot more about nmcli, the NetworkManager Command Line Interface.  Don't even try using that unless you are a NetworkManager God.  Stick with nmtui and you'll be good to go in no time.  You can use it to setup connections to open and encrypted access points that will automatically connect up when the board boots.  No fuss, no muss.  It worked perfectly for me.

Until this started to happen...
root@beaglebone:~# [ 2282.574398] INFO: task wpa_supplicant:485 blocked for more than 120 seconds.
[ 2282.581864] Tainted: G O 3.14.26-ti-r42 #1
[ 2282.592586] "echo 0 } /proc/sys/kernel/hung_task_timeout_secs" disables this message.
Then the 'Bone locked up solid.

FML.

I tried once or twice more and the dongle kept locking up hard after a short while, taking the 'Bone along with it.  This is where I started to think that maybe I've got a bad TP-Link dongle.  Fortunately for me, I have another dongle, this one based on a Zydas ZD1211 chipset that is also well supported in Linux.  It needs some firmware that currently isn't in the default image, so I rebooted the 'Bone and did the following before the board decided to crash again.
root@beaglebone:~# apt-get install zd1211-firmware
I plugged in the Zydas dongle and then rebooted the board so the kernel could detect it and load its firmware (might have worked if I hot-plugged it, might not have).  Then I ran nmtui again, set up the dongle, and the network has been working well since then.  But you better believe I'm keeping my fingers crossed.

Incidentally, some people have reported that some dongles work poorly when plugged directly into the 'Bone.  A popular fix seems to be plugging it in via a USB extension cable or powered hub to get a little separation between the two of them.  If you've followed this guide and still can't get things working, try giving that a shot.

This would be a good place to wrap things up with my usual "now go out and do something" line.  But heck, while I'm at this, I might as well throw in some other useful things I picked up along the way to get it all into one place.  If I don't put this stuff here, I'll just be looking for it myself later anyway.
How do you change the name of the dongle's device interface???  See here on the Archlinux wiki.

How do you check out NetworkManager's status?
root@beaglebone:~# systemctl status NetworkManager
How can you see what systemd is putting into its journal for NetworkManager?  (Tip: use the left and right arrow keys to move horizontally a half screen at a time to see long lines of text!)
root@beaglebone:~# journalctl -u NetworkManager
How do I reboot the 'Bone?
root@beaglebone:~# systemctl reboot
How do I power it off?
root@beaglebone:~# systemctl poweroff
Anyhoo, there you have it: a reasonably comprehensive and up-to-date tutorial on how to get wireless networking going on one of the more powerful little ARM boards out there.  Included at no extra charge was a bunch of related systemd stuff that was previously scattered all over the net.  Now get out there and make that 'Bone do something.

No comments:

Post a Comment