v4l2框架usb-driver代码分析

v4l2框架usb-driver代码分析

参考链接

USB摄像头驱动–UVC驱动的深入理解与编写
uvc_driver代码分析
嵌入式Linux驱动笔记(十七)------详解V4L2框架(UVC驱动)

驱动调用流程

分析内核摄像头驱动

对于linux内核4.13.0中,UVC驱动在drivers/media/usb/uvc/文件夹里,下面对uvc_driver.c进行分析。

构造一个usb_driver结构体

该结构体相当于告诉内核我这是一个USB设备,需要进行USB设备的相关函数调用。(UVC:usb video class)这里进行usb的设置。
kernel/msm-4.14/drivers/media/usb/uvc/uvcvideo.h

struct uvc_driver {
	struct usb_driver driver;
};

kernel/msm-4.14/include/linux/usb.h ----------- struct usb_driver标识usbcore的USB接口驱动程序

struct usb_driver {
    //驱动程序名称在USB驱动程序中应是唯一的,通常应与模块名称相同。
>---const char *name;  
    
    //调用此函数以查看驱动程序是否愿意管理设备上的特定接口。
    //如果是,probe返回零,并使用usb_set_intfdata()将特定于驱动程序的数据与接口关联。
    //它还可以使用usb_set_interface()来指定适当的ALTSETING。
    //如果不愿意管理接口,则返回-ENODEV,如果发生真正的IO错误,则返回适当的负errno值。
>---int (*probe) (struct usb_interface *intf,
>--->---      const struct usb_device_id *id);
    //当接口不再可访问时调用,通常是因为其设备已(或正在)断开连接或驱动程序模块正在卸载。
>---void (*disconnect) (struct usb_interface *intf);

>---int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,
>--->--->---void *buf);
    //当设备将由系统从系统睡眠或运行时挂起上下文挂起时调用。
    //返回值将在系统睡眠上下文中被忽略,因此如果在这种情况下挂起失败,请不要尝试继续使用设备。
    //相反,让恢复或重置恢复例程从故障中恢复。
>---int (*suspend) (struct usb_interface *intf, pm_message_t message);
    //当系统恢复设备时调用。
>---int (*resume) (struct usb_interface *intf);
    //当挂起的设备被重置而不是恢复时调用。
>---int (*reset_resume)(struct usb_interface *intf);

>---int (*pre_reset)(struct usb_interface *intf);
>---int (*post_reset)(struct usb_interface *intf);
    //USB驱动程序使用ID表支持热插拔。
    //将其与MODULE_DEVICE_TABLE(usb,…)一起导出。必须设置此值,否则将永远不会调用驱动程序的探测函数。
>---const struct usb_device_id *id_table;

>---struct usb_dynids dynids;
>---struct usbdrv_wrap drvwrap;  //驱动程序模型结构的包装器
>---unsigned int no_dynamic_id:1;
>---unsigned int supports_autosuspend:1;
>---unsigned int disable_hub_initiated_lpm:1;
>---unsigned int soft_unbind:1;
};

struct uvc_driver uvc_driver = {
	.driver = {
		.name		= "uvcvideo",
		.probe		= uvc_probe,  //支持的video设备插入就会进入
		.disconnect	= uvc_disconnect,
		.suspend	= uvc_suspend,
		.resume		= uvc_resume,
		.reset_resume	= uvc_reset_resume,
		.id_table	= uvc_ids,         //  .id_table 表示该USB驱动支持哪些设备
		.supports_autosuspend = 1,
	},
};

设置结构体uvc_probe代码分析

设置相当于将其中的函数定一下来,一开始可以写作空函数,以便判定框架是否正确。在probe函数中进行这个结构体的具体内容进行设置,包括分配video_device结构体,设置结构体,注册video_device结构体,fops结构体中需要进行ioctrl的具体操作进行设置。

uvc_probe
    kzalloc //分配video_device
        uvc_register_chains  
            uvc_register_terms  
                uvc_register_video
                    vdev->v4l2_dev = &dev->vdev; //设置video_device
                    vdev->fops = &uvc_fops; 
                    vdev->ioctl_ops = &uvc_ioctl_ops;
                    vdev->release = uvc_release;
                    video_register_device //注册video_device

uvc_probe代码分析,当特定的usb设备被插入时,就会触发probe函数:
函数太长了,省略了部分内容,但是可以看出,主要的就是做几件事情:
【1】分配一个dev
【2】给dev设置各种参数,如dev->udevudev
【3】调用uvc_parse_control函数分析设备的控制描述符
【4】调用v4l2_device_register函数初始化v4l2_dev
【5】调用uvc_ctrl_init_device函数初始化uvc控制设备
【6】调用uvc_register_chains函数注册所有通道
【7】调用uvc_status_init函数初始化uvc状态

