10.扫描视频设备链和注册视频设备

10.1 uvc视频链

struct uvc_video_chain {	//uvc视频链
struct uvc_device *dev; //uvc设备
struct list_head list; //uvc视频链链表头
struct list_head entities; //uvc实体链表头
struct uvc_entity *processing; //处理Unit实体
struct uvc_entity *selector; //选择器Unit实体
struct mutex ctrl_mutex; /* Protects ctrl.info */
};

10.2 uvc扫描设备

static int uvc_scan_device(struct uvc_device *dev)
{
struct uvc_video_chain *chain; //uvc视频链
struct uvc_entity *term; //uvc实体 list_for_each_entry(term, &dev->entities, list) { //遍历全局实体链表
if (!UVC_ENTITY_IS_OTERM(term)) //获取实体链表中的输出Terminal实体
continue;
if (term->chain.next || term->chain.prev) //已经添加到uvc视频链中了
continue;
chain = kzalloc(sizeof(*chain), GFP_KERNEL); //分配uvc视频链内存(有多少个输入Terminal就有多少个uvc_video_chain)
if (chain == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&chain->entities); //初始化视频链entities(实体)链表
mutex_init(&chain->ctrl_mutex);
chain->dev = dev; //捆绑uvc视频链和uvc设备
if (uvc_scan_chain(chain, term) < 0) { //扫描uvc视频链(处理所有相关的输入pin)
kfree(chain);
continue;
}
uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",uvc_print_chain(chain));
list_add_tail(&chain->list, &dev->chains); //添加到uvc设备的uvc视频链链表
}
if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found.\n");
return -1;
}
return 0;
}

10.3 uvc扫描视频链

static int uvc_scan_chain(struct uvc_video_chain *chain,struct uvc_entity *term)
{
struct uvc_entity *entity, *prev;
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
entity = term; //获取实体
prev = NULL; //前一个实体
while (entity != NULL) {
/* Entity must not be part of an existing chain */
if (entity->chain.next || entity->chain.prev) { //已经添加到uvc视频链中了
uvc_trace(UVC_TRACE_DESCR, "Found reference to entity %d already in chain.\n", entity->id);
return -EINVAL;
}
/* Process entity */
if (uvc_scan_chain_entity(chain, entity) < 0) //扫描当前实体
return -EINVAL;
/* Forward scan */
if (uvc_scan_chain_forward(chain, entity, prev) < 0) //向前扫描实体
return -EINVAL;
/* Backward scan */
prev = entity; //当前实体作为下一次while循环的前一个实体
if (uvc_scan_chain_backward(chain, &entity) < 0) //向后扫描实体
return -EINVAL;
}
return 0;
}

将uvc视频链的输入实体添加到uvc视频链的entities链表中
将uvc视频链添加到uvc设备的chains链表中

10.3.1 扫描当前实体

static int uvc_scan_chain_entity(struct uvc_video_chain *chain,struct uvc_entity *entity)
{
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT: //扩展Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- XU %d", entity->id);
if (entity->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n", entity->id);
return -1;
}
break;
case UVC_VC_PROCESSING_UNIT: //处理Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- PU %d", entity->id);
if (chain->processing != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Processing Units in chain.\n");
return -1;
}
chain->processing = entity; //如果是处理Unit则设置其为uvc视频链的processing对象
break;
case UVC_VC_SELECTOR_UNIT: //选择器Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- SU %d", entity->id);
/* Single-input selector units are ignored. */
if (entity->bNrInPins == 1)
break;
if (chain->selector != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector Units in chain.\n");
return -1;
}
chain->selector = entity; //如果是选择器Unit则设置其为uvc视频链的selector对象
break;
case UVC_ITT_VENDOR_SPECIFIC: //厂商特殊
case UVC_ITT_CAMERA: //输入Terminal camera
case UVC_ITT_MEDIA_TRANSPORT_INPUT: //输入Terminal Media transport
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
break;
case UVC_TT_STREAMING: //输入Terminal stream
if (UVC_ENTITY_IS_ITERM(entity)) {
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
}
else {
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" OT %d", entity->id);
}
break;
default:
uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type 0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
return -1;
}
list_add_tail(&entity->chain, &chain->entities); //添加到uvc视频链的实体链表
return 0;
}

10.3.2 向前扫描实体

