• UVC 设备框架在 Linux 4.15 内核的演变


    1. 概述

    发现之前的uvc框架和现在的还是有一些差别的(比如从videobuf 过渡到videobuf2),写个blog记录一下,方便以后查询,我的内核版本:Linux 4.15

    • UVC(USB Video Class)设备框架是建立在V4L2(Video4Linux version 2)子系统之上的。UVC框架主要负责管理通过USB接口连接的视频捕获设备,如网络摄像头。

    2. 流程分析

    • 打开设备文件 : 应用程序通过文件I/O打开设备时,内核创建一个uvc_fh 文件句柄实例,将其关联到特定的视频流
    • 缓冲区队列操作:uvc_fh通过它的视频流uvc_streaming 结构体间接操作vb2_queue ,以执行如缓冲区排队(qbuf),和缓冲区准备(reqbufs)等操作
    • 数据流传输: 视频数据通过 vb2_buffer 结构体在用户空间和UVC硬件之间传输

    3. 主要的内核结构体

    图1
    可以看出当前版本下的UVC驱动和V4L2之间的交互非常密切。V4L2是Linux内核中用于处理视频捕获和输出设备的一个标准API框架,而UVC驱动则是V4L2框架下用于支持符合USB视频类规范的摄像头和其他视频设备的一个具体实现

    3.1 UVC驱动和V4L2之间主要的交互方式:

    1. 设备注册和初始化
      UVC驱动在系统启动或USB视频设备插入时,会被初始化并注册为V4L2设备。这一过程包括设置设备的V4L2能力、支持的格式、控制操作等,确保UVC设备能够通过V4L2接口与用户空间应用程序交互。
    2. 控制查询和设置
      UVC驱动实现了一系列V4L2控制类(control class)接口,允许用户空间应用程序查询和设置视频设备的参数,如亮度、对比度、饱和度等。这些控制操作通过UVC驱动转换为USB传输,与硬件设备交互。
    3. 缓冲区管理
      UVC驱动使用V4L2提供的videobuf2 API来管理视频帧的缓冲区。这包括缓冲区的分配、队列管理、数据传输等。Videobuf2作为V4L2的一部分,提供了一个高效的机制来处理视频数据的缓冲和流转。
    4. 数据流控制
      用户空间应用程序可以通过V4L2接口来启动和停止视频流。UVC驱动响应这些请求,通过USB接口与硬件设备进行交互,控制视频数据的捕获和传输。
    5. 事件处理
      UVC驱动能够处理来自硬件的事件,比如状态变化、错误报告等,并通过V4L2框架将这些事件上报给用户空间应用程序,使得应用程序能够对特定的硬件事件做出响应。
    6. 格式协商
      在视频捕获或输出过程中,UVC驱动和用户空间应用程序会通过V4L2接口进行格式协商,确定视频数据的格式、分辨率、帧率等参数。UVC驱动根据这些协商结果配置USB视频设备,以满足应用程序的需求。

    4. UVC_Driver 驱动入口出口函数

    static int __init uvc_init(void)
    {
    ...
    	ret = usb_register(&uvc_driver.driver);
    	return 0;
    ...
    }
    
    static void __exit uvc_cleanup(void)
    {
    	usb_deregister(&uvc_driver.driver);
    }
    module_init(uvc_init);
    module_exit(uvc_cleanup);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接下里就是关于uvc_driver 结构体

    struct uvc_driver uvc_driver = {
    	.driver = {
    		.name		= "uvcvideo",
    		.probe		= uvc_probe,
    		.disconnect	= uvc_disconnect,
    		.suspend	= uvc_suspend,
    		.resume		= uvc_resume,
    		.reset_resume	= uvc_reset_resume,
    		.id_table	= uvc_ids,
    		.supports_autosuspend = 1,
    	},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.1 uvc_probe

    这里主要是分配 设置 设置结构体,以及一些其他的操作

    4.1.1 分配 uvc_device

    if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
    
    	INIT_LIST_HEAD(&dev->entities); 
    	INIT_LIST_HEAD(&dev->chains);  
    	INIT_LIST_HEAD(&dev->streams); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • entities: 指的也就是摄像头、输入终端(ITT)、输出终端(OTT)、处理单元(PU)和扩展单元(XU)等。每个实体代表设备的一个功能部分,可能是视频捕获的源头(例如摄像头),或是对视频数据进行处理的模块(例如编码器)。
    • chains: 视频链是由一系列实体(entities)连接而成的路径,从视频捕获的源头开始,经过一系列处理,最终到达输出。每个视频链代表了一种特定的视频流处理流程
    • streams :视频流可以理解为通过一个特定视频链(chains)实现的数据流,包含实际的视频帧数据
      在这里插入图片描述

    解析设备接口描述符 并将信息储存到 uvc_device *dev; 以便后面使用

    uvc_parse_control(dev)
    
    • 1

    4.1.2 分配 uvc_device注册 video 设备节点

    1. 注册uvc设备到V4L2框架下, 将V4L2设备结构体 v4l2_device 与内核结构体device 关联起来使其成为一个可以由用户空间访问和控制的设备
    if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
    		goto error;
    
    • 1
    • 2
    1. 初始化uvc设备的控制项
    if (uvc_ctrl_init_device(dev) < 0)
    		goto error;
    /* 
    uvc_ctrl_init_device 主要就是
    1.. 遍历所有与设备管理的实体(比如处理单元PU,控制单元CU等),并且为每
    个实体分配了一个 struct uvc_control *ctrl 的结构体
    3. 对于每个支持的控制项,函数设置控制的索引,并调用
     uvc_ctrl_init_ctrl 来进一步初始化控制项。
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 扫描设备实体(entities) 并注册成 视频链 (chains)
    if (uvc_scan_device(dev) < 0)
    		goto error;
    /* 1.遍历所有实体 从输出端OT 反向扫描
       2.为没用分配到的实体分配视频链结构体
       3.初始化视频链:
    	4.设置默认标志:
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 为识别到的视频链注册到video_device 设备节点
    uvc_register_chains(dev)	
    	uvc_register_terms(dev, chain);
    		uvc_register_video(dev, stream);//根据传输终端的 ID 查找视频流并将其注册为设备节点 dev/video x 
    
    • 1
    • 2
    • 3

    接下来分析一下uvc_register_video 这里面的函数

    a. 首先对uvc_queue 进行初始化, 设置它的队列的类型,以及是否丢帧行为 (uvc_no_drop_param),根据内核结构体那张图,我们还需要将其初始化到vb2_queue 里面去

    	ret = uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
    	if (ret)
    		return ret;
    /*
    ret = vb2_queue_init(&queue->queue);
    	if (ret)
    		return ret;
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看出在UVC驱动中,主要是使用videobuf2来管理视频流的数据传输,主要包括以下几个步骤:

    • 初始化队列:UVC驱动在初始化视频流时,会创建并初始化一个vb2_queue结构体实例。这个过程包括指定队列操作的回调函数和设置缓冲区的类型(捕获或输出)。

    • 缓冲区操作:UVC驱动会使用videobuf2提供的API来执行缓冲区的排队(queue)和出队(dequeue)操作

    • 数据处理:当缓冲区准备好数据后(例如,捕获了一帧视频数据),videobuf2框架会通知UVC驱动,驱动随后可以处理这些数据,比如将其传输给用户空间。

    • 与用户空间的交互:videobuf2还处理与用户空间应用程序的交互,包括应用程序对缓冲区的请求、查询和映射操作。

    b .初始化视频流 :确保视频流在开始传输数据之前已经正确配置,包括分辨率、帧率等参数

    ret = uvc_video_init(stream);
    	if (ret < 0) {
    		uvc_printk(KERN_ERR, "Failed to initialize the device "
    			"(%d).\n", ret);
    		return ret;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    c. 注册视频设备

    	/*设置相关的参数*/
    	vdev->v4l2_dev = &dev->vdev;
    	vdev->fops = &uvc_fops;
    	vdev->ioctl_ops = &uvc_ioctl_ops;
    	vdev->release = uvc_release;
    	vdev->prio = &stream->chain->prio;
    	if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
    		vdev->vfl_dir = VFL_DIR_TX;
    	strlcpy(vdev->name, dev->name, sizeof vdev->name);
    
    //设置视频设备的私有数据,确保在设备操作中可以访问到关联的 uvc_streaming 结构
    	video_set_drvdata(vdev, stream);
    	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
    	if (ret < 0) {
    		return ret;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    c. 更新视频流和链的能力

    	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
    		stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
    	else
    		stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
    
    	atomic_inc(&dev->nstreams); /*更新活跃视频流的计数*/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 将数据指针保存在数据接口中
    usb_set_intfdata(intf, dev);
    
    • 1
    1. 初始化中断 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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    5. UVC驱动的调用过程详解

    UVC驱动通过一系列文件操作和IO控制操作(ioctl)来管理视频捕获和控制流程

    5.1 核心操作集合

    uvc驱动的调用过程主要是涉及之前注册时候设置的

        vdev->fops = &uvc_fops; //文件操作
    	vdev->ioctl_ops = &uvc_ioctl_ops; //ioctl操作
    
    • 1
    • 2

    文件操作(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
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    IOCTL操作(uvc_ioctl_ops)

    const struct v4l2_ioctl_ops uvc_ioctl_ops = {
    	/*最基本的几个参数*/
    	.vidioc_querycap = uvc_ioctl_querycap,
    	.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
    	.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
    	.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
    	.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
    
    	.vidioc_reqbufs = uvc_ioctl_reqbufs,
    	.vidioc_querybuf = uvc_ioctl_querybuf,
    	.vidioc_qbuf = uvc_ioctl_qbuf,
    	.vidioc_dqbuf = uvc_ioctl_dqbuf,
    
    	
    	
    
    	.vidioc_streamon = uvc_ioctl_streamon,
    	/* uvc_ioctl_streamon
    	 *1.向USB摄像头设置参数 比如使用那个format 使用这个format下的那个frame(分辨率)
    	 *1.1 根据一个uvc_streaming_control 设置数据包,可以手工设置也可以读出
    	 *1.2 调用usb_control_msg 发出数据包
    	 
    	 *2.分配设置URB
    	 *3.提交URB以接受数据
    	*/
    	.vidioc_streamoff = uvc_ioctl_streamoff,
    
    	.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
    	.vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
    	.vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
    	.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
    	.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
    	.vidioc_create_bufs = uvc_ioctl_create_bufs,
    
    	.vidioc_enum_input = uvc_ioctl_enum_input,
    	.vidioc_g_input = uvc_ioctl_g_input,
    	.vidioc_s_input = uvc_ioctl_s_input,
    
    	/*查询 查询额外属性 获得 设置属性*/
    	.vidioc_queryctrl = uvc_ioctl_queryctrl,
    	.vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl,
    	.vidioc_g_ctrl = uvc_ioctl_g_ctrl,
    	.vidioc_s_ctrl = uvc_ioctl_s_ctrl,
    	
    	.vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
    	.vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
    	.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
    	.vidioc_querymenu = uvc_ioctl_querymenu,
    	.vidioc_g_selection = uvc_ioctl_g_selection,
    
    	/*设置帧间隔*/
    	.vidioc_g_parm = uvc_ioctl _g_parm,
    	.vidioc_s_parm = uvc_ioctl_s_parm,
    	/*枚举支持的分辨率 帧间隔参数*/
    	.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
    	.vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
    
    	.vidioc_subscribe_event = uvc_ioctl_subscribe_event,
    	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
    	.vidioc_default = uvc_ioctl_default,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    5.2 主要的调用过程

    5.2.1 open

    内核创建一个uvc_fh 文件句柄实例,将其关联到特定的视频流,以及初始化V4L2文件句柄

    static int uvc_v4l2_open(struct file *file)
    
    • 1

    5.2.2 VIDIOC_QUERYCAP 查询设备的功能

    static int uvc_ioctl_querycap(struct file *file, void *fh,
    			      struct v4l2_capability *cap)
    {
    ...
    	usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
    	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
    			  | chain->caps;
    	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
    		cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
    	else
    		cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5.2.3 VIDIOC_ENUM_FMT 枚举设备支持的格式

    枚举设备支持的格式 ,比如MJPEG或H264等 填充到v4l2_fmtdesc *fmt

    static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
    			      struct v4l2_fmtdesc *fmt)
    {
    	struct uvc_format *format;
    	enum v4l2_buf_type type = fmt->type;
    	__u32 index = fmt->index;
    
    	if (fmt->type != stream->type || fmt->index >= stream->nformats)
    		return -EINVAL;
    
    	memset(fmt, 0, sizeof(*fmt)); //将fmt结构内存清0 
    	fmt->index = index;
    	fmt->type = type;
    
    	format = &stream->format[fmt->index];
    	fmt->flags = 0;
    	if (format->flags & UVC_FMT_FLAG_COMPRESSED)
    		fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
    	strlcpy(fmt->description, format->name, sizeof(fmt->description));
    	fmt->description[sizeof(fmt->description) - 1] = 0;
    	fmt->pixelformat = format->fcc;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    5.2.4 VIDIOC_G_FMT 得到当前的支持的格式 比如分辨率等

    int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
    --------->	uvc_v4l2_get_format(stream, fmt); /*跳转到v4l2框架下的函数来进行*/
    
    • 1
    • 2
    static int uvc_v4l2_get_format(struct uvc_streaming *stream,
    	struct v4l2_format *fmt)
    {
    ...
    	fmt->fmt.pix.pixelformat = format->fcc;
    	fmt->fmt.pix.width = frame->wWidth;
    	fmt->fmt.pix.height = frame->wHeight;
    	fmt->fmt.pix.field = V4L2_FIELD_NONE; //逐行扫描
    	fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
    	fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
    	fmt->fmt.pix.colorspace = format->colorspace;//色彩空间
    	fmt->fmt.pix.priv = 0;
    	return ret;
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5.2.5 VIDIOC_S_FMT 更新UVC视频流的当前格式和帧大小 以匹配应用程序的请求配置

    static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
    -------> uvc_v4l2_set_format(stream, fmt);
    
    • 1
    • 2
    /*
    struct uvc_streaming *stream: 视频流对象的指针。
    struct v4l2_format *fmt: 应用程序请求的视频格式信息。
    */
    static int uvc_v4l2_set_format(struct uvc_streaming *stream,struct v4l2_format *fmt)
    {
    	/*尝试匹配和调整应用请求的格式*/
    	ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
    
    	
    	mutex_lock(&stream->mutex);
    	
    	if (uvc_queue_allocated(&stream->queue)) {
    		/*如果视频流帧缓冲区已经分配,此时不能修改格式*/
    		ret = -EBUSY;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    5.2.6 VIDIOC_TRY_FMT 检查格式是否匹配

    static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
    -------> uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
    
    • 1
    • 2

    5.2.3 VIDIOC_REQBUF

    static int uvc_ioctl_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
    -------> uvc_request_buffers(&stream->queue, rb);
    --------->vb2_reqbufs(&queue->queue, rb); //跳转到video_buf2
    -----------> __reqbufs(q, req)
    --------------> __vb2_queue_alloc(q, req->memory, num_buffers, num_planes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    /*
    struct vb2_queue *q: 指向视频缓冲区vb2队列对象的指针。
    struct v4l2_requestbuffers *req: 包含了请求分配缓冲区的信息。
    */
    
    int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
    {
    	/*验证请求的内存类型和视频流的类型是否匹配*/
    	int ret = __verify_memory_type(q, req->memory, req->type);
    	return ret ? ret : __reqbufs(q, req);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    __reqbufs() 启动流式传输

    应从驱动程序的 vidioc_reqbufs ioctl 处理程序中调用。
    该函数

    1. 验证从用户空间传递的流参数、
    2. 设置队列、
    3. 与驱动程序协商缓冲区数量和每个缓冲区的平面数
    4. 根据商定的参数分配内部缓冲区结构(struct vb2_buffer)。
    5. 对于 MMAP 内存类型,使用队列初始化过程中提供的内存处理/分配例程分配实际视频内存

    如果 req->count 为 0,则释放所有内存。
    如果队列之前已被分配(通过之前的 vb2_reqbufs)调用
    且队列不忙,内存将被重新分配。

    该函数的返回值可直接从 从驱动程序中的 vidioc_reqbufs 处理程序直接返回。

    static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
    {
    	unsigned int num_buffers, allocated_buffers, num_planes = 0;
    	int ret;
    
    	if (q->streaming) {
    		dprintk(1, "streaming active\n");
    		return -EBUSY;
    	}
    
    	if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
    		/*
    		 * We already have buffers allocated, so first check if they
    		 * are not in use and can be freed.
    		 */
    		mutex_lock(&q->mmap_lock);
    		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
    			mutex_unlock(&q->mmap_lock);
    			dprintk(1, "memory in use, cannot free\n");
    			return -EBUSY;
    		}
    
    		/*
    		 * Call queue_cancel to clean up any buffers in the PREPARED or
    		 * QUEUED state which is possible if buffers were prepared or
    		 * queued without ever calling STREAMON.
    		 */
    		__vb2_queue_cancel(q);
    		ret = __vb2_queue_free(q, q->num_buffers);
    		mutex_unlock(&q->mmap_lock);
    		if (ret)
    			return ret;
    
    		/*
    		 * In case of REQBUFS(0) return immediately without calling
    		 * driver's queue_setup() callback and allocating resources.
    		 */
    		if (req->count == 0)
    			return 0;
    	}
    
    	/*
    	 * Make sure the requested values and current defaults are sane.
    	 */
    	num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
    	num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
    	memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
    	memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
    	q->memory = req->memory;
    
    	/*
    	 * Ask the driver how many buffers and planes per buffer it requires.
    	 * Driver also sets the size and allocator context for each plane.
    	 */
    	ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes,
    		       q->plane_sizes, q->alloc_ctx);
    	if (ret)
    		return ret;
    
    	/* Finally, allocate buffers and video memory */
    	allocated_buffers = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes);
    	if (allocated_buffers == 0) {
    		dprintk(1, "memory allocation failed\n");
    		return -ENOMEM;
    	}
    
    	/*
    	 * There is no point in continuing if we can't allocate the minimum
    	 * number of buffers needed by this vb2_queue.
    	 */
    	if (allocated_buffers < q->min_buffers_needed)
    		ret = -ENOMEM;
    
    	/*
    	 * Check if driver can handle the allocated number of buffers.
    	 */
    	if (!ret && allocated_buffers < num_buffers) {
    		num_buffers = allocated_buffers;
    
    		ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
    			       &num_planes, q->plane_sizes, q->alloc_ctx);
    
    		if (!ret && allocated_buffers < num_buffers)
    			ret = -ENOMEM;
    
    		/*
    		 * Either the driver has accepted a smaller number of buffers,
    		 * or .queue_setup() returned an error
    		 */
    	}
    
    	mutex_lock(&q->mmap_lock);
    	q->num_buffers = allocated_buffers;
    
    	if (ret < 0) {
    		/*
    		 * Note: __vb2_queue_free() will subtract 'allocated_buffers'
    		 * from q->num_buffers.
    		 */
    		__vb2_queue_free(q, allocated_buffers);
    		mutex_unlock(&q->mmap_lock);
    		return ret;
    	}
    	mutex_unlock(&q->mmap_lock);
    
    	/*
    	 * Return the number of successfully allocated buffers
    	 * to the userspace.
    	 */
    	req->count = allocated_buffers;
    	q->waiting_for_buffers = !V4L2_TYPE_IS_OUTPUT(q->type);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    __vb2_queue_alloc -----分配videobuf 结构体
  • 相关阅读:
    2023国赛 C题论文 蔬菜类商品自动定价与补货策略
    Redis 阻塞管理
    一、nginx安装第三方模块[apt安装、源码安装]
    Armadillo矩阵库在Visual Studio软件C++环境中的配置方法
    对话框如何屏蔽ok和cancel按键 2023/10/21 上午11:36:08
    Spring cache源码分析
    U-boot(二):主Makefile
    牛客-模拟、枚举、贪心 2022.11.15
    【C语言基础】:深入理解指针(终篇)
    解决XXLJOB重复执行问题--Redis加锁+注解+AOP
  • 原文地址:https://blog.csdn.net/chunchun2021/article/details/136634409