fp6-vendor-blobs 1-r4: build the ath11k board data from the stock modem partition
The device tree asks ath11k for the Fairphone_Gen_6 board data entry. Upstream linux-firmware's WCN6750 board-2.bin has no Fairphone entry, so the driver silently falls back to the generic board-id 255 data; the FP6 Plus does not associate on it (user report 2026-10-14), the FP6 happens to. The stock modem partition carries the device's own calibration as image/qca6750/bdwlan.elf. New manifest directive ath11k-bdf: copy that ELF and wrap it into a container under /lib/firmware/updates (searched first by the loader) that is upstream's board-2.bin with every entry kept plus one BOARD entry under the requested name. Verified structurally (ELF magic, size, the container magic), no hash pin: calibration data is OEM data and the WLAN firmware checks what it is given. If the base already carries the name the override is not built and a stale one is removed. --refresh re-derives it. The fragment re-probes ath11k as the fallback for a driver that probed before extraction (WPSS restart, the association comes back via NetworkManager). zstd joins the dependencies to read the compressed base. On the dev phone the built container is byte-identical to the one hand-made from the stock image by the reporter (sha256 186202ee...), built in 1.2 s.
This commit is contained in:
parent
2c317d9e2e
commit
04695c128a
3 changed files with 103 additions and 4 deletions
17
aports/device/fp6-vendor-blobs/30-ath11k-board-data.manifest
Normal file
17
aports/device/fp6-vendor-blobs/30-ath11k-board-data.manifest
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# WCN6750 WLAN board data (calibration, Qualcomm/Fairphone) - taken from the
|
||||||
|
# stock modem partition on-device instead of being shipped (fp6 repo
|
||||||
|
# journal/base/ 2026-10-14, journal/blobs/). The device tree asks ath11k for
|
||||||
|
# the Fairphone_Gen_6 entry; upstream linux-firmware's board-2.bin has none,
|
||||||
|
# the driver silently falls back to generic data, and the FP6 Plus does not
|
||||||
|
# associate on that (user report 2026-10-14; the FP6 happens to work either
|
||||||
|
# way). The container built here is upstream's file with every entry kept
|
||||||
|
# plus the device's own bdwlan.elf under the requested name; it lives in
|
||||||
|
# /lib/firmware/updates, which the loader searches first. Active slot first,
|
||||||
|
# the other slot as fallback; no hash pin - calibration data is OEM data and
|
||||||
|
# the WLAN firmware verifies what it is given.
|
||||||
|
ath11k-bdf modem_a,modem_b image/qca6750/bdwlan.elf bus=ahb,qmi-chip-id=1,qmi-board-id=255,variant=Fairphone_Gen_6 /usr/lib/firmware/ath11k/WCN6750/hw1.0/board-2.bin.zst /usr/lib/firmware/updates/ath11k/WCN6750/hw1.0/board-2.bin
|
||||||
|
# Fallback for a driver that probed before extraction (the service runs
|
||||||
|
# pre-coldplug, so this normally no-ops): re-probing ath11k restarts the
|
||||||
|
# WPSS remoteproc and the firmware reads the new container; the association
|
||||||
|
# comes back through NetworkManager.
|
||||||
|
rebind platform 17110040.wifi
|
||||||
|
|
@ -21,19 +21,20 @@
|
||||||
maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"
|
maintainer="Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>"
|
||||||
pkgname=fp6-vendor-blobs
|
pkgname=fp6-vendor-blobs
|
||||||
pkgver=1
|
pkgver=1
|
||||||
pkgrel=3
|
pkgrel=4
|
||||||
pkgdesc="On-device extraction of vendor blobs from the stock Android partitions"
|
pkgdesc="On-device extraction of vendor blobs from the stock Android partitions"
|
||||||
url="https://forgejo.catcrafts.net/Catcrafts/fp6-img"
|
url="https://forgejo.catcrafts.net/Catcrafts/fp6-img"
|
||||||
arch="noarch"
|
arch="noarch"
|
||||||
license="MIT"
|
license="MIT"
|
||||||
# fallback mapper for when the initramfs didn't map the dynamic partitions
|
# fallback mapper for when the initramfs didn't map the dynamic partitions
|
||||||
depends="make-dynpart-mappings"
|
depends="make-dynpart-mappings zstd"
|
||||||
install="$pkgname.post-upgrade"
|
install="$pkgname.post-upgrade"
|
||||||
options="!check"
|
options="!check"
|
||||||
source="
|
source="
|
||||||
fp6-vendor-blobs-extract
|
fp6-vendor-blobs-extract
|
||||||
fp6-vendor-blobs.service
|
fp6-vendor-blobs.service
|
||||||
fp6-vendor-blobs.preset
|
fp6-vendor-blobs.preset
|
||||||
|
30-ath11k-board-data.manifest
|
||||||
"
|
"
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
|
|
@ -58,11 +59,13 @@ package() {
|
||||||
# extracted, no sound card. See journal/blobs/ 2026-08-28.
|
# extracted, no sound card. See journal/blobs/ 2026-08-28.
|
||||||
install -Dm644 "$srcdir"/fp6-vendor-blobs.preset \
|
install -Dm644 "$srcdir"/fp6-vendor-blobs.preset \
|
||||||
"$pkgdir"/usr/lib/systemd/system-preset/50-fp6-vendor-blobs.preset
|
"$pkgdir"/usr/lib/systemd/system-preset/50-fp6-vendor-blobs.preset
|
||||||
mkdir -p "$pkgdir"/usr/share/fp6-vendor-blobs/manifest.d
|
install -Dm644 "$srcdir"/30-ath11k-board-data.manifest \
|
||||||
|
"$pkgdir"/usr/share/fp6-vendor-blobs/manifest.d/30-ath11k-board-data.manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
sha512sums="
|
sha512sums="
|
||||||
e9618b4a1ccbe0913a608b32a1a9e87337e3da79a3d7ccc7f49292ae9aaa9406d2ea9817d63b3cfff2c4346b80dd2476c10725f4f0f95c859acd17b22ec03a16 fp6-vendor-blobs-extract
|
2aa740a2c4999d7e09c328664fe9c03e8a4ac8ddf1215b2d2b2755b12383dce873b9eafbce4706e87405cf2b0aa2e29ed582f31d9ee085346f911c11269c1f22 fp6-vendor-blobs-extract
|
||||||
b4c290095d9f39515378dfef08de720ce49324210342aa13c131dfce1103785e796e6f821f0c659671a4c44b46f466ce0e03f11f216fdcdee2a99db5e7970800 fp6-vendor-blobs.service
|
b4c290095d9f39515378dfef08de720ce49324210342aa13c131dfce1103785e796e6f821f0c659671a4c44b46f466ce0e03f11f216fdcdee2a99db5e7970800 fp6-vendor-blobs.service
|
||||||
9e79dd0aed13f11a71282aa24b2a26331e85c105e25ab0c0fed6189b8c300769a5f4308b18b91d9855868d658ad3a57c03e26c9b11bd27fd5e03f9a5decbbd6a fp6-vendor-blobs.preset
|
9e79dd0aed13f11a71282aa24b2a26331e85c105e25ab0c0fed6189b8c300769a5f4308b18b91d9855868d658ad3a57c03e26c9b11bd27fd5e03f9a5decbbd6a fp6-vendor-blobs.preset
|
||||||
|
1265a0f78aa4d9566e4a9f7827c8cead3a1848dc8bbd4288165085e87a824ea0c0940a16e5426f74074fcb29c1fddccf76f6dd3b16efa7ddffc2c437b9531439 30-ath11k-board-data.manifest
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#
|
#
|
||||||
# file <partition[,partition...]> <path-in-partition> <dest> <sha256>
|
# file <partition[,partition...]> <path-in-partition> <dest> <sha256>
|
||||||
# mbn <partition[,partition...]> <dir-in-partition> <name> <dest> <sha256|->
|
# mbn <partition[,partition...]> <dir-in-partition> <name> <dest> <sha256|->
|
||||||
|
# ath11k-bdf <partition[,partition...]> <path-in-partition> <entry-name> <base> <dest>
|
||||||
# rebind <bus> <device>
|
# rebind <bus> <device>
|
||||||
#
|
#
|
||||||
# Partition lists are tried in the order written, except that on an A/B
|
# Partition lists are tried in the order written, except that on an A/B
|
||||||
|
|
@ -47,6 +48,21 @@
|
||||||
# itself and refuses a damaged or foreign image (one flipped byte ->
|
# itself and refuses a damaged or foreign image (one flipped byte ->
|
||||||
# ERROR_ELF_SIGNATURE_ERROR, measured 2026-09-03). The sha256 of what was
|
# ERROR_ELF_SIGNATURE_ERROR, measured 2026-09-03). The sha256 of what was
|
||||||
# installed is logged either way.
|
# installed is logged either way.
|
||||||
|
# ath11k-bdf: the WLAN board data (calibration) the device tree asks ath11k
|
||||||
|
# for by name, e.g. bus=ahb,qmi-chip-id=1,qmi-board-id=255,variant=Fairphone_Gen_6.
|
||||||
|
# Upstream linux-firmware's board-2.bin carries no Fairphone entry, so the
|
||||||
|
# driver silently falls back to the generic board-id data; the FP6 Plus does
|
||||||
|
# not associate on it (user report 2026-10-14, fp6 repo journal/base/). The
|
||||||
|
# stock modem partition has the device's own file (image/qca6750/bdwlan.elf).
|
||||||
|
# This directive copies it and wraps it into a container at <dest> (the
|
||||||
|
# loader searches /lib/firmware/updates first): <base> (board-2.bin or
|
||||||
|
# board-2.bin.zst, every upstream entry kept byte for byte) + one BOARD
|
||||||
|
# entry named <entry-name> holding the ELF. Verified structurally (ELF
|
||||||
|
# magic, sane size, the container's own magic), never by a pinned hash:
|
||||||
|
# calibration data is OEM data and the WLAN firmware checks it itself.
|
||||||
|
# When <base> already carries <entry-name> (upstream caught up) nothing is
|
||||||
|
# built and a stale <dest> is removed so the base is used again.
|
||||||
|
# --refresh re-derives the dest like mbn (a new base after an upgrade).
|
||||||
# rebind: if this fragment's run extracted at least one file, unbind and
|
# rebind: if this fragment's run extracted at least one file, unbind and
|
||||||
# re-probe <device> on <bus> so the consuming driver picks the file up
|
# re-probe <device> on <bus> so the consuming driver picks the file up
|
||||||
# in the same boot. Unconditional on purpose: a still-bound consumer may
|
# in the same boot. Unconditional on purpose: a still-bound consumer may
|
||||||
|
|
@ -257,6 +273,62 @@ extract_mbn() { # <partition,...> <dir-in-partition> <name> <dest> <sha256|->
|
||||||
fail "no listed partition ($parts) yields $rname with sha256 $want - $dest NOT installed$kept"
|
fail "no listed partition ($parts) yields $rname with sha256 $want - $dest NOT installed$kept"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# One little-endian u32 as four raw bytes (busybox printf, octal escapes).
|
||||||
|
le32() { # <value>
|
||||||
|
printf "\\$(printf %03o $(($1 & 255)))\\$(printf %03o $((($1 >> 8) & 255)))\\$(printf %03o $((($1 >> 16) & 255)))\\$(printf %03o $((($1 >> 24) & 255)))"
|
||||||
|
}
|
||||||
|
pad4() { # <length> -> bytes of zero padding to the next multiple of 4, on stdout
|
||||||
|
n=$(( (4 - $1 % 4) % 4 ))
|
||||||
|
[ "$n" -gt 0 ] && head -c "$n" /dev/zero
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
extract_ath11k_bdf() { # <partition,...> <path-in-partition> <entry-name> <base> <dest>
|
||||||
|
parts=$1 bsrc=$2 bname=$3 bbase=$4 bdest=$5
|
||||||
|
[ -f "$bbase" ] || fail "ath11k-bdf: base container $bbase missing"
|
||||||
|
braw="$bdest.fp6-base.$$"
|
||||||
|
mkdir -p "${bdest%/*}" || fail "cannot create ${bdest%/*}"
|
||||||
|
case "$bbase" in
|
||||||
|
*.zst) zstd -dcq "$bbase" > "$braw" 2>/dev/null || { rm -f "$braw"; fail "ath11k-bdf: cannot decompress $bbase"; } ;;
|
||||||
|
*) cp "$bbase" "$braw" || { rm -f "$braw"; fail "ath11k-bdf: cannot read $bbase"; } ;;
|
||||||
|
esac
|
||||||
|
[ "$(head -c 16 "$braw")" = "QCA-ATH11K-BOARD" ] || { rm -f "$braw"; fail "ath11k-bdf: $bbase is not a QCA-ATH11K-BOARD container"; }
|
||||||
|
if grep -a -q -F "$bname" "$braw"; then
|
||||||
|
rm -f "$braw"
|
||||||
|
log "ath11k-bdf: $bbase already carries $bname - no override needed"
|
||||||
|
[ -e "$bdest" ] && { rm -f "$bdest"; log "removed the redundant $bdest"; }
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
for part in $(order_parts "$parts"); do
|
||||||
|
mount_part "$part" || { log "$part: not mountable, trying next"; continue; }
|
||||||
|
elf="$MNT/$bsrc"
|
||||||
|
[ -f "$elf" ] || { log "$part: no $bsrc, trying next"; continue; }
|
||||||
|
esz=$(fsize "$elf")
|
||||||
|
[ "$(hex4 "$elf")" = 7f454c46 ] || { log "$part:$bsrc: not an ELF image, trying next"; continue; }
|
||||||
|
[ "$esz" -ge 4096 ] && [ "$esz" -le $((1024 * 1024)) ] || { log "$part:$bsrc: $esz bytes is not a board data file, trying next"; continue; }
|
||||||
|
nlen=${#bname}
|
||||||
|
npad=$(( (nlen + 3) / 4 * 4 ))
|
||||||
|
dpad=$(( (esz + 3) / 4 * 4 ))
|
||||||
|
tmp="$bdest.fp6-extract.$$"
|
||||||
|
# container = base + BOARD IE { NAME IE, DATA IE }, each IE padded to 4
|
||||||
|
{
|
||||||
|
cat "$braw"
|
||||||
|
le32 0; le32 $((8 + npad + 8 + dpad))
|
||||||
|
le32 0; le32 "$nlen"; printf '%s' "$bname"; pad4 "$nlen"
|
||||||
|
le32 1; le32 "$esz"; cat "$elf"; pad4 "$esz"
|
||||||
|
} > "$tmp" 2>/dev/null || { rm -f "$tmp"; fail "ath11k-bdf: writing $tmp failed"; }
|
||||||
|
want=$(( $(fsize "$braw") + 8 + 8 + npad + 8 + dpad ))
|
||||||
|
[ "$(fsize "$tmp")" -eq "$want" ] || { rm -f "$tmp" "$braw"; fail "ath11k-bdf: $tmp is $(fsize "$tmp") bytes, expected $want"; }
|
||||||
|
got=$(sha256sum "$tmp" | awk '{print $1}')
|
||||||
|
chmod 644 "$tmp" && mv "$tmp" "$bdest" || { rm -f "$tmp" "$braw"; fail "installing $bdest failed"; }
|
||||||
|
rm -f "$braw"
|
||||||
|
log "built $bdest = $bbase + $bname from $part:$bsrc ($esz bytes, sha256 $got)"
|
||||||
|
return 0
|
||||||
|
done
|
||||||
|
rm -f "$braw"
|
||||||
|
kept=
|
||||||
|
[ -e "$bdest" ] && kept=" (the existing file is left in place)"
|
||||||
|
fail "no listed partition ($parts) yields a well-formed $bsrc - $bdest NOT installed$kept"
|
||||||
|
}
|
||||||
extract() { # <partition,...> <path-in-partition> <dest> <sha256>
|
extract() { # <partition,...> <path-in-partition> <dest> <sha256>
|
||||||
parts=$1 src=$2 dest=$3 want=$4
|
parts=$1 src=$2 dest=$3 want=$4
|
||||||
for part in $(order_parts "$parts"); do
|
for part in $(order_parts "$parts"); do
|
||||||
|
|
@ -320,6 +392,7 @@ for f in "$MANIFEST_DIR"/*.manifest; do
|
||||||
case "$kind" in
|
case "$kind" in
|
||||||
file) [ -e "$c" ] || missing=1 ;;
|
file) [ -e "$c" ] || missing=1 ;;
|
||||||
mbn) [ -e "$d" ] && [ -z "$REFRESH" ] || missing=1 ;;
|
mbn) [ -e "$d" ] && [ -z "$REFRESH" ] || missing=1 ;;
|
||||||
|
ath11k-bdf) [ -e "$e" ] && [ -z "$REFRESH" ] || missing=1 ;;
|
||||||
esac
|
esac
|
||||||
done < "$f"
|
done < "$f"
|
||||||
done
|
done
|
||||||
|
|
@ -345,6 +418,12 @@ for f in "$MANIFEST_DIR"/*.manifest; do
|
||||||
extract_mbn "$a" "$b" "$c" "$d" "$e" </dev/null
|
extract_mbn "$a" "$b" "$c" "$d" "$e" </dev/null
|
||||||
extracted=1
|
extracted=1
|
||||||
;;
|
;;
|
||||||
|
ath11k-bdf)
|
||||||
|
[ -n "$e" ] || fail "$f: malformed ath11k-bdf line"
|
||||||
|
[ -e "$e" ] && [ -z "$REFRESH" ] && continue
|
||||||
|
extract_ath11k_bdf "$a" "$b" "$c" "$d" "$e" </dev/null
|
||||||
|
extracted=1
|
||||||
|
;;
|
||||||
rebind)
|
rebind)
|
||||||
[ -n "$b" ] || fail "$f: malformed rebind line"
|
[ -n "$b" ] || fail "$f: malformed rebind line"
|
||||||
rebinds="$rebinds $a/$b"
|
rebinds="$rebinds $a/$b"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue