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);
}
}
接下来看一下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_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);
/*根据次设备号minor获取在video_register_device函数中注册的video_device*/
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
增加设备的引用计数
底层是通过kobject_get()来实现
kobject是通过内嵌的struct kref 来实现的。
struct kref { //kref是一个引用计数器,它被嵌套进其他的结构体中,记录所嵌套结构的引用计数
atomic_t refcount;
}
*/
video_get(vdev);
mutex_unlock(&videodev_lock);
if (vdev->fops->open) {
if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
ret = -ERESTARTSYS;
goto err;
}
/*如果设备已经注册,就调用具体设备提供的open函数*/
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);
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;
}
在v4l2_open函数中的关键操作就是vdev = video_devdata(filp),这个函数的作用就是根据设备的次设备号minor获取在video_register_device函数中注册的video_device。接下来分析该函数是如何获取video_device的。
struct video_device *video_devdata(struct file *file)
{
return video_device[iminor(file->f_path.dentry->d_inode)];
}
static inline unsigned iminor(const struct inode *inode)
{
return MINOR(inode->i_rdev);//通过inode节点返回设备的次设备号。
}
因此video_devdata函数实际上就是调用了video_device[设备的次设备号],即以设备的次设备号为下标,从数组video_device中获取一个video_device。
问题1:这个video_device是什么时候放入数组video_device中去的?
问题2:次设备是如何构建的呢?
对于这两个问题的分析在浅析video_register_device一文中已深入分析过。
-----------------------------------------------------------------------------------
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;
}