From 9b0593f527a18710760011a6f002488b1cf543a1 Mon Sep 17 00:00:00 2001 From: Janaki Ramaiah Thota Date: Wed, 3 Dec 2025 15:29:03 +0530 Subject: [PATCH] recipes-connectivity: Add bt-qca-set-bdaddr recipe to setup BT BDADDR 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 --- .../qca-set-bdaddr/bt-qca-set-bdaddr.bb | 34 ++++ .../files/qca_set_bdaddr.service | 12 ++ .../qca-set-bdaddr/files/qca_set_bdaddr.sh | 172 ++++++++++++++++++ recipes-products/images/qcom-minimal-image.bb | 1 + 4 files changed, 219 insertions(+) create mode 100644 recipes-connectivity/qca-set-bdaddr/bt-qca-set-bdaddr.bb create mode 100644 recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.service create mode 100644 recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.sh diff --git a/recipes-connectivity/qca-set-bdaddr/bt-qca-set-bdaddr.bb b/recipes-connectivity/qca-set-bdaddr/bt-qca-set-bdaddr.bb new file mode 100644 index 00000000..196965cb --- /dev/null +++ b/recipes-connectivity/qca-set-bdaddr/bt-qca-set-bdaddr.bb @@ -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" + +FILES:${PN} += " \ + ${systemd_unitdir}/system/qca_set_bdaddr.service \ + ${bindir}/qca_set_bdaddr.sh \ +" + +do_install() { + # Install systemd unit + install -D -m 0644 ${S}/qca_set_bdaddr.service \ + ${D}${systemd_unitdir}/system/qca_set_bdaddr.service + + # Install script + install -D -m 0755 ${S}/qca_set_bdaddr.sh \ + ${D}${bindir}/qca_set_bdaddr.sh +} + +RDEPENDS:${PN} += " \ + bluez5-noinst-tools \ +" diff --git a/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.service b/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.service new file mode 100644 index 00000000..4081be73 --- /dev/null +++ b/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.service @@ -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 diff --git a/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.sh b/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.sh new file mode 100644 index 00000000..038c2a18 --- /dev/null +++ b/recipes-connectivity/qca-set-bdaddr/files/qca_set_bdaddr.sh @@ -0,0 +1,172 @@ +#!/bin/sh +# Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. +# 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} + +# Choose an official Qualcomm 3-byte Organizationally Unique Identifier(OUI) +# 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)}') + 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 + log_error "Invalid BDA format: $BDA" + return 1 + fi + + sleep 1 + + # Set BD address using btmgmt + { + echo "public-addr $BDA" + sleep 1 + } | btmgmt >"$TMP_LOG" 2>&1 + 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 + + 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 +} diff --git a/recipes-products/images/qcom-minimal-image.bb b/recipes-products/images/qcom-minimal-image.bb index 1a529e48..a12bddef 100644 --- a/recipes-products/images/qcom-minimal-image.bb +++ b/recipes-products/images/qcom-minimal-image.bb @@ -12,6 +12,7 @@ REQUIRED_DISTRO_FEATURES = "pam systemd" CORE_IMAGE_BASE_INSTALL += " \ kernel-modules \ packagegroup-qcom-utilities-filesystem-utils \ + bt-qca-set-bdaddr \ " # Default root password: oelinux123