The audio module (C_MODULE_AUDIO)¶
C_MODULE_AUDIO owns the audio DSP and amplifiers. It carries out source changes that
the scheduler decides, holds the AUX input setting, and turns radio events,
including AUX signal changes, into DBUS signals. This page covers the 273 functions of
the family in the stock NAV SMEG5.43.A.R2 image.
How this was read¶
- Every function was decompiled through the shared Ghidra server; references come from the
same
bl/lis+addi/ data-pointer scan astools/survey.py(Firmware map). - 25 functions are read: the AUX path, the lifecycle, the event dispatch and the mute logic were followed by hand. They are listed below.
- 248 are inferred: classified from their names, strings and a machine digest of
the decompile (calls, fields,
Call_actionids), not read line by line. They are summarised by purpose, not listed one by one. - The only executed result is the media app's dispatch of
0xcc(below), emulated on the stock image.
What matters for AUX¶
- The AUX "status" the media app reacts to is a setting, not a signal. Read. There are
two AUX quantities:
Get_aux_status(0x013b9c8c) returns+0x8c, the stored AUX input setting: theaudio/Auxiliary_Statususer key, 0..3, set from the media options menu. Confirmed in disassembly: it loads0x8c(r31)whenever the lifecycle state+0x74is non-zero, and the only stores to+0x8cin the family that write this object aresetAUXGain(0x013bba28) andread_sqlite_AudioUserData(0x013caba8), which loads the key.Get_AUX_signal_status(0x013bc02c) returns signal presence from the radio front-end (C_I2C_SMART_RADIO).
- Two DBUS signals, and the media app listens to the setting one. Read.
AUDIO_AUX_INPUT_STATUS_CHANGEDis raised when the setting is written (setAUXGain, and the diagnosticSet_diag_aux).AUDIO_AUX_SIGNAL_STATUS_CHANGEDis raised on a signal change.C_BCM_HMI_AUDIO_CLIENTturns the first into HMI message0xcband the second into0xcc(li r4,0xcbat0x025cdf1c,li r4,0xccat0x025cded0). The media app's AUX handler runs on0xcb.C_HMI_MEDIA_APP_BASE::HandleDBUSMessagehas no0xcccase: its dispatch sends0xccto the default branch (executed: the dispatch window emulated on the stock image,tests/test_firmware_nav.py). The audio app uses0xcconly to refresh its menu (read). So no stock code path activates AUX because a signal appeared. - Every successful boot re-announces the setting once. Read.
ElabRADIO_READY_FOR_INIT_0ends withsetAUXGain(+0x8c, 1), which raisesAUDIO_AUX_INPUT_STATUS_CHANGED. Whether the media app is listening by then is not known. - Signal changes before the radio has started are dropped, and AUX is kept muted while it
is the current source with no signal: the flag at
+0x168, which the vendor's own log text callsAUXSign_mute.RadioMuteManageris its only functional reader, so the mute goes with one instruction —patches/aux-no-idle-mute.json(the AUX idle mute). Read, and the flag's path executed.
What this means for the patches is in The AUX chain and Bearing on the AUX patches below.
The module¶
C_MODULE_AUDIO is a singleton, object id 0x3c28 in C_OBJ_PTR_Table, created by
Instance(). It owns the audio DSP and amplifiers, reached through a Radio /
C_I2C_SMART_RADIO object.
- It runs its own VxWorks task
tAudTask, spawned inOpenwithAudio_task_handler->Module_routine. - The task serialises work through a message queue (
+0x64, depth0x32) and a mutex (+0x68). Read. - Callers outside the task use two paths:
- the
Cmd_*/Get_*/Set_*methods, which take the mutex and run on the caller's thread. Almost all are reached only fromC_SRV_AUDIO::bcm_*, the DBUS server, or from VxWorks shell helpers (do*); - the
Elab_event_*posts, for radio, VAN and CAN events.
State fields¶
| offset | meaning | tier |
|---|---|---|
+0x54 |
Radio* (C_I2C_SMART_RADIO) |
read |
+0x58 |
C_VAN_BM* |
read |
+0x5c |
UP_MOD* (user-profile settings) |
read |
+0x60 |
tAudTask id |
read |
+0x64 |
message queue | read |
+0x68 |
module mutex | read |
+0x16c |
amplifier-mute mutex | read |
+0x70 |
radio has started at least once | read |
+0x74 |
st_audio, the lifecycle state: 0 closed, 1 open (set when Open spawns tAudTask), 2 radio restarted, 9 radio early-started, 10 radio started, 0x28 error. Get_aux_status needs it non-zero; CheckStatus refuses commands at 0 and 0x28 |
read |
+0x7c / +0x7e |
balance / fader | inferred |
+0x87 |
AVC enabled | inferred |
+0x8a |
diagnostic "AUX fitted" option (AudioCommonData) | read |
+0x8c |
AUX input setting 0..3 (audio/Auxiliary_Status) |
read |
+0x90 |
HiFi mode (AudioConfigData 0..3) | read |
+0x94 |
Arkamys option | inferred |
+0x15c |
system mute flag | read |
+0x15d |
user mute flag (applies with +0x1ac) |
read |
+0x15e |
resulting radio mute | read |
+0x168 |
AUX no-signal mute — AUXSign_mute in the GetMuteParameters dump and aux_mute in RadioMuteManager's own line; read only by RadioMuteManager |
read |
+0x169 |
timed mute | read |
+0x1a4 |
current MGR_SRC_Source_t |
read |
+0x1a8 |
current AUDIO_SOURCES_TYPES (5 = AUX) |
read |
+0x1b0..+0x1c0 |
pointers to the current source's volume, bass, treble, loudness and EQ | read |
+0x1f8 |
touch-sound volume (audio/Vol_touch_sound) |
read |
Events in (subscriptions)¶
| from | event | handler id -> internal event -> handler |
|---|---|---|
| radio | 0x3f |
5 -> 6 -> ElabRADIO_READY_FOR_INIT_0 (subscribed in Open) |
| radio | 1 |
0 -> 1 -> ElabRADIO_READY_FOR_INIT_2 (subscribed in Open) |
| radio | 3 |
1 -> 2 -> ElabRADIO_STARTED |
| radio | 4, 5 |
2 -> 3 -> ElabRADIO_RESTART |
| radio | 0x3c |
3 -> 4 -> ElabRADIO_ANTENNA_DIAGNOSIS_CHANGED |
| radio | 0x3d |
4 -> 5 -> Elab_AUDIO_AUX_SIGNAL_STATUS_CHANGED |
| radio | 0x40 / 0x41 / 0x42 |
early started / handover ready / handover completed |
| VAN | 0x1b |
0xe -> ElabBCM_NET_CHANGED_VEHICLE_STATUS |
All but the first two are subscribed at the top of Module_routine (read). The module also
queues its own timer events: mute timers, beep ends, rear volume, amplifier thermal and car speed.
Actions out (Call_action -> C_SRV_AUDIO private message -> DBUS on com.MM.BCM_Audio)¶
| action | message | DBUS signal | raised by (read, from the decompiles) |
|---|---|---|---|
| 0 | 1 | AUDIO_STARTED |
ElabRADIO_STARTED |
| 1 | 2 | AUDIO_NOT_STARTED |
Module_routine |
| 2 | 3 | AUDIO_SOURCE_SWITCH |
Cmd_change_source, Cmd_end_mix, both init paths, ElabRADIO_STARTED |
| 3 | 4 | AUDIO_AUX_INPUT_STATUS_CHANGED |
setAUXGain (from Set_aux_status and both init paths); Set_diag_aux |
| 4 | 5 | AUDIO_AUX_SIGNAL_STATUS_CHANGED |
Elab_AUDIO_AUX_SIGNAL_STATUS_CHANGED |
| 5 | 6 | AUDIO_VOLUME_CHANGED |
Cmd_volume, Cmd_volume_limit |
| 6 | 7 | AUDIO_PARAM_CHANGED |
tone, fader, balance, loudness, EQ, AVC commands |
| 7 / 8 / 9 | 8 / 9 / 10 | AUDIO_USER_MUTED / _DEMUTING / _DEMUTED |
Cmd_user_mute, Cmd_user_demute |
0xb / 0xc / 0x13 / 0xe / 0xf |
0xb / 0xc / 0xd / 0xe / 0xf |
diagnosis / Arkamys / distribution / touch-sound / Arkamys-bypass updated | as named |
Actions 10, 0xd, 0x10-0x12 and 0x16 are not subscribed by SubscribeByAudio. Whether
anything else subscribes them is not known.
The AUX path end to end¶
Signal presence (read):
radio I2C decode -> C_I2C_SMART_RADIO event 0x3d
-> Audio_event_handler(4) -> Elab_event_AUDIO_AUX_SIGNAL_STATUS_CHANGED (queue 5)
-> tAudTask: Elab_AUDIO_AUX_SIGNAL_STATUS_CHANGED
st_audio < 10 -> dropped
source == AUX -> update +0x168, RadioMuteManager
Call_action(4) -> msg 5 -> DBUS AUDIO_AUX_SIGNAL_STATUS_CHANGED
-> C_BCM_AUDIO_CLIENT -> C_BCM_HMI_AUDIO_CLIENT -> HMI message 0xcc
-> C_HMI_AUDIO_APP_BASE::HandleDBUSMessage: refresh the audio menu if one is on screen
(C_HMI_MEDIA_APP_BASE::HandleDBUSMessage has no 0xcc case)
AUX input setting (read):
menu slider (C_HMI_MEDIA_MENU_BT_AUX_Option: 0/1/2 -> status 0/1/3)
-> C_BCM_HMI_AUDIO_CLIENT::Set_aux_status -> DBUS -> C_SRV_AUDIO::bcm_Set_aux_status
-> C_MODULE_AUDIO::Set_aux_status -> setAUXGain: radio cmd 0x33, +0x8c = status
-> Call_action(3) -> msg 4 -> DBUS AUDIO_AUX_INPUT_STATUS_CHANGED
-> C_BCM_AUDIO_CLIENT (+0x50 listener, Link A) -> C_BCM_HMI_AUDIO_CLIENT -> HMI message 0xcb
-> C_HMI_MEDIA_APP_BASE::HandleAudioAuxInputStatusChnged
state = (Get_aux_status != 0); acts only on a change -> ActivateSource(aux, true)
boot: Open -> readMemFlashInfo -> read_sqlite_AudioUserData loads +0x8c
ElabRADIO_READY_FOR_INIT_0 -> setAUXGain(+0x8c, 1) -> the same signal, once
Relation to C_MGR_SRC and C_I2C_SMART_RADIO¶
C_MGR_SRCdecides andC_MODULE_AUDIOexecutes (read).- The HMI source objects (
C_HMI_SrcAudio::SourceStart,SourcePauseEnd, the TA, nav and BT sources) callC_BCM_HMI_AUDIO_CLIENT::Cmd_change_source(slot, param)over DBUS. C_SRV_AUDIO::bcm_Cmd_change_sourcelooks the slot up in its table (this + slot*0x1c + 0x80..0x90: MGR source id, audio source type, two flags, parameters) and callsC_MODULE_AUDIO::Cmd_change_source.- The module never calls
C_MGR_SRC. How the slot table is filled (presumablyAllocateSource) was not read. C_I2C_SMART_RADIOcarries every DSP command.SendCmdRadiois the single choke point (2 070 instructions), and command0x33carries the AUX input selection, not a level (see the level trim below). It is also the source of all radio events, including AUX signal0x3d. The module reads signal presence only throughRadio::Get_AUX_signal_status(0x0131ae40) ->C_I2C_SMART_RADIO::Get_AUX_signal_status.
The per-source level trim — where the AUX gain is¶
Issue #8 asks for the default AUX gain. There is no single "AUX gain" knob: the module has two unrelated mechanisms, and only one of them is a level.
Input selection. C_MODULE_AUDIO::setAUXGain(TYPE_AUDIO_AUX_STATUS, bool) (0x013bb7ec)
maps the saved AUX setting (0..3) to a radio command — SendCmdRadio(TYPE_COMMAND, 0x33,
{u16 1, u16 <sel>}) — with one branch per setting. That chooses which AUX input the front end
listens to. It sets no level. (read)
Level. The level is a per-source trim held in the audio-parameters object.
C_RI_AUDIO_PARAMETERS_CLASS keeps five floats per AUDIO_SOURCES_TYPES, and
GetSourceScalers(t, float&) (0x0133b668) reads one of them at:
so a source's group starts at 0x358 + 20*t and the slot GetSourceScalers returns is 5t + 4
— for AUX (AUDIO_SOURCES_TYPES 5) that is object + 0x3bc. (read)
InitSourceScalers_in_dB() (0x01334764) -> C_RI_AUDIO_PARAMETERS_CLASS::InitSourceScalers
(0x0133a390) fills it from .rodata floats. Which set of floats is used is chosen by a
project-label jump table at 0x02F445E8 (16 entries, indexed by the label). (read)
On the NAV 5.43.A.R2 image the label global at 0x035DA760 reads 1, selecting the block
at 0x0133a3cc. For AUX that block writes +0x3bc..+0x3cc; the slot GetSourceScalers returns
(+0x3bc) comes from the float at 0x03071C34 = −13.95 dB. The neighbours are −11.48 and
+4.12. (read from the image — this is static, not emulated.)
So the AUX input is trimmed roughly 14 dB below full scale by default, which is the number issue #8 is looking for and is consistent with a piggyback landing quiet.
There is no patch for this yet, deliberately
What a patch would change is a .rodata float, and three things are not established:
- Which of the five floats is the level.
GetSourceScalersreturns5t + 4, but that the DSP treats it as a gain rather than a tonal/support parameter was not read. - Whether
0x03071C34is used only for AUX. A shared constant would move another source too.tools/xref.pydoes not seelfs-style references, so uniqueness is not known. - Whether raising it clips. This trim sits ahead of the DIRANA2 input, which is a hardware limit. Nothing here models the DSP's gain structure, so "does it clip" cannot be answered offline — it needs a unit and a signal generator, or a smaller step with listening tests.
So this section records the finding and stops. A patch is worth writing once the two unknowns above are settled; a guess at the float would be an unverified change to audio gain, which is exactly the kind of claim this project refuses to make.
Bearing on the AUX patches¶
Everything in this section is inferred from the reading above.
- A true "switch when a signal appears" needs a new wire. Stock routes signal changes only to a menu refresh. A patch would have to do two things:
- deliver
0xccto the media app's AUX handler; - make that handler test
Get_AUX_signal_status, notGet_aux_status.
Two constraints apply:
- the signal event is dropped before st_audio = 10;
- AUX is muted while silent (+0x168).
Both matter if AUX is selected with no signal.
Both changes are patches/aux-signal-switch.json: executed under emulation, not
flashed; see The AUX signal path. A detection before
st_audio = 10 is still lost, and aux-sticky is paired with it so silence does not
release AUX.
- Dropping the idle mute is independent of all of that. +0x168 has exactly one reader
that acts on it, RadioMuteManager, so patches/aux-no-idle-mute.json removes the mute with
one nop and touches no signal routing and no source switching
(the AUX idle mute). Inferred from the
reading, and the resulting mute decision executed.
- Boot to AUX. The boot-time AUDIO_AUX_INPUT_STATUS_CHANGED from
ElabRADIO_READY_FOR_INIT_0 means HandleAudioAuxInputStatusChnged may also run at boot.
aux-boot-restore clears PrOnly both there (0x02303474) and in InitApp
(0x022c0678). The request the boot restore sees comes from InitApp: clearing it there is
what made AUX match, and the pair with aux-boot-default boots to AUX on the car
(executed; Verification).
Functions read closely¶
Addresses are symbol starts in NAV 5.43.A.R2.
| address | function | insns | what it does | tier |
|---|---|---|---|---|
013b1a88 |
Set_hifi_mode(TYPE_HIFI_AUDIO_MODE) |
2 | Writes the HiFi mode +0x90 |
read |
013b3c5c |
RadioMuteManager(bool, short) |
271 | Computes the radio (DSP) mute: muted unless every mute flag (+0x15c system, +0x15d/+0x1ac user, +0x168 AUX no-signal, +0x169 timed) is clear and the source volume is non-zero; arms the radio-mute timer (40 ms for source type 10, else 200 ms). The only functional reader of +0x168 — every other reader of that offset is spy or log telemetry — so aux-no-idle-mute nops the +0x168 test at 0x013b3dfc (+0x1a0 in the function) |
read; the +0x168 term executed |
013b4e24 |
Get_ext_audio_src_req(bool&) |
2 | Stub: returns 0 without writing its out-parameter | read |
013b8478 |
Cmd_change_source(MGR_SRC_Source_t, AUDIO_SOURCES_TYPES, bool, bool, u |
870 | Applies a source change from the server's slot table: bounds and stores per-source vol/bass/treble/loudness/EQ pointers (+0x1b0..+0x1c0), current MGR source +0x1a4, audio source type +0x1a8; when the new type is AUX (5) sets the no-signal mute +0x168 from Get_AUX_signal_status; system mute, DSP routing (CmdSendComAud), Call_action(2) = AUDIO_SOURCE_SWITCH |
read (partly) |
013b9c8c |
Get_aux_status(TYPE_AUDIO_AUX_STATUS&) |
69 | Returns the stored AUX input setting +0x8c (the audio/Auxiliary_Status user key, 0..3) - not signal presence; -1 if the module is closed |
read |
013bb7ec |
setAUXGain(TYPE_AUDIO_AUX_STATUS, bool) |
167 | Maps the stored AUX input setting (0..3) to a DSP gain (0,1 -> 0; 2 -> 1; 3 -> 2), sends radio command 0x33; on success stores the setting at +0x8c and calls Call_action(3), i.e. raises DBUS AUDIO_AUX_INPUT_STATUS_CHANGED |
read |
013bba88 |
Set_aux_status(TYPE_AUDIO_AUX_STATUS) |
86 | DBUS Set_aux_status target: under the module mutex (+0x68) calls setAUXGain(status, 0); refuses when CheckStatus fails |
read |
013bc02c |
Get_AUX_signal_status(bool&) |
70 | Returns raw signal presence from Radio::Get_AUX_signal_status (C_I2C_SMART_RADIO); -1 if the module is closed (st_audio 0) |
read |
013c0508 |
Cmd_reset() |
2 | Stub: returns 0 | read |
013c0e00 |
Get_diag_aux(bool&) |
43 | Returns the diagnostic 'AUX fitted' flag +0x8a. No caller found |
read |
013c1364 |
Set_diag_aux(bool) |
140 | Diagnostic 'AUX fitted' option: writes +0x8a and the UP AudioCommonData, then Call_action(3) (raises AUDIO_AUX_INPUT_STATUS_CHANGED). No caller found |
read |
013ca78c |
read_sqlite_AudioUserData() |
293 | Loads audio/Vol_touch_sound into +0x1f8 and audio/Auxiliary_Status into +0x8c (accepted only if < 4) from the UP user keys |
read |
013cb3f4 |
readMemFlashInfo() |
421 | Resets defaults (+0x8c=0, +0x8a=0, beep presets ...) then loads AudioConfigData / AudioCommonData (HiFi mode +0x90, 'AUX fitted' +0x8a) and the user keys |
read (partly) |
013cbda0 |
Elab_event_AUDIO_AUX_SIGNAL_STATUS_CHANGED() |
18 | Queues internal event 5 on the module message queue (+0x64); called by Audio_event_handler for radio event 0x3d |
read |
013cbf08 |
Audio_event_handler(C_MODULE_AUDIO*, AUDIO_MY_EVENT) |
135 | Maps a subscribed radio/VAN event to one of 16 Elab_event_* queue posts (0 init_2 ... 4 AUX signal ... 0xf ext audio src) |
read |
013cc124 |
Module_routine() |
1069 | Task loop: subscribes the remaining radio events (3,4,5,0x3c,0x3d,0x40-0x42) and VAN event 0x1b, then receives internal events and dispatches (5 -> Elab_AUDIO_AUX_SIGNAL_STATUS_CHANGED, 6 -> init_0, 1 -> init_2, 0x11.. mute timers, 0x18/0x19 rear volume, 0x1d-0x21 spy beeps ...) |
read (dispatch only) |
013ce870 |
Elab_AUDIO_AUX_SIGNAL_STATUS_CHANGED() |
153 | Handles the internal AUX-signal event: ignored until st_audio (+0x74) reaches 10; when the current source (+0x1a8) is AUX (5), reads Radio::Get_AUX_signal_status and sets +0x168 while there is no signal, clears it when the signal returns, re-running RadioMuteManager only when the flag actually changed; always ends with Call_action(4), the DBUS AUDIO_AUX_SIGNAL_STATUS_CHANGED |
read; the flag's four arms executed |
013cf0fc |
CheckStatus() |
99 | Refuses commands while st_audio is 0 (closed) or 0x28 (error) |
read |
013d4514 |
ElabRADIO_RESTART() |
41 | Radio restart: st_audio back to 1, or 2 if the radio had started before (+0x70) |
read |
013d565c |
ElabRADIO_STARTED() |
195 | Radio started: st_audio=10, +0x70=1; if the current source is AUX, refreshes the no-signal mute +0x168; raises actions 0, 2, 0xb, 0xc |
read (partly) |
013d5968 |
ElabRADIO_READY_FOR_INIT_2() |
656 | Alternative init path (radio event 1): Arkamys option, fader/balance, and the same setAUXGain(+0x8c, 1) near the end |
read (partly) |
013d6614 |
ElabRADIO_READY_FOR_INIT_0() |
651 | Main boot-time audio init after the radio reports ready (event 0x3f): software level, DSP set-up commands, amplifier diagnosis/start-up, RadioMuteManager(true), beep presets, then setAUXGain(+0x8c, 1) - so every successful boot raises AUDIO_AUX_INPUT_STATUS_CHANGED once |
read |
013d7040 |
Cmd_emergency_switch(bool) |
2 | Stub: returns 0 (emergency-call switch not implemented here) | read |
013d70a4 |
Call_action(int) |
46 | Logs, then C_BASE::Call_action(n): runs the subscribers for action n. C_SRV_AUDIO subscribes action n to private message n+1 for n 0..9 (3 -> AUX input, 4 -> AUX signal) |
read |
013d79a4 |
Open(Radio*, UP_MOD*, C_VAN_BM*) |
452 | Stores Radio/UP/VAN pointers (+0x54/+0x5c/+0x58), creates message queue +0x64 and mutexes +0x68/+0x16c, readMemFlashInfo, subscribes radio events 0x3f and 1, spawns task tAudTask, sets st_audio=1 |
read |
The rest, by purpose¶
The remaining 248 functions were classified, not read. Tier: inferred for every row.
| purpose | functions |
|---|---|
| User audio parameters: volume, fader, balance, tone, loudness, EQ, speed-dependent volume (AVC), limits | 50 |
| Beeps (single, cyclical, parking-sensor, early-beep handover) and their volumes/speakers | 43 |
| Mute / amplifier management (system, user, timed, hardware and amplifier mutes; Dirana DSP PLL reset) | 26 |
Posts an internal event to the module queue (+0x64); runs on the radio/VAN/CAN thread that raised it |
23 |
| Diagnostics: amplifier/speaker diagnosis, thermal refresh, diagnostic configuration options | 18 |
| Lifecycle, task and event plumbing | 16 |
| Source routing and mixing (TTS/navigation mix, fast/end mix, source queries) | 13 |
Internal-event handler run on tAudTask by Module_routine |
11 |
| Arkamys sound processing option and speaker distribution | 8 |
| Timer callback trampoline: posts the matching internal event | 7 |
| Commands to the radio/DSP through C_I2C_SMART_RADIO | 7 |
| HiFi-over-CAN (external amplifier) volume/mode handling | 7 |
| Spy/diagnostic logging and status dumps | 7 |
| Microphone ADC/DAC gain and phone microphone path | 6 |
| Configuration, persistence and status queries | 4 |
| other single-purpose functions (lifecycle handlers, persistence, early-start) | 2 |
Not known¶
- Which radio I2C message sets event
0x3d, and its debounce (DecodeMsgXXX), belongs to theC_I2C_SMART_RADIOreading. - Whether the HMI listener (
C_BCM_AUDIO_CLIENT+0x50, Link A) exists whenElabRADIO_READY_FOR_INIT_0announces the AUX setting at boot. This needs a spy trace with DBUS logging, or emulation of the HMI start-up order. - The meaning of
TYPE_AUDIO_AUX_STATUSvalues 1 and 3 in the vendor UI; the menu offers three positions, mapped to 0/⅓. - Callers of
Set_diag_aux,Get_diag_auxand the other*_diag_*setters with "(none found)". They are probably reached through a diagnostic table this scan does not model.