static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct uvc_device *dev;
    int ret;

    //完成产品相关信息的打印工作
    /*
    #define uvc_trace(flag, msg...) \
    do { \
        if (uvc_trace_param & flag) \
            printk(KERN_DEBUG "uvcvideo: " msg); \
      } while (0)
      uvc_trace_param这个全局变量是通过用户空间来传值实现的
    */
    if (id->idVendor && id->idProduct)
        uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
                "(%04x:%04x)\n", udev->devpath, id->idVendor,
                id->idProduct);
    else
        uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
                udev->devpath);

    /* Allocate memory for the device and initialize it. */
    if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)  //【1】分配一个dev
        return -ENOMEM;

    /*初始化链表*/
    INIT_LIST_HEAD(&dev->entities);//初始化entities(实体)链表 Terminal或Unit
    INIT_LIST_HEAD(&dev->chains);//初始化chains(链)链表
    INIT_LIST_HEAD(&dev->streams);//初始化streams(视频流)链表 

    /*设置原子变量的值,以便后续做原子操作*/
    atomic_set(&dev->nstreams, 0);
    atomic_set(&dev->users, 0);
    atomic_set(&dev->nmappings, 0);

    /*
    //以下两个函数在写法上使用container_of()只需要简单的操作就能返回原结构体
    usb_get_dev():通过struct usb_device --> struct device --> struct kobject
    增加引用计数
    usb_get_intf():通过struct usb_interface --> struct device --> struct kobject
    增加引用计数
    同时将struct usb_device及其struct usb_interface赋值给uvc结构体中的struct usb_device
    和struct usb_interface 结构体成员
    */
    dev->udev = usb_get_dev(udev);    //【2】给dev设置各种参数,如dev->udevudev
    dev->intf = usb_get_intf(intf);

    /*通过接口找到接口描述符,这样得到接口数量*/
    dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;

    //quirks: 怪癖的意思,也就是说它某种特性与通常的USB设备不相同
    /*uvc_quirks_param 这个全局变量来自用户空间的输入*/
    dev->quirks = (uvc_quirks_param == -1)
            ? id->driver_info : uvc_quirks_param;

    /*
    此段代码的主要功能是将strcut usb_device 中的一些产品的
    信息(如产品号、版本号等)写入到struct uvc_device中,
    主要涉及到 struct uvc_device -> char name[32]
    */
    if (udev->product != NULL)
        strlcpy(dev->name, udev->product, sizeof dev->name);
    else
        snprintf(dev->name, sizeof dev->name,
            "UVC Camera (%04x:%04x)",
            le16_to_cpu(udev->descriptor.idVendor),
            le16_to_cpu(udev->descriptor.idProduct));

    /* Parse the Video Class control descriptor. */
    /*uvc解析usb视频类控制描述符*/
    /*整个函数都是围绕着struct usb_host_interface 中的extra成员展开的*/
    if (uvc_parse_control(dev) < 0) {       //【3】调用uvc_parse_control函数分析设备的控制描述符
        uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
            "descriptors.\n");
        goto error;
    }

    uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
        dev->uvc_version >> 8, dev->uvc_version & 0xff,
        udev->product ? udev->product : "<unnamed>",
        le16_to_cpu(udev->descriptor.idVendor),
        le16_to_cpu(udev->descriptor.idProduct));

    if (dev->quirks != id->driver_info) {
        uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
            "parameter for testing purpose.\n", dev->quirks);
        uvc_printk(KERN_INFO, "Please report required quirks to the "
            "linux-uvc-devel mailing list.\n");
    }

    /* Register the media and V4L2 devices. */
    /*
    以下的操作均为对 struct uvc_device --> struct media_device 的
    成员进行赋值操作,赋值的成员如下:
    struct device *dev
    char model[32]
    char serial[40]
    char bus_info[32]
    u32 hw_revision
    u32 driver_version
    然后使用了media_device_register()函数进行media设备的注册工作
    */
#ifdef CONFIG_MEDIA_CONTROLLER
    dev->mdev.dev = &intf->dev;
    strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
    if (udev->serial)
        strlcpy(dev->mdev.serial, udev->serial,
            sizeof(dev->mdev.serial));
    strcpy(dev->mdev.bus_info, udev->devpath);
    dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
    dev->mdev.driver_version = DRIVER_VERSION_NUMBER;

    /*                      
    1.在下面的函数中发现了struct media_file_operations 其中有open、release、ioctl函数。
    但是上述的函数都是空函数,没有任何意义
    */
    if (media_device_register(&dev->mdev) < 0)
        goto error;
    /*
    struct uvc_device --> strcut v412_device vdev -->struct media_device
    */
    dev->vdev.mdev = &dev->mdev;
#endif
    /*
    【4】调用v4l2_device_register函数初始化v4l2_dev
    【5】调用uvc_ctrl_init_device函数初始化uvc控制设备
    【6】调用uvc_register_chains函数注册所有通道
    【7】调用uvc_status_init函数初始化uvc状态
    */
    if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
        goto error;

    /* Initialize controls. */
    if (uvc_ctrl_init_device(dev) < 0)
        goto error;

    /* Scan the device for video chains. */
    if (uvc_scan_device(dev) < 0)
        goto error;

    /* Register video device nodes. */
    /*      下面这个函数存在着以下调用关系
        uvc_register_chains() --> uvc_register_terms() --> uvc_register_video()

        函数初始化关系图如下:见笔记章节

        其中uvc_register_video()函数非常重要!
        因为这个函数中存在着 v4l2操作函数集(uvc_fops) 和 真正的ioctl操作
        集(3.19以后的内核才存在vdev->ioctl_ops = &uvc_ioctl_ops;)

        vdev->fops = &uvc_fops;
        vdev->release = uvc_release;
    */
    if (uvc_register_chains(dev) < 0)
        goto error;

    /* Save our data pointer in the interface data. */
    usb_set_intfdata(intf, dev);

    /* Initialize the interrupt URB. */
    /*
      uvc_status_init()这个函数主要有三个作用:
      1.动态申请一个urb(interrupt urb)
      2.给struct uvc_device --> _u8 *status 申请一个内存空间(作为urb的缓冲区)
      3.初始化中断urb
    */
    if ((ret = uvc_status_init(dev)) < 0) {
        uvc_printk(KERN_INFO, "Unable to initialize the status "
            "endpoint (%d), status interrupt will not be "
            "supported.\n", ret);
    }

    uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
    //打开usb设备的自动挂起功能,以便实现低功耗的要求
    usb_enable_autosuspend(udev);
    return 0;

error:
    uvc_unregister_video(dev);
    return -ENODEV;
}
uvc_parse_control函数

我们来一个个分析下:
【3】:调用uvc_parse_control函数
看下调用关系:

uvc_parse_control(dev)
    uvc_parse_standard_control(dev, buffer, buflen)
        uvc_parse_streaming(dev, intf)      

跟踪下uvc_parse_streaming函数:

static int uvc_parse_streaming(struct uvc_device *dev,
    struct usb_interface *intf)
{
    /*以下大部分内容省略,只显示重要的*/
    struct uvc_streaming *streaming = NULL;
    struct uvc_format *format;
    struct uvc_frame *frame;

    streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
    size = nformats * sizeof *format + nframes * sizeof *frame
         + nintervals * sizeof *interval;

    format = kzalloc(size, GFP_KERNEL);//申请format数组存放格式
    streaming->format = format;//设置格式
    streaming->nformats = nformats;//最多支持nformats种格式
    ret = uvc_parse_format(dev, streaming, format,
                &interval, buffer, buflen);//分析格式
    list_add_tail(&streaming->list, &dev->streams);
    return 0;
}

这里面申请了streaming和format内存
streaming是uvc_streaming 结构体,视频流,很重要,大部分参数都是存在里面。这函数里申请了之后进行了很多设置,不过现在我省略了写。
format内存存放的是视频的格式,frame存放的是如分辨率
这里面都把他设置到了streaming里面(streaming->format = format;streaming->nformats = nformats;)
最后调用uvc_parse_format函数分析格式:

