#!/bin/sh
# pocketds-bt-address — give the wcn7850 Bluetooth controller a usable
# public address so it registers with BlueZ.
#
# The Pocket DS's QCA wcn7850 reports an all-zero BD_ADDR (its NVM,
# qca/hmtnv20.bin, ships no per-device address and the board DT has no
# local-bd-address). hci_qca does NOT set HCI_QUIRK_USE_BDADDR_PROPERTY,
# so the kernel has no address to fall back on. The controller finishes
# firmware setup and HCI init, comes up, but hci_core.c immediately
# force-closes it because bdaddr == 00:00:00:00:00:00 and there is no
# static address (net/bluetooth/hci_core.c hci_power_on). Result: the
# controller never registers with mgmt -> bluetoothctl reports
# "No default controller available". WiFi (ath12k) is unaffected; it
# gets its MAC from board data.
#
# The management API's Set Public Address command (btmgmt public-addr)
# works on this not-yet-configured controller: it stores the address,
# flags the controller HCI_CONFIG, and re-powers it; on that re-open the
# kernel programs the address into the chip via qca_set_bdaddr, so
# READ_BD_ADDR now returns a valid address, the controller stays up and
# registers, and bluetoothd powers it on.
#
# We derive a stable, locally-administered address per device from
# /etc/machine-id (a hardcoded DT address would collide across the
# fleet). qca_set_bdaddr is RAM-only (not persisted in the chip), so
# this must run every boot -- the unit is a per-boot oneshot. The
# command requires CAP_NET_ADMIN (the mgmt socket must be "trusted"),
# i.e. it must run as root; the systemd unit does.
set -eu

DEV=hci0
SYS=/sys/class/bluetooth/$DEV

# The controller is registered by serdev early, then force-closed by the
# kernel a moment later; either way the sysfs node exists. Wait for it.
i=0
while [ ! -e "$SYS" ]; do
	i=$((i + 1))
	if [ "$i" -gt 60 ]; then
		echo "pocketds-bt-address: $DEV never appeared; nothing to do" >&2
		exit 0
	fi
	sleep 0.5
done

# Derive 5 stable octets from the machine-id (fall back to boot_id, then
# a constant, on the off chance machine-id isn't populated yet). Octet 1
# is fixed to 0x02: bit 1 set = locally administered, bit 0 clear =
# unicast.
ID=$(cat /etc/machine-id 2>/dev/null || true)
[ -n "$ID" ] || ID=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null || true)
[ -n "$ID" ] || ID=pocketds-fallback-id

H=$(printf '%s' "$ID" | sha256sum | cut -c1-10)
ADDR=$(printf '02:%s:%s:%s:%s:%s' \
	"$(printf '%s' "$H" | cut -c1-2)" \
	"$(printf '%s' "$H" | cut -c3-4)" \
	"$(printf '%s' "$H" | cut -c5-6)" \
	"$(printf '%s' "$H" | cut -c7-8)" \
	"$(printf '%s' "$H" | cut -c9-10)")

echo "pocketds-bt-address: configuring $DEV public address $ADDR" >&2

# At early boot the controller is still in HCI_SETUP (the kernel's own
# open of the zero-BD_ADDR controller, and the close that follows, are
# still in flight -- QCA "setup on UART is completed" lands ~5.4s in),
# and the mgmt layer answers Set Public Address with "Invalid Index"
# until that clears. Retry until the controller settles and accepts the
# address; succeed on the first "complete", and stop on any other
# terminal state (e.g. already configured on a re-run).
i=0
while [ "$i" -lt 90 ]; do
	i=$((i + 1))
	out=$(btmgmt --index "$DEV" public-addr "$ADDR" 2>&1 || true)
	case "$out" in
	*complete*)
		echo "pocketds-bt-address: $DEV configured ($ADDR)" >&2
		exit 0
		;;
	*"Invalid Index"*)
		sleep 1
		;;
	*)
		echo "pocketds-bt-address: $out" >&2
		exit 0
		;;
	esac
done

echo "pocketds-bt-address: gave up after $i tries (last: $out)" >&2
exit 0
