跳到內容

USB 音訊核心(CM5)

CM5 用原版核心時,ESP32-S3 USB 音效卡能正常識別,aplay -l 裡能看到,PipeWire 也把它選成預設輸出, 播放看起來在“進行”——但喇叭和耳機口完全沒有聲音。鍵盤等其他 USB 裝置都正常。

  • ESP32-S3 音效卡是一個全速(12 Mbit/s)USB 音訊類裝置,掛在板上的高速 FE2.1 hub 後面。 hub 的事務轉換器(TT)負責轉換,所以主機必須把每個音訊包拆成一串按 USB 微幀對齊的 split 事務 (start-split / complete-split)發出去。
  • CM5 模組的 USB 引腳只連到 dwc2 控制器,而 dwc2 把微幀時序交給驅動軟體來安排。
  • 當 TT 排程表為空時,dwc2_uframe_schedule_split() 會把等時 OUT 的 split 排在相對微幀 0,於是 第一個攜帶資料的 start-split 實際落在上一幀的微幀 7。包大於 188 位元組時,剩下的 start-split 落到下一幀,同一個全速事務就橫跨了一個高速幀邊界。
  • Linux 的 EHCI 驅動明確禁止這種排法(提交 8c05dc598e5b,“USB: EHCI: No SSPLIT allowed in uframe 7”), 而且從不在微幀 5 之後開始全速等時事務。FE2.1 的 TT 會悄無聲息地丟棄這樣的事務:每個 URB 都 “成功”完成、沒有任何錯誤位,但全速匯流排上什麼也沒收到。

drivers/usb/dwc2/hcd_queue.c 裡 24 行的改動:dwc2_uframe_schedule_split() 不再為等時 OUT split 選擇相對微幀 0。

上游 PR raspberrypi/linux#7554——“usb: dwc2: host: No ISOC OUT start-split allowed in uframe 7”(撰寫本文時仍為 open)
相關、已合入 #6936 和 #7290 修的是中斷 split(hub 下的鍵盤滑鼠)——不涵蓋等時音訊
我們的建置 樹莓派 rpi-6.18.y(6.18.44,基線 ce0873cb5e0c)+ 該補丁,bcm2712_defconfig,CONFIG_LOCALVERSION="-dwc2fix",make bindeb-pkg
下載 release kernel-dwc2fix-6.18.44-4——linux-image + linux-headers 的 deb 和 SHA256SUMS

等這個 PR 合入並隨樹莓派核心包釋出後,原版核心就能出聲,本頁也就不再需要了。

make bindeb-pkg 打出來的 .deb 沒有 Raspberry Pi OS 的啟動鉤子,所以啟動檔案要手動放置—— 和映像建置時執行的步驟一樣:

Terminal window
REL=https://github.com/TypixNode/pi-gen/releases/download/kernel-dwc2fix-6.18.44-4
mkdir -p ~/dwc2fix && cd ~/dwc2fix
curl -fLO "$REL/SHA256SUMS"
for f in $(awk '{print $2}' SHA256SUMS); do curl -fLO "$REL/$f"; done
sha256sum -c SHA256SUMS
sudo dpkg -i linux-image-*.deb linux-headers-*.deb
KVER=$(ls /lib/modules | grep dwc2fix | head -n1)
sudo update-initramfs -c -k "$KVER"
# boot files: uncompressed kernel image + initramfs under our own names
if [ "$(od -An -tx1 -N2 /boot/vmlinuz-$KVER | tr -d ' ')" = 1f8b ]; then
zcat "/boot/vmlinuz-$KVER" | sudo tee /boot/firmware/kernel-dwc2fix.img >/dev/null
else
sudo cp "/boot/vmlinuz-$KVER" /boot/firmware/kernel-dwc2fix.img
fi
sudo cp "/boot/initrd.img-$KVER" /boot/firmware/initramfs-dwc2fix
# device trees and overlays built with this kernel
sudo cp /usr/lib/linux-image-$KVER/broadcom/bcm2712*.dtb /boot/firmware/
sudo cp /usr/lib/linux-image-$KVER/overlays/* /boot/firmware/overlays/

然後只讓 CM5 用它啟動——追加到 /boot/firmware/config.txt:

[cm5]
kernel=kernel-dwc2fix.img
initramfs initramfs-dwc2fix followkernel
[all]

重啟並檢查:

Terminal window
uname -r # 6.18.44-dwc2fix+
speaker-test -c 2 -t sine -l 1
  • 回退到原版核心:註釋掉 kernel= 那行即可;原版的 kernel_2712.img 一直在,並繼續由 apt 更新。
  • apt 升級原版核心時會把它自己的裝置樹寫回去,覆蓋上面複製的那些。這對原版核心沒有影響;如果升級後 dwc2fix 核心工作異常,重新執行那兩行 cp 即可。
  • 如果電量計驅動是通過 DKMS 安裝的,它會自動為這個核心編譯, 因為對應的 headers 包也一起裝上了。
  • 為什麼不乾脆把整條 USB 匯流排強制成全速?那樣確實沒有 split 了,但低速裝置(很多滑鼠)會在 hub 後面 列舉失敗,而且所有 USB 裝置都降到 12 Mbit/s。我們試過,打補丁的核心才是更好的取捨。