-
Notifications
You must be signed in to change notification settings - Fork 39
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| SUMMARY = "Boot-time BDADDR setup for Qualcomm QCA Bluetooth device" | ||
| 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 | ||
|
|
||
| SRC_URI = " \ | ||
| file://qca_set_bdaddr.service \ | ||
| file://qca_set_bdaddr.sh \ | ||
| " | ||
|
|
||
| S = "${UNPACKDIR}" | ||
|
|
||
| SYSTEMD_SERVICE:${PN} = "qca_set_bdaddr.service" | ||
| SYSTEMD_AUTO_ENABLE:${PN} = "enable" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a default
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure , will remove line SYSTEMD_AUTO_ENABLE:${PN} = "enable" |
||
|
|
||
| FILES:${PN} += " \ | ||
| ${systemd_unitdir}/system/qca_set_bdaddr.service \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also should be handled by systemd.bbclass
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes will remove this as well, thanks. |
||
| ${bindir}/qca_set_bdaddr.sh \ | ||
| " | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, will inform bitbake to skip do_configure and do_compile |
||
| do_install() { | ||
| # Install systemd unit | ||
| install -D -m 0644 ${S}/qca_set_bdaddr.service \ | ||
| ${D}${systemd_unitdir}/system/qca_set_bdaddr.service | ||
|
|
||
| # Install script | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, will remove |
||
| install -D -m 0755 ${S}/qca_set_bdaddr.sh \ | ||
| ${D}${bindir}/qca_set_bdaddr.sh | ||
| } | ||
|
|
||
| RDEPENDS:${PN} += " \ | ||
| bluez5-noinst-tools \ | ||
| " | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [Unit] | ||
| Description=Set Bluetooth Device Address (BDADDR) for unconfigured Qualcomm BT chipsets. | ||
| ConditionPathExists=/sys/devices/soc0/serial_number | ||
| Requires=bluetooth.service | ||
| After=bluetooth.service | ||
|
|
||
| [Service] | ||
| Type=oneshot | ||
| ExecStart=+/usr/bin/qca_set_bdaddr.sh | ||
|
|
||
| [Install] | ||
| WantedBy=bluetooth.target |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| #!/bin/sh | ||
| # Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to use QTI copyright here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, use:
|
||
| # SPDX-License-Identifier: BSD-3-Clause-Clear | ||
|
|
||
| # Exit on error | ||
| set -eu | ||
|
|
||
| # Configurable paths | ||
| SERIAL_FILE=${SERIAL_FILE:-/sys/devices/soc0/serial_number} | ||
| TMP_LOG=$(mktemp /tmp/btmgmt_output.XXXXXX) | ||
|
|
||
| # Cleanup on exit | ||
| trap 'rm -f "$TMP_LOG"' EXIT | ||
|
|
||
| # Debug mode (set DEBUG=1 to enable) | ||
| DEBUG=${DEBUG:-0} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, will enable debug logging. |
||
|
|
||
| # Choose an official Qualcomm 3-byte Organizationally Unique Identifier(OUI) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove extra whitespace from the end.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
| # See IEEE/lookup citations: https://standards-oui.ieee.org/. | ||
| OUI="A0:BD:71" | ||
|
|
||
| debug_log() { | ||
| if [ "$DEBUG" -eq 1 ]; then | ||
| echo "[DEBUG] $*" >&2 | ||
| fi | ||
| } | ||
|
|
||
| # Helper to log errors with timestamps | ||
| log_error() { | ||
| echo "$(date '+%Y-%m-%d %H:%M:%S') ERROR: $*" >&2 | ||
| } | ||
|
|
||
| # Verify required external commands | ||
| REQUIRED_CMDS="printf sed grep hciconfig bluetoothctl btmgmt awk" | ||
| for cmd in $REQUIRED_CMDS; do | ||
| if ! command -v "$cmd" >/dev/null 2>&1; then | ||
| log_error "Required command '$cmd' not found" | ||
| exit 1 | ||
| fi | ||
| done | ||
|
|
||
| # Ensure script is run as root | ||
| if [ "$(id -u)" -ne 0 ]; then | ||
| log_error "This script must be run as root" | ||
| exit 1 | ||
| fi | ||
|
|
||
| set_bda() { | ||
| # Read serial number | ||
| if [ ! -f "$SERIAL_FILE" ]; then | ||
| log_error "Serial number file not found: $SERIAL_FILE" | ||
| return 1 | ||
| fi | ||
|
|
||
| serial_number=$(cat "$SERIAL_FILE") | ||
| debug_log "Serial number: $serial_number" | ||
|
|
||
| if [ -z "$serial_number" ]; then | ||
| log_error "Serial number is empty" | ||
| return 1 | ||
| fi | ||
|
|
||
| case "$serial_number" in | ||
| *[!0-9]*) | ||
| log_error "Serial number is not numeric" | ||
| return 1 | ||
| ;; | ||
| esac | ||
|
|
||
| # 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)}') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. Also, as you are not using an allocate BT address, it is called 'Random Static Address'. You must set two MSB bits.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for the valuable input, will include this in next patch.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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'.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. 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: Reference: <https://github.com/RPi-Distro/pi-bluetooth/blob/master/usr/bin/bthelper> Key snippet: 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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_log "Padded hex serial number: $dev_suffix_hex" | ||
|
|
||
| # Validate non-empty and not all zeros | ||
| if [ -z "$dev_suffix_hex" ] || [ "$dev_suffix_hex" = "000000" ]; then | ||
| log_error "Invalid device suffix: $dev_suffix_hex" | ||
| return 1 | ||
| fi | ||
|
|
||
| # Format as colon-separated bytes | ||
| dev_suffix=$(echo "$dev_suffix_hex" | sed 's/\(..\)/\1:/g;s/:$//') | ||
| debug_log "Formatted serial number: $dev_suffix" | ||
|
|
||
| # Build BD_ADDR | ||
| BDA="$OUI:$dev_suffix" | ||
| 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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to understand, why did you put it here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. Invalid formats blocked by check:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need to check the string you have just generated??
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| log_error "Invalid BDA format: $BDA" | ||
| return 1 | ||
| fi | ||
|
|
||
| sleep 1 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
|
|
||
| # Set BD address using btmgmt | ||
| { | ||
| echo "public-addr $BDA" | ||
| sleep 1 | ||
| } | btmgmt >"$TMP_LOG" 2>&1 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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).
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly doesn't work? How?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the feedback. Root Cause: Verification: Fix: This ensures btmgmt has a valid stdin for epoll and works reliably across boots. so now it is working without any pipe(|) as below
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| btmgmt_status=$? | ||
|
|
||
| if [ "$btmgmt_status" -ne 0 ]; then | ||
| log_error "btmgmt failed (exit $btmgmt_status)" | ||
| cat "$TMP_LOG" >&2 | ||
| return 1 | ||
| fi | ||
|
|
||
| if ! grep -q "public-addr $BDA" "$TMP_LOG"; then | ||
| log_error "btmgmt output does not confirm address set" | ||
| cat "$TMP_LOG" >&2 | ||
| return 1 | ||
| fi | ||
|
|
||
| debug_log "BD address successfully set to $BDA" | ||
| return 0 | ||
| } | ||
|
|
||
| validate_and_set_bda() { | ||
| attempts=0 | ||
| max_attempts=10 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need 10 attempts here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no particular reason, as you suggest will reduce it to 5 iterations
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need several attempts at all?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now we removed and retries and waiting, it is working fine |
||
|
|
||
| while [ "$attempts" -lt "$max_attempts" ]; do | ||
| hciconfig_output=$(hciconfig) | ||
| echo "$hciconfig_output" | grep -q 'BD Address' || { | ||
| log_error "hciconfig output missing BD Address" | ||
| return 1 | ||
| } | ||
|
|
||
| bd_address=$(echo "$hciconfig_output" | grep 'BD Address' | awk '{print $3}') | ||
| debug_log "Current BD Address: $bd_address" | ||
| unconfigured=$(echo "$hciconfig_output" | grep -o 'DOWN RAW') | ||
| configured=$(echo "$hciconfig_output" | grep -o 'DOWN') | ||
|
|
||
| # Check if the BD Address is 00:00:00:00:00:00 | ||
| if [ "$bd_address" = "00:00:00:00:00:00" ]; then | ||
| sleep 1 | ||
| elif [[ "$unconfigured" == "DOWN RAW" ]]; then | ||
| break | ||
| elif [[ "$configured" == "DOWN" ]]; then | ||
| echo "BD-Address already configured!!" | ||
| return 0 | ||
| else | ||
| break | ||
| fi | ||
|
|
||
| sleep 1 | ||
| attempts=$((attempts + 1)) | ||
| done | ||
|
|
||
| if [ "$attempts" -ge "$max_attempts" ]; then | ||
| log_error "Max attempts reached without configuring BD address" | ||
| return 1 | ||
| fi | ||
|
|
||
| if ! set_bda; then | ||
| log_error "set_bda failed" | ||
| return 1 | ||
| fi | ||
|
|
||
| return 0 | ||
| } | ||
|
|
||
| # Check bluetoothctl output | ||
| bluetoothctl_output=$(bluetoothctl show || true) | ||
| echo "$bluetoothctl_output" | grep -q "No default controller available" && { | ||
| if ! validate_and_set_bda; then | ||
| log_error "validate_and_set_bda failed" | ||
| exit 1 | ||
| fi | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ REQUIRED_DISTRO_FEATURES = "pam systemd" | |
| CORE_IMAGE_BASE_INSTALL += " \ | ||
| kernel-modules \ | ||
| packagegroup-qcom-utilities-filesystem-utils \ | ||
| bt-qca-set-bdaddr \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sort in alphabetical order.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, will follow order and rise as separate commit.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Separate commit
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
| " | ||
|
|
||
| # Default root password: oelinux123 | ||
|
|
||

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.