一、v4l2_device.c:v4l2框架中充当所有v4l2_subdev的父设备,管理着注册在其下的子设备
1.1 module_init(videodev_init) //videodev_init初始化函数申请了v4l2的字符设备号,注册了类设备
module_exit(videodev_exit)
1.2 __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner) //register video4linux devices
1) 检查设备类型:GRABBER、VBI 、RADIO、SUBDEV
2)找到一个 free minor(硬盘分区编号), device node number and device index
3)初始化设备
4) 系统中注册设备
1.3 用户空间使用Open,read,write,close处理devicenode(/dev/videoX)
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,}
二、V4L2_subdev.c 代表子设备,包含了子设备的相关属性和操作
const struct v4l2_file_operations v4l2_subdev_fops ={
.owner = THIS_MODULE,
.open = subdev_open,
.unlocked_ioctl = subdev_ioctl,
.release = subdev_close,
.poll = subdev_poll,
};
三、V4L2_fh.c 保存用于 V4L2 框架的文件句柄(子设备)特定数据的简单方法
void v4l2_fh_init(struct v4l2_fh *fh, structvideo_device *vdev)
//初始化v4l2_fh,添加v4l2_ctrl_handler到v4l2_fh:
void v4l2_fh_add(struct v4l2_fh *fh)
//添加v4l2_fh到video_device,方便核心层调用
v4l2_fh_is_singular(struct v4l2_fh *fh)
//检查 v4l2_fh 结构体是否是相关设备节点打开的唯一文件句柄。是返回1,否则返回0
用于保存子设备控制方法集的结构体,对于视频设备这些ctrls包括设置亮度、饱和度、对比度和清晰度等,用链表的方式来保存ctrls,可以通过v4l2_ctrl_new_std函数向链表添加ctrls。
五、Linux设备驱动之IO控制
大部分驱动除了需要具备读写设备的能力之外,还需要具备对硬件控制的能力
int ioctl(int fd,unsigned long cmd,...); /* fd:文件描述符 cmd:控制命令 ...:可选参数:插入*argp,具体内容依赖于cmd */ //用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情
//nr为序号,datatype为数据类型,如int 在驱动程序中实现的ioctl函数体内,实际上是有一个switch {case}结构,每一个case对应一个命令码,做出一些相应的操作 _IO(type, nr ) //没有参数的命令 _IOR(type, nr, datatype) //从驱动中读数据 _IOW(type, nr, datatype) //写数据到驱动 _IOWR(type,nr, datatype) //双向传送 在Linux核心中是这样定义一个命令码的: ____________________________________| 设备类型 | 序列号 | 方向 | 数据尺寸 |
|----------|--------|------|-------- |
| 8 bit | 8 bit | 2 bit |8~14 bit|
|----------|--------|------|-------- |
用户空间通过打开/dev/目录下的设备节点,获取到文件的file结构体,通过系统调用ioctl把cmd和arg传入到内核。通过一系列的调用后最终会调用到
__video_do_ioctl函数,然后通过cmd检索v4l2_ioctls[],判断是INFO_FL_STD还是INFO_FL_FUNC。如果是INFO_FL_STD会直接调用到视频设备驱动中
video_device->v4l2_ioctl_ops函数集。如果是INFO_FL_FUNC会先调用到v4l2自己实现的标准回调函数,然后根据arg再调用
到video_device->v4l2_ioctl_ops或v4l2_fh->v4l2_ctrl_handler函数集。
常见的ioctl命令:
VIDIOC_QUERYCAP /* 获取设备支持的操作 */ VIDIOC_G_FMT /* 获取设置支持的视频格式 */ VIDIOC_S_FMT /* 设置捕获视频的格式 */ VIDIOC_REQBUFS /* 向驱动提出申请内存的请求 */ VIDIOC_QUERYBUF /* 向驱动查询申请到的内存 */ VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列 */ VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 */ VIDIOC_STREAMON /* 打开视频流 */ VIDIOC_STREAMOFF /* 关闭视频流 */ VIDIOC_QUERYCTRL /* 查询驱动是否支持该命令 */ VIDIOC_G_CTRL /* 获取当前命令值 */ VIDIOC_S_CTRL /* 设置新的命令值 */ VIDIOC_G_TUNER /* 获取调谐器信息 */ VIDIOC_S_TUNER /* 设置调谐器信息 */ VIDIOC_G_FREQUENCY /* 获取调谐器频率 */ VIDIOC_S_FREQUENCY /* 设置调谐器频率 */
1、Linux下的poll机制 : 在文件描述符上等待一些事件
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events 实际发生的事件*/
};
当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno。
2、EXPORT_SYMBOL使用
EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。
使用方法:
第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)
第二、在掉用该函数的模块中使用extern对之声明
第三、首先加载定义该函数的模块,再加载调用该函数的模块
和System.map做一下对比:
System.map 中的是连接时的函数地址。连接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。
EXPORT_SYMBOL 的符号, 是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。/