在上一篇文章中,我們探討了 Windows 積極的緩存機制如何導致剛寫入隨身碟的資料,在未經安全移除的情況下被遺失。為了解決這個問題,除了確保底層的資料同步(使用 fsync 甚至直接 flush block device),我們還面臨一個更棘手的 UX(使用者體驗)問題:如何讓 Windows 知道檔案系統已經更新了?
想像一下這個情境:
.bin 映像檔,然後重新掛載。為什麼會這樣?因為 Windows 的 USB 大量儲存驅動程式 (Mass Storage Driver) 非常依賴初始掛載時的快取。除非 Windows 偵測到硬體發生了「拔出」再「插入」的動作,否則它不會重新掃描檔案配置表 (FAT)。
難道我們要要求護理人員每次量測完,都要手動去拔掉再插上那根 USB 線嗎?這絕對會被客訴到爆。
起初,我們試圖在 Windows 端寫一支小程式,透過 Windows API 去觸發磁碟重新整理,甚至嘗試送出 IOCTL_DISK_UPDATE_PROPERTIES。但這不僅需要額外在 PC 端安裝軟體,還常常因為權限不足而失敗。我們的目標是打造一個真正的「隨插即用」無頭設備 (Headless Device),不應該依賴 PC 端的任何客製化軟體。
回到 EdgeNode (Raspberry Pi) 的 Linux 底層。我們是透過 ConfigFS 來模擬 USB 隨身碟的。在 Linux USB Gadget 驅動的設計中,虛擬設備的生命週期與 UDC (USB Device Controller) 緊密綁定。
當我們把設定好的 Gadget 名稱寫入 UDC 檔案時,Linux 核心就會在實體的 USB 介面上觸發一個硬體級別的電位變化(Pull-up resistor 改變),告訴主機端(Windows):「嗨!有一個新設備插進來囉!」
相反地,如果我們把一個空字串寫入 UDC 檔案,Linux 就會解除電位,主機端就會以為設備被「拔出」了。
這就是所謂的 軟體重插拔 (Software Re-plug)!
掌握了這個原理,我們的解決方案就變得異常優雅且完全不需要依賴硬體動作。
當 EdgeNode 內部寫入新資料後,我們只需執行以下腳本:
#!/bin/bash
# software_replug.sh
GADGET_DIR="/sys/kernel/config/usb_gadget/edge_node"
UDC_FILE="$GADGET_DIR/UDC"
# 1. 取得當前的 UDC 名稱 (例如 20980000.usb 或 fe980000.usb)
CURRENT_UDC=$(ls /sys/class/udc | head -n 1)
# 2. 模擬拔出 (Unbind)
echo "拔出虛擬 USB 設備..."
echo "" > "$UDC_FILE"
# 等待一下,讓 Windows 確實反應設備已移除
sleep 1
# 3. 模擬插入 (Bind)
echo "插入虛擬 USB 設備..."
echo "$CURRENT_UDC" > "$UDC_FILE"
[!TIP]
關鍵細節:為何要sleep 1?
如果拔出和插入的間隔太短,Windows 的 USB 驅動可能會把這個短暫的斷線視為電氣雜訊 (Glitch) 而忽略它。給予大約 1 秒的緩衝,能確保 Windows 確實觸發了 Device Removal 事件,並清除該磁碟的緩存。
結合我們前幾天的知識,EdgeNode 處理新資料的完整生命週期變為:
.bin 映像檔,並強制 sync。透過這招「軟體重插拔」,我們成功在不修改任何硬體線路、不增加任何實體按鈕的情況下,完美解決了檔案系統更新不同步的世紀難題。
這就是軟硬整合的浪漫:用兩行指令,改變真實世界的物理行為。
明天,我們將跨入一個全新的領域:當系統越來越複雜,我們該如何控制 EdgeNode 上那顆至關重要的 RGB 指示燈?敬請期待 Day 20:多執行緒的混亂!