First FreeBSD experience: something with Wi-Fi and Apple hardware
Asking What is the Wi-Fi password? gets usually the job done. You're connected to the Internet and ready to watch the latest race of Jelle's Marble Run. But what if your operating system does not know how to control your wireless adapter?
Some background
When looking back at the history of operating systems, I've always found the BSD
family quite fascinating. Today, BSD's influence is still reflected in many
operating systems including the open-source FreeBSD and NetBSD, but also in MacOS.
A large part of the MacOS userland experience on the command line is BSD. Everytime
you use cd
, grep
, ifconfig
, ls
, ssh
, etc on your Mac, you're using
a BSD program (run for example man cd
to see for yourself).
But wait, why FreeBSD in particular? They are many other BSD variants out there. My short answer would be beginner friendliness: FreeBSD has a large community and well-written documentation.
So let's move on. I've got FreeBSD installed on a Macbook Pro (Mid 2015) running macOS Mojave and the Wi-Fi isn't working, what do I do next? Well, 2 things came into my mind:
- I need to know more about the built-in Wi-Fi on my laptop.
- Does FreeBSD support this chip? In other words, can I somehow install a program (i.e. device driver) that will allow FreeBSD to control my Wi-Fi?
System Information
On my laptop, I went to System Information
and under Network -> Wi-Fi
I found
the Ethernet interface en0
. The first 2 lines are:
Card Type: AirPort Extreme
Firmware Version: Broadcom BCM43xx
That's pretty valuable information already. So I went on to see if I can find some information about FreeBSD drivers support.
When trying to look up general information about Broadcom drivers support on FreeBSD you might get quite confusing results. The most reliable information on the subject is probably provided by Landon Fuller, who has been working on improving FreeBSD support for Broadcom Wi-Fi devices. Another more general source on open-source wireless devices can be found on Wikipedia.
But wait, what was one the reasons to choose FreeBSD in the first place? Exactly, its documentation! From my very limited experience, I can highly recommend to just stick to the documentation as much as possible at the beginning of your journey. So let's see, it looks like Section 8.3 from the Handbook is exactly what we need. We already know that our vendor is Broadcom so we can check if we can find a manual page about it. Luckily there are a few man pages where our vendor is mentioned. When we scroll through the list we see that bwn(4) and bwi(4) might be exactly what we are looking for. Cool, we're already making quite some progress!
Ports and Packages
After reading through the manual pages, it turns out that the difference between
bwn(4)
and bwi(4)
is that the former uses a newer (i.e. v4) version of the
Broadcom's firmware whereas the letter uses v3. Furthermore, both require
an addinational port to be installed (ports/net/bwn-firmware-kmod
or
ports/net/bwi-firmware-kmod
, respectively).
At this point, we don't really need to know which driver we'll need, but what we do want to know is what ports are and how to install one without direct Internet access. Again the Handbook is the perfect source for this kind of information. Scanning through Chapter 4 we learn that a port is a set of Makefiles, patches and description files which has to be compiled first, whereas a package is already pre-compiled and available as a binary.
So can we maybe get bwn-firmware-kmod
port as a package? A quick search on
FreshPorts
reveals that there is no package available of this port; reason: this is a
modified version of a restricted firmware. We also learn that this port
requires kernel source files in /usr/src and has 1 build dependency called
b43-fwcutter
which
is a firmware extractor.
Build system
To get a better understanding of the build system, let's take a look at the
Makefile
for bwn-firmware-kmod
. As always the Handbook will help us to understand
what's going on exactly. In this case we need to read
Section 5.4.
In short, when building the port the compiler will
- Try to find 3
DISTFILES
inside a localDISTDIR
(which defaults to/usr/ports/distfiles
) or otherwise try to download them from one of the providedMASTER_SITES
. - Then it will use the
b43-fwcutter
to extract the firmware from the drivers - The final step is to add some other firmwares and build 3 kernel modules
Looks pretty straightforward so far, but we are not there yet. The build will
need some additional files and settings such as requirements declared by the
USES
macro.
In this case its values are kmod
and uidfix
. Moreover, every Makefile
must end
with bsd.port.mk
which also requires a bunch of other files for licensing, checksums, patching, etc.
To make your life easier I've created a small shell script
that will download the bare minimum you need to successfully build bwn(4)
and bwi(4)
without relying on an Internet connection. The shell script will create and
populate the following directories under usr/ports
:
├── Keywords
├── Mk
│ ├── Scripts
│ ├── Uses
├── Templates
├── devel
│ └── gmake
├── distfiles
├── net
│ ├── bwi-firmware-kmod
│ └── bwn-firmware-kmod
└── sysutils
└── b43-fwcutter
In additional the script will also download the kernel source code and place it
under usr/src
. See
Section 23.5.3
and Appendix A.3.5
for more details about the source code. Make sure to download the correct version, otherwise
you'll get a version mismatch error when trying to load the module later on.
Note: An alternative approach would be to download the entire ports collection instead of cherry-picking.
Kernel modules
Once we have all dependencies we can build our ports! But wait, first we need to
transfer all these files to our offline FreeBSD installation. Yeah that's right.
Probably the easiest way is to use an USB flash drive. When partitioning
we need to make sure that FreeBSD will recognize our flash drive. A safe choice would be
the Master Boot Record partiion scheme and a FAT format such as FAT32. Depending on your FreeBSD
settings the OS might try to automatically mount your flash drive. You can run for example
gpart status
to see if your device has been recognized.
Section 3.6
provides more information on how disks are organized in FreeBSD. In my case, I can unmount my USD
drive with umount /dev/da0s1
and then mount it back again with mount -t msdosfs /dev/da0s1 media
to the media
directory.
Finally we can build our ports! Let's start with bwn(4)
(the same steps also apply to bwi(4)
).
According to the ports website we should use make install clean
to install the port,
so let's do that. If the build was successful ls /boot/modules | grep bwn
should show
the three freshly built kernel modules! Now we can add the following lines to /boot/loader.conf
if_bwn_load="YES" # loaded from /boot/kernel
if_v4_ucode_load="YES" # replace by if_v4_lp_ucode_load="YES" for low power PHY
After rebooting our machine (e.g. shutdown -r now
), run kldstat
to check if the
kernel modules have indeed been loaded. If something went wrong, you might try to load
them manually using kldload
and check the output of dmesg
for some useful debug information.
Great, we've successfully built our first kernel modules! The next step is crucial and very
exciting. Run sysctl net.wlan.devices
to see if the kernel can operate our Wi-Fi
device using the driver(s) we just installed. Depending on which driver you used the output
should probably be either bwn0
or bwi0
. Cool, this was the hardest part. The rest is
just a matter of Wi-Fi configurations. There are many resources out there which you can follow.
One of them is this YouTube video by RoboNuggie.
Small remark regarding the video: you might want to use service network restart
instead of
service netif restart
.
Does my Wi-Fi finally work?
Unfortunately, for me the answer was still No. It turned out that on my machine BCM43xx
stands
for BCM4360
(or to be more specific: BCM43602
) which is a quite modern chipset and neither
bwn(4)
nor bwi(4)
supports it. According to Landon Fuller, FreeBSD
might support
these newer chipsets in the future.
Note: OpenBSD and NetBSD already support BCM43602.
Easy way out
After running out of options, I was forced to use the Ralink wireless adapter from my Raspberry Pi.
Luckily, the RT5370 chipset is supported by the run(4)
driver which is part of the FreeBSD kernel.
So adding the following two lines to /boot/loader.conf
sealed the deal:
if_run_load="YES"
runfw_load="YES"
Now I can finally watch the latest race of Jelle's Marble Run on FreeBSD!