Skip to content

Day 111 — IIO Buffered Acquisition and DMA Integration

Today's Goal

Understand how the Linux Industrial I/O (IIO) subsystem represents buffered acquisition and how DMA-backed buffers connect hardware sample streams to the IIO userspace interface.

The main focus is to move beyond raw DMA buffer ownership and understand the data semantics above the DMA transport layer.

The main topics are:

  • IIO devices and channels
  • Direct and buffered acquisition
  • IIO scan elements and scan layout
  • scan_index
  • Active scan masks
  • realbits vs storage width
  • Logical scan_bytes
  • Triggered buffers
  • Software timestamps
  • DMA-backed IIO buffers
  • IIO DMA block submission and completion
  • Hardware scan layout vs logical IIO scan layout
  • Scan repacking
  • DMA-compatible scan configurations
  • Block-level scan conversion

The implementation goal is to build a simplified IIO scan simulator that accepts a fixed ADC hardware layout, applies an active scan mask, and converts hardware scans or blocks into a logical IIO-style layout.


1. From DMA Streaming to IIO

Day110 modeled continuous DMA streaming using a persistent DMA mapping and a ring of independently owned slots.

The central ownership model was:

DMA_OWNED
READY
CPU_OWNED
FREE
DMA reuse

That model answers an important transport question:

Which side currently owns each region of the DMA buffer?

However, it does not describe what the bytes mean to a subsystem or to userspace.

Day111 adds the next abstraction layer:

DMA Transport
Completed Sample Data
IIO Scan Semantics
IIO Buffer
Userspace

IIO introduces concepts such as channels, scan indices, active scan masks, scan layout, and buffered acquisition.

The distinction is:

DMA
    → moves bytes

IIO
    → describes and exposes sampled data

2. IIO Device and Channel Model

An IIO device represents a data-acquisition or data-conversion device such as an ADC, DAC, accelerometer, gyroscope, or other sensor.

The device exposes one or more channels.

For a four-channel ADC:

IIO Device
├── CH0
├── CH1
├── CH2
└── CH3

Each channel has metadata describing how its data is represented.

For buffered acquisition, one important property is:

scan_index

The scan index determines the channel's position in the scan model and the corresponding bit position used by scan masks.

Therefore:

channel number
scan_index

They may happen to match for a simple device, but they represent different concepts.


3. IIO Scan

A scan represents one logical acquisition containing the enabled scan elements.

For a four-channel ADC with every channel enabled:

Scan

CH0 | CH1 | CH2 | CH3

If each channel occupies 16 bits of storage:

CH0      CH1      CH2      CH3
2 bytes  2 bytes  2 bytes  2 bytes

scan_bytes = 8 bytes

The logical scan size is determined by the storage occupied by enabled scan elements, not simply by the ADC resolution.

For example, a 12-bit ADC may store each sample in a 16-bit element:

ADC resolution = 12 bits
Storage width  = 16 bits
Storage size   = 2 bytes

This corresponds to the distinction between the effective data width and the storage representation.


4. Active Scan Mask

The active scan mask describes which scan elements are enabled.

For the simulated four-channel ADC:

bit 0 → CH0
bit 1 → CH1
bit 2 → CH2
bit 3 → CH3

All channels enabled:

active_scan_mask = 0xF
                 = 1111b

CH0 CH1 CH2 CH3

A sparse configuration:

active_scan_mask = 0xD
                 = 1101b

CH0 CH2 CH3

The simulator calculates the logical scan size from the storage size of the enabled channels.

For 16-bit storage:

mask 0xF
    → 4 channels
    → 8 bytes

mask 0xD
    → 3 channels
    → 6 bytes

An empty scan mask is rejected because it does not describe a usable buffered scan.


5. Hardware Scan Layout vs Logical Scan Layout

The hardware data layout does not necessarily change when userspace disables an IIO channel.

Consider an ADC whose hardware interface always produces:

CH0 CH1 CH2 CH3

If CH1 is disabled logically, the hardware may still produce a fixed-width frame such as:

CH0 0x0000 CH2 CH3

DMA transfers the bytes generated by the hardware interface.

Therefore the DMA-visible layout remains:

CH0 CH1 CH2 CH3

while the requested logical IIO scan may be:

CH0 CH2 CH3

If the framework or driver promises the compact logical layout, software must transform the hardware representation.

Hardware DMA scan layout and logical IIO scan conversion

The important distinction is:

hardware scan format
logical IIO scan format

6. Scan Repacking

The Day111 simulator models this transformation explicitly.

For example:

Hardware Scan

CH0     CH1     CH2     CH3
0x1000  0x2000  0x3000  0x4000

with:

active_scan_mask = 0xD

is repacked into:

Logical IIO Scan

CH0     CH2     CH3
0x1000  0x3000  0x4000

The simulator maintains two offsets during this operation:

hw_offset
    → advances for every hardware channel

iio_offset
    → advances only for enabled logical channels

For the sparse scan:

             hw_offset     iio_offset

CH0              0              0
copy 2 B
                 2              2

CH1              2              2
skip
                 4              2

CH2              4              2
copy 2 B
                 6              4

CH3              6              4
copy 2 B
                 8              6

The result is:

hardware scan size = 8 bytes
logical scan size  = 6 bytes

This transformation requires a CPU-side copy in the simulator.


7. Full Layout and Direct DMA Compatibility

If all four channels are enabled:

active_scan_mask = 0xF

the logical layout matches the fixed hardware layout:

Hardware:
CH0 CH1 CH2 CH3

Logical IIO:
CH0 CH1 CH2 CH3

The simulator still performs a copy because sim_iio_pack_scan() is a layout-conversion helper.

However, architecturally this configuration is important because the data layout itself is directly compatible with DMA-backed buffering.

Conceptually:

Fixed Hardware Layout
Active Scan Mask
   ┌────┴────┐
   │         │
  0xF       0xD
   │         │
   ▼         ▼
Direct      Repacking
layout      required
compatible

A performance-oriented driver may choose to support only DMA-compatible scan masks rather than repacking every DMA block.

Therefore a driver policy may be:

DMA-compatible mask
        → direct DMA-backed path

incompatible mask
        → repack or reject

The correct choice depends on the hardware layout, subsystem requirements, and performance goals.


8. Triggered Buffer vs DMA-Backed Buffer

IIO buffered acquisition can be implemented through different producer architectures.

IIO triggered and DMA-backed buffered acquisition architecture

A software-driven triggered path may look conceptually like:

Trigger
Poll Function
Read Device
Build Logical Scan
Push to IIO Buffer

Because the CPU already participates in each acquisition, selecting channels, constructing the scan, and adding a software timestamp are natural parts of the path.

A DMA-backed path instead emphasizes continuous or block-based hardware transfer:

Hardware Sample Stream
DMA Engine
DMA Buffer Block
IIO Buffer Management
Userspace

The DMA path reduces per-sample CPU involvement but works best when the hardware data layout already matches the logical buffered layout.


9. Timestamp Granularity

Timestamp semantics depend on the acquisition architecture.

For a triggered acquisition, software may associate a timestamp with an individual scan or trigger event.

For FIFO-based acquisition, one interrupt or DMA transaction may retrieve several samples that were generated earlier inside the device.

Therefore:

FIFO read completion timestamp
exact timestamp of every sample

Assigning the same timestamp to every sample in a multi-sample FIFO block would normally lose the temporal spacing between those samples.

A driver may instead reconstruct per-sample timestamps using information such as:

  • FIFO sample rate
  • Hardware sample counters
  • Device timestamps
  • Interrupt timing
  • Known sampling intervals

Timestamp handling is therefore part of the acquisition model rather than a property automatically provided by DMA.


10. IIO DMA Buffer Architecture

The Linux IIO subsystem provides generic DMA buffer support that separates buffer management from the DMA Engine backend.

Conceptually:

IIO Buffer Core
IIO DMA Buffer Queue
DMAengine Backend
DMA Engine
DMA Controller Driver

The generic IIO DMA layer manages buffer blocks and their lifecycle.

The DMAengine backend converts those blocks into DMA transactions.

This creates an important abstraction boundary:

IIO Block
DMA Descriptor

An IIO block represents a buffer-management unit.

A DMA descriptor represents a DMA Engine transaction.

The backend connects the two.


11. DMA Block Submission and Completion