static int uvc_scan_chain_forward(struct uvc_video_chain *chain,struct uvc_entity *entity, struct uvc_entity *prev)
{
struct uvc_entity *forward;
int found;
/* Forward scan */
forward = NULL;
found = 0;
while (1) { //获取实体前面的所以实体处理直到前面的实体forward=NULL为止跳出死循环
forward = uvc_entity_by_reference(chain->dev, entity->id,forward); //获取前一个实体
if (forward == NULL)
break;
if (forward == prev)
continue;
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT: //扩展Unit
if (forward->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n",entity->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities); //添加uvc实体到uvc视频链的entities中
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" XU %d", forward->id);
found = 1;
}
break;
case UVC_OTT_VENDOR_SPECIFIC: //厂商特殊
case UVC_OTT_DISPLAY: //输出Termianl display
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: //输出Terminal media transport
case UVC_TT_STREAMING: //输出Terminal stream
if (UVC_ENTITY_IS_ITERM(forward)) {
uvc_trace(UVC_TRACE_DESCR, "Unsupported input terminal %u.\n", forward->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities); //添加uvc实体到uvc视频链的entities中
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" OT %d", forward->id);
found = 1;
}
break;
}
}
if (found)
printk(")");
return 0;
}

10.3.3 向后扫描实体

static int uvc_scan_chain_backward(struct uvc_video_chain *chain,struct uvc_entity **_entity)
{
struct uvc_entity *entity = *_entity;
struct uvc_entity *term;
int id = -EINVAL, i;
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT: //扩展Unit
case UVC_VC_PROCESSING_UNIT: //处理Unit处理Unit的输入Terminal个数只能为1
id = entity->baSourceID[0]; //获取输入pin(Unit/Terminal)的ID
break;
case UVC_VC_SELECTOR_UNIT: //选择器实体
/* Single-input selector units are ignored. */
if (entity->bNrInPins == 1) { //若输入pin个数为1
id = entity->baSourceID[0]; //获取输入in(Unit/Terminal)的ID
break;
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT");
chain->selector = entity; //uvc视频链的selector对象指向uvc实体
for (i = 0; i < entity->bNrInPins; ++i) { //总共有多少个输入pin
id = entity->baSourceID[i]; //获取输入in(Unit/Terminal)的ID
term = uvc_entity_by_id(chain->dev, id); //获取对应的输入pin实体
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d input %d isn't connected to an input terminal\n", entity->id, i);
return -1;
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);
list_add_tail(&term->chain, &chain->entities); //添加uvc实体到uvc视频链的entities链表
uvc_scan_chain_forward(chain, term, entity); //向前扫描实体
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk("\n");
id = 0;
break;
case UVC_ITT_VENDOR_SPECIFIC:
case UVC_ITT_CAMERA:
case UVC_ITT_MEDIA_TRANSPORT_INPUT:
case UVC_OTT_VENDOR_SPECIFIC:
case UVC_OTT_DISPLAY:
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
case UVC_TT_STREAMING:
id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
break;
}
if (id <= 0) {
*_entity = NULL;
return id;
}
entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to unknown entity %d.\n", id);
return -EINVAL;
}
*_entity = entity;
return 0;
}

注意到trace打印的语句会发现有一条

uvcvideo: Scanning UVC chain: OT 2 <- XU 5 <- XU 4 <- PU 3 <- IT 1

可以看到这些Unit和Terminal是如何组建起来的

这里补充一下:

1.打开trace:echo 0xffff > /sys/module/uvcvideo/par 消息用dmesg查看,清除dmesg信息带上-c参数就行

2.留意之前lsusb打印出来的描述符表,对应的bTerminalID就是trace打印信息中对应的Unit或Terminal的数字,而baSourceID则是它的前一级Unit或Terminal的ID号

10.3.4 添加链表

list_add_tail(&entity->chain, &chain->entities);

11.注册uvc视频链

11.1 uvc注册视频链

static int uvc_register_chains(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
int ret;
list_for_each_entry(chain, &dev->chains, list) { //遍历uvc设备的uvc视频链链表
ret = uvc_register_terms(dev, chain); //注册uvc视频链
if (ret < 0)
return ret;
}
return 0;
}

11.2 uvc注册实体

static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain)
{
struct uvc_streaming *stream;
struct uvc_entity *term;
int ret;
list_for_each_entry(term, &chain->entities, chain) { //遍历uvc视频链的uvc实体链表
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) //不是输入Terminal streaming类型
continue;
stream = uvc_stream_by_id(dev, term->id); //获取uvc视频流
if (stream == NULL) {
uvc_printk(KERN_INFO, "No streaming interface found for terminal %u.", term->id);
continue;
}
stream->chain = chain; //捆绑uvc视频流和uvc视频链
ret = uvc_register_video(dev, stream); //注册uvc视频流
if (ret < 0)
return ret;
}
return 0;
}

11.3 uvc注册视频

