原文: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;
}
