最近打算翻翻linux-2.6.32内核中V4L2的源码,linux-2.6.32.2/Documentation/Video4linux/v4l2-framework.txt有关于V4L2驱动的结构说明,V4L2感觉挺复杂的,所以打算从vivi虚拟驱动入手掌握V4L2驱动
vivi驱动涉及文件:
vivi.c 驱动的具体实现
v4l2-common.c
V4L2-dev.c video_register_device(struct video_device *vdev...);
V4L2-device.c v4l2_device_register(struct device *dev,struct v4l2_device *v4l2_device);
videobuf_core.c
videobuf_vmalloc.c
如果你单独在make menuconfig中将vivi驱动编译为模块时,你需要依次加入这些模块:insmod videobuf_core.ko,insmod videobuf_vmalloc.ko,insmod video_common.ko,insmod vivi.ko等。
主要关注以下几个结构体:
struct video_device 视频类设备的基类,另外字符设备驱动的基类为cdev,所有的操作都是围绕cdev结构体
struct v4l2_file_operations 包含vivi_read,vivi_open等函数具体实现。在系统调用的时候,会首先调用cdev的file_operations的v4l2_read,v4l2_open等函数,在这些函数中会使用struct file中的video_device来调用vivi_read等函数具体去实现。
struct v4l2_ioctl_ops ioctl操作的函数的具体实现
struct V4l2_device
struct videobuf_buffer 视频数据缓冲区,对应一帧视频,里面包含视频的大小等信息
struct videobuf_queue 视频缓冲区队列
以上几个结构体可能不会被单独使用,一般都被包含在某个更大的对象结构中,比如video_device和v4l2_device都包含在vivi_dev中,而vivi_dev被包含在viv_fh中,videobuf_buffer被包含在vivi_buffer中。
下面有必要看看struct vivi_fh和struct vivi_dev和vivi_buffer结构体
struct vivi_fh{
struct vivi_dev *dev;
struct vivi_fmt *fmt;
unsigned int width,height;
structvideo_buf_queue vb_vidq;
enum v4l2_buf_type type;
unsigned charbars[8][3];//用于保存8个色块的RGB值或YUV值
int input;//控制选择色块彩条的标准
}
struct vivi_dev{
struct list_head vivi_devlist;//内核双向链表
struct v4l2_device v4l2_dev;
spinlock_t slock;
struct mutex mutex;
int users;
struct video_device *vfd;
struct vivi_damqueue vidq;//DMA队列
int h,m,s,ms;//定时器定义
unsigned long jiffies;
char timestr[13];
int mv_count;//控制彩条移动
int input;
int qctrl_regs[ARRAY_SIZE(vivi_qctrl)];
}
struct vivi_buffer{
struct videobuf_buffer vb;
struct vivi_fmt *fmt;
}
下面开始分析程序流程:
在vivi.c文件中
vivi_create_instance()
v4l2_device_register()
给video_device vdev对象赋值v4l2_file_operations和v4l2_ioctl_ops等
video_device_register()
初始化video_device对象vdev
初始化video_device中的cdev结构体
cdev_add()注册字符设备
初始化video_device中struct device结构体
device_register()在/dev/目录下创建设备节点
struct v4l2_file_operations vivi_fops={
.owner = THIS_MODULE,
.open = vivi_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
.ioctl = video_ioctl2,
.mmap = vivi_mmap,
};
需要说明的是整个vivi_fops是属于video_device的,这里的vivi_open,vivi_read都是最底层的具体实现,被其他上层的调用。在V4l2_dev.c文件中
struct file_operations v4l2_fops = {
.owner = THIS_MODULE,
.read = v4l2_read,
.write = v4l2_write,
.open = v4l2_open,
.mmap = v4l2_mmap,
.ioctl = v4l2_ioctl,
...
}
这个file_opertions v4l2_fops是字符设备给上层提供的系统调用,这里的v4l2_open函数最终调用的还是上面的vivi_open的实现,来看看v4l2_open的实现
static int v4l2_open(struct inode *inode, struct file *filp)
{
struct video_device *vdev;
vdev = video_devdata(filp);
if (vdev->fops->open)
ret = vdev->fops->open(filp);
}
另外一个很重要的ioctl呢?
在file_operations中的v4l2_ioctl调用的是
static int v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
struct video_device *vdev = video_devdata(filp);
return vdev->fops->ioctl(filp, cmd, arg);
}
v4l2_dev.c文件里file_operations中的v4l2_ioctl最终调用的还是vivi.c文件里v4l2_file_operations中的video_ioctl2函数,但是这里的video_ioctl2不像vivi_open,vivi_read等是具体的实现,video_ioctl2具体的实现已经单独拿出来放到v4l2_ioctl.c文件中了,所以video_ioctl2最终调用的是v4l2_ioctl.c文件中的__video_do_ioctl(file,cmd,arg)函数,而__video_do_ioctl(file,cmd,arg)函数又回过来调用vivi_ioctl中vivi_ioctl_ops来具体实现。
以上的调用关系似乎有点复杂,来个图方便理解吧。
设备也注册完了,设备的系统调用也具体实现了,接下来该看看视频缓冲区的申请与填写等操作了。
videobuf_queue,videobuf_buffer关系、vivi_buffer与viv_damqueue关系需要理解Linux设备模型中的面向对象的概念,由于C语言不支持面向对象操作,所以为了使用面向对象的方法,采用在一个结构中嵌入另外一个结构的技术。videobuf_queue相当于是缓冲队列,来管理很多块缓冲区,videobuf_buffer相当于是一块缓冲区,但是一般在驱动中不直接使用这两个概念,而是将它们嵌入到更大的结构中vivi_buffer和vivi_damqueue结构中
通过在vivi_open中使用函数videobuf_queue_vmalloc_init来初始化缓冲区并肩通过缓冲区加入缓冲队列链表,其中在videobuf_queue_ops中实现了缓冲队列的一些操作,buffer_setup,buffer_prepare,buffer_queue,buffer_release等,但是这些申请的缓冲队列都是在内核空间,如何将它映射到用户空间呢,在vivi_mmap函数中videobuf_mmap_mapper(struct videobuf_queue *q,struct vm_area_struct *vma);将缓冲队列映射到vm_area_struct虚拟地址
此外,关于自动创建设备节点要多说两句,有篇文章写的挺好的,http://www.cnblogs.com/skywang12345/archive/2013/05/15/driver_class.html
class_simple_create() —— class_create() —— class_register()
class_simple_destory() —— class_destory() —— class_unregister()
class_device_create() —— device_create() —— class_device_register()
class_device_destory() —— device_destory() —— class_device_unregister()
这里只是简单说说我的理解class_create与class_register功能上类似,只是class_register需要分配struct class对象内存并初始化
自动创建设备节点class_create与device_create或者class_register,device_register,用户空间的udev会根据device_create创建相应的设备节点
有一篇文章写的不错可以参考一下:http://www.360doc.com/content/13/1010/16/7775902_320343471.shtml