Skip to content

Commit c56923b

Browse files
committed
src/module: Add README.md with architecture deep dive
Adds architecture processing flows and state transition documentation for the generic component API. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com>
1 parent 4c65254 commit c56923b

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

src/module/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Audio Processing Modules (`src/module`)
2+
3+
The `src/module` directory and the `src/include/module` headers define the Sound Open Firmware (SOF) modern Audio Processing Module API. This architecture abstracts the underlying OS and pipeline scheduler implementations from the actual audio signal processing logic, allowing modules to be written once and deployed either as statically linked core components or as dynamically loadable Zephyr EXT (LLEXT) modules.
4+
5+
## Architecture Overview
6+
7+
The SOF module architecture is built around three core concepts:
8+
9+
1. **`struct module_interface`**: A standardized set of operations (`init`, `prepare`, `process`, `reset`, `free`, `set_configuration`) that every audio processing module implements.
10+
2. **`struct processing_module`**: The runtime instantiated state of a module. It holds metadata, configuration objects, memory allocations (`module_resources`), and references to interconnected streams.
11+
3. **Module Adapter (`src/audio/module_adapter`)**: The system glue layer. It masquerades as a legacy pipeline `comp_dev` to the SOF schedulers, but acts as a sandbox container for the `processing_module`. It intercepts IPC commands, manages the module's state machine, manages inputs/outputs, and safely calls the `module_interface` operations.
12+
13+
```mermaid
14+
graph TD
15+
subgraph SOF Core Pipeline Scheduler
16+
P[Pipeline Scheduler <br> LL/DP Domains]
17+
end
18+
19+
subgraph Module Adapter System Layer
20+
MA[Module Adapter `comp_dev`]
21+
IPC[IPC Command Dispatch]
22+
MEM[Memory Resource Manager]
23+
end
24+
25+
subgraph Standardized Module Framework API
26+
INF[`module_interface` Ops]
27+
SRC[Source API <br> `source_get_data`]
28+
SNK[Sink API <br> `sink_get_buffer`]
29+
end
30+
31+
subgraph Custom Audio Modules
32+
VOL[Volume]
33+
EQ[EQ FIR/IIR]
34+
CUSTOM[Loadable 3rd Party <br> Zephyr LLEXT]
35+
end
36+
37+
P <-->|Execute| MA
38+
IPC -->|Config/Triggers| MA
39+
40+
MA -->|Invoke| INF
41+
MA -->|Manage| MEM
42+
43+
INF --> VOL
44+
INF --> EQ
45+
INF --> CUSTOM
46+
47+
VOL -->|Read| SRC
48+
VOL -->|Write| SNK
49+
EQ -->|Read| SRC
50+
EQ -->|Write| SNK
51+
```
52+
53+
## Module State Machine
54+
55+
Every processing module is strictly governed by a uniform runtime state machine managed by the `module_adapter`. Modules must adhere to the transitions defined by `enum module_state`:
56+
57+
1. `MODULE_DISABLED`: The module is loaded but uninitialized, or has been freed. No memory is allocated.
58+
2. `MODULE_INITIALIZED`: After a successful `.init()` call. The module parses its IPC configuration and allocates necessary local resources (delay lines, coefficient tables).
59+
3. `MODULE_IDLE`: After a successful `.prepare()` call. Audio stream formats are fully negotiated and agreed upon (Stream params, channels, rate).
60+
4. `MODULE_PROCESSING`: When the pipeline triggers a `START` command. The `.process()` callback is actively handling buffers.
61+
62+
```mermaid
63+
stateDiagram-v2
64+
[*] --> MODULE_DISABLED: Module Created
65+
66+
MODULE_DISABLED --> MODULE_INITIALIZED: .init() / IPC NEW
67+
MODULE_INITIALIZED --> MODULE_DISABLED: .free() / IPC FREE
68+
69+
MODULE_INITIALIZED --> MODULE_IDLE: .prepare() / Pipeline Setup
70+
MODULE_IDLE --> MODULE_INITIALIZED: .reset() / Pipeline Reset
71+
72+
MODULE_IDLE --> MODULE_PROCESSING: .trigger(START) / IPC START
73+
MODULE_PROCESSING --> MODULE_IDLE: .trigger(STOP/PAUSE) / IPC STOP
74+
```
75+
76+
## Data Flows and Buffer Management
77+
78+
Modules do not directly manipulate underlying DMA, ALSA, or Zephyr `comp_buffer` pointers. Instead, they interact via the decoupled **Source and Sink APIs**. This allows the adapter to seamlessly feed data from varying topological sources without changing module code.
79+
80+
The flow operates primarily in a "get -> manipulate -> commit/release" pattern:
81+
82+
```mermaid
83+
sequenceDiagram
84+
participant Adapter as Module Adapter
85+
participant Mod as Processing Module (.process)
86+
participant Src as Source API (Input)
87+
participant Snk as Sink API (Output)
88+
89+
Adapter->>Mod: Process Trigger (sources[], sinks[])
90+
91+
Mod->>Src: source_get_data(req_size)
92+
Src-->>Mod: Provides read_ptr, available_bytes
93+
94+
Mod->>Snk: sink_get_buffer(req_size)
95+
Snk-->>Mod: Provides write_ptr, free_bytes
96+
97+
note over Mod: Execute DSP Algorithm <br> (Read from read_ptr -> Write to write_ptr)
98+
99+
Mod->>Src: source_release_data(consumed_bytes)
100+
Mod->>Snk: sink_commit_buffer(produced_bytes)
101+
102+
Mod-->>Adapter: Return Status
103+
```
104+
105+
### Source API (`src/module/audio/source_api.c`)
106+
- modules request data by calling `source_get_data_s16` or `s32`. This establishes an active read lock.
107+
- Once done, the module must call `source_release_data()` releasing only the frames actually consumed.
108+
109+
### Sink API (`src/module/audio/sink_api.c`)
110+
- modules request destination buffers by calling `sink_get_buffer_s16` or `s32`.
111+
- After processing into the provided memory array, the module marks the memory as valid by calling `sink_commit_buffer()` for the exact number of frames successfully written.

0 commit comments

Comments
 (0)