leesagacious 原创,欢迎转载
vivi入口函数: vivi_init( )
vivi_init(void)
static int __init vivi_init(void)
{
.....
for(i = 0;i < n_devs;i++){ //这里的n_devs的值是 1
//调用了这个函数,来创建设备
ret = vivi_create_instance(i);
}
....
}
vivi_create_instance(int inst)
static int __init vivi_create_instance(int inst)
{
struct video_device * vfd;//这是一个核心的结构,对应视频设备节点
......
//动态分配了一个video_device,用kzalloc实现
vfd = video_device_alloc();
if(!vfd){
goto unreg_dev;
}
/**这里vfd被设置为vivi_template了,
在后面的代码中会把这个vfd根据次设备号为索引值放入到video_device[]中,
在其他函数依据次设备号从这个数组中获取vide_device,获取的就是vivi_template
*/
*vfd = vivi_templaate;
/**注册video_device,
VFL_TYPE_GRABBER:下面的代码会根据传入的这个类型,使用switch语句 来选择设备的名字和次设备号(i + minor_offset)
node_nr 值为-1,表示第一个可用的数字,比如/dev/video0 /dev/video1
那么接下来的这个设备就是video2了,下面详细讲解
*/
ret = video_register_device(vfd,VFL_TYPE_GRABBER,node_nr);
}
注册video_device :video_register_device()
int video_register_device(struct video_device *vdev, int type, int nr)
{
//调用这个函数来注册video_device
return __video_register_device(vdev, type, nr, 1);
}
static int __video_register_device()
{
....
vdev->minor = -1; //次设备号被设置为-1,表示这个video_device还没有被注册过
WARN_ON(!vdev->release){
if(!vdev->release){ //如果要注册的video_device没有提供release函数,就会报错。
return -EINVAL;
}
}
//根据传入的类型,来选择设备的名字,传入的类型是VFL_TYPE_GRABBER
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
case VFL_TYPE_VTX:
name_base = "vtx";
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
.....
vdev->cdev = cdev_alloc(); //动态分配cdev,cdev一般被嵌入一个风大的结构体中,用来代表字符设备的通用信息
if(vdev->cdev == NULL){
return -ENOMEM;
goto cleanup;
}
/**
cdev的file_operations被设置为了v4l2_fops
应用程序调用open、read、ioctl....系统调用时,v4l2_fops中对应的函数会被调用,对应的函数又会去调用具体设备提供的函数
*/
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);//添加到内核
.....
//任何一个设备的注册都要经过这个函数,注册过程请见另一篇博文
ret = device_register(&vdev->dev); //注册这个设备
....
//获取互斥锁----访问临界区-----释放互斥锁
mutex_lock(&videodev_lock);
/**
依据次设备号位下标,将video_device(这里就是vivi_template)放入到video_device[]中,
static struct video_device *video_device[256];
以后在其他函数中依据次设备号从这个数组中获取的都是vivi_tempalte
*/
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock); //释放互斥锁
}
vivi_template
根据上面的分析,vivi_template在入口函数中就已经被放入到了video_device[]中了,其他函数依据其次设备号为索引值从video_device[]中获取到的就是vivi_template,
看看这个结构体张什么样
static struct video_device vivi_template = {
.name = "vivi",
.fops = &vivi_fops, //file_operations,为vfs提供调用接口
//查询设备性能、设置、获取、列:举数据格式、制式、缓冲区、启动摄像头.....涉及到整个数据的获取过程
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
//用于一些制式的操作。对于摄像头驱动,不是必需的
.tvnorms = V4L2_STD_525_60,
.current_norm = V4L2_STD_NTSC_M,
};
通用的file_operations : v4l2_fops
/**
一下定义的这些函数,对应与应用程序调用open、ioclt系统调用
看我们要讨论的unlocked_ioctl ,它对应与应用程序中的ioctl()
*/
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,
};
v4l2_ioctl
当应用程序调用ioctl的时候,v4l2_fops 结构中定义的v4l2_ioctl函数会被调用
static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
/**根据次设备号位下标,从video_device[]数组中取出video_device,
这里取出来的是vivi_tempalte
*/
struct video_device *vdev = video_devdata(filp);
/**
如果vivi_template的v4l2_file_operations中定义了unlocked_ioctl
就调用这个unlocked_ioctl
*/
if (vdev->fops->unlocked_ioctl) {
if (vdev->lock && mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
}
vivi_template 中的fops
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vivi_close,
.read = vivi_read,
.poll = vivi_poll,
/**
经过上面的分析,当应用程序调用ioctl的时候
这个函数会被调用
*/
.unlocked_ioctl = video_ioctl2,
.mmap = vivi_mmap,
};
video_ioctl2
long video_ioctl2(struct file *file,unsigned int cmd, unsigned long arg)
{ //根据命令cmd的不同,调用__video_do_ioctl,将数据拷贝到内核空间
return video_usercopy(file, cmd, arg, __video_do_ioctl);
}
__video_do_ioctl
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
{ //依据次设备号位下标,从video_device[]数组中取出来vivi_tempalte
struct video_device *vfd = video_devdata(file);
//获取vivi_tempalte的ioctl_ops
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
......
case VIDIOC_QUERYCAP: //cmd命令
{
//v4l2设备的能力结构体
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
if (!ops->vidioc_querycap)
break;
cap->version = LINUX_VERSION_CODE;
/**
看这里,根据不同的cmd,这里调用到了ioctl_ops中定义的vidioc_querycap
*/
ret = ops->vidioc_querycap(file, fh, cap);
...
break;
}
}
在入口函数vivi_init()中,
1 : video_device被设置成了vivi_template
2 :vdev->cdev->ops = &v4l2_fops;
3 : video_device[vdev->minor] = vdev;
当用户空间调用ioctl的时候:
1 : v4l2_fops中的v4l2_ioctl会被调用
在v4l2_ioctl()中
vivi_template->fops->unlocked_ioctl(filp, cmd, arg);
即 video_ioctl2( )函数会被调用
在video_ioctl2( )中,__
video_do_ioctl( )函数会被调用
在video_do_ioctl()函数中,
根据cmd来调用vivi_template中vivi_ioctl_ops结构体其中对应的函数