static int uvc_parse_format()
{
    fmtdesc = uvc_format_by_guid(&buffer[5]);//通过GUID找到格式format
    /*里面还会对frame进行各种分析和设置,
     *如设置format->nframes得出最多有多少种分辨率选择
     *暂时忽略*/
}

里面uvc_format_by_guid函数会从uvc_fmts数组中通过匹配guid找到格式:

static struct uvc_format_desc uvc_fmts[] = {
    {
        .name       = "YUV 4:2:2 (YUYV)",
        .guid       = UVC_GUID_FORMAT_YUY2,
        .fcc        = V4L2_PIX_FMT_YUYV,
    },
    {
        .name       = "YUV 4:2:2 (YUYV)",
        .guid       = UVC_GUID_FORMAT_YUY2_ISIGHT,
        .fcc        = V4L2_PIX_FMT_YUYV,
    },
    {
        .name       = "YUV 4:2:0 (NV12)",
        .guid       = UVC_GUID_FORMAT_NV12,
        .fcc        = V4L2_PIX_FMT_NV12,
    },
    {
        .name       = "MJPEG",
        .guid       = UVC_GUID_FORMAT_MJPEG,
        .fcc        = V4L2_PIX_FMT_MJPEG,
    },
    /*后面省略......*/
}

v4l2_device_registe函数

这样【3】的工作就完成了,我们来看下【4】的:

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
    INIT_LIST_HEAD(&v4l2_dev->subdevs);//用来管理v4l2_device 下的subdevs实例
    spin_lock_init(&v4l2_dev->lock);
    v4l2_prio_init(&v4l2_dev->prio);
    kref_init(&v4l2_dev->ref);
    get_device(dev);
    v4l2_dev->dev = dev;
    if (!v4l2_dev->name[0])
        snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
            dev->driver->name, dev_name(dev));
    if (!dev_get_drvdata(dev))//dev->driver_data 域 为 NULL
        dev_set_drvdata(dev, v4l2_dev);//就将其指向 v4l2_dev
    return 0;
}

简单,没啥好讲的,就是初始化v4l2_dev->subdevs子设备实例的链表,然后设置名字和设置dev->driver_data

uvc_ctrl_init_device函数

看下【5】调用uvc_ctrl_init_device

int uvc_ctrl_init_device(struct uvc_device *dev)
{
    /*省略了部分内容*/
    list_for_each_entry(entity, &dev->entities, list) {
        bmControls = entity->extension.bmControls;//控制位图
        bControlSize = entity->extension.bControlSize;//控制位域大小
        entity->controls = kcalloc(ncontrols, sizeof(*ctrl),
                       GFP_KERNEL);//分配ncontrols个uvc控制内存
        if (entity->controls == NULL)
            return -ENOMEM;
        entity->ncontrols = ncontrols;//设置uvc控制个数

        /* Initialize all supported controls */
        ctrl = entity->controls;//指向uvc控制数组
        for (i = 0; i < bControlSize * 8; ++i) {
            if (uvc_test_bit(bmControls, i) == 0)//跳过控制位域设置0的
                continue;
            ctrl->entity = entity;
            ctrl->index = i;//设置控制位域索引
            uvc_ctrl_init_ctrl(dev, ctrl);//初始化uvc控件
            ctrl++;//uvc控制 指向下一个uvc控制数组项
        }
    }
}

uvc_ctrl_init_device主要就是初始化控制参数,里面就会遍历uvc设备实体entities链表,然后设置位图和位域大小
最后还会调用uvc_ctrl_init_ctrl函数设置背光,色温等等
————————————————————————————————————————————————

int uvc_ctrl_init_device(struct uvc_device *dev)
{
    struct uvc_entity *entity;
    unsigned int i;

    /* Walk the entities list and instantiate controls */
    list_for_each_entry(entity, &dev->entities, list) {
        struct uvc_control *ctrl;
        unsigned int bControlSize = 0;//控制位域大小
        unsigned int ncontrols = 0;//控制组件个数
        __u8 *bmControls = NULL;//控制位图

        /*判断每次遍历到的实体的类型,并对类型相应的参数进行赋值*/
        if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
            bmControls = entity->extension.bmControls;
            bControlSize = entity->extension.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
            bmControls = entity->processing.bmControls;
            bControlSize = entity->processing.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
            bmControls = entity->camera.bmControls;
            bControlSize = entity->camera.bControlSize;
        }

        /* Remove bogus/blacklisted controls 移除假的/黑名单控制组件*/
        uvc_ctrl_prune_entity(dev, entity);

        /* Count supported controls and allocate the controls array */
        for (i = 0; i < bControlSize; ++i)
            /*
            hweight8()函数的功能:能快速得出一个字段内有多少个bit是1

            bmControls 中的每一位表示一种控制组件,所以我们只需要通过使用
            hweight8()函数就可以得出我们控制组件的个数

            又因为 bmControls 的数据类型为 __u8 所以我们将数据分为三组存储,
            这就是这里需要用到for循环的原因(bmControls 这个数据实际存在24位)
            */
            ncontrols += hweight8(bmControls[i]);
        if (ncontrols == 0)
            continue;
        /*给每一个实体中的所有要使用到的控制组件都申请一段内存*/
        entity->controls = kzalloc(ncontrols * sizeof(*ctrl),
                       GFP_KERNEL);
        if (entity->controls == NULL)
            return -ENOMEM;
        entity->ncontrols = ncontrols;

        /* Initialize all supported controls */
        ctrl = entity->controls;
        for (i = 0; i < bControlSize * 8; ++i) {
            if (uvc_test_bit(bmControls, i) == 0)
                continue;

            ctrl->entity = entity;
            ctrl->index = i;

            uvc_ctrl_init_ctrl(dev, ctrl);//初始化uvc控制组件
            ctrl++;
        }
    }

    return 0;
}
uvc_ctrl_init_ctrl
static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
{
    const struct uvc_control_info *info = uvc_ctrls;
    const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
    const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
    const struct uvc_control_mapping *mend =
        mapping + ARRAY_SIZE(uvc_ctrl_mappings);

    /* XU controls initialization requires querying the device for control
     * information. As some buggy UVC devices will crash when queried
     * repeatedly in a tight loop, delay XU controls initialization until
     * first use.
     */
    if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
        return;

    /*
    uvc_entity_match_guid()函数的主要作用:
    1.首先通过传入的uvc_control中的实体成员判断其实体的类型,主要是筛选以下
      四种类型(UVC_ITT_CAMERA、UCV_ITT_MEDIA_TRANSPORT_INPUT、UVC_VC_PROCESSING_UNIT、
      UVC_VC_EXTENSION_UNIT)
    2.然后将控制信息(uvc_control_info)中的实体标识与默认的类型标识做对比,如果一致
      则不需要添加新的控制信息,否则需要使用uvc_ctrl_add_info()去添加
    */
    for (; info < iend; ++info) {
        if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
            ctrl->index == info->index) {
            uvc_ctrl_add_info(dev, ctrl, info);
            break;
         }
    }

    if (!ctrl->initialized)
        return;

    for (; mapping < mend; ++mapping) {
        if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
            ctrl->info.selector == mapping->selector)
            __uvc_ctrl_add_mapping(dev, ctrl, mapping);
    }
}
uvc_scan_device函数
 /*
    struct uvc_entity 这个结构体描述的主要为uvc协议中的:
    IT、OT、PU、SU、EU、XU 这些实体
*/
static int uvc_scan_device(struct uvc_device *dev)
{
    struct uvc_video_chain *chain;//uvc视频链
    struct uvc_entity *term;//uvc实体

    /*
    遍历整个实体链表
    循环遍历每一个term中的list子项, &dev->entities为链表头
    */
    list_for_each_entry(term, &dev->entities, list) {

    /*
    获取实体链表中的输出Terminal实体 
    通过struct uvc_entity --> __u16 type 来判断Terminal的类型

    #define UVC_ENTITY_IS_TERM(entity)  (((entity)->type & 0xff00) != 0)    
    #define UVC_ENTITY_IS_OTERM(entity) \
        (UVC_ENTITY_IS_TERM(entity) && \
        ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)

    这里主要完成了两个判断:
    (其中的0x8000为判断Terminal类型的掩码,0xff00位判断实体类型的掩码)
    1.判断这个实体是否为Terminal 
    2.判断这个实体是否为output Terminal
    */
        if (!UVC_ENTITY_IS_OTERM(term))
            continue;

        /* If the terminal is already included in a chain, skip it.
         * This can happen for chains that have multiple output
         * terminals, where all output terminals beside the first one
         * will be inserted in the chain in forward scans.
         */
        if (term->chain.next || term->chain.prev)
            continue;

        chain = kzalloc(sizeof(*chain), GFP_KERNEL);
        if (chain == NULL)
            return -ENOMEM;
        /*初始化uvc视频链entities(实体)链表*/
        INIT_LIST_HEAD(&chain->entities);
        mutex_init(&chain->ctrl_mutex);
        /*捆绑uvc视频链和uvc设备*/
        chain->dev = dev;

        /*
        (自己的观点)
        uvc_scan_chain()这个函数主要作用扫描到的每个实体经过
        类型判断及其相关信息打印后,将实体添加到uvc视频链
        */
        if (uvc_scan_chain(chain, term) < 0) {
            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);
    }

    if (list_empty(&dev->chains)) {
        uvc_printk(KERN_INFO, "No valid video chain found.\n");
        return -1;
    }

    return 0;
}
uvc_register_chains函数

