Build Modules and Drivers for NVIDIA Jetson TK1 – L4T 21.1

The release of Linux for Tegra (L4T) 21.1 for the NVIDIA Jetson TK1 brought with it the use of UBoot as the new kernel boot loader. UBoot makes it easier to load modules and drivers dynamically. I thought it would be nice to build a driver for an Intel 7260 PCIe wireless card, and show that as an example.

Note 7-16-15: This article was originally written to address a bug in the software at the time the article was written. The issue has since been resolved (tested under Linux L4T 21.4). The bug exhibited itself as not showing the wireless access points in the connection menu after loading the 7260 firmware. Since the bug has been squashed, to enable wireless for the 7260 all that is needed is to execute in a Terminal:

$ sudo apt-get install linux-firmware

and then reboot the machine. The access points will then show up in the connection menu. However, the information in the article shows how to build a kernel module, which is a useful knowledge in and of itself.

As a preface, I’ll note that I hate upgrades. I’m convinced that the reason that we have software professionals is so they can scurry about and try to deal with everybody “upgrading” everything all the time. In a typical upgrade, bits and pieces are changed which bring about what I am told are good things, but from my perspective it just tends to break all of my stuff that I rely on for day to day stuff. For example, upgrade to 21.1, break the wireless. It’s simple I’m told, just recompile the kernel and drivers.

For some reason, I don’t get real excited about that.

Anyway, I wanted my wireless to work so off to build land. “How hard could it be?” I thought. This changed quickly to “How could that be?”. Cutting to the chase, here’s the video of how to build the modules and drivers. Looky here:

Here’s the Github Repository buildJetsonModules with the scripts. The file gives a pretty good description of how to do the actual building and install.

Here’s the story of some of the trials and tribulations of building the wireless driver. I thought it would be easy and straightforward, which it is up until the part where you fall over the cliff. In my naiveté, I downloaded the Kernel Sources and decompressed the files from the Linux for Tegra R21 page onto my Jetson into /usr/src.

So far so good. I was happy to see that the kernel had the source for the Intel 7260 driver. There are three parts to this. First, there is the wireless Module that loads the driver and does the protocol-ing stuff. There are a couple of these needed, cfg80211 and mac80211. These are built and available on the Jetson in stock configuration, as is a wireless driver for Realtek products called rtlwifi.

The second part is a Driver for the Intel 7260. This is called iwlwifi and consists of Jetson side code which talks to the card. The third part is the Firmware, which is loaded onto the card itself. This is available to the Jetson through a repository:

$ sudo apt-get install linux-firmware

This installs a bunch of firmware for different sets of hardware, but I wasn’t really concerned. This is installed in /lib/firmware.

Eventually I figured out that I needed configure the kernel:

$ make menuconfig

and was promptly transported back to a 1985 era DOS looking tool. I’m old enough to remember “the good old days”, I’ll just say that these types of interfaces make me regret not taking drugs back then. After a whole bunch of fiddling to actually figure out how to build the driver for iwlwifi, I saved the configuration .config file.

Let me just add, the Terminal program on Ubuntu needs to be taken out back and shot. The great thing about menuconfig is that it expands to the size of the Terminal window. And as you know, the Terminal type is only 80 characters so there’s not much of a need to ever expand the width of the Terminal window. However, menuconfig spills over the 80 character limit (like in the name for the iwlwifi driver). It didn’t really occur to me that I could make the Terminal window wider than 80 characters and see any change. Until I accidentally resized the window, then I was really pissed off.

The interface for menuconfig is just like an adventure game of the 1980s too, “a twisty passage of halls, all alike”, everything randomly scattered about. Seriously, someone needs to modernize that stuff.

The first build was uneventful once I figured out where everything should go. I just built the iwlwifi driver:

make prepare
make modules_prepare
make modules SUBDIRS=drivers/net/wireless

and copied the resultant iwlwifi folder to the correct spot into the intuitively named /lib/modules/3.10.40-g8c4516e/kernel/drivers/net/wireless folder, and rebooted. Nothing showed up in the network menu, which was disappointing but not surprising. Looking through the dmesg log, I saw that the version of the driver didn’t match the version of the kernel, so the driver wasn’t loaded. To be clear, the driver version was 3.10.40, and the kernel version is: 3.10.40-g8c4516e. Seriously?

Back to menuconfig land, and try to figure out how to set the postfix to match. Ok, fix that (eventually) reload and reboot. Still no joy. It turns out that it tried to load the driver, but there was a missing symbol in mac80211. This confused me a little, as I thought that the source that was provided was the same as what was running. How could a symbol be missing?

Once I got over that, build mac80211 too:

make prepare
make modules_prepare
make modules SUBDIRS=net/mac80211
make modules SUBDIRS=drivers/net/wireless

Install the mac80211 module into /lib/modules/3.10.40-g8c4516e/kernel/net/mac80211. Tell the Jetson to rebuild the Module symbol table:

depmod -a

Reboot …

OK. Dmesg says that cfg80211 rolled over and died. Here’s the thing, there’s no map somewhere that says cfg80211 is the “wireless” directory. So it’s like a hunt just to figure out where it is in menuconfig and where it should go after building:

make prepare
make modules_prepare
make modules SUBDIRS=net/wireless
make modules SUBDIRS=net/mac80211
make modules SUBDIRS=drivers/net/wireless

Of course, it goes in /lib/modules/3.10.40-g8c4516e/kernel/net/wireless. Rebuild module symbol table. Reboot.

Now we’re getting somewhere. Rfkill has killed it, and turned off iwlwifi. This is good, because I remember what the answer is to that issue. It turns out that the Jetson needs to turn on General Purpose Input Output (GPIO) 191 to be able to talk to the mini-PCIe card. That’s no problem, the solution is to add:

echo 191 > /sys/class/gpio/export;
echo out > /sys/class/gpio/gpio191/direction;
echo 1 > /sys/class/gpio/gpio191/value;
# Reload the wifi driver for the Intel mini PCIe card
modprobe -r iwlwifi;
modprobe iwlwifi;

to the file /etc/rc.local

Reboot, and Voila! Wireless Access! All is swell again. Until the next upgrade …


You’ll see error messages when you’re building the Modules and Drivers. These take the form “Could not find Module.symvers”. There are several reasons for this, but as far as I can figure Module.symvers is generated when the kernel itself is compiled and ought to be provided to the user as part of the kernel build environment package. I couldn’t find it (which doesn’t mean that it’s not available, just that I lost patience hunting for it). So what do you give up by not having it? Basically you give up versioning of the module, but that wasn’t something I really care about right now.

To me, the point of modules is that you should only have to build the module to add it to a system. If you have to compile the entire kernel, all the modules and drivers in order to get them to work, ain’t much point in modules is there?