视频驱动V4L2子系统驱动架构-框架

V4L2驱动框架

v4l2驱动架构如图所示,v4l2也就是video for linux two,那么也就是说还有One了,v4l2前面还有v4l

图中芯片模块对应Soc的各个子模块,video_device结构体主要用来控制Soc的video模块,v4l2_device会包含多个v4l2_subdev ,每个v4l2_subdev 用来控制各自的子模块,某些驱动不需要v4l2_subdev ,依靠video模块就能实现功能

这里写图片描述

v4l2驱动代码在drivers\media\v4l2-core文件夹下,文件看起来很多,但是根据字面意思来理解基本的功能都了解差不多了。videobuf是实现视频的内存分配的,对于v4l2和v4l分别对应不同的文件,如videobuf-core和videobuf2-core;v4l2-dev,v4l2-device,v4l2-subdev分别对应video_device,v4l2_device ,v4l2_subdev 的实现;v4l2-ioctl是实现ioctl等等。

video驱动代码在driver/media/目录下,下面分好多子目录,platform目录存放的是不同SoC的驱动代码,对应video_device,其他大多子目录如i2c、mmc、usb、tuners、radio等对应subdev的实现

v4l2驱动框架最重要的是理解ioctl,另外v4l2驱动框架最主要的是各个ioctl实现的功能,这些实现方式需要在实际操作中多加理解,不是难点。

用一个比较粗糙的图来表现他们之间的关系,大致为:

设备实例(v4l2_device)

           |______子设备实例(v4l2_subdev)

           |______视频设备节点(video_device)

           |______文件访问控制(v4l2_fh)

           |______视频缓冲的处理(videobuf/videobuf2)

struct v4l2_device; 
用来描述一个v4l2设备实例

struct v4l2_subdev, 
用来描述一个v4l2的子设备实例

struct video_device; 
用来创建设备节点/dev/videoX

struct v4l2_fh; 
用来跟踪文件句柄实例

v4l2-dev.c                  //linux版本2视频捕捉接口,主要结构体 video_device 的注册
v4l2-common.c               //在Linux操作系统体系采用低级别的操作一套设备structures/vectors的通用视频设备接口。
                            //此文件将替换videodev.c的文件配备常规的内核分配。
v4l2-device.c               //V4L2的设备支持。注册v4l2_device
v4l22-ioctl.c               //处理V4L2的ioctl命令的一个通用的框架。
v4l2-subdev.c               //v4l2子设备
v4l2-mem2mem.c              //内存到内存为Linux和videobuf视频设备的框架。设备的辅助函数,使用其源和目的地videobuf缓冲区。

头文件linux/videodev2.h、media/v4l2-common.h、media/v4l2-device.h、media/v4l2-ioctl.h、media/v4l2-dev.h、media/v4l2-ioctl.h等。

-----------------------------------------------------------------------------------------------------------------------------------------------------------

 V4L2支持三类设备:视频输入输出设备、VBI设备和radio设备(其实还支持更多类型的设备,暂不讨论),分别会在/dev目录下产生videoX、radioX和vbiX设备节点。我们常见的视频输入设备主要是摄像头,也是本文主要分析对象。下图V4L2在Linux系统中的结构图:

Linux系统中视频输入设备主要包括以下四个部分:

字符设备驱动程序核心:V4L2本身就是一个字符设备,具有字符设备所有的特性,暴露接口给用户空间;

V4L2驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数;

平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev。

具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。

V4L2的核心源码位于drivers/media/v4l2-core,源码以实现的功能可以划分为四类:

核心模块实现:由v4l2-dev.c实现,主要作用申请字符主设备号、注册class和提供video device注册注销等相关函数;

V4L2框架:由v4l2-device.c、v4l2-subdev.c、v4l2-fh.c、v4l2-ctrls.c等文件实现,构建V4L2框架;

Videobuf管理:由videobuf2-core.c、videobuf2-dma-contig.c、videobuf2-dma-sg.c、videobuf2-memops.c、videobuf2-vmalloc.c、v4l2-mem2mem.c等文件实现,完成videobuffer的分配、管理和注销。

Ioctl框架:由v4l2-ioctl.c文件实现,构建V4L2ioctl的框架。

----------------------------------------------------------------------------------------------------------------------------------------------------------

V4L2框架(2)

         结构体v4l2_device、video_device、v4l2_subdev和v4l2_fh是搭建框架的主要元素。下图是V4L2框架的结构图:

