当前位置: 首页 > news >正文

v4l2 subdev 设备节点和属性创建过程

  • v4l2_subdev(子设备):通常是底层硬件组件(如摄像头传感器、ISP 模块、多路复用器等),负责原始数据采集或硬件处理,通过 v4l2_device_register_subdev 注册,对应 sysfs 中的 v4l-subdevX 节点(如 /sys/class/video4linux/v4l-subdev2)。

v4l2_subdev 内存描述和 实际设备节点的创建,是一件比较困惑的事情,如何创建自定义的v4l2_subdev 属性,来实现对设备的特殊操作,笔者就此,做了一番下面的分析探究。

下面就此分享

1  v4l2_device_register_subdev

不管是v4l2 subdev 的同步创建和异步创建,最终都是调用了v4l2_device_register_subdev这个函数,其中我们重点关注异步创建,因为视频流通路上的前后拓扑节点模块驱动 probe 的先后顺序不可控制,所以异步的注册创建显得尤为普遍。

在v4l2-async.c 文件v4l2_async_match_notify 回调中,notifier 实现对subdev描述的注册。

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,

                struct v4l2_subdev *sd)

{

#if defined(CONFIG_MEDIA_CONTROLLER)

    struct media_entity *entity = &sd->entity;

#endif

    int err;

    /* Check for valid input */

    if (v4l2_dev == NULL || sd == NULL || !sd->name[0])

        return -EINVAL;

    /* Warn if we apparently re-register a subdev */

    WARN_ON(sd->v4l2_dev != NULL);

    /*

     * The reason to acquire the module here is to avoid unloading

     * a module of sub-device which is registered to a media

     * device. To make it possible to unload modules for media

     * devices that also register sub-devices, do not

     * try_module_get() such sub-device owners.

     */

    sd->owner_v4l2_dev = v4l2_dev->dev && v4l2_dev->dev->driver &&

        sd->owner == v4l2_dev->dev->driver->owner;

    if (!sd->owner_v4l2_dev && !try_module_get(sd->owner))

        return -ENODEV;

    sd->v4l2_dev = v4l2_dev;

    if (sd->internal_ops && sd->internal_ops->registered) {

        err = sd->internal_ops->registered(sd);

        if (err)

            goto error_module;

    }

    /* This just returns 0 if either of the two args is NULL */

    err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL);

    if (err)

        goto error_unregister;

#if defined(CONFIG_MEDIA_CONTROLLER)

    /* Register the entity. */

    if (v4l2_dev->mdev) {

        err = media_device_register_entity(v4l2_dev->mdev, entity);

        if (err < 0)

            goto error_unregister;

    }

#endif

    spin_lock(&v4l2_dev->lock);

    list_add_tail(&sd->list, &v4l2_dev->subdevs);

    spin_unlock(&v4l2_dev->lock);

    return 0;

error_unregister:

    if (sd->internal_ops && sd->internal_ops->unregistered)

        sd->internal_ops->unregistered(sd);

error_module:

    if (!sd->owner_v4l2_dev)

        module_put(sd->owner);

    sd->v4l2_dev = NULL;

    return err;

}

EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);

v4l2_device_register_subdev 首先判定 owner_v4l2_dev

如果owner_v4l2_dev不为真,通过try_module_get 加载驱动

绑定从属的v4l2_dev

如果sd->internal_ops和 sd->internal_ops->registered 设置,调用这个回调

通过v4l2_ctrl_add_handler  添加subdev 的控制操作集

media 框架 注册这个entity

添加到从属v4l2_dev的 subdevs链表

从上可以看出,v4l2_device_register_subdev 这个函数,只是实现一些内存中具体描述,不实现设备节点的创建。

2   实际设备节点和/sys 设备树中节点属性的创建

1)v4l2_device_register_subdev_nodes

v4l2_device_register_subdev_nodes 这个函数,其中的sd flag中如果配置了V4L2_SUBDEV_FL_HAS_DEVNODE,那么 就分配一个vdev,指向传入的v4l2_dev,v4l2的操作集v4l2_subdev_fops,系统默认的v4l2_device_release_subdev_node, 通过__video_register_device 来具体创建之。

2) __video_register_device

 __video_register_device 在v4l2-dev.c,实现了实际的设备节点的创建和sys 节点和属性的初始化,是一个完整综合的过程。

int __video_register_device(struct video_device *vdev, int type, int nr,

        int warn_if_nr_in_use, struct module *owner)