The DMAengine-backed IIO path follows the same DMA Engine lifecycle studied in previous days.

Conceptually:

IIO Block
submit block
prepare DMA descriptor
dmaengine_submit()
issue pending
DMA transfer
completion callback
IIO block done

DMA completion returns control to the IIO DMA buffer layer.

The completion path can update the number of valid bytes using DMA residue information before marking the block complete.

This reconnects the Day103 concept:

requested transfer size
        -
DMA residue
        =
completed bytes

with buffered subsystem management.


12. Block Ownership and Consumer Wakeup

DMA completion does more than report that the controller finished moving data.

The buffer framework must also transition the completed block into a state where the consumer can access it.

Conceptually:

Block Available
DMA In Flight
DMA Completion
Completed Block
Consumer
Reusable Block

The generic IIO DMA buffer protects internal block state using synchronization appropriate for the completion path.

After a completed input block becomes available, the framework can wake readers or poll waiters.

This connects several previously studied kernel mechanisms:

DMA Completion
Buffer State Transition
Wait Queue Wakeup
poll() / read()
Userspace

13. Mapping Lifetime vs Block Ownership

A DMA-backed streaming buffer may remain mapped across many block transfers.

Therefore:

DMA mapping lifetime
individual block ownership lifetime

Conceptually:

map
┌─────────────────────────────────────┐
│ block available                     │
│      ↓                              │
│ DMA in flight                       │
│      ↓                              │
│ completed                           │
│      ↓                              │
│ consumer                            │
│      ↓                              │
│ reusable                            │
│      ↓                              │
│ DMA in flight again                 │
└─────────────────────────────────────┘
unmap

This is the same ownership principle modeled by the persistent mapping and slot states in Day110, but the IIO framework manages the higher-level buffer lifecycle.


14. Block-Level Scan Conversion

A DMA completion commonly represents more than one hardware scan.

The simulator therefore extends single-scan packing into block-level conversion.

For four hardware scans:

Hardware Block

4 scans × 8 bytes
= 32 bytes

with:

active_scan_mask = 0xD
logical scan size = 6 bytes

the resulting logical block is:

Logical IIO Block

4 scans × 6 bytes
= 24 bytes

sim_iio_pack_block() reuses sim_iio_pack_scan() for each complete hardware scan.

This keeps the responsibilities separate:

sim_iio_pack_scan()
    → convert one hardware scan

sim_iio_pack_block()
    → iterate multiple complete scans

The input block must contain an integral number of hardware scans.

An incomplete trailing scan is rejected rather than silently exposed as valid data.


15. Day111 Simulator

The simulator introduces simplified IIO objects rather than reproducing Linux IIO internals.

The main channel description is:

struct sim_iio_channel {
    unsigned int channel;
    unsigned int scan_index;
    size_t storage_bytes;
};

The active scan configuration is:

struct sim_iio_scan_config {
    unsigned long active_scan_mask;
    size_t scan_bytes;
};

The simulated device contains:

struct sim_iio_device {
    const struct sim_iio_channel *channels;
    size_t num_channels;

    struct sim_iio_scan_config scan_config;
};

These structures model only the concepts required by the lab.

They are not replacements for Linux:

struct iio_dev
struct iio_chan_spec

16. Case 1 — Full Scan Mask

The first test enables all four channels:

active_scan_mask = 0xF

The expected logical scan size is:

4 × 2 bytes = 8 bytes

The observed result was:

[CASE 1] Full scan mask
[PASS] mask=0xf scan_bytes=8

17. Case 2 — Sparse Scan Mask

The second test enables:

CH0
CH2
CH3

using:

active_scan_mask = 0xD

The expected logical scan size is:

3 × 2 bytes = 6 bytes

The observed result was:

[CASE 2] Sparse scan mask
[PASS] mask=0xd scan_bytes=6

18. Case 3 — Empty Scan Mask

An empty scan configuration is invalid.

The simulator rejects:

active_scan_mask = 0

The observed result was:

[CASE 3] Empty scan mask
[PASS] empty scan mask rejected

19. Case 4 — Sparse Scan Repacking

The fourth test verifies the actual hardware-to-logical data transformation.

Input:

0x1000 0x2000 0x3000 0x4000

with:

active_scan_mask = 0xD

produces:

