源码欣赏
在前几篇博文中我们知道:
用户层调用ioctl(),经过v4l2_ioctl —->video_ioctl2——>__video_do_ioctl()。
__video_do_ioctl这个函数会根据cmd的不同来调用struct v4l2_ioctl_ops结构中定义的不同函数。
我们现在来讨论cmd为VIDIOC_QUERYCAP命令的过程。
static long __video_do_ioctl(struct file *file,unsigned int cmd, void *arg)
{
/*
根据次设备号为索引值从video_device[]中获取对应的video_device。
1 : 那么 是在什么时候 填充上的呢?
这一点,像极了fb_info、fb_info[].套路都是一样的,都是在注册的时候添加到对应的数组中的.然后再其他函数中 在从该数组中 取出来.
详细对比 请见我以前博文的分析
http://blog.csdn.net/leesagacious/article/details/49851555
static inline int __must_check video_register_device(struct video_device *vdev,
int type, int nr)
{
return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
{
....
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
....
vdev->minor = i + minor_offset;
....
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
}
}
*/
struct video_device *vfd = video_devdata(file);
/**
根据上面获取的vfd,获取v4l2_ioctl_ops
下面的代码会根据cmd的不同来调用这个v4l2_ioctl_ops结构中定义的函数
*/
const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
/**
从打开文件的私有数据中获取到vivi_fh
那么,vivi_fh是在什么时候被赋值给file->private_data的呢?
这个赋值是发生在具体设备驱动的open函数中的,如vivi_open()
static int vivi_open(struct file *file)
{
struct vivi_fh *fh = NULL;
fh = kzalloc(sizeof(*fh), GFP_KERNEL); 这里其实已经为videobuf_queue分配好了空间
....
file->private_data = fh; //将vivi_fh赋值给file->private_data
....
}
*/
void *fh = file->private_data;
.....
/**
查看这个video_device是否提供了v4l2_ioctl_ops,否则出错返回
*/
if (ops == NULL) {
printk(KERN_WARNING "videodev: \"%s\" has no ioctl_ops.\n",
vfd->name);
return ret;
}
....
switch(cmd) //根据cmd来调用不同的函数
{
case VIDIOC_QUERYCAP:
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
//如果具体设备的file_operations并没有定义vidioc_querycap这个函数
if (!ops->vidioc_querycap)
{
break;
}
/**
调用vidioc_querycap
参数 :
fh : vivi_fh 在vivi_open()中分配,并放入file->private_data
cap : 用户空间传入的struct v4l2_capability 指针
*/
ret = ops->vidioc_querycap(file, fh, cap);
}
}
下面看看被调用的vidioc_querycap()函数
/**
查询设备的性能。
参数 :
v4l2_capability : v4l2的能力描述符
返回值:
成功 返回 0 。cap变量中包含了该视频设备所支持的能力信息
如 : V4L2_CAP_VIDEO_CAPTURE 、V4L2_CAP_STREAMING....
失败 返回 -1
应用程序往往打开设备后就执行这个ioctl
用户空间:
用户层调用 ioctl(fd,VIDIOC_QUERYCAP,CAP),这个函数会被执行。
当执行完成这个命令后,CAP变量中包含了该设备的能力信息。
代码:
#define ERR_EXIT(m)\
do{
perror(m);
exit(EXIT_FAILURE);\
}while(0)
struct v4l2_capability cap;
ret = ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(ret == -1)
{
ERR_EXIT(ioctl);
}
*/
/*
应用程序用这个命令,用来判断它是否是一个视频捕获设备
*/
static int vidioc_querycap(struct file * file,void *priv,struct v4l2_capability *cap)
{
/**
先来看看这个v4l2_capability
struct v4l2_capability {
__u8 driver[16]; //驱动名字
__u8 card[32]; //设备名字
__u8 bus_info[32]; //设备在系统中的位置 Location of the device in the system
__u32 version; //驱动的版本号
__u32 capabilities; //设备能力集
__u32 reserved[4]; //保留字段,驱动必须要将这个数组设置为0
};
*/
struct vivi_fh *fh = priv;
//从vivi_fh中获取vivi_dev
struct vivi_dev *dev = fh->dev;
strcpy(cap->driver,"vivi"); //驱动的名字
strcpy(cap->card,"vivi"); //设备的名字
//设备在系统中的位置
strlcpy(cap->bus_info,dev->v4l2_dev.name,sizeof(cap->bus_info));
cap->version = VIVI_VERSION; //版本号
//能力集合
//V4L2_CAP_VIDEO_CAPTURE : 是一个视频捕捉设备
//V4L2_CAP_STREAMING :支持ioctl系统调用来获取数据
//V4L2_CAP_READWRITE :支持read/write系统调用来获取数据
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
return 0;
}
下面是 源码中调用的过程。
这个函数 就是 要调用 vdev->fops->unlocked_iotcl().
uvc :
uvc_init()
{
ret = usb_register(&uvc_driver.driver);
}
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
//probe被调用
static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
{
if (uvc_register_chains(dev) < 0){
goto error;
}
}
static int uvc_register_chains(struct uvc_device *dev)
{
list_for_each_entry(chain, &dev->chains, list) {
ret = uvc_register_terms(dev, chain);
if (ret < 0){
return ret;
}
}
}
static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain)
{
list_for_each_entry(term, &chain->entities, chain) {
ret = uvc_register_video(dev, stream);
}
}
static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream)
{
...
vdev = video_device_alloc();
vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops; //看 这个uvc_fops中定义的函数会被调用到
vdev->release = uvc_release;
strlcpy(vdev->name, dev->name, sizeof vdev->name);
..
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
}
const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.unlocked_ioctl = uvc_v4l2_ioctl, //这个函数。
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
#ifndef CONFIG_MMU
.get_unmapped_area = uvc_v4l2_get_unmapped_area,
#endif
};
那么,这个函数终于被调用到了
static long uvc_v4l2_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
{
if (uvc_trace_param & UVC_TRACE_IOCTL) {
uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
v4l_printk_ioctl(cmd);
printk(")\n");
}
//这个函数会被调用
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,v4l2_kioctl func)
{
...
...
err = func(file, cmd, parg);
...
}
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
...
switch (cmd) {
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = arg;
/**
填充这个结构体
*/
memset(cap, 0, sizeof *cap);
strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
strlcpy(cap->card, vdev->name, sizeof cap->card);
usb_make_path(stream->dev->udev,cap->bus_info, sizeof(cap->bus_info));
cap->version = LINUX_VERSION_CODE;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE){
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE| V4L2_CAP_STREAMING;
}
else{
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT| V4L2_CAP_STREAMING;
}
break;
}
case ...
case ...
}
}
- 实验验证