{

    int i = 0;

    int ret;

    int minor_offset = 0;

    int minor_cnt = VIDEO_NUM_DEVICES;

    const char *name_base;

    /* A minor value of -1 marks this video device as never

       having been registered */

    vdev->minor = -1;

    /* the release callback MUST be present */

    if (WARN_ON(!vdev->release))

        return -EINVAL;

    /* the v4l2_dev pointer MUST be present */

    if (WARN_ON(!vdev->v4l2_dev))

        return -EINVAL;

    /* v4l2_fh support */

    spin_lock_init(&vdev->fh_lock);

    INIT_LIST_HEAD(&vdev->fh_list);

    /* Part 1: check device type */

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

        break;

    case VFL_TYPE_SDR:

        /* Use device name 'swradio' because 'sdr' was already taken. */

        name_base = "swradio";

        break;

    default:

        printk(KERN_ERR "%s called with unknown type: %d\n",

               __func__, type);

        return -EINVAL;

    }

    vdev->vfl_type = type;

    vdev->cdev = NULL;

    if (vdev->dev_parent == NULL)

        vdev->dev_parent = vdev->v4l2_dev->dev;

    if (vdev->ctrl_handler == NULL)

        vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

    /* If the prio state pointer is NULL, then use the v4l2_device

       prio state. */

    if (vdev->prio == NULL)

        vdev->prio = &vdev->v4l2_dev->prio;

    /* Part 2: find a free minor, device node number and device index. */

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES

    /* Keep the ranges for the first four types for historical

     * reasons.

     * Newer devices (not yet in place) should use the range

     * of 128-191 and just pick the first free minor there

     * (new style). */

    switch (type) {

    case VFL_TYPE_GRABBER:

        minor_offset = 0;

        minor_cnt = 64;

        break;

    case VFL_TYPE_RADIO:

        minor_offset = 64;

        minor_cnt = 64;

        break;

    case VFL_TYPE_VBI:

        minor_offset = 224;

        minor_cnt = 32;

        break;

    default:

        minor_offset = 128;

        minor_cnt = 64;

        break;

    }

#endif

    /* Pick a device node number */

    mutex_lock(&videodev_lock);

    nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);

    if (nr == minor_cnt)

        nr = devnode_find(vdev, 0, minor_cnt);

    if (nr == minor_cnt) {

        printk(KERN_ERR "could not get a free device node number\n");

        mutex_unlock(&videodev_lock);

        return -ENFILE;

    }

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES

    /* 1-on-1 mapping of device node number to minor number */

    i = nr;

#else

    /* The device node number and minor numbers are independent, so

       we just find the first free minor number. */

    for (i = 0; i < VIDEO_NUM_DEVICES; i++)

        if (video_device[i] == NULL)

            break;

    if (i == VIDEO_NUM_DEVICES) {

        mutex_unlock(&videodev_lock);

        printk(KERN_ERR "could not get a free minor\n");

        return -ENFILE;

    }

#endif

    vdev->minor = i + minor_offset;

    vdev->num = nr;

    devnode_set(vdev);

    /* Should not happen since we thought this minor was free */

    WARN_ON(video_device[vdev->minor] != NULL);

    vdev->index = get_index(vdev);

    video_device[vdev->minor] = vdev;

    mutex_unlock(&videodev_lock);

    if (vdev->ioctl_ops)

        determine_valid_ioctls(vdev);

    /* Part 3: Initialize the character device */

    vdev->cdev = cdev_alloc();

    if (vdev->cdev == NULL) {

        ret = -ENOMEM;

        goto cleanup;

    }

    vdev->cdev->ops = &v4l2_fops;

    vdev->cdev->owner = owner;

    ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

    if (ret < 0) {

        printk(KERN_ERR "%s: cdev_add failed\n", __func__);

        kfree(vdev->cdev);

        vdev->cdev = NULL;

        goto cleanup;

    }

    /* Part 4: register the device with sysfs */

    vdev->dev.class = &video_class;

    vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);

    vdev->dev.parent = vdev->dev_parent;

    dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);

    ret = device_register(&vdev->dev);

    if (ret < 0) {

        printk(KERN_ERR "%s: device_register failed\n", __func__);

        goto cleanup;

    }

    /* Register the release callback that will be called when the last

       reference to the device goes away. */

    vdev->dev.release = v4l2_device_release;

    if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)

        printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,

            name_base, nr, video_device_node_name(vdev));

    /* Increase v4l2_device refcount */

    v4l2_device_get(vdev->v4l2_dev);

#if defined(CONFIG_MEDIA_CONTROLLER)

    /* Part 5: Register the entity. */

    if (vdev->v4l2_dev->mdev &&

        vdev->vfl_type != VFL_TYPE_SUBDEV) {

        vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;

        vdev->entity.name = vdev->name;

        vdev->entity.info.dev.major = VIDEO_MAJOR;

        vdev->entity.info.dev.minor = vdev->minor;

        ret = media_device_register_entity(vdev->v4l2_dev->mdev,

            &vdev->entity);

        if (ret < 0)

            printk(KERN_WARNING

                   "%s: media_device_register_entity failed\n",

                   __func__);

    }

