初识v4l2(四)-------v4l2_open、v4l2_read、v4l2_write浅析
原文: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;
}
初识v4l2(四)-------v4l2_open、v4l2_read、v4l2_write浅析的更多相关文章
- 初识v4l2(五)-------v4l2_ioctl浅析
上一篇文章中,已经介绍了v4l2_open.v4l2_read.v4l2_write的调用过程,相对于v4l2_ioctl,它们是比较简单的.下面来分析v4l2_ioctl.注意在这里还是分析以viv ...
- 初识V4l2(二)-------浅析video_register_device
在V4l2初识(一)中,我们已经知道当插上一个摄像头的时候,在uvc_driver.c中最终会调用函数video_register_device函数.接下来我们就简要分析这个函数做了哪些事情,揭开其神 ...
- 初识V4L2(三)-------分析vivi.c 虚拟视频驱动
1.分配video_device结构体 2.设置 3.注册 video_register_device 分析vivi.c: vivi_init( )//入口函数 vivi_create_instan ...
- 初识V4L2(一)
V4L2驱动框架概述 V4L2(video for linux two)是linux为视频设备提供的一套标准接口.它也属于字符设备驱动程序. 首先回顾普通字符设备驱动程序的写法: app : ...
- 初识Haskell 四:函数function之二 常见函数
对Discrete Mathematics Using a Computer的第一章Introduction to Haskell进行总结.环境Windows,关于函数的部分太长了,分开写. 常用的对 ...
- pandas 初识(四)
Pandas 和 sqlalchemy 配合实现分页查询 Mysql 并获取总条数 @api.route('/show', methods=["POST"]) def api_sh ...
- 初识PHP(四)PDO对象配置于使用
一.PDO的概念 PDO其实就是一个数据库的抽象层,使用PDO编程可以方便的在之后的实际运营中随时更改数据库而不用变更源代码.PDO的位置如下图所示: 二.PDO的开启 PDO需要使用php 5.1 ...
- Hive初识(四)
Hive本质上是一个数据仓库,但不存储数据(只存储元数据(metadata),Hive中的元数据包括表的名字,表的列和分区及分区及其属性,表的属性(是否为外部表等),表的数据所在目录等),用户可以借助 ...
- Python正则表达式初识(四)
今天继续给大家分享Python正则表达式基础知识,主要给大家介绍一下特殊字符“{}”的用法,具体的教程如下. 特殊字符“{}”实质上也是一个限定词的用法,其限定前面字符所出现的次数,其常用的模式有三种 ...
随机推荐
- 201271050130-滕江南-《面向对象程序设计(java)》第十六周学习总结
201271050130-滕江南-<面向对象程序设计(java)>第十六周学习总结 博文正文开头格式:(2分) 项目 内容 这个作业属于哪个课程 https://www.cnblogs.c ...
- HAproxy四层TCP负载均衡配置及测试
--------------------------------------------------centos 7 处理--------------------------------------- ...
- python time和datetime常用写法格式
python 的time和datetime常用写法 import time from datetime import datetime from datetime import timedelta # ...
- 开源规则引擎 drools
java语言开发的开源业务规则引擎 DROOLS(JBOSS RULES )具有一个易于访问企业策略.易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快.效率高.业务分析师或审核人员可以利用 ...
- git 邮箱错误-git log 中发现 XXX@163.com邮箱不符合要求,请务必使用公司邮箱。
场景描述: 重新配置电脑之后,提交了git,邮箱发生错误. git log # 查看日志git reset 版本 # 回到之前的版本,版本回退git config --global user.name ...
- 【ECharts】1.学习ECharts从现在开始:第一个Echart图形
首先,你需要下载ECharts所需的文件,我使用的是echarts-2.0.2版本,点击这里下载:echarts-2.0.2 下载解压后,下面有一系列文件夹,其中build中有我们需要引入的JS文件, ...
- win10 mysql5.7.28 配置安装
如果有服务,使用下面命令删除,管理员身份打开cmd : sc delete mysql 1.下载 https://dev.mysql.com/downloads/mysql/5.7.html 没有的O ...
- (二十四)golang--错误处理
在默认情况下,遇到错误时,程序会崩溃: 在发生错误时,我们可以捕获错误,使程序可以继续运行,并抛出错误提示: 错误处理: (1)Go语言追求简洁优雅,所以不支持传统的try catch finally ...
- python asyncio 获取协程返回值和使用callback
1. 获取协程返回值,实质就是future中的task import asyncioimport timeasync def get_html(url): print("start get ...
- LINQ 之 SelectMany
声明:本文为www.cnc6.cn原创,转载时请注明出处,谢谢! 一.第一种用法: public static IEnumerable<TResult> SelectMany<TSo ...