Fixing the Broadcom BCM4331 wireless drivers on Ubuntu 13.10

How to restore stable WiFi on the Broadcom Corporation BCM4331 wireless chip after upgrading from Ubuntu 13.04 (Raring) to 13.10 (Saucy Salamander).

Introduction

I have a Macbook Pro 9.2 at work, though the first thing I did upon receiving it was to install (Ubuntu) Linux on it. Surprisingly, after figuring out the nuances of the EFI booting, everything worked surprisingly well. This included wireless, bluetooth and backlight control, all working out of the box.

Now, this all changed when I upgraded to 13.10 however. Suddenly, I no longer found my wireless to be particularly stable. At home, this manifested itself in particularly unstable SSH sessions, though oddly, it was usually fine besides that. At work however, the experience was much worse, giving frequent disconnects, packet loss and overall high latency. The kernel log was also full of ERROR @wl_cfg80211_get_station : Wrong Mac address, though I was seeing this on my home network as well.

Some searching around quickly revealed there are lots of people with similar issues, the general concensus being Broadcom’s proprietary drivers are just, well, crap. (There’s also an opensource driver which does work with the chip in this Macbook, but after trying it out, I can safely say that experience was even worse.)

Fed up with the situation at last, I decided to try and downgrade back to the drivers shipped with 13.04 in an effort to get back to that previous level of stability. To make a long story short, after some research and experimentation, I have successfully downgraded and am now enjoying a stable wireless experience again. The rest of this post will be a guide on how to perform these steps yourself as well, in case you find yourself in the same boat I was in.

Downgrading to 6.20.155.1+bdcom-0ubuntu6

First, grab the 6.20.155.1+bdcom-0ubuntu6 version of bcmwl-kernel-source and then install it. It will fail to build the kernel module once dkms kicks in, but that’s okay. It won’t prevent the package itself from installing.

# Find a mirror close to you at:
# http://packages.ubuntu.com/raring/i385/bcmwl-kernel-source/download (i386)
# or:
# http://packages.ubuntu.com/raring/amd64/bcmwl-kernel-source/download (amd64, like this macbook)
wget http://nl.archive.ubuntu.com/ubuntu/pool/restricted/b/bcmwl/bcmwl-kernel-source_6.20.155.1+bdcom-0ubuntu6_amd64.deb

# And then install it
dpkg -i bcmwl-kernel-source_6.20.155.1+bdcom-0ubuntu6_amd64.deb

To make it build on the 3.11 kernel currently shipped with Ubuntu 13.10, two extra patches are needed. I managed to find these in the 6.30.223.141+bdcom-0ubuntu1 version of bcmwl-kernel-source that we just replaced, but for your convenience I’ve mirrorred them here as well:

The first patch needs to be patched (har har) however to apply cleanly. If you downloaded my version above, I’ve already done this for you.

--- 0008-add-support-for-linux-3.9.0.patch
+++ 0008-add-support-for-linux-3.9.0.patch
@@ -4,8 +4,8 @@
 Subject: [PATCH 1/1] Add support for Linux 3.9
 
 ---
---- a/src/wl/sys/wl_cfg80211_hybrid.c
-+++ a/src/wl/sys/wl_cfg80211_hybrid.c
+--- a/src/wl/sys/wl_cfg80211.c
++++ a/src/wl/sys/wl_cfg80211.c
 @@ -2048,7 +2048,11 @@
				ie_len = (size_t)(ies->len);
				rcu_read_unlock();

Place these into /usr/src/bcmwl-6.20.155.1+bdcom/patches, then adjust /usr/src/bcmwl-6.20.155.1+bdcom/dkms.conf to use them:

--- /usr/src/bcmwl-6.20.155.1+bdcom/dkms.conf
+++ /usr/src/bcmwl-6.20.155.1+bdcom/dkms.conf
@@ -12,6 +12,8 @@
 PATCH[4]="0005-add-support-for-linux-3.4.0.patch"
 PATCH[5]="0006-add-support-for-linux-3.8.0.patch"
 PATCH[6]="0007-nl80211-move-scan-API-to-wdev.patch"
+PATCH[7]="0008-add-support-for-linux-3.9.0.patch"
+PATCH[8]="0009-add-support-for-linux-3.10.0.patch"
 #PATCH_MATCH[3]="^3.[23456]"
 #PATCH_MATCH[4]="^3.[56]"
 AUTOINSTALL="yes"

At this point, it should build and install successfully:

dkms build -m bcmwl -v 6.20.155.1+bdcom && dkms install -m bcmwl -v 6.20.155.1+bdcom

Assuming that went off without a hitch, you can reload the wl module to use this version now (note: this will of course disconnect you):

rmmod wl && modprobe wl

To verify you really are running the 6.20.155.1 version of the driver, you can check the last few lines of dmesg output. It should show Broadcom BCM4331 802.11 Hybrid Wireless Controller 6.20.155.1 (r326264) (note the version number there).

Making sure it stays put

Finally, you’ll also want to ensure that an update doesn’t pull the newer version back in, which would undo all the hard work you’ve just done. This can be done by pinning the version of bcmwl-kernel-source. Create /etc/apt/preferences.d/bcmwl-kernel-source with the following contents, and you should be all set.

Package: bcmwl-kernel-source
Pin: version 6.20.155.1+bdcom*
Pin-Priority: 1001

Closing notes

None of this is officially supported by Ubuntu of course, so don’t blame me if it doesn’t work or stops working on a kernel update some day in the future :) That said, the procedure is safe, and if you ever want to return back to the original state, you only have to remove the /etc/apt/preferences.d/bcmwl-kernel-source file to undo the pinning, then issue an apt-get upgrade or aptitude upgrade to install the latest version again.