Connect your devices with udev

From LXF Wiki

Revision as of 19:14, 30 Jan 2009; view current revision
←Older revision | Newer revision→
Table of contents

DEVICE-NAMING SYSTEM: Connect your devices with udev

(Original version written by Neil Bothwick for LXF issue 66.)

Learn how to code a name for your hardware when 'Bob' just won't do.

'Everything is a file' is one of the Unix creeds. It sounds strange at first, but in a way it's true. Of course, we're not suggesting that your hard disk is a file—we all know it's a precision-engineered piece of electromechanical hardware designed to store as much of your valuable data as possible before crashing the heads into the disk and destroying the lot.

However, your hard disk is represented as a file in the Linux filesystem, usually as /dev/hda/. You probably already know this, but any piece of hardware you connect to your computer is represented by a device file in /dev, be it your MP3 player or your webcam.

The /dev directory was originally a standard directory containing device files for every piece of hardware likely to be connected. This usually meant that whenever a driver was installed, the relevant files were created in /dev. This had two really important disadvantages. The first was that as more devices were supported, the number of files in the directory was becoming unmanageable.

It also meant that if you tried to connect a piece of hardware for which there was no device file, you had to create it yourself, first scouring Google for the correct major and minor device number to pass to the mknod command.

As the number of devices supported by Linux increased, especially the huge number of removable devices that could be connected to USB or IEEE1394 (aka Firewire) ports, this became unacceptable. Not only was /dev becoming totally unwieldy, but we were in danger of running out of major and minor device numbers to cover every possible device that could be connected, even though any one computer would only ever see a tiny fraction of them.

The solution was devfs, a system in the Linux kernel that would react to devices being connected or discovered and create their /dev entries automatically. While this improved the situation, there are some issues with devfs that mean its use is now deprecated. The main problem with devfs is that it has a number of bugs, ranging from annoying to serious, some of which cannot be fixed.

Knowing u – a-ha!

Udev is a new alternative developed by Greg Kroah-Hartman that can do all that devfs needs to do but in user space, avoiding the need to keep any code for it inside the kernel. Using the new /sys filesystem from kernel 2.6 and the hotplug system for connecting peripherals, all the device node creation is handled by user space programs. As devfs is not being actively maintained now, udev has become the default choice. If you have installed a recent distribution, you probably already have udev without realising it.

At this point, you may be wondering why all this matters to you. After all, the main differences between devfs and udev seem to lie in the implementation, and how they affect the system from a development point of view. So how does it affect the end user? Well, we've saved one of udev's best advantages until last, and it's a feature that will make a real difference to you.

The feature is called persistent device naming, and it works like this. Devices are normally named in the order in which they are connected. That’s fine if you only have one of each type of device, but this is becoming less common. For example, many devices use the USB storage module to appear as disk drives. These include digital cameras, MP3 players, USB key disks and memory card readers, as well as external disk drives.

If you connect your camera, say, it will often be seen as /dev/sda/. If you then hook up your USB keyring it will appear as /dev/sdb/. But if you connect the keyring first, that will appear as /dev/sda/.

This makes dealing with these devices through fstab entries or automounters more complex than it needs to be. The situation is potentially worse with printers. I have two USB printers: a laser for text documents and an inkjet for printing photographs. One is /dev/lp0 and the other /dev/lp1, but which gets which depends on which is detected first. If one of the printers is turned off when I boot, the devices can be reversed.

Udev fixes this nonsense by enabling you to specify your own device names for each product. Using a simple set of rules, udev will set the device name according to the identification data available from each device. It will also create symlinks, so a device can have more than one name. For example, a DVD-ROM drive could be accessed as any one of /dev/hdc, /dev/cdrom or /dev/dvd. So, how do we write our own udev rules?

Making up the rules

The rules are contained in files in /etc/udev/rules.d. The default file is usually called 50-udev.rules. Don't change this file as it could be overwritten when you upgrade udev. Instead, write your rules in a file called 10-udev.rules. The low number ensures it will take priority over any definitions in the default file.

Each time a device is detected by the hotplug system, the files are read in order, line by line, until a match is found. This may be useful in more complex systems as you can set up specific rules followed by more general ones—but we're getting ahead of ourselves here.

The basic format of a rule is:

key1=”value”, key2=”value”, ... keyN=”value”,name=”value”, symlink=”value”      

You must provide at least one key and a name. Extra keys are optional, but all must match for the rule to be applied. Symlinks are optional too. Here is an example of a udev rule, used to detect and name an iRiver MP3/Ogg player.

BUS=”usb”, KERNEL=”sd[a-z]1”, SYSFS{product}=”iRiver H300 Series”, NAME=”%k”, SYMLINK=”usb/iriver”  

The first three items are keys used to identify the device. The NAME, as you would expect, defines the name to be used. %k is the name that the kernel would have given it, such as /dev/sda1, so this rule leaves the name unchanged, but sets a symlink to /dev/usb/iriver. The /dev/usb directory does not need to exist, as udev will create it when needed and delete it when the last device in there is removed. There is no standard convention to use /dev/usb; I just find it convenient to have all hotplugged USB devices appear here.

There are other substitutions that can be used in NAME and SYMLINK. After %k, %n is probably the most useful (it contains the kernel number of the device). If %k contains sda3, %n contains 3. See the udev man page for a full list of substitutions.

Configuring udev

The real work is done by the keys, of course, so how do we know what to use here? There are several keys available but the three most useful ones are BUS, KERNEL and SYSFS.

  • BUS covers how the device is connected.
  • KERNEL refers to the standard kernel identification of the device (as used by devfs or a static /dev).
  • SYSFS keys use the information on each device that appears in the /sys directory. This directory was added for kernel 2.6 and is a virtual filesystem, somewhat like /proc, containing information on various devices.

You can browse through this filesystem to find information on a device, but udev provides a tool to make this task easier. The udevinfo command is used to extract information from /sys. You will need to be logged in as root to do most of this, so open a terminal window and type su to become root. Now plug in your USB device, wait a few seconds for it to be detected, type dmesg and look for information on the device at the end of the output. It will look something like this:

usb 1-1: new high speed USB device using ehci_hcd and address 6
scsi8 : SCSI emulation for USB Mass Storage devices
usb-storage: device found at 6
usb-storage: waiting for device to settle before scanning
 Vendor: TOSHIBA Model: MK2004GAL        Rev: JC10
 Type: Direct-Access               ANSI SCSI revision: 00
SCSI device sdd: 39063024 512-byte hdwr sectors (20000 MB)
sdd: assuming drive cache: write through
SCSI device sdd: 39063024 512-byte hdwr sectors (20000 MB)
sdd: assuming drive cache: write through
sdd: sdd1
Attached scsi disk sdd at scsi8, channel 0, id 0, lun 0
Attached scsi generic sg5 at scsi8, channel 0, id 0, lun 0, type 0
usb-storage: device scan complete