接下来继续看【6】调用uvc_register_chains函数:
调用关系

uvc_register_chains
    uvc_register_terms(dev, chain)
        uvc_stream_by_id
        uvc_register_video
    uvc_mc_register_entities(chain)
uvc_register_terms
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) {
        /*如果判断此实体的类型不是streaming的话则重新遍历*/
        if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
            continue;
        /*找到每个实体相对应的视频流(也就是说找到与之对应的 struct uvc_streaming 这个结构体)*/
        stream = uvc_stream_by_id(dev, term->id);
        if (stream == NULL) {
            uvc_printk(KERN_INFO, "No streaming interface found "
                   "for terminal %u.", term->id);
            continue;
        }
        /*将uvc视频链和uvc流中的uvc视频链进行绑定*/
        stream->chain = chain;
        /*对其中的uvc视频流做初始化工作,并初始化视频设备且注册*/
        ret = uvc_register_video(dev, stream);
        if (ret < 0)
            return ret;
        /*将uvc实体中的struct video_device和uvc流中的struct video_device进行绑定*/
        term->vdev = stream->vdev;
    }

    return 0;
}

uvc_stream_by_id函数会通过函数传入的id和dev->streams链表的header.bTerminalLink匹配,寻找到stream
这不是重点,我们的重点是uvc_register_video函数,找到stream会就要注册:

static int uvc_register_video(struct uvc_device *dev,
        struct uvc_streaming *stream)
{
    /*部分内容省略......*/
    struct video_device *vdev = &stream->vdev;

    ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);//初始化队列
    ret = uvc_video_init(stream);//初始化

    uvc_debugfs_init_stream(stream);
    vdev->v4l2_dev = &dev->vdev;
    vdev->fops = &uvc_fops;//v4l2操作函数集
    vdev->ioctl_ops = &uvc_ioctl_ops;//设置真正的ioctl操作集
    vdev->release = uvc_release;//释放方法
    vdev->prio = &stream->chain->prio;

    strlcpy(vdev->name, dev->name, sizeof vdev->name);
    video_set_drvdata(vdev, stream);//将uvc视频流作为v4l2设备的驱动数据

    ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);//注册
    return 0;
}

这是非常重要的函数,我们来一点一点分析:
看下uvc_queue_init函数,队列初始化,队列这东西,我们视频传输时会调用到,在ioctl里操作:

static struct vb2_ops uvc_queue_qops = {
    .queue_setup = uvc_queue_setup,
    .buf_prepare = uvc_buffer_prepare,
    .buf_queue = uvc_buffer_queue,
    .buf_finish = uvc_buffer_finish,
    .wait_prepare = vb2_ops_wait_prepare,
    .wait_finish = vb2_ops_wait_finish,
    .start_streaming = uvc_start_streaming,
    .stop_streaming = uvc_stop_streaming,
};
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
            int drop_corrupted)
{
    queue->queue.type = type;
    queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
    queue->queue.drv_priv = queue;
    queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
    queue->queue.ops = &uvc_queue_qops;//stream->queue->queue.ops
    queue->queue.mem_ops = &vb2_vmalloc_memops;
    queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
        | V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
    queue->queue.lock = &queue->mutex;
    ret = vb2_queue_init(&queue->queue);//初始化queue

    mutex_init(&queue->mutex);
    spin_lock_init(&queue->irqlock);
    INIT_LIST_HEAD(&queue->irqqueue);//初始化stream->queue->irqqueue
    queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;

    return 0;
}

