Linux v4l2架构学习总链接
vivi驱动分析如下:
step 1 : 打开设备节点
int fd = open("/dev/video0", flag);
应用层的open函数最后会对应到驱动的fops的open函数
vivi驱动中配置如下
video_device[vdev->minor] = vdev;
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
static const struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.open = v4l2_open,
...
};
接下来分析 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);
/*
* 这里从filp中得到vdev
* 原理是获取filp的inode,然后从inode->i_rdev中获取次设备号
* 以次设备号为下标从数组video_device中获取vdev
* 注册video节点的时候保存方式是 video_device[vdev->minor] = vdev;
*/
vdev = video_devdata(filp);
/*
* video_is_registered通过判断 vdev->flags中是否置位 V4L2_FL_REGISTERED
* 驱动中注册video的时候置位代码
* set_bit(V4L2_FL_REGISTERED, &vdev->flags);
*/
/* 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);
/*
* vdev->fops->open
* vivi驱动中有这句代码
* *vfd = vivi_template;
* 所以可以知道 open就是 vivi_template的fops的open函数
* 也就是 v4l2_fh_open
*/
if (vdev->fops->open) {
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
}
if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
printk(KERN_DEBUG "%s: open (%d)\n",
video_device_node_name(vdev), ret);
/* decrease the refcount in case of an error */
if (ret)
video_put(vdev);
return ret;
}
跟进分析 v4l2_fh_open
这里的fh是file handler的缩写
void v4l2_fh_add(struct v4l2_fh *fh)
{
unsigned long flags;
/*
* fh->vdev->prio 是 global全局的,是一个数组记录prio值
* fh->prio 是 local本地的prio值
* v4l2_prio_open的操作就是
* 1. fh->vdev->prios[V4L2_PRIORITY_DEFAULT]++
* 2. local值 fh->prio = V4L2_PRIORITY_DEFAULT
*/
v4l2_prio_open(fh->vdev->prio, &fh->prio);
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
/*
* fh通过list挂载到链表vdev->fh_list上
*/
list_add(&fh->list, &fh->vdev->fh_list);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
}
void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
{
fh->vdev = vdev;
/*
* 这里的ctrl_handler很重要,后面调用handler ctrl的时候需要它
* 后面会专门分析 handler ctrl
*/
/* Inherit from video_device. May be overridden by the driver. */
fh->ctrl_handler = vdev->ctrl_handler;
INIT_LIST_HEAD(&fh->list);
/*
* V4L2_FL_USES_V4L2_FH标志位置位
* 后面处理handler ctrl的时候应该会用到,遇到再分析
*/
set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
/*
* determine_valid_ioctls() does not know if struct v4l2_fh
* is used by this driver, but here we do. So enable the
* prio ioctls here.
*/
/*
* VIDIOC_G_PRIORITY 和 VIDIOC_S_PRIORITY
* 同样遇到再分析
*/
set_bit(_IOC_NR(VIDIOC_G_PRIORITY), vdev->valid_ioctls);
set_bit(_IOC_NR(VIDIOC_S_PRIORITY), vdev->valid_ioctls);
fh->prio = V4L2_PRIORITY_UNSET;
init_waitqueue_head(&fh->wait);
INIT_LIST_HEAD(&fh->available);
INIT_LIST_HEAD(&fh->subscribed);
fh->sequence = -1;
}
int v4l2_fh_open(struct file *filp)
{
/*
* vdev的获取方法见上面的分析
*/
struct video_device *vdev = video_devdata(filp);
struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
filp->private_data = fh;
if (fh == NULL)
return -ENOMEM;
v4l2_fh_init(fh, vdev);
v4l2_fh_add(fh);
return 0;
}
总体来看,vivi的open操作主要就是操作了fh,这个fh的作用暂时看不出来。
后面遇到的时候再分析。