Commit graph zephyr/subsys/bluetooth
Author SHA1 Message Date
Johan Hedberg
2b9422c426 Bluetooth: addr: Change LE address string format to prefix-based
Replace the "XX:XX:XX:XX:XX:XX (type)" string representation of LE
addresses with a compact form that carries the address type as a
single-character prefix:

- "P:XX:XX:XX:XX:XX:XX" for public addresses
- "R:XX:XX:XX:XX:XX:XX" for random addresses

The address and its type are now one whitespace-free token, which makes
shell input trivial to parse and lets an address be pasted from log or
shell output straight into a command.

bt_addr_le_to_str() produces the new format. Address type values that
carry HCI-level extension bits, such as the Controller-resolved
identity types, are formatted by their base type, and the unresolved-RPA
and anonymous sentinels as random addresses; the extra information has
to be conveyed separately by the caller. BT_ADDR_LE_STR_LEN shrinks from
30 to 20 bytes.

bt_addr_le_from_str() loses its separate type argument and accepts only
the new format, with the prefix, like the hexadecimal digits, accepted
in either case. Neither the old output nor the old two-token input form
is retained: the string representation was never documented as a
stable format, and the Bluetooth Working Group agreed to switch to the
new one directly rather than carry a deprecation period.

All shell commands taking an LE address (bt connect, disconnect,
select, info, clear, directed-adv, adv-create/adv-param directed,
per-adv-sync-create, oob-remote, fal-add, fal-rem, id-create, id-reset,
gatt resubscribe, bap_broadcast_assistant add_src, bap_scan_delegator
add_src and cap_commander broadcast_reception_start) now take the
address as a single argument. Their argument counts, positional
argument indices, help strings and shell documentation are updated
accordingly. While at it, bap_scan_delegator add_src is fixed to
reference the right argv slots in its error messages and metadata
parsing, and id-create no longer proceeds with an uninitialized address
after failing to parse its argument.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-22 17:53:33 +02:00
Emil Gydesen
2126bb20a1 Bluetooth: BAP: SD: Always call bis_sync_req on NO_PREF
If the remote device is requesting BT_BAP_BIS_SYNC_NO_PREF
then the scan delegator will no longer make a choice on whether this
counts like a new request, as that choice is left to the
higher layers.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-22 10:29:26 +02:00
Lyle Zhu
cb583765de bluetooth: le: audio: add LE connection type checks
There is an issue found that when both Bluetooth Classic and LE audio
are enabled, the LE audio driver may cause an assertion when a Classic
connection notification occurs because it doesn't check the connection
type.

Add `bt_conn_is_type()` checks to Bluetooth audio subsystem callback
functions to ensure they only process LE connections. This prevents
potential issues when BR/EDR or other connection types trigger these
callbacks.

The checks are added early in callback functions before accessing
LE-specific connection fields or performing LE-specific operations.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-22 10:27:21 +02:00
Jamie McCrae
1909f76910 kconfig: Correctly have EXPERIMENTAL prompt
Fixes various issues with Kconfigs, including:
  - Using the wrong indentation
  - Not having [EXPERIMENTAL] in the prompt, or not having it at
    the end
  - Wrongly stating the CONFIG_EXPERIMENTAL is needed to enable
    an experimental Kconfig

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2026-09-21 16:27:52 -04:00
Jamie McCrae
64d8dbe48b kconfig: Fix formatting, correctly select DEPRECATED and have prompt
Fixes various issues with Kconfigs, including:
  - Redundent `default n` on bools
  - Not select deprecated on supposed deprecated symbols
  - Using the wrong indentation
  - Not having [DEPRECATED] in the prompt, or not having it at the
    end

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
2026-09-21 16:27:52 -04:00
Emil Gydesen
3996bbdab7 Bluetooth: CAP: Fix issue with started sub-cb for sink-only
When starting a set of sink streams only, the callback for the
start subprocedure was not called, because the subproc_initiated
was not set.

When we are starting sink streams only, there is also a race
condition between the CIS connected event and the ASE GATT
notification that was unhandled.

When get_next_proc_param returns NULL, we now check if that is the
case, and if all streams are already in the streaming state,
we can finish the procedure directly, or fail if they are not.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-21 16:27:11 -04:00
Sharon Lin
42742c9e2b Bluetooth: Host: Add bt_id_reset_irk API
Add bt_id_reset_irk(uint8_t id, uint8_t *irk) to replace the local
Identity Resolving Key for an identity without changing its identity
address.

The caller can provide an IRK, or pass NULL or an all-zero buffer to
have the stack generate one and return it in the buffer.

Reject the operation when the stack is not ready, any advertising set
is active, the identity has a created extended advertising set, the
default identity is used for scanning or initiating, or bonds exist.
Bonds must be removed with bt_unpair() before resetting the IRK.

Persist the new IRK when CONFIG_BT_SETTINGS is enabled.
Invalidate the current RPA state and the cached per-identity RPA
after a successful reset.
The API is available when CONFIG_BT_PRIVACY is enabled.
Preserve -ENOTSUP when random generation is unsupported and map other
random generation failures to -EIO.

Assisted-by: Augment:GPT-5.6
Signed-off-by: Sharon Lin <slin@atmosic.com>
2026-09-21 16:25:23 -04:00
Johan Hedberg
7d713288b4 Bluetooth: Kconfig: Deprecate BT_CUSTOM
Mark the custom, non-HCI stack selection as deprecated. The option
dates from the early days of the subsystem, when a product offloaded
the whole Bluetooth Host behind the Zephyr Bluetooth API. Nothing in
the tree selects it, the placeholder for custom drivers under it has
been empty since it was added, and HCI transports are regular device
drivers today. Out-of-tree stacks add their own entry to the choice,
which stays as that extension point; only this entry goes after the
deprecation period.

