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. Android 开机动画源码分析

    Android系统在启动SystemServer进程时,通过两个阶段来启动系统所有服务,在第一阶段启动本地服务,如SurfaceFlinger,SensorService等,在第二阶段则启动一系列的J ...

  2. C++拷贝构造函数详解

    转自:http://blog.csdn.net/lwbeyond/article/details/6202256 对于一个空类,编译器默认生成四个成员函数:默认构造函数.析构函数.拷贝构造函数.赋值函 ...

  3. Linux DM9000网卡驱动程序完全分析

    Linux DM9000网卡驱动程序完全分析http://blog.csdn.net/ypoflyer/article/details/6209922

  4. c语言 ,回调函数[个人理解]

    回调函数:把需要调用的方法的指针pCallBackFuncX作为参数传递给一个函数UsrFunction,以便该UsrFunction函数在处理相似事件的时候可以灵活的使用不同的方法.   以在fla ...

  5. postgres-xc手册生成方法

    步骤   检测编译环境  安装编译工具  编译 以上只在linux环境当中进行,本人所用系统ubuntu15.04 检测编译环境 在posgtgresql目录下运行./configure,并安装需要安 ...

  6. VS 2013上Python的配置

    最近有点不务正业,去看了下Python (主要是学校OJ有这个语言,然后可以轻松解决大数据问题,不要说我太坑~~~) 目前感觉python和matlab有些类似,缺少了变量类型声明,总感觉自己写出来的 ...

  7. hdu1284经典钱币兑换问题

    钱币兑换问题. 题目 http://acm.hdu.edu.cn/showproblem.php?pid=1284 完全背包. 这种是求背包问题最多的组合方案 参考了一些资料   http://blo ...

  8. (Problem 6)Sum square difference

    Hence the difference between the sum of the squares of the first ten natural numbers and the square ...

  9. Stackful 协程库 libgo(单机100万协程)

    libgo 是一个使用 C++ 编写的协作式调度的stackful协程库, 同时也是一个强大的并行编程库. 设计之初是为高并发分布式Linux服务端程序开发提供底层框架支持,可以让链接进程序的同步的第 ...

  10. 转:js包装DOM对象

    我们在日常的应用中,使用Javascript大多数时间都是在用DOM ,以致于很多人都有一种看法就是DOM==JS,虽然这种看法是错误的,但是也可以说明DOM的重要性. 这就导致了我们在写JS的时候, ...