87 lines
5.2 KiB
Markdown
87 lines
5.2 KiB
Markdown
# 任意 U 盘透传实现(2026-11-04)
|
||
|
||
在已验证的真实 USB 透传链路上,将盒子端策略从固定 VID/PID/序列号扩展为
|
||
“任意纯 USB Mass Storage 设备”。键盘、鼠标、Hub 和显示设备仍由盒子本地使用。
|
||
|
||
## 行为
|
||
|
||
- 一个盒子、一个 VM 同时只导出一个存储设备;多个 U 盘同时插入时按盒子扫描顺序使用,
|
||
当前设备拔出后再接入下一个。
|
||
- 设备必须是纯存储设备,所有 USB interface class 都必须为 `08`;键鼠、Hub 和复合设备拒绝。
|
||
- 盒子检查分区是否本地挂载、是否有 holder 或作为 swap;发现本地占用时等待,不强制卸载。
|
||
- QEMU 使用 `0x08:-1:-1:-1:1` 过滤器,允许任意 USB Mass Storage,仍拒绝其他 USB 类别。
|
||
- PVE QMP watcher 在 VM/PVE 重启后重新建立运行时 `usb-redir` 设备。
|
||
|
||
## 部署入口
|
||
|
||
```sh
|
||
python tools/deploy-usb-device-poc.py \
|
||
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 --any-storage
|
||
```
|
||
|
||
部署前应在 Windows 中安全弹出当前 U 盘。部署器会安装盒子监控服务、USB 隧道和
|
||
`vdi-usb-qmp-100.service`;不会修改 VM 固定配置、GPU、显示或键鼠输入链路。
|
||
|
||
旧的精确设备策略仍可用:
|
||
|
||
```sh
|
||
python tools/deploy-usb-device-poc.py \
|
||
--pve PVE_HOST --box armbian@BOX_HOST --vm 100 \
|
||
--vendor 0951 --product 1666 --serial SERIAL
|
||
```
|
||
|
||
## 验收
|
||
|
||
至少用三只不同品牌或不同 VID/PID 的 U 盘测试:插入后 Windows 出现真实
|
||
USB Mass Storage 和盘符,读写大文件后校验 SHA-256;再测试安全弹出、拔插、
|
||
两个 U 盘同时插入,以及 VM/PVE 重启后的自动恢复。当前 Rust 单元测试 10/10、
|
||
Python QMP 测试 4/4 通过。已现场验证另一只 SanDisk 0781:5591 在 Windows
|
||
识别为 USBSTOR,F: NTFS 数据分区和 G: FAT32 UQITONG_ESP 分区均 Healthy。
|
||
多盘吞吐和 VM/PVE 重启后的恢复仍待验收;QMP watcher 已实现,但尚未现场安装。
|
||
|
||
## 插入体验优化
|
||
|
||
2026-11-04 已将当前 Windows 用户 test 的 StorageOnArrival 从 MSOpenFolder
|
||
改为 MSPromptEachTime,并保留自动播放开启。双分区装机盘仍显示两个卷,
|
||
不会再按“打开文件夹”这个默认操作自动打开两个窗口;提示数量和通知外观由 Windows 决定。
|
||
|
||
脚本 `tools/set-usb-experience.ps1` 可重复 Apply,并支持 Restore;仅修改三项
|
||
自动播放首选项,使用首次运行保存的原值回退。Windows 已执行 Apply 并回读成功。
|
||
备份路径:`C:\ProgramData\VDI\usb-experience-20261104\preferences.clixml`。
|
||
|
||
盒子监控程序使用已处理的 USB udev 事件即时唤醒,替代原来的每 2 秒轮询;
|
||
500ms 兜底扫描负责启动时已有设备和事件监听不可用的情况。活动会话直接核对
|
||
已导出的 sysfs 设备身份,不再因另一个 USB 设备扫描失败而误断开。
|
||
快速插盘时须等 `bNumInterfaces` 声明的所有接口出现后才做存储类型判断,
|
||
防止接口尚未枚举完整的存储/HID 复合设备被误接入。
|
||
日志包含 `usb_time_ms`、`local_check_ms`、`since_latest_usb_event_ms`;最后一项
|
||
仅为最近事件至导出调用的时间,不代表从物理插入至 Windows 完全可用的总时间。
|
||
|
||
盒子已离线测试并编译、部署(更新时无 U 盘连接),服务 active,udevadm 子进程正常。
|
||
旧二进制备份在 `/home/armbian/vdi-terminal/backups/usb-events-20261104-110747/`。
|
||
未重启 VM、Moonlight、Sunshine、Explorer,未修改启动分区和系统原生安全删除行为。
|
||
随后实测事件至导出调用为 5~8ms(仅盒子内部时间)。
|
||
|
||
继续排查发现发行版 usbredirect 0.13.0 的连接回调无条件订阅 G_IO_OUT,
|
||
现场导出进程曾占约 96% 的一个 CPU 核心。官方 0.15.0 的 update_watch() 按
|
||
usbredirhost_has_data_to_write() 动态监听;已从官方发布包编译私有版本:
|
||
|
||
- 源码:https://www.spice-space.org/download/usbredir/usbredir-0.15.0.tar.xz
|
||
- 源码 SHA256:`6dc2a380277688a068191245dac2ab7063a552999d8ac3ad8e841c10ff050961`
|
||
- 私有程序:`/usr/local/libexec/vdi-usbredirect`
|
||
- 二进制 SHA256:`58b58b9d9ab017d3221e44f83c0794ae2977ef9e020fcb8df959eef997ae8e4a`
|
||
- 构建脚本:`tools/build-usbredirect-private.sh`;头文件仅下载并解包,没有安装或升级系统 GLib/libusb。
|
||
- 盒子配置:`/etc/vdi-usb-monitor/device.env` 新增
|
||
`VDI_USBREDIRECT_BINARY=/usr/local/libexec/vdi-usbredirect`。
|
||
- 切换前备份:`/home/armbian/vdi-terminal/backups/usb-exporter-20261104-112031/`。
|
||
|
||
运行时 CPU 实测降至约 1%。官方 filter 29 项、serializer 1 项测试全部通过;
|
||
Rust 10 项测试通过。Windows 自动播放设置已回读验证,PowerShell 语法检查通过。
|
||
用户第一次复测仍反馈菜单慢,后续保持连接并再测后明确反馈“现在已经很快了”。
|
||
已确认不再自动打开文件夹;右下角提示和弹出菜单速度通过本次用户现场验收,
|
||
但没有测量从物理插入至菜单可用的完整耗时,也不代表长时间稳定性验收完成。
|
||
|
||
回退时先在 Windows 安全弹出并物理拔下 U 盘,再停止盒子 USB 服务,
|
||
从上述备份恢复二进制和 device.env,启动服务即可。Windows 自动播放可用
|
||
`C:\ProgramData\VDI\usb-experience-20261104\set-usb-experience.ps1 -Action Restore` 回退。
|