初识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正则表达式基础知识,主要给大家介绍一下特殊字符“{}”的用法,具体的教程如下. 特殊字符“{}”实质上也是一个限定词的用法,其限定前面字符所出现的次数,其常用的模式有三种 ...
随机推荐
- CentOs篇
Advanced-高级配置.Security-安全.Boot-启动引导: 1.Removable Devices-移动设备 2.Hard Drive-本地硬盘 3.CD-ROM- Drive-光盘 4 ...
- Codeforces Round #552 (Div. 3) EFG(链表+set,dp,枚举公因数)
E https://codeforces.com/contest/1154/problem/E 题意 一个大小为n(1e6)的数组\(a[i]\)(n),两个人轮流选数,先找到当前数组中最大的数然后选 ...
- Paper | Xception: Deep Learning with Depthwise Separable Convolutions
目录 故事 Inception结构和思想 更进一步,以及现有的深度可分离卷积 Xception结构 实验 这篇论文写得很好.只要你知道卷积操作或公式,哪怕没看过Inception,也能看懂. 核心贡献 ...
- 剑指offer:对称的二叉树(镜像,递归,非递归DFS栈+BFS队列)
1. 题目描述 /** 请实现一个函数,用来判断一颗二叉树是不是对称的. 注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的 */ 2. 递归 思路: /** 1.只要pRoot.left和 ...
- MySQL统计信息简介
作者:王小龙@网易乐得DBA 原文地址: http://mp.weixin.qq.com/s/698g5lm9CWqbU0B_p0nLMw MySQL执行SQL会经过SQL解析和查询优化的过程,解析器 ...
- Python连载19-装饰器
一.检视一个函数相同的另一种方法 利用属性:函数._name def hello(): print("我是一个测试程序") f = hello print(f.__name__) ...
- 大话设计模式Python实现-状态模式
状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 下面是一个状态模式的demo: #!/usr/bin/env python # -*- ...
- MyBatis 构造动态 SQL 语句
以前看过一个本书叫<深入浅出 MFC >,台湾 C++ 大师写的一本书.在该书中写到这样一句话,“勿在浮沙筑高台”,这句话写的的确对啊.编程很多语言虽然相通,但是真正做还是需要认真的学习, ...
- redis之漏斗限流
Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell.该模块也使用了漏斗算法,并提供了原子的限流指令.有了这个模块,限流问题就非常简单了.
- [STL] UVA 10815 安迪的第一个字典 Andy's First Dictionary
1.set 集合 哦....对了,set有自动按照字典序排序功能..... 声明和插入操作 #include <cstdio> #include <vector> #inclu ...