world leader in high performance signal processing
Trace: » dev-management

Device Node Management (/dev)

The /dev directory is where all of the device nodes live. This is a fundamental system for userspace applications to differentiate between devices and to interact with kernel device drivers. The files stored in this directory represent both physical (actual hardware) and logical devices (devices which implement special system functions e.g. /dev/null). In Linux devices are mapped to device files to provide congruity. For example, this mapping allows function calls such as open, close, read, write, etc. to be functional for devices as well as files. This interaction is of course a lengthy topic and deserves a discussion all its own. Here we'll focus on the more mundane management of the actual device nodes themselves.

How exactly do these device nodes get created ? Where do the filenames come from ? How do you know which major/minor number pairs go with which device nodes ?

Historically, this information was all static and maintained completely by hand. With the explosion of device drivers in the Linux world, this constant static list was no longer usable nor scalable, so systems nowadays do everything dynamically. While you could still design your system so that everything in it is statically allocated, it makes more sense to just go with the flow :).

The first attempt at a dynamic /dev was the devfs filesystem and a devfsd userspace daemon. This in time proved to not sit well with the kernel maintainers at the design level (the exact reasons are out of scope here; see this document if you really want to know), and people were not maintaining the code base.

This paved the way for the current system -- userspace management via sysfs and hotplug. Userspace is notified dynamically as to all of the devices that are currently available to the Linux kernel and can create device nodes as it choose. It can also name things as it likes and setup whatever permissions are appropriate.

Here we'll cover the two main methods that are in use in the uclinux distribution by default. You're certainly free to manage your system however you want, so don't take this information to mean it is the only way things can be done.

Configuration

There is a configuration option in the uclinux distribution vendors/user settings menu so you can quickly switch between the two options. The default behavior for all Analog Devices boards before the 2008R1 release was to use a static device nodes table. Starting with 2008R1, we've switched to a pure dynamic /dev.

Miscellaneous Configuration --->
  Device Nodes --->
    [ ] Dynamic
    [ ] Static

Static

The static device table is quite large and can normally be found in the vendors/<company>/<board>/device_table.txt file. Documentation on the exact format of the file can be found in the file itself and thus will not be covered here. Suffice to say it allows you to set the device node filename, permissions, ownership, and major/minor numbers.

When generating root filesystem images, the utilities that do this (such as mkfs.jffs2 or genext2fs) are given this file as an option. This way they know what types of nodes to create without you having to create all of them by hand as a root user on your development system. Once the filesystem images get mounted at runtime, all the device nodes that will ever be needed now exist on the system.

This setup has a few issues such as:

  • a lot of nodes exist that represent devices that do not actually exist or are enabled
  • inodes (and thus space -- memory and disk) get wasted
  • device nodes cannot be created on the fly
  • the major/minor numbers must manually be kept in sync with the major/minor numbers the kernel actually uses
  • systems that may serve a variety of arbitrary/dynamic devices must have everything preallocated (think USB)

However, it is not uncommon for embedded systems to be a constrained environment. In other words, all devices are known ahead of time and is small, so there is no problem with creating the small number of devices statically. You will obviously need to review your system and weigh the benefits to make the right choice.

Dynamic

Even though the /dev directory is managed dynamically at runtime, there are a small number of device nodes that always need to be created statically (such as /dev/console). This list is maintained in vendors/<company>/<board>/device_table-min.txt file. (Yes, all the other device nodes could be created dynamically if you know what you're doing, but it is much easier on people if a few common device nodes are created ahead of time before the execution of any userspace applications such as /dev/null.)

So how do you get from a mostly empty /dev directory to a full useful one? This happens in two steps. During userspace boot, the sysfs filesystem (mounted at /sys) is scanned for devices. All devices found get their respective device nodes created on the fly. Then during runtime as new devices are added or removed (e.g. by device drivers being added or removed, or new devices being plugged in or removed), a hotplug event is sent to userspace and an application processes it to create or remove the associated device node. The exact mechanics do not really matter but if you want to know more, consult the hotplug document.

In the default uclinux distribution, we manage things with the mdev program. The userspace boot step is handled by simpleinit by executing the /etc/rc file. Here we create a ram-backed mount point at /dev and tell mdev to scan /sys to create all the associated device nodes. We tell the kernel to process all further hotplug events by executing mdev and so we handle device nodes on the fly. The policy for filenaming, ownership, and permissions are all maintained at /etc/mdev.conf.

There are some obvious (minor) disadvantages here over a static /dev directory:

  • all nodes created on the fly and thus can create a small bit of overhead during runtime (measure in milliseconds though, so not a real issue for the majority of people)
  • the /dev is typically mounted on a ramfs (the actual ram usage is quite small, so it will really only matter for people who are scrounging for free space on the order of kilobytes)

Examples

Here are some common device nodes you might encounter on your Linux journey

  • /dev/console - This device file is mapped to the console.
  • /dev/dsp - This device is mapped to any audio codecs that are available via the OSS layer.
  • /dev/kmem - This device is mapped to kernel virtual memory space.
  • /dev/mem - Physical memory access.
  • /dev/mtd# - This device provides raw character access to the flash memory (see mtd).
  • /dev/mtdblock# - This device provides block access to the flash memory (see mtd).
  • /dev/mtdr# - This device provides read-only raw character access to the flash memory (see mtd).
  • /dev/null - Everything written to this device is discarded. Reading from this device returns nothing.
  • /dev/ram0 - The first static RAM disk. Historically, it is often used with initrd's or the uClinux MTD mapping for the root filesystem in RAM (but has been replaced by initramfs).
  • /dev/random - A non-deterministic random number generator. It may block due to lack of entropy in the system (common on embedded systems).
  • /dev/rtc# - The RTC devices that are available in the system (typically this is the Blackfin's Real Time Clock (RTC).
  • /dev/sport# - This device file is mapped to a Serial Port (SPORT) on the Blackfin device.
  • /dev/tty - This device file is mapped to the current console.
  • /dev/ttyBF# - This device file is mapped to a Universal Asynchronous Receiver Transmitter (UART) on the Blackfin device.
  • /dev/urandom - Faster, less secure random number generator. When entropy is available, it is as good as /dev/random, but otherwise falls back to a “good” pseudo number generator so it will not block.
  • /dev/zero - This device generates as many zeros as requested when it is read. This device cannot be written to.

For more information:

  • linux-2.6.x/Documentation/devices.txt

Troubleshooting

All major numbers that the kernel knows about are maintained in the file /proc/devices. If your device driver is not listed here, it means that you did not properly register things. Consult your initialization code and the kernel documentation.

The other common file is /proc/misc. All so called “misc devices” (which use the “misc” major number) are listed here.

The minor number is always passed to the registered device driver and it is up to it to interpret its meaning. Any major number that you attempt to use that is not registered will result in an obvious error.

No Such Device

If you get errors such as no such device whenever you open a device node, this is usually because the major number of the device node is not actually registered with the kernel. Creating device nodes because it was not created automatically is a common mistake. Doing this rarely (if ever) fixes the problem. The correct answer is to check and fix your kernel device driver.

Missing Device Node

You tried opening a device node only to find it does not actually exist? This can occur for a variety of reasons, but the answer almost always lies in the kernel, not userspace (see the previous topic as well).

In order for a device node to be created automatically, the following steps need to occur:

  • hotplug support is enabled in the kernel
  • the device drivers are actually available -- either compiled in or loaded as a module
  • the device driver detects the device -- for platform device drivers, proper platform resources must be declared in the board resources file
  • the device driver correctly registers itself via the class/kobject/whatever subsystem -- this will automatically trigger and generate the required hotplug messages
  • there is a userspace application processing the hotplug messages from the kernel -- if you aren't using the default settings with the mdev program from busybox, you will need to make sure things are properly configured in your system