应用open调用流程
使用open函数将/dev/videoX其打开
fd = open("/dev/video0", O_RDWR);
对应会调用到video_device->cdev->ops->open函数也就是v4l2_open函数
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,
};
/* Override for the open function */
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 video_device[minor]
/* 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) { //这里再vivi.c中赋值了fops= vivi_fops
if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
ret = -ERESTARTSYS;
goto err;
}
if (video_is_registered(vdev))
//这里就回调video_device->fops->open函数,也就是v4l2_fh_open
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
if (vdev->lock)
mutex_unlock(vdev->lock);
}
err:
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
那么看一下v4l2_fh_open()函数干了什么
int v4l2_fh_open(struct file *filp)
{
struct video_device *vdev = video_devdata(filp);
//定义fh并分配空间
struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
//将fh赋值给filp->private_data
filp->private_data = fh;
if (fh == NULL)
return -ENOMEM;
/* 主要就是初始化fh, 1.将fh->vdev指向vdev
* 2.设置fh->vdev->flags V4L2_FL_USES_V4L2_FH
* 3.fh->prio = V4L2_PRIORITY_UNSET;
*/
v4l2_fh_init(fh, vdev);
v4l2_fh_add(fh);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fh_open);
void v4l2_fh_add(struct v4l2_fh *fh)
{
unsigned long flags;
//在vivi驱动里面设置了,条件成立
if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags))
v4l2_prio_open(fh->vdev->prio, &fh->prio);
//v4l2_prio_open在这里有条件不满足所以v4l2_prio_change(global, local,
//V4L2_PRIORITY_DEFAULT);直接返回,否则会做下面几件事
//将V4L2_PRIORITY_DEFAULT赋值给fh->prio,
//增加fh->vdev->prio->prios[new]的计数
//并将fh->vdev->prio->prios[*local]计数减1
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
//将fh加入到video_device->fh_list链表中
list_add(&fh->list, &fh->vdev->fh_list);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
}
EXPORT_SYMBOL_GPL(v4l2_fh_add);
open调用过程还是简单,大概就如下图