Skip to content

Restore radio power mode and calibration on resume - #216

Open
beaglemoo wants to merge 1 commit into
dlundqvist:masterfrom
beaglemoo:fix/resume-radio-recalibration
Open

Restore radio power mode and calibration on resume#216
beaglemoo wants to merge 1 commit into
dlundqvist:masterfrom
beaglemoo:fix/resume-radio-recalibration

Conversation

@beaglemoo

@beaglemoo beaglemoo commented Aug 15, 2026

Copy link
Copy Markdown

Problem

After a system suspend/resume, a controller associates for about 10 seconds, drops, reassociates, and loops indefinitely. Power-cycling the controller appears to fix it for a few seconds each time, which is misleading - the broken end is the dongle.

Everything looks healthy while this happens: the dongle enumerates, the modules are loaded, the interface is bound and the firmware is present. The tell is that /sys/bus/xone-gip/devices/ is empty between attempts, and the controller re-registers with a climbing input index:

[1029242.617792] PM: suspend exit
[1029247.025222] input: Microsoft Xbox Controller ... input49
[1029271.359313] xone-gip gip0: gip_init_chunk_buffer: already initialized (in)
[1029306.671157] input: Microsoft Xbox Controller ... input51
[1029394.862664] input: Microsoft Xbox Controller ... input55     <- every 10-13s, indefinitely

Only a full USB re-enumeration, which reruns init_radio(), recovered it.

Cause

xone_mt76_resume_radio() restores less than xone_mt76_init_radio() sets up. It clears wake-on-wireless, switches channel, re-enables the beacon and sets MAC RX/TX, but it never calls set_power_mode(MT_RADIO_ON) and never calls calibrate_radio().

Across a system suspend the MCU leaves MT_RADIO_ON and the RXDCOC / RC / temperature-sensor calibrations applied at init are lost, and nothing restores them. The MAC layer does survive, which is why the dongle still beacons and a controller still completes association - but the uncalibrated RF path cannot sustain the link, so it collapses seconds later. calibrate_radio()'s own comment notes that the reset it performs is "required for reliable WLAN associations".

This affects the .resume path only. .reset_resume already reinitialises via xone_dongle_init(), which is why machines that lose dongle power across suspend are unaffected.

Fix

Mirror init_radio() in the resume path: set_power_mode(MT_RADIO_ON), calibrate_radio(), then the existing channel switch plus the mandatory post-channel-change delay.

Testing

Hardware: Valve Steam Machine (SteamOS / holo), Xbox Wireless Adapter 045e:02fe (MT7613), Xbox Wireless Controller.
Kernels: 6.16.12-neptune-616 and 6.18.42-neptune-618. Builds clean on both, no changes needed for 6.18.

Measured across a resume, before and after the patch:

before after
controller registrations after wake 8 in 4 min (every 10-13s) 1
add_client repeats many 0
gip_init_chunk_buffer errors 4 0

Then confirmed across a 6.95-day suspend (suspend entry at uptime 1001s, suspend exit at 601194s, so 600193 seconds asleep) - the same multi-day sleep that originally triggered the fault. One controller registration on resume, zero errors, link stable afterwards.

xone_mt76_resume_radio: resumed is the dev_dbg at the end of the function, and both new calls sit above it with return err on failure, so reaching that line in the log confirms both succeeded.

Unrelated observation

Not addressed here, but noticed while instrumenting this: the client is added roughly 0.7s before resume_radio() finishes, which trips xone-gip gip0: PM: parent 3-1:1.0 should not be sleeping. It appeared harmless in testing, but it may be worth a separate look.

xone_mt76_resume_radio() only cleared wake-on-wireless, switched
channel, re-enabled beacons and set MAC RX/TX. It never returned the
MCU to MT_RADIO_ON and never redid the RXDCOC/RC/temperature
calibrations that init_radio() performs via calibrate_radio().

The MAC layer survives a system suspend, so after resume the dongle
still beacons and a controller still associates - but the uncalibrated
RF path cannot sustain the link. It collapses a few seconds later and
the controller reassociates roughly every 10 seconds indefinitely.
Only a full USB re-enumeration (which reruns init_radio) recovered it.

Mirror init_radio() in the resume path: set_power_mode(MT_RADIO_ON),
calibrate_radio(), then the existing channel switch plus the mandatory
post-channel-change delay.

This affects the .resume path only. The .reset_resume path already
reinitialises via xone_dongle_init().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant