在调试一个双摄像头(双UVC设备)的过程中,我在 Linux 系统下遇到了一个现象:我需要打开 /dev/video0 和 /dev/video2,而不是预想中的 /dev/video0 和 /dev/video1。这意味着,单个 UVC 设备实际上生成了两个 /dev/video 设备节点。
通过查阅资料得知,这是因为在标准的 Linux 内核 UVC 驱动实现中,一个 UVC 设备会创建两个设备节点——一个用于获取视频流(video stream),另一个用于获取元数据(metadata)。
为了满足某些特定调试场景的需求(例如希望设备号连续,或简化应用层逻辑),我们可能需要修改驱动,使一个 UVC 设备只对应一个 /dev/video 节点。下文将分享具体的修改方法。
问题复现与定位
首先,在配置好 WSL2 的 UVC 支持 的环境后,插入 UVC 设备,查看 /dev 目录下的视频设备:
qinyunti@qinyunti:~$ ls /dev/video
video0 video1 video2 video3

可以看到有四个设备节点。接着,通过 dmesg 命令查看内核日志,可以确认两个 UVC 设备已被识别:
[ 547.038592] usb 1-1: new high-speed USB device number 2 using vhci_hcd
[ 547.169054] usb 1-1: SetAddress Request (2) to port 0
[ 547.198884] usb 1-1: Found UVC 1.00 device XXXX.UVC (1993:0101)
[ 547.204401] usb 1-1: Found UVC 1.00 device XXXX.UVC (1993:0101)

日志中两次“Found UVC”的打印,进一步印证了一个物理设备生成了两个逻辑设备。我们就在内核源码中搜索这个关键日志信息“Found UVC”。

该打印位于 drivers/media/usb/uvc/uvc_driver.c 文件的 uvc_probe 函数中。设备节点(如/dev/videoX)的‘X’号由 devnode_find 函数分配。通过分析代码调用链,我们可以梳理出设备节点的注册路径:
uvc_register_chains() -> uvc_register_terms() -> uvc_meta_register()
关键的一步发生在 uvc_register_terms 函数中,它会尝试注册元数据节点。

修改驱动代码
为了让一个UVC设备只生成一个 /dev/video 节点,我们需要阻止元数据节点的注册。修改位于 drivers/media/usb/uvc/uvc_driver.c 的 static int uvc_register_terms(struct uvc_device *dev, ...) 函数。
找到其中调用 uvc_meta_register(stream); 的代码行,将其注释掉即可。

修改后的代码片段示意如下(注意:实际修改需在源码中进行):
// 原代码:ret = uvc_meta_register(stream);
// 修改为:
// uvc_meta_register(stream);
验证修改结果
完成代码修改并重新编译、加载 UVC 驱动 模块后,再次插入UVC设备并列出设备节点:
qinyunti@qinyunti:~$ ls /dev/video
video0 video1

现在,两个物理UVC设备恰好对应 /dev/video0 和 /dev/video1 两个节点,达到了我们预期的效果。
总结
通过对 Linux 内核 UVC 驱动源码的分析与修改,我们明确了设备节点翻倍的原因在于元数据节点的默认注册。在 uvc_register_terms 函数中注释掉对 uvc_meta_register 的调用,即可实现一个 UVC 设备仅生成一个 /dev/video 设备节点。这种方法适用于特定的开发调试场景,但在生产环境中修改前需评估其对元数据功能的影响。更多类似的底层驱动调试技巧,可以在 云栈社区 的对应板块中找到深入讨论。