#endif

    /* Part 6: Activate this minor. The char device can now be used. */

    set_bit(V4L2_FL_REGISTERED, &vdev->flags);

    return 0;

cleanup:

    mutex_lock(&videodev_lock);

    if (vdev->cdev)

        cdev_del(vdev->cdev);

    video_device[vdev->minor] = NULL;

    devnode_clear(vdev);

    mutex_unlock(&videodev_lock);

    /* Mark this video device as never having been registered. */

    vdev->minor = -1;

    return ret;

}

EXPORT_SYMBOL(__video_register_device);

整个函数的过程如下

首先判定vdev 这个video_device指针有没有初始化vdev->release和vdev->v4l2_dev,

然后根据type指定name 的命名前缀,其中  我们关注的 VFL_TYPE_GRABBER,VFL_TYPE_SUBDEV 分别对应于输出流的设备节点videoX和前端的subdevY 类型。

初始化没有配置dev_parent,ctrl_handler,prio字段

通过devnode_find查找这个设备号是否已经存在,存在就退出了,不存在继续

从数组 video_device[] 找出一个空指针的元素,指向传入的dev 

通过cdev_alloc 分配字符设备描述符

通过 cdev_add 创建/dev下设备节点

vdev->dev  关联 video_class

通过 device_register 注册创建/sys/class/video4linux/下  节点

配置vdev->dev.release 

通过v4l2_device_get  使设备计数加1

通过 media_device_register_entity  注册有向图的entity项

flag 设置注册标志

退出

从1) 2) 可以看出,具体设备节点的创建,是 通过__video_register_device和其变体函数来实现的。

 

3)v4l2_device_register_subdev_nodes 的被调场景

在瑞芯微平台上,查到有cif 和isp1 这两类调用到了这个函数。

v4l2_device_register_subdev_nodes在isp1 dev.c 中

v4l2_device_register_subdev_nodes  被isp subdev的subdev_notifier_complete 调用,实现在bound 完成时机的 v4l2_dev 设备节点和链路上的subdev 的设备节点和创建。在cif 中也是同样如此。

subdev_notifier_complete  是异步回调,在解析dts endpoint 匹配后实现前后source 和sink pad 的bound

总结

所以v4l2 subdev 设备节点和属性是在被hold的v4l2_dev 的匹配异步操作过程中被创建的,v4l2_device_register_subdev是先期在内存中建立v4l2_subdev具体描述,和 v4l2_dev的从属关系,然后在后级的具体的 v4l2_dev的bound 回调中批量的来创建所有链路上的v4l2_subdev相应的各类文件系统(/dev,/sys)上的设备节点和属性。

所以我们需要创建v4l2_subdev对应设备节点的扩展属性,必须在v4l2_device_register_subdev_nodes  被调用之后,在这个之前都是无效的。

http://www.dtcms.com/a/579270.html

相关文章:

  • (论文速读)基于图像堆栈的低频超宽带SAR叶簇隐蔽目标变化检测
  • 懒人之家网站模板做网站的实践报告
  • 网站建设去哪网站注册商标
  • 垂直越权和水平越权是什么
  • 过关斩将编程题
  • 做自己看视频的网站wordpress网站备份
  • html制作手机网站做网络课堂的平台有哪些网站
  • 进程一、进程基本概念
  • 供求信息网站开发背景计算机软件开发培训机构
  • 如何做照片ppt模板下载网站微信公众平台开发者工具
  • 营销型网站建设细节东莞市建设厅官方网站
  • 网站开发合同技术目标专门做简历的网站软件
  • 4G车联网终端TBOX知识详解
  • 郑州本地做团购的网站wordpress获取自定义文章分类名
  • 自己做的网站别人怎么上网找到合作在惠州做网站
  • 中国建设银行网站密码是什么做淘宝电商比较厉害的网站
  • RFID 技术赋能汽车零件装配线:智能化升级的关键引擎
  • 1、prometheus基础理论
  • 设计社交网站手机可以建网站吗
  • 响应式网站要多久手机站点cn
  • 网站鼠标悬停动态效果古田路9号设计网站
  • 突破 GEO 优化瓶颈:孟庆涛引领的 AI 时代营销新范式
  • 网站模版编辑器企业简介范文
  • 基于Vue的智慧楼宇报修平台设计与实现066z15wb(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • php网站开发套模板步骤张家界城乡建设网站
  • 一元云购网站建设教程wordpress侧边栏插件
  • 惠州城市建设建筑网站cms网站开发网站模板
  • 18年手机网站加强机关网站内容建设
  • 村镇建设年度报表登录网站百度搜索引擎官网
  • 中石化两学一做网站机票网站开发