STM32WB55 Low Power Requester Bits Are Not Reference Counted Locks
Two operations that share one low-power requester bit can accidentally allow Stop mode while one operation is still active. In STM32CubeWB’s tiny low-power manager, disabling a mode sets a bit and enabling it clears that bit. Repeating disable does not create a second reference that must later be released.
This source review targets STM32WB55 with STM32CubeWB v1.24.0, commit 7c5aa7dcd2c0abe787f922bb06a6213c724d08e4. It addresses CPU1 policy ownership before low-power entry. The separate WB clock and hardware-semaphore handoff must remain intact. No sleep transition, DMA operation or current measurement was performed.
Read the state update as a bit operation
The pinned stm32_lpm.c maintains separate StopModeDisable and OffModeDisable masks. UTIL_LPM_DISABLE ORs the supplied requester mask into the selected veto mask. UTIL_LPM_ENABLE clears those bits. The implementation contains no per-requester nesting counter.
The same source selects Sleep if any Stop veto remains, Stop if no Stop veto remains but an Off veto does, and Off when neither mask has a veto. “Enable Stop” consequently means that this requester no longer prohibits it. It is not a command to enter Stop immediately, and it does not override another requester’s veto.
The public header expresses the argument as a bit mapping. Review whether a call supplies one intended bit or several. Accidentally passing an enum value directly rather than constructing its bit mask can change which owners are represented.

Original illustrative state trace. It does not predict the board’s eventual current or wake capability.
Reproduce the overlapping-owner mistake on paper
Consider an application bit shared by a UART transmission and a sensor transfer. The UART path disables Stop, setting the bit. The sensor path also disables Stop; the mask is unchanged because the bit was already set. When UART completion enables Stop, the bit clears even though the sensor transfer remains active.
The manager has followed its contract. The application supplied one Boolean veto for two independently owned lifetimes without preserving their combined state.
This defect can hide in serial tests. Each operation alone produces a balanced disable/enable pair, and normal traffic may finish in a consistent order. Review overlaps and cancellation paths rather than accepting a count of matching function calls as an ownership proof.
The reverse error also matters: an early failure can leave a veto set indefinitely. That may increase power use without corrupting the transfer, so a functional test can pass while the power policy remains wrong.
Give independently owned lifetimes distinct representation
One solution is a separate requester bit for each subsystem that independently decides whether a mode is safe. The WB55 app_conf.h provides the application’s requester enumeration. Extend it consistently within the supported mapping width and reserve ownership of every allocated bit.
Another solution is one subsystem owner that derives its single vote from all active operations. That owner can maintain an internal active count or explicit operation set, then clear its veto only when none remain. Such bookkeeping belongs above tiny_lpm; adding repeated disable calls does not create it automatically.
For a counted owner, an original policy sketch is:
first active operation:
establish the Stop veto before starting unsafe work
additional operations:
retain the same veto
operation completion or accepted cancellation:
retire exactly that operation
last operation retired:
clear the veto
The count and mask updates need one reviewed synchronization protocol. Avoid a gap where another context sees no active operation and enters low power before the first veto is established. An underflow or duplicate completion must not silently clear a live owner’s protection.
Tie release to hardware completion rather than API return
For asynchronous work, a successful start function usually establishes that the operation was accepted, not that its hardware dependencies are no longer needed. Hold the relevant policy veto until the product-specific completion or safe-cancellation boundary.
Do not guess which low-power mode a peripheral tolerates from the word DMA. Determine the required clocks, wake capability and selected transfer mode from the actual peripheral configuration. A subsystem might need an Off veto while allowing Stop, or it might need to prohibit Stop as well. Encode the demonstrated requirement, not the deepest mode that happens to pass one bench run.
Keep this application policy separate from stm32_lpm_if.c, which implements the target-specific entry and exit work. A correctly owned vote does not replace the WB dual-core transition protocol.
Qualify both premature release and permanent veto
Proposed tests start two operations, complete them in each order, fail one at admission, cancel one after starting and inject a duplicate completion into the application model. At each step, compare active ownership with the effective vote rather than observing only the final idle state.
Also test initialization and reset of the software manager. Reinitializing masks while operations remain active discards the policy history; it should occur only at the lifecycle boundary the application design permits.
A release review should account for every veto owner, its acquisition point, all terminal paths and the evidence that release is safe. That record distinguishes a genuine peripheral low-power limitation from a bookkeeping error that either lets the system sleep too early or prevents it from sleeping at all.