Drivers and Device Files
Last edited
Pithy
A driver translates between a specific hardware device and the kernel’s standard interface.
A device driver is an abstraction layer that manages the system’s interaction with a particular type of hardware so that the rest of the kernel doesn’t need to know its specifics. The driver translates between the hardware commands understood by the device and a stylized programming interface defined (and used) by the kernel. The driver layer helps keep the majority of the kernel device-independent.
Drivers for other operating systems (e.g., Windows) do not work on UNIX and Linux.
Device files
In most cases, device drivers are part of the kernel; they are not user processes. However, a driver can be accessed both from within the kernel and from user space, usually through “device files” that live in the /dev directory.
The files in /dev each have a major and minor device number associated with them. The kernel uses these numbers to map device-file references to the corresponding driver.
Important
When a program performs an operation on a device file:
- the kernel intercepts the reference
- looks up the appropriate function name in a table
- transfers control to the appropriate part of the driver
Pseudo Devices
It is sometimes convenient to implement an abstraction as a device driver even when it controls no actual device.
Such phantom devices are known as pseudo-devices.
Example, a user who logs in over the network is assigned a pseudo-TTY (PTY) that looks, feels, and smells like a serial port from the perspective of high-level software.
This trick allows programs written in the days when everyone used a physical terminal to continue to function in the world of windows and networks. /dev/zero, /dev/null and /dev/urandom are some other examples of pseudo-devices.
Driver Example
Samsung Bar 3.1 plugged in:
- Kernel detects device, loads USB mass storage driver
- Driver creates
/dev/sdb, auto-mounts to/media/samsung - User:
cp test.txt /media/samsung/test.txt - The following pseudocode runs:
// User opens file on mounted filesystem
open("/media/samsung/test.txt", O_WRONLY | O_CREAT)
// Kernel creates file descriptor pointing to inode on /dev/sdb
// fd = 3
// write() syscall for the mounted filesystem
write(fd, "hello world", 11)
// We're calling write on /dev/sdb
// Kernel intercepts, translates to block device write
block_write(/dev/sdb, sector_123, data)
// Kernel looks up driver for major:minor 8:0
driver_function = driver_table[8][0]
// Calls driver
// Samsung bar is a generic USB device so it uses a generic USB driver
usb_mass_storage_write(sector_123, data)
// Driver sends USB WRITE command to Samsung Bar
// Device writes to flash, returns success
Device file management
When a new device is detected, both systems automatically create the device’s corresponding device files.
When a device goes away (e.g., a USB thumb drive is unplugged), its device files are removed.
/dev: interface to talk to the device/sys: metadata about the device
Example:
/dev/sdb → write data to it
/sys/block/sdb/size → query how many sectors it has
/sys/block/sdb/queue/hw_sector_size → query sector size
/sys/devices/pci0000:00/... → IRQ, power state, driver infoudev
udev is a daemon that:
- listens for when devices appear in
/sys - reads its metadata
- creates the appropriate
/dev/file with the right permissions - can run scripts/actions (mount, notify, etc.)
Example: plug in Samsung Bar -> udev creates /dev/sdb and auto-mounts it.
Without udev, you’d manually create /dev files for every device.
udevadm
udevadm is a tool that queries device info. It can be used to control the udevd daemon and help admins build and test rules for udev.
udevadm output
The results are layered kernel abstractions.
udevadm info -a -p /block/sdb/sdb1 Top layers = arbitrary kernel labels
Bottom layers = actual physical/electrical path from your USB port to the PCI bus
Rules and persistent names
udevd relies on a set of rules to guide its management of devices.
- default rules
/lib/udev/rules.d/*.rules - local rules
/etc/udev/rules.d/*.rules
You never need to edit or delete the default rules; you can ignore or override a default rules file by creating a new file with the same name in the custom rules directory.
Rules are in the form of:
match_clause, [match_clause, ...] assign_clause [assign_clause, ...]Match clauses
The match clauses define the situations in which the rule is to be applied.
The assignment clauses tell udevd what to do when a device is consistent with all the rule’s match clauses.
Most match keys refer to device properties (which udevd obtains from the /sys filesystem), but some refer to other context-dependent attributes, such as the operation being handled (e.g., device addition or removal).
| Match key | Function |
|---|---|
| ACTION | Matches the event type, e.g., add or remove |
| ATTR{filename} | Matches a device’s sysfs values |
| DEVPATH | Matches a specific device path |
| DRIVER | Matches the driver used by a device |
| ENV{key} | Matches the value of an environment variable |
| KERNEL | Matches the kernel’s name for the device |
| PROGRAM | Runs an external command; matches if the return code is 0 |
| RESULT | Matches the output of the last call through PROGRAM |
| SUBSYSTEM | Matches a specific subsystem |
| TEST{omask} | Tests whether a file exists; the omask is optional |
Assignment clauses
The most important assignment key is NAME, which indicates how udevd should name a new device.
Creating a rule
Insert my USB
lsusb Bus 001 Device 002: ID 413c:2113 Dell Computer Corp. KB216 Wired Keyboard Bus 001 Device 004: ID 6557:0021 Emtec USB DISK 2.0 # my USB # We can also watch the kernel logs to figure this out journalctl --dmsg -f Jun 07 13:17:49 main kernel: usb 1-5: Product: USB DISK 2.0 Jun 07 13:17:50 main kernel: sd 8:0:0:0: [sdb] 60628992 512-byte logical blocks: (31.0 GB/28.9 GiB) # The drive was recognized as sdb # So that means udevd will store the drive metadata in `/sys` ls /sys/block nvme0n1 sda sdbFind unique fields to match
# Use udevadm to investigate and find the matches we want to use udevadm info -a -p /block/sdb/sdb1 # Filter the results and use these to match this device ATTRS{model}=="USB DISK 2.0" ATTRS{serial}=="070823B935AD4425"Write a rule for this
sudo vim /etc/udev/rules.d/10-local.rules # Simple match for the serial number and write to a file when the drive is added (inserted) ACTION=="add", ATTRS{serial}=="070823B935AD4425", KERNEL=="sd[a-z]*", RUN+="/bin/sh -c 'echo matched >> /tmp/ate-flash-test.log'"Testing
# Plug in the USB cat /tmp/ate-flash-test.log matched # Success, the rule triggered