昨天我們暸解了 USB OTG 的概念,知道 Raspberry Pi 可以透過 USB Gadget 框架把自己偽裝成隨身碟。 但在實作階段,許多新手(包括當年的我)會在網路上找到教學,跟著輸入 sudo modprobe g_mass_storage,卻只得到一行冷酷的錯誤訊息:No such device。
這究竟是為什麼?今天,我們必須稍微往下挖,看看 Linux 核心在開機的那一刻,發生了什麼事。
[問題情境] Kernel 的失憶症
想像一下,Linux 核心 (zImage 或 vmlinuz) 就像是一個剛被喚醒的失憶特務。它會算數學、會排程,但它不知道自己被裝在什麼硬體上。 「我是筆電嗎?還是樹莓派?我有幾個 USB 接口?那些接口是接在主機板的哪根銅線上?」
在古早的 ARM Linux 時代,這些硬體資訊是被直接「硬編碼 (Hardcoded)」在 Kernel 的 C 語言原始碼裡的。每次出一塊新板子,Linus Torvalds 的信箱就會被塞滿一堆只改了幾個暫存器位址的垃圾 patch。
為了解決這個亂象,Device Tree (設備樹, DTS) 誕生了。
[底層原理] Device Tree 與 MMIO 魔法
Device Tree 是一份純文字文件(編譯後變成二進位的 .dtb),它就像是一張寫給 Linux Kernel 的**「硬體導覽地圖」**。
當 Raspberry Pi 開機時,Bootloader 會先讀取這張地圖,然後把地圖交給 Kernel。地圖上會標示類似這樣的資訊:
「在記憶體位址 0x3F980000 的地方,藏著一顆負責管 USB 的晶片,它的名字叫 dwc2 (DesignWare USB 2.0 controller)。」
這種將硬體晶片的控制腳位,直接映射到 CPU 記憶體位址的技術,稱為 MMIO (Memory-Mapped I/O)。 (註:Pi 3 的 USB 暫存器位址通常在 0x3F980000,而 Pi 4 因為架構升級,位址移到了 0xFE980000。)
重點來了:Raspberry Pi 為了確保一般使用者的滑鼠鍵盤能正常運作,預設會把這個 dwc2 控制器強制鎖死在 Host (主機) 模式,使用的是一個叫 dwc_otg 的舊版閹割驅動。 在這種模式下,無論你的線材有沒有拔掉 ID Pin,它都絕對不允許 Pi 變成 Device (Gadget)。這就是你輸入 modprobe 會失敗的根本原因!
[最終解決方案] 修改 config.txt 解除封印
要讓我們的 EdgeNode 支援 USB 隨身碟模式,我們必須在開機前,就修改這張「硬體地圖」,告訴 Kernel 換一個允許 OTG 的驅動程式。
在 Raspberry Pi 上,我們不需要自己重寫 C 語言或重新編譯 Device Tree,我們只要掛載 Device Tree Overlay 即可。
步驟一:修改 /boot/config.txt 打開這個檔案,在最後一行加上:
inidtoverlay=dwc2
這短短一行字的意義是:「把預設的 dwc_otg 驅動趕走,換上完整支援 Host/Device 動態切換的 dwc2 驅動程式。」
步驟二:修改 /boot/cmdline.txt 在同一行的參數中插入 modules-load=dwc2,確保核心在開機的極早期就載入這個模組:
textconsole=serial0,115200 root=/dev/mmcblk0p2 ... modules-load=dwc2
重開機後,如果執行 lsmod | grep dwc2 看到它成功載入,恭喜你,你的 Raspberry Pi 已經解除了硬體封印,正式具備了變身為 USB 隨身碟的物理條件!
預告
硬體打通了,接下來就要上軟體了。 早期我們會直接載入 g_mass_storage 這個模組來捏出一顆隨身碟,但如果我們同時還想要一組虛擬網卡呢? 明天,我們將探討現代 Linux 的優雅解法:拋棄傳統 monolithic 核心模組,擁抱 ConfigFS 虛擬檔案系統!