Linux UVC (USB Video Class) 驱动框架深度解析
目录
- [UVC 协议架构解析](#UVC 协议架构解析)
- [1.1 UVC 协议栈分层](#1.1 UVC 协议栈分层)
- [1.2 描述符体系详解](#1.2 描述符体系详解)
- [1.3 控制请求时序](#1.3 控制请求时序)
- [Linux 内核实现剖析](#Linux 内核实现剖析)
- [2.1 驱动模块架构](#2.1 驱动模块架构)
- [2.2 关键数据结构](#2.2 关键数据结构)
- [2.3 数据流管道](#2.3 数据流管道)
- [V4L2 集成开发指南](#V4L2 集成开发指南)
- [3.1 设备注册流程](#3.1 设备注册流程)
- [3.2 IOCTL 调用序列](#3.2 IOCTL 调用序列)
- [3.3 帧格式协商](#3.3 帧格式协商)
- 开发实战内容
- [4.1 扩展控制开发](#4.1 扩展控制开发)
- [4.2 调试与分析](#4.2 调试与分析)
- [4.3 性能优化](#4.3 性能优化)
1. UVC 协议架构解析
UVC (USB Video Class) 协议定义了视频流设备在 USB 总线上的标准行为,使得操作系统无需安装特定驱动即可支持摄像头等设备。
1.1 UVC 协议栈分层
UVC 设备由一个视频控制接口 (VideoControl Interface, VC) 和一个或多个视频流接口 (VideoStreaming Interface, VS) 组成。
Device Descriptor VideoControl (VC) Interface VideoStreaming (VS) Interface Configuration Descriptor Class-Specific VS Header VS Format Descriptor (MJPEG, YUY2...) VS Frame Descriptor (640x480, 30fps...) Color Matching Descriptor Class-Specific VC Header Input Terminal (IT) - Camera Sensor Processing Unit (PU) - Brightness, Contrast... Output Terminal (OT) - USB IN Endpoint Extension Unit (XU) - Vendor Specific
1.2 描述符体系详解
UVC 利用 USB 的类特定描述符 (Class-Specific Descriptors) 来描述拓扑结构和流参数。
1.2.1 视频流接口输入头描述符 (CS_VS_INPUT_HEADER_DESCRIPTOR)
该描述符位于 VS 接口的开头,包含流的整体信息。
c
// 伪代码结构表示
struct uvc_input_header_descriptor {
__u8 bLength; // 描述符长度
__u8 bDescriptorType; // CS_INTERFACE (0x24)
__u8 bDescriptorSubtype; // VS_INPUT_HEADER (0x01)
__u8 bNumFormats; // 支持的视频格式数量 (p)
__u16 wTotalLength; // 此头部及后续所有格式/帧描述符的总长度
__u8 bEndpointAddress; // 传输视频数据的端点地址
__u8 bmInfo; // 动态格式切换支持等信息
__u8 bTerminalLink; // 关联的 OT ID
__u8 bStillCaptureMethod; // 静态抓拍方法
__u8 bTriggerSupport; // 硬件触发支持
__u8 bTriggerUsage; // 触发用途
__u8 bControlSize; // bmaControls 数组的大小 (n)
__u8 bmaControls[p*n]; // 每个格式支持的 Commit/Probe 控件位图
};
1.2.2 视频格式与帧描述符
- 格式描述符 (Format Descriptor): 定义编码格式(如 MJPEG, Uncompressed YUY2)。GUID 字段是区分格式的关键。
- 帧描述符 (Frame Descriptor): 定义分辨率、帧率区间。
1.3 控制请求时序
主机与设备之间的视频流参数协商通过 Probe 和 Commit 两个阶段完成。
- Probe: 主机询问设备是否支持某种配置,设备返回最近似的有效配置。
- Commit: 主机确认配置,设备锁定参数准备传输。
Host Device 1. Probe Phase (协商参数) SET_CUR (PROBE, Desired_Params) ACK GET_CUR (PROBE) Return (Negotiated_Params) GET_MIN/MAX/DEF (PROBE) Return Limits 2. Commit Phase (提交参数) SET_CUR (COMMIT, Final_Params) ACK (Hardware Configured) 3. Streaming Phase (开启视频流) SET_INTERFACE (Alt Setting > 0) Video Stream (Isochronous/Bulk) Host Device
2. Linux 内核实现剖析
Linux 内核中的 UVC 驱动位于 drivers/media/usb/uvc/,核心模块为 uvcvideo.ko。
2.1 驱动模块架构
Kernel Space V4L2 Core Subsystem UVC Driver (uvcvideo.ko) USB Core Subsystem User Space ioctl/mmap Submit URB Probe/Disconnect struct usb_driver URB Management uvc_driver.c uvc_video.c uvc_ctrl.c uvc_v4l2.c uvc_queue.c v4l2_device video_device videobuf2-core V4L2 Application
2.2 关键数据结构
struct uvc_device 是驱动的核心结构,代表一个物理 UVC 设备;struct uvc_streaming 代表一个视频流接口。
c
// include/uapi/linux/uvcvideo.h & drivers/media/usb/uvc/uvcvideo.h
struct uvc_device {
struct usb_device *udev;
struct v4l2_device vdev;
struct list_head entities; // 设备内部拓扑单元链表 (Terminals, Units)
struct list_head streams; // 视频流接口链表
// ...
};
struct uvc_streaming {
struct uvc_device *dev;
struct video_device vdev; // 对应的 /dev/videoX 节点
struct uvc_queue queue; // vb2 视频缓冲队列
struct uvc_format *format; // 当前选中的格式
struct uvc_frame *cur_frame; // 当前选中的帧参数
struct urb *urb[UVC_URBS]; // 用于 USB 传输的 URB 数组
// ...
};
2.3 数据流管道
从 USB 数据包到用户空间 V4L2 Buffer 的完整路径。
3. V4L2 集成开发指南
3.1 设备注册流程
当 USB 设备插入时,uvc_probe 函数被调用。
uvc_probe Parse Descriptors Build Topology Chains Initialize Controls Register V4L2 Device Register /dev/videoX Ready
3.2 IOCTL 调用序列
标准视频采集应用的 IOCTL 调用流程:
open("/dev/video0"): 打开设备。VIDIOC_QUERYCAP: 查询设备能力(是否支持 Video Capture, Streaming)。VIDIOC_ENUM_FMT: 枚举支持的像素格式(MJPEG, YUYV)。VIDIOC_S_FMT: 设置具体的格式和分辨率(触发 UVC Probe/Commit 过程)。VIDIOC_REQBUFS: 申请缓冲区。VIDIOC_QBUF: 将空闲缓冲区放入队列。VIDIOC_STREAMON: 开启视频流(提交 URB)。select/poll: 等待数据。VIDIOC_DQBUF: 取出已填充数据的缓冲区。VIDIOC_STREAMOFF: 停止视频流。
3.3 帧格式协商
V4L2 的 S_FMT 调用会转化为 UVC 的 Probe/Commit 流程。
示例交互:
- App :
ioctl(VIDIOC_S_FMT, width=1920, height=1080, pix_fmt=MJPEG) - Driver :
- 查找对应的 UVC Format Descriptor (MJPEG)。
- 查找最接近的 Frame Descriptor (1920x1080)。
- 构建 Probe Control Block。
- 发送
SET_CUR(PROBE)给设备。 - 发送
GET_CUR(PROBE)读取设备修正后的参数。 - 如果不满意,可能多次迭代。
- 最后发送
SET_CUR(COMMIT)。
- Device: 切换内部 ISP/Sensor 模式。
4. 开发实战内容
4.1 扩展控制开发
UVC 允许厂商定义 Extension Unit (XU) 来实现标准协议未涵盖的功能(如控制红外灯、固件升级)。
内核代码示例: 添加一个简单的 XU 映射。
c
/* 1. 定义 XU GUID (需与设备固件一致) */
#define UVC_GUID_MY_XU \
{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF, \
0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF}
/* 2. 定义 V4L2 菜单或控件映射 */
static struct uvc_control_mapping my_xu_ctrl_mapping = {
.id = V4L2_CID_BRIGHTNESS, // 可以复用现有 ID 或定义私有 ID
.name = "My Custom XU Control",
.entity = UVC_GUID_MY_XU,
.selector = 1, // XU 中的 Control Selector (CS)
.size = 8, // 数据位宽
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
};
/* 3. 在 uvc_ctrl_init_device 中注册 (通常通过 uvcdynctrl 工具在用户空间动态加载更方便) */
4.2 调试与分析
4.2.1 uvcvideo 模块参数
可以通过 uvc_trace 参数开启详细日志。
bash
# 查看参数定义
modinfo uvcvideo
# 开启所有 trace (掩码位)
# 0x1=PROBE, 0x2=DESCR, 0x4=CONTROL, 0x8=FORMAT, 0x10=CAPTURE...
echo 0xffff > /sys/module/uvcvideo/parameters/trace
4.2.2 usbmon 抓包
使用 Wireshark + usbmon 抓取 USB 交互数据。
bash
# 加载 usbmon
modprobe usbmon
# 抓取 USB 总线 1 的数据
cat /sys/kernel/debug/usb/usbmon/1u > /tmp/uvc_trace.log
# 或者直接用 Wireshark 监听 usbmon1
4.3 性能优化
-
零拷贝 (Zero-Copy):
- 使用
V4L2_MEMORY_DMABUF模式。 - UVC 驱动将 USB 数据直接 DMA 到用户空间提供的 dmabuf 中(如果是 scatter-gather DMA 支持的控制器)。
- 避免了
copy_to_user的 CPU 开销。
- 使用
-
带宽计算:
- UVC 设备的带宽占用由
dwMaxPayloadTransferSize决定。 - 如果多个摄像头同时工作报错
No space left on device(ENOSPC),通常是 USB 带宽不足。 - 优化 : 降低帧率、分辨率,或修改设备固件减小
dwMaxPayloadTransferSize声明。
- UVC 设备的带宽占用由
附录:参考文档
- USB Video Class 1.5 Specification : USB.org
- Linux V4L2 API Documentation : Kernel.org