V4L2引入(含浅析UVC)
Posted zongzi10010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了 V4L2引入(含浅析UVC)相关的知识,希望对你有一定的参考价值。
title: V4L2引入(含浅析UVC)
date: 2019/4/23 19:00:00
toc: true
---
V4L2引入(含浅析UVC)
基本框架
V4L2
全名是video for linux 2
之前还有个老版本v4l
,也就是video for linux 1.0版本
V4L2
不仅仅用于摄像头,也用于视频输出接口,收音机接口等,完整的框架可以参考这里
基本框架图如下:摘录自 Linux摄像头驱动1——vivid
代码入手
我们插入USB,使用dmesg
查看usb的输出信息
uvcvideo: Found UVC 1.00 device USB 2.0 Camera (05a3:9310)
搜索内核源码可以找到相关函数
cd linux-3.4.2/
cd drivers/
$ grep "Found UVC" * -nR
media/video/uvc/uvc_driver.c:1848: uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s
这里就涉及到UVC
这个名词了,所谓的UVC也就是usb video class
类的驱动,也就是USB硬件相关的驱动,也就是应该会向上注册这个驱动程序
UVC流程简述
这里简单分析下UVC驱动的流程,详细分析到USB摄像头那里,对于UVC
驱动也是一个驱动,从入口函数分析下
static int __init uvc_init(void)
{
int ret;
uvc_debugfs_init();
ret = usb_register(&uvc_driver.driver);
if (ret < 0) {
uvc_debugfs_cleanup();
return ret;
}
printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
return 0;
}
注册了一个结构体,看下这个结构体的成员
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,
},
};
按照以往的经验,应该就是先匹配id_table
,然后执行probe
来初始化,查看下这个probe
,深入分析最后是调用cdev
注册了一个v4l2_fops
的file_operation
,也就是说最终app
的操作函数就是这个啦.
v4l2_fops > 具体的驱动
简单流程一览,抓住关键注册函数是uvc_register_chains
,而v4l2_device_register
并不重要在整体上
// drivers\media\video\uvc\uvc_driver.c
uvc_probe
v4l2_device_register //这个不重要,只是进行一些初始化
..
uvc_register_chains //这里才是实际的注册v4l2管理结构
>uvc_register_terms
>uvc_register_video(dev, stream)
>video_device_alloc //分配
>vdev->v4l2_dev = &dev->vdev;
---- >vdev->fops = &uvc_fops; //这个是最终的读写函数
>vdev->release = uvc_release;
>video_set_drvdata(vdev, stream); // 设置
>video_register_device(vdev, VFL_TYPE_GRABBER, -1) //注册
// \include\media\v4l2-dev.h
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);
}
----这个实际的 __video_register_device 注册函数是在 drivers\media\video\v4l2-dev.c 也就是它为下层提供注册接口的
__video_register_device
>case VFL_TYPE_GRABBER:
name_base = "video";
> 获得一个空的次设备号
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL) break;
> 这里就有字符设备驱动的了
>vdev->cdev = cdev_alloc();
>vdev->cdev->ops = &v4l2_fops; //这个也就是具体的 /dev/video的fileoperation
>vdev->cdev->owner = owner;
>ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
调用流程
app
的open,read,write
最终就会调用到vdev->cdev->ops=v4l2_fops
// \drivers\media\video\v4l2-dev.c
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,
};
open
vdev = video_devdata(filp); //return video_device[iminor(file->f_path.dentry->d_inode)];
>vdev->fops->open(filp)
read
这里调用的最终就是vdev->fops->read
video_device *vdev = video_devdata(filp);
> return video_device[iminor(file->f_path.dentry->d_inode)];
vdev->fops->read(filp, buf, sz, off)
这个是在uvc_register_video
中设置的
vdev->fops = &uvc_fops;
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
};
v4l2_ioctl
也就是调用了`uvc_v4l2_ioctl>uvc_v4l2_do_ioctl
struct video_device *vdev = video_devdata(filp);
vdev->fops->unlocked_ioctl(filp, cmd, arg)
videodev_init(字符设备驱动注册)
这个是v4l2-dev.c
的入口,这里就是常规的字符设备驱动,这里使用了主设备号81
static int __init videodev_init(void)
{
dev_t dev = MKDEV(VIDEO_MAJOR, 0);
int ret;
printk(KERN_INFO "Linux video capture interface: v2.00\n");
ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
if (ret < 0) {
printk(KERN_WARNING "videodev: unable to get major %d\n",
VIDEO_MAJOR);
return ret;
}
ret = class_register(&video_class);
if (ret < 0) {
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
printk(KERN_WARNING "video_dev: class_register failed\n");
return -EIO;
}
return 0;
}
以上是关于 V4L2引入(含浅析UVC)的主要内容,如果未能解决你的问题,请参考以下文章