1. 程式人生 > >視訊驅動V4L2子系統驅動架構-框架

視訊驅動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);