January 21, 2005

USB Wi-Fi adapters under Linux

Author: Keith Winston

I needed to connect my new desktop PC wirelessly from my second floor office to my first floor network. As I started researching the options for wireless USB adapters, I realized I might have some work ahead of me. Wireless USB in Linux is still in the early stages of development. But a little searching and some trial and error led to a successful connection.

While I did not expect the configuration to be easy, I did not expect it to be especially difficult either. To be fair, a lot of the complexities have nothing to do with the USB drivers, but are more related to the device naming and mapping changes that occurred in the upgrade from the 2.4 to the 2.6 kernel.

To better handle dynamic, hotplug devices on USB and FireWire, changes were made in the 2.6 kernel to provide persistent device names. The kernel now handles device management via two subsystems called sysfs and udev. If you run a 2.6 kernel, you may notice a new virtual directory called /sys in the root of your system. The /sys directory works like /proc in that it maps directly to part of system memory. While /proc tracks kernel parameters and state, /sys tracks device names known to the system. The device names in /sys are persistent because they are based on unique hardware and bus identifiers. This allows the kernel to always assign the same name to a dynamic device, something that was not possible in the 2.4 kernel.

In the 2.4 kernel the order you plug in USB devices can affect the name that gets assigned to it. The name of a device is neither unique nor guaranteed. The sysfs subsystem in the 2.6 kernel tries to solve that problem by naming devices using a unique identifier. The result is something not very useful to humans, as the device name for my wireless USB adapter turned out to be /sys/devices/pci0000:00/0000:00:02.1/usb3/3-1/3-1:1.0. That name is not very handy to deal with, so the udev subsystem provides a mapping between the /sys device name and the more familiar device names like /dev/wlan0.

Configuration

My initial research on wireless USB adapters landed me at the hardware compatibility list maintained by Absolute Value Systems, the company that maintains the linux-wlan project and provides drivers for several wireless USB and PCMCIA chipsets. After scanning the compatibility list, I decided to get the Linksys WUSB12 802.11b adapter. It was inexpensive and seemed to be supported.

My primary desktop runs SUSE Professional 9.1 using the 2.6.5 kernel. I plugged the new wireless adapter into an open USB port and fired up the SUSE administration tool, YaST, to see if it had been recognized. No dice. I ran an lsmod command to see if the kernel had recognized it, and was pleased to see the prism2_usb kernel module loaded. I made an attempt to configure the wireless USB card in YaST anyway, and YaST dutifully created a configuration file for it, but I was no closer to getting it to work. It was time to look at the wireless USB configuration itself.

The default location for configuration files is in /etc/wlan/ and the main file is
wlan.conf. Wlan.conf is a shell script, and the only thing it does is set some environment variables.

There are only three settings in wlan.conf to check:

WLAN_DEVICES="wlan0"
SSID_wlan0="stayoutofmynet"
ENABLE_wlan0="y"

The SSID_wlan0 setting is the network identifier for the wireless network. The system looks for a network-specific configuration file based on the SSID_wlan0 setting. In this example, it looks for a file called /etc/wlan/wlancfg-stayoutofmynet for additional settings. If it does not find the network-specific file, it reads the /etc/wlan/wlancfg-DEFAULT file. The wlancfg-stayoutofmynet file contains mostly WEP encryption settings, including which key to use and the key itself.

Another important file in the process is /etc/wlan/shared, a shell script that contains the functions to enable, start, and stop the network interface. There is no need to change the /etc/wlan/shared file; it is called by other scripts to do the dirty work.

In theory, when the adapter is plugged into a USB port, the hotplug system fires an event that loads the kernel modules, then the udev and sysfs subsystems assign a name to the device and interface. When the interface is registered, more hotplug events are triggered to read the configuration files. There is a lot of new code in play here, including the SUSE shell scripts that glue everything together. After much anguish and gnashing of teeth, I could not get it to work using YaST and the default SUSE configuration. Maybe it would have worked if I had turned off encryption on the access point, but I was not going to run a wireless network and be undefended.

Manual labor

The configuration program for wireless USB cards is called wlanctl-ng. The configuration scripts that come with the linux-wlan package call wlanctl-ng with various non-intuitive parameters. Going back to Google, I found a few pages that showed manually constructed working configurations. I eventually distilled a script that has been working well for me:

#! /bin/sh
#
# Wireless USB setup
#

# Step 1 - enable wireless USB for wlan0
wlanctl-ng wlan0 lnxreq_ifstate ifstate=enable

# Step 2 - set SSID for your network
wlanctl-ng wlan0 lnxreq_autojoin ssid=stayoutofmynet authtype=opensystem

# Step 3 - set WEP attributes
wlanctl-ng wlan0 lnxreq_hostwep encrypt=true decrypt=true
wlanctl-ng wlan0 dot11req_mibset mibattribute=dot11PrivacyInvoked=true

# Step 4 - set WEP key
wlanctl-ng wlan0 dot11req_mibset mibattribute=dot11WEPDefaultKeyID=3
wlanctl-ng wlan0 dot11req_mibset mibattribute=dot11WEPDefaultKey3=12:34:56:78:9A

# Step 5 - set IP configuration
ifconfig wlan0 192.168.1.100 netmask 255.255.255.0 broadcast 192.168.1.255
route add default gw 192.168.1.11

I have used this script on both SUSE 9.1 and SimplyMEPIS 2004.06 and found it to work well for both. It should work on any 2.6 kernel-based distribution with the wlan-ng package installed, but you'll need to customize it for your own system:

Step 2 needs to be modified to match the SSID of your wireless network.Step 4 needs to be modified to match your encryption key ID and key. This setting is zero-based, so the first key is KeyID=0. I was using key 4, and thus KeyID=3 in my example. On the following line, the mibattribute=dot11WEPDefaultKey3 value changes depending on which key you are using. For example, if you are using KeyID=0, then the
following line would use mibattribute=dot11WEPDefaultKey0=12:34:56:78:9A. To complicate things further, the encryption key value can be extended. My example uses low-grade encryption. If you are using 128-bit encryption, extend the length of the key value.Step 5 needs to be modified for the right IP address and gateway.

DISCLAIMER: You are free to use this script, hack it, copy it, sell it, digest it, and pass it through your body. However, it comes with zero technical support.

I changed my boot configuration to call this script at the very end of the boot process after all the other services are up.

The Linuxant option

While researching, I ran across Linuxant's DriverLoader product, which allows you to use the Windows binary drivers that ship with many wireless cards in Linux. It is not free software, but has a low cost and can be used for a free 30-day trial.

The Linuxant Web site provides binary packages for a number of Linux distributions. It also has the source available if you want to compile it to work with your custom kernel. I have not tested DriverLoader, but I have read testimonials indicating that it works well for supported hardware. NewsForge covered it recently.

Filling the ether with bits

After several months of continuous use, I am pleased to report the Linksys WUSB12 adapter is holding up well. It does not overheat and the throughput matches my laptop's old PCMCIA card.

A few years ago, wireless USB was leading edge technology. I still consider it leading edge in the Linux world, but with a little elbow grease, your Linux systems
can fill the ether with bits.