里面先对队列进行初始化设置,如设置type和ops。
这里queue->queue.ops = &uvc_queue_qops非常重要,之后我们调用vidioc_streamon回调函数时就是调用到这里的uvc_queue_qops结构体里的.start_streaming函数
这函数里对各种队列进行了初始化:

vb2_queue_init(&queue->queue)
    q->buf_ops = &v4l2_buf_ops;
    vb2_core_queue_init(struct vb2_queue *q)
        INIT_LIST_HEAD(&q->queued_list);//stream->queue->queue->queued_list
        INIT_LIST_HEAD(&q->done_list);//stream->queue->done_list
INIT_LIST_HEAD(&queue->irqqueue);//初始化stream->queue->irqqueue

我们继续看回uvc_register_video函数,里面接着调用了uvc_video_init函数初始化UVC视频设备:
完整:

int uvc_video_init(struct uvc_streaming *stream)
{
    struct uvc_streaming_control *probe = &stream->ctrl;
    struct uvc_format *format = NULL;
    struct uvc_frame *frame = NULL;
    unsigned int i;
    int ret;

    /*首先检查UVC视频流中是否有格式信息,如果没有则返回错误*/
    if (stream->nformats == 0) {
        uvc_printk(KERN_INFO, "No supported video formats found.\n");
        return -EINVAL;
    }

    atomic_set(&stream->active, 0);

    /* Initialize the video buffers queue. */
    /*初始化队列,并且通过队列来管理视频缓冲区*/
    uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);

    /* Alternate setting 0 should be the default, yet the XBox Live Vision
     * Cam (and possibly other devices) crash or otherwise misbehave if
     * they don't receive a SET_INTERFACE request before any other video
     * control request.
     */
     /*这个函数功能在自己看来作用为:改变指定usb设备的接口设置*/
    usb_set_interface(stream->dev->udev, stream->intfnum, 0);

    /* Set the streaming probe control with default streaming parameters
     * retrieved from the device. Webcams that don't suport GET_DEF
     * requests on the probe control will just keep their current streaming
     * parameters.
     */
     /*
        uvc_get_video_ctrl()获得uvc流的默认参数设置,如果参数一切正常将返回0,
        否则返回负值
        uvc_set_video_ctrl()设置uvc视频控制  
    */
    if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
        uvc_set_video_ctrl(stream, probe, 1);

    /* Initialize the streaming parameters with the probe control current
     * value. This makes sure SET_CUR requests on the streaming commit
     * control will always use values retrieved from a successful GET_CUR
     * request on the probe control, as required by the UVC specification.
     */
     /*获取当前的控制值,确认上面的设置是否正确*/
    ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
    if (ret < 0)
        return ret;

    /* Check if the default format descriptor exists. Use the first
     * available format otherwise.
     */
    for (i = stream->nformats; i > 0; --i) {
        format = &stream->format[i-1];
        if (format->index == probe->bFormatIndex)
            break;
    }

    /*检查frame的选择数量,如果为0则返回错误值*/
    if (format->nframes == 0) {
        uvc_printk(KERN_INFO, "No frame descriptor found for the "
            "default format.\n");
        return -EINVAL;
    }

    /* Zero bFrameIndex might be correct. Stream-based formats (including
     * MPEG-2 TS and DV) do not support frames but have a dummy frame
     * descriptor with bFrameIndex set to zero. If the default frame
     * descriptor is not found, use the first available frame.
     */
    for (i = format->nframes; i > 0; --i) {
        frame = &format->frame[i-1];
        if (frame->bFrameIndex == probe->bFrameIndex)
            break;
    }

    //设置uvc视频流控制的格式索引为uvc格式的索引
    probe->bFormatIndex = format->index;
    //设置uvc视频流控制的帧索引为uvc帧索引
    probe->bFrameIndex = frame->bFrameIndex;

    /*设置UVC视频流的当前的UVC视频格式及其UVC视频帧*/
    stream->cur_format = format;
    stream->cur_frame = frame;
            .
            .
         省     略
            .
            .
    return 0;
}

————————————————————————————————————

int uvc_video_init(struct uvc_streaming *stream)
{
    /*省略部分内容*/
    struct uvc_streaming_control *probe = &stream->ctrl;//获取uvc数据流的uvs数据流控制对象

    if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)//先得到定义的控制参数
        uvc_set_video_ctrl(stream, probe, 1);//再设置uvc视频控制
    ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);//最后在get一次
    for (i = stream->nformats; i > 0; --i) {
        format = &stream->format[i-1];//获取对应的uvc格式
        if (format->index == probe->bFormatIndex)
            break;
    }
    probe->bFormatIndex = format->index;//设置uvc视频流控制的格式索引为uvc格式的索引
    probe->bFrameIndex = frame->bFrameIndex;//设置uvc视频流控制的分辨率索引为uvc分辨率的索引

    stream->def_format = format;
    stream->cur_format = format;//设置uvc格式为uvc数据流的cur_format成员
    stream->cur_frame = frame;//设置uvc帧为uvc数据流的cur_frame成员

    if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {//视频采集
        if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
            stream->decode = uvc_video_decode_isight;
        else if (stream->intf->num_altsetting > 1)
            stream->decode = uvc_video_decode_isoc;//同步方式
        else
            stream->decode = uvc_video_decode_bulk;//bluk方式
    } 
    return 0;
}

这里面内容就比较多了,先得到,然后设置uvc的控制参数,里面会操作urb发出usb数据。
然后通过probe->bFormatIndex索引找到使用的format格式和通过probe->bFrameIndex找到对应的frame分辨率,然后设置到stream里。
最后选择解码方式,如同步方式或者bluk方式,解码方式会在数据完成时被回调函数complete里调用。

再次回到uvc_register_video函数,没办法,这个函数太重要了:
里面继续:

    vdev->fops = &uvc_fops;//v4l2操作函数集
    vdev->ioctl_ops = &uvc_ioctl_ops;//设置真正的ioctl操作集
    vdev->release = uvc_release;//释放方法
    ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);

里面就是vdev->v4l2_dev = &dev->vdev;这样v4l2_device就与video_device关联起来,也就是我们文章一开始那个图看到的。
然后设置fops操作函数vdev->fops = &uvc_fops,虽然这不是给用户空间使用的open、read、write函数,但是最后vdev->cdev->ops还是最调用到这个uvc_fops的,所以用户空间实际上的pen、read、write函数还是会在这调用。 然后ioctl操作函数最终是会调用到vdev->ioctl_ops = &uvc_ioctl_ops。可以说,V4L2最重要的就是各种形式的ioctl了,这里先不讲,下一节在分析看看。
然后最终就是我们的注册函数了:video_register_device里调用到__video_register_device函数:

