Linux
Drivers
How the kernel represents hardware, and the paths by which that hardware becomes reachable from userspace — the piece that connects the protocol pages in this notebook to actual running code.
- What it is
- How the kernel matches devices to drivers, and the paths (spidev, i2c-dev, tty, gpiochip) by which userspace reaches them.
- Mental model
- A device tree entry is a job posting; a driver's compatible string is the résumé — the bus matches them and calls probe().
- Key numbers
- 4 generic userspace paths cover most protocols this notebook covers: spidev, i2c-dev, tty, gpiochip.
- Reach for it when
- Debugging why a peripheral isn't showing up, or deciding between a quick userspace hack and a real kernel driver.
The device model, in one paragraph
A bus (platform, SPI, I2C, USB, ...) knows how to
enumerate or is told about the devices attached to it.
A driver registers with a bus, declaring which devices
it knows how to handle (by compatible string, ID table, etc.). The bus
matches devices to drivers and calls the driver's probe()
function for each match. On most embedded ARM/RISC-V boards, the
devices themselves — and which bus/pins/IRQ they're wired to — are
described in the device tree, not hardcoded in the
kernel.
How this notebook's protocols reach userspace
| Protocol | Kernel-side concept | Userspace path |
|---|---|---|
| SPI | SPI master/controller driver + device tree node | /dev/spidevB.C via the spidev driver |
| I2C | I2C adapter driver + device tree node | /dev/i2c-N via i2c-dev |
| UART | Serial (tty) driver | /dev/ttySN, /dev/ttyUSBN, etc., configured with termios |
| GPIO | GPIO controller driver | /dev/gpiochipN via libgpiod |
Note the pattern: each subsystem exposes a generic userspace interface (spidev, i2c-dev, tty, gpiochip) that doesn't know anything about the specific chip on the other end — that chip-specific knowledge (register maps, command framing) lives in application code or, for devices with a real in-kernel driver, in a dedicated kernel module that exposes a higher-level interface instead (e.g. an IIO device for a sensor, rather than raw register access).
Character devices vs. a real kernel driver
Using spidev/i2c-dev directly from userspace is
the fast path for prototyping or one-off tools, but it pushes all
protocol logic into userspace and gives up kernel-level integration
(power management, standard subsystem interfaces like IIO or input,
interrupt-driven operation without a userspace polling loop). Writing a
dedicated kernel driver is more work but is the right call for a
peripheral that needs to behave like a first-class kernel device.
Finding what's bound to what
# what's on this I2C bus, and did anything claim address 0x68?
i2cdetect -l
i2cdetect -y 1
# device tree nodes actually seen by the running kernel
ls /sys/firmware/devicetree/base/
# which driver is bound to a given platform device
ls /sys/bus/platform/devices/<device>/driver
Real-world applications
- Custom sensor boards — a new I2C/SPI sensor on a carrier board typically needs a device tree overlay to bind it to the right driver before any of the Sensors pages' code examples can talk to it.
- Industrial gateway devices — a Linux gateway aggregating CAN, I2C, and SPI peripherals relies entirely on this binding model to expose them all as usable device nodes.
- Board bring-up — porting Linux to new hardware is largely device-tree and driver-binding work — getting the device model right before any application code can run.
- Rapid prototyping — spidev/i2c-dev exist specifically so a prototype doesn't need a real kernel driver written before it can talk to hardware.
"The device tree looks right but the device isn't showing up" is
usually one of: the driver for that compatible string
isn't built/loaded, the node is disabled (status = "disabled"),
or an overlay didn't actually get applied — check
dmesg for probe failures before assuming the wiring
or device tree syntax is wrong.