深入学习Linux摄像头系列
深入学习Linux摄像头(二)v4l2驱动框架
文章目录
一、V4L2 框架
1.1 相关对象
v4l2驱动框架主要的对象有video_device
、v4l2_device
、v4l2_subdev
、videobuf
-
video_device
一个字符设备,为用户空间提供设备节点(/dev/videox),提供系统调用的相关操作(open、ioctl…)
-
v4l2_device
嵌入到video_device中,表示一个v4l2设备的实例
-
v4l2_subdev
依附在v4l2_device之下,并表示一个v4l2设备的子设备,一个v4l2_devide下可以有多个sub_device
-
videobuf
v4l2驱动的缓存管理
下面有必要对v4l2_device和v4l2_subdev来进行说明
subdev的设计目的是为了多路复用,就是用一个v4l2_device可以服务多个v4l2_subdev
下面以我们手机的摄像头来举例
-
CMOS摄像头
对于一款CMOS摄像头来说,有两个接口,一个是
摄像头接口
,一个是I2C接口
摄像头接口负责传输图像数据,I2C接口负责传输控制信息,所以又可以将CMOS摄像头看作是一个I2C模块
如下图所示
-
芯片片上资源
在一款芯片上面,摄像头相关的有
摄像头控制器
、摄像头接口
、I2C总线
SOC上可以有多个摄像头控制器,多个摄像头接口,多个I2C总线
摄像头控制器负责接收和处理摄像头数据,摄像头接口负责传输图像数据,I2C总线负责传输控制信息
如下图所示
对于手机而言,一般都有两个摄像头,一个前置摄像头,一个后置摄像头,其接发下图所示
我们可以选择让控制器去操作哪一个摄像头,这就做到了使用一个摄像头控制器来控制多个摄像头,这就是多路复用
上面说要使用一个摄像头控制器去操作多个摄像头,这是我们的目的,那么在软件中是怎么实现的呢?
我们回到V4L2来,再来谈v4l2_device
和v4l2_subdev
上面我们介绍到v4l2_device
表示一个v4l2实例
在V4L2驱动中,使用v4l2_device
来表示摄像头控制器
使用v4l2_subdev
来表示具体的某一个摄像头的I2C控制模块
,进而通过其控制摄像头
v4l2_device
里有一个v4l2_subdev
链表,可以选择v4l2_device
去控制哪一个v4l2_subdev
相信到此,你对v4l2_device
和v4l2_subdev
就有所了解了
当然某些驱动是没有v4l2_subdev,只有video_device
经过上面的讲解,我们用一张图来总结
前面说video_device是一个字符设备,从图中可以看出,video_device内含一个cdev
v4l2_device是一个v4l2实例,嵌入到video_device中
v4l2_device维护者一个链表管理v4l2_subdev,v4l2_subdev表示摄像头的I2C控制模块
1.2 V4L2 框架
在理清楚V4L2中的主要对象后,我们来介绍V4L2的框架
在介绍V4L2驱动框架前,我们先回顾一下简单的字符设备的编写
- 分配一个字符设备(cdev)
- 设置一个fops
- 注册字符设备
复杂的字符设备
对于复杂的字符设备,内核都是采用分层的方法,一般分驱动核心层还有硬件相关层
核心层会帮你完成字符设备的分配,fops的设置,注册字符设备,并向硬件相关层提供一个相应的对象和注册接口
硬件相关层则需要分配相应的对象,设置对象和对象的fops,并注册到核心层中
当应用层发生系统调用,会先来到核心层,核心层再通过回调函数调用到硬件相关层的驱动
对于V4L2的驱动框架也是如此,可分为V4L2驱动核心层和硬件相关层
下面先用一张图来总结大致V4L2的驱动框架
从图中可以看出V4L2分为核心层还有硬件相关层
核心层负责注册字符设备,然后提供video_device对象和相应的注册接口给硬件相关层使用
硬件相关层需要分配一个video_device
并设置它,然后向核心层注册,核心层会为其注册字符设备并且创建设备节点(/dev/videox)。同时硬件相关层还需要分配和设置相应的v4l2_device
和v4l2_subdev
,其中v4l2_device
的一个比较重要的意义就是管理v4l2_subdev
,当然有一些驱动并不需要实现v4l2_subdev
,此时v4l2_device
的意义就不是很大了
当应用层通过/dev/video
来操作设备的时候,首先会来到V4L2的核心层,核心层通过注册进的video_device
的回调函数调用相应的操作函数,video_device
可以直接操作硬件或者是通过v4l2_subdev
来操作硬件
二、V4L2的数据结构
介绍完V4L2的驱动框架后,来看一看内核中各对象的数据结构
2.1 V4L2主要对象的数据结构
-
video_device
struct video_device { /* character device */ struct cdev *cdev; /* v4l2_device parent */ struct v4l2_device *v4l2_dev; /* device ops */ const struct v4l2_file_operations *fops; /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; };
可以看到video_device中含有一个cdev还有v4l2_device,此外还有fops和ioctl_ops,从应用层进行系统调用会经过v4l2的核心层回调到这里
其中
v4l2_file_operations
和v4l2_ioctl_ops
如下struct v4l2_file_operations { struct module *owner; ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*ioctl) (struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct file *); int (*release) (struct file *); };
熟悉v4l2应用编程的应该都知道v4l2有很多ioctl操作,具体实现都在这里
struct v4l2_ioctl_ops { int (*vidioc_querycap)(struct file