Skip to content

Day111 Lab — IIO Buffered Acquisition and Scan Packing

Goal

Build a simplified IIO buffered-acquisition simulator that models the relationship between:

  • Fixed ADC hardware scan layout
  • IIO channel metadata
  • Active scan masks
  • Logical scan size
  • Single-scan packing
  • Multi-scan block packing
  • Direct DMA-compatible layouts
  • Sparse layouts that require repacking

The lab focuses on the data-layout boundary between DMA and IIO.

The simulator does not reproduce the internal implementation of the Linux IIO subsystem.


1. Hardware Model

The simulated ADC provides four channels:

CH0 CH1 CH2 CH3

Each hardware sample uses 16-bit storage:

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

Therefore one complete hardware scan occupies:

4 × 2 bytes = 8 bytes

The hardware scan layout is fixed even when the logical IIO configuration disables a channel.

For example, disabling CH1 does not change the DMA-visible hardware frame:

Hardware:

CH0 CH1 CH2 CH3

The logical IIO scan may instead request:

Logical:

CH0 CH2 CH3

This difference is the main problem modeled by the lab.


2. Simulated IIO Channel Model

Each simulated channel describes:

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

The lab uses:

static const struct sim_iio_channel adc_channels[] = {
    {
        .channel = 0,
        .scan_index = 0,
        .storage_bytes = sizeof(uint16_t),
    },
    {
        .channel = 1,
        .scan_index = 1,
        .storage_bytes = sizeof(uint16_t),
    },
    {
        .channel = 2,
        .scan_index = 2,
        .storage_bytes = sizeof(uint16_t),
    },
    {
        .channel = 3,
        .scan_index = 3,
        .storage_bytes = sizeof(uint16_t),
    },
};

For this simulator:

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

The scan index determines the corresponding bit position in the active scan mask.


3. Active Scan Configuration

The simulated scan configuration contains:

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

The full scan mask is:

0xF = 1111b

CH0 CH1 CH2 CH3

With 2 bytes per channel:

scan_bytes = 8

A sparse scan mask used by the lab is:

0xD = 1101b

CH0 CH2 CH3

The resulting logical scan size is:

scan_bytes = 6

scan_bytes represents the logical size of one enabled IIO scan.

It is not the size of the fixed hardware scan.


4. Scan Configuration API

The simulator initializes the IIO device using:

bool sim_iio_device_init(struct sim_iio_device *indio_dev,
                         const struct sim_iio_channel *channels,
                         size_t num_channels);

The active scan configuration is applied using:

bool sim_iio_configure_scan(struct sim_iio_device *indio_dev,
                            unsigned long active_scan_mask);

The configuration logic:

  1. Validates the device and channel table.
  2. Rejects an empty scan mask.
  3. Builds the valid mask from channel scan_index values.
  4. Rejects unsupported scan bits.
  5. Adds the storage_bytes of enabled channels.
  6. Commits the resulting active_scan_mask and scan_bytes.

Channel state can be queried using:

bool sim_iio_scan_channel_enabled(const struct sim_iio_device *indio_dev,
                                  unsigned int scan_index);

5. Hardware Scan vs Logical Scan

The relationship between the fixed DMA layout and logical IIO layout is shown below.

Hardware DMA scan layout and logical IIO scan conversion

For a full scan:

Hardware:
CH0 CH1 CH2 CH3

mask = 0xF

Logical:
CH0 CH1 CH2 CH3

The layouts match.

For a sparse scan:

Hardware:
CH0 CH1 CH2 CH3

mask = 0xD

Logical:
CH0 CH2 CH3

The logical layout is smaller and requires repacking.


6. Single-Scan Packing

One hardware scan is converted using:

bool sim_iio_pack_scan(const struct sim_iio_device *indio_dev,
                       const void *hw_scan,
                       size_t hw_scan_bytes,
                       void *iio_scan,
                       size_t iio_scan_capacity);

The API operates on exactly one complete hardware scan.

Therefore:

hw_scan_bytes == expected hardware scan size

is required.

A larger input is not silently accepted as multiple scans.

Sparse Example

Input:

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

Configuration:

active_scan_mask = 0xD

Output:

CH0     CH2     CH3
0x1000  0x3000  0x4000

The packing algorithm maintains two offsets:

hw_offset
    → advances for every hardware channel

iio_offset
    → advances only when the channel is enabled

Conceptually:

CH0 enabled
    copy
    hw_offset  += 2
    iio_offset += 2

CH1 disabled
    skip
    hw_offset  += 2
    iio_offset unchanged

CH2 enabled
    copy
    hw_offset  += 2
    iio_offset += 2

CH3 enabled
    copy
    hw_offset  += 2
    iio_offset += 2

The final sizes are:

hardware = 8 bytes
logical  = 6 bytes

7. Hardware Scan Size Helper

The hardware scan size is independent of the active logical scan mask.

The simulator therefore calculates it from all hardware channels:

static size_t
sim_iio_get_hw_scan_bytes(const struct sim_iio_device *indio_dev);

Conceptually:

hardware scan bytes
    =
sum of storage_bytes for every hardware channel

For this lab:

2 + 2 + 2 + 2
= 8 bytes