从上图V4L2框架是一个标准的树形结构,v4l2_device充当了父设备,通过链表把所有注册到其下的子设备管理起来,这些设备可以是GRABBER、VBI或RADIO。V4l2_subdev是子设备,v4l2_subdev结构体包含了对设备操作的ops和ctrls,这部分代码和硬件相关,需要驱动工程师根据硬件实现,像摄像头设备需要实现控制上下电、读取ID、饱和度、对比度和视频数据流打开关闭的接口函数。Video_device用于创建子设备节点,把操作设备的接口暴露给用户空间。V4l2_fh是每个子设备的文件句柄,在打开设备节点文件时设置,方便上层索引到v4l2_ctrl_handler,v4l2_ctrl_handler管理设备的ctrls,这些ctrls(摄像头设备)包括调节饱和度、对比度和白平衡等。

------------------------------------------------------------------------------------------------------------------------------------------------------

V4L2 core介绍

video_device

用于实现SoC的video模块

struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
    struct media_entity entity;
    struct media_intf_devnode *intf_devnode;
    struct media_pipeline pipe;
#endif
    /* device ops */
    const struct v4l2_file_operations *fops;--------------------------具体video模块实现函数

    /* sysfs */
    struct device dev;      /* v4l device */
    struct cdev *cdev;      /* character device */--------------------上层接口

    struct v4l2_device *v4l2_dev;   /* v4l2_device parent */----------v4l2_device 
    /* Only set parent if that can't be deduced from v4l2_dev */
    struct device *dev_parent;  /* device parent */

    /* Control handler associated with this device node. May be NULL. */
    struct v4l2_ctrl_handler *ctrl_handler;

    /* vb2_queue associated with this device node. May be NULL. */
    struct vb2_queue *queue;

    /* Priority state. If NULL, then v4l2_dev->prio will be used. */
    struct v4l2_prio_state *prio;

    /* device info */
    char name[32];
    int vfl_type;   /* device type */
    int vfl_dir;    /* receiver, transmitter or m2m */
    /* 'minor' is set to -1 if the registration failed */
    int minor;--------------------------------------------------------video-x的次设备号
    u16 num;
    /* use bitops to set/clear/test flags */
    unsigned long flags;
    /* attribute to differentiate multiple indices on one physical device */
    int index;

    /* V4L2 file handles */
    spinlock_t      fh_lock; /* Lock for all v4l2_fhs */
    struct list_head    fh_list; /* List of struct v4l2_fh */

    /* Internal device debug flags, not for use by drivers */
    int dev_debug;

    /* Video standard vars */
    v4l2_std_id tvnorms;        /* Supported tv norms */

    /* callbacks */
    void (*release)(struct video_device *vdev);

    /* ioctl callbacks */
    const struct v4l2_ioctl_ops *ioctl_ops;-------------------------具体功能的实现函数
    DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);

    /* serialization lock */
    DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
    struct mutex *lock;
};

struct video_device
     {
         /*设备操作函数 */
         const struct v4l2_file_operations *fops;

         /* 虚拟文件系统 */
         struct device dev;        /* v4l 设备 */
         struct cdev *cdev;        /* 字符设备 */

        struct device *parent;        /*父设备 */
        struct v4l2_device *v4l2_dev;    /* v4l2_device parent */

        /* 设备信息 */
         char name[32];
         int vfl_type;
         /* 'minor' is set to -1 if the registration failed */
        int minor;
        u16 num;
         /* use bitops to set/clear/test flags */
         unsigned long flags;
         /*属性来区分一个物理设备上的多个索引 */
        int index;

         /* V4L2 文件句柄 */
         spinlock_t        fh_lock; /*锁定所有的 v4l2_fhs */
         struct list_head    fh_list; /* List of struct v4l2_fh */

         int debug;            /* Activates debug level*/

         /* Video standard vars */
         v4l2_std_id tvnorms;        /* Supported tv norms */
         v4l2_std_id current_norm;    /* Current tvnorm */

         /* 释放的回调函数 */
        void (*release)(struct video_device *vdev);

         /* 控制的回调函数 */
         const struct v4l2_ioctl_ops *ioctl_ops;
     }

函数介绍:

//注册函数
static inline int __must_check video_register_device(struct video_device *vdev,
        int type, int nr)
//卸载函数
void video_unregister_device(struct video_device *vdev);    

v4l2_device

对应子模块的实现,包含多个子模块

