bad.robot

good robots do what they're told

Disable Edimax Wifi Dongle's LED

Pi specific instructions to disable the LED on the Edimax EW-7811UN USB wireless adapter.

The only way I found to disable the LED is by modifying the kernel module. Compiling that meant recompiling the associated kernel to get all the dependencies lined up.

If you don’t want to have a go at compiling the kernel, you can always download the output of my efforts here (built against 4.9.17-v7+).

Gather Information

You’ll need to know the specific kernel version. Run the following.

$ uname -a

It’ll show something like this

Linux raspberrypi 4.1.13+ #826 PREEMPT Fri Nov 13 20:13:22 GMT 2015 armv6l GNU/Linux

The Edimax uses the 8192cu module. You can check it’s loaded with lsmod. You’ll see something like this.

$ lsmod
Module                  Size  Used by
cfg80211              499834  0
rfkill                 22491  2 cfg80211
8192cu                569532  0
...

For interest, you can get more information running modinfo 8192cu.

filename:       /lib/modules/4.1.13+/kernel/drivers/net/wireless/rtl8192cu/8192cu.ko
version:        v4.0.2_9000.20130911
author:         Realtek Semiconductor Corp.
description:    Realtek Wireless Lan Driver
license:        GPL
srcversion:     133EACDEB0C6BEBC3ECA8D0
vermagic:       4.1.13+ preempt mod_unload modversions ARMv6
...

Get the Source

You need both the module and kernel source.

The latest driver version (v4.0.2_9000) on the Realtek site isn’t actually the latest version. At least, it’s been modified for the Pi. The good news is that the modified version is bundled with the Pi kernel source at https://github.com/raspberrypi/linux.git. On the Pi, run the following (matching your running kernel version with the --branch option).

$ git clone --branch=rpi-4.1.y --depth=50 https://github.com/raspberrypi/linux.git
$ ln -s linux linux-$(uname -r)

The git clone command will download the full source (including headers and all built-in drivers) into a new folder called linux. The symbolic link is just a handy reminder of what you’ve cloned.

The latest source may not match your running kernel version (uname -r). You can check in the Makefile;

VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
...

This is version 4.1.15 whereas my version was 4.1.13. Major versions are stored as branches in the repository (hence the --branch=rpi-4.1.y option above) but if like me, your version is a minor level, you have to scan the commits from the appropriate branch. For example, 4.1.13 and 4.1.12 were documented by Greg Kroah-Hartman in the commit messages. You could also try something git log --oneline | grep "Linux 4.1.18" to save manually scanning the logs.

The upshot is that you may need to roll back to the revision that is specifically for your kernel version. That’s why I used --depth=50 in the hope of catching the revision I’m interested in.

$ cd linux
$ git checkout 1f2ce4a2     # the SHA of your specific version, this is 4.1.13

Manually Install the Headers

Compiling anything in Linux usually requires you have the kernel header files available. The usual way to get these is to run apt-get install linux-headers-$(uname -r) but the maintainers for the Raspberry Pi linux distribution don’t make them available like this. Instead, we have to rely on the full kernel source you’ve just downloaded.

Create a symbolic link to fill in for the missing build folder in /lib/modules before you try and compile the driver:

$ cd ..
$ ln -s linux /lib/modules/$(uname -r)/build

This creates the missing folder but points at the newly downloaded source. It’s what fixes the infamous error;

make[1]: *** /lib/modules/4.1.13+/build: No such file or directory

Setup your Config

Before we build the kernel, we need to create a .config file containing the current kernel configuration. The current config should be in the /proc/config.gz file on the Pi. If the file doesn’t exist, run sudo modprobe configs and check again.

$ cd linux
$ zcat /proc/config.gz > .config

Compile the Kernel

This isn’t as scary as it sounds. We need to compile the kernel source. We’re not going to install it, but we do want to create various dependencies that are needed to compile the driver. For example, compiling the driver would fail with missing files like include/generated/autoconf.h or include/config/auto.conf. Compiling the entire kernel is probably a bit overkill but I’ve found it easier than chasing down individual errors.

Before compiling the kernel, get some extra dependencies

$ sudo apt-get install build-essential
$ sudo apt-get install libncurses5-dev      # required for menuconfig
$ sudo apt-get install bc                   # required for timeconst.h

You can have a go at running just make from the linux folder at this point but various options need to be set and it’s probably easier to use menuconfig. Make sure you created the .config from earlier then run the following.

$ cd linux
$ make menuconfig

Scan the options but as they’re based on your current settings (via .config), you should just be able to quit (ESC, ESC) and something like the following will be output.

  HOSTCC  scripts/kconfig/mconf.o
  HOSTCC  scripts/kconfig/zconf.tab.o
  HOSTCC  scripts/kconfig/lxdialog/checklist.o
  HOSTCC  scripts/kconfig/lxdialog/util.o
  HOSTCC  scripts/kconfig/lxdialog/inputbox.o
  HOSTCC  scripts/kconfig/lxdialog/textbox.o
  HOSTCC  scripts/kconfig/lxdialog/yesno.o
  HOSTCC  scripts/kconfig/lxdialog/menubox.o
  HOSTLD  scripts/kconfig/mconf