static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream)
{
struct video_device *vdev;
int ret;
/* Initialize the streaming interface with default streaming parameters.*/
ret = uvc_video_init(stream); //13.uvc视频初始化
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc(); //分配v4l2设备内存
if (vdev == NULL) {
uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",ret);
return -ENOMEM;
}
vdev->parent = &dev->intf->dev; //v4l2设备的父设备为usb接口设备
vdev->fops = &uvc_fops; //v4l2操作函数集
vdev->release = uvc_release; //释放方法
strlcpy(vdev->name, dev->name, sizeof vdev->name); //设置名字
stream->vdev = vdev; //捆绑uvc视频流和v4l2设备
video_set_drvdata(vdev, stream); //将uvc视频流作为v4l2设备的驱动数据
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); //注册v4l2设备
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",ret);
stream->vdev = NULL;
video_device_release(vdev);
return ret;
}
atomic_inc(&dev->nstreams);
return 0;
}

12.uvc设备状态初始化

uvc状态的处理由中断端点来控制处理

int uvc_status_init(struct uvc_device *dev)
{
struct usb_host_endpoint *ep = dev->int_ep; //获取usb_host_endpoint
unsigned int pipe;
int interval;
if (ep == NULL)
return 0;
uvc_input_init(dev); //初始化uvc输入设备
dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); //分配uvc设备状态内存
if (dev->status == NULL)
return -ENOMEM;
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb
if (dev->int_urb == NULL) {
kfree(dev->status);
return -ENOMEM;
}
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); //中断输入端点
interval = ep->desc.bInterval; //获取间隔
if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&(dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) //高速设备
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,dev, interval); //填充中断urb
return 0;
}

这里只填充了urb信息,urb的提交请参看14.2.2.1 uvc_status_start启动状态,在打开uvc的V4L2设备方法时调用

12.1 初始化uvc输入事件

static int uvc_input_init(struct uvc_device *dev)
{
struct input_dev *input;
int ret;
input = input_allocate_device(); //分配input设备内存
if (input == NULL)
return -ENOMEM;
usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); //设备节点路径
strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
input->name = dev->name; //输入设备名
input->phys = dev->input_phys; //输入设备节点路径
usb_to_input_id(dev->udev, &input->id);
input->dev.parent = &dev->intf->dev; //输入设备的父设备为usb接口设备
__set_bit(EV_KEY, input->evbit); //设置输入事件类型
__set_bit(KEY_CAMERA, input->keybit); //设置按钮
if ((ret = input_register_device(input)) < 0) //注册input设备
goto error;
dev->input = input; //uvc设备捆绑输入设备
return 0;
error:
input_free_device(input);
return ret;
}

12.2 urb回调函数

static void uvc_status_complete(struct urb *urb)
{
struct uvc_device *dev = urb->context;
int len, ret;
switch (urb->status) {
case 0:
break;
case -ENOENT: /* usb_kill_urb() called. */
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
case -EPROTO: /* Device is disconnected (reported by some host controller). */
return;
default:
uvc_printk(KERN_WARNING, "Non-zero status (%d) in status completion handler.\n", urb->status);
return;
}
len = urb->actual_length;
if (len > 0) {
switch (dev->status[0] & 0x0f) {
case UVC_STATUS_TYPE_CONTROL: //VC事件
uvc_event_control(dev, dev->status, len); //Table 2-2 Status Packet Format (VideoControl Interface as the Originator)
break;
case UVC_STATUS_TYPE_STREAMING: //VS事件
uvc_event_streaming(dev, dev->status, len); //Table 2-3 Status Packet Format (VideoStreaming Interface as the Originator)
break;
default
uvc_trace(UVC_TRACE_STATUS, "Unknown status event type %u.\n", dev->status[0]);
break;
}
}
/* Resubmit the URB. */
urb->interval = dev->int_ep->desc.bInterval;
if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { //提交urb
uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",ret);
}
}

12.2.1 VC状态变化事件处理

static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
{
char *attrs[3] = { "value", "info", "failure" };
if (len < 6 || data[2] != 0 || data[4] > 2) {//长度应该为6,且data[2](bEvent)为0表示(Control Change),data[4]大于2部分为保留值
uvc_trace(UVC_TRACE_STATUS, "Invalid control status event received.\n");
return;
}
uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",data[1], data[3], attrs[data[4]], len);
}

12.2.2 VS状态变化事件处理

static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
{
if (len < 3) { //长度等于4
uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event received.\n");
return;
}
if (data[2] == 0) { //data[2](bevent)--0x00(Button Press)摄像头上的按钮按下
if (len < 4)
return;
uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",data[1], data[3] ? "pressed" : "released", len);
uvc_input_report_key(dev, KEY_CAMERA, data[3]); //上报按键事件
}
else {
uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x ""len %d.\n", data[1], data[2], data[3], len);
}
}