This must not be confused with:

indio_dev->scan_config.scan_bytes

which represents the currently configured logical scan.


8. Block-Level Packing

A DMA transfer commonly contains multiple complete hardware scans.

The simulator therefore provides:

bool sim_iio_pack_block(const struct sim_iio_device *indio_dev,
                        const void *hw_block,
                        size_t hw_block_bytes,
                        size_t hw_scan_bytes,
                        void *iio_block,
                        size_t iio_block_capacity,
                        size_t *iio_block_bytes);

The input block must contain only complete scans:

hw_block_bytes % hw_scan_bytes == 0

The number of scans is:

num_scans
    =
hw_block_bytes / hw_scan_bytes

The required output size is:

required_iio_bytes
    =
num_scans × scan_bytes

The block-level API does not duplicate channel-selection logic.

Instead, it repeatedly calls:

sim_iio_pack_scan()

for each complete hardware scan.


9. Block Conversion Example

The lab uses four hardware scans:

Scan 0:
0x1000 0x1100 0x1200 0x1300

Scan 1:
0x2000 0x2100 0x2200 0x2300

Scan 2:
0x3000 0x3100 0x3200 0x3300

Scan 3:
0x4000 0x4100 0x4200 0x4300

Each hardware scan occupies:

8 bytes

Therefore:

hardware block
    =
4 × 8
    =
32 bytes

With:

active_scan_mask = 0xD

each logical scan occupies:

6 bytes

and the resulting logical block occupies:

4 × 6
=
24 bytes

The expected logical values are:

Scan 0:
0x1000 0x1200 0x1300

Scan 1:
0x2000 0x2200 0x2300

Scan 2:
0x3000 0x3200 0x3300

Scan 3:
0x4000 0x4200 0x4300

10. DMA Compatibility

The simulator also distinguishes between logical layouts that match the fixed DMA hardware layout and layouts that require transformation.

The API is:

bool sim_iio_scan_is_dma_compatible(
    const struct sim_iio_device *indio_dev);

For the fixed four-channel hardware model:

active_scan_mask = 0xF
    → CH0 CH1 CH2 CH3
    → DMA compatible

active_scan_mask = 0xD
    → CH0 CH2 CH3
    → repacking required

This models a possible real driver policy.

A driver may choose between:

incompatible scan
      ├── repack in software
      └── reject direct DMA mode

The lab implements repacking so that both layouts can be examined.


11. Buffered Acquisition Context

The simulator focuses on scan layout rather than reproducing the complete IIO buffer implementation.

The architectural context is:

IIO triggered and DMA-backed buffered acquisition architecture

The important separation is:

DMA
    → transfers the hardware representation

IIO scan configuration
    → defines the logical representation

When these representations match, a direct DMA-backed layout is possible.

When they differ, software transformation may be required.


12. Test Cases

Case 1 — Full Scan Mask

Configure:

mask = 0xF

Expected:

scan_bytes = 8
all channels enabled

Observed:

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

Case 2 — Sparse Scan Mask

Configure:

mask = 0xD

Expected:

CH0 enabled
CH1 disabled
CH2 enabled
CH3 enabled

scan_bytes = 6

Observed:

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

Case 3 — Empty Scan Mask

Configure:

mask = 0

Expected:

configuration rejected

Observed:

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

Case 4 — Sparse Scan Repacking

Input:

0x1000 0x2000 0x3000 0x4000

Expected:

0x1000 0x3000 0x4000

Observed:

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

Case 5 — Full Scan Packing

Input:

0x1000 0x2000 0x3000 0x4000

Expected:

0x1000 0x2000 0x3000 0x4000

Observed:

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

Case 6 — Sparse Block Packing

Input:

4 hardware scans × 8 bytes
= 32 bytes

Expected:

4 logical scans × 6 bytes
= 24 bytes

Observed:

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

Case 7 — DMA Scan Compatibility

Expected:

full scan
    → DMA compatible

sparse scan
    → requires repacking

Observed:

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

13. Final Test Result

The complete test run finishes with:

=== ALL TESTS PASSED ===

The simulator therefore verifies:

  • Active scan-mask handling
  • Logical scan-size calculation
  • Empty-mask rejection
  • Fixed hardware scan validation
  • Sparse single-scan repacking
  • Full-layout packing
  • Multi-scan block conversion
  • Produced-byte accounting
  • DMA layout compatibility

Key Takeaways

  1. DMA transfers the hardware-defined data representation.

  2. An IIO active scan mask describes the logical buffered representation.

  3. scan_index determines the channel's scan-mask bit position.

  4. scan_bytes follows enabled element storage size rather than only ADC resolution.

  5. A fixed hardware frame may remain unchanged when a logical channel is disabled.

  6. If the hardware and logical layouts differ, compact logical scans require repacking or the configuration must be rejected.

  7. One hardware scan and one logical IIO scan can have different sizes.

  8. A multi-scan DMA block can therefore produce a smaller logical IIO block after repacking.

  9. Direct DMA-backed buffering is simplest when the hardware scan layout already matches the consumer-visible IIO scan layout.

  10. Scan-layout transformation belongs above the raw DMA transport model and should not be confused with DMA ownership or mapping lifetime.