当前位置:首页 » 《随便一记》 » 正文

android的camera学习(4)——V4L2框架及源码整理

8 人参与  2022年07月21日 10:57  分类 : 《随便一记》  评论

点击全文阅读


文章目录

一、什么是V4L2二、V4l2的基本流程三、v4l2框架整理四、V4l2的buf管理五、V4L2的整体流程
参考笔记:

https://blog.csdn.net/yhg20090519/article/details/78366179https://blog.csdn.net/yhg20090519/article/details/78496936https://www.cnblogs.com/vedic/p/10763838.htmlhttps://blog.csdn.net/kuangzuxiaoN/article/details/77048509

一、什么是V4L2

相机驱动层位于HAL Moudle与硬件层之间,借助linux内核驱动框架,以文件节点的方式暴露接口给用户空间,让HAL Module通过标准的文件访问接口,从而能够将请求顺利地下发到内核中,而在内核中,为了更好的支持视频流的操作,早先提出了v4l视频处理框架,但是由于操作复杂,并且代码无法进行较好的重构,难以维护等原因,之后便衍生出了v4l2框架。

按照v4l2标准,它将一个数据流设备抽象成一个videoX节点,从属的子设备都对应着各自的v4l2_subdev实现,并且通过media controller进行统一管理,整个流程复杂但高效,同时代码的扩展性也较高。

所以我们可以看到对应的相机节点再dev目录下

mvk_8qxp:/ # ls /dev/video*/dev/vidie0   /dev/video1  /dev/video3  /dev/video4

这也就是针对我们调试相机的时候可以看到的一些基础信息

二、V4l2的基本流程

在这里插入图片描述

1. 打开video设备
在需要进行视频数据流的操作之前,首先要通过标准的字符设备操作接口open方法来打开一个video设备,并且将返回的字符句柄存在本地,之后的一系列操作都是基于该句柄,而在打开的过程中,会去给每一个子设备的上电,并完成各自的一系列初始化操作。

2. 查看并设置设备
在打开设备获取其文件句柄之后,就需要查询设备的属性,该动作主要通过ioctl传入VIDIOC_QUERYCAP参数来完成,其中该系列属性通过v4l2_capability结构体来表达,除此之外,还可以通过传入VIDIOC_ENUM_FMT来枚举支持的数据格式,通过传入VIDIOC_G_FMT/VIDIOC_S_FMT来分别获取和获取当前的数据格式,通过传入VIDIOC_G_PARM/VIDIOC_S_PARM来分别获取和设置参数。

3. 申请帧缓冲区
完成设备的配置之后,便可以开始向设备申请多个用于盛装图像数据的帧缓冲区,该动作通过调用ioctl并且传入VIDIOC_REQBUFS命令来完成,最后将缓冲区通过mmap方式映射到用户空间

4. 将帧缓冲区入队
申请好帧缓冲区之后,通过调用ioctl方法传入VIDIOC_QBUF命令来将帧缓冲区加入到v4l2 框架中的缓冲区队列中,静等硬件模块将图像数据填充到缓冲区中

5. 开启数据流
将所有的缓冲区都加入队列中之后便可以调用ioctl并且传入VIDIOC_STREAMON命令,来通知整个框架开始进行数据传输,其中大致包括了通知各个子设备开始进行工作,最终将数据填充到V4L2框架中的缓冲区队列中。

6. 将帧缓冲区出队
一旦数据流开始进行流转了,我们就可以通过调用ioctl下发VIDIOC_DQBUF命令来获取帧缓冲区,并且将缓冲区的图像数据取出,进行预览、拍照或者录像的处理,处理完成之后,需要将此次缓冲区再次放入V4L2框架中的队列中等待下次的图像数据的填充。
整个采集图像数据的流程现在看来还是比较简单的,接口的控制逻辑很清晰,主要原因是为了提供给用户的接口简单而且抽象,这样方便用户进行集成开发,其中的大部分复杂的业务处理都被V4L2很好的封装了,接下来我们来详细了解下V4L2框架内部是如何表达以及如何运转的。

对应的函数流程可以用这个图来表示:
在这里插入图片描述
其实可以看出,V4L2的整个流程都是用ioctl完成的,关于ioctl,我们后面说。

三、v4l2框架整理

在这里插入图片描述
v4l2_device 充当父类,通过链表把所有注册到其下的子设备管理起来,这些设备可以是GRABBER也可以是RADIO或者VBI的。
这就是里面一些属性,有一些没有看明白名,所以这里就没有详解。

// kernel_imx/include/media/v4l2-device.hstruct v4l2_device {    struct device *dev;//成功创建设备的指针    struct media_device *mdev;//创建成功的媒体设备的指针    struct list_head subdevs;//头节点,用于遍历后面的跟踪子设备    spinlock_t lock;//自旋锁,可以使驱动程序已经这个结构嵌入到一个更大的结构(这个还没看懂怎么用的)    char name[V4L2_DEVICE_NAME_SIZE]; //设备名    void (*notify)(struct v4l2_subdev *sd,    unsigned int notification, void *arg);//函数指针,报告一些子设备调用的回调函数    struct v4l2_ctrl_handler *ctrl_handler;//控制处理数据结构    struct v4l2_prio_state prio;//优先级状态    struct kref ref;//计数    void (*release)(struct v4l2_device *v4l2_dev);//释放v4l2设备函数指针};

v4l2_subdev结构体包含了对设备操作的ops和ctrls,这部分代码和硬件相关,需要驱动工程师根据硬件实现控制上下电、读取ID、饱和度、对比度和视频数据流打开关闭等接口函数。
这个结构体代表每一个子设备在初始化的时候都要挂载在v4l2_device上,将其统一管理。

// kernel_imx/include/media/v4l2-subdev.hstruct v4l2_subdev {#if defined(CONFIG_MEDIA_CONTROLLER)    struct media_entity entity;#endif    struct list_head list;    struct module *owner;//模块拥有者    bool owner_v4l2_dev;    u32 flags;    struct v4l2_device *v4l2_dev;//指向父设备    const struct v4l2_subdev_ops *ops;//提供一些控制v4l2设备的接口    const struct v4l2_subdev_internal_ops *internal_ops;//向V4L2框架提供的接口函数    struct v4l2_ctrl_handler *ctrl_handler;//subdev控制接口    char name[V4L2_SUBDEV_NAME_SIZE];//子设备名    u32 grp_id;    void *dev_priv;    void *host_priv;    struct video_device *devnode;//video_device 设备节点    struct device *dev;    struct fwnode_handle *fwnode;    struct list_head async_list;    struct v4l2_async_subdev *asd;    struct v4l2_async_notifier *notifier;    struct v4l2_async_notifier *subdev_notifier;    struct v4l2_subdev_platform_data *pdata;};

下面两个结构体是

 const struct v4l2_subdev_ops *ops;//提供一些控制v4l2设备的接口 const struct v4l2_subdev_internal_ops *internal_ops;//向V4L2框架提供的接口函数

这两个属性的结构体:

