[PATCH v9 08/10] Bluetooth: hci_sync: Add NVMEM-backed BD address retrieval
Loic Poulain <[email protected]> Thu, 30 Jul 2026 18:00:38 +0200
| Newsgroups | org.infradead.lists.ath10k,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-block,org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel,org.kernel.vger.linux-mmc,org.kernel.vger.linux-wireless,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> Reviewed-by: Piotr Kwapulinski <[email protected]> Signed-off-by: Loic Poulain <[email protected]> --- include/net/bluetooth/hci.h | 18 ++++++++++++++++++ net/bluetooth/hci_sync.c | 41 ++++++++++++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 6 ++++-- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 38186a245f14dbd2a26cbc331e7f1b3e72f1a607..145ab94e51482f903f4c9133d8c97561ac1c1e00 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -161,6 +161,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 3be8c3581c6caadeeea56e14e09d4123deb4ec03..c0ed7fb14d19bd79429a6892ec1c7cc89bc7ab3a 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> @@ -3593,6 +3594,39 @@ 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). + * + * Return: 0 on success, or a negative error code on failure. + */ +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 @@ -5072,12 +5106,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); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d23ca1dd089365fe4606600c68475dceb561e5ee..42b6866f96a4c81850d98041b34b7fcb0f9d0e47 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -614,7 +614,8 @@ static bool is_configured(struct hci_dev *hdev) return false; if ((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)) && !bacmp(&hdev->public_addr, BDADDR_ANY)) return false; @@ -630,7 +631,8 @@ static __le32 get_missing_options(struct hci_dev *hdev) options |= MGMT_OPTION_EXTERNAL_CONFIG; if ((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)) && !bacmp(&hdev->public_addr, BDADDR_ANY)) options |= MGMT_OPTION_PUBLIC_ADDRESS; -- 2.34.1