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. 记录一次SQL查询语句

    以前发现比较经典的句子,都是记录在电脑上,我今天想搬到博客上,在我看来,写博客真的是一件非常头疼的事,它是内心的一道坎,我必须得跨过它. CREATE TABLE t_jeff ( id int NO ...

  2. 【Eclipse】报错提示删掉@Override

    是因为项目的JRE System Library版本不对,点击Edit进入Edit Library 界面,因为项目默认是使用Eclipse自带的jdk版本(Workspace default JRE) ...

  3. ListView判断滑动底部

    通过实现OnScrollListener这个接口,然后复写 public abstract void onScroll (AbsListView view, int firstVisibleItem, ...

  4. 树莓派常用Linux命令

    转自小五义 1.ls命令:列出文件目录的常用命令,主要参数见下表. -a 列出目录下的所有文件,包括以.开头的隐含文件. -b 把文件名中不可输出的字符用反斜杠加字符编号(就象在C语言里一样)的形式列 ...

  5. Git 配置editor编辑器

    Git 配置editor编辑器 在ubuntu系统下,Git默认的编辑器是命令行,学名叫V什么的,使用起来诸多不便 在编辑提交日志的时候,用的比较多. 可以选择unbuntu默认的文档编辑器作为git ...

  6. AzureDev 社区活动获奖者公布

    今天,我们高兴地宣布 AzureDev社区活动的获奖者,并向这 5 个非盈利技术教育组织发放 10 万美元奖金.在 2013 年的Build大会上宣布的 AzureDev 活动专注于通过代码改变世界, ...

  7. 凡客副总裁被曝离职:或因IPO受阻|凡客|王春焕|离职_互联网_新浪科技_新浪网

    凡客副总裁被曝离职:或因IPO受阻|凡客|王春焕|离职_互联网_新浪科技_新浪网 凡客副总裁被曝离职:或因IPO受阻 2013年05月07日 00:56   每日经济新闻    我有话说     每经 ...

  8. ListView中加入Button后,Button的点击事件和ListView的点击事件冲突

    1.在ItemView配置的xml文件里的根节点加入属性android:descendantFocusability="blocksDescendants" 2.在要加入事件的控件 ...

  9. 嵌入式环境:挂载开发板根NFS文件系统失败

    挂载开发板根NFS文件系统的时候,发现了下面的问题: Looking up port of RPC 100003/2 on 192.168.2.109rpcbind: server 192.168.2 ...

  10. 极光IM使用教程-极光推送

    链接地址:http://jingyan.baidu.com/article/a948d65178a6ea0a2ccd2e7e.html 极光IM使用教程,如果您的 App 需要同时集成 Push 功能 ...