struct v4l2_subdev_ops {    const struct v4l2_subdev_core_ops   *core;//视频设备通用的操作:初始化、加载FW、上电和RESET等    const struct v4l2_subdev_tuner_ops  *tuner;//tuner特有的操作    const struct v4l2_subdev_audio_ops  *audio;//audio特有的操作    const struct v4l2_subdev_video_ops  *video;//视频设备的特有操作:裁剪图像、开关视频流等    const struct v4l2_subdev_vbi_ops    *vbi;    const struct v4l2_subdev_ir_ops     *ir;    const struct v4l2_subdev_sensor_ops *sensor;    const struct v4l2_subdev_pad_ops    *pad;};/** * struct v4l2_subdev_internal_ops - V4L2 subdev internal ops * * @registered: called when this subdev is registered. When called the v4l2_dev *  field is set to the correct v4l2_device. * * @unregistered: called when this subdev is unregistered. When called the *  v4l2_dev field is still set to the correct v4l2_device. * * @open: called when the subdev device node is opened by an application. * * @close: called when the subdev device node is closed. Please note that *  it is possible for @close to be called after @unregistered! * * @release: called when the last user of the subdev device is gone. This *  happens after the @unregistered callback and when the last open *  filehandle to the v4l-subdevX device node was closed. If no device *  node was created for this sub-device, then the @release callback *  is called right after the @unregistered callback. *  The @release callback is typically used to free the memory containing *  the v4l2_subdev structure. It is almost certainly required for any *  sub-device that sets the V4L2_SUBDEV_FL_HAS_DEVNODE flag. * * .. note:: *  Never call this from drivers, only the v4l2 framework can call *  these ops. */struct v4l2_subdev_internal_ops { /* 当subdev注册时被调用,读取IC的ID来进行识别 */    int (*registered)(struct v4l2_subdev *sd);    void (*unregistered)(struct v4l2_subdev *sd);     /* 当设备节点被打开时调用,通常会给设备上电和设置视频捕捉FMT */    int (*open)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);    int (*close)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh);  /* 释放对应的subdev */    void (*release)(struct v4l2_subdev *sd);};

调用 video_device 结构体进行设备的注册

// kernel_imx/include/media/v4l2-dev.h struct video_device{#if defined(CONFIG_MEDIA_CONTROLLER)struct media_entity entity;struct media_intf_devnode *intf_devnode;struct media_pipeline pipe;#endifconst struct v4l2_file_operations *fops;// 设备描述符号u32 device_caps;/* sysfs */struct device dev;// v4l 设备struct cdev *cdev;// 字符设备struct v4l2_device *v4l2_dev;// v4l2_device 父设备 struct device *dev_parent;// 父设备struct v4l2_ctrl_handler *ctrl_handler;// 设备节点的控制处理结构,可能为NULLstruct vb2_queue *queue;struct v4l2_prio_state *prio;/* device info */char name[32];enum vfl_devnode_type vfl_type;enum vfl_devnode_direction vfl_dir;int minor;u16 num;unsigned long flags;int index;/* V4L2 file handles */spinlock_tfh_lock;struct list_headfh_list;int dev_debug;v4l2_std_id tvnorms;/* callbacks *//* ioctl回调函数集,提供file_operations中的ioctl调用 */void (*release)(struct video_device *vdev);// 释放函数const struct v4l2_ioctl_ops *ioctl_ops;DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);struct mutex *lock;ANDROID_KABI_RESERVE(1);ANDROID_KABI_RESERVE(2);};

v4l2_fh是用来保存子设备的特有操作方法,也就是下面要分析到的v4l2_ctrl_handler,内核提供一组v4l2_fh的操作方法,通常在打开设备节点时进行v4l2_fh注册。

// kernel_imx/include/media/v4l2-fh.h/** * struct v4l2_fh - Describes a V4L2 file handler * * @list: list of file handlers * @vdev: pointer to &struct video_device * @ctrl_handler: pointer to &struct v4l2_ctrl_handler * @prio: priority of the file handler, as defined by &enum v4l2_priority * * @wait: event' s wait queue * @subscribe_lock: serialise changes to the subscribed list; guarantee that *          the add and del event callbacks are orderly called * @subscribed: list of subscribed events * @available: list of events waiting to be dequeued * @navailable: number of available events at @available list * @sequence: event sequence number * * @m2m_ctx: pointer to &struct v4l2_m2m_ctx */struct v4l2_fh {    struct list_head    list;    struct video_device *vdev;    struct v4l2_ctrl_handler *ctrl_handler;    enum v4l2_priority  prio;    /* Events */    wait_queue_head_t   wait;    struct mutex        subscribe_lock;    struct list_head    subscribed;    struct list_head    available;    unsigned int        navailable;    u32         sequence;    struct v4l2_m2m_ctx *m2m_ctx;};/*这个头文件中还包含了如何去初始化子设备等、添加子设备、删除子设备*/void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev);/** * v4l2_fh_add - Add the fh to the list of file handles on a video_device. * * @fh: pointer to &struct v4l2_fh * * .. note:: *    The @fh file handle must be initialised first. */void v4l2_fh_add(struct v4l2_fh *fh);/** * v4l2_fh_open - Ancillary routine that can be used as the open\(\) op *  of v4l2_file_operations. * * @filp: pointer to struct file * * It allocates a v4l2_fh and inits and adds it to the &struct video_device * associated with the file pointer. */int v4l2_fh_open(struct file *filp);/** * v4l2_fh_del - Remove file handle from the list of file handles. * * @fh: pointer to &struct v4l2_fh * * On error filp->private_data will be %NULL, otherwise it will point to * the &struct v4l2_fh. * * .. note::

v4l2_ctrl_handler是用于保存子设备控制方法集的结构体,对于视频设备这些ctrls包括设置亮度、饱和度、对比度和清晰度等,用链表的方式来保存ctrls,可以通过v4l2_ctrl_new_std函数向链表添加ctrls。
其实下面的注释已经有对应的说明了

// kernel_imx/include/media/v4l2-ctrls.hstruct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,                    const struct v4l2_ctrl_ops *ops,                    u32 id, s64 min, s64 max, u64 step,                    s64 def);/** * v4l2_ctrl_new_std_menu() - Allocate and initialize a new standard V4L2 *  menu control. * * @hdl:    The control handler. 已经初始化的v4l2_ctrl_handler的结构体 * @ops:    The control ops.已经设置好的v4l2_ctrl_ops结构体 * @id:     The control ID.对应的ID通过;ioctl传递的参数,也就是序列号 * @max:    The control's maximum value.操作范围的最大值和最小值 * @mask:   The control's skip mask for menu controls. This makes it *      easy to skip menu items that are not valid. If bit X is set, *      then menu item X is skipped. Of course, this only works for *      menus with <= 64 menu items. There are no menus that come *      close to that number, so this is OK. Should we ever need more, *      then this will have to be extended to a bit array. * @def:    The control's default value. * * Same as v4l2_ctrl_new_std(), but @min is set to 0 and the @mask value * determines which menu items are to be skipped. * * If @id refers to a non-menu control, then this function will return NULL. */

上面基本就是V4L2的框架图的一些源码解释,至于在代码中如何实现,还是需要根据代码去查看逻辑。主要是从注册设备去跟踪代码吧,因为工作中暂时没这些方面的需求,所以我这里也知识做一个简单的介绍。

四、V4l2的buf管理

因为我们可以通过v4l2的相关命令进行摄像头的数据流的抓取,在这过程中就涉及到一些文件的读写。
v4l2有三种:使用read/write方式;内存映射方式(mmap)和用户指针模式(USERPTR)。

read和write 是基本帧IO访问方式,通过read读取每一帧数据,数据需要在内核和用户之间拷贝,这种方式访问速度可能会非常慢;

内存映射缓冲区 (V4L2_MEMORY_MMAP): 是在内核空间开辟缓冲区,应用通过mmap()系统调用映射到用户地址空间。这些缓冲区可以是大而连续DMA缓冲区、通过vmalloc()创建的虚拟缓冲区,或者直接在设备的IO内存中开辟的缓冲区(如果硬件支持);

用户空间缓冲区(V4L2_MEMORY_USERPTR) 是用户空间的应用中开辟缓冲区,用户与内核空间之间交换缓冲区指针。很明显,在这种情况下是不需要mmap()调用的,但驱动为有效的支持用户空间缓冲区,其工作将也会更困难。
在这里插入图片描述   Camera sensor捕捉到图像数据通过并口或MIPI传输到CAMIF(camera interface),CAMIF可以对图像数据进行调整(翻转、裁剪和格式转换等)。然后DMA控制器设置DMA通道请求AHB将图像数据传到分配好的DMA缓冲区。待图像数据传输到DMA缓冲区之后,mmap操作把缓冲区映射到用户空间,应用就可以直接访问缓冲区的数据。而为了使设备支持流IO这种方式,v4l2需要实现对video buffer的管理,即实现:

/* vb2_queue代表一个videobuffer队列,vb2_buffer是这个队列中的成员,vb2_mem_ops是缓冲内存的操作函数集,vb2_ops用来管理队列 */struct vb2_queue {    enum v4l2_buf_type type;  //buffer类型    unsigned int io_modes;  //访问IO的方式:mmap、userptr etc    const struct vb2_ops *ops;  //buffer队列操作函数集合    const struct vb2_mem_ops *mem_ops;  //buffer memory操作集合    struct vb2_buffer *bufs[VIDEO_MAX_FRAME];  //代表每个frame buffer    unsignedint num_buffers;  //分配的buffer个数    ..........};/* vb2_mem_ops包含了内存映射缓冲区、用户空间缓冲区的内存操作方法 */struct vb2_mem_ops {    void *(*alloc)(void *alloc_ctx, unsignedlong size);  //分配视频缓存    void (*put)(void *buf_priv);  //释放视频缓存    /* 获取用户空间视频缓冲区指针 */    void *(*get_userptr)(void *alloc_ctx, unsigned long vaddr, unsignedlong size, int write);    void (*put_userptr)(void *buf_priv);  //释放用户空间视频缓冲区指针    /* 用于缓存同步 */    void (*prepare)(void *buf_priv);    void (*finish)(void *buf_priv);    /* 缓存虚拟地址 & 物理地址 */    void *(*vaddr)(void *buf_priv);    void *(*cookie)(void *buf_priv);    unsignedint (*num_users)(void *buf_priv);  //返回当期在用户空间的buffer数    int (*mmap)(void *buf_priv, structvm_area_struct *vma);  //把缓冲区映射到用户空间    ..............};/* mem_ops由kernel自身实现并提供了三种类型的视频缓存区操作方法:连续的DMA缓冲区、集散的DMA缓冲区以及vmalloc创建的缓冲区,分别由videobuf2-dma-contig.c、videobuf2-dma-sg.c和videobuf-vmalloc.c文件实现,可以根据实际情况来使用。*//* vb2_ops是用来管理buffer队列的函数集合,包括队列和缓冲区初始化等 */struct vb2_ops {    //队列初始化    int(*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt,                       unsigned int *num_buffers, unsigned int*num_planes,                       unsigned int sizes[], void *alloc_ctxs[]);    //释放和获取设备操作锁    void(*wait_prepare)(struct vb2_queue *q);    void(*wait_finish)(struct vb2_queue *q);    //对buffer的操作    int(*buf_init)(struct vb2_buffer *vb);    int(*buf_prepare)(struct vb2_buffer *vb);    int(*buf_finish)(struct vb2_buffer *vb);    void(*buf_cleanup)(struct vb2_buffer *vb);    //开始/停止视频流    int(*start_streaming)(struct vb2_queue *q, unsigned int count);    int(*stop_streaming)(struct vb2_queue *q);    //把VB传递给驱动,以填充frame数据    void(*buf_queue)(struct vb2_buffer *vb);};
//kernel_imx/drivers/media/v4l2-core/v4l2-compat-ioctl32.c struct v4l2_buffer32 {    __u32           index;// buffer的序号    __u32           type;   /* enum v4l2_buf_type 类型*/    __u32           bytesused;// 已经使用的byte数    __u32           flags;    __u32           field;  /* enum v4l2_field */    struct {        compat_s64  tv_sec;        compat_s64  tv_usec;    }           timestamp;// 时间戳,代表帧捕获的时间    struct v4l2_timecode    timecode;    __u32           sequence;    /* memory location */    __u32           memory; /* enum v4l2_memory */    union {        __u32           offset;        compat_long_t   userptr;        compat_caddr_t  planes;        __s32       fd;    } m;    __u32           length;// 缓冲区大小,单位byte    __u32           reserved2;    __s32           request_fd;};

五、V4L2的整体流程

因为我这里自己跟踪流程的时候我已经跟蒙了,所以这里我就后续学会了再补充,这里我是根据设备注册开始跟踪的

// kernel_imx/drivers/media/v4l2-core/v4l2-dev.c int __video_register_device(struct video_device *vdev,    enum vfl_devnode_type type,    int nr, int warn_if_nr_in_use,    struct module *owner){int i = 0;int ret;int minor_offset = 0;int minor_cnt = VIDEO_NUM_DEVICES;const char *name_base;/* A minor value of -1 marks this video device as never   having been registered */vdev->minor = -1;/* the release callback MUST be present */if (WARN_ON(!vdev->release))return -EINVAL;/* the v4l2_dev pointer MUST be present */if (WARN_ON(!vdev->v4l2_dev))return -EINVAL;/* the device_caps field MUST be set for all but subdevs */if (WARN_ON(type != VFL_TYPE_SUBDEV && !vdev->device_caps))return -EINVAL;/* v4l2_fh support */spin_lock_init(&vdev->fh_lock);INIT_LIST_HEAD(&vdev->fh_list);/* Part 1: check device type */switch (type) {case VFL_TYPE_VIDEO: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:/* Use device name 'swradio' because 'sdr' was already taken. */name_base = "swradio";break;case VFL_TYPE_TOUCH:name_base = "v4l-touch";break;default:pr_err("%s called with unknown type: %d\n",       __func__, type);return -EINVAL;}vdev->vfl_type = type;vdev->cdev = NULL;if (vdev->dev_parent == NULL)vdev->dev_parent = vdev->v4l2_dev->dev;if (vdev->ctrl_handler == NULL)vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;/* If the prio state pointer is NULL, then use the v4l2_device   prio state. */if (vdev->prio == NULL)vdev->prio = &vdev->v4l2_dev->prio;/* Part 2: find a free minor, device node number and device index. */#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES/* Keep the ranges for the first four types for historical * reasons. * Newer devices (not yet in place) should use the range * of 128-191 and just pick the first free minor there * (new style). */switch (type) {case VFL_TYPE_VIDEO:minor_offset = 0;minor_cnt = 64;break;case VFL_TYPE_RADIO:minor_offset = 64;minor_cnt = 64;break;case VFL_TYPE_VBI:minor_offset = 224;minor_cnt = 32;break;default:minor_offset = 128;minor_cnt = 64;break;}#endif/* Pick a device node number */mutex_lock(&videodev_lock);nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);if (nr == minor_cnt)nr = devnode_find(vdev, 0, minor_cnt);if (nr == minor_cnt) {pr_err("could not get a free device node number\n");mutex_unlock(&videodev_lock);return -ENFILE;}#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES/* 1-on-1 mapping of device node number to minor number */i = nr;#else/* The device node number and minor numbers are independent, so   we just find the first free minor number. */for (i = 0; i < VIDEO_NUM_DEVICES; i++)if (video_devices[i] == NULL)break;if (i == VIDEO_NUM_DEVICES) {mutex_unlock(&videodev_lock);pr_err("could not get a free minor\n");return -ENFILE;}#endifvdev->minor = i + minor_offset;vdev->num = nr;/* Should not happen since we thought this minor was free */if (WARN_ON(video_devices[vdev->minor])) {mutex_unlock(&videodev_lock);pr_err("video_device not empty!\n");return -ENFILE;}devnode_set(vdev);vdev->index = get_index(vdev);video_devices[vdev->minor] = vdev;mutex_unlock(&videodev_lock);if (vdev->ioctl_ops)determine_valid_ioctls(vdev);/* Part 3: Initialize the character device */vdev->cdev = cdev_alloc();if (vdev->cdev == NULL) {ret = -ENOMEM;goto cleanup;}vdev->cdev->ops = &v4l2_fops;vdev->cdev->owner = owner;ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);if (ret < 0) {pr_err("%s: cdev_add failed\n", __func__);kfree(vdev->cdev);vdev->cdev = NULL;goto cleanup;}/* Part 4: register the device with sysfs */vdev->dev.class = &video_class;vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);vdev->dev.parent = vdev->dev_parent;dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);ret = device_register(&vdev->dev);if (ret < 0) {pr_err("%s: device_register failed\n", __func__);goto cleanup;}/* Register the release callback that will be called when the last   reference to the device goes away. */vdev->dev.release = v4l2_device_release;if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)pr_warn("%s: requested %s%d, got %s\n", __func__,name_base, nr, video_device_node_name(vdev));/* Increase v4l2_device refcount */v4l2_device_get(vdev->v4l2_dev);/* Part 5: Register the entity. */ret = video_register_media_controller(vdev);/* Part 6: Activate this minor. The char device can now be used. */set_bit(V4L2_FL_REGISTERED, &vdev->flags);return 0;cleanup:mutex_lock(&videodev_lock);if (vdev->cdev)cdev_del(vdev->cdev);video_devices[vdev->minor] = NULL;devnode_clear(vdev);mutex_unlock(&videodev_lock);/* Mark this video device as never having been registered. */vdev->minor = -1;return ret;}

因为v4l2的框架还是有点多,我也没有对应的项目,所以这里就先写到这里。。
为完待续…


点击全文阅读


本文链接:http://zhangshiyu.com/post/43607.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1