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 都 “成功”完成、沒有任何錯誤位,但全速匯流排上什麼也沒收到。
修復與上游狀態
Section titled “修復與上游狀態”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 合入並隨樹莓派核心包釋出後,原版核心就能出聲,本頁也就不再需要了。
在原版 Raspberry Pi OS 上安裝
Section titled “在原版 Raspberry Pi OS 上安裝”make bindeb-pkg 打出來的 .deb 沒有 Raspberry Pi OS 的啟動鉤子,所以啟動檔案要手動放置——
和映像建置時執行的步驟一樣:
REL=https://github.com/TypixNode/pi-gen/releases/download/kernel-dwc2fix-6.18.44-4mkdir -p ~/dwc2fix && cd ~/dwc2fixcurl -fLO "$REL/SHA256SUMS"for f in $(awk '{print $2}' SHA256SUMS); do curl -fLO "$REL/$f"; donesha256sum -c SHA256SUMS
sudo dpkg -i linux-image-*.deb linux-headers-*.debKVER=$(ls /lib/modules | grep dwc2fix | head -n1)sudo update-initramfs -c -k "$KVER"
# boot files: uncompressed kernel image + initramfs under our own namesif [ "$(od -An -tx1 -N2 /boot/vmlinuz-$KVER | tr -d ' ')" = 1f8b ]; then zcat "/boot/vmlinuz-$KVER" | sudo tee /boot/firmware/kernel-dwc2fix.img >/dev/nullelse sudo cp "/boot/vmlinuz-$KVER" /boot/firmware/kernel-dwc2fix.imgfisudo cp "/boot/initrd.img-$KVER" /boot/firmware/initramfs-dwc2fix
# device trees and overlays built with this kernelsudo 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.imginitramfs initramfs-dwc2fix followkernel[all]重啟並檢查:
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。我們試過,打補丁的核心才是更好的取捨。