int __video_register_device(struct video_device *vdev, int type, int nr,
        int warn_if_nr_in_use, struct module *owner)
{
    /*省略部分函数*/
    vdev->minor = -1;//-1表明这个video device从未被注册过
    switch (type) {//根据type选择设备名称
    case VFL_TYPE_GRABBER:
        name_base = "video";
        break;
    case VFL_TYPE_VBI:
        name_base = "vbi";
        break;
    case VFL_TYPE_RADIO:
        name_base = "radio";
        break;
    case VFL_TYPE_SUBDEV:
        name_base = "v4l-subdev";
        break;
    case VFL_TYPE_SDR:
        name_base = "swradio";
        break;
    default:
        printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type);
        return -EINVAL;
    }
    switch (type) {//选择得到次设备号偏移值
    case VFL_TYPE_GRABBER://用于视频输入/输出设备的 videoX
        minor_offset = 0;
        minor_cnt = 64;
        break;
    case VFL_TYPE_RADIO://用于广播调谐器的 radioX
        minor_offset = 64;
        minor_cnt = 64;
        break;
    case VFL_TYPE_VBI://用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视)
        minor_offset = 224;
        minor_cnt = 32;
        break;
    default:
        minor_offset = 128;
        minor_cnt = 64;
        break;
    }
    nr = devnode_find(vdev, 0, minor_cnt);//获取一个没有被使用的设备节点序号
    for (i = 0; i < VIDEO_NUM_DEVICES; i++)
        if (video_device[i] == NULL)//从video_device[]数组中选择一个空缺项,这个空缺项的索引值放到i中
            break;
    vdev->minor = i + minor_offset;//设备的次设备号
    video_device[vdev->minor] = vdev;//注意:将设置好的video_device放入到video_device[]
    vdev->cdev->ops = &v4l2_fops;//操作用户空间操作函数集
    ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);//添加字符设备到系统
    ret = device_register(&vdev->dev);//设备注册
    set_bit(V4L2_FL_REGISTERED, &vdev->flags);//将flags第0为设置为1,表示这个video_device是注册过的了

    return 0;

}

我们梳理一下里面做的事情:
1.确定设备名称,也就是我们在/dev/下生成的video啊,radio之类的
2.得到次设备的偏移值
3.找到一个空的video_device数组,把vdev存进去
4.设置vdev->cdev,这里就设置了vdev->cdev->ops = &v4l2_fops;里面就是真正的用户空间操作集合
5.注册video_device设备
6.就是标志此video_device以注册

最后【6】调用uvc_register_chains函数里还会调用一个uvc_mc_register_entities函数,里面继续调用uvc_mc_init_entity函数,这就是v4l2_device_register_subdev函数,进行注册v4l2_subdev,同时初始化然后连接到v4l2_dev->subdevs管理。

好了,【6】调用uvc_register_chains函数:就分析完了,我们最后剩一个了:

uvc_status_init函数
int uvc_status_init(struct uvc_device *dev)
{
    /*省略部分函数*/
    struct usb_host_endpoint *ep = dev->int_ep;//获取usb_host_endpoint

    uvc_input_init(dev);//初始化uvc输入设备,里面注册input设备
    dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);//分配urb设备状态内存
    dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);//分配urb
    pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);//中断输入端点
    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的一些东西了,看看就好。

最后,我们用户空间怎么才操作的?
看看__video_register_device函数里的:vdev->cdev->ops = &v4l2_fops;

static const struct file_operations v4l2_fops = {
    .owner = THIS_MODULE,
    .read = v4l2_read,
    .write = v4l2_write,
    .open = v4l2_open,
    .get_unmapped_area = v4l2_get_unmapped_area,
    .mmap = v4l2_mmap,
    .unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = v4l2_compat_ioctl32,
#endif
    .release = v4l2_release,
    .poll = v4l2_poll,
    .llseek = no_llseek,
};
static int v4l2_open(struct inode *inode, struct file *filp)
{
    /*省略部分函数*/
    struct video_device *vdev;
    vdev = video_devdata(filp);//根据次设备号从video_devices[]数组中得到video_device
    if (vdev->fops->open) {
        if (video_is_registered(vdev))
            ret = vdev->fops->open(filp);//实际就是vdev->fops
        else
            ret = -ENODEV;
    }
}

记得我们之前把video_device放入到video_device[]吗?就是这里取了出来
然后调用vdev->fops->open(filp)

vdev->fops就是我们在uvc_register_video函数里设置的:
vdev->fops = &uvc_fops
const struct v4l2_file_operations uvc_fops = {//实际的用户操作
    .owner      = THIS_MODULE,
    .open       = uvc_v4l2_open,
    .release    = uvc_v4l2_release,
    .unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT
    .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
    .read       = uvc_v4l2_read,
    .mmap       = uvc_v4l2_mmap,
    .poll       = uvc_v4l2_poll,
#ifndef CONFIG_MMU
    .get_unmapped_area = uvc_v4l2_get_unmapped_area,
#endif
};

至于这个uvc_fops 里的回调函数,特别是ioctl,这是V4L2的重头,就在下一章试着分析吧,我对这个也是比较模糊……
嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl

注册结构体

uvc_init
static int __init (void)
{
	int ret;

	uvc_debugfs_init();

	ret = usb_register(&uvc_driver.driver);
	if (ret < 0) {
		uvc_debugfs_cleanup();
		return ret;
	}

	return 0;
}

/* use a define to avoid include chaining to get THIS_MODULE & friends */
#define usb_register(driver) \
>---usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
usb_register_driver

register a USB interface driver
向USB核心注册USB接口驱动程序。每当添加新驱动程序时,将重新扫描未连接接口的列表,允许新驱动程序连接到任何已识别的接口.
返回:失败时为负错误代码,成功时为0。
注意:如果您希望驱动程序使用USB主号码,则必须调用USB_register_dev()以启用该功能。这个函数不再处理这个问题。

