win德赢网址|官方网站win德赢网址|官方网站

vwin德赢唯一授权官网
德赢体育官网

V4L2 driver -整体架构

我的uvc开源地址:gitee-uvc

字符设备驱动程序核心:V4L2本身就是一个字符设备,具有字符设备所有的特性,暴露接口给用户空间。V4L2 驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数。平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev。具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。

1 从字符设备开始:

熟悉v4l2用户空间编程的都知道, v4l2编程主要是调用一系列的ioctl函数去对v4l2设备进行打开, 关闭, 查询, 设置等操作. v4l2设备是一个字符设备, 而且其驱动的主要工作就是实现各种各样的ioctl.

v4l2的整体框架如下图所示:

V4L2 :video for linux version 2 ,是 linux 里一套标准的视频驱动。本文来分析一下它的核心框架。

在v4l2的核心中对这个file_operations的实现如下:

static const struct file_operations v4l2_fops = { .owner = THIS_MODULE, .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_ioctl,#ifdef CONFIG_COMPAT .compat_ioctl = v4l2_compat_ioctl32,#endif .release = v4l2_release, .poll = v4l2_poll, .llseek = no_llseek,};

这个v4l2_fops函数最终绑定在一个cdev上, 并注册到系统中。

v4l2_open为例(代码在kerneldriversmediav4l2-core中):

/* Override for the open function */static int v4l2_open(struct inode *inode, struct file *filp){ struct video_device *vdev; int ret = 0; /* Check if the video device is available */ mutex_lock(&videodev_lock); vdev = video_devdata(filp); /* return ENODEV if the video device has already been removed. */ if (vdev == NULL || !video_is_registered(vdev)) { mutex_unlock(&videodev_lock); return -ENODEV; } /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); if (vdev->fops->open) { if (video_is_registered(vdev)) //这里就是调用了file_operations的open函数 ret = vdev->fops->open(filp); else ret = -ENODEV; } if (vdev->debug) printk(KERN_DEBUG "%s: open (%d)", video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ if (ret) video_put(vdev); return ret;}

2. video_device结构体:

video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间。

我们是使用video_device来操作的,看看video_device这个结构体:

struct video_device{#if defined(CONFIG_MEDIA_CONTROLLER) struct media_entity entity;#endif /* device ops */ const struct v4l2_file_operations *fops; /* sysfs */ struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ /* Set either parent or v4l2_dev if your driver uses v4l2_device */ struct device *parent; /* device parent */ struct v4l2_device *v4l2_dev; /* v4l2_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; 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 */ int debug; /* Activates debug level*/ /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ v4l2_std_id current_norm; /* Current tvnorm */ /* 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;};

3. V4L2_device结构体:

这个结构体中包含了一个非常重要的结构体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; /* BKL replacement mutex. Temporary solution only. */ struct mutex ioctl_lock; /* 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);};

3.1 v4l2_device的注册和注销:

int v4l2_device_register(struct device*dev, struct v4l2_device *v4l2_dev)static void v4l2_device_release(struct kref *ref)

4. v4l2_subdev结构体

V4l2_subdev代表子设备,包含了子设备的相关属性和操作。先来看下结构体原型:

struct v4l2_subdev { struct v4l2_device *v4l2_dev; //指向父设备 //提供一些控制v4l2设备的接口 const struct v4l2_subdev_ops *ops; //向V4L2框架提供的接口函数 const struct v4l2_subdev_internal_ops *internal_ops; //subdev控制接口 struct v4l2_ctrl_handler *ctrl_handler; /* name must be unique */ charname[V4L2_SUBDEV_NAME_SIZE]; /*subdev device node */ struct video_device *devnode; };

4.1 subdev的注册和注销

当我们把v4l2_subdev需要实现的成员都已经实现,就可以调用以下函数把子设备注册到V4L2核心层:

int v4l2_device_register_subdev(struct v4l2_device*v4l2_dev, struct v4l2_subdev *sd)

当卸载子设备时,可以调用以下函数进行注销:

void v4l2_device_unregister_subdev(struct v4l2_subdev*sd)

5. 应用层具体流程框架:

我们使用一张图来体现吧:

欢迎阅读本文章: 荣宏宇

vwin德赢体育app

vwin德赢唯一授权官网