Repository navigation
fix(bindings): reset a BlueZ controller the kernel lost track of - #577
Merged
Merged
Conversation
On Linux 6.18 with BlueZ 5.87 a Raspberry Pi 4 stopped accepting any new
advertisement ("Failed to register advertisement") while BlueZ reported
every slot free, and the state outlived the service: each replacement
container failed the same way until the controller was reset.
The kernel clears its advertising flag on every LE connection complete,
assuming the controller stopped. A controller only stops for a link in
which it is the peripheral. When advertising is on while our central's
dial is pending (a discovery restart re-enables it), the dial completes
in the central role, the controller keeps advertising, and the kernel
believes it stopped. From then on every LE Set Advertising Parameters is
answered Command Disallowed. Captured in the HCI trace on the Pi.
- BlePeripheral resets the adapter once (Adapter1.Powered off and on,
which sends an HCI Reset) when BlueZ refuses an advertisement that way
and reports a free slot, then starts again. Once only: the reset drops
every link on the adapter.
- bluez_restarts also re-registers a running role when an adapter powers
back on. BlueZ drops every discovery session on power-off, so without
it the central in the same process went deaf after the reset.
Verified on a wedged Pi: the start reset the adapter once and was
advertising a second later, with the controller accepting the advertising
parameters again.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On two Raspberry Pi 4s (Linux 6.18, BlueZ 5.87) the Python peripheral failed every start with
Failed to register advertisement, and every replacement service failed the same way. It looked like advertisements left behind by replaced containers were filling the slots. They weren't: BlueZ reportedActiveInstances 0andSupportedInstances 5while it was failing, and a full set of slots gets a different answer ("Maximum advertisements reached").Root cause
Linux loses track of the controller's advertising state.
hci_event.c(le_conn_complete_evt, line 5650 in v6.18) clearsHCI_LE_ADVon every LE Connection Complete, assuming the controller stopped advertising. A controller only stops for a link in which it is the peripheral.Captured in the HCI trace on the Pi (13:41:40 UTC):
LE Connection Complete, role Central. The kernel clears its flag, but the controller keeps advertising.LE Set Advertising ParametersandLE Set Random Addressis answeredCommand Disallowed (0x0c), so every new advertisement is refused until the controller is reset.Fix
BlePeripheral._bring_up: on Linux, when the start fails withFailed to register advertisementand BlueZ reports a free slot, it powers the adapter off and on throughAdapter1.Powered(which sends an HCI Reset and clears the stale advertising), then starts once more. It does this once only, because the reset drops every link on the adapter. Any other refusal is raised unchanged.bluez_restarts: an adapter'sPoweredturning true now triggers the same re-registration as a bluetoothd restart. BlueZ drops every discovery session on power-off, so without this the central in the same process stopped scanning after the reset. The same applies to any power toggle by another process.docs/bridges/python.mdP17, the bridges README row, and a CHANGELOG entry.Verification
pytest bindings/python/tests: 1128 passed. There are new tests for the reset path (it resets once, any other error is left alone, the free-slot check, the power-off-then-on order) and for the power-on signal. Each of 7 deliberate code breakages makes a test fail.Command Disallowed, thenReset, thenSet Advertising ParametersandSet Advertise Enablesucceeding.Not covered