int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
			const char *mod_name)
{
	int retval = 0;

	if (usb_disabled())
		return -ENODEV;

	new_driver->drvwrap.for_devices = 0;
	new_driver->drvwrap.driver.name = new_driver->name;
	new_driver->drvwrap.driver.bus = &usb_bus_type;
	new_driver->drvwrap.driver.probe = usb_probe_interface;
	new_driver->drvwrap.driver.remove = usb_unbind_interface;
	new_driver->drvwrap.driver.owner = owner;
	new_driver->drvwrap.driver.mod_name = mod_name;
	new_driver->drvwrap.driver.dev_groups = new_driver->dev_groups;
	spin_lock_init(&new_driver->dynids.lock);
	INIT_LIST_HEAD(&new_driver->dynids.list);

	retval = driver_register(&new_driver->drvwrap.driver);
	if (retval)
		goto out;

	retval = usb_create_newid_files(new_driver);
	if (retval)
		goto out_newid;

	pr_info("%s: registered new interface driver %s\n",
			usbcore_name, new_driver->name);

out:
	return retval;

out_newid:
	driver_unregister(&new_driver->drvwrap.driver);

	pr_err("%s: error %d registering interface driver %s\n",
		usbcore_name, retval, new_driver->name);
	goto out;
}
EXPORT_SYMBOL_GPL(usb_register_driver);

在probe()函数中,进行video的相关设置,其实就是给video外面套上一个usb的壳子,具体的核心还是fops以及ioctl_ops。

static const struct v4l2_file_operations myuvc_fops = {
	.owner		= THIS_MODULE,
    .open       = myuvc_open,
    .release    = myuvc_close,
    .mmap       = myuvc_mmap,
    .unlocked_ioctl      = video_ioctl2, /* V4L2 ioctl handler */
    .poll       = myuvc_poll,
};

		myuvc_vdev->fops    = &myuvc_fops;/*文件操作结构体,*/
  
        myuvc_vdev->ioctl_ops = &myuvc_ioctl_ops;/*ioctl函数集*/
        
static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
        // 表示它是一个摄像头设备

		/*在内核中函数名字可能改变,但是成员名字不会变*/
        .vidioc_querycap      = myuvc_vidioc_querycap,

        /* 用于列举、获得、测试、设置摄像头的数据的格式 */
        .vidioc_enum_fmt_vid_cap  = myuvc_vidioc_enum_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap     = myuvc_vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap   = myuvc_vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap     = myuvc_vidioc_s_fmt_vid_cap,
        
        /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
        .vidioc_reqbufs       = myuvc_vidioc_reqbufs,
        .vidioc_querybuf      = myuvc_vidioc_querybuf,
        .vidioc_qbuf          = myuvc_vidioc_qbuf,
        .vidioc_dqbuf         = myuvc_vidioc_dqbuf,
        

		/*查询/获得/设置属性*/
		.vidioc_queryctrl = myuvc_ioctl_queryctrl,
		.vidioc_g_ctrl = myuvc_ioctl_g_ctrl,
		.vidioc_s_ctrl = myuvc_ioctl_s_ctrl,
        // 启动/停止
        .vidioc_streamon      = myuvc_vidioc_streamon,
        .vidioc_streamoff     = myuvc_vidioc_streamoff,   
};

最重要的就是ioctl2,它使用video_usercopy()获得用户空间传进来的参数,调用__video_do_ioctl()在v4l2_ioctls[]数组里找到对应的uvc_ioctl_ops。
v4l2_ioctls[]数组定义是在内核中v4l2_ioctl.c中定义:
static struct v4l2_ioctl_info v4l2_ioctls[]

结构体:

struct usb_device

/**
 * struct usb_device - kernel's representation of a USB device
 * @devnum: device number; address on a USB bus
 * @devpath: device ID string for use in messages (e.g., /port/...)
 * @route: tree topology hex string for use with xHCI
 * @state: device state: configured, not attached, etc.
 * @speed: device speed: high/full/low (or error)
 * @rx_lanes: number of rx lanes in use, USB 3.2 adds dual-lane support
 * @tx_lanes: number of tx lanes in use, USB 3.2 adds dual-lane support
 * @ssp_rate: SuperSpeed Plus phy signaling rate and lane count
 * @tt: Transaction Translator info; used with low/full speed dev, highspeed hub
 * @ttport: device port on that tt hub
 * @toggle: one bit for each endpoint, with ([0] = IN, [1] = OUT) endpoints
 * @parent: our hub, unless we're the root
 * @bus: bus we're part of
 * @ep0: endpoint 0 data (default control pipe)
 * @dev: generic device interface
 * @descriptor: USB device descriptor
 * @bos: USB device BOS descriptor set
 * @config: all of the device's configs
 * @actconfig: the active configuration
 * @ep_in: array of IN endpoints
 * @ep_out: array of OUT endpoints
 * @rawdescriptors: raw descriptors for each config
 * @bus_mA: Current available from the bus
 * @portnum: parent port number (origin 1)
 * @level: number of USB hub ancestors
 * @devaddr: device address, XHCI: assigned by HW, others: same as devnum
 * @can_submit: URBs may be submitted
 * @persist_enabled:  USB_PERSIST enabled for this device
 * @have_langid: whether string_langid is valid
 * @authorized: policy has said we can use it;
 *	(user space) policy determines if we authorize this device to be
 *	used or not. By default, wired USB devices are authorized.
 *	WUSB devices are not, until we authorize them from user space.
 *	FIXME -- complete doc
 * @authenticated: Crypto authentication passed
 * @wusb: device is Wireless USB
 * @lpm_capable: device supports LPM
 * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
 * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
 * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
 * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
 * @usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled
 * @usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled
 * @string_langid: language ID for strings
 * @product: iProduct string, if present (static)
 * @manufacturer: iManufacturer string, if present (static)
 * @serial: iSerialNumber string, if present (static)
 * @filelist: usbfs files that are open to this device
 * @maxchild: number of ports if hub
 * @quirks: quirks of the whole device
 * @urbnum: number of URBs submitted for the whole device
 * @active_duration: total time device is not suspended
 * @connect_time: time device was first connected
 * @do_remote_wakeup:  remote wakeup should be enabled
 * @reset_resume: needs reset instead of resume
 * @port_is_suspended: the upstream port is suspended (L2 or U3)
 * @wusb_dev: if this is a Wireless USB device, link to the WUSB
 *	specific data for the device.
 * @slot_id: Slot ID assigned by xHCI
 * @removable: Device can be physically removed from this port
 * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
 * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
 * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout.
 * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm()
 *	to keep track of the number of functions that require USB 3.0 Link Power
 *	Management to be disabled for this usb_device.  This count should only
 *	be manipulated by those functions, with the bandwidth_mutex is held.
 * @hub_delay: cached value consisting of:
 *	parent->hub_delay + wHubDelay + tTPTransmissionDelay (40ns)
 *	Will be used as wValue for SetIsochDelay requests.
 * @use_generic_driver: ask driver core to reprobe using the generic driver.
 *
 * Notes:
 * Usbcore drivers should not set usbdev->state directly.  Instead use
 * usb_set_device_state().
 */
