原文:https://blog.csdn.net/leesagacious/article/details/49995729
1、app: open("/dev/video0",….)
drv: v4l2_fops
.v4l2_open //这个函数主要做的是,调用具体设备提供的open函数
/* 问题来了,应用程序调用open("/dev/video0",….),v4l2_open为什么会最终会被调用?
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 *filp)
{
struct video_device *vdev;
int ret = 0;
/* Check if the video device is available */
mutex_lock(&videodev_lock);
vdev = video_devdata(filp);
/*
video_device[iminor(file->f_path.dentry->d_inode)];
iminor(const struct inode *inode)
MINOR(inode->i_rdev);
分析: struct inode *inode = file->f_path.dentry->d_inode;
int minor = MINOR(inode ->i_rdev)
根据次设备号minor获取在video_register_device函数中注册的video_device,它是放在video_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);
}
}
*/
/*
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;
}
/* and increase the device refcount_*/_
/*
增加设备的引用计数
底层是通过kobject_get()来实现
kobject是通过内嵌的struct kref 来实现的。
struct kref { //kref是一个引用计数器,它被嵌套进其他的结构体中,记录所嵌套结构的引用计数
atomic_t refcount;
}
*/
video_get(vdev); mutex_unlock(&videodev_lock);//释放互斥锁 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); }
err:
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
-----------------------------------------------------------------------------------
2、app: read
drv:v4l2_fops
.v4l2_read //这个函数主要做的就是调用具体设备提供的read函数
如果清楚了v4l2_open的过程,那么对于v4l2_read的过程应该是很简单了,现只将源码贴出:
static ssize_t v4l2_read(struct file *filp, char __user *buf,
size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV;
if (!vdev->fops->read)
return -EINVAL;
if (vdev->lock && mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
if (vdev->lock)
mutex_unlock(vdev->lock);
return ret;
}
-------------------------------------------------------------------------------------------------
3、app: write
drv: v4l2_fops
.v4l2_write//这个函数主要做的就是调用具体设备提供的read函数
static ssize_t v4l2_write(struct file *filp, const char __user *buf,
size_t sz, loff_t *off)
{
struct video_device *vdev = video_devdata(filp);
int ret = -ENODEV;
if (!vdev->fops->write)
return -EINVAL;
if (vdev->lock && mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->write(filp, buf, sz, off);
if (vdev->lock)
mutex_unlock(vdev->lock);
return ret;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章