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