struct usb_device {
	int		devnum;
	char		devpath[16];
	u32		route;
	enum usb_device_state	state;
	enum usb_device_speed	speed;
	unsigned int		rx_lanes;
	unsigned int		tx_lanes;
	enum usb_ssp_rate	ssp_rate;

	struct usb_tt	*tt;
	int		ttport;

	unsigned int toggle[2];

	struct usb_device *parent;
	struct usb_bus *bus;
	struct usb_host_endpoint ep0;

	struct device dev;

	struct usb_device_descriptor descriptor;
	struct usb_host_bos *bos;
	struct usb_host_config *config;

	struct usb_host_config *actconfig;
	struct usb_host_endpoint *ep_in[16];
	struct usb_host_endpoint *ep_out[16];

	char **rawdescriptors;

	unsigned short bus_mA;
	u8 portnum;
	u8 level;
	u8 devaddr;

	unsigned can_submit:1;
	unsigned persist_enabled:1;
	unsigned have_langid:1;
	unsigned authorized:1;
	unsigned authenticated:1;
	unsigned wusb:1;
	unsigned lpm_capable:1;
	unsigned usb2_hw_lpm_capable:1;
	unsigned usb2_hw_lpm_besl_capable:1;
	unsigned usb2_hw_lpm_enabled:1;
	unsigned usb2_hw_lpm_allowed:1;
	unsigned usb3_lpm_u1_enabled:1;
	unsigned usb3_lpm_u2_enabled:1;
	int string_langid;

	/* static strings from the device */
	char *product;
	char *manufacturer;
	char *serial;

	struct list_head filelist;

	int maxchild;

	u32 quirks;
	atomic_t urbnum;

	unsigned long active_duration;

#ifdef CONFIG_PM
	unsigned long connect_time;

	unsigned do_remote_wakeup:1;
	unsigned reset_resume:1;
	unsigned port_is_suspended:1;
#endif
	struct wusb_dev *wusb_dev;
	int slot_id;
	struct usb2_lpm_parameters l1_params;
	struct usb3_lpm_parameters u1_params;
	struct usb3_lpm_parameters u2_params;
	unsigned lpm_disable_count;

	u16 hub_delay;
	unsigned use_generic_driver:1;
};

struct uvc_device

 struct uvc_device {  
    struct usb_device *udev;    //usb设备指针  
    struct usb_interface *intf; //usb接口指针  
    unsigned long warnings;  
    __u32 quirks;  
    int intfnum;    //接口数  
    char name[32];  //设备名  
    enum uvc_device_state state;    //uvc设备状态  
    atomic_t users;  
    atomic_t nmappings;  
    /* Video control interface */  
    __u16 uvc_version;  //UVC协议版本  
    __u32 clock_frequency;  //时钟频率  
    struct list_head entities;  //uvc实体链表头(挂着uvc设备的Terminal和Unit)  
    struct list_head chains;    //uvc视频链链表头  
    /* Video Streaming interfaces */  
    struct list_head streams;   //uvc视频流链表头  
    atomic_t nstreams;//uvc视频流个数  
    /* Status Interrupt Endpoint */  
    struct usb_host_endpoint *int_ep;   //usb_host_endpoint对象  
    struct urb *int_urb;    //中断urb  
    __u8 *status;   //uvc设备状态标志  
    struct input_dev *input;    //输入设备  
    char input_phys[64];    //输入设备设备节点路径  
};

struct uvc_streaming

 struct uvc_streaming {  
    struct list_head list;  //uvc视频流链表头  
    struct uvc_device *dev; //uvc设备  
    struct video_device *vdev;  //V4L2视频设备  
    struct uvc_video_chain *chain;  //uvc视频链  
    atomic_t active;  
    struct usb_interface *intf; //usb接口设备  
    int intfnum;    //usb接口号  
    __u16 maxpsize; //最大包尺寸  
    struct uvc_streaming_header header; //uvc视频流头部  
    enum v4l2_buf_type type;    //V4L2缓冲区类型 输入/输出  
    unsigned int nformats;  //uvc格式个数  
    struct uvc_format *format;  //uvc格式指针  
    struct uvc_streaming_control ctrl;  //uvc数据流控制  
    struct uvc_format *cur_format;  //当前uvc格式指针  
    struct uvc_frame *cur_frame;    //当前uvc帧指针  
    struct mutex mutex;  
    unsigned int frozen : 1;  
    struct uvc_video_queue queue;   //uvc视频队列  
    void (*decode) (struct urb *urb, struct uvc_streaming *video,struct uvc_buffer *buf);//解码函数  
    struct {  
        __u8 header[256];  
        unsigned int header_size;  
        int skip_payload;  
        __u32 payload_size;  
        __u32 max_payload_size;  
    } bulk;  
    struct urb *urb[UVC_URBS];//urb数组  
    char *urb_buffer[UVC_URBS]; //urb缓冲区  
    dma_addr_t urb_dma[UVC_URBS];//urb DMA缓冲区  
    unsigned int urb_size;    
    __u32 sequence;  
    __u8 last_fid;  
};   

struct uvc_format

 struct uvc_format { //uvc格式  
    __u8 type;  //类型  
    __u8 index; //索引  
    __u8 bpp;   //bits per pixel 每像素位数  
    __u8 colorspace;    //颜色空间  
    __u32 fcc;  //压缩格式  
    __u32 flags;    //标记  
    char name[32];  //名字  
    unsigned int nframes;   //所含uvc帧个数  
    struct uvc_frame *frame;    //uvc帧指针  
};   

struct uvc_frame

 struct uvc_frame {  //uvc帧  
    __u8  bFrameIndex;  //帧索引号  
    __u8  bmCapabilities;   //uvc帧兼容性  
    __u16 wWidth;   //宽度  
    __u16 wHeight;  //高度  
    __u32 dwMinBitRate; //最新位流  
    __u32 dwMaxBitRate; //最大位流  
    __u32 dwMaxVideoFrameBufferSize;    //最大视频帧缓冲区  
    __u8  bFrameIntervalType;   //间隙类型  
    __u32 dwDefaultFrameInterval;   //默认帧间隙  
    __u32 *dwFrameInterval; //帧间隙指针  
};   
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值