scripts/kconfig/mconf  Kconfig
configuration written to .config

*** End of the configuration.
*** Execute 'make' to start the build or try 'make help'.

The last remaining config files will have now been created, so you can do the actual build with;

make ARCH=arm

or as Avi P points out below if you’re running on a multi-core Pi like the Pi 2 or Pi 3;

make -j4 ARCH=arm

This takes a while; on my Pi Zero, over 12 hours. There’s always the option to cross compile if you’re in a hurry.

For extra background, I found an interesting guide on Stack Exchange about Configuring, Compiling and Installing Kernels (although we’re not going as far as installing the built kernel here).

Modify the Driver

This is the step that actually disables the LED on the dongle.

Locate the autoconf.h file in the drivers folder (linux/drivers/net/wireless/rtl8192cu/include or linux/drivers/net/wireless/realtek/rtl8192cu/include in newer linux versions) and comment out the CONFIG_LED macro definition. It should look like this when you’re done.

// #define CONFIG_LED           // <-- comment this line out to disable LED
#ifdef CONFIG_LED
    #define CONFIG_SW_LED
    #ifdef CONFIG_SW_LED
        //#define CONFIG_LED_HANDLED_BY_CMD_THREAD
    #endif
#endif // CONFIG_LED

Compile the Driver

The dependencies should all be available now, so you’re ready to compile the driver. Compile from the location of driver source (probably linux/drivers/net/wireless/rtl8192cu or linux/drivers/net/wireless/realtek/rtl8192cu).

$ cd linux/drivers/net/wireless/realtek/rtl8192cu
$ make ARCH=arm

Again, use the -j4 flag if you’re on a big boy Pi.

Test & Install the Driver

Once it’s compiled, remove the old driver with sudo rmmod 8192cu and from the driver folder, manually startup the newly compiled one; sudo insmod 8192cu.ko. Note that you’ll loose network connectivity after removing the old module. Make sure you’ve got a way to connect back to your Pi (like a console cable).

Running modinfo 8192cu doesn’t help verify the new driver as none of the meta-data has changed but you can check the datestamp of the .ko and you should see that there’s no LED flashing.

To keep the change, I renamed the patched module to 8192cu-no-led.ko and copied it into the Pi’s main kernel drivers folder. I renamed the original driver to 8192cu-original.ko and created a symbolic link for the true module name 8192cu.ko. This is because I want to be able to swtich back easily and not have to modify any additional configuration (for example, any /etc/modprobe.d/8219cu.conf settings) or black lists.

$ mv 8192cu.ko 8192cu-no-led.ko
$ sudo cp 8192cu-no-led.ko /lib/modules/4.1.13+/kernel/drivers/net/wireless/rtl8192cu/
$ cd /lib/modules/4.1.13+/kernel/drivers/net/wireless/rtl8192cu/
$ sudo mv 8192cu.ko 8192cu-original.ko
$ sudo ln -s 8192cu-no-led.ko 8192cu.ko

You should see something like this.

$ ll
total 1332
lrwxrwxrwx 1 root root     13 Jan 12 17:58 8192cu.ko -> 8192cu-no-led.ko
-rw-r--r-- 1 root root 672500 Jan 12 17:58 8192cu-no-led.ko
-rw-r--r-- 1 root root 686160 Nov 18 16:01 8192cu-original.ko

You can enable the original driver by reassigning the symbolic link.

Common Problems

No armv6l folder

$ cd linux/arch
$ sudo ln -s arm armv6l

or always run the following when compiling

make ARCH=arm

No build folder

$ make[1]: *** /lib/modules/4.9.58+/build: No such file or directory.  Stop.

Create a sym

$ sudo ln -s /home/pi/code/linux build

The folder should look something like this.

$ ll
total 1.9M
drwxr-xr-x 11 root root 4.0K Oct 27 18:52 kernel
lrwxrwxrwx  1 root root   19 Oct 29 11:33 build -> /home/pi/code/linux
-rw-r--r--  1 root root 471K Oct 27 18:53 modules.alias
-rw-r--r--  1 root root 486K Oct 27 18:53 modules.alias.bin
-rw-r--r--  1 root root 4.7K Oct 27 18:52 modules.builtin
...
-rw-r--r--  1 root root 249K Oct 27 18:53 modules.symbols.bin

Edimax Sleeps and Drops the Network

Setup some config in the modprobe.d folder.

$ cd /etc/modprobe.d/
$ ll
total 16
-rw-r--r-- 1 root root  73 Jan  1 19:45 8192cu.conf
$ cat 8192cu.conf
options 8192cu rtw_power_mgnt=0 rtw_enusbss=0

Over to you...