uvc摄像头代码解析6的更多相关文章

  1. uvc摄像头代码解析1

    一.FAQ 1.判断自己的摄像头是否支持uvc标准 输入lsusb //列出usb设备 [cpp]   Bus 001 Device 001: ID 1d6b:0002 Linux Foundatio ...

  2. uvc摄像头代码解析5

    8.初始化uvc控制 8.1 重要结构体 struct uvc_control { //uvc控制 struct uvc_entity *entity; //uvc实体 struct uvc_cont ...

  3. uvc摄像头代码解析7

    13.uvc视频初始化 13.1 uvc数据流控制 struct uvc_streaming_control { __u16 bmHint; __u8 bFormatIndex; //视频格式索引 _ ...

  4. c#专业的UVC摄像头深控类库-SharpCamera介绍

    SharpCamera是专业的UVC摄像头深控类库.允许您在C#代码内修改摄像头的高级参数,比如亮度.对比度.清晰度.色调.饱和度.伽玛值.白平衡.逆光对比.增益.缩放.焦点.曝光.光圈.全景.倾斜. ...

  5. C#控制操控操作多个UVC摄像头设备

    有时,我们需要在C#代码中对多个UVC摄像头进行操作,如何实现呢? 建立基于SharpCamera的项目 首先,请根据之前的一篇博文 点击这里 中的说明,建立基于SharpCamera的摄像头控制项目 ...

  6. C#采集UVC摄像头画面并支持旋转和分辨率切换

    在项目中,我们会需要控制uvc摄像头,采集其实时画面,或者对其进行旋转.目前市面上大多数USB摄像头都支持UVC协议.那么如何采集呢?当然是采用SharpCamera!因为SharpCamera支持对 ...

  7. VBA常用代码解析

    031 删除工作表中的空行 如果需要删除工作表中所有的空行,可以使用下面的代码. Sub DelBlankRow() DimrRow As Long DimLRow As Long Dimi As L ...

  8. [nRF51822] 12、基础实验代码解析大全 · 实验19 - PWM

    一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指 ...

  9. [nRF51822] 11、基础实验代码解析大全 · 实验16 - 内部FLASH读写

     一.实验内容: 通过串口发送单个字符到NRF51822,NRF51822 接收到字符后将其写入到FLASH 的最后一页,之后将其读出并通过串口打印出数据. 二.nRF51822芯片内部flash知识 ...

随机推荐

  1. C#高级编程随笔

    1.把类创作的变量叫做对象2.类就是对象的模版3.类定义了每个对象的数据和功能4.接口不能被实例化,抽象类不能被实例化5.抽象基类可以包含非抽象方法,而接口只能包含抽象方法6.一个类可以实现多个接口7 ...

  2. access数据库

    //访问动态创建access数据库 string conn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Server.M ...

  3. Week5(10月10日):国庆之后,让我们整装期待元旦吧

    Part I:提问  =========================== 1.要将“Hello, Admin",从控制器传递到视图,该如何操作? (1)使用ViewData (2)使用V ...

  4. jquery删除动态增加的li

    <script type="text/jscript"> //楼主帮你修改调整了下 $(document).ready(function () { $('.zuo li ...

  5. iOS开发中关于本地数据中SQLite数据库常用的SQL语句

    创建表 CREATE TABLE IF NOT EXISTS "student" ("number" INTEGER PRIMARY KEY AUTOINCRE ...

  6. IOS中的数据存储 简单总结

      1.  NSKeyedArchiver(加密形式)   2.  plist   3.  NSUserDefaults   4.  writeToFile    5.  SQLite3 ==== N ...

  7. svn:怎样批量删除.svn文件

    怎样批量删除.svn文件 使用SVN工具的时候会生成一些以“svn”作为后缀的文件,而且每个文件夹下都有,如果想删除这些.svn文件夹,通过手动删除的渠道是很繁琐的事情. 通过以下的简单步骤可以在右键 ...

  8. .net嵌入c#代码(投票练习)

    .net嵌入c#代码(投票练习) <%@ Page Language="C#" AutoEventWireup="true" CodeFile=" ...

  9. JVM-- 先行发生原则

    本文中需要的基础知识:指令重排 线程中两个非常重要的问题就是:原子性与可见性. 而下面的先行发生原则就是用来解决可见性问题的. 先行发生原则--是判断是否存在数据竞争.线程是否安全的主要依据. 先行发 ...

  10. 18-UIKit(Core Animation、广播设计模式)

    目录: 一.Core Animation 二.广播设计模式 回到顶部 一.Core Animation 1.  是什么? 底层的动画框架 2.  框架对比 UIKit           UI     ...