A build with the option enabled has not compiled since commit
d382fca6ff ("Bluetooth: Controller: Fix HCI command buffer allocation
failure"), which added a static assertion on HCI-only buffer options to
a header the common code includes, and nobody reported it.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-20 19:06:48 +02:00
Wai-Hong Tam
2ea7b9c4a1 Bluetooth: Kconfig: Only restrict system workqueue priority when needed
Enabling CONFIG_BT restricts SYSTEM_WORKQUEUE_PRIORITY to the
cooperative range for every build, but the requirement comes from the
individual components that submit work to the system workqueue, not
from Bluetooth being enabled.

An application that asks for a preemptible system workqueue therefore
cannot build with Bluetooth enabled at all, even when no Bluetooth
code uses that queue. Running the system workqueue below every other
thread is a common way to service a watchdog only while the whole
system is still making progress.

Add BT_SYSTEM_WORKQUEUE_COOP and apply the range only when it is set.
The components that need the system workqueue to stay cooperative
select the option themselves, so the requirement is recorded next to
the code that has it and a new work item cannot slip past it: the
host stack, the H:5 and STM32WB0 drivers, the NXP wake-on-BT LED
blink option, and BT_CTLR_PRIVACY when the split link layer builds
ull_filter.c.

Mesh, Audio and the other Host users need the Host, so they are
covered by its select; they get their own when the Host drops it.

No in-tree configuration changes behavior: SYSTEM_WORKQUEUE_PRIORITY
already defaults to -1, and no Bluetooth test or sample overrides it.

Signed-off-by: Wai-Hong Tam <waihong@google.com>
2026-09-20 19:06:12 +02:00
Cheng Chang
e753f31b64 bluetooth: shell: bip: Fix incorrect application parameter tag ID
Use BT_BIP_APPL_PARAM_TAG_ID_SERVICE_ID instead of
BT_BIP_APPL_PARAM_TAG_ID_STORE_FLAG when setting the service ID
application parameter in cmd_bip_client_start_print() and
cmd_bip_client_start_archive().

The service ID parameter should be used to identify the printing
and archiving services respectively, not the store flag parameter.

Signed-off-by: Cheng Chang <cheng.chang@nxp.com>
2026-09-18 11:18:47 +01:00
David Brown
fae6df2add Bluetooth: Classic: L2CAP: Bound BR/EDR SDU reassembly
bt_l2cap_br_recv_seg() reassembles an SDU from the segments of an
I-frame stream, for channels that supply an alloc_buf callback. It
takes the declared SDU length from a SAR_START segment and never
compares it against the channel's negotiated receive MTU, so the peer
can announce an SDU of up to 65535 bytes on a channel configured for
far less, and reassembly is not bounded by anything the two sides
agreed on. The seg_recv path in bt_l2cap_br_recv_seg_direct() already
rejects such an SDU; apply the same check here so the two paths agree.
Every in-tree profile using this path sets rx.mtu at or above the size
of the buffers its alloc_buf callback hands out, so none of them needs
to receive an SDU larger than the MTU it advertised.

The running total the function compares against that declared length is
also wrong. Segments are appended with net_buf_append_bytes(), which
chains a new fragment whenever the current buffer fills up, so once
reassembly spills past the head buffer the data lands in the fragment
chain and the head buffer's len stops growing. Every test written in
terms of _sdu->len then measures the head buffer alone rather than how
much has actually arrived: the overflow guard stops rejecting oversized
input, and the SAR_END completeness check rejects a complete SDU as
short. Use net_buf_frags_len() for all three reads of the accumulated
length, the debug log included, so they describe the whole chain.

Assisted-By: Claude:opus-5
Signed-off-by: David Brown <david.brown@linaro.org>
2026-09-18 11:17:35 +01:00
David Brown
412767b311 Bluetooth: Classic: GOEP: Do not wait for L2CAP RX buffers
The buffer callback of a GOEP v2 L2CAP channel allocates with
K_FOREVER. L2CAP calls it from the Bluetooth RX workqueue, both for a
new SDU and for every further fragment appended to it, and it is that
same workqueue that releases the buffers once the SDU has been handed
to OBEX. The pool is shared by all GOEP v2 channels and one SDU can
take several buffers, so once it runs dry the wait can never be
satisfied and it stops all HCI receive processing instead.

The callback also has no timeout parameter, so a caller that asks for
a non-blocking append has no way to say so; the callback has to
decide, and here the only workable answer is not to wait.

Allocate without waiting. L2CAP already turns a failed allocation on
this channel into -ENOBUFS and reports the channel as locally busy to
the peer, which is the back pressure the peer expects, unlike a
blocked host.

Compile-tested only; there is no GOEP v2 test coverage in the tree.

Assisted-By: Claude:opus-5
Signed-off-by: David Brown <david.brown@linaro.org>
2026-09-18 11:17:35 +01:00
tallplay lin
b0be80a30d Bluetooth: Host: shell: fix adv_set_opt bitmap indexing
The shell declared one option bitmap for all advertising sets instead
of one per set. Creating a second advertising set then wrote its
options past the end of that bitmap, corrupting adjacent state such
as adv_sets[0]: after "bt adv-create" twice, "bt adv-info" no longer
showed the first advertiser correctly.

Declare the set dimension inside ATOMIC_DEFINE so every advertising
set owns a complete option bitmap, and add a compile-time check that
the array size matches CONFIG_BT_EXT_ADV_MAX_ADV_SET.

Signed-off-by: tallplay lin <tlin@atmosic.com>
2026-09-18 11:16:57 +01:00
Johan Hedberg
bf9cd11485 Bluetooth: Add HCI lockstep command helper
Add hci_lockstep.h, a helper on top of the HCI packet helpers for
sending HCI commands one at a time and waiting for their responses
through a caller-provided transport send function. The header lives
with the HCI driver API under include/zephyr/drivers/bluetooth, since
HCI drivers are its users, and the implementation in
subsys/bluetooth/common is part of every CONFIG_BT build, like the
packet helpers.

The helper is for HCI drivers that need to exchange HCI commands with
the controller over their own transport, typically for vendor-specific
controller initialization, without the Host's command machinery and
regardless of whether a Host is present in the build. It relies on the
HCI traffic being in lockstep during such initialization windows, so
it needs no opcode correlation state machine. Command flow control
still applies: the helper sends a command only once the controller
allows one, which every command response fed to it announces through
Num_HCI_Command_Packets, the NOP Command Complete included, so a
controller that allows no further command holds the next one until it
does.

The helper is shaped after what its users have: the transport send
function receives the HCI device, so a driver's own raw send function
serves as-is; received packets are fed as the bytes the driver holds
them in; a NULL command buffer stands for a command without parameters;
the command timeout, which covers both the wait for the controller to
allow the command and the wait for its response, defaults to the Host's
own 10 seconds; and every failure is logged with the opcode by the
helper, so that drivers need no error reporting of their own.

The helper's state is one byte under a short spinlock, with a single
semaphore that only signals a change of it, so an instance costs 40
bytes on Cortex-M, or 48 with CONFIG_POLL widening the semaphore.

Add a test suite for the lockstep behavior to the HCI packet helper
test, including feeding the helper from ISR context, the command flow
control and transport failures, and the documentation page and release
notes.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-18 11:15:45 +01:00
Johan Hedberg
d3157a6d6d Bluetooth: Host: Fix bt_addr_le_is_resolved() matching non-identity types
bt_addr_le_is_resolved() tests bit 1 of the HCI address type. That bit
is set for the two Controller-resolved identity types (0x02 and 0x03),
but also for the sentinels the LE Extended Advertising Report event
uses for an anonymous advertiser (0xff) and for a directed address the
Controller could not resolve (0xfe).

le_adv_recv() checks for a resolved address before it checks for an
anonymous advertiser, so since commit baad0c300f ("BluetootH: Host:
add helper functions for resolved addresses") replaced the explicit
identity-type comparison with the helper, the anonymous branch has been
unreachable: reports from anonymous advertisers were passed through
bt_addr_le_copy_resolved() and delivered to scan callbacks with an
address of type 0xfd instead of BT_ADDR_LE_ANY, contrary to the
bt_le_scan_recv_info documentation.

Compare against the two identity types explicitly and document that in
the helper's contract.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-17 16:47:23 -07:00
Zhijie Zhong
8b38527130 bluetooth: host: gatt: silently cancel CCC write on unsubscribe
Subscribing and unsubscribing to a notification are asynchronous,
callback-driven procedures. Each is a CCC write, i.e. one ATT request,
and a single bt_gatt_subscribe_params backs one such write at a time
(one value, one list node, one flags word). bt_gatt_subscribe() sends
the enable CCC write to the peer as an ATT transaction with the
30-second ATT timeout running; an unsubscribe reuses the same params
for the disable write.

With the subscribe CCC write still in flight (WRITE_PENDING set),
bt_gatt_unsubscribe() called bt_gatt_cancel() to abort it.
bt_gatt_cancel() runs the aborted request's completion synchronously,
so gatt_write_ccc_rsp() ran with BT_ATT_ERR_UNLIKELY: the subscribe
callback saw a spurious failure and the subscription was torn down
(NULL notification, node removed). bt_gatt_unsubscribe() then queued
the disable write on the same params, whose completion delivered the
subscribe and NULL callbacks a second time. One unsubscribe thus
produced a spurious subscribe failure and duplicate completions, with
the params and its list node reused while the first completion was
still unwinding.

Cancel the in-flight write silently instead. Factor the body of
bt_gatt_cancel() into a helper taking whether to run the completion,
and call it from bt_gatt_unsubscribe() without the completion, clearing
WRITE_PENDING in its place. The superseded write's response is then
ignored, the disable write is queued behind it exactly as before, and
the application sees one NULL notification, as for any other
unsubscribe. No new flag, and no locking beyond what bt_gatt_cancel()
already takes.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Zhijie Zhong <zhongzhijie1@xiaomi.com>
2026-09-17 16:42:54 -07:00
Zhijie Zhong
c8e360006a bluetooth: host: att: fix NULL channel request dereference
bt_att_find_req_by_user_data() walks every ATT channel on the
connection and dereferences chan->req unconditionally. A channel with
no request in flight has chan->req set to NULL, so the lookup faults as
soon as it visits such a channel. With EATT connected this is easy to
hit: the pending request sits on one channel while the others are idle.

The lookup is already reachable on this path today. bt_gatt_unsubscribe(),
called while a subscribe CCC write is still pending, uses bt_gatt_cancel()
to find that in-flight request; the traversal then dereferences an idle
channel's NULL req and crashes before any callback runs.

Guard the dereference with an explicit chan->req != NULL check. Also
reject a NULL user_data up front, which would otherwise match the first
request that happens to carry one. These are pre-existing defects with
no behaviour change, so they can be applied on their own.

Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Zhijie Zhong <zhongzhijie1@xiaomi.com>
2026-09-17 16:42:54 -07:00
Aric Radzin
e4b42116ae Bluetooth: Host: shell: Add extended scanner filter policy scan option
Add a --ext-filter-policy flag to the bt scan command, so the shell can
start a scanner that uses the extended scanner filter policy. Like
--coded, the flag is always accepted by the parser and the stack rejects
it when the required Kconfig or Controller support is missing.

Print the target address of a directed advertisement on its own line,
marking it as unresolved when the Controller could not resolve it. The
existing report line is left untouched, as it is reproduced verbatim in
the shell documentation.

Tested by building tests/bluetooth/shell on native_sim with and without
CONFIG_BT_SCAN_EXT_FILTER_POLICY. The shell command itself was not
exercised on a target.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Aric Radzin <aric.radzin@gmail.com>
2026-09-17 16:41:20 -07:00
Aric Radzin
ef3630ea64 Bluetooth: Controller: Mark unresolved TargetA in legacy ext adv reports
le_ext_adv_legacy_report() reports the target address of a directed
advertisement with the address type taken verbatim from the PDU, without
looking at node_rx->rx_ftr.direct. A directed advertisement that was
accepted only because the scanner uses an extended filter policy, and
whose target address the Controller was therefore unable to resolve, is
reported as an ordinary random address. The host cannot tell it apart
from a directed advertisement addressed to the local identity.

Report BT_ADDR_LE_UNRESOLVED for that case, as le_ext_adv_report()
already does through ext_adv_direct_addr_type(). This is the path taken
for legacy advertising PDUs received while scanning with the extended
scanning commands, so without it the extended scanner filter policies
only produce usable reports when the host uses the legacy scanning
commands.

The rx_ftr.direct field only exists when CONFIG_BT_CTLR_EXT_SCAN_FP is
enabled, so the new branch is compiled out otherwise and the reported
address type is unchanged there.

Tested by building tests/bluetooth/init on nrf52840dk/nrf52840 with
CONFIG_BT_CTLR_EXT_SCAN_FP both enabled and disabled. Not run on
hardware.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Aric Radzin <aric.radzin@gmail.com>
2026-09-17 16:41:20 -07:00
Aric Radzin
b98806c137 Bluetooth: Host: Add support for the LE Directed Advertising Report
The host had no support for the LE Directed Advertising Report event.
Its event code, structures and event mask bit were defined in
hci_types.h, but le_set_event_mask() never enabled the event and
meta_events[] had no handler for it, so a Controller was neither allowed
to generate it nor able to get it processed.

That event is how a Controller reports a directed advertisement whose
target address is a resolvable private address it was unable to resolve,
while scanning with one of the extended scanner filter policies. Without
it such an advertisement is invisible to the application, even though it
is the case the extended filter policies exist to surface.

Enable the event mask bit and add bt_hci_le_direct_adv_report(), which
turns each report into a bt_le_scan_recv_info and passes it to the
existing scan callbacks. The event mask bit is only set when the
Controller supports the Extended Scanner Filter Policies, as a Controller
may reject Set Event Mask with a bit it does not know.

Report the target address through the new bt_le_scan_recv_info.direct_addr
member. The extended advertising report carries the same address and it
was being discarded, so populate it from there as well. This gives one
representation for both scanning paths, which matters because the same
on-air advertisement is reported as an LE Directed Advertising Report
with the legacy scanning commands and as an LE Extended Advertising
Report with the extended ones. The address type is BT_ADDR_LE_UNRESOLVED
when the Controller could not resolve the target address. The legacy
event only reports an unresolved target address, and Core 6.3 Vol 4 Part
E 7.7.65.11 defines only BT_ADDR_LE_RANDOM for its Direct_Address_Type,
so normalise that address type to give the application one representation
across both report types.

le_adv_recv() drops directed reports when scanning explicitly without
privacy and without CONFIG_BT_SCAN_WITH_IDENTITY, to avoid reports for
the local identity while scanning with a non-resolvable private address.
An unresolved target address is by construction not the local identity,
so exclude that case from the rule. Without this the feature would be a
no-op in the default configuration. This cannot affect existing users:
per the specification a Controller only reports an unresolved target
address while using a filter policy that the host requests exclusively
for BT_LE_SCAN_OPT_EXT_FILTER_POLICY.

Tested by building tests/bluetooth/init with
CONFIG_BT_SCAN_EXT_FILTER_POLICY=y on qemu_cortex_m3 and
nrf52840dk/nrf52840, with CONFIG_BT_EXT_ADV both enabled and disabled,
and by confirming the existing configurations that set
CONFIG_BT_CTLR_EXT_SCAN_FP=n still build with the new symbol unavailable.

Fixes #114236

Assisted-by: Claude:claude-opus-5
Signed-off-by: Aric Radzin <aric.radzin@gmail.com>
2026-09-17 16:41:20 -07:00
Aric Radzin
3e5ff3a3e3 Bluetooth: Host: Add support for the extended scanner filter policy
The host has always requested one of the basic scanning filter policies,
BT_HCI_LE_SCAN_FP_BASIC_NO_FILTER or BT_HCI_LE_SCAN_FP_BASIC_FILTER, in
both start_le_scan_ext() and start_le_scan_legacy(). The extended
policies BT_HCI_LE_SCAN_FP_EXT_NO_FILTER and BT_HCI_LE_SCAN_FP_EXT_FILTER
were defined in hci_types.h but had no users, so there was no way for an
application to ask the Controller to report directed advertisements whose
target address is a resolvable private address it could not resolve.
Those advertisements were silently discarded by the Controller.

Add BT_LE_SCAN_OPT_EXT_FILTER_POLICY, which selects the extended variant
of whichever policy would otherwise be picked, so that all four policies
are now reachable. The option is gated by the new Kconfig symbol
BT_SCAN_EXT_FILTER_POLICY, which depends on the Controller advertising
support for the feature through BT_CTLR_EXT_SCAN_FP when the Controller
is built in tree.

Not every Link Layer supports the extended scanner filter policies, and a
Controller that does not shall reject the scan parameters with Invalid
HCI Command Parameters. Check LE feature bit 7 in bt_le_scan_start() and
return -ENOTSUP instead, so the application gets an unambiguous error
before any HCI command is sent. Bit 7 had no BT_FEAT_LE_*() accessor, so
add BT_FEAT_LE_EXT_SCAN() next to the other feature test macros.

Consuming the resulting reports is handled separately. Applications that
do not set the new option are unaffected: the option bit is rejected by
valid_le_scan_param() unless the Kconfig symbol is enabled, and the
filter policy sent to the Controller is unchanged otherwise.

Both scan start paths now select the filter policy through one helper,
which gates the filter accept list on BT_FILTER_ACCEPT_LIST. The legacy
path already did so, the extended one did not. This only changes the
extended path when BT_FILTER_ACCEPT_LIST is disabled and the application
sets BT_LE_SCAN_OPT_FILTER_ACCEPT_LIST anyway: it now asks for no
filtering instead of filtering against the accept list. Nothing can add
entries to that list in this configuration, as
bt_le_filter_accept_list_add() is compiled out, so the scanner used to
report nothing at all.

Tested by building tests/bluetooth/init with the new
prj_scan_ext_fp.conf on qemu_cortex_m3 and nrf52840dk/nrf52840, and by
checking that CONFIG_BT_SCAN_EXT_FILTER_POLICY resolves against
CONFIG_BT_CTLR_EXT_SCAN_FP on the latter.

Fixes #114103

Assisted-by: Claude:claude-opus-5
Signed-off-by: Aric Radzin <aric.radzin@gmail.com>
2026-09-17 16:41:20 -07:00
Keith Packard
9e92a8eb21 subsys/bluetooth: Cast OTS offset from off_t to long when logging
Log formats were using %ld, assuming that off_t was equivalent to
long. Place an explicit cast in the code so that when off_t is wider, the
value is still displayed correctly.

Signed-off-by: Keith Packard <keithp@keithp.com>
2026-09-17 12:12:05 +01:00
Johan Hedberg
8b4af37731 Bluetooth: Classic: AVRCP: Order static functions before their callers
cleanup_fragmentation_context() was forward-declared although nothing
forces its definition below avrcp_disconnected(): move it above its
first caller and drop the declaration. No functional change.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-16 21:34:58 +01:00
Vinayak Kariappa Chettimada
c91fb6ead2 Bluetooth: Controller: Fix flash sync unsigned underflow
Fix flash sync implementation unsigned underflow that caused
timeout with Bluetooth and settings enabled.

Fixed by using the Controller's EVENT_OVERHEAD_START_US CPU
overhead timing as the minimum interval between flash
operation in co-operation with Bluetooth tickers.

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
2026-09-16 16:26:01 +01:00
Emil Gydesen
bcb44b68f8 Bluetooth: CAP: Move subproc_initiated assignment under lock
Modifications to `active_proc` shall be done while it is
locked.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-16 16:20:40 +01:00
Kai Cheng
e81355223f bluetooth: classic: shell: include the Report ID in Boot Protocol reports
The mouse report the hid_device shell sends on the Interrupt channel left
the Report ID out in Boot Protocol Mode, while the GET_REPORT response of
the same shell always included it. HID spec v1.1.2 Section 3.3.2 puts both
under one rule: every report a Device sends in Boot Protocol Mode, either
asynchronously on the Interrupt channel or in answer to GET_REPORT, has to
include the Report ID. A Host that follows this strips the first byte, so
what it read as the button bits was the Report ID and the whole report was
off by one.

Send it in both modes, which leaves the wheel byte as the only difference
between them and gives the two paths the same shape. The value does not
change: Boot Protocol Mode fixes the mouse Report ID to 2, and the
descriptor this shell registers declares 2 as well.

Signed-off-by: Kai Cheng <chengkai@xiaomi.com>
2026-09-16 09:23:53 +02:00
Emil Gydesen
503e06521b Bluetooth: BAP: SD: Re-request broadcast code after bad code
When a broadcast sink fails to decrypt a BIG due to a bad broadcast
code, the BASS receive state is left at Bad_Code. If the broadcast
assistant subsequently sends a Modify Source operation requesting BIS
sync again before providing a new broadcast code, the scan delegator
did not notify Broadcast_Code_Required again, leaving the assistant
with no indication that a new code is expected.

Reset the receive state back to Broadcast_Code_Required as soon as the
Modify Source operation requesting a BIS sync is received, matching
the BASS requirement to request a Broadcast_Code whenever the server
does not have a key to decrypt a BIS it is trying to sync to. This is
done directly in the scan delegator when the request is received,
rather than depending on the application calling
bt_bap_broadcast_sink_sync() again.

Extend the bsim BAP broadcast sink/assistant tests to cover the
retry-after-bad-code scenario: the assistant requests BIS sync again
before sending the correct code, and the test verifies the receive
state changes back to Broadcast_Code_Required instead of staying at
Bad_Code.

Tested by running the extended
bap_broadcast_audio_assistant_incorrect_code bsim test, which fails
without the fix (times out waiting for the repeated Broadcast_Code
notification) and passes with it. Also ran
bap_broadcast_audio_assistant, bap_broadcast_audio_encrypted and
bap_broadcast_audio_encrypted_incorrect_code to confirm no
regressions. Not built or tested on hardware.

Fixes #111790

Assisted-by: Copilot:claude-opus-5
Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-16 07:39:34 +02:00
Emil Gydesen
81e9d94d00 Bluetooth: ISO: Add send_failed callback
Add callback to handle cases where an accepted send fails due to a
disconnect or a call to bt_disable.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-16 07:31:55 +02:00
Johan Hedberg
7e9d9d41ee Bluetooth: Host: Reject RPA-directed advertising the peer cannot receive
A directed advertisement with an RPA target address can only be
received by a peer that supports address resolution: an initiator
responds only to a directed advertisement that carries its own device
address (Core 6.3, Vol 6, Part B, 4.4.4.1), and an RPA target address
is only recognized as its own if the initiator resolves it with its
local IRK (Vol 6, Part B, 4.7 and 6.4). Starting such advertising
towards a peer known not to support address resolution can therefore
never lead to a connection.

Fail bt_le_adv_start(), bt_le_ext_adv_create() and
bt_le_ext_adv_update_param() with -ENOTSUP when
BT_LE_ADV_OPT_DIR_ADDR_RPA is used towards a bonded peer whose stored
Central Address Resolution value says address resolution is not
supported. An unknown value keeps the previous behaviour, as
recommended by Core 6.3, Vol 3, Part C, 12.4. This also aligns with
GAP, which allows a privacy-enabled Peripheral to use directed
connectable mode only when the peer supports address resolution in
the Controller (Vol 3, Part C, 10.7.1.1).

Fixes: #101955

Assisted-by: Claude:claude-fable-5
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-16 07:29:35 +02:00
Johan Hedberg
aa0bad8767 Bluetooth: GATT: Track the peer's Central Address Resolution support
The GAP section of the Core specification recommends that a Peripheral
checks whether its peer supports address resolution, by reading the
Central Address Resolution characteristic, before using directed
advertising with an RPA target address. The host has so far left both
the read and the conclusion entirely to the application.

Read the characteristic once when a bond is created, following the
same pattern as the other automatic GATT client operations, and store
the result in the bond. An absent characteristic means address
resolution is not supported (Core 6.3, Vol 3, Part C, 12.4). The read
is also triggered on an encrypted reconnection when the answer is
missing, which covers bonds created before this option existed. Errors
other than Attribute Not Found leave the answer unknown, to be read
again on the next connection.

The stored answer is available to the application through the new
bt_le_bond_addr_res_support(), so that it can take the peer's ability
into account when choosing how to advertise. The completion of the
read is reported through a new bt_conn_auth_info_cb callback, so that
the application knows when the answer is available.

The stored keys format is unchanged: the answer lives in two
previously unused bits of the flags field, so existing bonds load as
having an unknown value.

Part of fixing #101955

Assisted-by: Claude:claude-fable-5
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-16 07:29:35 +02:00
Lyle Zhu
54f07618dc bluetooth: classic: sdp: remove K_FOREVER from service buf allocation
Add a new function `sdp_svc_create_pdu()` that uses `K_NO_WAIT`
timeout to allocate the net buffer for SDP service role.

Add response buffer management to struct bt_sdp with `rsp_buf` field
to hold pre-allocated response buffers.

Call the function `sdp_svc_create_pdu()` to allocate response buffer
and save to `sdp->rsp_buf` in the function `bt_sdp_recv()`. And use
function `net_buf_take()` to take the pre-allocated buffer in the
functions `sdp_svc_search_req()`, `sdp_svc_att_req()`,
`sdp_svc_search_att_req()`, and `send_err_rsp()`.

Add receive buffer tracking with `recv_pending` and `recv_state` fields
to save incoming requests when response buffers are unavailable.

Implement `sdp_svc_work_handler()` to retry processing pending SDP
requests when net buffers become available.

Update `sdp_destroy()` callback to trigger retry of SDP service
processing by submitting sdp_svc_worker when buffers are freed.

Add cleanup of response and pending buffers in `bt_sdp_disconnected()`
to prevent memory leaks.

The changes prevent the `bt_workq` from blocking when there are no
available net buffers in `sdp_pool` for SDP service role.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-16 05:53:25 +02:00
Lyle Zhu
c43dcb2656 bluetooth: classic: sdp: remove K_FOREVER from client buf allocation
Add a new function sdp_client_create_pdu() that uses `K_NO_WAIT`
timeout to allocate the net buffer for SDP Client role.

Add error handling for buffer allocation failures in service search,
attribute search, and service attribute search functions.

Introduce a flag `SDP_CLIENT_FLAG_WAIT_FOR_TX_BUF` to track when client
is waiting for TX buffer availability.

Add retry logic in `sdp_client_work_handler()` to resume SDP record
discovery if the flag `SDP_CLIENT_FLAG_WAIT_FOR_TX_BUF` of the client
has been set.

Implement sdp_destroy() callback for the net pool `sdp_pool` to
trigger retry of SDP discovery when any net buffers become available.

Also, clear client flags during request cleanup.

The changes prevent the `bt_workq` from blocking when there are no
available net buffers in `sdp_pool`.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-16 05:53:25 +02:00
Kai Cheng
8c398ec652 bluetooth: classic: shell: add HID Host commands
Add hid_host shell commands to exercise the HID Host profile:

- register / unregister
- sdp_discover
- connect / disconnect / select <address>
- get_report <type> <id> <buf_size>
- set_report <type> <hex...>
- get_protocol / set_protocol <boot|report>
- send <hex...> (output report on the interrupt channel)
- suspend / exit_suspend
- vcu (virtual cable unplug)

The profile does not read the HID service record, so sdp_discover does it
here: it reads the report descriptor and the device attributes and walks
the descriptor for a Report ID Global item. The Report ID is then split off
incoming reports and added to GET_REPORT requests, which is the work an
application has to do (HID spec v1.1.2 Section 3.1.2.3 and Section 3.3.1).
Only the descriptor is required; the remaining attributes are optional in
the record, so a missing one is reported and the rest is still read.

The profile associates with one HID Device per ACL connection, so the state
the shell keeps for a peer is indexed by connection index as well: the
report descriptor outcome, the protocol mode, the pending unplug and the
SDP discovery parameters. Sharing one set would mix up a keyboard that
declares Report IDs with a mouse that does not, destroy the bonding of the
wrong peer, or put the same SDP request node on two lists. The connected
callback points the selected instance at whatever associated last, so
select reaches the other one while both are up.

A Virtual Cable Unplug destroys the bonding as required by Section
3.1.2.2.3, deferred until the channels have closed so that the ACL is not
dropped from under them.

Signed-off-by: Kai Cheng <chengkai@xiaomi.com>
2026-09-16 05:50:36 +02:00
Kai Cheng
f7858e15e0 bluetooth: HID: add HID Host profile support
Add the HID Host side of the Bluetooth Classic HID profile, letting a
Zephyr device drive HID peripherals such as keyboards, mice and gamepads,
as defined in HID spec v1.1.2.

The profile owns the HIDP framing and the association:

- host-initiated connection, opening the control channel first and the
  interrupt channel once it is up (Section 5.2.2)
- device-initiated connection through L2CAP servers on both PSMs
- GET_REPORT / SET_REPORT and GET_PROTOCOL / SET_PROTOCOL on the control
  channel, with a single outstanding transaction (Section 3.2.1)
- output reports sent and input reports received on the interrupt channel
- SUSPEND / EXIT_SUSPEND and Virtual Cable Unplug in both directions
- connection pool indexed by ACL connection index

Reading the HID service record is left to the application. Both PSMs are
fixed, so the profile needs nothing from the record to open the channels,
and report payloads are handed over as received: whether one starts with a
Report ID follows from the report descriptor and from the protocol mode,
which only the application knows (Section 3.1.2.3 and Section 3.3.1).
This matches the HID Device side, which does not store a descriptor
either, and follows MAP and PBAP, where SDP discovery lives in the
application.

Both channels request an MTU of BT_L2CAP_RX_MTU as required by Section
5.2.3.1, the interrupt channel is closed before the control channel per
Section 5.2.2, and a control transaction that is never answered is bounded
by a timer so the association cannot stay blocked (Section 5.2.6).

Signed-off-by: Kai Cheng <chengkai@xiaomi.com>
2026-09-16 05:50:36 +02:00
Kai Cheng
77c2c27df2 bluetooth: classic: HID: share the protocol values between both roles
Move the HID protocol values an application needs into a new
<zephyr/bluetooth/classic/hid.h> and include it from hid_device.h, so a
second role can reuse them instead of defining its own copy.

Protocol mode, report type and HANDSHAKE result codes are carried by the
same HIDP messages in both directions, so they belong to neither role.
Keeping a copy per role means two public headers cannot be included in the
same translation unit unless every replacement list stays textually
identical, which is not something a compiler warns about until it breaks.

The internal copies go away with it: hid_internal.h loses the HANDSHAKE
result codes it repeated from the public header and BT_HID_PAR_REP_TYPE_*,
which named the same report types a second time. Message types, HID_CONTROL
operations and the header field masks stay internal since they are HIDP
framing that no application has to know about.

This follows the layout other stacks use for the same reason: BTstack keeps
btstack_hid.h next to hid_host.h and hid_device.h, and Fluoride keeps
hiddefs.h next to hidh_api.h and hidd_api.h.

Signed-off-by: Kai Cheng <chengkai@xiaomi.com>
2026-09-16 05:50:36 +02:00
Pisit Sawangvonganan
5febb0aa90 Bluetooth: classic: shell: avoid recomputing strlen() in loops
Optimize HFP command handlers where strlen(action) was unnecessarily
recomputed on every loop iteration even though the string argument
never changed within the loop.

The affected functions:
- cmd_ag_vre_state(): strlen(action) recomputed in action parsing
- cmd_ag_vre_text(): strlen(action) recomputed in action parsing

Store the string length in a local variable before the loop and reuse it
throughout, eliminating redundant strlen() calls.

Assisted-by: Claude:Haiku 4.5
Signed-off-by: Pisit Sawangvonganan <pisit@ndrsolution.com>
2026-09-16 05:43:25 +02:00
Johan Hedberg
36374b133d Bluetooth: Host: Drop the forward declaration of get_pending_per_adv_sync()
The function is defined before its first use, so the declaration
serves no purpose. No functional change.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-15 07:15:07 -04:00
Johan Hedberg
a6ef755158 Bluetooth: Host: CS: Order clear_on_disconnect() before its use
clear_on_disconnect() is only referenced by the connection callback
table: define the table after the function and drop the forward
declaration. No functional change.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-15 07:14:57 -04:00
Johan Hedberg
61e0fc777a Bluetooth: Host: Order the ECC work handlers before their work items
generate_pub_key() and generate_dh_key() are only referenced by the
work items defined with K_WORK_DEFINE(): define the work items after
the handlers and drop the forward declarations. No functional change.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-15 07:14:49 -04:00
Johan Hedberg
dc7d7634af Bluetooth: Host: Drop redundant forward declarations in direction.c
The six declared functions are all defined before their first use, so
the declarations serve no purpose. No functional change.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-15 07:14:37 -04:00
Johan Hedberg
c8c2438f05 Bluetooth: Classic: AVRCP: Do not wait for browsing RX buffers
The buffer callback of the browsing channel allocates with K_FOREVER.
L2CAP calls it from the Bluetooth RX workqueue, both for a new SDU and
for every further fragment appended to it, and releases the buffers on
that same workqueue once the SDU has been delivered. The pool is
shared by all browsing channels and one SDU can take several buffers,
so once it runs dry the wait can never be satisfied and stops all HCI
receive processing instead.

Allocate without waiting. L2CAP handles a failed allocation on this
channel by disconnecting it, which the application can recover from,
unlike a blocked host.

Compile-tested only; there is no AVRCP browsing test coverage in the
tree.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-14 08:42:57 -04:00
Johan Hedberg
8529242080 Bluetooth: AVCTP: Do not block the workqueue on reassembly allocation
The reassembly buffer for a fragmented AVCTP message is allocated with
K_FOREVER from the receive path, which runs in the Bluetooth RX
workqueue. That workqueue is also the only context that releases
reassembly buffers, once the reassembled message has been dispatched,
so a wait there can never be satisfied and would stop all HCI receive
processing instead. The in-tree pool sizing, one buffer per session,
keeps the wait from triggering today, and the failure path behind it
returned -ENOMEM, which makes L2CAP tear down the channel, unlike
every other failure in this function, which drops the fragment and
keeps the channel up.

Allocate without waiting and treat a failed allocation like the other
reassembly failures: drop the fragment and let the peer time out.

Compile-tested only; there is no AVCTP test coverage in the tree.

Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-14 08:42:57 -04:00
Jasen Liu
d0e3e07de4 bluetooth: audio: guard PA sync FILTER_DUPLICATE with ADI feature
The bap_broadcast_sink sample was previously fixed to only set
BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE when the controller supports
the Periodic Advertising ADI Support feature. Several other broadcast
sink/assistant code paths still set this option unconditionally.

BT_LE_PER_ADV_SYNC_OPT_FILTER_DUPLICATE requires the controller to
support the Periodic Advertising ADI Support feature. On controllers
that do not support it (e.g. external HCI controllers such as NXP
IW612), bt_le_per_adv_sync_create() rejects the option and returns
-ENOTSUP, so the periodic advertising sync is never created.

Apply the same fix as bap_broadcast_sink to the remaining paths:
check BT_FEAT_LE_PER_ADV_ADI_SUPP via bt_le_get_local_features() and
fall back to BT_LE_PER_ADV_SYNC_OPT_NONE when the feature is not
supported.

Updated PA sync creation paths:
- cap_acceptor: pa_sync_without_past() - PA sync when PAST is not used
- cap_acceptor: scan_check_and_sync_broadcast() - PA sync from scan
- shell/bap: pa_sync_broadcast_sink() - PA sync from scan results
- shell/bap_scan_delegator: pa_sync_no_past() - PA sync without PAST

Signed-off-by: Jasen Liu <jasen.liu@nxp.com>
2026-09-14 08:42:48 -04:00
Emil Gydesen
1254bf165a Bluetooth: CAP: add subprocedure callbacks for unicast start and stop
The CAP unicast audio start and stop procedures consist of several
subprocedures, and applications may need to know when each of these have
completed, e.g. to reconfigure the unicast group after the streams have
been codec configured but before they are QoS configured.

Add new callbacks to bt_cap_initiator_cb for each subprocedure of the
unicast audio start and unicast audio stop procedures. The callbacks are
only called if at least one request was sent to a CAP acceptor as part of
the subprocedure, so that subprocedures that are skipped because all
streams are already in the requested state do not trigger a callback. To
support this a new subproc_initiated field has been added to the common
procedure struct, which is reset whenever a new subprocedure is set.

The unit tests have been extended to verify both that the callbacks are
called when the subprocedures are performed, and that they are not called
when the subprocedures are skipped, and the BSIM CAP initiator unicast
test now waits for the new callbacks as part of the start and stop
procedures.

Assisted-by: Copilot:claude-opus-4.5

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-14 08:42:10 -04:00
Emil Gydesen
b01df6e903 Bluetooth: ASCS: Refactor ASCS to use sys_wq for cp_rsp
Refactor ASCS to use the system workqueue for the control
point notification. This is to ensure that we always send
the notification, even if we may be out of ATT buffers at
any point, but also ensures that the control point notification
is sent before any ASE notifications triggered by that control
point write.

Some refactoring was done to account for the changes when cleaning
up the ASEs.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-12 07:52:56 -04:00
Emil Gydesen
92c6927cb1 Bluetooth: Host: Set BT_GAP_SID_INVALID for legacy adv
The SID is only used for extended advertising sets. For any
legacy advertising sets, we now set the adv->sid to
BT_GAP_SID_INVALID. This is similar to how the SID for
legacy scan reports are handled in scan.c

bt_le_ext_adv_update_param could also have a new SID for the
advertising set that was never stored in adv->sid. This has
also been fixed in this commit.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
2026-09-11 17:34:52 +02:00
Johan Hedberg
e9c3252c24 Bluetooth: Host: Validate periodic advertising report length
The periodic advertising report handler checks that the event header is
present and that the data fits in the reassembly buffer, but not that
the event actually carries as many data bytes as its length field
claims. With CONFIG_BT_PER_ADV_SYNC_BUF_SIZE > 0, a report with a
length field larger than the remaining event payload makes
net_buf_simple_add_mem() copy up to 255 bytes from beyond the end of
the HCI event buffer into the reassembly buffer, and the result is
passed on to the application.

Drop such reports the same way as reports that do not fit in the
reassembly buffer.

Fixes #117587

Assisted-by: Claude:claude-fable-5
Signed-off-by: Johan Hedberg <johan.hedberg@silabs.com>
2026-09-11 17:26:33 +02:00
Lyle Zhu
35fcd3947f bluetooth: classic: l2cap: Fix data drop waiting for config rsp sent
In commit 15bb270 (bluetooth: classic: l2cap: Fix race condition in
config rsp handling), the channel transitions from the `CONFIG` state
to the `CONNECTED` state of L2CAP channel will be set after the
configuration response frame has been sent via the sent callback
`l2cap_br_config_rsp_sent_cb()`. The sent callback will be called by
`hci_num_completed_packets()` when the HCI event
`NUM_COMPLETED_PACKETS` is notified. However, the time duration
between the completion of the configuration response transmission
and the arrival of the `NUM_COMPLETED_PACKETS` HCI event is
indeterminate; it depends entirely on the controller.

From the peer's perspective, the L2CAP channel is successfully
established once the configuration response is received, whereas the
local side remains in the `CONFIG` state until the
`NUM_COMPLETED_PACKETS` event arrives. Consequently, it is possible
for the peer to transmit data over the established channel before the
`NUM_COMPLETED_PACKETS` event arrives. Since the local side is still
in the `CONFIG` state, this data will be discarded.

Add `L2CAP_FLAG_CONF_RSP_SENDING` flag, set before queuing the
configuration response and cleared in the sent callback. In the
receive path, attempt to complete the configuration state when this
flag is set, so that data arriving before the HCI
`NUM_COMPLETED_PACKETS` event does not get silently discarded.

The change aims to prevent the dropping of received data while waiting
for the HCI event `NUM_COMPLETED_PACKETS`.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-11 05:24:55 -04:00
Lyle Zhu
a8707cca92 bluetooth: classic: rfcomm: add retry timeout mechanism
Add a retry timeout mechanism to prevent indefinite retry attempts
when buffer allocation failures persist, ensuring the RFCOMM state
machine eventually fails gracefully instead of retrying forever.

Introduce per-DLC `retry_timeout_work` and per-session
`retry_timeout_work` timers (5 seconds) that run in parallel with
the randomized retry mechanism. When a retry timeout expires, trigger
the RTX timeout handler to tear down the connection.

Add `rfcomm_dlc_retry_timeout()` and `rfcomm_session_retry_timeout()`
handlers that reschedule the RTX work with `K_NO_WAIT` to initiate
immediate disconnect when retry attempts exceed the timeout threshold.

Schedule retry timeout when setting retry flags for:
- DLC operations: PN_REQ, PN_RSP, UA_FOR_SABM, DM, UA_FOR_DISC,
  MSC_FC_CLEAR_REQ
- Session operations: FC_ON_REQ, FC_ON_RSP, FC_OFF_RSP, UA_FOR_DISC

Cancel retry timeout when operations succeed to prevent spurious
disconnects after successful recovery.

Rename `RFCOMM_RETRY_TIMEOUT` to `RFCOMM_RETRY_INTERVAL` and introduce
new `RFCOMM_RETRY_TIMEOUT` constant (5 seconds) to distinguish between
the randomized retry interval (0-100ms) and the overall retry timeout
threshold.

Update all retry scheduling calls to use `RFCOMM_RETRY_INTERVAL` for
the randomized delay and `RFCOMM_RETRY_TIMEOUT` for the timeout timer.

Cancel retry timeout work in cleanup paths:
- `rfcomm_dlc_destroy()` for DLC cleanup
- `rfcomm_disconnected()` for session cleanup

Add early return in `rfcomm_session_retry_timeout()` when the
underlying connection is already gone to handle race conditions
during cleanup.

This change complements the randomized retry mechanism by adding an
upper bound on retry duration, preventing resource exhaustion and
ensuring predictable failure behavior when buffer allocation issues
cannot be resolved within a reasonable timeframe.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-11 05:24:36 -04:00
Lyle Zhu
082367e49b bluetooth: classic: rfcomm: add randomized retry mechanism
Refactor RFCOMM retry mechanism to improve robustness and prevent
thundering herd issues when multiple operations fail simultaneously
due to buffer exhaustion.

Add the session-level `retry_work` supports randomized retry delays
for session-scoped operations. Convert DLC `tx_work` from `k_work`
to `k_work_delayable` to support randomized retry delays for
DLC-scoped operations.

Since the FCON and FCOFF apply to all DLCS, they should be handled at
the session-scoped rather the DLC-level. Move their handling to the
session-level `retry_work` instead of handling them per-DLC
`retry_work`. Besides, UA for DISC of DLCI0 also handled by the
session-level `retry_work`.

Introduce randomized retry timeout (0-100ms) using `sys_rand32_get()`
to prevent synchronized retries when multiple DLCs or sessions
experience buffer allocation failures concurrently.

Add session-level retry handler `rfcomm_session_retry_handler()` to
process deferred session operations (FC_ON_REQ, FC_ON_RSP, FC_OFF_RSP,
UA_FOR_SABM, UA_FOR_DISC, DISCONN_REQ) with proper flag-based state
tracking.

Add DLC-level retry handler `rfcomm_dlc_retry()` within the tx_worker
to process deferred DLC operations (PN_REQ, PN_RSP, UA_FOR_SABM, DM)
before entering the connected state. And add DLC-level retry handler
`rfcomm_dlc_reprocess_disc()` within the tx_worker to process
deferred DLC operations (UA_FOR_DISC).

Extract send operations into dedicated functions with comprehensive
error handling:
- `rfcomm_dlc_send_pn_req()` / `rfcomm_dlc_send_pn_rsp()` for PN
- `rfcomm_dlc_send_ua_for_sabm()` for SABM response
- `rfcomm_dlc_send_dm()` for security rejection
- `rfcomm_session_send_fc_on_req()` / `rfcomm_session_send_fc_on_rsp()`
- `rfcomm_session_send_fc_off_rsp()` for flow control
- `rfcomm_session_send_ua_for_disc()` for session disconnect
- `rfcomm_session_send_ua_for_sabm()` for session connect
- `rfcomm_session_disconn_req()` for session L2CAP disconnect

Add `rfcomm_dlc_process_disc()` and `rfcomm_dlc_reprocess_disc()` to
handle DISC processing with retry support for UA response failures.

Extract `rfcomm_session_control_channel_connected()` to centralize
DLC startup logic after session establishment.

Add `BT_RFCOMM_STATE_SECURITY_FAILED` state to properly track security
rejection scenarios and enable deferred DM sending.

Reorder states in the enum to group security-related states together
before CONFIG state.

Cancel session retry_work in `rfcomm_disconnected()` cleanup path.

These changes complement the previous buffer allocation error handling
by distributing retry attempts over time, reducing contention on the
buffer pool and improving overall system stability under load.

Signed-off-by: Lyle Zhu <lyle.zhu@nxp.com>
2026-09-11 05:24:36 -04:00