0x1000 0x3000 0x4000

The observed result was:

[CASE 4] Sparse scan repacking
[PASS] input : 0x1000 0x2000 0x3000 0x4000
[PASS] output: 0x1000 0x3000 0x4000

20. Case 5 — Full Scan Packing

The fifth test verifies that the same packing API preserves the full layout when every channel is enabled.

The observed result was:

[CASE 5] Full scan packing
[PASS] input : 0x1000 0x2000 0x3000 0x4000
[PASS] output: 0x1000 0x2000 0x3000 0x4000

The simulator still copies the data, but the input and logical layouts are compatible.


21. Case 6 — Sparse Block Packing

The sixth test converts four complete hardware scans.

Input:

4 × 8-byte hardware scans
= 32 bytes

Output:

4 × 6-byte logical scans
= 24 bytes

The observed result was:

[CASE 6] Sparse block packing
[PASS] hardware block bytes=32
[PASS] logical IIO block bytes=24

This verifies that logical scan conversion can change the number of valid bytes in a completed data block.


22. Case 7 — DMA Scan Compatibility

The final test distinguishes a directly compatible fixed hardware layout from a sparse logical layout.

The simulator policy is:

0xF
    → DMA compatible

0xD
    → requires repacking

The observed result was:

[CASE 7] DMA scan compatibility
[PASS] full scan is DMA compatible
[PASS] sparse scan requires repacking

This models an important driver design choice: an implementation may either transform incompatible layouts or reject them from a direct DMA path.


23. Important Observations

DMA Moves the Hardware Representation

DMA does not understand IIO channel selection.

If hardware produces:

CH0 CH1 CH2 CH3

DMA transfers that hardware representation unless another hardware layer changes it.

Logical Channel Selection May Require Copying

If userspace requests:

CH0 CH2 CH3

while hardware still produces four fixed channel positions, a compact logical layout requires repacking.

scan_bytes Follows Storage Layout

A 12-bit ADC stored in a 16-bit element occupies two bytes per enabled channel in the scan.

Therefore scan size follows storage representation rather than only ADC resolution.

scan_index Defines Scan-Mask Position

The bit position in an active scan mask is defined by scan_index.

It should not be inferred only from the number of channels.

Direct DMA Works Best With Layout Compatibility

The most efficient DMA-backed architecture is possible when:

hardware layout
        =
buffer layout
        =
consumer-visible scan layout

Otherwise software transformation or a more restrictive scan-mask policy may be required.

DMA Mapping and Buffer Ownership Remain Separate

A long-lived DMA mapping may contain blocks that move repeatedly between DMA and consumer ownership.

The existence of a DMA mapping does not mean DMA currently owns every mapped byte.

Timestamp Semantics Are Independent of DMA Completion

A DMA or FIFO block may contain multiple samples generated at different times.

DMA completion identifies a transfer event, not necessarily the acquisition time of every sample.


Summary

Day111 connected continuous DMA acquisition with the Linux IIO buffered data model.

The central progression is:

Hardware Samples
Hardware Scan Layout
DMA Transport
IIO Buffer Management
Active Scan Configuration
Logical IIO Scan Layout
Userspace

The most important distinction is:

DMA hardware layout
IIO logical scan layout

When the layouts match, a direct DMA-backed path can avoid unnecessary data transformation.

When the layouts differ, the driver or framework must either repack the data or reject the incompatible scan configuration.

The Day111 simulator verified:

  1. Full scan-mask configuration
  2. Sparse scan-mask configuration
  3. Empty scan-mask rejection
  4. Single-scan repacking
  5. Full-layout packing
  6. Multi-scan block packing
  7. Direct DMA layout compatibility

This extends the Day110 ownership model from:

Who owns the DMA buffer?

to the subsystem-level question:

How should the completed DMA bytes be represented and delivered to userspace?

Next Plan

Continue from IIO buffered acquisition toward the lifecycle used to configure, enable, run, and disable a real IIO buffer.

The next topic should build on:

IIO Device
Buffer Configuration
Scan Configuration
Buffer Enable
Acquisition Start
Buffered Data Flow
Buffer Disable

The goal is to understand how IIO coordinates buffer setup and teardown around an acquisition path before moving deeper into real driver integration.