struct v4l2_device {
    /* dev->driver_data points to this struct.
       Note: dev might be NULL if there is no parent device
       as is the case with e.g. ISA devices. */
    struct device *dev;
#if defined(CONFIG_MEDIA_CONTROLLER)
    struct media_device *mdev;
#endif
    /* used to keep track of the registered subdevs */
    struct list_head subdevs;
    /* lock this struct; can be used by the driver as well if this
       struct is embedded into a larger struct. */
    spinlock_t lock;
    /* unique device name, by default the driver name + bus ID */
    char name[V4L2_DEVICE_NAME_SIZE];
    /* notify callback called by some sub-devices. */
    void (*notify)(struct v4l2_subdev *sd,
            unsigned int notification, void *arg);
    /* The control handler. May be NULL. */
    struct v4l2_ctrl_handler *ctrl_handler;
    /* Device's priority state */
    struct v4l2_prio_state prio;
    /* Keep track of the references to this struct. */
    struct kref ref;
    /* Release function that is called when the ref count goes to 0. */
    void (*release)(struct v4l2_device *v4l2_dev);
};

struct v4l2_device {
//指向设备模型的指针
struct device *dev;
#if defined(CONFIG_MEDIA_CONTROLLER)
//指向一个媒体控制器的指针
struct media_device *mdev;
#endif
//管理子设备的双向链表,所有注册到的子设备都需要加入到这个链表当中
struct list_head subdevs;
//全局锁
spinlock_t lock;
//设备名称
char name[V4L2_DEVICE_NAME_SIZE];
//通知回调函数,通常用于子设备传递事件,这些事件可以是自定义事件
void (*notify)(struct v4l2_subdev*sd, uint notification, void *arg);
//控制句柄
struct v4l2_ctrl_handler*ctrl_handler;
//设备的优先级状态,一般有后台,交互,记录三种优先级,依次变高
struct v4l2_prio_state prio;
//ioctl操作的互斥量
struct mutex ioctl_lock;
//本结构体的引用追踪
struct kref ref;
//设备释放函数
void (*release)(struct v4l2_device*v4l2_dev);
};

函数介绍:

//注册函数
int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
                        struct v4l2_subdev *sd);
//卸载函数
void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
                        struct v4l2_subdev *sd);

v4l2_subdev

具体子模块的实现

struct 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;--------------------------功能实现函数
    /* Never call these internal ops from within a driver! */
    const struct v4l2_subdev_internal_ops *internal_ops;
    /* The control handler of this subdev. May be NULL. */
    struct v4l2_ctrl_handler *ctrl_handler;
    /* name must be unique */
    char name[V4L2_SUBDEV_NAME_SIZE];
    /* can be used to group similar subdevs, value is driver-specific */
    u32 grp_id;
    /* pointer to private data */
    void *dev_priv;
    void *host_priv;
    /* subdev device node */
    struct video_device *devnode;
    /* pointer to the physical device, if any */
    struct device *dev;
    /* The device_node of the subdev, usually the same as dev->of_node. */
    struct device_node *of_node;
    /* Links this subdev to a global subdev_list or @notifier->done list. */
    struct list_head async_list;
    /* Pointer to respective struct v4l2_async_subdev. */
    struct v4l2_async_subdev *asd;
    /* Pointer to the managing notifier. */
    struct v4l2_async_notifier *notifier;
    /* common part of subdevice platform data */
    struct v4l2_subdev_platform_data *pdata;
};
每个模块对应的实现函数
struct v4l2_subdev_ops {
    const struct v4l2_subdev_core_ops   *core;
    const struct v4l2_subdev_tuner_ops  *tuner;
    const struct v4l2_subdev_audio_ops  *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 {
#if defined(CONFIG_MEDIA_CONTROLLER)
//媒体控制器的实体,和v4l2_device
struct media_entity entity;
#endif
struct list_head list;
struct module *owner;
u32 flags;
//指向一个v4l2设备
struct v4l2_device *v4l2_dev;
//子设备的操作函数集
const struct v4l2_subdev_ops *ops;
//子设备的内部操作函数集
const struct v4l2_subdev_internal_ops*internal_ops;
//控制函数处理器
struct v4l2_ctrl_handler*ctrl_handler;
//子设备的名称
char name[V4L2_SUBDEV_NAME_SIZE];
//子设备所在的组标识
u32 grp_id;
//子设备私有数据指针,一般指向总线接口的客户端
void *dev_priv;
//子设备私有的数据指针,一般指向总线接口的host端
void *host_priv;
//设备节点
struct video_device devnode;
//子设备的事件
unsigned int nevents;
};

函数介绍:

//注册函数
struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
        struct i2c_adapter *adapter, const char *client_type,
        u8 addr, const unsigned short *probe_addrs);
struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
        struct spi_master *master, struct spi_board_info *info);

猜你喜欢

转载自blog.csdn.net/lxllinux/article/details/81743751