V4L2框架分析学习


一、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

、v4l2_ctrl_handler 

用于保存子设备控制方法集的结构体,对于视频设备这些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机制 : 在文件描述符上等待一些事件

  #include <poll.h>

       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这些符号,这样才能找到对应的地址连接。/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值