User-space I/O: handling hardware from an application
How to handle GPIO, I2C, SPI and UART through /dev, sysfs, ioctl and libgpiod without writing a driver.
Method for each device
| Device | Interface | Tools / libraries |
|---|---|---|
| GPIO | /dev/gpiochipN (character device) | libgpiod, gpioset, gpioget |
| I2C | /dev/i2c-N | i2cdetect, i2cget, ioctl(I2C_RDWR) |
| SPI | /dev/spidevX.Y | spidev ioctl |
| UART | /dev/ttyS, /dev/ttyUSB | termios |
| PWM | /sys/class/pwm/ | sysfs |
| ADC | /sys/bus/iio/devices/ | IIO |
The old /sys/class/gpio interface is being retired, so new code uses libgpiod.
I2C read example (Python)
from smbus2 import SMBus
with SMBus(1) as bus:
raw = bus.read_i2c_block_data(0x68, 0x3B, 6) # MPU6050 acceleration
RoadmapSee where this note sits in Embedded Linux→When user space, when a driver
| User space is good when | A driver is needed when |
|---|---|
| Prototypes, slow sensors | µs-level response, high interrupt rates |
| Development speed matters | Several programs share the device |
| Integrating with a kernel subsystem (network, input devices) |
Mind the real-time behaviour
A Linux user program can be paused by the scheduler at any time, giving several ms of jitter. A common arrangement is to leave precise PWM and motor control to an MCU and give Linux the higher-level decisions and communication.