Drv::LinuxGpioDriver
1. Introduction
The LinuxGpioDriver component provides a Linux-specific implementation of a General Purpose Input/Output (GPIO) pin driver. It implements the Drv.Gpio interface to enable reading, writing, and interrupt detection on a single GPIO line.
The component wraps the Linux GPIO character device ABI (/dev/gpiochip*), configuring and accessing a line through ioctl requests rather than the deprecated sysfs interface. Each component instance manages exactly one GPIO line, and that line is fixed to a single mode chosen at configuration time.
For more information on the driver interfaces see: Drv FPP Interfaces.
2. Requirements
| Name | Description | Validation |
|---|---|---|
| LINUX-GPIO-COMP-001 | The LinuxGpioDriver component shall implement the Drv.Gpio interface | inspection |
| LINUX-GPIO-COMP-002 | The LinuxGpioDriver component shall configure a GPIO line as input, output, or interrupt using the Linux GPIO character device | inspection |
| LINUX-GPIO-COMP-003 | The LinuxGpioDriver component shall set a caller-supplied default state when a line is configured as output | inspection |
| LINUX-GPIO-COMP-004 | The LinuxGpioDriver component shall reject read and write requests that do not match the configured mode | inspection |
| LINUX-GPIO-COMP-005 | The LinuxGpioDriver component shall detect line transitions on a rising edge, a falling edge, or both edges as configured | inspection |
| LINUX-GPIO-COMP-006 | The LinuxGpioDriver component shall provide a dedicated thread for interrupt detection | inspection |
| LINUX-GPIO-COMP-007 | The LinuxGpioDriver component shall emit a timestamped interrupt on the gpioInterrupt output port when a configured transition occurs | inspection |
| LINUX-GPIO-COMP-008 | The LinuxGpioDriver component shall report configuration and runtime errors via events | inspection |
3. Design
LinuxGpioDriver is a passive component implementing the design specified by the Drv.Gpio interface.
3.1 Port Descriptions
| Name | Type | Kind | Description |
|---|---|---|---|
| gpioRead | Drv.GpioRead |
sync input | Reads the current logic state of the line |
| gpioWrite | Drv.GpioWrite |
sync input | Sets the logic state of the line |
| gpioInterrupt | Svc.Cycle |
output | Emits a timestamp when a configured line transition is detected |
The component declares no commands, telemetry channels, or parameters. Since the component is passive, gpioRead and gpioWrite execute on the caller's thread.
3.2 Architecture
The component consists of the following key elements:
- Chip and Line Configuration: Opens the
/dev/gpiochip*device, validates the requested line, and requests either a line handle or a line event from the kernel - Read/Write Handlers: Synchronous access to the line value via the
gpioReadandgpioWriteports - Interrupt Thread: An optional thread that polls the line event descriptor and drives the
gpioInterruptoutput port - Status Translation: Maps
errnovalues from the underlying system calls ontoOs::File::StatusandDrv::GpioStatus - Error Reporting: Reports open, polling, and read failures via events
3.3 Pin Configuration
A line is configured once through the open() method, which selects one of five modes:
| Configuration | Direction | Supported Operations |
|---|---|---|
| GPIO_OUTPUT | Output | gpioWrite |
| GPIO_INPUT | Input | gpioRead |
| GPIO_INTERRUPT_RISING_EDGE | Input | gpioInterrupt on low-to-high transition |
| GPIO_INTERRUPT_FALLING_EDGE | Input | gpioInterrupt on high-to-low transition |
| GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES | Input | gpioInterrupt on either transition |
open() performs the following steps:
- Opens the GPIO chip device and retrieves chip information
- Verifies that the requested line number exists on that chip
- Retrieves line information, including the name and any current consumer
- Requests a line handle (
GPIO_GET_LINEHANDLE_IOCTL) for the input and output modes, or a line event (GPIO_GET_LINEEVENT_IOCTL) for the interrupt modes - Retains the resulting file descriptor and configuration on success
The component name is passed to the kernel as the consumer label for the line (when object names are enabled via FW_OBJECT_NAMES), so the owning component is identifiable in tools such as gpioinfo.
3.4 Read and Write Operations
The read and write handlers are strictly gated on the configured mode:
gpioReadreturns the line state only when the line is configured asGPIO_INPUTgpioWritesets the line state only when the line is configured asGPIO_OUTPUT
A request that does not match the configured mode returns Drv::GpioStatus::INVALID_MODE and performs no hardware access. Notably, the interrupt configurations do not support gpioRead; a line that must be both polled and interrupt-driven requires a separate component instance.
3.5 Interrupt Detection
Interrupt detection runs on a dedicated thread started by start(). The thread is only available in the three interrupt configurations; calling start() in any other mode returns Drv::GpioStatus::INVALID_MODE and starts no thread.
The polling loop performs the following:
- Polls the line event descriptor for readable data, using a fixed
GPIO_POLL_TIMEOUTof 500 milliseconds - Reads the kernel event record when the descriptor becomes ready
- Captures a timestamp with
Os::RawTime - Invokes the
gpioInterruptoutput port with that timestamp - Reports short reads, polling failures, and timestamp failures via events; on a timestamp failure the interrupt is still emitted with the (possibly invalid) timestamp
Because gpioInterrupt is invoked directly from the polling thread, the receiving component is responsible for any thread-safety and execution-time constraints on that path. Connecting the port to an async input port, such as CycleIn on Svc::ActiveRateGroup, keeps work off the polling thread.
3.6 Threading Model
The interrupt thread is controlled by three methods:
start(): sets the running flag and starts the polling task; returnsDrv::GpioStatus::UNKNOWN_ERRORif the task fails to startstop(): clears the running flag, requesting shutdownjoin(): blocks until the polling task exits
The running flag is guarded by a mutex. The poll timeout bounds how long the loop can block, so the thread observes a stop() request and exits within one timeout period. stop() must be called before join(), otherwise join() will not return.
3.7 Platform Support
Outside of the stubbed build, this component is restricted to Linux targets. When the FPRIME_USE_STUBBED_DRIVERS CMake option is set, a stub implementation is built instead, allowing topologies that instantiate this component to build on platforms without GPIO character device support. The stub returns Os::File::Status::NOT_SUPPORTED from open() and Drv::GpioStatus::UNKNOWN_ERROR from the port handlers, and its polling loop delays instead of accessing hardware.
4. Usage
The LinuxGpioDriver must be configured before use. Each instance drives exactly one GPIO line, so a topology using several lines instantiates several components.
4.1 Configuration Example
The component should be instantiated in the FPP topology and configured using separate functions following F´ patterns:
// Configuration function - called during topology setup
void configureTopology() {
// Configure an output pin, driven low until written
Os::File::Status status = gpioLed.open("/dev/gpiochip0", // GPIO chip device
17, // Line number on that chip
Drv::LinuxGpioDriver::GPIO_OUTPUT, // Pin configuration
Fw::Logic::LOW); // Default output state
if (status != Os::File::Status::OP_OK) {
// Handle configuration error
}
// Configure an interrupt pin
status = gpioButton.open("/dev/gpiochip0", 27, Drv::LinuxGpioDriver::GPIO_INTERRUPT_RISING_EDGE);
if (status != Os::File::Status::OP_OK) {
// Handle configuration error
}
...
}
// Startup function - called when starting tasks
void setupTopology() {
// Start the interrupt thread; only valid for interrupt configurations
Drv::GpioStatus gpioStatus = gpioButton.start(GPIO_PRIORITY, // Thread priority
Os::Task::TASK_DEFAULT, // Thread stack size
Os::Task::TASK_DEFAULT); // Thread CPU affinity mask
if (gpioStatus != Drv::GpioStatus::OP_OK) {
// Handle startup error
}
}
// Shutdown function - called during teardown
void teardownTopology() {
gpioButton.stop();
gpioButton.join();
}
4.2 Topology Connections
The read and write ports are connected to the component that uses the line, and an interrupt line is connected to a consumer of Svc.Cycle:
# In topology.fpp connections section
connections Gpio {
# A user component drives an output pin
ledManager.gpioWrite -> gpioLed.gpioWrite
# Interrupt pin drives a rate group
gpioButton.gpioInterrupt -> buttonRateGroup.CycleIn
}
5. Configuration
5.1 Open Parameters
| Parameter | Type | Description | Valid Values |
|---|---|---|---|
| device | const char* | Path to the GPIO chip device | Linux device path (e.g. "/dev/gpiochip0") |
| gpio | U32 | Line number on the given chip | Less than the line count reported by the chip |
| configuration | Drv::LinuxGpioDriver::GpioConfiguration | Pin mode | See pin configuration table |
| default_state | Fw::Logic | Initial state for output pins | Fw::Logic::LOW (default), Fw::Logic::HIGH |
5.2 Thread Configuration
The interrupt thread can be configured with:
| Parameter | Type | Default | Description |
|---|---|---|---|
| priority | FwTaskPriorityType | TASK_PRIORITY_DEFAULT | Thread priority |
| stackSize | FwSizeType | TASK_DEFAULT | Thread stack size |
| cpuAffinity | FwSizeType | TASK_DEFAULT | CPU affinity mask |
| identifier | FwTaskIdType | TASK_DEFAULT | Task identifier |
5.3 Status Codes
Port handlers return Drv::GpioStatus:
| Status | Meaning |
|---|---|
| OP_OK | Operation succeeded |
| NOT_OPENED | Pin was never opened |
| INVALID_MODE | Operation not permitted with the current configuration |
| UNKNOWN_ERROR | An unknown error occurred |
open() returns Os::File::Status, translated from the errno reported by the underlying system call. Note: in the current implementation, a request for a line number beyond the chip's line count logs OpenPinError but returns the preceding status (OP_OK); callers should not rely on open() returning an error in that case.
5.4 Events
The component generates the following events:
| Event | Severity | Description |
|---|---|---|
| OpenChip | diagnostic | Chip and line configured successfully |
| OpenChipError | warning high | GPIO chip device could not be opened or queried |
| OpenPinError | warning high | GPIO line could not be configured |
| InterruptReadError | warning high | Interrupt event record read returned an unexpected size |
| PollingError | warning high | Interrupt polling returned an error |
| InterruptTimeError | warning high | Interrupt timestamp could not be read |