Linux V4L2子系统-videobuf2框架分析(三)-my write

1.概述

Linux系统上的Video设备多种多样,如通过Camera Host控制器接口连接的摄像头,通过USB总线连接的摄像头等。为了兼容更多的硬件,Linux内核抽象了V4L2(Video for Linux Two)子系统。V4L2子系统是Linux内核中关于Video(视频)设备的API接口,是V4L(Video for Linux)子系统的升级版本。V4L2子系统向上为虚拟文件系统提供了统一的接口,应用程序可通过虚拟文件系统访问Video设备。V4L2子系统向下给Video设备提供接口,同时管理所有Video设备。Video设备又分为主设备和从设备,对于Camera来说,Camera Host控制器为主设备,负责图像数据的接收和传输,从设备为Camera Sensor,一般为I2C接口,可通过从设备控制Camera采集图像的行为,如图像的大小、图像的FPS等。主设备可通过v4l2_subdev_call的宏调用从设备提供的方法,反过来从设备可以调用主设备的notify方法通知主设备某些事件发生了。

V4L2框架

2.V4L2子系统

V4L(Video for Linux)是Linux内核中关于视频设备的API接口,涉及视频设备的音频和视频信息采集及处理、视频设备的控制。V4L出现于Linux内核2.1版本,经过修改bug和添加功能,Linux内核2.5版本推出了V4L2(Video for Linux Two)子系统,功能更多且更稳定。V4L2的主设备号是81,次设备号范围0~255,这些次设备号又分为多类设备,如视频设备(次设备号范围0-63)、Radio(收音机)设备(次设备号范围64-127)、Teletext设备(次设备号范围192-223)、VBI设备(次设备号范围224-255)。V4L2设备对应的设备节点有/dev/videoX、/dev/vbiX、/dev/radioX。这里只讨论视频设备,视频设备对应的设备节点是/dev/videoX。视频设备以高频头或Camera为输入源,Linux内核驱动该类设备,接收相应的视频信息并处理。

2.1.V4L2主设备数据结构

V4L2主设备实例使用struct v4l2_device结构体表示,v4l2_device是V4L2子系统的入口,管理着V4L2子系统的主设备和从设备。简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体嵌入到一个更大的结构体中。需要与媒体框架整合的驱动必须手动设置dev->driver_data,指向包含v4l2_device结构体实例的驱动特定设备结构体。这可以在注册V4L2设备实例前通过dev_set_drvdata()函数完成。同时必须设置v4l2_device结构体的mdev域,指向适当的初始化并注册过的media_device实例。
对于视频设备,Camera控制器可以视为主设备,接在Camera控制器上的摄像头可以视为从设备。V4L2子系统使用v4l2_device结构体管理设备,设备的具体操作方法根据设备类型决定,若是视频设备,则需要注册video_device结构体,并提供相应的操作方法。

    [include/media/v4l2-device.h]
    struct v4l2_device {
        struct device *dev;  // 父设备指针
    #if defined(CONFIG_MEDIA_CONTROLLER)  // 多媒体设备配置选项
        // 用于运行时数据流的管理,
        struct media_device *mdev;
    #endif
        // 注册的子设备的v4l2_subdev结构体都挂载此链表中
        struct list_head subdevs;
        // 同步用的自旋锁
        spinlock_t lock;
        // 独一无二的设备名称,默认使用driver name + bus ID
        char name[V4L2_DEVICE_NAME_SIZE];
        // 被一些子设备回调的通知函数,但这个设置与子设备相关。子设备支持的任何通知必须在
        // include/media/<subdevice>.h 中定义一个消息头。
        void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
        // 提供子设备(主要是video和ISP设备)在用户空间的特效操作接口,
        // 比如改变输出图像的亮度、对比度、饱和度等等
        struct v4l2_ctrl_handler *ctrl_handler;
        // 设备优先级状态
        struct v4l2_prio_state prio;
        /* BKL replacement mutex. Temporary solution only. */
        struct mutex ioctl_lock;
        // struct v4l2_device结构体的引用计数,等于0时才释放
        struct kref ref;
        // 引用计数ref为0时,调用release函数进行释放资源和清理工作
        void (*release)(struct v4l2_device *v4l2_dev);
    };

  • 1

使用v4l2_device_register注册v4l2_device结构体.如果v4l2_dev->name为空,则它将被设置为从dev中衍生出的值(为了更加精确,形式为驱动名后跟bus_id)。如果在调用v4l2_device_register前已经设置好了,则不会被修改。如果dev为NULL,则必须在调用v4l2_device_register前设置v4l2_dev->name。可以基于驱动名和驱动的全局atomic_t类型的实例编号,通过v4l2_device_set_name()设置name。这样会生成类似ivtv0ivtv1等名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如:cx18-0cx18-1等。dev参数通常是一个指向pci_devusb_interfaceplatform_device的指针,很少使其为NULL,除非是一个ISA设备或者当一个设备创建了多个PCI设备,使得v4l2_dev无法与一个特定的父设备关联。
使用v4l2_device_unregister卸载v4l2_device结构体。如果dev->driver_data域指向 v4l2_dev,将会被重置为NULL。主设备注销的同时也会自动注销所有子设备。如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。由于v4l2_device有一个指向父设备的指针必须被清除,同时标志父设备
已消失,所以必须调用v4l2_device_disconnect函数清理v4l2_device中指向父设备的dev指针。v4l2_device_disconnect并不注销主设备,因此依然要调用v4l2_device_unregister函数注销主设备。

    [include/media/v4l2-device.h]
    // 注册v4l2_device结构体,并初始化v4l2_device结构体
    // dev-父设备结构体指针,若为NULL,在注册之前设备名称name必须被设置,
    // v4l2_dev-v4l2_device结构体指针
    // 返回值-0成功,小于0-失败
    int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)

    // 卸载注册的v4l2_device结构体
    // v4l2_dev-v4l2_device结构体指针
    void v4l2_device_unregister(struct v4l2_device *v4l2_dev)

    // 设置设备名称,填充v4l2_device结构体中的name成员
    // v4l2_dev-v4l2_device结构体指针
    // basename-设备名称基本字符串
    // instance-设备计数,调用v4l2_device_set_name后会自加1
    // 返回值-返回设备计数自加1的值
    int v4l2_device_set_name(struct v4l2_device *v4l2_dev, 
            const char *basename, atomic_t *instance)

    // 热插拔设备断开时调用此函数
    // v4l2_dev-v4l2_device结构体指针
    void v4l2_device_disconnect(struct v4l2_device *v4l2_dev);

同一个硬件的情况下。如ivtvfb驱动是一个使用ivtv硬件的帧缓冲驱动,同时alsa驱动也使用此硬件。可以使用如下例程遍历所有注册的设备:

    static int callback(struct device *dev, void *p)
    {
        struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);

        /* 测试这个设备是否已经初始化 */
        if (v4l2_dev == NULL)
            return 0;
        ...
        return 0;
    }

    int iterate(void *p)
    {
        struct device_driver *drv;
        int err;

        /* 在PCI 总线上查找ivtv驱动。
        pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */
        drv = driver_find("ivtv", &pci_bus_type);
        /* 遍历所有的ivtv设备实例 */
        err = driver_for_each_device(drv, NULL, p, callback);
        put_dri
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小硕算法工程师

你的鼓励将是我创作的最大动力哈

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值