Photonicat2: AIC8800 and kernel 6.18.35 update
This post is a full technical report and reproducible runbook for our Photonicat2 update. We tested the vendor-provided kernel and AIC8800 USB Wi-Fi driver bundle recommended in the support thread, preserved a local vendor package snapshot, verified the U-Boot boot chain and DKMS state before reboot, upgraded from kernel 6.12.45 to 6.18.35 and AIC8800 3.0 to 5.0, and then checked that USB Wi-Fi came back after reboot. The post includes the exact commands, pre-reboot gates, post-reboot results, remaining warnings, and rollback notes so that the procedure can be reviewed, reproduced, or audited.
Status: done
Started: 2026-06-29 09:41
Finished: 2026-06-29 11:48
Scope: Preserving the Photonicat vendor snapshot, checking the boot-chain and DKMS, updating kernel 6.12.45 to 6.18.35 and AIC8800 3.0 to 5.0, with USB Wi-Fi verification after reboot.
Related: server:photonicat2-debian | file:/srv/nvme_data/work/photonicat2/drivers/vendor-20260629 | file:/etc/kernel/postinst.d/zz-update-kernel | file:/etc/modprobe.d/99-aic8800-loglevel.conf | url:https://dl.photonicat.com/repos/linux-mainline/photonicat2/ | url:https://photonicat.com/forums/technical-support/debian-aic8800-usb-driver-warnings-trace_printk-regulatory-db-and-recommended-driver-version
0. Implementation map
- Result: the update completed successfully; the system booted into
6.18.35, USB Wi-Fi wlxb841d9ebe0c5 came up and connected to SSID TT.
- The AIC8800 stack was updated to
5.0+git20260123.5f7be68d-4; DKMS was built for 6.12.45 and 6.18.35.
- Excessive journaling is absent:
LOGINFO=0, LOGDEBUG=0, LOGTRACE=0, LOGDATA=0; the previous memcpy/FORTIFY and invalid cmd: lvl_adj_5g_chan_* did not recur after reboot.
-
linux-image-*-dbg and the downloaded linux-libc-dev_* were not installed; the old linux-image-6.12.45 and linux-headers-6.12.45 were kept as rollback scope.
- Remaining items:
trace_printk, permissive regulatory warning, and three vendor AICWFDBG(LOGERROR) Read FW mem messages persist; these are residual vendor warnings, not rollback blockers.
1. Initial state
Before the update:
Kernel: 6.12.45
Architecture: aarch64
aic8800-firmware: 3.0+git20240327.3561b08f-7
aic8800-usb-dkms: 3.0+git20240327.3561b08f-7
The working USB Wi-Fi used the modules:
/lib/modules/6.12.45/updates/dkms/aic8800_fdrv_usb.ko
/lib/modules/6.12.45/updates/dkms/aic_load_fw_usb.ko
The loglevel parameters had already been pinned through the actual DKMS names:
# /etc/modprobe.d/99-aic8800-loglevel.conf
options aic_load_fw_usb aicwf_dbg_level=1
options aic8800_fdrv_usb aicwf_dbg_level=1
Photonicat support recommended checking the new kernel and AIC8800 driver from the vendor repository:
https://dl.photonicat.com/repos/linux-mainline/photonicat2/
The point of the decision: do not continue local DKMS patching of the old aic8800-usb/3.0, but check the official kernel + headers + firmware + DKMS bundle.
2. Boot-chain nuance
Photonicat2 boots through U-Boot, not through ordinary GRUB selection. /boot/boot.cmd loads unversioned files:
load mmc ${devnum}:1 ${fdt_addr_r} rockchip.dtb
load mmc ${devnum}:1 ${kernel_addr_r} Image
load mmc ${devnum}:1 ${ramdisk_addr_r} initrd.img
booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
Therefore, before reboot, it is necessary to check not only the installed packages, but also the active boot artifacts:
/boot/Image
/boot/initrd.img
/boot/rockchip.dtb
The system has a local hook:
/etc/kernel/postinst.d/zz-update-kernel
It updates the U-Boot artifacts after kernel package installation:
zcat ${LATEST_KERNEL_PATH} > /boot/Image.new
mv /boot/Image.new /boot/Image
mkimage ... -d ${LATEST_KERNEL_INITRD_PATH} /boot/initrd.img.new
mv /boot/initrd.img.new /boot/initrd.img
cp ${LATEST_KERNEL_DTB_PATH} /boot/rockchip.dtb
mkimage was installed:
/usr/bin/mkimage
mkimage version 2025.01
3. Vendor snapshot
All vendor repository files were saved locally:
/srv/nvme_data/work/photonicat2/drivers/vendor-20260629
Download command:
set -euo pipefail
BASE="/srv/nvme_data/work/photonicat2/drivers"
DEST="$BASE/vendor-20260629"
SRC_URL="https://dl.photonicat.com/repos/linux-mainline/photonicat2/"
SUPPORT_URL="https://photonicat.com/forums/technical-support/debian-aic8800-usb-driver-warnings-trace_printk-regulatory-db-and-recommended-driver-version"
mkdir -p "$DEST"
cd "$DEST"
cat > README.txt <<EOF
Photonicat2 vendor driver/kernel snapshot
=========================================
Snapshot date:
$(date -u '+%Y-%m-%d %H:%M:%S UTC')
Local path:
$DEST
Source repository:
$SRC_URL
Support thread:
$SUPPORT_URL
Purpose:
Vendor package snapshot for Photonicat2 kernel and AIC8800 USB Wi-Fi driver upgrade.
Important boot note:
This device boots unversioned /boot/Image, /boot/initrd.img and /boot/rockchip.dtb through U-Boot.
Before reboot after a kernel upgrade, verify these active boot artifacts explicitly.
EOF
wget \
--recursive \
--level=1 \
--no-parent \
--no-host-directories \
--no-directories \
--reject 'index.html*' \
--execute robots=off \
--server-response \
--timestamping \
"$SRC_URL" \
2>&1 | tee "$DEST/download.log"
find . -maxdepth 1 -type f -name '*.deb' -printf '%P\0' \
| sort -z \
| xargs -0 sha256sum \
> "$DEST/SHA256SUMS.vendor.txt"
{
echo
echo "Downloaded inventory:"
find . -maxdepth 1 -type f -printf '%f\t%s bytes\n' | sort
echo
echo "SHA256SUMS.vendor.txt:"
cat SHA256SUMS.vendor.txt
} >> README.txt
Saved .deb files:
aic8800-firmware_5.0+git20260123.5f7be68d-4_all.deb
aic8800-usb-dkms_5.0+git20260123.5f7be68d-4_all.deb
linux-headers-6.18.33_6.18.33-2_arm64.deb
linux-headers-6.18.35_6.18.35-2_arm64.deb
linux-image-6.18.33_6.18.33-2_arm64.deb
linux-image-6.18.33-dbg_6.18.33-2_arm64.deb
linux-image-6.18.35_6.18.35-2_arm64.deb
linux-image-6.18.35-dbg_6.18.35-2_arm64.deb
linux-libc-dev_6.18.33-2_arm64.deb
linux-libc-dev_6.18.35-2_arm64.deb
Only the following were used for the completed update:
aic8800-firmware_5.0+git20260123.5f7be68d-4_all.deb
aic8800-usb-dkms_5.0+git20260123.5f7be68d-4_all.deb
linux-headers-6.18.35_6.18.35-2_arm64.deb
linux-image-6.18.35_6.18.35-2_arm64.deb
Not installed:
linux-image-6.18.33*
linux-image-6.18.35-dbg_6.18.35-2_arm64.deb
linux-libc-dev_6.18.33-2_arm64.deb
linux-libc-dev_6.18.35-2_arm64.deb
4. Checking packages without installation
Before install, metadata, control scripts, and payload were checked:
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
cd "$DEST"
for f in ./*.deb; do
echo "===== $f ====="
dpkg-deb -f "$f" Package Version Architecture Depends 2>/dev/null || true
echo
done
INSPECT="$DEST/inspect"
mkdir -p "$INSPECT"
for deb in ./*.deb; do
base="$(basename "$deb" .deb)"
mkdir -p "$INSPECT/$base/control" "$INSPECT/$base/data"
dpkg-deb -I "$deb" > "$INSPECT/$base/info.txt"
dpkg-deb -e "$deb" "$INSPECT/$base/control"
dpkg-deb -c "$deb" > "$INSPECT/$base/contents.txt"
dpkg-deb -x "$deb" "$INSPECT/$base/data"
done
For linux-image-6.18.35, the following were confirmed to be present:
/boot/vmlinuz-6.18.35
/usr/lib/linux-image-6.18.35/rockchip/rk3576-photonicat2.dtb
For aic8800-usb-dkms, standard dh_dkms scripts and dependencies were confirmed:
Package: aic8800-usb-dkms
Version: 5.0+git20260123.5f7be68d-4
Depends: aic8800-firmware (= 5.0+git20260123.5f7be68d-4), dkms (>= 2.1.0.0)
dkms.conf builds three modules:
aic_load_fw_usb.ko
aic8800_fdrv_usb.ko
aic_btusb_usb.ko
Result: module names for Wi-Fi remained compatible with /etc/modprobe.d/99-aic8800-loglevel.conf.
5. Backup before install
Before the changes, /boot, the baseline, and AIC paths were saved:
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$DEST/install-logs-$(date '+%Y%m%d-%H%M%S')"
BACK="$DEST/preinstall-backup-$(date '+%Y%m%d-%H%M%S')"
mkdir -p "$LOG" "$BACK"
{
echo '=== date ==='
date -Is
echo
echo '=== uname ==='
uname -a
echo
echo '=== dpkg baseline ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers|linux-libc-dev|dkms|u-boot-tools|initramfs-tools' || true
echo
echo '=== dkms baseline ==='
dkms status || true
echo
echo '=== boot cmd ==='
cat /boot/boot.cmd || true
echo
echo '=== boot files ==='
ls -lah /boot
echo
echo '=== loaded AIC/cfg80211 modules ==='
lsmod | grep -E 'aic|cfg80211' || true
echo
echo '=== current AIC module files ==='
find /lib/modules/$(uname -r)/updates/dkms -maxdepth 1 -type f -name '*aic*' -print 2>/dev/null || true
} | tee "$BACK/baseline.txt" "$LOG/01-baseline-before-install.log"
cp -a /boot "$BACK/boot"
find /usr/src -maxdepth 1 -type d -name 'aic8800-usb*' -print > "$BACK/aic-src-paths.txt" || true
find /lib/modules/$(uname -r)/updates/dkms -maxdepth 1 -type f -name '*aic*' -print > "$BACK/aic-module-paths.txt" || true
tar --ignore-failed-read -czf "$BACK/aic8800-current-src-and-modules.tar.gz" \
-T "$BACK/aic-src-paths.txt" \
-T "$BACK/aic-module-paths.txt" \
2>&1 | tee "$BACK/tar.log" || true
Package-level rollback was preserved: the old linux-image-6.12.45 and linux-headers-6.12.45 were not removed.
6. Installation
The installation was performed in stages: first AIC firmware/DKMS, then headers, then the kernel image. This reduces the risk of ending up with both a broken DKMS and a modified boot-chain at the same time.
6.1. Simulation
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
cd "$DEST"
apt-get -s install \
./aic8800-firmware_5.0+git20260123.5f7be68d-4_all.deb \
./aic8800-usb-dkms_5.0+git20260123.5f7be68d-4_all.deb \
./linux-headers-6.18.35_6.18.35-2_arm64.deb \
./linux-image-6.18.35_6.18.35-2_arm64.deb \
2>&1 | tee "$LOG/00-apt-simulation-all.log"
Before continuing, check that the simulation does not remove:
linux-image-6.12.45
initramfs-tools
cryptsetup
dkms
u-boot-tools
6.2. Phase A: firmware + DKMS
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
cd "$DEST"
DEBIAN_FRONTEND=noninteractive apt-get install -y \
./aic8800-firmware_5.0+git20260123.5f7be68d-4_all.deb \
./aic8800-usb-dkms_5.0+git20260123.5f7be68d-4_all.deb \
2>&1 | tee "$LOG/10-install-aic8800-firmware-dkms.log"
Check after Phase A:
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
{
echo '=== dpkg aic ==='
dpkg -l | grep -E 'aic8800'
echo
echo '=== dkms aic8800 ==='
dkms status -m aic8800-usb || true
echo
echo '=== current kernel AIC modules on disk ==='
find "/lib/modules/$(uname -r)/updates/dkms" -maxdepth 1 -type f -name '*aic*' \
-print -exec modinfo {} \; 2>/dev/null \
| grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
echo
echo '=== dkms make logs ==='
find /var/lib/dkms/aic8800-usb -name make.log -type f -print -exec tail -80 {} \; 2>/dev/null || true
} | tee "$LOG/11-check-aic8800-dkms-current-kernel.log"
Result on the current kernel 6.12.45:
aic8800-usb/5.0+git20260123.5f7be68d-4, 6.12.45, aarch64: installed
The build completed with exit code: 0. The make.log contained warnings sprintf argument 3 overlaps destination object 'aic_fw_path' [-Wrestrict]; they did not stop DKMS.
6.3. Phase B: headers 6.18.35
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
cd "$DEST"
DEBIAN_FRONTEND=noninteractive apt-get install -y \
./linux-headers-6.18.35_6.18.35-2_arm64.deb \
2>&1 | tee "$LOG/20-install-linux-headers-6.18.35.log"
Check after headers:
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
{
echo '=== dkms status after headers ==='
dkms status -m aic8800-usb || true
echo
echo '=== 6.18.35 AIC modules if already built ==='
find /lib/modules/6.18.35/updates/dkms -maxdepth 1 -type f -name '*aic*' \
-print -exec modinfo {} \; 2>/dev/null \
| grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
echo
echo '=== dkms make logs ==='
find /var/lib/dkms/aic8800-usb -name make.log -type f -print -exec tail -80 {} \; 2>/dev/null || true
} | tee "$LOG/21-check-dkms-after-headers.log"
In the actual update, modules for 6.18.35 had not appeared yet after installing headers. This was an acceptable intermediate state. The build for 6.18.35 happened after installing linux-image-6.18.35.
6.4. Phase C: kernel image 6.18.35
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
cd "$DEST"
DEBIAN_FRONTEND=noninteractive apt-get install -y \
./linux-image-6.18.35_6.18.35-2_arm64.deb \
2>&1 | tee "$LOG/30-install-linux-image-6.18.35.log"
After image installation, DKMS was built for both kernels:
aic8800-usb/5.0+git20260123.5f7be68d-4, 6.12.45, aarch64: installed
aic8800-usb/5.0+git20260123.5f7be68d-4, 6.18.35, aarch64: installed
7. Gate before reboot
Reboot is allowed only after the full gate.
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
LOG="$(find "$DEST" -maxdepth 1 -type d -name 'install-logs-*' | sort | tail -1)"
{
echo '=== installed packages ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers|linux-libc-dev|dkms|u-boot-tools|initramfs-tools' || true
echo
echo '=== dkms status ==='
dkms status || true
echo
echo '=== 6.18.35 AIC module files ==='
find /lib/modules/6.18.35/updates/dkms -maxdepth 1 -type f -name '*aic*' \
-print -exec modinfo {} \; 2>/dev/null \
| grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
echo
echo '=== boot files ==='
ls -lah /boot/Image /boot/initrd.img /boot/vmlinuz-6.18.35 /boot/initrd.img-6.18.35 /boot/rockchip.dtb 2>/dev/null || true
echo
echo '=== /boot/Image embedded version ==='
grep -a -m1 'Linux version' /boot/Image || true
echo
echo '=== initrd formats ==='
file /boot/initrd.img /boot/initrd.img-6.18.35 2>/dev/null || true
echo
echo '=== dumpimage /boot/initrd.img ==='
dumpimage -l /boot/initrd.img 2>/dev/null || true
echo
echo '=== DTB compare ==='
if cmp -s /boot/rockchip.dtb /usr/lib/linux-image-6.18.35/rockchip/rk3576-photonicat2.dtb; then
echo 'DTB OK: /boot/rockchip.dtb matches rk3576-photonicat2.dtb'
else
echo 'DTB MISMATCH'
sha256sum /boot/rockchip.dtb /usr/lib/linux-image-6.18.35/rockchip/rk3576-photonicat2.dtb || true
fi
echo
echo '=== modprobe aic config ==='
grep -R --line-number -E 'aic|aicwf_dbg_level' /etc/modprobe.d /lib/modprobe.d 2>/dev/null || true
echo
echo '=== initramfs contains aic modprobe config? ==='
lsinitramfs /boot/initrd.img-6.18.35 2>/dev/null | grep -E 'aic|99-aic8800|modprobe' || true
} | tee "$LOG/40-pre-reboot-gate.log"
Minimum conditions for reboot:
/boot/Image contains target kernel version
/boot/initrd.img is u-boot legacy uImage
/boot/rockchip.dtb matches /usr/lib/linux-image-<kernel>/rockchip/rk3576-photonicat2.dtb
aic8800-usb DKMS is installed for target kernel
/lib/modules/<kernel>/updates/dkms contains aic_load_fw_usb.ko, aic8800_fdrv_usb.ko, aic_btusb_usb.ko
/etc/modprobe.d/99-aic8800-loglevel.conf is present in target initramfs
The actual gate passed:
/boot/Image contains Linux version 6.18.35
/boot/initrd.img: u-boot legacy uImage
/boot/initrd.img-6.18.35: Zstandard compressed data
DTB OK: /boot/rockchip.dtb matches rk3576-photonicat2.dtb
/etc/modprobe.d/99-aic8800-loglevel.conf present in initramfs
After that, the following was executed:
sync
sleep 2
systemctl reboot
8. Check after reboot
Check command:
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
POST="$DEST/postreboot-check-$(date '+%Y%m%d-%H%M%S').log"
{
echo '=== date ==='
date -Is
echo
echo '=== kernel ==='
uname -a
uname -r
echo
echo '=== packages ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers' || true
echo
echo '=== dkms ==='
dkms status -m aic8800-usb || true
echo
echo '=== loaded modules ==='
lsmod | grep -E 'aic|cfg80211|bluetooth|btusb' || true
echo
echo '=== modinfo target modules ==='
modinfo aic_load_fw_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|parm:.*aicwf_dbg_level' || true
modinfo aic8800_fdrv_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
modinfo aic_btusb_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|depends:' || true
echo
echo '=== runtime debug params ==='
for p in \
/sys/module/aic_load_fw/parameters/aicwf_dbg_level \
/sys/module/aic8800_fdrv/parameters/aicwf_dbg_level \
/sys/module/aic_btusb/parameters/aicwf_dbg_level
do
[ -e "$p" ] && printf '%s=' "$p" && cat "$p"
done
echo
echo '=== network ==='
ip link
iw dev 2>/dev/null || true
echo
echo '=== AIC journal filtered ==='
journalctl -b -k --no-pager \
| grep -Ei 'aic|aicwf|trace_printk|regulatory|cfg80211|memcpy|fortify|LOGINFO|LOGDEBUG|LOGTRACE|LOGDATA|LOGERROR|invalid cmd' \
| tee /tmp/aic8800-journal-current.txt || true
echo
echo '=== counters ==='
for x in LOGINFO LOGDEBUG LOGTRACE LOGDATA LOGERROR trace_printk regulatory memcpy FORTIFY 'invalid cmd'; do
printf '%-16s ' "$x"
grep -ci "$x" /tmp/aic8800-journal-current.txt || true
done
} | tee "$POST"
echo "$POST"
Actual result after reboot:
Linux photonicat2-debian 6.18.35 #2 SMP PREEMPT Thu Jun 18 18:12:26 JST 2026 aarch64 GNU/Linux
6.18.35
Packages after reboot:
aic8800-firmware 5.0+git20260123.5f7be68d-4
aic8800-usb-dkms 5.0+git20260123.5f7be68d-4
linux-image-6.12.45
linux-image-6.18.35
linux-headers-6.12.45
linux-headers-6.18.35
DKMS:
aic8800-usb/5.0+git20260123.5f7be68d-4, 6.12.45, aarch64: installed
aic8800-usb/5.0+git20260123.5f7be68d-4, 6.18.35, aarch64: installed
Runtime loglevel:
/sys/module/aic_load_fw/parameters/aicwf_dbg_level=1
/sys/module/aic8800_fdrv/parameters/aicwf_dbg_level=1
Wi-Fi:
wlxb841d9ebe0c5: UP, LOWER_UP
SSID: TT
channel 1 (2412 MHz), width: 20 MHz
Journal counters after reboot:
LOGINFO 0
LOGDEBUG 0
LOGTRACE 0
LOGDATA 0
LOGERROR 3
trace_printk 1
regulatory 3
memcpy 0
FORTIFY 0
invalid cmd 0
Remaining vendor messages:
AICWFDBG(LOGERROR) Read FW mem: 00120198
AICWFDBG(LOGERROR) 120198=177390
AICWFDBG(LOGERROR) 1201a0=177c00
** trace_printk() being used. Allocating extra memory. **
** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **
9. Standalone runbook for the next update
This block is intended for independently repeating the procedure. The commands are intended for a root shell. If the work is done through SSH and USB Wi-Fi is the only access path, reboot only after a successful pre-reboot gate. Otherwise, it is possible to get correctly installed packages and an inaccessible device at the same time.
9.1. Prepare variables and download vendor snapshot
set -euo pipefail
BASE="/srv/nvme_data/work/photonicat2/drivers"
STAMP="$(date '+%Y%m%d')"
DEST="$BASE/vendor-$STAMP"
SRC_URL="https://dl.photonicat.com/repos/linux-mainline/photonicat2/"
SUPPORT_URL="https://photonicat.com/forums/technical-support/debian-aic8800-usb-driver-warnings-trace_printk-regulatory-db-and-recommended-driver-version"
mkdir -p "$DEST"
cd "$DEST"
cat > README.txt <<EOF
Photonicat2 vendor driver/kernel snapshot
=========================================
Snapshot date:
$(date -u '+%Y-%m-%d %H:%M:%S UTC')
Local path:
$DEST
Source repository:
$SRC_URL
Support thread:
$SUPPORT_URL
Purpose:
Vendor package snapshot for Photonicat2 kernel and AIC8800 USB Wi-Fi driver upgrade.
Boot note:
Photonicat2 boots unversioned /boot/Image, /boot/initrd.img and /boot/rockchip.dtb through U-Boot.
Before reboot after kernel upgrade, verify these active boot artifacts explicitly.
EOF
wget \
--recursive \
--level=1 \
--no-parent \
--no-host-directories \
--no-directories \
--reject 'index.html*' \
--execute robots=off \
--server-response \
--timestamping \
"$SRC_URL" \
2>&1 | tee "$DEST/download.log"
find . -maxdepth 1 -type f -name '*.deb' -printf '%P\0' \
| sort -z \
| xargs -0 sha256sum \
> SHA256SUMS.vendor.txt
{
echo
echo "Downloaded inventory:"
find . -maxdepth 1 -type f -printf '%f\t%s bytes\n' | sort
echo
echo "SHA256SUMS.vendor.txt:"
cat SHA256SUMS.vendor.txt
} >> README.txt
9.2. Choose target kernel and target packages
For the known-good update, 6.18.35 and AIC 5.0+git20260123.5f7be68d-4 were used. For the next vendor drop, first check which versions were actually downloaded:
set -euo pipefail
cd "$DEST"
echo '=== available kernel images ==='
find . -maxdepth 1 -type f -name 'linux-image-[0-9]*_*.deb' ! -name '*-dbg_*' -printf '%f\n' | sort -V
echo
echo '=== available headers ==='
find . -maxdepth 1 -type f -name 'linux-headers-[0-9]*_*.deb' -printf '%f\n' | sort -V
echo
echo '=== available AIC packages ==='
find . -maxdepth 1 -type f \( -name 'aic8800-firmware_*.deb' -o -name 'aic8800-usb-dkms_*.deb' \) -printf '%f\n' | sort -V
Choose the target kernel. For the completed update:
TARGET_KERNEL="6.18.35"
Select files:
set -euo pipefail
cd "$DEST"
FW_DEB="$(find . -maxdepth 1 -type f -name 'aic8800-firmware_*_all.deb' -printf '%f\n' | sort -V | tail -1)"
DKMS_DEB="$(find . -maxdepth 1 -type f -name 'aic8800-usb-dkms_*_all.deb' -printf '%f\n' | sort -V | tail -1)"
HDR_DEB="$(find . -maxdepth 1 -type f -name "linux-headers-${TARGET_KERNEL}_*_arm64.deb" -printf '%f\n' | sort -V | tail -1)"
IMG_DEB="$(find . -maxdepth 1 -type f -name "linux-image-${TARGET_KERNEL}_*_arm64.deb" ! -name '*-dbg_*' -printf '%f\n' | sort -V | tail -1)"
for f in "$FW_DEB" "$DKMS_DEB" "$HDR_DEB" "$IMG_DEB"; do
[ -n "$f" ] && [ -f "$f" ] || { echo "missing target package: $f" >&2; exit 1; }
done
printf 'FW_DEB=%s\nDKMS_DEB=%s\nHDR_DEB=%s\nIMG_DEB=%s\n' "$FW_DEB" "$DKMS_DEB" "$HDR_DEB" "$IMG_DEB"
FW_VER="$(dpkg-deb -f "$FW_DEB" Version)"
DKMS_VER="$(dpkg-deb -f "$DKMS_DEB" Version)"
[ "$FW_VER" = "$DKMS_VER" ] || { echo "AIC firmware/DKMS version mismatch: $FW_VER vs $DKMS_VER" >&2; exit 1; }
Do not install linux-image-*-dbg unless there is a separate kernel debugging task. Do not install linux-libc-dev_* for an ordinary kernel boot and AIC8800 DKMS check.
9.3. Capture baseline
set -euo pipefail
cd "$DEST"
LOG="$DEST/install-logs-$(date '+%Y%m%d-%H%M%S')"
BACK="$DEST/preinstall-backup-$(date '+%Y%m%d-%H%M%S')"
mkdir -p "$LOG" "$BACK"
{
echo '=== date ==='
date -Is
echo
echo '=== kernel ==='
uname -a
uname -r
echo
echo '=== packages ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers|linux-libc-dev|dkms|u-boot-tools|initramfs-tools' || true
echo
echo '=== dkms ==='
dkms status || true
echo
echo '=== boot.cmd ==='
cat /boot/boot.cmd || true
echo
echo '=== boot files ==='
ls -lah /boot
echo
echo '=== AIC modules current kernel ==='
find /lib/modules/$(uname -r)/updates/dkms -maxdepth 1 -type f -name '*aic*' -print 2>/dev/null || true
} | tee "$BACK/baseline.txt" "$LOG/01-baseline-before-install.log"
cp -a /boot "$BACK/boot"
9.4. Check package payload before installation
set -euo pipefail
cd "$DEST"
INSPECT="$DEST/inspect"
mkdir -p "$INSPECT"
for deb in "$FW_DEB" "$DKMS_DEB" "$HDR_DEB" "$IMG_DEB"; do
base="$(basename "$deb" .deb)"
mkdir -p "$INSPECT/$base/control" "$INSPECT/$base/data"
dpkg-deb -I "$deb" > "$INSPECT/$base/info.txt"
dpkg-deb -e "$deb" "$INSPECT/$base/control"
dpkg-deb -c "$deb" > "$INSPECT/$base/contents.txt"
dpkg-deb -x "$deb" "$INSPECT/$base/data"
done
IMG_BASE="$(basename "$IMG_DEB" .deb)"
DKMS_BASE="$(basename "$DKMS_DEB" .deb)"
grep -nE "\\./boot/vmlinuz-${TARGET_KERNEL}|rk3576-photonicat2\\.dtb" "$INSPECT/$IMG_BASE/contents.txt" || {
echo "target linux-image does not contain expected vmlinuz or Photonicat2 DTB" >&2
exit 1
}
cat "$INSPECT/$DKMS_BASE/control/control"
sed -n '1,220p' "$INSPECT/$DKMS_BASE/control/postinst" 2>/dev/null || true
find "$INSPECT/$DKMS_BASE/data" -name dkms.conf -type f -print -exec sed -n '1,220p' {} \;
Manually check in dkms.conf that the destination modules remain as expected:
aic_load_fw_usb
aic8800_fdrv_usb
aic_btusb_usb
If the Wi-Fi module names changed, /etc/modprobe.d/99-aic8800-loglevel.conf must be reviewed before reboot.
9.5. Check boot-chain hooks
set -euo pipefail
{
echo '=== relevant kernel/initramfs hooks ==='
grep -R --line-number -E 'mkimage|boot\.scr|boot\.cmd|/boot/Image|initrd\.img|rockchip|update-initramfs' \
/etc/kernel /etc/initramfs-tools /usr/share/initramfs-tools 2>/dev/null || true
echo
echo '=== mkimage ==='
command -v mkimage || true
mkimage -V 2>/dev/null || true
} | tee "$LOG/02-boot-chain-hooks.log"
On Photonicat2, the hook /etc/kernel/postinst.d/zz-update-kernel must be present. If the hook is absent, the kernel package may install, but U-Boot will continue loading the old /boot/Image or an incorrect initrd. In this case, do not reboot until the active boot artifacts have been built and checked.
9.6. Simulation and staged install
set -euo pipefail
cd "$DEST"
apt-get -s install \
"./$FW_DEB" \
"./$DKMS_DEB" \
"./$HDR_DEB" \
"./$IMG_DEB" \
2>&1 | tee "$LOG/00-apt-simulation-all.log"
Stop conditions in the simulation:
REMOVE linux-image-<current>
REMOVE initramfs-tools
REMOVE cryptsetup
REMOVE dkms
REMOVE u-boot-tools
unmet dependencies
held broken packages
If the simulation is normal:
DEBIAN_FRONTEND=noninteractive apt-get install -y \
"./$FW_DEB" \
"./$DKMS_DEB" \
2>&1 | tee "$LOG/10-install-aic8800-firmware-dkms.log"
dkms status -m aic8800-usb | tee "$LOG/11-dkms-after-aic.log"
DEBIAN_FRONTEND=noninteractive apt-get install -y \
"./$HDR_DEB" \
2>&1 | tee "$LOG/20-install-headers.log"
dkms status -m aic8800-usb | tee "$LOG/21-dkms-after-headers.log"
DEBIAN_FRONTEND=noninteractive apt-get install -y \
"./$IMG_DEB" \
2>&1 | tee "$LOG/30-install-image.log"
9.7. Hard gate before reboot
set -euo pipefail
GATE="$LOG/40-pre-reboot-gate.log"
{
echo '=== packages ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers|linux-libc-dev|dkms|u-boot-tools|initramfs-tools' || true
echo
echo '=== dkms ==='
dkms status -m aic8800-usb || true
echo
echo '=== target AIC modules ==='
find "/lib/modules/$TARGET_KERNEL/updates/dkms" -maxdepth 1 -type f -name '*aic*' \
-print -exec modinfo {} \; 2>/dev/null \
| grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
echo
echo '=== boot files ==='
ls -lah /boot/Image /boot/initrd.img "/boot/vmlinuz-$TARGET_KERNEL" "/boot/initrd.img-$TARGET_KERNEL" /boot/rockchip.dtb 2>/dev/null || true
echo
echo '=== Image version ==='
grep -a -m1 'Linux version' /boot/Image || true
echo
echo '=== initrd format ==='
file /boot/initrd.img "/boot/initrd.img-$TARGET_KERNEL" 2>/dev/null || true
dumpimage -l /boot/initrd.img 2>/dev/null || true
echo
echo '=== DTB compare ==='
if cmp -s /boot/rockchip.dtb "/usr/lib/linux-image-$TARGET_KERNEL/rockchip/rk3576-photonicat2.dtb"; then
echo 'DTB OK'
else
echo 'DTB MISMATCH'
sha256sum /boot/rockchip.dtb "/usr/lib/linux-image-$TARGET_KERNEL/rockchip/rk3576-photonicat2.dtb" || true
fi
echo
echo '=== modprobe config ==='
grep -R --line-number -E 'aic|aicwf_dbg_level' /etc/modprobe.d /lib/modprobe.d 2>/dev/null || true
echo
echo '=== target initramfs modprobe config ==='
lsinitramfs "/boot/initrd.img-$TARGET_KERNEL" 2>/dev/null | grep -E 'aic|99-aic8800|modprobe' || true
} | tee "$GATE"
Reboot is forbidden if at least one condition is not met:
dkms status does not show target kernel as installed
/lib/modules/<target>/updates/dkms has no aic_load_fw_usb.ko or aic8800_fdrv_usb.ko
/boot/Image does not contain target kernel version
/boot/initrd.img is not U-Boot legacy uImage
/boot/rockchip.dtb does not match rk3576-photonicat2.dtb from target linux-image
/etc/modprobe.d/99-aic8800-loglevel.conf is missing from target initramfs
If the gate is green:
sync
sleep 2
systemctl reboot
9.8. If the hook did not update /boot before reboot
The normal path is to let /etc/kernel/postinst.d/zz-update-kernel do this automatically. This fallback is needed only if the package installed, but the pre-reboot gate shows an old /boot/Image or an incorrect /boot/initrd.img.
set -euo pipefail
cp -a /boot "$BACK/boot-before-manual-active-artifacts"
update-initramfs -u -k "$TARGET_KERNEL"
zcat "/boot/vmlinuz-$TARGET_KERNEL" > /boot/Image.new
mv /boot/Image.new /boot/Image
mkimage -A arm -O linux -T ramdisk -C none -a 0 -e 0 \
-d "/boot/initrd.img-$TARGET_KERNEL" \
/boot/initrd.img.new
mv /boot/initrd.img.new /boot/initrd.img
cp "/usr/lib/linux-image-$TARGET_KERNEL/rockchip/rk3576-photonicat2.dtb" /boot/rockchip.dtb
After manual rebuild, it is mandatory to repeat the hard gate from the previous section. Do not reboot without repeating the gate.
9.9. Post-reboot check
set -euo pipefail
DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-$(date '+%Y%m%d')"
[ -d "$DEST" ] || DEST="/srv/nvme_data/work/photonicat2/drivers/vendor-20260629"
POST="$DEST/postreboot-check-$(date '+%Y%m%d-%H%M%S').log"
{
echo '=== date ==='
date -Is
echo
echo '=== kernel ==='
uname -a
uname -r
echo
echo '=== packages ==='
dpkg -l | grep -E 'aic8800|linux-image|linux-headers' || true
echo
echo '=== dkms ==='
dkms status -m aic8800-usb || true
echo
echo '=== loaded modules ==='
lsmod | grep -E 'aic|cfg80211|bluetooth|btusb' || true
echo
echo '=== modinfo AIC ==='
modinfo aic_load_fw_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|parm:.*aicwf_dbg_level' || true
modinfo aic8800_fdrv_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|depends:|parm:.*aicwf_dbg_level' || true
modinfo aic_btusb_usb 2>/dev/null | grep -E 'filename:|version:|name:|vermagic:|depends:' || true
echo
echo '=== runtime debug params ==='
for p in \
/sys/module/aic_load_fw/parameters/aicwf_dbg_level \
/sys/module/aic8800_fdrv/parameters/aicwf_dbg_level \
/sys/module/aic_btusb/parameters/aicwf_dbg_level
do
[ -e "$p" ] && printf '%s=' "$p" && cat "$p"
done
echo
echo '=== network ==='
ip link
iw dev 2>/dev/null || true
echo
echo '=== AIC journal filtered ==='
journalctl -b -k --no-pager \
| grep -Ei 'aic|aicwf|trace_printk|regulatory|cfg80211|memcpy|fortify|LOGINFO|LOGDEBUG|LOGTRACE|LOGDATA|LOGERROR|invalid cmd' \
| tee /tmp/aic8800-journal-current.txt || true
echo
echo '=== counters ==='
for x in LOGINFO LOGDEBUG LOGTRACE LOGDATA LOGERROR trace_printk regulatory memcpy FORTIFY 'invalid cmd'; do
printf '%-16s ' "$x"
grep -ci "$x" /tmp/aic8800-journal-current.txt || true
done
} | tee "$POST"
echo "$POST"
Success criteria:
uname -r equals target kernel
USB Wi-Fi interface is present and associated
DKMS installed for target kernel
modinfo points to /lib/modules/<target>/updates/dkms/*
aic_load_fw and aic8800_fdrv aicwf_dbg_level are 1
LOGINFO/LOGDEBUG/LOGTRACE/LOGDATA are 0
10. Rollback notes
The old kernel was kept as a package-level rollback:
linux-image-6.12.45
linux-headers-6.12.45
But there is no automatic GRUB-like selection. After an unsuccessful reboot, recovery may require local/serial access and restoring unversioned files from backup:
/boot/Image
/boot/initrd.img
/boot/rockchip.dtb
/boot/boot.cmd
/boot/boot.scr
If the gate failed before reboot, active boot artifacts can be rolled back from the saved backup without rebooting:
set -euo pipefail
# example: BACK=/srv/nvme_data/work/photonicat2/drivers/vendor-20260629/preinstall-backup-YYYYmmdd-HHMMSS
[ -n "${BACK:-}" ]
[ -d "$BACK/boot" ]
cp -a "$BACK/boot/Image" /boot/Image
cp -a "$BACK/boot/initrd.img" /boot/initrd.img
cp -a "$BACK/boot/rockchip.dtb" /boot/rockchip.dtb
cp -a "$BACK/boot/boot.cmd" /boot/boot.cmd 2>/dev/null || true
cp -a "$BACK/boot/boot.scr" /boot/boot.scr 2>/dev/null || true
sync
If reboot has already been performed and the device does not come online, further rollback can be done only through a local console or serial. Until stabilization, do not remove 6.12.45 and do not clean up the vendor snapshot.
11. Final assessment
The update has positive expected value and was left in operation. It solved the main operational problem and removed part of the vendor defects in the old driver stack. The remaining trace_printk, permissive regulatory warning, and three LOGERROR messages are classified as residual vendor warnings, not as a regression.