zoukankan      html  css  js  c++  java
  • UVC 驱动调用过程与驱动框架的简单分析

    UVC 驱动整体调用流程:

    /* 打开设备描述符 */
    1. open:
            uvc_v4l2_open
    
    /* 查询设备属性 */
    2. VIDIOC_QUERYCAP
            if (video->streaming->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;
    
    /* 枚举设备支持的格式 */
    3. VIDIOC_ENUM_FMT
            format = &video->streaming->format[fmt->index];
    
    /* 得到设备当前所使用的 format 与 frame */
    4. VIDIOC_G_FMT
            uvc_v4l2_get_format
                struct uvc_format *format = video->streaming->cur_format;
                struct uvc_frame *frame = video->streaming->cur_frame;
    
    /* Check if the hardware supports the requested format. */
    5. VIDIOC_TRY_FMT
            uvc_v4l2_try_format
                 
    /* 设置数据,此时并没有真正的设置,而是在启动视频流时将数据发送给设备 */
    6. VIDIOC_S_FMT
            uvc_v4l2_set_format
                uvc_v4l2_try_format
                video->streaming->cur_format = format;
                video->streaming->cur_frame = frame;
    
    /* 分配视频缓冲区 */
    7. VIDIOC_REQBUFS
            uvc_alloc_buffers
                for (; nbuffers > 0; --nbuffers) {
                    mem = vmalloc_32(nbuffers * bufsize);
                    if (mem != NULL)
                        break;
                }
    
    /* 查询并获取到分配的缓冲区信息 */
    8. VIDIOC_QUERYBUF
            uvc_query_buffer
                __uvc_query_buffer
    
    /* 映射缓冲区地址到用户空间 */
    9. mmap
            uvc_v4l2_mmap
    
    /* 将 V4L2 信息块放入队列 */
    10. VIDIOC_QBUF
            uvc_queue_buffer
                list_add_tail(&buf->queue, &queue->irqqueue);
                list_add_tail(&buf->stream, &queue->mainqueue);
    
    /* 设置设备并启动视频流 */
    11. VIDIOC_STREAMON
            uvc_video_enable(video, 1)
                /* Commit the streaming parameters. */
                uvc_commit_video
                    /* 设置 format, frame */
                    uvc_set_video_ctrl  
                        
                /* 启动:Initialize isochronous/bulk URBs and allocate transfer buffers. */
                uvc_init_video(video, GFP_KERNEL);
                        uvc_init_video_isoc / uvc_init_video_bulk
                            
                        usb_submit_urb    
    
    /* 休眠等待数据 */
    12. poll
            uvc_v4l2_poll            
                uvc_queue_poll
                    poll_wait(file, &buf->wait, wait);
    
    /* 取出信息块 */
    13. VIDIOC_DQBUF
            uvc_dequeue_buffer
                list_del(&buf->stream);
    
    /* 关闭视频流 */
    14. VIDIOC_STREAMOFF            
            uvc_video_enable(video, 0);
                usb_kill_urb(urb);
                usb_free_urb(urb);
    驱动分析,首先找到 UVC 驱动的入口点:
    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,
        },
    };
    
    usb_register(&uvc_driver.driver);

    注册了 usb 驱动,如果系统中出现了与其 id_table 匹配的设备,则驱动会与它建立关系并调用 probe 函数:

    我们选择比较重要的函数进行分析,在 probe 中的函数调用:

    uvc_register_chains -> uvc_register_terms -> uvc_register_video(对类型为 UVC_TT_STREAMING 的video调用本函数)

    uvc_register_video 函数如下:

    static int uvc_register_video(struct uvc_device *dev,
            struct uvc_streaming *stream)
    {
        struct video_device *vdev;
        int ret;
    
        /* 对 video 做一些初始化 */
        ret = uvc_video_init(stream);
        if (ret < 0) {
            uvc_printk(KERN_ERR, "Failed to initialize the device "
                "(%d).
    ", ret);
            return ret;
        }
    
        uvc_debugfs_init_stream(stream);
    
        /* 分配一个 video device */
        vdev = video_device_alloc();
        if (vdev == NULL) {
            uvc_printk(KERN_ERR, "Failed to allocate video device (%d).
    ",
                   ret);
            return -ENOMEM;
        }
    
        /* 配置 video device */
        vdev->v4l2_dev = &dev->vdev;
        vdev->fops = &uvc_fops;
        vdev->release = uvc_release;
        strlcpy(vdev->name, dev->name, sizeof vdev->name);
    
        stream->vdev = vdev;
        video_set_drvdata(vdev, stream);
    
        /* 注册 video device */
        ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
        if (ret < 0) {
            uvc_printk(KERN_ERR, "Failed to register video device (%d).
    ",
                   ret);
            stream->vdev = NULL;
            video_device_release(vdev);
            return ret;
        }
    
        atomic_inc(&dev->nstreams);
        return 0;
    }

    在这里面有个重要的结构,即:

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

    uvc_v4l2_ioctl 就是实现 V4L2 操作的函数集。

    这个 unlocked_ioctl 属性的赋值需要注意一下,当它为 uvc_v4l2_ioctl(当前内核所使用)时,调用的是 uvc_v4l2.c 中现有的函数; 但如果将它赋值为 video_ioctl2 时,内核将调用我们在驱动中 vdev 的 ioctl_ops 属性所赋给的函数集进行操作。

    __video_do_ioctl 中:
        const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; 之后就调用 ops 中的一系列函数来操作 video

    所以这一系列的函数可以由我们自己来编写。

    转:https://www.cnblogs.com/GyForever1004/p/8647543.html
  • 相关阅读:
    移植Fatfs文件系统到工程中
    利用官方的ucosiii包中测试板的工程移植到属于自己的开发板(stmf103ZE)上
    华为Liteos移植到stm32F03ZE
    如何使用postman模拟https的post和get请求
    ubuntu 16 搭建只能上传不可下载删除ftp服务
    使用STM32F103ZET霸道主板实现LCD显示屏显示
    资源任务调度算法实现(大数据云计算作业来的)
    STM32F10xx(高容量)WiFi模块的初始化和使用
    使用STM32F103ZET霸道主板实现SD卡的读写(非文件系统)
    单片机期末实训---- 密码锁和交通灯
  • 原文地址:https://www.cnblogs.com/Lxk0825/p/10326740.html
Copyright © 2011-2022 走看看