* [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: Sergey Senozhatsky @ 2026-06-09 12:10 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Mark-yw Chen, Sean Wang
Cc: Tomasz Figa, linux-bluetooth, linux-kernel, linux-arm-kernel,
linux-mediatek, Sergey Senozhatsky, stable
Every once in a while we see a hung btmtksdio_flush() task:
INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
__cancel_work_timer+0x3f4/0x460
cancel_work_sync+0x1c/0x2c
btmtksdio_flush+0x2c/0x40
hci_dev_open_sync+0x10c4/0x2190
[..]
It all boils down to incorrect time_is_before_jiffies() usage in
btmtksdio_txrx_work(). The btmtksdio_txrx_work() loop is expected
to be terminated if running for longer than 5*HZ. However the
timeout check is twisted: time_is_before_jiffies(old_jiffies + 5*HZ)
evaluates to true when old_jiffies + 5*HZ is in the past i.e. when a
timeout has occurred. Using OR with time_is_before_jiffies(txrx_timeout)
means that:
- before the 5-second timeout: the condition is `int_status || false`,
so it loops as long as there are pending interrupts.
- after the 5-second timeout: the condition becomes `int_status || true`,
which is always true.
When the loop becomes infinite btmtksdio_txrx_work() loop never
terminates and never releases the SDIO host.
Fix loop termination condition to actually enforce a 5*HZ timeout.
Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work")
Cc: stable@vger.kernel.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
drivers/bluetooth/btmtksdio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 5b0fab7b89b5..c6f80c419e90 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -620,7 +620,7 @@ static void btmtksdio_txrx_work(struct work_struct *work)
if (btmtksdio_rx_packet(bdev, rx_size) < 0)
bdev->hdev->stat.err_rx++;
}
- } while (int_status || time_is_before_jiffies(txrx_timeout));
+ } while (int_status && time_is_after_jiffies(txrx_timeout));
/* Enable interrupt */
if (bdev->func->irq_handler)
--
2.54.0.1064.gd145956f57-goog
^ permalink raw reply related
* [Kernel Bug] possible deadlock in vhci_send_frame
From: Longxing Li @ 2026-06-09 11:59 UTC (permalink / raw)
To: syzkaller, marcel, Luiz Augusto von Dentz, linux-bluetooth,
linux-kernel
Dear Linux kernel developers and maintainers,
We would like to report a new kernel bug found by our tool. possible
deadlock in vhci_send_frame. Details are as follows.
Kernel commit: v5.15.189
Kernel config: see attachment
report: see attachment
C repro and Syz repro: see attachment
We are currently analyzing the root cause. We will provide further
updates in this thread as soon as we have more information.
Best regards,
Longxing Li
======================================================
WARNING: possible circular locking dependency detected
5.15.189 #1 Not tainted
------------------------------------------------------
kworker/u3:2/7652 is trying to acquire lock:
ffff88801c5ea518 (&data->open_mutex){+.+.}-{3:3}, at:
vhci_send_frame+0xb0/0x120 drivers/bluetooth/hci_vhci.c:71
but task is already holding lock:
ffffc90001effda0 ((work_completion)(&hdev->cmd_work)){+.+.}-{0:0}, at:
process_one_work+0x8f5/0x1530 kernel/workqueue.c:2285
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #3 ((work_completion)(&hdev->cmd_work)){+.+.}-{0:0}:
__flush_work+0x12d/0xc10 kernel/workqueue.c:3090
hci_dev_do_open+0xab6/0x1b40 net/bluetooth/hci_core.c:1626
hci_power_on+0x133/0x680 net/bluetooth/hci_core.c:2265
process_one_work+0x9db/0x1530 kernel/workqueue.c:2310
worker_thread+0x686/0x1180 kernel/workqueue.c:2457
kthread+0x3d0/0x4c0 kernel/kthread.c:334
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:287
-> #2 (&hdev->req_lock){+.+.}-{3:3}:
__mutex_lock_common kernel/locking/mutex.c:596 [inline]
__mutex_lock+0x183/0x1300 kernel/locking/mutex.c:729
hci_dev_do_close+0x5d/0x1250 net/bluetooth/hci_core.c:1737
hci_rfkill_set_block+0x1be/0x210 net/bluetooth/hci_core.c:2235
rfkill_set_block+0x200/0x550 net/rfkill/core.c:345
rfkill_sync_work+0x8a/0xc0 net/rfkill/core.c:1030
process_one_work+0x9db/0x1530 kernel/workqueue.c:2310
worker_thread+0x686/0x1180 kernel/workqueue.c:2457
kthread+0x3d0/0x4c0 kernel/kthread.c:334
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:287
-> #1 (rfkill_global_mutex){+.+.}-{3:3}:
__mutex_lock_common kernel/locking/mutex.c:596 [inline]
__mutex_lock+0x183/0x1300 kernel/locking/mutex.c:729
rfkill_register+0x36/0xb00 net/rfkill/core.c:1045
hci_register_dev+0x440/0xd90 net/bluetooth/hci_core.c:3960
__vhci_create_device+0x2c8/0x5e0 drivers/bluetooth/hci_vhci.c:129
vhci_create_device drivers/bluetooth/hci_vhci.c:153 [inline]
vhci_get_user drivers/bluetooth/hci_vhci.c:210 [inline]
vhci_write+0x2c0/0x460 drivers/bluetooth/hci_vhci.c:290
call_write_iter include/linux/fs.h:2172 [inline]
new_sync_write+0x4b2/0x680 fs/read_write.c:507
vfs_write+0x7d9/0xb40 fs/read_write.c:594
ksys_write+0x13a/0x260 fs/read_write.c:647
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x34/0x80 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x66/0xd0
-> #0 (&data->open_mutex){+.+.}-{3:3}:
check_prev_add kernel/locking/lockdep.c:3053 [inline]
check_prevs_add kernel/locking/lockdep.c:3172 [inline]
validate_chain kernel/locking/lockdep.c:3788 [inline]
__lock_acquire+0x293e/0x5590 kernel/locking/lockdep.c:5012
lock_acquire kernel/locking/lockdep.c:5623 [inline]
lock_acquire+0x1a8/0x4c0 kernel/locking/lockdep.c:5588
__mutex_lock_common kernel/locking/mutex.c:596 [inline]
__mutex_lock+0x183/0x1300 kernel/locking/mutex.c:729
vhci_send_frame+0xb0/0x120 drivers/bluetooth/hci_vhci.c:71
hci_send_frame+0x1c0/0x400 net/bluetooth/hci_core.c:4256
hci_cmd_work+0x223/0x3b0 net/bluetooth/hci_core.c:5201
process_one_work+0x9db/0x1530 kernel/workqueue.c:2310
worker_thread+0x686/0x1180 kernel/workqueue.c:2457
kthread+0x3d0/0x4c0 kernel/kthread.c:334
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:287
other info that might help us debug this:
Chain exists of:
&data->open_mutex --> &hdev->req_lock --> (work_completion)(&hdev->cmd_work)
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock((work_completion)(&hdev->cmd_work));
lock(&hdev->req_lock);
lock((work_completion)(&hdev->cmd_work));
lock(&data->open_mutex);
*** DEADLOCK ***
2 locks held by kworker/u3:2/7652:
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
arch_atomic64_set arch/x86/include/asm/atomic64_64.h:34 [inline]
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
arch_atomic_long_set include/linux/atomic/atomic-long.h:41 [inline]
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
atomic_long_set include/linux/atomic/atomic-instrumented.h:1198
[inline]
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
set_work_data kernel/workqueue.c:635 [inline]
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
set_work_pool_and_clear_pending kernel/workqueue.c:662 [inline]
#0: ffff888057be9938 ((wq_completion)hci0#3){+.+.}-{0:0}, at:
process_one_work+0x8c1/0x1530 kernel/workqueue.c:2281
#1: ffffc90001effda0
((work_completion)(&hdev->cmd_work)){+.+.}-{0:0}, at:
process_one_work+0x8f5/0x1530 kernel/workqueue.c:2285
stack backtrace:
CPU: 0 PID: 7652 Comm: kworker/u3:2 Not tainted 5.15.189 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Workqueue: hci0 hci_cmd_work
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0xfc/0x174 lib/dump_stack.c:106
check_noncircular+0x277/0x310 kernel/locking/lockdep.c:2133
check_prev_add kernel/locking/lockdep.c:3053 [inline]
check_prevs_add kernel/locking/lockdep.c:3172 [inline]
validate_chain kernel/locking/lockdep.c:3788 [inline]
__lock_acquire+0x293e/0x5590 kernel/locking/lockdep.c:5012
lock_acquire kernel/locking/lockdep.c:5623 [inline]
lock_acquire+0x1a8/0x4c0 kernel/locking/lockdep.c:5588
__mutex_lock_common kernel/locking/mutex.c:596 [inline]
__mutex_lock+0x183/0x1300 kernel/locking/mutex.c:729
vhci_send_frame+0xb0/0x120 drivers/bluetooth/hci_vhci.c:71
hci_send_frame+0x1c0/0x400 net/bluetooth/hci_core.c:4256
hci_cmd_work+0x223/0x3b0 net/bluetooth/hci_core.c:5201
process_one_work+0x9db/0x1530 kernel/workqueue.c:2310
worker_thread+0x686/0x1180 kernel/workqueue.c:2457
kthread+0x3d0/0x4c0 kernel/kthread.c:334
ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:287
</TASK>
==================================================================
https://drive.google.com/file/d/13nr1pPRHCrZqYxz08ngNiCvmcRe-d_oK/view?usp=drive_link
https://drive.google.com/file/d/11Ox0bimeaa3r8BaGqhXBBymrs03Haz8o/view?usp=drive_link
https://drive.google.com/file/d/1yA-bbVeMCxS_p7ZjDH97NK4iI_1CLeAz/view?usp=drive_link
https://drive.google.com/file/d/1XqJJtZ0jKV_oFm8M6SuQV2YAuKEqWHbn/view?usp=drive_link
^ permalink raw reply
* [bluez/bluez]
From: BluezTestBot @ 2026-06-09 11:49 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1105699
Home: https://github.com/bluez/bluez
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [bluez/bluez] 0828cc: shared: rap: Add the is_central parameter to verif...
From: Bhavani @ 2026-06-09 11:49 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1108507
Home: https://github.com/bluez/bluez
Commit: 0828ccc601b1a2a788b21a5aef31d06fb5f42055
https://github.com/bluez/bluez/commit/0828ccc601b1a2a788b21a5aef31d06fb5f42055
Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
Date: 2026-06-09 (Tue, 09 Jun 2026)
Changed paths:
M src/shared/rap.h
Log Message:
-----------
shared: rap: Add the is_central parameter to verify whether the local role is central before sending the HCI CS Security Enable command.
Commit: d6cc95002b6b86b843711f04be4175fad1473fdd
https://github.com/bluez/bluez/commit/d6cc95002b6b86b843711f04be4175fad1473fdd
Author: Naga Bhavani Akella <naga.akella@oss.qualcomm.com>
Date: 2026-06-09 (Tue, 09 Jun 2026)
Changed paths:
M profiles/ranging/rap.c
M profiles/ranging/rap_hci.c
Log Message:
-----------
profiles: ranging: Add command flow and event support for CS Initiator
Introduce support for LE Channel Sounding (CS)
ranging procedures in the Initiator role by enabling required
HCI command sequencing and event handling.
- Add transmission of core HCI LE CS commands:
HCI_LE_CS_Read_Local_Supported_Capabilities,
HCI_LE_CS_Read_Remote_Supported_Capabilities,
HCI_LE_CS_Security_Enable,
HCI_LE_CS_Create_Config,
HCI_LE_CS_Remove_Config,
HCI_LE_CS_Read_Remote_FAE_Table,
HCI_LE_CS_Set_Channel_Classification,
HCI_LE_CS_Set_Procedure_Parameters,
HCI_LE_CS_Procedure_Enable
- Add handling of event:
HCI_LE_CS_Read_Remote_FAE_Table_Complete
This enables cs capability discovery, cs configuration management
and execution of CS ranging procedures in the Initiator role.
- CS init pipeline not yet wired up pending D-Bus API.
bt_rap_read_local_supported_capabilities() has no caller,
mark __maybe_unused till DBus APIs are introduced.
Compare: https://github.com/bluez/bluez/compare/0828ccc601b1%5E...d6cc95002b6b
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* [bluez/bluez] cb89e8: shared/bap: add ASE Control Point error responses
From: raghava447 @ 2026-06-09 11:49 UTC (permalink / raw)
To: linux-bluetooth
Branch: refs/heads/1108483
Home: https://github.com/bluez/bluez
Commit: cb89e837f32c3d7fde71f588ac87e6f912392128
https://github.com/bluez/bluez/commit/cb89e837f32c3d7fde71f588ac87e6f912392128
Author: raghu447 <raghavendra.rao@collabora.com>
Date: 2026-06-09 (Tue, 09 Jun 2026)
Changed paths:
M src/shared/bap.c
Log Message:
-----------
shared/bap: add ASE Control Point error responses
These changes are required to Pass BAP/USR/SPE/BI-01[5]-C tests.
Commit: 29bfabd2e21982b8cd70f4f846fa2d3d64ece7c7
https://github.com/bluez/bluez/commit/29bfabd2e21982b8cd70f4f846fa2d3d64ece7c7
Author: raghu447 <raghavendra.rao@collabora.com>
Date: 2026-06-09 (Tue, 09 Jun 2026)
Changed paths:
M unit/test-bap.c
Log Message:
-----------
unit/bap: add SPE tests
Add unit tests for the PTS BAP/USR/SPE/BI-01[5]-C tests.
Compare: https://github.com/bluez/bluez/compare/cb89e837f32c%5E...29bfabd2e219
To unsubscribe from these emails, change your notification settings at https://github.com/bluez/bluez/settings/notifications
^ permalink raw reply
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-09 11:05 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan,
Bartosz Golaszewski
In-Reply-To: <CAFEp6-1syMQsvuQ+dLU39bnDeL5Ok7vK1mA7CS0v1m7cjhyMQw@mail.gmail.com>
On Tue, 9 Jun 2026 12:13:08 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> Hi bartosz,
>
...
>>
>> On the other hand, we don't want to not provide the block device just because
>> someone added a DT property on one that's too big. I'd say: warn, but return 0.
>> Does it make sense?
>
> It’s still technically an error in the sense that we cannot provide
> the required nvmem feature. However, it only becomes a real issue if a
> consumer actually attempts to use it.
> Also, the block device should still be added, since the return code
> from add_dev is not checked. In any case, I’m fine with either
> approach, as long as we emit a warning message.
>
We don't check the return value now (why?) but if we ever do, the safer option
is to return 0 IMO.
>>
>> > + }
>> > +
>> > + config.id = NVMEM_DEVID_NONE;
>> > + config.dev = dev;
>> > + config.name = dev_name(dev);
>> > + config.owner = THIS_MODULE;
>> > + config.priv = (void *)(uintptr_t)dev->devt;
>> > + config.reg_read = blk_nvmem_reg_read;
>> > + config.size = bdev_nr_bytes(bdev);
>> > + config.word_size = 1;
>> > + config.stride = 1;
>> > + config.read_only = true;
>> > + config.root_only = true;
>> > + config.ignore_wp = true;
>> > + config.of_node = to_of_node(dev->fwnode);
>> > +
>> > + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
>>
>> And that was a wrong suggestion on my part too because I was under the
>> impression that we're in the probe() path, not device_add(). You can't use
>> devres here as the device at this point is not yet bound and may never be.
>
> So I understand The bd_device is purely a class device with no bus, no driver.
> For driverless devices, devres_release_all() is called explicitly
> within device_del() .
>
Right.
>>
>> Which leads me to the second point: this is not the moment to add the nvmem
>> provider. This should happen at or after probe(). Once nvmem_register()
>> returns, you have a visible nvmem resource but nothing backing it in the block
>> layer.
>
> There is a short window during which a read attempt will 'properly'
> fail, but this does seem somewhat fragile indeed.
>
>> Either do this in block core when registering a new device or schedule
>> a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
>> callback.
>
> So in the end, it seems that the simpler and more robust approach is
> probably to move away from the class_interface driver and instead
> register/unregister the nvmem directly in add_disk/del_gendisk.
> If that's ok I will move to this approach in the next version.
>
Yes, I think it's the right approach.
Bart
^ permalink raw reply
* [PATCH BlueZ v1 2/2] profiles: ranging: Add command flow and event support for CS Initiator
From: Naga Bhavani Akella @ 2026-06-09 10:49 UTC (permalink / raw)
To: linux-bluetooth
Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
Naga Bhavani Akella
In-Reply-To: <20260609104953.3122718-1-naga.akella@oss.qualcomm.com>
Introduce support for LE Channel Sounding (CS)
ranging procedures in the Initiator role by enabling required
HCI command sequencing and event handling.
- Add transmission of core HCI LE CS commands:
HCI_LE_CS_Read_Local_Supported_Capabilities,
HCI_LE_CS_Read_Remote_Supported_Capabilities,
HCI_LE_CS_Security_Enable,
HCI_LE_CS_Create_Config,
HCI_LE_CS_Remove_Config,
HCI_LE_CS_Read_Remote_FAE_Table,
HCI_LE_CS_Set_Channel_Classification,
HCI_LE_CS_Set_Procedure_Parameters,
HCI_LE_CS_Procedure_Enable
- Add handling of event:
HCI_LE_CS_Read_Remote_FAE_Table_Complete
This enables cs capability discovery, cs configuration management
and execution of CS ranging procedures in the Initiator role.
- CS init pipeline not yet wired up pending D-Bus API.
bt_rap_read_local_supported_capabilities() has no caller,
mark __maybe_unused till DBus APIs are introduced.
---
profiles/ranging/rap.c | 3 +-
profiles/ranging/rap_hci.c | 876 +++++++++++++++++++++++++++++++++----
2 files changed, 783 insertions(+), 96 deletions(-)
diff --git a/profiles/ranging/rap.c b/profiles/ranging/rap.c
index e0a46a87a..e3b50c145 100644
--- a/profiles/ranging/rap.c
+++ b/profiles/ranging/rap.c
@@ -421,7 +421,8 @@ static int rap_accept(struct btd_service *service)
bt_rap_set_conn_handle(data->hci_sm,
data->rap, handle,
(const uint8_t *) bdaddr,
- bdaddr_type);
+ bdaddr_type,
+ btd_device_is_initiator(device));
} else {
error("Failed to find connection handle for device %s",
addr);
diff --git a/profiles/ranging/rap_hci.c b/profiles/ranging/rap_hci.c
index febe23384..873bbe6cb 100644
--- a/profiles/ranging/rap_hci.c
+++ b/profiles/ranging/rap_hci.c
@@ -16,14 +16,20 @@
#include <unistd.h>
#include <string.h>
#include <endian.h>
+#include <time.h>
#include "lib/bluetooth/bluetooth.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/rap.h"
+#include "src/shared/att.h"
#include "src/log.h"
#include "monitor/bt.h"
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+
/* Macro to sign-extend an N-bit value to 16-bit signed integer */
#define SIGN_EXTEND_TO_16(val, bits) \
((int16_t)(((val) ^ (1U << ((bits)-1))) - (1U << ((bits)-1))))
@@ -67,6 +73,7 @@ struct cs_state_machine {
struct bt_rap_hci_cs_options cs_opt; /* Per-instance CS options */
uint8_t role_enable; /* Role value for HCI commands (1, 2, or 3) */
struct queue *conn_mappings; /* Per-instance connection mappings */
+ struct timespec last_chan_class_time; /* For 1-second rate limit */
};
/* Connection Handle Mapping */
@@ -74,10 +81,18 @@ struct rap_conn_mapping {
uint16_t handle;
uint8_t bdaddr[6];
uint8_t bdaddr_type;
+ bool is_central; /* true if local device is BLE Central on this link */
struct bt_att *att;
struct bt_rap *rap;
};
+/* Function declarations */
+static bool bt_rap_read_remote_fae_table(void *hci_sm, uint16_t handle);
+static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
+ uint16_t handle);
+static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
+ uint16_t handle);
+
/* Connection Mapping Helper Functions */
static void mapping_free(void *data)
{
@@ -97,14 +112,6 @@ static bool match_mapping_handle(const void *a, const void *b)
return mapping->handle == handle;
}
-static bool match_mapping_rap(const void *a, const void *b)
-{
- const struct rap_conn_mapping *mapping = a;
- const struct bt_rap *rap = b;
-
- return mapping->rap == rap;
-}
-
static struct rap_conn_mapping *find_mapping_by_handle(
struct cs_state_machine *sm,
uint16_t handle)
@@ -118,19 +125,14 @@ static struct rap_conn_mapping *find_mapping_by_handle(
static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
const uint8_t *bdaddr, uint8_t bdaddr_type,
- struct bt_att *att, struct bt_rap *rap)
+ bool is_central, struct bt_att *att,
+ struct bt_rap *rap)
{
struct rap_conn_mapping *mapping;
if (!sm)
return false;
- if (!sm->conn_mappings) {
- sm->conn_mappings = queue_new();
- if (!sm->conn_mappings)
- return false;
- }
-
/* Check if mapping already exists */
mapping = find_mapping_by_handle(sm, handle);
if (mapping) {
@@ -138,6 +140,7 @@ static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
if (bdaddr)
memcpy(mapping->bdaddr, bdaddr, 6);
mapping->bdaddr_type = bdaddr_type;
+ mapping->is_central = is_central;
mapping->att = att;
mapping->rap = rap;
return true;
@@ -152,6 +155,7 @@ static bool add_conn_mapping(struct cs_state_machine *sm, uint16_t handle,
if (bdaddr)
memcpy(mapping->bdaddr, bdaddr, 6);
mapping->bdaddr_type = bdaddr_type;
+ mapping->is_central = is_central;
mapping->att = att;
mapping->rap = rap;
@@ -171,13 +175,28 @@ static void remove_conn_mapping(struct cs_state_machine *sm, uint16_t handle)
mapping_free(mapping);
}
-static void remove_rap_mappings(struct cs_state_machine *sm)
+static struct bt_rap *resolve_handle_to_rap(struct cs_state_machine *sm,
+ uint16_t handle)
{
- if (!sm || !sm->conn_mappings)
- return;
+ struct rap_conn_mapping *mapping;
- queue_remove_all(sm->conn_mappings, match_mapping_rap, sm->rap,
- mapping_free);
+ if (!sm)
+ return NULL;
+
+ /* Try to find in mapping cache */
+ mapping = find_mapping_by_handle(sm, handle);
+ if (mapping && mapping->rap) {
+ DBG("Found handle 0x%04X in mapping cache", handle);
+ return mapping->rap;
+ }
+
+ /* Profile layer should have called bt_rap_set_conn_handle() during
+ * connection establishment. If we reach here, the mapping was not set.
+ */
+ DBG("No mapping found for handle 0x%04X", handle);
+ DBG("Profile layer should call bt_rap_set_conn_handle() on connect");
+
+ return NULL;
}
/* State Machine Functions */
@@ -194,6 +213,7 @@ static void cs_state_machine_init(struct cs_state_machine *sm,
sm->hci = hci;
sm->initiator = false;
sm->procedure_active = false;
+ sm->conn_mappings = queue_new();
/* Store role_enable for HCI commands (1, 2, or 3 from config) */
sm->role_enable = role;
@@ -219,8 +239,8 @@ static void cs_set_state(struct cs_state_machine *sm,
return;
/* Validate state values before array access */
- if (sm->current_state > CS_STATE_UNSPECIFIED ||
- new_state > CS_STATE_UNSPECIFIED) {
+ if ((unsigned int)sm->current_state >= ARRAY_SIZE(state_names) ||
+ (unsigned int)new_state >= ARRAY_SIZE(state_names)) {
error("Invalid state transition attempted");
return;
}
@@ -238,11 +258,85 @@ static enum cs_state cs_get_current_state(struct cs_state_machine *sm)
return sm ? sm->current_state : CS_STATE_UNSPECIFIED;
}
+static bool is_initiator_role(const struct cs_state_machine *sm)
+{
+ return sm->role_enable == 0x01 || sm->role_enable == 0x03;
+}
+
+/* Helper function to send read remote capabilities for all connections */
+static void send_read_remote_cap_for_mapping(void *data, void *user_data)
+{
+ struct rap_conn_mapping *mapping = data;
+ struct cs_state_machine *sm = user_data;
+
+ if (!mapping || !sm)
+ return;
+
+ DBG("Sending read remote capabilities for handle 0x%04X",
+ mapping->handle);
+ bt_rap_read_remote_supported_capabilities(sm, mapping->handle);
+}
+
/* HCI Event Callbacks */
+static void rap_rd_loc_supp_cap_done_cb(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_rsp_le_cs_rd_loc_supp_cap *rsp;
+ struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+
+ if (!sm || !data ||
+ size < sizeof(struct bt_hci_rsp_le_cs_rd_loc_supp_cap))
+ return;
+
+ DBG("size=0x%02X", size);
+
+ rsp = (const struct bt_hci_rsp_le_cs_rd_loc_supp_cap *) data;
+
+ if (rsp->status != 0) {
+ error("Read Local Supported Capabilities failed: 0x%02X",
+ rsp->status);
+ return;
+ }
+
+ DBG("Local CS Capabilities:");
+ DBG(" Num Config Supported: %u", rsp->num_config_supported);
+ DBG(" Max Consecutive Procedures: %u",
+ rsp->max_consecutive_procedures_supported);
+ DBG(" Num Antennas: %u", rsp->num_antennas_supported);
+ DBG(" Max Antenna Paths: %u", rsp->max_antenna_paths_supported);
+ DBG(" Roles Supported: 0x%02X", rsp->roles_supported);
+ DBG(" Modes Supported: 0x%02X", rsp->modes_supported);
+ DBG(" RTT Capability: 0x%02X", rsp->rtt_capability);
+ DBG(" RTT AA Only N: %u", rsp->rtt_aa_only_n);
+ DBG(" RTT Sounding N: %u", rsp->rtt_sounding_n);
+ DBG(" RTT Random Payload N: %u", rsp->rtt_random_payload_n);
+ DBG(" NADM Sounding Capability: 0x%04X",
+ rsp->nadm_sounding_capability);
+ DBG(" NADM Random Capability: 0x%04X", rsp->nadm_random_capability);
+ DBG(" CS Sync PHYs Supported: 0x%02X", rsp->cs_sync_phys_supported);
+ DBG(" Subfeatures Supported: 0x%04X", rsp->subfeatures_supported);
+ DBG(" T_IP1 Times Supported: 0x%04X", rsp->t_ip1_times_supported);
+ DBG(" T_IP2 Times Supported: 0x%04X", rsp->t_ip2_times_supported);
+ DBG(" T_FCS Times Supported: 0x%04X", rsp->t_fcs_times_supported);
+ DBG(" T_PM Times Supported: 0x%04X", rsp->t_pm_times_supported);
+ DBG(" T_SW Time Supported: %u", rsp->t_sw_time_supported);
+ DBG(" TX SNR Capability: 0x%02X", rsp->tx_snr_capability);
+
+ /* Transition to INIT state before reading remote capabilities */
+ cs_set_state(sm, CS_STATE_INIT);
+
+ /* Send read remote capabilities for all connected devices */
+ if (sm->conn_mappings) {
+ DBG("Sending read remote capabilities for all connections");
+ queue_foreach(sm->conn_mappings,
+ send_read_remote_cap_for_mapping, sm);
+ }
+}
+
static void rap_def_settings_done_cb(const void *data, uint8_t size,
void *user_data)
{
- struct bt_hci_rsp_le_cs_set_def_settings *rp;
+ const struct bt_hci_rsp_le_cs_set_def_settings *rp;
struct cs_state_machine *sm = user_data;
if (!sm || !data || size < sizeof(*rp))
@@ -250,10 +344,11 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
DBG("size=0x%02X", size);
- rp = (struct bt_hci_rsp_le_cs_set_def_settings *) data;
+ rp = (const struct bt_hci_rsp_le_cs_set_def_settings *) data;
- if (cs_get_current_state(sm) != CS_STATE_INIT) {
- DBG("Event received in Wrong State!! Expected : CS_STATE_INIT");
+ if (cs_get_current_state(sm) == CS_STATE_STOPPED ||
+ cs_get_current_state(sm) == CS_STATE_UNSPECIFIED) {
+ DBG("Def settings response in terminal state, ignoring");
return;
}
@@ -261,9 +356,13 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
/* Success - proceed to configuration */
cs_set_state(sm, CS_STATE_WAIT_CONFIG_CMPLT);
- /* Reflector role */
- DBG("Waiting for CS Config Completed event...");
- /* TODO: Initiator role - Send CS Config complete cmd */
+ /* If role is initiator, send CS Create Config command */
+ if (is_initiator_role(sm)) {
+ rap_send_hci_cs_create_config_command(sm, rp->handle);
+ } else {
+ /* Reflector role */
+ DBG("Reflector role: Waiting for CS Config Completed");
+ }
} else {
/* Error - transition to stopped */
error("CS Set default setting failed with status 0x%02X",
@@ -272,8 +371,196 @@ static void rap_def_settings_done_cb(const void *data, uint8_t size,
}
}
-static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
+static void rap_send_hci_cs_create_config_command(struct cs_state_machine *sm,
+ uint16_t handle)
+{
+ struct bt_hci_cmd_le_cs_create_config cmd;
+ unsigned int status;
+
+ uint8_t channel_map[10] = {
+ 0xFC, 0xFF, 0x7F, 0xFC, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x1F
+ };
+
+ if (!sm || !sm->hci) {
+ error("CS Create Config: sm or hci is null");
+ return;
+ }
+
+ DBG("Sending CS Create Config command for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+ cmd.create_context = 1;
+ /* Default values, will change to pick user given values later */
+ cmd.config_id = 0x00;
+ cmd.main_mode_type = 0x01;
+ cmd.sub_mode_type = 0xFF;
+ cmd.min_main_mode_steps = 0x02;
+ cmd.max_main_mode_steps = 0x03;
+ cmd.main_mode_repetition = 0x01;
+ cmd.mode_0_steps = 0x02;
+ cmd.role = 0x00;
+ cmd.rtt_type = 0x00;
+ cmd.cs_sync_phy = 0x01;
+ memcpy(cmd.channel_map, channel_map, 10);
+ cmd.channel_map_repetition = 0x01;
+ cmd.channel_selection_type = 0x00;
+ cmd.ch3c_shape = 0x00;
+ cmd.ch3c_jump = 0x02;
+ cmd.reserved = 0x00;
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_CREATE_CONFIG,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Create Config command");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("CS Create Config command sent successfully");
+}
+
+static void rap_send_hci_cs_remove_config_command(struct cs_state_machine *sm,
uint16_t handle)
+{
+ struct bt_hci_cmd_le_cs_remove_config cmd;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("CS Remove Config: sm or hci is null");
+ return;
+ }
+
+ DBG("Sending CS Remove Config command for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+ cmd.config_id = 0x00; /* Default config ID */
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_REMOVE_CONFIG,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Remove Config command");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("CS Remove Config command sent successfully");
+}
+
+static void rap_send_hci_cs_security_enable_command(
+ struct cs_state_machine *sm, uint16_t handle)
+{
+ struct bt_hci_cmd_le_cs_sec_enable cmd;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("CS Security Enable: sm or hci is null");
+ return;
+ }
+
+ DBG("Sending CS Security Enable command for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SEC_ENABLE,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Security Enable command");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("CS Security Enable command sent successfully");
+}
+
+static bool rap_send_hci_cs_set_procedure_parameters(
+ struct cs_state_machine *sm, uint16_t handle)
+{
+ struct bt_hci_cmd_le_cs_set_proc_params cmd;
+ unsigned int status;
+ uint8_t min_sub_event_len[3] = {
+ 0x00, 0x20, 0x00
+ };
+
+ uint8_t max_sub_event_len[3] = {
+ 0x03, 0x20, 0x00
+ };
+
+ if (!sm || !sm->hci) {
+ error("CS Set Procedure Parameters: sm or hci is null");
+ return false;
+ }
+
+ DBG("Sending CS Set Procedure Parameters for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+ /* Default values, will change to pick user given values later */
+ cmd.config_id = 0x00;
+ cmd.max_procedure_len = 0x0640;
+ cmd.min_procedure_interval = 0x1E;
+ cmd.max_procedure_interval = 0x96;
+ cmd.max_procedure_count = 0x00;
+ memcpy(cmd.min_subevent_len, min_sub_event_len, 3);
+ memcpy(cmd.max_subevent_len, max_sub_event_len, 3);
+ cmd.tone_antenna_config_selection = 0x07;
+ cmd.phy = 0x01;
+ cmd.tx_power_delta = 0x80;
+ cmd.preferred_peer_antenna = 0x03;
+ cmd.snr_control_initiator = 0xFF;
+ cmd.snr_control_reflector = 0xFF;
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_PROC_PARAMS,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Set Procedure Parameters command");
+ return false;
+ }
+
+ DBG("CS Set Procedure Parameters command sent successfully");
+ return true;
+}
+
+static bool rap_send_hci_cs_procedure_enable(struct cs_state_machine *sm,
+ uint16_t handle,
+ bool enable_proc)
+{
+ struct bt_hci_cmd_le_cs_proc_enable cmd;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("CS Procedure Enable: sm or hci is null");
+ return false;
+ }
+
+ DBG("Sending CS Procedure Enable for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+ cmd.config_id = 0x00; /* Default config Id */
+ cmd.enable = enable_proc ? 0x01 : 0x00;
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_PROC_ENABLE,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Procedure Enable command");
+ return false;
+ }
+
+ DBG("CS Procedure Enable command sent successfully");
+ return true;
+}
+
+static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
+ const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *ev)
{
struct bt_hci_cmd_le_cs_set_def_settings cp;
unsigned int status;
@@ -285,8 +572,8 @@ static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
memset(&cp, 0, sizeof(cp));
- if (handle)
- cp.handle = handle;
+ if (ev->handle)
+ cp.handle = ev->handle;
cp.role_enable = sm->role_enable; /* Use preserved HCI command value */
cp.cs_sync_antenna_selection = sm->cs_opt.cs_sync_ant_sel;
@@ -302,12 +589,93 @@ static void rap_send_hci_def_settings_command(struct cs_state_machine *sm,
error("Failed to send default settings cmd");
}
+static void rap_rd_rem_fae_cmplt_evt(const void *data, uint8_t size,
+ void *user_data)
+{
+ struct cs_state_machine *sm = (struct cs_state_machine *) user_data;
+ const struct bt_hci_evt_le_cs_rd_rem_fae_complete *evt;
+ struct iovec iov;
+ int i;
+
+ if (!sm || !data ||
+ size < sizeof(struct bt_hci_evt_le_cs_rd_rem_fae_complete))
+ return;
+
+ /* Initialize iovec with the event data */
+ iov.iov_base = (void *) data;
+ iov.iov_len = size;
+
+ /* Pull the entire structure at once */
+ evt = util_iov_pull_mem(&iov, sizeof(*evt));
+
+ if (!evt) {
+ error("Failed to pull remote FAE complete struct");
+ return;
+ }
+
+ DBG("status=0x%02X, handle=0x%04X", evt->status, evt->handle);
+
+ /* Check status */
+ if (evt->status != 0) {
+ /* Status 0x11 (Unsupported Feature or Parameter Value) means
+ * the remote has zero FAE, the procedure continues
+ * to the Default Settings step.
+ */
+ if (evt->status == 0x11) {
+ DBG("Remote FAE=0 (No_FAE), proceed to Def Settings");
+ if (is_initiator_role(sm)) {
+ struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete
+ tmp_ev;
+
+ memset(&tmp_ev, 0, sizeof(tmp_ev));
+ tmp_ev.handle = evt->handle;
+ DBG("Initiator: send def settings (No_FAE)");
+ rap_send_hci_def_settings_command(sm, &tmp_ev);
+ } else {
+ DBG("Reflector role: continuing after No_FAE");
+ cs_set_state(sm, CS_STATE_INIT);
+ }
+ return;
+ }
+ error("Remote FAE Table read failed with status 0x%02X",
+ evt->status);
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("Remote FAE Table received:");
+ for (i = 0; i < 72; i += 8) {
+ DBG(" [%02d-%02d]: %02X %02X %02X %02X %02X %02X %02X %02X",
+ i, i+7,
+ evt->remote_fae_table[i], evt->remote_fae_table[i+1],
+ evt->remote_fae_table[i+2], evt->remote_fae_table[i+3],
+ evt->remote_fae_table[i+4], evt->remote_fae_table[i+5],
+ evt->remote_fae_table[i+6], evt->remote_fae_table[i+7]);
+ }
+
+ /* After receiving FAE Table, send default settings */
+ /* Local capabilities already read before this event */
+ if (is_initiator_role(sm)) {
+ struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete tmp_ev;
+
+ memset(&tmp_ev, 0, sizeof(tmp_ev));
+ tmp_ev.handle = evt->handle;
+ DBG("Initiator role: send def settings after FAE table");
+ rap_send_hci_def_settings_command(sm, &tmp_ev);
+ } else {
+ DBG("Reflector role: Proceeding after FAE Table");
+ cs_set_state(sm, CS_STATE_INIT);
+ }
+}
+
static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
void *user_data)
{
struct cs_state_machine *sm = user_data;
const struct bt_hci_evt_le_cs_rd_rem_supp_cap_complete *evt;
+ struct bt_rap *rap;
struct iovec iov;
+ uint16_t subfeatures_supported;
if (!sm || !data || size < sizeof(*evt))
return;
@@ -334,6 +702,16 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
return;
}
+ /* Resolve handle to RAP instance */
+ rap = resolve_handle_to_rap(sm, evt->handle);
+
+ if (!rap) {
+ DBG("[WARN] Could not resolve handle 0x%04X to RAP instance",
+ evt->handle);
+ /* Continue with state machine RAP for now */
+ rap = sm->rap;
+ }
+
DBG("num_config=%u, ",
evt->num_config_supported);
DBG("max_consecutive_proc=%u, num_antennas=%u, ",
@@ -343,9 +721,26 @@ static void rap_rd_rmt_supp_cap_cmplt_evt(const void *data, uint8_t size,
evt->max_antenna_paths_supported,
evt->roles_supported,
evt->modes_supported);
+ subfeatures_supported = le16_to_cpu(evt->subfeatures_supported);
+ DBG("subfeatures_supported=0x%04X", subfeatures_supported);
- rap_send_hci_def_settings_command(sm, evt->handle);
- cs_set_state(sm, CS_STATE_INIT);
+ /* Check Bit 1 of subfeatures_supported (0x0002) */
+ if (!(subfeatures_supported & 0x0002)) {
+ DBG("Bit 1 not set, sending Read Remote FAE Table");
+ bt_rap_read_remote_fae_table(sm, evt->handle);
+ return;
+ }
+
+ /* Local capabilities already read before this event */
+ if (is_initiator_role(sm)) {
+ DBG("Initiator role: send def settings cmd for handle 0x%04X",
+ evt->handle);
+ rap_send_hci_def_settings_command(sm, evt);
+ } else {
+ DBG("Reflector role: send def settings cmd");
+ cs_set_state(sm, CS_STATE_INIT);
+ rap_send_hci_def_settings_command(sm, evt);
+ }
}
static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
@@ -383,8 +778,15 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
/* Check status */
if (evt->status != 0) {
- error("Configuration failed with status 0x%02X",
- evt->status);
+ if (evt->action != 0x00) {
+ /* Create/update failed — try to remove the config */
+ error("Configuration failed with status 0x%02X",
+ evt->status);
+ rap_send_hci_cs_remove_config_command(sm, evt->handle);
+ } else {
+ error("CS Config Remove failed with status 0x%02X",
+ evt->status);
+ }
cs_set_state(sm, CS_STATE_STOPPED);
return;
}
@@ -428,12 +830,40 @@ static void rap_cs_config_cmplt_evt(const void *data, uint8_t size,
rap_ev.main_mode_type, rap_ev.sub_mode_type,
rap_ev.role, rap_ev.rtt_type);
+ if (rap_ev.action == 0x00) {
+ cs_set_state(sm, CS_STATE_UNSPECIFIED);
+ DBG("CS Config Removed !!!");
+ bt_rap_hci_cs_config_complete_callback(size, &rap_ev, sm->rap);
+ return;
+ }
/* Success - proceed to Security enable complete */
cs_set_state(sm, CS_STATE_WAIT_SEC_CMPLT);
- /* Reflector role */
- DBG("Waiting for security enable event...");
- /* TODO: Initiator role - Send CS Security enable cmd */
+ /* CS Security Enable may only be issued by the BLE Central */
+ if (rap_ev.role == 0x00) {
+ /* Initiator role */
+ struct rap_conn_mapping *mapping;
+
+ mapping = find_mapping_by_handle(sm, evt->handle);
+ if (!mapping || !mapping->is_central) {
+ error("CS Security Enable skipped: not BLE Central");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ if (bt_att_get_security(mapping->att, NULL) <
+ BT_ATT_SECURITY_MEDIUM) {
+ error("CS Security Enable skipped: not encrypted");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("Central,encrypted: Sending CS Security Enable command");
+ rap_send_hci_cs_security_enable_command(sm, evt->handle);
+ } else {
+ /* Reflector role */
+ DBG("Reflector role: Waiting for security enable event...");
+ }
/* Send callback to RAP Profile */
bt_rap_hci_cs_config_complete_callback(size, &rap_ev, sm->rap);
@@ -486,9 +916,27 @@ static void rap_cs_sec_enable_cmplt_evt(const void *data, uint8_t size,
/* Success - proceed to configuration */
cs_set_state(sm, CS_STATE_WAIT_PROC_CMPLT);
- /* Reflector role */
- DBG("Waiting for CS Proc complete event...");
- /* TODO: Initiator - Send CS Proc Set Parameter and enable */
+ /* Check if role is initiator */
+ if (sm->cs_opt.role == CS_INITIATOR) {
+ DBG("Initiator role: Sending CS Set Procedure Params");
+ if (!rap_send_hci_cs_set_procedure_parameters(
+ sm, handle)) {
+ error("Failed to send CS Set Procedure Params");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+
+ DBG("Initiator role: Sending CS Procedure Enable");
+ if (!rap_send_hci_cs_procedure_enable(sm, handle,
+ true)) {
+ error("Failed to send CS Procedure Enable");
+ cs_set_state(sm, CS_STATE_STOPPED);
+ return;
+ }
+ } else {
+ // Reflector role
+ DBG("Reflector role: Waiting for CS Proc compl event");
+ }
} else {
/* Error - transition to stopped */
error("Security enable failed with status 0x%02X",
@@ -565,8 +1013,13 @@ static void rap_cs_proc_enable_cmplt_evt(const void *data, uint8_t size,
rap_ev.proc_intrvl);
/* Success - procedure started */
- cs_set_state(sm, CS_STATE_STARTED);
- sm->procedure_active = true;
+ if (rap_ev.state == 0x01) {
+ cs_set_state(sm, CS_STATE_STARTED);
+ sm->procedure_active = true;
+ } else if (rap_ev.state == 0x00) {
+ cs_set_state(sm, CS_STATE_STOPPED);
+ sm->procedure_active = false;
+ }
/* Send callback to RAP Profile */
bt_rap_hci_cs_procedure_enable_complete_callback(size,
@@ -800,6 +1253,54 @@ static void parse_cs_step(struct iovec *iov, struct cs_step_data *step,
}
}
+/*
+ * Handle the common step-parsing tail shared by both subevent result variants.
+ * Fixes truncation (num_steps_reported > CS_MAX_STEPS) by zeroing the step
+ * count and trimming send_len to header_size, matching the abort-status path.
+ */
+static void cs_parse_steps(struct iovec *iov,
+ uint8_t num_steps_reported,
+ uint8_t proc_done_status,
+ uint8_t subevt_done_status,
+ uint8_t abort_reason,
+ uint8_t cs_role, uint8_t cs_rtt_type,
+ uint8_t max_paths,
+ struct cs_step_data *step_data,
+ uint8_t *num_steps_out,
+ size_t *send_len,
+ size_t header_size)
+{
+ uint8_t steps = MIN(num_steps_reported, CS_MAX_STEPS);
+ uint8_t i;
+
+ if (num_steps_reported > CS_MAX_STEPS) {
+ DBG("Too many steps reported: %u (max %u)",
+ num_steps_reported, CS_MAX_STEPS);
+ *num_steps_out = 0;
+ *send_len = header_size;
+ return;
+ }
+
+ if (subevt_done_status == 0xF || proc_done_status == 0xF) {
+ DBG("CS Procedure/Subevent aborted: ");
+ DBG("sub evt status = %d, proc status = %d, reason = %d",
+ subevt_done_status, proc_done_status, abort_reason);
+ /*
+ * Step bytes were never parsed; zero-initialised step_data[]
+ * entries would appear as spurious mode-0 quality=0 steps to
+ * the BCS algorithm. Clear the count so an aborted subevent
+ * carries no fake measurements.
+ */
+ *num_steps_out = 0;
+ *send_len = header_size;
+ return;
+ }
+
+ for (i = 0; i < steps; i++)
+ parse_cs_step(iov, &step_data[i], cs_role, cs_rtt_type,
+ max_paths);
+}
+
static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
void *user_data)
{
@@ -822,7 +1323,6 @@ static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
uint8_t abort_reason;
uint8_t num_ant_paths;
uint8_t num_steps_reported;
- uint8_t i;
if (!sm || !data ||
size < sizeof(struct bt_hci_evt_le_cs_subevent_result))
@@ -883,28 +1383,13 @@ static void rap_cs_subevt_result_evt(const void *data, uint8_t size,
rap_ev->num_ant_paths = num_ant_paths;
rap_ev->num_steps_reported = steps;
- if (num_steps_reported > CS_MAX_STEPS) {
- DBG("Too many steps reported: %u (max %u)",
- num_steps_reported, CS_MAX_STEPS);
- goto send_event;
- }
-
- /* Early exit for error conditions */
- if (rap_ev->subevt_done_status == 0xF ||
- rap_ev->proc_done_status == 0xF) {
- DBG("CS Procedure/Subevent aborted: ");
- DBG("sub evt status = %d, proc status = %d, reason = %d",
- rap_ev->subevt_done_status, rap_ev->proc_done_status,
- rap_ev->abort_reason);
- goto send_event;
- }
+ cs_parse_steps(&iov, num_steps_reported,
+ proc_done_status, subevt_done_status, abort_reason,
+ cs_role, cs_rtt_type, max_paths,
+ rap_ev->step_data, &rap_ev->num_steps_reported,
+ &send_len,
+ offsetof(struct rap_ev_cs_subevent_result, step_data));
- /* Parse interleaved step data from remaining iovec data */
- for (i = 0; i < steps; i++)
- parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
- max_paths);
-
-send_event:
DBG("CS subevent result processed: %zu bytes, ", send_len);
bt_rap_hci_cs_subevent_result_callback(send_len, rap_ev, sm->rap);
free(rap_ev);
@@ -920,7 +1405,7 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
uint8_t cs_rtt_type;
uint8_t max_paths;
uint8_t steps;
- size_t send_len = 0;
+ size_t send_len;
uint16_t handle;
uint8_t config_id;
uint8_t proc_done_status;
@@ -928,7 +1413,6 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
uint8_t abort_reason;
uint8_t num_ant_paths;
uint8_t num_steps_reported;
- uint8_t i;
if (!sm || !data ||
size < sizeof(struct bt_hci_evt_le_cs_subevent_result_continue))
@@ -981,28 +1465,14 @@ static void rap_cs_subevt_result_cont_evt(const void *data, uint8_t size,
rap_ev->num_ant_paths = num_ant_paths;
rap_ev->num_steps_reported = steps;
- if (num_steps_reported > CS_MAX_STEPS) {
- DBG("Too many steps reported: %u (max %u)",
- num_steps_reported, CS_MAX_STEPS);
- goto send_event;
- }
-
- /* Early exit for error conditions */
- if (rap_ev->subevt_done_status == 0xF ||
- rap_ev->proc_done_status == 0xF) {
- DBG("CS Procedure/Subevent aborted: ");
- DBG("sub evt status = %d, proc status = %d, reason = %d",
- rap_ev->subevt_done_status, rap_ev->proc_done_status,
- rap_ev->abort_reason);
- goto send_event;
- }
-
- /* Parse interleaved step data from remaining iovec data */
- for (i = 0; i < steps; i++)
- parse_cs_step(&iov, &rap_ev->step_data[i], cs_role, cs_rtt_type,
- max_paths);
+ cs_parse_steps(&iov, num_steps_reported,
+ proc_done_status, subevt_done_status, abort_reason,
+ cs_role, cs_rtt_type, max_paths,
+ rap_ev->step_data, &rap_ev->num_steps_reported,
+ &send_len,
+ offsetof(struct rap_ev_cs_subevent_result_cont,
+ step_data));
-send_event:
DBG("CS subevent result cont processed: %zu bytes, ", send_len);
bt_rap_hci_cs_subevent_result_cont_callback(send_len, rap_ev, sm->rap);
free(rap_ev);
@@ -1010,6 +1480,208 @@ send_event:
/* Subevent handler function type */
+/* This cmd is used by host to update channel classification
+ * function will be used when user sets channel classification
+ * keeping it unused till DBUS API is added */
+bool __maybe_unused bt_rap_set_channel_classification(void *hci_sm,
+ const uint8_t *channel_map)
+{
+ struct cs_state_machine *sm = hci_sm;
+ struct bt_hci_cmd_le_cs_set_chan_class cmd;
+ struct timespec now;
+ unsigned int status;
+ uint8_t default_map[10];
+ int i, enabled_count = 0;
+ long diff_ms;
+
+ if (!sm || !sm->hci) {
+ error("Invalid state machine or HCI");
+ return false;
+ }
+
+ /* If no channel map provided, create default map */
+ if (!channel_map) {
+ memset(default_map, 0, sizeof(default_map));
+
+ /* Enable channels 2-22 and 26-76 (total 72 channels)
+ * Channels 0, 1, 23, 24, 25, 77, 78, 79 are reserved/disabled
+ */
+ for (i = 2; i <= 22; i++) {
+ default_map[i / 8] |= (1 << (i % 8));
+ }
+ for (i = 26; i <= 76; i++) {
+ default_map[i / 8] |= (1 << (i % 8));
+ }
+
+ channel_map = default_map;
+ } else {
+ /* Validate the provided channel map */
+ /* Check reserved channels are disabled */
+ if ((channel_map[0] & 0x03) != 0) { /* Channels 0, 1 */
+ error("Channels 0 and 1 must be disabled (reserved)");
+ return false;
+ }
+ if ((channel_map[2] & 0x80) != 0) { /* Channel 23 */
+ error("Channel 23 must be disabled (reserved)");
+ return false;
+ }
+ if ((channel_map[3] & 0x03) != 0) { /* Channels 24, 25 */
+ error("Channels 24 and 25 must be disabled (reserved)");
+ return false;
+ }
+ if ((channel_map[9] & 0xE0) != 0) { /* Ch 77, 78 and RFU bit */
+ error("Channels 77, 78, 79 must be disabled (res)");
+ return false;
+ }
+
+ /* Count enabled channels using popcount; mask reserved bits:
+ * ch 0-1 (byte 0 bits 0-1), ch 23 (byte 2 bit 7),
+ * ch 24-25 (byte 3 bits 0-1), ch 77-79 (byte 9 bits 5-7).
+ */
+ {
+ uint8_t masked[10];
+
+ memcpy(masked, channel_map, 10);
+ masked[0] &= 0xFC;
+ masked[2] &= 0x7F;
+ masked[3] &= 0xFC;
+ masked[9] &= 0x1F;
+ for (i = 0; i < 10; i++)
+ enabled_count += __builtin_popcount(masked[i]);
+ }
+
+ if (enabled_count < 15) {
+ error("At least 15 channels must be enabled (found %d)",
+ enabled_count);
+ return false;
+ }
+ }
+
+ /* Controller rejects with Command Disallowed (0x0C)
+ * if less than 1 second has elapsed since the
+ * last invocation. Enforce the limit here to avoid silent failures.
+ */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (sm->last_chan_class_time.tv_sec != 0 ||
+ sm->last_chan_class_time.tv_nsec != 0) {
+ diff_ms = (now.tv_sec -
+ sm->last_chan_class_time.tv_sec) * 1000 +
+ (now.tv_nsec -
+ sm->last_chan_class_time.tv_nsec) / 1000000;
+
+ if (diff_ms < 1000) {
+ error("CS Set Channel Classification throttled: "
+ "only %ldms since last call (min 1000ms)",
+ diff_ms);
+ return false;
+ }
+ }
+ sm->last_chan_class_time = now;
+
+ DBG("Sending CS Set Channel Classification command");
+
+ memset(&cmd, 0, sizeof(cmd));
+ memcpy(cmd.channel_classification, channel_map, 10);
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_SET_CHAN_CLASS,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send CS Set Channel Classification command");
+ return false;
+ }
+
+ DBG("CS Set Channel Classification command sent successfully");
+ return true;
+}
+
+static bool bt_rap_read_remote_fae_table(void *hci_sm, uint16_t handle)
+{
+ struct cs_state_machine *sm = hci_sm;
+ struct bt_hci_cmd_le_cs_rd_rem_fae cmd;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("Invalid state machine or HCI");
+ return false;
+ }
+
+ DBG("Sending Read Remote FAE Table for handle 0x%04X", handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_REM_FAE,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send Read Remote FAE Table command");
+ return false;
+ }
+
+ DBG("Read Remote FAE Table command sent successfully");
+ return true;
+}
+
+/* This cmd is used by host to start cs distance measurement procedure
+ * function will be used when user start distance measurement
+ * keeping it unused till DBUS API is added */
+static bool __maybe_unused bt_rap_read_local_supported_capabilities(
+ void *hci_sm)
+{
+ struct cs_state_machine *sm = hci_sm;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("Invalid state machine or HCI");
+ return false;
+ }
+
+ DBG("Sending Read Local Supported Capabilities command");
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_LOC_SUPP_CAP,
+ NULL, 0, rap_rd_loc_supp_cap_done_cb,
+ sm, NULL);
+
+ if (!status) {
+ error("Failed to send Read Local Supported Capabilities");
+ return false;
+ }
+
+ DBG("Read Local Supported Capabilities command sent successfully");
+ return true;
+}
+
+static bool bt_rap_read_remote_supported_capabilities(void *hci_sm,
+ uint16_t handle)
+{
+ struct cs_state_machine *sm = hci_sm;
+ struct bt_hci_cmd_le_cs_rd_rem_supp_cap cmd;
+ unsigned int status;
+
+ if (!sm || !sm->hci) {
+ error("Invalid state machine or HCI");
+ return false;
+ }
+
+ DBG("Sending Read Remote Supported Capabilities for handle 0x%04X",
+ handle);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.handle = cpu_to_le16(handle);
+
+ status = bt_hci_send(sm->hci, BT_HCI_CMD_LE_CS_RD_REM_SUPP_CAP,
+ &cmd, sizeof(cmd), NULL, sm, NULL);
+
+ if (!status) {
+ error("Failed to send Read Remote Capabilities command");
+ return false;
+ }
+
+ DBG("Read Remote Capabilities command sent successfully");
+ return true;
+}
+
static void unregister_event_id(void *data, void *user_data)
{
struct bt_hci *hci = user_data;
@@ -1041,6 +1713,11 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
max_tx_power);
sm->event_ids = queue_new();
+ if (!sm->event_ids) {
+ error("Failed to allocate event_ids queue");
+ free(sm);
+ return NULL;
+ }
/* Register each LE Meta subevent individually */
id = bt_hci_register_subevent(hci,
@@ -1051,6 +1728,15 @@ void *bt_rap_attach_hci(struct bt_rap *rap, struct bt_hci *hci,
queue_push_tail(sm->event_ids, UINT_TO_PTR(id));
+ id = bt_hci_register_subevent(hci,
+ BT_HCI_EVT_LE_CS_RD_REM_FAE_COMPLETE,
+ rap_rd_rem_fae_cmplt_evt, sm, NULL);
+
+ if (!id)
+ goto fail;
+
+ queue_push_tail(sm->event_ids, UINT_TO_PTR(id));
+
id = bt_hci_register_subevent(hci,
BT_HCI_EVT_LE_CS_CONFIG_COMPLETE,
rap_cs_config_cmplt_evt, sm, NULL);
@@ -1100,12 +1786,14 @@ fail:
error("Failed to register hci le meta subevents");
queue_foreach(sm->event_ids, unregister_event_id, hci);
queue_destroy(sm->event_ids, NULL);
+ queue_destroy(sm->conn_mappings, mapping_free);
free(sm);
return NULL;
}
-bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
- const uint8_t *bdaddr, uint8_t bdaddr_type)
+bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap,
+ uint16_t handle, const uint8_t *bdaddr, uint8_t bdaddr_type,
+ bool is_central)
{
struct cs_state_machine *sm = hci_sm;
struct bt_att *att;
@@ -1124,7 +1812,8 @@ bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
bdaddr[2], bdaddr[1], bdaddr[0], bdaddr_type);
}
- return add_conn_mapping(sm, handle, bdaddr, bdaddr_type, att, rap);
+ return add_conn_mapping(sm, handle, bdaddr, bdaddr_type, is_central,
+ att, rap);
}
void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle)
@@ -1157,9 +1846,6 @@ void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm)
queue_destroy(sm->event_ids, NULL);
/* Clean up per-instance connection mappings */
- remove_rap_mappings(sm);
-
- /* Destroy the connection mappings queue */
queue_destroy(sm->conn_mappings, mapping_free);
/* Free the state machine */
--
^ permalink raw reply related
* [PATCH BlueZ v1 1/2] shared: rap: Add the is_central parameter to verify whether the local role is central before sending the HCI CS Security Enable command.
From: Naga Bhavani Akella @ 2026-06-09 10:49 UTC (permalink / raw)
To: linux-bluetooth
Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
Naga Bhavani Akella
In-Reply-To: <20260609104953.3122718-1-naga.akella@oss.qualcomm.com>
---
src/shared/rap.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/shared/rap.h b/src/shared/rap.h
index d3ced61b1..4d6a3d1c8 100644
--- a/src/shared/rap.h
+++ b/src/shared/rap.h
@@ -213,5 +213,6 @@ void bt_rap_detach_hci(struct bt_rap *rap, void *hci_sm);
/* Connection handle mapping functions */
bool bt_rap_set_conn_handle(void *hci_sm, struct bt_rap *rap, uint16_t handle,
- const uint8_t *bdaddr, uint8_t bdaddr_type);
+ const uint8_t *bdaddr, uint8_t bdaddr_type,
+ bool is_central);
void bt_rap_clear_conn_handle(void *hci_sm, uint16_t handle);
--
^ permalink raw reply related
* [PATCH BlueZ v1 0/2] Initial Channel Sounding Support for
From: Naga Bhavani Akella @ 2026-06-09 10:49 UTC (permalink / raw)
To: linux-bluetooth
Cc: luiz.dentz, quic_mohamull, quic_hbandi, quic_anubhavg,
Naga Bhavani Akella
This series adds initial LE Channel Sounding (CS) Initiator support by
introducing the required HCI command flow and event handling for CS
procedures.
The changes include
1. Adding an is_central parameter to validate that
current local role is central before issuing
HCI_LE_CS_Security_Enable command.
2. Introduction of initial LE Channel Sounding (CS)
Initiator support by adding required HCI command flow
and event handling for CS capability discovery,
configuration management, and ranging procedures.
The CS init pipeline is not yet wired to D-Bus APIs,
so bt_rap_read_local_supported_capabilities()
is currently marked __maybe_unused.
Naga Bhavani Akella (2):
shared: rap: Add the is_central parameter to verify whether the local
role is central before sending the HCI CS Security Enable command.
profiles: ranging: Add command flow and event support for CS
Initiator
profiles/ranging/rap.c | 3 +-
profiles/ranging/rap_hci.c | 876 +++++++++++++++++++++++++++++++++----
src/shared/rap.h | 3 +-
3 files changed, 785 insertions(+), 97 deletions(-)
--
^ permalink raw reply
* Re: [PATCH v1] Bluetooth: qca: Add BT FW build version log
From: Bartosz Golaszewski @ 2026-06-09 10:49 UTC (permalink / raw)
To: Paul Menzel
Cc: Xiuzhuo Shang, Marcel Holtmann, Luiz Augusto von Dentz,
linux-arm-msm, linux-bluetooth, linux-kernel, cheng.jiang,
quic_chezhou, wei.deng, shuai.zhang, mengshi.wu, jinwang.li,
Bartosz Golaszewski
In-Reply-To: <8697f148-5e21-44b4-a949-9d348e39c2ea@molgen.mpg.de>
On Tue, 9 Jun 2026 12:29:41 +0200, Paul Menzel <pmenzel@molgen.mpg.de> said:
> Dear Xiuzhuo, dear Bartosz
>
>
> Am 09.06.26 um 11:39 schrieb Bartosz Golaszewski:
>> On Tue, 9 Jun 2026 09:54:17 +0200, Xiuzhuo Shang said:
>>> Printf BT FW build version log after BT FW downloaded.
>
> Please paste the new lines, so reviewers see how it is going to look
> like. Please also document a motivation (answering Why?).
>
>>> Signed-off-by: Xiuzhuo Shang <xiuzhuo.shang@oss.qualcomm.com>
>>> ---
>>> drivers/bluetooth/btqca.c | 2 ++
>>> 1 file changed, 2 insertions(+)
>>>
>>> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
>>> index dda76365726f..04ebe290bc78 100644
>>> --- a/drivers/bluetooth/btqca.c
>>> +++ b/drivers/bluetooth/btqca.c
>>> @@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
>>>
>>> hci_set_fw_info(hdev, "%s", build_label);
>>>
>>> + bt_dev_info(hdev, "QCA FW build version: %s", build_label);
>>> +
>>> kfree(build_label);
>>> out:
>>> kfree_skb(skb);
>>
>> This string can be read from debugfs, do we need an additional message in the
>> kernel log?
>
> In my opinion the firmware version should be part of the Linux logs, as
> that is what users share with bug reports, and you do not want to have
> several cycles collecting information. For example, currently for
> QCA6174 it’s currently:
>
> Bluetooth: hci0: QCA: patch rome 0x302 build 0x3e8, firmware rome
> 0x302 build 0x111
>
>
> Kind regards,
>
> Paul
>
>
> PS:
>
> ```
> $ sudo dmesg | grep -e ath10k -e Bluetooth
> [ 17.880668] ath10k_pci 0000:3a:00.0: enabling device (0000 -> 0002)
> [ 17.883428] ath10k_pci 0000:3a:00.0: pci irq msi oper_irq_mode 2
> irq_mode 0 reset_mode 0
> [ 18.138972] ath10k_pci 0000:3a:00.0: qca6174 hw3.2 target 0x05030000
> chip_id 0x00340aff sub 1a56:1535
> [ 18.138977] ath10k_pci 0000:3a:00.0: kconfig debug 1 debugfs 1
> tracing 1 dfs 0 testmode 0
> [ 18.139068] ath10k_pci 0000:3a:00.0: firmware ver
> WLAN.RM.4.4.1-00309- api 6 features wowlan,ignore-otp,mfp crc32 0793bcf2
> [ 18.206593] ath10k_pci 0000:3a:00.0: board_file api 2 bmi_id N/A
> crc32 d2863f91
> [ 18.221145] Bluetooth: Core ver 2.22
> [ 18.221802] Bluetooth: HCI device and connection manager initialized
> [ 18.221807] Bluetooth: HCI socket layer initialized
> [ 18.221808] Bluetooth: L2CAP socket layer initialized
> [ 18.221813] Bluetooth: SCO socket layer initialized
> [ 18.258187] Bluetooth: hci0: using rampatch file:
> qca/rampatch_usb_00000302.bin
> [ 18.258192] Bluetooth: hci0: QCA: patch rome 0x302 build 0x3e8,
> firmware rome 0x302 build 0x111
> [ 18.301557] ath10k_pci 0000:3a:00.0: htt-ver 3.87 wmi-op 4 htt-op 3
> cal otp max-sta 32 raw 0 hwcrypto 1
> [ 18.381567] ath10k_pci 0000:3a:00.0 wlp58s0: renamed from wlan0
> [ 18.619003] Bluetooth: hci0: using NVM file: qca/nvm_usb_00000302.bin
> [ 18.643646] Bluetooth: hci0: HCI Enhanced Setup Synchronous
> Connection command is advertised, but not supported.
> ```
>
Fair enough.
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v1] Bluetooth: qca: Add BT FW build version log
From: Paul Menzel @ 2026-06-09 10:29 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Xiuzhuo Shang, Marcel Holtmann, Luiz Augusto von Dentz,
linux-arm-msm, linux-bluetooth, linux-kernel, cheng.jiang,
quic_chezhou, wei.deng, shuai.zhang, mengshi.wu, jinwang.li
In-Reply-To: <CAMRc=Met_U-1gDmGFM9hSWFc5vvs4SuRQOa3hZcfUo190rh59w@mail.gmail.com>
Dear Xiuzhuo, dear Bartosz
Am 09.06.26 um 11:39 schrieb Bartosz Golaszewski:
> On Tue, 9 Jun 2026 09:54:17 +0200, Xiuzhuo Shang said:
>> Printf BT FW build version log after BT FW downloaded.
Please paste the new lines, so reviewers see how it is going to look
like. Please also document a motivation (answering Why?).
>> Signed-off-by: Xiuzhuo Shang <xiuzhuo.shang@oss.qualcomm.com>
>> ---
>> drivers/bluetooth/btqca.c | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
>> index dda76365726f..04ebe290bc78 100644
>> --- a/drivers/bluetooth/btqca.c
>> +++ b/drivers/bluetooth/btqca.c
>> @@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
>>
>> hci_set_fw_info(hdev, "%s", build_label);
>>
>> + bt_dev_info(hdev, "QCA FW build version: %s", build_label);
>> +
>> kfree(build_label);
>> out:
>> kfree_skb(skb);
>
> This string can be read from debugfs, do we need an additional message in the
> kernel log?
In my opinion the firmware version should be part of the Linux logs, as
that is what users share with bug reports, and you do not want to have
several cycles collecting information. For example, currently for
QCA6174 it’s currently:
Bluetooth: hci0: QCA: patch rome 0x302 build 0x3e8, firmware rome
0x302 build 0x111
Kind regards,
Paul
PS:
```
$ sudo dmesg | grep -e ath10k -e Bluetooth
[ 17.880668] ath10k_pci 0000:3a:00.0: enabling device (0000 -> 0002)
[ 17.883428] ath10k_pci 0000:3a:00.0: pci irq msi oper_irq_mode 2
irq_mode 0 reset_mode 0
[ 18.138972] ath10k_pci 0000:3a:00.0: qca6174 hw3.2 target 0x05030000
chip_id 0x00340aff sub 1a56:1535
[ 18.138977] ath10k_pci 0000:3a:00.0: kconfig debug 1 debugfs 1
tracing 1 dfs 0 testmode 0
[ 18.139068] ath10k_pci 0000:3a:00.0: firmware ver
WLAN.RM.4.4.1-00309- api 6 features wowlan,ignore-otp,mfp crc32 0793bcf2
[ 18.206593] ath10k_pci 0000:3a:00.0: board_file api 2 bmi_id N/A
crc32 d2863f91
[ 18.221145] Bluetooth: Core ver 2.22
[ 18.221802] Bluetooth: HCI device and connection manager initialized
[ 18.221807] Bluetooth: HCI socket layer initialized
[ 18.221808] Bluetooth: L2CAP socket layer initialized
[ 18.221813] Bluetooth: SCO socket layer initialized
[ 18.258187] Bluetooth: hci0: using rampatch file:
qca/rampatch_usb_00000302.bin
[ 18.258192] Bluetooth: hci0: QCA: patch rome 0x302 build 0x3e8,
firmware rome 0x302 build 0x111
[ 18.301557] ath10k_pci 0000:3a:00.0: htt-ver 3.87 wmi-op 4 htt-op 3
cal otp max-sta 32 raw 0 hwcrypto 1
[ 18.381567] ath10k_pci 0000:3a:00.0 wlp58s0: renamed from wlan0
[ 18.619003] Bluetooth: hci0: using NVM file: qca/nvm_usb_00000302.bin
[ 18.643646] Bluetooth: hci0: HCI Enhanced Setup Synchronous
Connection command is advertised, but not supported.
```
^ permalink raw reply
* [BlueZ PATCH v3 1/2] shared/bap: add ASE Control Point error responses
From: raghu447 @ 2026-06-09 10:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: raghu447
In-Reply-To: <20260609102548.6887-1-raghavendra.rao@collabora.com>
These changes are required to Pass BAP/USR/SPE/BI-01[5]-C tests.
---
src/shared/bap.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 88 insertions(+), 2 deletions(-)
diff --git a/src/shared/bap.c b/src/shared/bap.c
index 8fc2fb14d..edb84efca 100644
--- a/src/shared/bap.c
+++ b/src/shared/bap.c
@@ -57,6 +57,11 @@
(1<<BAP_DURATION_LTV_TYPE)|\
(1<<BAP_FRAME_LEN_LTV_TYPE))
+#define BAP_METADATA_PREF_CONTEXT_LTV_TYPE 0x01
+#define BAP_METADATA_CONTEXT_LTV_TYPE 0x02
+#define BAP_METADATA_PROGRAM_INFO_LTV_TYPE 0x03
+#define BAP_METADATA_LANGUAGE_LTV_TYPE 0x04
+
struct bt_bap_pac_changed {
unsigned int id;
bt_bap_pac_func_t added;
@@ -3304,6 +3309,40 @@ static uint8_t stream_enable(struct bt_bap_stream *stream, struct iovec *meta,
return 0;
}
+static bool ascs_metadata_rsp(struct bt_bap_endpoint *ep, struct iovec *meta,
+ struct iovec *rsp)
+{
+ struct bt_ltv *ltv;
+ uint16_t supported_context = 0;
+ uint16_t context;
+
+ if (ep->stream && ep->stream->lpac)
+ supported_context = ep->stream->lpac->qos.supported_context;
+
+ ltv = meta->iov_base;
+ if (meta->iov_len >= sizeof(*ltv) &&
+ (ltv->type < BAP_METADATA_PREF_CONTEXT_LTV_TYPE ||
+ ltv->type > BAP_METADATA_LANGUAGE_LTV_TYPE)) {
+ ascs_ase_rsp_add(rsp, ep->id,
+ BT_ASCS_RSP_METADATA_UNSUPPORTED, ltv->type);
+ return true;
+ }
+
+ if (meta->iov_len >= sizeof(*ltv) + sizeof(context) &&
+ ltv->type == BAP_METADATA_CONTEXT_LTV_TYPE &&
+ ltv->len == sizeof(context) + 1) {
+ context = get_le16(ltv->value);
+ if (!context || (context & ~supported_context)) {
+ ascs_ase_rsp_add(rsp, ep->id,
+ BT_ASCS_RSP_METADATA_INVALID,
+ ltv->type);
+ return true;
+ }
+ }
+
+ return false;
+}
+
static uint8_t ep_enable(struct bt_bap_endpoint *ep, struct bt_bap *bap,
struct bt_ascs_enable *req, struct iovec *iov,
struct iovec *rsp)
@@ -3335,6 +3374,9 @@ static uint8_t ep_enable(struct bt_bap_endpoint *ep, struct bt_bap *bap,
return 0;
}
+ if (ascs_metadata_rsp(ep, &meta, rsp))
+ return 0;
+
if (!ep->stream) {
DBG(bap, "No stream found");
ascs_ase_rsp_add(rsp, ep->id,
@@ -3568,6 +3610,9 @@ static uint8_t ep_metadata(struct bt_bap_endpoint *ep,
meta.iov_base = util_iov_pull_mem(iov, req->len);
meta.iov_len = req->len;
+ if (ascs_metadata_rsp(ep, &meta, rsp))
+ return 0;
+
return stream_metadata(ep->stream, &meta, rsp);
}
@@ -3673,6 +3718,23 @@ static struct iovec *ascs_ase_cp_rsp_new(uint8_t op)
return iov;
}
+static void ascs_ase_cp_rsp_add_truncated(struct iovec *rsp)
+{
+ ascs_ase_rsp_add_errno(rsp, 0x00, -ENOMSG);
+}
+
+static bool ascs_ase_cp_rsp_invalid_len(uint8_t op, size_t len, uint8_t num)
+{
+ switch (op) {
+ case BT_ASCS_METADATA:
+ return len == num;
+ case BT_ASCS_RELEASE:
+ return true;
+ default:
+ return false;
+ }
+}
+
static void ascs_ase_cp_write(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
@@ -3697,7 +3759,7 @@ static void ascs_ase_cp_write(struct gatt_db_attribute *attrib,
return;
}
- if (len < sizeof(*hdr)) {
+ if (!len) {
DBG(bap, "invalid len %u < %u sizeof(*hdr)", len,
sizeof(*hdr));
gatt_db_attribute_write_result(attrib, id,
@@ -3705,9 +3767,26 @@ static void ascs_ase_cp_write(struct gatt_db_attribute *attrib,
return;
}
+ if (len < sizeof(*hdr)) {
+ DBG(bap, "invalid len %u < %u sizeof(*hdr)", len,
+ sizeof(*hdr));
+
+ rsp = ascs_ase_cp_rsp_new(value[0]);
+ ascs_ase_cp_rsp_add_truncated(rsp);
+ ret = 0;
+ goto respond;
+ }
+
hdr = util_iov_pull_mem(&iov, sizeof(*hdr));
rsp = ascs_ase_cp_rsp_new(hdr->op);
+ if (!hdr->num) {
+ DBG(bap, "invalid Number_of_ASEs 0");
+ ascs_ase_cp_rsp_add_truncated(rsp);
+ ret = 0;
+ goto respond;
+ }
+
for (handler = handlers; handler && handler->str; handler++) {
if (handler->op != hdr->op)
continue;
@@ -3716,7 +3795,14 @@ static void ascs_ase_cp_write(struct gatt_db_attribute *attrib,
DBG(bap, "invalid len %u < %u "
"hdr->num * handler->size", len,
hdr->num * handler->size);
- ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
+ if (ascs_ase_cp_rsp_invalid_len(hdr->op, iov.iov_len,
+ hdr->num)) {
+ ascs_ase_cp_rsp_add_truncated(rsp);
+ ret = 0;
+ } else
+ ret = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
goto respond;
}
^ permalink raw reply related
* [BlueZ PATCH v3 2/2] unit/bap: add SPE tests
From: raghu447 @ 2026-06-09 10:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: raghu447
In-Reply-To: <20260609102548.6887-1-raghavendra.rao@collabora.com>
Add unit tests for the PTS BAP/USR/SPE/BI-01[5]-C tests.
---
unit/test-bap.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/unit/test-bap.c b/unit/test-bap.c
index 3a67e7016..e43d1d2ef 100644
--- a/unit/test-bap.c
+++ b/unit/test-bap.c
@@ -4349,6 +4349,69 @@ static void test_scc_metadata(void)
test_usr_scc_metadata();
}
+#define ASE_CP_RSP(_op, _ase, _code, _reason) \
+ IOV_DATA(0x1b, CP_HND, _op, 0x01, _ase, _code, _reason)
+
+#define ASE_CP_RSP_TRUNCATED(_op) \
+ IOV_DATA(0x1b, CP_HND, _op, 0xff, 0x00, 0x02, 0x00)
+
+#define SPE_CP_TRUNCATED(_op) \
+ IOV_DATA(0x52, CP_HND, _op), \
+ ASE_CP_RSP_TRUNCATED(_op)
+
+#define SPE_CP_ZERO_ASES(_op) \
+ IOV_DATA(0x52, CP_HND, _op, 0x00), \
+ ASE_CP_RSP_TRUNCATED(_op)
+
+#define SPE_METADATA_TRUNCATED(_ase) \
+ IOV_DATA(0x52, CP_HND, 0x07, 0x01, _ase), \
+ ASE_CP_RSP_TRUNCATED(0x07)
+
+#define SPE_METADATA_UNSUPPORTED(_ase) \
+ SCC_SRC_ENABLE, \
+ IOV_DATA(0x52, CP_HND, 0x07, 0x01, _ase, 0x02, 0x01, 0xfc), \
+ ASE_CP_RSP(0x07, _ase, 0x0a, 0xfc)
+
+#define SPE_METADATA_INVALID_CONTEXT(_ase) \
+ SCC_SRC_ENABLE, \
+ IOV_DATA(0x52, CP_HND, 0x07, 0x01, _ase, 0x04, 0x03, 0x02, \
+ 0x00, 0x10), \
+ ASE_CP_RSP(0x07, _ase, 0x0c, 0x02)
+
+/* Unicast Server Rejects Invalid ASE Control Point Procedures
+ *
+ * Test Purpose:
+ * Verify the behavior of a Unicast Server IUT when a Unicast Client writes
+ * invalid ASE Control Point parameters.
+ *
+ * Pass verdict:
+ * The IUT sends a notification of the ASE Control Point characteristic with
+ * the expected Response_Code and Reason values.
+ */
+static void test_usr_spe(void)
+{
+ define_test("BAP/USR/SPE/BI-01-C [USR ASE Control Point truncated]",
+ test_setup_server, test_server, NULL,
+ SPE_CP_TRUNCATED(0x03));
+ define_test("BAP/USR/SPE/BI-02-C [USR ASE Control Point zero ASEs]",
+ test_setup_server, test_server, NULL,
+ SPE_CP_ZERO_ASES(0x03));
+ define_test("BAP/USR/SPE/BI-03-C [USR Update Metadata truncated]",
+ test_setup_server, test_server, NULL,
+ SPE_METADATA_TRUNCATED(SRC_ID(0)));
+ define_test("BAP/USR/SPE/BI-04-C [USR Update Metadata unsupported]",
+ test_setup_server, test_server, &cfg_src_enable,
+ SPE_METADATA_UNSUPPORTED(SRC_ID(0)));
+ define_test("BAP/USR/SPE/BI-05-C [USR Update Metadata invalid context]",
+ test_setup_server, test_server, &cfg_src_enable,
+ SPE_METADATA_INVALID_CONTEXT(SRC_ID(0)));
+}
+
+static void test_spe(void)
+{
+ test_usr_spe();
+}
+
#define SNK_ENABLE \
IOV_DATA(0x52, 0x22, 0x00, 0x03, 0x01, 0x01, 0x04, 0x03, 0x02, 0x01, \
00), \
@@ -10259,6 +10322,7 @@ int main(int argc, char *argv[])
tester_init(&argc, &argv);
test_disc();
+ test_spe();
test_scc();
test_bsrc_scc();
test_bsnk_scc();
^ permalink raw reply related
* [BlueZ PATCH v3 0/2] shared/bap: add ASE Control Point error responses
From: raghu447 @ 2026-06-09 10:25 UTC (permalink / raw)
To: linux-bluetooth; +Cc: raghu447
In-Reply-To: <CABBYNZKQuFeXvtEE3d3nOUbkKZTAFsfB=1=NDFp48CFEg2e-=Q@mail.gmail.com>
Added bap unit tests in a seperate patch.
These changes are required to Pass BAP/USR/SPE/BI-01[5]-C tests.
raghu447 (2):
shared/bap: add ASE Control Point error responses
unit/bap: add SPE tests
src/shared/bap.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++--
unit/test-bap.c | 64 ++++++++++++++++++++++++++++++++++
2 files changed, 152 insertions(+), 2 deletions(-)
^ permalink raw reply
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Loic Poulain @ 2026-06-09 10:13 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan
In-Reply-To: <CAMRc=MfuiPMkSm=-G6VEJpqKhos0TD_pf0ScBGqfwLHT0uk8yQ@mail.gmail.com>
Hi bartosz,
On Tue, Jun 9, 2026 at 10:52 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>
> On Tue, 9 Jun 2026 09:52:29 +0200, Loic Poulain
> <loic.poulain@oss.qualcomm.com> said:
> > From: Daniel Golle <daniel@makrotopia.org>
> >
> > On embedded devices using an eMMC it is common that one or more partitions
> > on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> > data. Allow referencing the partition in device tree for the kernel and
> > Wi-Fi drivers accessing it via the NVMEM layer.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > ---
> > block/Kconfig | 9 +++++
> > block/Makefile | 1 +
> > block/blk-nvmem.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 124 insertions(+)
> >
> > diff --git a/block/Kconfig b/block/Kconfig
> > index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> > --- a/block/Kconfig
> > +++ b/block/Kconfig
> > @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> > by falling back to the kernel crypto API when inline
> > encryption hardware is not present.
> >
> > +config BLK_NVMEM
> > + bool "Block device NVMEM provider"
> > + depends on OF
> > + depends on NVMEM
> > + help
> > + Allow block devices (or partitions) to act as NVMEM providers,
> > + typically used with eMMC to store MAC addresses or Wi-Fi
> > + calibration data on embedded devices.
> > +
> > source "block/partitions/Kconfig"
> >
> > config BLK_PM
> > diff --git a/block/Makefile b/block/Makefile
> > index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> > --- a/block/Makefile
> > +++ b/block/Makefile
> > @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> > blk-crypto-sysfs.o
> > obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> > obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
> > +obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
> > diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..a6e62fa98675ee9bcb9c7035a611b5a573ab9091
> > --- /dev/null
> > +++ b/block/blk-nvmem.c
> > @@ -0,0 +1,114 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * block device NVMEM provider
> > + *
> > + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + *
> > + * Useful on devices using a partition on an eMMC for MAC addresses or
> > + * Wi-Fi calibration EEPROM data.
> > + */
> > +
> > +#include <linux/file.h>
> > +#include <linux/nvmem-provider.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/pagemap.h>
> > +#include <linux/property.h>
> > +
> > +#include "blk.h"
> > +
> > +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> > + void *val, size_t bytes)
> > +{
> > + blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> > + dev_t devt = (dev_t)(uintptr_t)priv;
> > + size_t bytes_left = bytes;
> > + loff_t pos = from;
> > + int ret = 0;
> > +
> > + struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> > + if (IS_ERR(bdev_file))
> > + return PTR_ERR(bdev_file);
> > +
> > + while (bytes_left) {
> > + pgoff_t f_index = pos >> PAGE_SHIFT;
> > + struct folio *folio;
> > + size_t folio_off;
> > + size_t to_read;
> > +
> > + folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> > + if (IS_ERR(folio)) {
> > + ret = PTR_ERR(folio);
> > + break;
> > + }
> > +
> > + folio_off = offset_in_folio(folio, pos);
> > + to_read = min(bytes_left, folio_size(folio) - folio_off);
> > + memcpy_from_folio(val, folio, folio_off, to_read);
> > + pos += to_read;
> > + bytes_left -= to_read;
> > + val += to_read;
> > + folio_put(folio);
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int blk_nvmem_register(struct device *dev)
> > +{
> > + struct block_device *bdev = dev_to_bdev(dev);
> > + struct nvmem_config config = {};
> > +
> > + /* skip devices which do not have a device tree node */
> > + if (!dev_of_node(dev))
> > + return 0;
> > +
> > + /* skip devices without an nvmem layout defined */
> > + struct device_node *child __free(device_node) =
> > + of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> > + if (!child)
> > + return 0;
> > +
> > + /*
> > + * skip block device too large to be represented as NVMEM devices,
> > + * the NVMEM reg_read callback uses an unsigned int offset
> > + */
> > + if (bdev_nr_bytes(bdev) > UINT_MAX) {
> > + dev_warn(dev, "block device too large to be an NVMEM provider\n");
> > + return -ENODEV;
>
> Wait, I must have suggested -ENODEV here on too little coffee. This callback
> is called from device_add(), not when the device is bound so it's not the same
> thing as returning -ENODEV from probe().
>
> On the other hand, we don't want to not provide the block device just because
> someone added a DT property on one that's too big. I'd say: warn, but return 0.
> Does it make sense?
It’s still technically an error in the sense that we cannot provide
the required nvmem feature. However, it only becomes a real issue if a
consumer actually attempts to use it.
Also, the block device should still be added, since the return code
from add_dev is not checked. In any case, I’m fine with either
approach, as long as we emit a warning message.
>
> > + }
> > +
> > + config.id = NVMEM_DEVID_NONE;
> > + config.dev = dev;
> > + config.name = dev_name(dev);
> > + config.owner = THIS_MODULE;
> > + config.priv = (void *)(uintptr_t)dev->devt;
> > + config.reg_read = blk_nvmem_reg_read;
> > + config.size = bdev_nr_bytes(bdev);
> > + config.word_size = 1;
> > + config.stride = 1;
> > + config.read_only = true;
> > + config.root_only = true;
> > + config.ignore_wp = true;
> > + config.of_node = to_of_node(dev->fwnode);
> > +
> > + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
>
> And that was a wrong suggestion on my part too because I was under the
> impression that we're in the probe() path, not device_add(). You can't use
> devres here as the device at this point is not yet bound and may never be.
So I understand The bd_device is purely a class device with no bus, no driver.
For driverless devices, devres_release_all() is called explicitly
within device_del() .
>
> Which leads me to the second point: this is not the moment to add the nvmem
> provider. This should happen at or after probe(). Once nvmem_register()
> returns, you have a visible nvmem resource but nothing backing it in the block
> layer.
There is a short window during which a read attempt will 'properly'
fail, but this does seem somewhat fragile indeed.
> Either do this in block core when registering a new device or schedule
> a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
> callback.
So in the end, it seems that the simpler and more robust approach is
probably to move away from the class_interface driver and instead
register/unregister the nvmem directly in add_disk/del_gendisk.
If that's ok I will move to this approach in the next version.
Regards,
Loic
^ permalink raw reply
* Re: [PATCH v1] Bluetooth: qca: Add BT FW build version log
From: Bartosz Golaszewski @ 2026-06-09 9:39 UTC (permalink / raw)
To: Xiuzhuo Shang
Cc: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
linux-arm-msm, linux-bluetooth, linux-kernel, cheng.jiang,
quic_chezhou, wei.deng, shuai.zhang, mengshi.wu, jinwang.li
In-Reply-To: <20260609075417.1160702-1-xiuzhuo.shang@oss.qualcomm.com>
On Tue, 9 Jun 2026 09:54:17 +0200, Xiuzhuo Shang
<xiuzhuo.shang@oss.qualcomm.com> said:
> Printf BT FW build version log after BT FW downloaded.
>
> Signed-off-by: Xiuzhuo Shang <xiuzhuo.shang@oss.qualcomm.com>
> ---
> drivers/bluetooth/btqca.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
> index dda76365726f..04ebe290bc78 100644
> --- a/drivers/bluetooth/btqca.c
> +++ b/drivers/bluetooth/btqca.c
> @@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
>
> hci_set_fw_info(hdev, "%s", build_label);
>
> + bt_dev_info(hdev, "QCA FW build version: %s", build_label);
> +
> kfree(build_label);
> out:
> kfree_skb(skb);
> --
> 2.43.0
>
>
This string can be read from debugfs, do we need an additional message in the
kernel log?
Bart
^ permalink raw reply
* RE: Support for block device NVMEM providers
From: bluez.test.bot @ 2026-06-09 9:31 UTC (permalink / raw)
To: linux-bluetooth, loic.poulain
In-Reply-To: <20260609-block-as-nvmem-v4-1-45712e6b22c6@oss.qualcomm.com>
[-- Attachment #1: Type: text/plain, Size: 4013 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1108363
---Test result---
Test Summary:
CheckPatch FAIL 5.52 seconds
VerifyFixes PASS 0.08 seconds
VerifySignedoff PASS 0.07 seconds
GitLint FAIL 1.74 seconds
SubjectPrefix FAIL 0.54 seconds
BuildKernel PASS 27.52 seconds
CheckAllWarning PASS 31.14 seconds
CheckSparse PASS 32.08 seconds
BuildKernel32 PASS 27.75 seconds
TestRunnerSetup PASS 604.95 seconds
TestRunner_l2cap-tester PASS 63.61 seconds
TestRunner_iso-tester PASS 83.95 seconds
TestRunner_bnep-tester PASS 19.76 seconds
TestRunner_mgmt-tester FAIL 232.93 seconds
TestRunner_rfcomm-tester PASS 27.19 seconds
TestRunner_sco-tester PASS 35.12 seconds
TestRunner_ioctl-tester PASS 28.16 seconds
TestRunner_mesh-tester FAIL 26.95 seconds
TestRunner_smp-tester PASS 24.92 seconds
TestRunner_userchan-tester PASS 21.24 seconds
TestRunner_6lowpan-tester PASS 23.97 seconds
IncrementalBuild PASS 55.67 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v4,4/8] block: implement NVMEM provider
WARNING: please write a help paragraph that fully describes the config symbol with at least 4 lines
#200: FILE: block/Kconfig:212:
+config BLK_NVMEM
+ bool "Block device NVMEM provider"
+ depends on OF
+ depends on NVMEM
+ help
+ Allow block devices (or partitions) to act as NVMEM providers,
+ typically used with eMMC to store MAC addresses or Wi-Fi
+ calibration data on embedded devices.
+
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#222:
new file mode 100644
total: 0 errors, 2 warnings, 133 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14618741.patch has style problems, please review.
NOTE: Ignored message types: UNKNOWN_COMMIT_ID
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v4,8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
1: T1 Title exceeds max length (85>80): "[v4,8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses"
##############################
Test: SubjectPrefix - FAIL
Desc: Check subject contains "Bluetooth" prefix
Output:
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
"Bluetooth: " prefix is not specified in the subject
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.282 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 1.972 seconds
Mesh - Send cancel - 2 Timed out 1.997 seconds
https://github.com/bluez/bluetooth-next/pull/296
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: 6lowpan: fix NHC entry use-after-free on error path
From: bluez.test.bot @ 2026-06-09 9:30 UTC (permalink / raw)
To: linux-bluetooth, zhaoyz24
In-Reply-To: <20260609080054.4541-1-zhaoyz24@mails.tsinghua.edu.cn>
[-- Attachment #1: Type: text/plain, Size: 4094 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1108371
---Test result---
Test Summary:
CheckPatch FAIL 0.75 seconds
VerifyFixes PASS 0.13 seconds
VerifySignedoff PASS 0.14 seconds
GitLint PASS 0.34 seconds
SubjectPrefix FAIL 0.13 seconds
BuildKernel PASS 28.31 seconds
CheckAllWarning PASS 31.95 seconds
CheckSparse PASS 29.63 seconds
BuildKernel32 PASS 27.13 seconds
TestRunnerSetup PASS 596.34 seconds
TestRunner_l2cap-tester PASS 65.08 seconds
TestRunner_iso-tester PASS 86.94 seconds
TestRunner_bnep-tester PASS 20.27 seconds
TestRunner_mgmt-tester FAIL 224.52 seconds
TestRunner_rfcomm-tester PASS 26.68 seconds
TestRunner_sco-tester PASS 34.10 seconds
TestRunner_ioctl-tester PASS 27.51 seconds
TestRunner_mesh-tester FAIL 26.96 seconds
TestRunner_smp-tester PASS 24.40 seconds
TestRunner_userchan-tester PASS 21.02 seconds
TestRunner_6lowpan-tester PASS 23.79 seconds
IncrementalBuild PASS 26.60 seconds
Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
6lowpan: fix NHC entry use-after-free on error path
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#120:
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#121:
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#122:
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#123:
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#124:
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
WARNING: Reported-by: should be immediately followed by Closes: with a URL to the report
#125:
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: GLM:GLM-5.1
total: 0 errors, 6 warnings, 0 checks, 10 lines checked
NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.
/github/workspace/src/patch/14618776.patch has style problems, please review.
NOTE: Ignored message types: UNKNOWN_COMMIT_ID
NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.
##############################
Test: SubjectPrefix - FAIL
Desc: Check subject contains "Bluetooth" prefix
Output:
"Bluetooth: " prefix is not specified in the subject
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.265 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.252 seconds
Mesh - Send cancel - 2 Timed out 1.985 seconds
https://github.com/bluez/bluetooth-next/pull/298
---
Regards,
Linux Bluetooth
^ permalink raw reply
* RE: [v1] Bluetooth: qca: Add BT FW build version log
From: bluez.test.bot @ 2026-06-09 9:15 UTC (permalink / raw)
To: linux-bluetooth, xiuzhuo.shang
In-Reply-To: <20260609075417.1160702-1-xiuzhuo.shang@oss.qualcomm.com>
[-- Attachment #1: Type: text/plain, Size: 988 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1108364
---Test result---
Test Summary:
CheckPatch PASS 0.51 seconds
VerifyFixes PASS 0.09 seconds
VerifySignedoff PASS 0.09 seconds
GitLint PASS 0.23 seconds
SubjectPrefix PASS 0.08 seconds
BuildKernel PASS 19.65 seconds
CheckAllWarning PASS 21.90 seconds
CheckSparse PASS 20.60 seconds
BuildKernel32 PASS 19.93 seconds
TestRunnerSetup PASS 414.40 seconds
IncrementalBuild PASS 21.45 seconds
https://github.com/bluez/bluetooth-next/pull/297
---
Regards,
Linux Bluetooth
^ permalink raw reply
* Re: [PATCH v4 4/8] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-09 8:52 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
In-Reply-To: <20260609-block-as-nvmem-v4-4-45712e6b22c6@oss.qualcomm.com>
On Tue, 9 Jun 2026 09:52:29 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> From: Daniel Golle <daniel@makrotopia.org>
>
> On embedded devices using an eMMC it is common that one or more partitions
> on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> data. Allow referencing the partition in device tree for the kernel and
> Wi-Fi drivers accessing it via the NVMEM layer.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> block/Kconfig | 9 +++++
> block/Makefile | 1 +
> block/blk-nvmem.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 124 insertions(+)
>
> diff --git a/block/Kconfig b/block/Kconfig
> index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> --- a/block/Kconfig
> +++ b/block/Kconfig
> @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> by falling back to the kernel crypto API when inline
> encryption hardware is not present.
>
> +config BLK_NVMEM
> + bool "Block device NVMEM provider"
> + depends on OF
> + depends on NVMEM
> + help
> + Allow block devices (or partitions) to act as NVMEM providers,
> + typically used with eMMC to store MAC addresses or Wi-Fi
> + calibration data on embedded devices.
> +
> source "block/partitions/Kconfig"
>
> config BLK_PM
> diff --git a/block/Makefile b/block/Makefile
> index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> --- a/block/Makefile
> +++ b/block/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> blk-crypto-sysfs.o
> obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED) += holder.o
> +obj-$(CONFIG_BLK_NVMEM) += blk-nvmem.o
> diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..a6e62fa98675ee9bcb9c7035a611b5a573ab9091
> --- /dev/null
> +++ b/block/blk-nvmem.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * block device NVMEM provider
> + *
> + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * Useful on devices using a partition on an eMMC for MAC addresses or
> + * Wi-Fi calibration EEPROM data.
> + */
> +
> +#include <linux/file.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/pagemap.h>
> +#include <linux/property.h>
> +
> +#include "blk.h"
> +
> +static int blk_nvmem_reg_read(void *priv, unsigned int from,
> + void *val, size_t bytes)
> +{
> + blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> + dev_t devt = (dev_t)(uintptr_t)priv;
> + size_t bytes_left = bytes;
> + loff_t pos = from;
> + int ret = 0;
> +
> + struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> + if (IS_ERR(bdev_file))
> + return PTR_ERR(bdev_file);
> +
> + while (bytes_left) {
> + pgoff_t f_index = pos >> PAGE_SHIFT;
> + struct folio *folio;
> + size_t folio_off;
> + size_t to_read;
> +
> + folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> + if (IS_ERR(folio)) {
> + ret = PTR_ERR(folio);
> + break;
> + }
> +
> + folio_off = offset_in_folio(folio, pos);
> + to_read = min(bytes_left, folio_size(folio) - folio_off);
> + memcpy_from_folio(val, folio, folio_off, to_read);
> + pos += to_read;
> + bytes_left -= to_read;
> + val += to_read;
> + folio_put(folio);
> + }
> +
> + return ret;
> +}
> +
> +static int blk_nvmem_register(struct device *dev)
> +{
> + struct block_device *bdev = dev_to_bdev(dev);
> + struct nvmem_config config = {};
> +
> + /* skip devices which do not have a device tree node */
> + if (!dev_of_node(dev))
> + return 0;
> +
> + /* skip devices without an nvmem layout defined */
> + struct device_node *child __free(device_node) =
> + of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> + if (!child)
> + return 0;
> +
> + /*
> + * skip block device too large to be represented as NVMEM devices,
> + * the NVMEM reg_read callback uses an unsigned int offset
> + */
> + if (bdev_nr_bytes(bdev) > UINT_MAX) {
> + dev_warn(dev, "block device too large to be an NVMEM provider\n");
> + return -ENODEV;
Wait, I must have suggested -ENODEV here on too little coffee. This callback
is called from device_add(), not when the device is bound so it's not the same
thing as returning -ENODEV from probe().
On the other hand, we don't want to not provide the block device just because
someone added a DT property on one that's too big. I'd say: warn, but return 0.
Does it make sense?
> + }
> +
> + config.id = NVMEM_DEVID_NONE;
> + config.dev = dev;
> + config.name = dev_name(dev);
> + config.owner = THIS_MODULE;
> + config.priv = (void *)(uintptr_t)dev->devt;
> + config.reg_read = blk_nvmem_reg_read;
> + config.size = bdev_nr_bytes(bdev);
> + config.word_size = 1;
> + config.stride = 1;
> + config.read_only = true;
> + config.root_only = true;
> + config.ignore_wp = true;
> + config.of_node = to_of_node(dev->fwnode);
> +
> + return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &config));
And that was a wrong suggestion on my part too because I was under the
impression that we're in the probe() path, not device_add(). You can't use
devres here as the device at this point is not yet bound and may never be.
Which leads me to the second point: this is not the moment to add the nvmem
provider. This should happen at or after probe(). Once nvmem_register()
returns, you have a visible nvmem resource but nothing backing it in the block
layer.
Either do this in block core when registering a new device or schedule
a notifier here for the BUS_NOTIFY_BOUND_DRIVER event and do it in the notifier
callback.
Sorry, I should have paid more attention, I forgot how the class interface
works.
> +}
> +
> +static struct class_interface blk_nvmem_bus_interface __refdata = {
> + .class = &block_class,
> + .add_dev = &blk_nvmem_register,
> +};
> +
> +static int __init blk_nvmem_init(void)
> +{
> + int ret;
> +
> + ret = class_interface_register(&blk_nvmem_bus_interface);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +device_initcall(blk_nvmem_init);
>
> --
> 2.34.1
>
>
Bart
^ permalink raw reply
* [PATCH] 6lowpan: fix NHC entry use-after-free on error path
From: Yizhou Zhao @ 2026-06-09 8:00 UTC (permalink / raw)
To: linux-bluetooth
Cc: Yizhou Zhao, Alexander Aring, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-wpan, netdev,
linux-kernel, Yuxiang Yang, Ao Wang, Xuewei Feng, Qi Li, Ke Xu,
stable
lowpan_nhc_do_uncompression() looks up an NHC descriptor while holding
lowpan_nhc_lock. If the descriptor has no uncompress callback, the error
path drops the lock before printing nhc->name.
lowpan_nhc_del() removes descriptors under the same lock and then relies
on synchronize_net() before the owning module can be unloaded. That only
waits for net RX RCU readers. lowpan_header_decompress() is also exported
and can be reached from callers that are not necessarily covered by the net
core RX critical section, for example the Bluetooth 6LoWPAN L2CAP receive
path.
This leaves a race where one task drops lowpan_nhc_lock in the error path,
another task unregisters and frees the matching descriptor after
synchronize_net() returns, and the first task then dereferences nhc->name
for the warning.
With the post-unlock window widened, KASAN reports:
BUG: KASAN: slab-use-after-free in lowpan_nhc_do_uncompression+0x1f4/0x220
Read of size 8
lowpan_nhc_do_uncompression
lowpan_header_decompress
Fix this by printing the warning before dropping lowpan_nhc_lock, so the
descriptor name is read while unregister is still excluded. The malformed
packet is still rejected with -ENOTSUPP.
Fixes: 92aa7c65d295 ("6lowpan: add generic nhc layer interface")
Cc: stable@vger.kernel.org
Reported-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
Reported-by: Yuxiang Yang <yangyx22@mails.tsinghua.edu.cn>
Reported-by: Ao Wang <wangao@seu.edu.cn>
Reported-by: Xuewei Feng <fengxw06@126.com>
Reported-by: Qi Li <qli01@tsinghua.edu.cn>
Reported-by: Ke Xu <xuke@tsinghua.edu.cn>
Assisted-by: GLM:GLM-5.1
Signed-off-by: Yizhou Zhao <zhaoyz24@mails.tsinghua.edu.cn>
---
diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c
index 7b374595328d..a4dde85664f2 100644
--- a/net/6lowpan/nhc.c
+++ b/net/6lowpan/nhc.c
@@ -117,9 +117,9 @@ int lowpan_nhc_do_uncompression(struct sk_buff *skb,
return ret;
}
} else {
- spin_unlock_bh(&lowpan_nhc_lock);
netdev_warn(dev, "received nhc id for %s which is not implemented.\n",
nhc->name);
+ spin_unlock_bh(&lowpan_nhc_lock);
return -ENOTSUPP;
}
} else {
--
2.43.0
^ permalink raw reply related
* [PATCH v1] Bluetooth: qca: Add BT FW build version log
From: Xiuzhuo Shang @ 2026-06-09 7:54 UTC (permalink / raw)
To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz
Cc: linux-arm-msm, linux-bluetooth, linux-kernel, cheng.jiang,
quic_chezhou, wei.deng, shuai.zhang, mengshi.wu, jinwang.li,
xiuzhuo.shang
Printf BT FW build version log after BT FW downloaded.
Signed-off-by: Xiuzhuo Shang <xiuzhuo.shang@oss.qualcomm.com>
---
drivers/bluetooth/btqca.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f..04ebe290bc78 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -143,6 +143,8 @@ static int qca_read_fw_build_info(struct hci_dev *hdev)
hci_set_fw_info(hdev, "%s", build_label);
+ bt_dev_info(hdev, "QCA FW build version: %s", build_label);
+
kfree(build_label);
out:
kfree_skb(skb);
--
2.43.0
^ permalink raw reply related
* [PATCH v4 8/8] arm64: dts: qcom: arduino-imola: Describe NVMEM layout for WiFi/BT addresses
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Konrad Dybcio, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
On Arduino Uno-Q, the eMMC boot1 partition is factory provisioned
with device-specific information such as the WiFi MAC address
and the Bluetooth BD address. This partition can serve as an
alternative to additional non-volatile memory, such as a
dedicated EEPROM.
The eMMC boot partitions are typically good candidates, as they
are relatively small, read-only by default (and can be enforced
as hardware read-only), and are not affected by board reflashing
procedures, which generally target the eMMC user or GP partitions.
Describe the corresponding nvmem-layout for the WiFi and Bluetooth
addresses, and point the WiFi and Bluetooth nodes to the appropriate
NVMEM cells to retrieve them.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
index bf088fa9807f040f0c8f405f9111b01790b09377..128c7a7e76b5b089044745f5d6407d6391055fc2 100644
--- a/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
+++ b/arch/arm64/boot/dts/qcom/qrb2210-arduino-imola.dts
@@ -409,7 +409,40 @@ &sdhc_1 {
no-sdio;
no-sd;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
status = "okay";
+
+ card@0 {
+ compatible = "mmc-card";
+ reg = <0>;
+
+ partitions-boot1 {
+ compatible = "fixed-partitions";
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ wifi_mac_addr: mac-addr@4400 {
+ compatible = "mac-base";
+ reg = <0x4400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+
+ bd_addr: bd-addr@5400 {
+ compatible = "mac-base";
+ reg = <0x5400 0x6>;
+ #nvmem-cell-cells = <1>;
+ };
+ };
+ };
+ };
};
&spi5 {
@@ -512,6 +545,9 @@ bluetooth {
vddch0-supply = <&pm4125_l22>;
enable-gpios = <&tlmm 87 GPIO_ACTIVE_HIGH>;
max-speed = <3000000>;
+
+ nvmem-cells = <&bd_addr 0>;
+ nvmem-cell-names = "local-bd-address";
};
};
@@ -557,6 +593,9 @@ &wifi {
qcom,ath10k-calibration-variant = "ArduinoImola";
firmware-name = "qcm2290";
+ nvmem-cells = <&wifi_mac_addr 0>;
+ nvmem-cell-names = "mac-address";
+
status = "okay";
};
--
2.34.1
^ permalink raw reply related
* [PATCH v4 7/8] Bluetooth: qca: Set NVMEM BD address quirks when address is invalid
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
When the controller BD address is invalid (zero or default),
set the NVMEM quirks to allow retrieving the address from a
'local-bd-address' NVMEM cell. The BD address is often stored
alongside the WiFi MAC address in big-endian format, so also
set the big-endian quirk.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
drivers/bluetooth/btqca.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index dda76365726f0bfe0e80e05fe04859fa4f0592e1..df33eacfd29fa680f393f90215150743e6001d5b 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -721,8 +721,11 @@ static int qca_check_bdaddr(struct hci_dev *hdev, const struct qca_fw_config *co
}
bda = (struct hci_rp_read_bd_addr *)skb->data;
- if (!bacmp(&bda->bdaddr, &config->bdaddr))
+ if (!bacmp(&bda->bdaddr, &config->bdaddr)) {
hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+ }
kfree_skb(skb);
--
2.34.1
^ permalink raw reply related
* [PATCH v4 6/8] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
From: Loic Poulain @ 2026-06-09 7:52 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Loic Poulain, Bartosz Golaszewski
In-Reply-To: <20260609-block-as-nvmem-v4-0-45712e6b22c6@oss.qualcomm.com>
Some devices store the Bluetooth BD address in non-volatile
memory, which can be accessed through the NVMEM framework.
Similar to Ethernet or WiFi MAC addresses, add support for
reading the BD address from a 'local-bd-address' NVMEM cell.
As with the device-tree provided BD address, add a quirk to
indicate whether a device or platform should attempt to read
the address from NVMEM when no valid in-chip address is present.
Also add a quirk to indicate if the address is stored in
big-endian byte order.
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
---
include/net/bluetooth/hci.h | 18 ++++++++++++++++++
net/bluetooth/hci_sync.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 56 insertions(+), 1 deletion(-)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 572b1c620c5d653a1fe10b26c1b0ba33e8f4968f..7686466d1109253b0d75edeb5f6a99fb98ce4cc6 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -164,6 +164,24 @@ enum {
*/
HCI_QUIRK_BDADDR_PROPERTY_BROKEN,
+ /* When this quirk is set, the public Bluetooth address
+ * initially reported by HCI Read BD Address command
+ * is considered invalid. The public BD Address can be
+ * retrieved via a 'local-bd-address' NVMEM cell.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_USE_BDADDR_NVMEM,
+
+ /* When this quirk is set, the Bluetooth Device Address provided by
+ * the 'local-bd-address' NVMEM is stored in big-endian order.
+ *
+ * This quirk can be set before hci_register_dev is called or
+ * during the hdev->setup vendor callback.
+ */
+ HCI_QUIRK_BDADDR_NVMEM_BE,
+
/* When this quirk is set, the duplicate filtering during
* scanning is based on Bluetooth devices addresses. To allow
* RSSI based updates, restart scanning if needed.
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index fd3aacdea512a37c22b9a2be90c89ddca4b4d99f..589ccdfa26c1281d6eb979370523fff0d7920302 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -7,6 +7,7 @@
*/
#include <linux/property.h>
+#include <linux/of_net.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -3588,6 +3589,37 @@ int hci_powered_update_sync(struct hci_dev *hdev)
return 0;
}
+/**
+ * hci_dev_get_bd_addr_from_nvmem - Get the Bluetooth Device Address
+ * (BD_ADDR) for a HCI device from
+ * an NVMEM cell.
+ * @hdev: The HCI device
+ *
+ * Search for 'local-bd-address' NVMEM cell in the device firmware node.
+ *
+ * All-zero BD addresses are rejected (unprovisioned).
+ */
+static int hci_dev_get_bd_addr_from_nvmem(struct hci_dev *hdev)
+{
+ struct device_node *np = dev_of_node(hdev->dev.parent);
+ u8 ba[sizeof(bdaddr_t)];
+ int err;
+
+ if (!np)
+ return -ENODEV;
+
+ err = of_get_nvmem_eui48(np, "local-bd-address", ba);
+ if (err)
+ return err;
+
+ if (hci_test_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE))
+ baswap(&hdev->public_addr, (bdaddr_t *)ba);
+ else
+ bacpy(&hdev->public_addr, (bdaddr_t *)ba);
+
+ return 0;
+}
+
/**
* hci_dev_get_bd_addr_from_property - Get the Bluetooth Device Address
* (BD_ADDR) for a HCI device from
@@ -5042,12 +5074,17 @@ static int hci_dev_setup_sync(struct hci_dev *hdev)
* its setup callback.
*/
invalid_bdaddr = hci_test_quirk(hdev, HCI_QUIRK_INVALID_BDADDR) ||
- hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY);
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) ||
+ hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
if (!ret) {
if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_PROPERTY) &&
!bacmp(&hdev->public_addr, BDADDR_ANY))
hci_dev_get_bd_addr_from_property(hdev);
+ if (hci_test_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM) &&
+ !bacmp(&hdev->public_addr, BDADDR_ANY))
+ hci_dev_get_bd_addr_from_nvmem(hdev);
+
if (invalid_bdaddr && bacmp(&hdev->public_addr, BDADDR_ANY) &&
hdev->set_bdaddr) {
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox