STM32WB55 HCI Notifications Can Arrive Outside the IPCC Interrupt

An RTOS port can receive its first BLE event correctly and then stop processing a burst because its notification hook was designed only for interrupts. On STM32WB55, hci_notify_asynch_evt is also called from the background event processor and from flow resumption. The hook must schedule work correctly from all three origins.

The scope is STM32CubeWB v1.24.0, commit 7c5aa7dcd2c0abe787f922bb06a6213c724d08e4, using the WB55 BLE transport and the P-NUCLEO-WB55.Nucleo example. This is a steady-state event-dispatch review, not CPU2 startup sequencing or STM32WBA integration. No firmware was compiled or run on a board.

Read the notification contract before choosing an RTOS API

The pinned hci_tl.h declaration identifies three calling contexts: IPCC reception, hci_user_evt_proc and hci_resume_flow. A hook named as an asynchronous notification is therefore not an assurance that every invocation is an ISR.

A port that unconditionally calls an interrupt-only RTOS primitive needs review. So does a port that unconditionally uses a task-only primitive. Select the API family and any yield behavior from the actual supported caller context, following the chosen RTOS’s contract and interrupt-priority restrictions. Do not guess context from whether the device is currently connected.

An alternative is a deliberately context-independent scheduling primitive whose implementation has been reviewed for these callers. The ST sequencer example uses a task request; a custom RTOS bridge must establish its own equivalent behavior rather than copying the function name alone.

HCI notification paths from an IPCC interrupt, the event processor and flow resumption converge on one background scheduling bridge.

Original source-derived call graph. The arrows describe allowed calling paths, not measured event timing.

Preserve the request made by the consumer itself

In the pinned hci_user_evt_proc implementation, one invocation reports at most one queued asynchronous event. If enabled flow and another queued event remain, it calls the notification hook again. That gives other background work an opportunity to run between events.

This detail invalidates a common porting assumption: one wakeup does not imply that one call drains the entire queue. A single original interrupt can leave several queued events that require the consumer’s own subsequent scheduling requests.

Consider a hypothetical bridge with a Boolean pending flag. Its worker calls hci_user_evt_proc and then clears pending. During the call, the transport may set pending again because another event remains. Clearing the flag afterward discards that new request. The worker goes idle with queued data and may not recover until an unrelated interrupt supplies another notification.

The problem is the ordering of notification consumption, not an insufficient delay or an unreliable radio.

Give the wakeup primitive an explicit consumption rule

Use a scheduling mechanism that retains requests issued while the worker is executing. For a bit-based bridge, the conceptual ordering is:

producer, in any supported context:
    atomically mark work pending and wake the consumer

consumer:
    atomically consume the pending request
    run hci_user_evt_proc in background context
    return to the scheduler
    run again if another request is pending

This is an original synchronization sketch, not complete RTOS code. The atomic consume and sleep transition need the selected scheduler’s lost-wakeup protection. A plain volatile variable does not supply that protection, and a separate “check flag, then sleep” sequence still needs review.

Do not replace deferred scheduling with a direct recursive call to hci_user_evt_proc inside the hook. That would make the processing context depend on the caller and remove the intended background boundary. Under queued traffic, recursion also changes stack usage and fairness.

Do not count notifications as received packets

Multiple notifications can collapse into one pending task bit, while one event-processing call can request another pass. Consequently, a notification counter is not an application event count. Keep separate diagnostic counters for hook entries, worker invocations and decoded application events.

The BLE_p2pServer bridge requests the registered HCI asynchronous-event task from its hook. When adapting that pattern, preserve the scheduling meaning without assuming the callback’s data argument is an application-owned packet to enqueue indefinitely.

A consumer that runs too slowly can still create pressure on finite transport storage. Correct notifications do not provide unlimited buffering. Establish the required service rate and keep expensive parsing, formatting and unrelated storage operations out of the critical event-dispatch path.

Test all three producers and the quiet ending

A useful software fixture exercises an interrupt-origin request, a consumer-origin re-request and a flow-resume request. Include a burst that ends while multiple events remain queued. That quiet ending is important: repeated external traffic can hide a lost self-request by continually waking the worker again.

Record the pending state before and after each consume, the context of each hook entry and whether the worker returned to the scheduler between events. Do not clear or inspect the transport’s private queue from competing contexts merely to make the trace convenient.

Acceptance requires every retained request to produce eventual background processing under the stated scheduler assumptions, without ISR-only calls from task context or processing recursion from the notification hook. The checks are proposed, not executed. They identify the specific integration contract that a “first event works, later events stall” report often leaves unexamined.

Similar Posts