Skip to content

fix(trainer): SBUS trainer context ownership - #7699

Open
raphaelcoeffic wants to merge 2 commits into
mainfrom
sbus-trainer-ownership
Open

fix(trainer): SBUS trainer context ownership#7699
raphaelcoeffic wants to merge 2 commits into
mainfrom
sbus-trainer-ownership

Conversation

@raphaelcoeffic

Copy link
Copy Markdown
Member

The SBUS trainer receiver is a single consumer (trainerInput[]), so only one source can feed it at a time. Which one is decided by the trainer mode, but the two sources used to register on completely different lifecycles: the external module claimed the context when the trainer mode selected it, while an AUX serial port claimed it at port configuration time, from serialSetCallBacks().

That asymmetry let both be registered at once and left dangling contexts behind:

  • trainer_stop_module_sbus() never released the context, so after modulePortDeInit() zeroed the serial state the receiver was left pointing at a freed driver context.

  • conversely, re-configuring an AUX port that is in SBUS trainer mode cleared the context even when the external module trainer was the current owner, silently killing trainer input.

Make the trainer own the context for both sources: sbusTrainerAcquire() claims it and arms the IDLE callback, sbusTrainerRelease() disarms and drops it, and both are driven from checkTrainerSettings()/stopTrainer(). Ordering is arm-last / disarm-first, so no callback can start once release() returns.

Serial port teardown revokes via sbusTrainerReleaseCtx(), which only releases if that port is the current owner, and checkTrainerSettings() re-acquires when the port becomes usable again, so no caller of serialInit() needs to know about the trainer.

Also:

  • stm32_usart_deinit(): NVIC_DisableIRQ() alone leaves an already latched IRQ pending, which would then be taken against a de-initialised, clock-gated peripheral. Add the barrier and clear the pending bit.
  • stm32_serial_deinit(): make de-init idempotent.
  • isTrainerModeAvailable(): TRAINER_MODE_MASTER_SERIAL ignored UART_MODE_SBUS_TRAINER_INV, so the mode was reported unavailable on radios using an inverted SBUS trainer port.

Fixes #7644

The SBUS trainer receiver is a single consumer (trainerInput[]), so only
one source can feed it at a time. Which one is decided by the trainer
mode, but the two sources used to register on completely different
lifecycles: the external module claimed the context when the trainer
mode selected it, while an AUX serial port claimed it at port
configuration time, from serialSetCallBacks().

That asymmetry let both be registered at once and left dangling
contexts behind:

 - trainer_stop_module_sbus() never released the context, so after
   modulePortDeInit() zeroed the serial state the receiver was left
   pointing at a freed driver context.

 - conversely, re-configuring an AUX port that is in SBUS trainer mode
   cleared the context even when the external module trainer was the
   current owner, silently killing trainer input.

Make the trainer own the context for both sources: sbusTrainerAcquire()
claims it and arms the IDLE callback, sbusTrainerRelease() disarms and
drops it, and both are driven from checkTrainerSettings()/stopTrainer().
Ordering is arm-last / disarm-first, so no callback can start once
release() returns.

Serial port teardown revokes via sbusTrainerReleaseCtx(), which only
releases if that port is the current owner, and checkTrainerSettings()
re-acquires when the port becomes usable again, so no caller of
serialInit() needs to know about the trainer.

Also:
 - stm32_usart_deinit(): NVIC_DisableIRQ() alone leaves an already
   latched IRQ pending, which would then be taken against a
   de-initialised, clock-gated peripheral. Add the barrier and clear
   the pending bit.
 - stm32_serial_deinit(): make de-init idempotent.
 - isTrainerModeAvailable(): TRAINER_MODE_MASTER_SERIAL ignored
   UART_MODE_SBUS_TRAINER_INV, so the mode was reported unavailable on
   radios using an inverted SBUS trainer port.
@raphaelcoeffic
raphaelcoeffic force-pushed the sbus-trainer-ownership branch from 52ce0f0 to f9585a1 Compare August 23, 2026 10:11
UART_MODE_SBUS_TRAINER_INV drives the USART into inverting RX itself, for
ports that have no hardware inverter. That only exists on H5/H7, as
stm32_serial_init() already reflects: elsewhere the polarity request is
silently dropped and the port receives plain, non-inverted data.

The mode is hidden from the port selection on such targets, but a
configuration written on another radio still selects it, and the port
was then offered as trainer input even though it cannot decode the
signal. Restrict serialGetSbusTrainerPort() to the families that can
actually invert, so the trainer and isTrainerModeAvailable() agree and
no context is claimed on a port that cannot work.
@pfeerick

pfeerick commented Aug 23, 2026

Copy link
Copy Markdown
Member

It looks like this overlaps #7696 ???

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.

RadioMaster Boxer crashes when selecting Trainer mode Master/Serial with an SBUS signal connected to AUX1

2 participants