
本文提到的entity和unit实际上是一个东西,都代指uvc设备中的某个单元,只不过uvc驱动中把它叫做eneity实体本文仅仅做了对uvc注册和框架的研究,至于音视频类的内容没有深究usb摄像头描述信息概述[USB Device: 4a54:5232 Generic Web Camera(USB2.0/ Bus Powered 500mA)]│ ├─►[IAD0: Video Function(Class14, SubClass3)]──────────────────────┐ │ │ │ │ ├─► Interface0: Video Control(VC, 接口号0)│ │ │ │ │ │ │ ├─► 包含硬件 Entity 拓扑(IT1-PU7-OT3, XU6)├─► 绑定 uvcvideo 驱动 │ │ └─► Endpoint 0x82(EP2IN, Interrupt, 64B)── 状态事件上报 │ 生成 /dev/video* │ │ │ │ └─► Interface1: Video Streaming(VS, 接口号1)│ │ │ │ │ ├─► AltSetting0: 零带宽空闲态(0Endpoints)│ │ ├─► AltSetting1~7: 等时传输态(Isochronous, EP4IN 0x84)│ │ │ * 最大带宽包达 3x10243072字节/微帧 │ │ └─► 支持格式: │ │ * Format1: MJPEG(1080P/720P/544P/480P/360P 30fps)│ │ * Format2: YUY2(Uncompressed, 1080P1fps, 720P1.5fps)│ │ │ └─►[IAD1: Audio Function(Class1, SubClass0)]───────────────────────┼─────────────────────────┐ │ │ │ ├─► Interface2: Audio Control(AC, 接口号2)│ │ │ │ │ │ │ ├─► 包含音频拓扑(IT3Mic -FU5Feature -OT4USB Stream)│ ├─► 绑定 snd-usb-audio 驱动 │ └─► 支持控制: Mute静音, Volume音量 │ 生成 ALSA 声卡(pcmC*D*c)│ │ │ └─► Interface3: Audio Streaming(AS, 接口号3)│ │ │ │ │ ├─► AltSetting0: 空闲态(0Endpoints)│ │ └─► AltSetting5: 工作态(EP3IN 0x83, Isochronous, 64B)│ │ * PCM 单声道(Mono),16-bit,32000Hz 采样率 ┘ ┘uvc_driver.cuvc设备即usb video class设备,其将isp等功能直接封装在了设备上,通过usb来传输图像和控制信号linux内核中针对uvc设备写了一套完整的驱动程序,能够通过uvc设备的usb的Video Control的interface进行自匹配uvc_ids中定义了很多uvc设备的interface信息对于不在上面这些特定厂商的产品中的uvc设备,则通过最后这两条,不匹配vid和pid而是通过上面这个宏拼接起来的uvc设备类型进行匹配,只看interface的描述符是不是usb video class其中sc是interface的subclass,这里是1,就有如下对应关系bInterfaceSubClass 1 Video Control然后将其id_table以及probe函数等注册到usb总线上此时这个id_table实际上匹配的是subClass 为 Video Control的usb interface设备中所有eneity即unit是怎么接的,分别是干嘛的,都在Video Control的interface中进行了描述uvc_probe函数对于绝大多数linux驱动来说,最关键的地方就在probe函数上面这段代码主要是处理初始化一些用到的信息其中1995行-1997行初始化的三个链表是uvc设备独有的-entities 是uvc设备内部的功能单元的静态描述信息-chains 是视频数据通路的静态描述信息,即从output unit-input unit-streams 是对chain的使用说明书,其中包含了用哪条chain,开多大的urb,设置输出什么分辨率等等,有几个stream就对应着/dev下有几个video设备节点一般可能会有video0和video1两个节点,video0是结构体数据,video1是纯纯的元数据,一般也用不到video12017行的uvc_parse_control()函数就是负责解析video control interface并按照其描述填充entites链表同时内部也会解析usb video streaming中对于stream的描述信息,比如支持哪些分辨率,帧率以及通过哪个端口协商这些,填充到streams链表上stream按照支持的格式,比如mjpeg,yuyv进行分开这段代码是将uvc设备注册到linux中的media controller子系统框架中Media ControllerMC框架则负责向用户态提供整个硬件内部的拓扑结构、实体Entity与数据链路Pad / Link关系其通常会被注册为/dev/mediax节点使用media-ctl工具,即可查看设备内部entities的拓扑图media-ctl -d /dev/media0 -pDevice topology - entity1: Web Camera: Web Camera(1pad,1link)typeNode subtype V4L flags1devicenodename /dev/video0 pad0: Sink-Extension 6:1[ENABLED,IMMUTABLE]- entity4: Web Camera: Web Camera(0pad,0link)typeNode subtype V4L flags0devicenodename /dev/video1 - entity8: Extension6(2pads,2links)typeV4L2 subdev subtype Unknown flags0pad0: Sink-Processing 7:1[ENABLED,IMMUTABLE]pad1: Source -Web Camera: Web Camera:0[ENABLED,IMMUTABLE]- entity11: Processing7(2pads,2links)typeV4L2 subdev subtype Unknown flags0pad0: Sink-Camera 1:0[ENABLED,IMMUTABLE]pad1: Source -Extension 6:0[ENABLED,IMMUTABLE]- entity14: Camera1(1pad,1link)typeV4L2 subdev subtype Sensor flags0pad0: Source -Processing 7:0[ENABLED,IMMUTABLE]pad是数据引脚,source表示流出,sink表示流入ENABLE表示开启通路,IMMUTABLE表示不可更改(因为我这个usb摄像头很便宜,内部实体之间的数据流向固定死了,也就是chain是固定的)2050行是将当前uvc设备作为一个总设备注册成为v4l2设备,可以理解为将其注册成一个资源管理器后面所有被注册成video_device的stream就都是它的子设备了2054行,遍历所有前面得到的entities链表并且按照entities描述信息,前面uvc_parse_control是给每个entity分配了一个支持功能的位图这里将位图中 为真 的项,实例化成一个个control挂到entity上2058行,从output 实体开始往回遍历entities,每找到一条完整的从output到 input实体的chain,就添加一条chain此时chain就是包含完整control信息的从input的eneity到output的eneity的一条拓扑路径2062行,是真正注册video_device的地方,可以理解为其将每个stream注册为了一个/dev下面的video设备其先会调用uvc_register_chains(structuvc_device*dev)---uvc_register_terms(structuvc_device*dev,structuvc_video_chain*chain)uvc_register_terms函数中会调用uvc_stream_by_iduvc_stream_by_id内部最终会调用uvc_register_video函数,通过bTerminalLink和bTerminalID匹配后,将每个stream和每条chain中的output实体进行匹配,只有匹配成功的stream才会走向后面被注册所以这里又引出一个知识点,那么就是一个chain可以对应多个stream然后接下来走到uvc_register_video函数uvc_video_init函数是给stream设置默认的分辨率帧率等,剩下的那些调节白平衡呀啥的,会在iotcl中实现这里v4l2为了兼容性,和看门狗子系统一样,都是在硬件操作的fops上套了一层变成了字符设备给用户空间交互这里vdev指向的ops函数结构体中,实际上操作的是vdev私有数据,即stream之后所有设置控制参数操作,就都是通过stream找到对应的chain上的eneity进行操作从而实现的设置分辨率和帧率则是通过video streaming interface中的EP0进行选择至此,一个uvc设备就被成功从usb设备注册成了一个v4l2下的一个或者多个video_device总结最后我让ai帮我总结了一下[USB 设备插入 / 枚举识别(4a54:5232)]│ ▼1. 总线驱动匹配(Probe)uvc_probe(struct usb_interface *intf)(仅绑定 Class14, SubClass1的 VC 接口)│ ┌───────────────────────┴───────────────────────┐ ▼ ▼2. 控制端解析(VC Interface)3. 数据流端解析(VS Interface)uvc_parse_control(dev)uvc_parse_streaming(dev)- 提取 IT, PU, XU, OT 实体 - 解析所有流接口(Interface1)- 生成 dev-entities 链表 - 解析格式/分辨率/帧率表(Format/Frame)│ - 记录端点(EP4IN)与带宽 AltSetting │ - 生成 dev-streams 链表 ▼ │4. 硬件画质控件初始化 │ uvc_ctrl_init_device(dev)│ - 遍历每个 Entity 的 bmControls 位图 │ - 匹配分配 entity-controls(亮度/曝光等)│ │ │ └───────────────────────┬───────────────────────┘ ▼5. 拓扑回溯与缝合(Video Chain)uvc_scan_device(dev)- 从 Output Terminal(OT3)逆向寻源回溯 - 将 IT1、PU7、OT3串联为 struct uvc_video_chain - 【缝合】将 dev-streams 中的 stream 挂入 chain-streams │ ▼6. 设备暴露与初始化(Register)uvc_register_chains(dev)┌────────────────────┴────────────────────┐ ▼ ▼ uvc_video_init(stream)uvc_register_terms(dev, chain)- 选定初始默认格式与分辨率 - 注册主视频流 video_device(/dev/video0)- 执行 VS ProbeCommit 初始握手 - 挂载 chain-ctrl_handler(暴露画质 Controls)- 指定解码回调(uvc_video_decode_isoc)- 若支持注册元数据流(/dev/video1)1. 总线驱动匹配USB Bus Probe设备插入 USB 总线完成物理枚举后USB Core 识别到该复合设备包含 Video FunctionIAD 0。USB Core 根据bInterfaceClass 14 (Video)和bInterfaceSubClass 1 (Video Control)将Interface 0VC 控制接口与uvc_driver绑定触发调用uvc_probe()。2. 控制接口解析提取实体uvc_parse_control驱动读取 Interface 0 的类特定描述符。识别摄像头内部的硬件结构单元为每个单元动态分配struct uvc_entity并填入dev-entities链表Input Terminal (IT: 1)Camera Sensor记录光学控制项自动曝光、曝光时间。Processing Unit (PU: 7)ISP 图像处理模块记录画质控制项亮度、对比度、饱和度、白平衡等。Extension Unit (XU: 6)厂商扩展单元。Output Terminal (OT: 3)向 USB 传输视频的输出端记录其输入源来自前级单元。3. 数据流接口解析提取流配置uvc_parse_streaming驱动主动遍历该 USB 设备上的所有其他接口查找bInterfaceSubClass 2 (Video Streaming)的VS 接口Interface 1。为每一个流接口分配一个struct uvc_streaming挂入dev-streams。解析该流支持的所有图像格式MJPEG、YUY2、各格式下的分辨率与帧率列表1080P/720P 等以及可用的传输端点0x84 EP 4 IN和不同带宽档位的AltSetting。4. 硬件控制项实例化uvc_ctrl_init_device遍历dev-entities中的每个实体。读取其bmControls位图识别硬件声明支持的功能。动态分配entity-controls数组将底层的 UVC 控制选择器Control Selector与 Linux 内核 V4L2 Control 规范对应起来。5. 拓扑回溯与流缝合uvc_scan_device以各个输出终端OT为起点利用bSourceID逆向寻源Walk Backwards将数据流动沿途经过的 EntityIT→\to→PU→\to→OT打包为一条逻辑流水线struct uvc_video_chain。数据流与控制链路缝合通过 OT 描述符中声明的终端关联从dev-streams中取出对应的uvc_streaming插入该 Chain 的chain-streams链表完成数据通路与控制骨架的绑定。6. 最终注册与参数初始化uvc_register_chains驱动遍历装配完成的 Chain完成两项核心落地操作流环境与默认协商初始化uvc_video_init将 VS 接口切换为静默空闲态AltSetting 0。选定一个开机默认格式与分辨率如 1080P MJPEG通过 Endpoint 0 向 VS 接口发起首次Probe Commit控制请求完成基本参数协商。根据端点属性绑定数据解码函数如等时传输绑定uvc_video_decode_isoc。生成用户空间节点video_register_device主视频节点注册生成/dev/video0赋予V4L2_CAP_VIDEO_CAPTURE与V4L2_CAP_STREAMING能力并将该 Chain 上由各 Entity 收集的画质参数挂到节点暴露给用户空间。辅助/元数据节点若设备描述符包含多输出终端或内核启用了元数据支持为次级终端注册生成/dev/video1V4L2_CAP_META_CAPTURE用于向应用层导出逐帧硬件时间戳与快门快照字节流。