-
Notifications
You must be signed in to change notification settings - Fork 27
recipes-connectivity: Add bt-qca-set-bdaddr recipe to setup BT BDADDR #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduce a new recipe `bt-qca-set-bdaddr.bb` that installs a systemd service and script to configure the Bluetooth Device Address (BDADDR) for uninitialized Qualcomm QCA Bluetooth SoCs at boot time. Ensures proper BDADDR configuration for Qualcomm Bluetooth chipsets that remain unconfigured when they do not have an OTP (One-Time Programmable) Bluetooth address programmed, preventing Bluetooth initialization failure. Signed-off-by: Janaki Ramaiah Thota <janaki.thota@oss.qualcomm.com>
| CORE_IMAGE_BASE_INSTALL += " \ | ||
| kernel-modules \ | ||
| packagegroup-qcom-utilities-filesystem-utils \ | ||
| bt-qca-set-bdaddr \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sort in alphabetical order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, will follow order and rise as separate commit.
| @@ -0,0 +1,172 @@ | |||
| #!/bin/sh | |||
| # Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to use QTI copyright here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, use:
Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
| S = "${UNPACKDIR}" | ||
|
|
||
| SYSTEMD_SERVICE:${PN} = "qca_set_bdaddr.service" | ||
| SYSTEMD_AUTO_ENABLE:${PN} = "enable" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure , will remove line SYSTEMD_AUTO_ENABLE:${PN} = "enable"
| SYSTEMD_AUTO_ENABLE:${PN} = "enable" | ||
|
|
||
| FILES:${PN} += " \ | ||
| ${systemd_unitdir}/system/qca_set_bdaddr.service \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also should be handled by systemd.bbclass
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes will remove this as well, thanks.
| ${systemd_unitdir}/system/qca_set_bdaddr.service \ | ||
| ${bindir}/qca_set_bdaddr.sh \ | ||
| " | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do_configure[noexec] = "1"
do_compile[noexec] = "1"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, will inform bitbake to skip do_configure and do_compile
| { | ||
| echo "public-addr $BDA" | ||
| sleep 1 | ||
| } | btmgmt >"$TMP_LOG" 2>&1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... You've ignored the previous review. Don't do that (and don't do this too).
btmgmt --index $index public-addr $BDA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, we have not ignored the previous comment, we tried as you suggested previously it is not working during every boot time, with current approach it is working as expected every time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly doesn't work? How?
If something, please reply to the comments instead of letting others have an assumption that they were missed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the feedback.
Root Cause:
When running btmgmt public-addr $BDA from service, strace shows:
epoll_ctl(3, EPOLL_CTL_ADD, 0, {events=0, data=0xaaaadcec4560}) = -1 EPERM (Operation not permitted)
This happens because epoll trying to add the stdin (fd 0:/dev/null/) of the current process to epoll to get the notification events but /dev/null does not support epoll (see epoll_ctl man page). epoll requires file descriptors that implement a .poll handler (e.g., sockets, pipes, tty). /dev/null is "always ready" and lacks this mechanism.
Verification:
When run script manually, stdin is /dev/pts/0 (a tty), which supports poll (tty_io.c),so it is working as expected
Fix:
To fix this, we changed stdin to tty which is epoll-able by adding below in systemd service unit file
StandardInput=tty
StandardOuput=journal
StandardError=journal
This ensures btmgmt has a valid stdin for epoll and works reliably across boots.
so now it is working without any pipe(|) as below
btmgmt public-addr $BDA
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please try the first patch from bluez/bluez#1751 ? If it works for you, please submit it to OE-Core.
| CORE_IMAGE_BASE_INSTALL += " \ | ||
| kernel-modules \ | ||
| packagegroup-qcom-utilities-filesystem-utils \ | ||
| bt-qca-set-bdaddr \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
| debug_log "Target BDA: $BDA" | ||
|
|
||
| # Validate BD_ADDR format (must be 6 octets) | ||
| if ! echo "$BDA" | grep -Eq '^([0-9A-F]{2}:){5}[0-9A-F]{2}$'; then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've spent significant efforts generating the address. Can it now be not properly formatted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it is properly formatted, it is just fail safe check, if you want remove ? we can remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to understand, why did you put it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This final check ensures we only pass an exactly formatted 6‑octet, colon‑delimited address to btmgmt, avoiding failures.
Example:
Properly formatted:
A0:BD:71:7C:AA:E8
Invalid formats blocked by check:
A0:BD:71:30:7C:AA:E8 <-- 7 octets (too many)
A0:BD:71:7C:AA:E8: <-- trailing colon
A0:BD:71:7C:A:E8 <-- one byte has only 1 hex digit
a0:bd:71:7c:aa:e8 <-- lowercase unless you allow [0-9A-Fa-f]
A0-BD-71-7C-AA-E8 <-- hyphens, not colons
A0:BD:71:7C:AA:E8\n <-- extra whitespace/characters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need to check the string you have just generated??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the next patch as suggested hashing serial number, will properly generate string and will remove this check
|
|
||
| # Extract exactly 6 hex characters (3 bytes) from the serial number. | ||
| # If fewer than 6 digits, pad with leading zero | ||
| dev_suffix_hex=$(printf "%06X" "$serial_number" | awk '{print substr($0, length($0)-5)}') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... You are using SoC serial number and then broadcasting it over the air.
The practice which was frowned upon some time ago for the privacy reasons.
I'd suggest at least hashing it.
Also, as you are not using an allocate BT address, it is called 'Random Static Address'. You must set two MSB bits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the valuable input, will include this in next patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the inputs. We have now implemented hashing of the SoC serial number for BDA formation. Since the generated BDA remains the same across boots and is fixed for that device, can we consider this as a public address?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference between public and random static addresses? Could you please check the docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bluetooth Low Energy (BLE) introduced random addresses to enhance privacy by allowing a device's address to change periodically, making long-term tracking more difficult. The significance of the bytes in random addresses depends on their specific type, indicated by the two most significant bits (MSBs) of the address:
Static Random Addresses: These addresses are randomly generated and usually remain the same until the device is powered off and on again. Their two MSBs are set to 0b11 (first hex digit is C, D, E, or F). The remaining 46 bits are a random value.
Setting a static address on the device does not transition it out of the un-configured state, which can impact BLE functionality even after the device has been configured
we can see below print setting public address only brings out the device from un-configured state
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main difference is that the Public address is allocated by the vendor, it should have corresponding OUI prefix and is guaranteed to be unique. As the address you are setting is generated rather than assigned, it can't be called 'Public'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the inputs. This implementation is specifically for development boards that do not have OTP burnt for BD-Address. Last year, Johan Hovald changed the code for QCOM SoCs without unique BDAs to mark them as unconfigured. This makes devices unusable until a unique BDA is set, which is only possible by opening the mgmt socket and setting the BDA.
Note: This behavior affects only QCOM SoCs.
Bluetooth: qca: generalise device address check The default device address apparently comes from the NVM configuration file and can differ quite a bit between controllers. Store the default address when parsing the configuration file and use it to determine whether the controller has been provisioned with an address. This makes sure that devices without a unique address start as unconfigured unless a valid address has been provided in the devicetree.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/bluetooth/btqca.c?id=dd336649ba89789c845618dcbc09867010aec673
We also checked other development boards like Raspberry Pi, which form the BDADDR from the serial number (3 bytes) combined with an OUI (3 bytes) and set the BDA on every boot using a Vendor Specific Command (VSC: write_bda). In their case, the device does not go into the unconfigured state, so they can send VSC successfully.
Reference: <https://github.com/RPi-Distro/pi-bluetooth/blob/master/usr/bin/bthelper>
Key snippet:
sh SERIAL=cat /proc/device-tree/serial-number | cut -c9-
B1=echo $SERIAL | cut -c3-4
B2=echo $SERIAL | cut -c5-6
B3=echo $SERIAL | cut -c7-8
BDADDR=printf '0x%02x 0x%02x 0x%02x 0xeb 0x27 0xb8' $((0x$B3 ^ 0xaa)) $((0x$B2 ^ 0xaa)) $((0x$B1 ^ 0xaa))
/usr/bin/hcitool -i $dev cmd 0x3f 0x001 $BDADDR ```
This shows that generating BDADDR from hardware identifiers is a common approach for boards without OTP, provided privacy measures (like hashing) Our method improves privacy by hashing the serial instead of using it directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't turn that address into a properly allocated Public. Notice the difference in how RPi generates the address and how it's done here. Your approach generates Random Static address. As such, corresponding bits must be set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please reach out the QCA team and get input from them if you can't follow corresponding standards.
|
| # Debug mode (set DEBUG=1 to enable) | ||
| DEBUG=${DEBUG:-0} | ||
|
|
||
| # Choose an official Qualcomm 3-byte Organizationally Unique Identifier(OUI) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove extra whitespace from the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
| return 1 | ||
| fi | ||
|
|
||
| sleep 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will remove this sleep, next commit,it is working as expected even without sleep.
|
|
||
| validate_and_set_bda() { | ||
| attempts=0 | ||
| max_attempts=10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need 10 attempts here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no particular reason, as you suggest will reduce it to 5 iterations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need several attempts at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we observed hciconfig is not immediately reliable during early boot during Firmware download (e.g., reports 00:00:00:00:00:00) until the controller transitions from DOWN RAW to a stable DOWN state after firmware initialization. To avoid false failures, we use a short, bounded retry loop to wait for a non‑zero stable state before attempting btmgmt public-addr and verification.
we will add comment for better readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have a sysfs uevent for that transition? If not, please add it instead of just waiting and retrying
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now we removed and retries and waiting, it is working fine
| DESCRIPTION = "Systemd service and script to generate Device Address (BDADDR) \ | ||
| for unconfigured Qualcomm QCA BT SoCs and program it using btmgmt at every boot." | ||
|
|
||
| inherit systemd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inherit allarch as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, will update architecture independent as well.
| install -D -m 0644 ${S}/qca_set_bdaddr.service \ | ||
| ${D}${systemd_unitdir}/system/qca_set_bdaddr.service | ||
|
|
||
| # Install script |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove both comments (no need, quite clear what the line is doing), and have both lines together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will remove
| trap 'rm -f "$TMP_LOG"' EXIT | ||
|
|
||
| # Debug mode (set DEBUG=1 to enable) | ||
| DEBUG=${DEBUG:-0} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just enable debug / verbose by default, logs will go to systemd, and it is always useful to have a more extensive log for the unit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, will enable debug logging.
Introduce a new recipe
bt-qca-set-bdaddr.bbthat installs a systemd service and script to configure the Bluetooth Device Address (BDADDR) for uninitialized Qualcomm QCA Bluetooth SoCs at boot time.Ensures proper BDADDR configuration for Qualcomm Bluetooth chipsets that remain unconfigured when they do not have an OTP (One-Time Programmable) Bluetooth address programmed, preventing Bluetooth initialization failure.