V4L2框架之v4l2_open
V4L2整体框架如下图:
图片来源于http://blog.csdn.net/leesagacious/article/details/49948163
其整体驱动框架分为三个层次:
第一个层为字符设备驱动程序,主要是上层应用创建视频设备节点。这一层是整个V4L2层框架的外包接口,应用层通过API函数之后,最先进入的是字符设备驱动的fops。字符设备驱动程序由V4L2框架统一管理,其使用这个字符设备提供统一的外包接口。
第二层是V4L2核心层,其核心是video_device设备,其完成了视上尖设备的核心抽象。
第三层是V4L2的下层回调接口,是与硬件相关的。
其实本人对此的理解相是:第一层的字符设备类似于windows的direct show,第二层为ks层,第三层为具体的物理设备驱动,如usbvideo.sys。
V4L2设备初始化可详见:https://www.usbzh.com/article/detail-166.html 其核心是通过uvc_register_video_device注册视频设备。其结构体如下:
struct video_device
{
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
struct media_intf_devnode *intf_devnode;
struct media_pipeline pipe;
#endif
const struct v4l2_file_operations *fops;
u32 device_caps;
/* sysfs */
struct device dev;
struct cdev *cdev; //
struct v4l2_device *v4l2_dev;
struct device *dev_parent;
struct v4l2_ctrl_handler *ctrl_handler;
struct 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_t fh_lock;
struct list_head fh_list;
int dev_debug;
v4l2_std_id tvnorms;
/* callbacks */
void (*release)(struct video_device *vdev);
const struct v4l2_ioctl_ops *ioctl_ops;
DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
struct mutex *lock;
};
其字符设备在uvc_register_video_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);
可见,其字符设备结构体是动态申请创建,其对应的OPS为v4l2_fops
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视频设备对应用层的外包接口。
video_device全局数组与索引minor
在Windows内核中,设备一般是通过LIST_ENTRY链接在,但在Linux这就随其框架决定的。在V4L2中他其实就是一个数组,数组的索引就是vdev->minor
#define VIDEO_NUM_DEVICES 256
static struct video_device *video_devices[VIDEO_NUM_DEVICES];
而数组的索引就是vdev->minor
video_devices[vdev->minor] = vdev;
而这个minor的来源于视频分类中查找一个空的索引。
#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_GRABBER:
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;
}
#endif
vdev->minor = i + minor_offset;
v4l2_open
最后我们查看字符设备的v4l2_open
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))
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
dprintk("%s: open (%d)\n",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
其就是通过minor使用video_devdata函数查找到对应的video_device
static inline struct inode *file_inode(const struct file *f)
{
return f->f_inode;
}
static inline unsigned iminor(const struct inode *inode)
{
return MINOR(inode->i_rdev);
}
struct video_device *video_devdata(struct file *file)
{
return video_devices[iminor(file_inode(file))];
}
最后将打开操作向下传递到vdev的open。
if (vdev->fops->open) {
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
}