linux应用项目(二)摄像头(1)V4L2框架分析

来源:互联网 发布:jenkins php 自动部署 编辑:程序博客网 时间:2024/06/01 08:46

1、回顾字符设备驱动程序基本框架

1.1、简单的字符设备驱动程序

(1)file_operations

(2)register_chrdev(major,name,file_operations)

(3)module_init

(4)module_exit

(5)MODULE_LICENSE("GPL");

新:

(1)分配cdev结构体

(2)设置cdev结构体

(2)cdev_add


1.2、复杂的字符设备驱动程序(分层)

内核中:fbmen.c(核心层,内核提供)

(1)file_operations

(2)register_chrdev(major,name,file_operations)

(3)module_init

(4)module_exit

(5)MODULE_LICENSE("GPL");

硬件相关:

(1)分配一个fb_info结构体

(2)设置fb_info结构体

(3)注册fb_info

(4)硬件相关操作

注册就是把结构体fb_info告诉fbmen.c,应用程序open、read、write的时候,首先调用fbmen的open、read、write。这个ope、nread、write里面调用到注册的fb_info里面提供的函数,或者根据这个结构体里面来操作硬件。分层的时候我只需要关注于硬件相关层的内容。




对于分层的字符设备驱动程序怎么写:

内核提供了上层的程序,我们只需要

(1)    分配某个结构体

(2)    设置这个结构体

(3)    注册这个结构体

(4)    硬件相关操作

结构体具体驱动具体分析,对于lcd是fb_info

2、V4L2框架分析: video for linux version 2

猜测:

也分为至少两层

核心层一样,系统写好的,有一个file_operation结构体,和提供给下层的注册方法。


搜索:grep"Found UVC" * -nR

drivers/media/video/uvc/uvc_driver.c:

media/video/uvc/uvc_driver.c:1848:      uvc_printk(KERN_INFO, "Found UVC%u.%02x device %s (%04x:%04x)\n"

2.1、uvc_driver.c代码分析

(1)对于USB类驱动程序,会构造一个usb_driver结构体

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,},};

结构体里有一个uvc_probe函数,当发现uvc_ids支持的设备时候时会调用uvc_probe函数,在uvc_probe里面一定有分配注册某个结构体。

看看Probe函数

uvc_probe{v4l2_device_registeruvc_register_chains{uvc_register_terms{uvc_register_video{struct video_device *vdev;vdev = video_device_alloc();vdev->fops = &uvc_fops;//核心层提供的函数V4l2-dev.c (linux-3.4.2\drivers\media\video):ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); }{vdev->cdev = cdev_alloc();vdev->cdev->ops = &v4l2_fops;cdev_add}}}}

应用程序openread 最终调用驱动的open read(v4l2里面file_operation)file_operation里面的函数会调用到videv——device里面的方法和属性

2.2、分析Vivi.c虚拟视频驱动-硬件驱动部分 (linux-3.4.2\drivers\media\video)

1.分配video_device
2.设置
3.注册:video_register_device(注册的思路就是将结构体存入数组,引用程序open的时候从数组中以次设备号为索引取出,并调用里面的open函数)

(1)分析Vivi.c代码

vivi_init{vivi_create_instance{ // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数v4l2_device_register video_device_alloc//分配*vfd = vivi_template;//设置{static struct video_device vivi_template = {.name= "vivi",.fops           = &vivi_fops,.ioctl_ops = &vivi_ioctl_ops,.release= video_device_release,.tvnorms              = V4L2_STD_525_60,.current_norm         = V4L2_STD_NTSC_M,};}video_register_device//注册{__video_register_device{switch (type) {case VFL_TYPE_GRABBER:name_base = "video";break;case VFL_TYPE_VBI:name_base = "vbi";break;case VFL_TYPE_RADIO:name_base = "radio";break;case VFL_TYPE_SUBDEV:name_base = "v4l-subdev";vdev->cdev = cdev_alloc();vdev->cdev->ops = &v4l2_fops;cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);video_device[vdev->minor] = vdev;}}}}

注册的结果是更新数组,将 video_device   放入数组中video_device[vdev->minor] = vdev; 以此设备号为下标存起来。


(2)分析vivi.c的open,read,write,ioctl过程

1. open
app:     open("/dev/video0",....)
---------------------------------------------------
drv:     v4l2_fops.v4l2_open
            vdev = video_devdata(filp);   // 根据次设备号从数组video_device中得到video_device
            ret = vdev->fops->open(filp);
                        vivi_ioctl_ops.open
                            v4l2_fh_open

struct video_device *video_devdata(struct file *file)
{
return video_device[iminor(file->f_path.dentry->d_inode)];
}

2. read
app:    read ....
---------------------------------------------------
drv:    v4l2_fops.v4l2_read
            struct video_device *vdev = video_devdata(filp);
            ret = vdev->fops->read(filp, buf, sz, off);


3. ioctl
app:   ioctl
----------------------------------------------------
drv:   v4l2_fops.unlocked_ioctl
            v4l2_ioctl
                struct video_device *vdev = video_devdata(filp);
                ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
                            video_ioctl2
                                video_usercopy(file, cmd, arg, __video_do_ioctl);
                                    __video_do_ioctl
                                        struct video_device *vfd = video_devdata(file);
                                        根据APP传入的cmd来获得、设置"某些属性"

(3)怎么写V4L2驱动


2.3、xawtv源码分析(应用程序如和调用驱动,实现视频播放)

(1)xawtv的几大函数:
1. v4l2_open
2. v4l2_read_attr/v4l2_write_attr
3. v4l2_start_streaming
4. v4l2_nextframe/v4l2_waiton

(2)根据虚拟驱动vivi的使用过程彻底分析摄像头驱动
// 1~7都是在v4l2_open里调用
1. open
2. ioctl(4, VIDIOC_QUERYCAP


// 3~7 都是在get_device_capabilities里调用
3. for()
        ioctl(4, VIDIOC_ENUMINPUT   // 列举输入源,VIDIOC_ENUMINPUT/VIDIOC_G_INPUT/VIDIOC_S_INPUT不是必需的
4. for()
        ioctl(4, VIDIOC_ENUMSTD  // 列举标准(制式), 不是必需的
5. for()        
        ioctl(4, VIDIOC_ENUM_FMT // 列举格式


6. ioctl(4, VIDIOC_G_PARM
7. for()
        ioctl(4, VIDIOC_QUERYCTRL    // 查询属性(比如说亮度值最小值、最大值、默认值)


// 8~10都是通过v4l2_read_attr来调用的        
8. ioctl(4, VIDIOC_G_STD            // 获得当前使用的标准(制式), 不是必需的
9. ioctl(4, VIDIOC_G_INPUT 
10. ioctl(4, VIDIOC_G_CTRL           // 获得当前属性, 比如亮度是多少


11. ioctl(4, VIDIOC_TRY_FMT          // 试试能否支持某种格式
12. ioctl(4, VIDIOC_S_FMT            // 设置摄像头使用某种格式




// 13~16在v4l2_start_streaming
13. ioctl(4, VIDIOC_REQBUFS          // 请求系统分配缓冲区
14. for()
        ioctl(4, VIDIOC_QUERYBUF         // 查询所分配的缓冲区
        mmap        
15. for ()
        ioctl(4, VIDIOC_QBUF             // 把缓冲区放入队列        
16. ioctl(4, VIDIOC_STREAMON             // 启动摄像头




// 17里都是通过v4l2_write_attr来调用的
17. for ()
        ioctl(4, VIDIOC_S_CTRL           // 设置属性
    ioctl(4, VIDIOC_S_INPUT              // 设置输入源
    ioctl(4, VIDIOC_S_STD                // 设置标准(制式), 不是必需的


// v4l2_nextframe > v4l2_waiton    
18. v4l2_queue_all
    v4l2_waiton    
        for ()
        {
            select(5, [4], NULL, NULL, {5, 0})      = 1 (in [4], left {4, 985979})
            ioctl(4, VIDIOC_DQBUF                // de-queue, 把缓冲区从队列中取出
            // 处理, 之以已经通过mmap获得了缓冲区的地址, 就可以直接访问数据        
            ioctl(4, VIDIOC_QBUF                 // 把缓冲区放入队列
        }


(3)摄像头驱动程序必需的11个ioctl:(vivi.c提供iocrl函数,会被应用程序所调用)

    // 表示它是一个摄像头设备
.vidioc_querycap      = vidioc_querycap,


    /* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,


    /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs       = vidioc_reqbufs,
.vidioc_querybuf      = vidioc_querybuf,
.vidioc_qbuf          = vidioc_qbuf,
.vidioc_dqbuf         = vidioc_dqbuf,


// 启动/停止
.vidioc_streamon      = vidioc_streamon,
.vidioc_streamoff     = vidioc_streamoff,

未完待续。。。。。。。



阅读全文
2 0
原创粉丝点击