这个函数主要做了一下事情 :
/**
v4l2_open(): 这个函数主要做的是:
调用具体设备提供的open函数
应用程序调用open(/dev/videox)的时候,这个函数最终会被调用
1 : 为什么?
video_register_device
{
__video_register_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);
}
}
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,
};
*/
static int v4l2_open(struct inode * inode,struct file * file)
{
struct video_device * vdev;
int ret = 0;
mutex_lock(&videodev_lock);
/**
依据次设备号为索引值从video_devices[]中获取对应的video_device
原理已经在 "video_register_device()浅析"博文中说过,下面简单列出来 方便阅读
:
那么:video_device是再什么时候被放入到video_device[]的呢?
次设备号又是怎么构建的呢?
i : video_device[]第一个空缺项的下标
minor_offset : 根据传入的类型 选择得到
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;
}
vdev->minor = i + minor_offset;
...
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
是video_register_device()来实现的。
video_register_device()
{
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner)
{
再将设置好的video_device放入到video_device[]之前,设置它的成员flags(unsigned long 类型)
的第0位,表示这个video_device是注册过的了,
再其他位置会调用video_is_registered()来判断,其依据还是flags的第0位的值
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
依据次设备号为下标,将设置好的video_device放入到video_device[]中
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
}
}
*/
vdev = video_devdata(file)
/**
{
1 : 获取打开文件对应的索引节点对象inode
struct inode * inode = file->f_path.dentry->d_inode
获取次设备号
2 : int minor = MINOR(inode->i_rdev);
3 : 根据minor获取在video_register_device()中注册的video_device
return video_devices[iminor(file->f_path.dentry->d_inode)];
}
*/
/**
static inline int video_is_registered(struct video_device *vdev)
{
返回flags的第0位,
如果为1 表示已经注册过了,否则表明没有注册过。
在注册的时候 ,就已经设置flags的第0位了。
看注册的源码
v4l2_dev.c :
video_register_device()
{
__video_register_device()
{
1 : 设置flags的第0位,表示它已经被注册过了。
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
2 : 获取互斥锁 --- 访问临界区 ----- 加锁
mutex_lock(&videodev_lock);
//依据次设备号为索引值将设置好的video_device放到video_device[]中
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
}
}
return test_bit(V4L2_FL_REGISTERED, &vdev->flags);
}
*/
if (vdev == NULL || !video_is_registered(vdev))
{
mutex_unlock(&videodev_lock);
return -ENODEV;
}
/**
增加设备的引用计数
底层是通过kobject_get()来实现
kobject是通过内嵌的struct kref 来实现的。
struct kref { //kref是一个引用计数器,它被嵌套进其他的结构体中,记录所嵌套结构的引用计数
atomic_t refcount;
}
*/
video_get(vdev);
mutex_unlock(&videodev_lock);//释放互斥锁
/**
这里的fops是具体设备的v4l2_file_operations
比如:vivi
在vivi.c
vivi_init()
{
vivi_create_instance()
{
struct video_device *vfd;
.....
为video_device分配内存空间
vfd = video_device_alloc();
*vfd = vivi_template;
上面一行,video_device被设置成了vivi_template
这个vivi_template会依据次设备为下标放入到video_device[]中,
开头的几行代码依据次设备取出来的video_device在vivi中就是那个被放入的vivi_template
下面是vivi_template的代码
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.unlocked_ioctl = video_ioctl2, V4L2 ioctl handler 当应用程序调用ioctl,这个ioctl会被调用,根据cmd,会调用 vivi_ioctl_ops中的ioctl
.mmap = vivi_mmap,
};
所以 vdev->fops 就是vivi_fops
vdev->fops->open就是v4l2_fh_open了
}
}
如果具体设备的v4l2_file_operations提供了open函数
就调用这个open函数:v4l2_fh_open.否则什么也不做
*/
if (vdev->fops->open)
{
//这个lock是再v4l2_device_register()中初始化过了
//这里主要是判断有没有进行初始化,如果初始化了,core层会帮你释放这个锁。具体是在这个函数的最后一段代码中。
if(vdev->lock && mutex_lock_interruptible(vdev->lock))
{
ret = -ERESTARTSYS;
goto err;
}
//如果设备已经是被注册的了,就调用它提供的open函数
if (video_is_registered(vdev))
{
ret = vdev->fops->open(filp); //调用具体设备的fops的open函数
}
else{
ret = -ENODEV;
}
//如果使用v4l2_device_register()进行初始化了,就释放这把锁
if (vdev->lock)
{
mutex_unlock(vdev->lock);
}
}
}