关闭

video_register_device()浅析

标签: video
1502人阅读 评论(0) 收藏 举报
分类:

这里写图片描述


/**
    注册一个video_device 
    如果注册失败,video_device中的release()函数不会被调用。
    调用者负责释放所有的数据,通常是调用video_device_release()函数来释放
    uvc_driver.c
    static int uvc_register_video()
    {
        struct video_device *vdev;
        ...
        vdev = video_device_alloc();
        vdev->fops = &uvc_fops;
        vdev->release = uvc_release;
        ....
        ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
        if (ret < 0) {
            uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",ret);
            stream->vdev = NULL;
            video_device_release(vdev);
            return ret;
        }
    }
*/
static inline int __must_check video_register_device(struct video_device *vdev)
{
    return __video_register_device(vdev,type,nr,1,vdev->fops->owner);
}
/**
    注册一个video4linux设备
    参数:
        vdev : 要注册的video_device
        type : 设备的类型
        nr   : 一般都选择使用 -1,代表生成设备节点号在原来号上递增。
*/
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; //minor = i + minor_offset
    int minor_cnt = VIDEO_NUM_DEVICES;//用于设备节点序号
    const char * name_base;           //设备的名称会根据传入的type来选择
    /**
        次设备号设置为-1,表示这个video_device还没有被注册过
    */
    vdev->minor = -1;
    /**
        这个video_device 一定要设置release函数,否则要出错返回
    */
    if (WARN_ON(!vdev->release))
    {
        return -EINVAL;
    }

    spin_lock_init(&vdev->fh_lock); //获取自旋锁
    INIT_LIST_HEAD(&vdev->fh_list); //初始化链表头

    /**
        根据传入的类型type,选择设备的名称
        后面会调用下面的这个函数 来设置设备的名称
        dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
    */
    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;
        default:
            printk(KERN_ERR "%s called with unknown type: %d\n",__func__, type);
            return -EINVAL;
    }

    vdev->vfl_type = type;
    vdev->cdev = NULL;
    if (vdev->v4l2_dev) {
        if (vdev->v4l2_dev->dev)
            vdev->parent = vdev->v4l2_dev->dev;
        if (vdev->ctrl_handler == NULL)
            /*
                在注册video_device之前的vivi_create_instance()函数中,
                初始化v4l2_ctrl_handler
                v4l2_ctrl_handler_init(hdl, 11);

                创建v4l2_ctrl 并放入到v4l2_ctrl_handler链表
                v4l2_ctrl_new_std()
                ...
                v4l2_ctrl_new_custom()

                dev->v4l2_dev.ctrl_handler = hdl;

                dev->v4l2_dev : v4l2_device 每一个v4l2设备都用这个结构来描述
                下面的代码是将v4l2_ctrl_handler与video_device进行了关联
            */
            vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
        if (vdev->prio == NULL)
            vdev->prio = &vdev->v4l2_dev->prio;
    }

    #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
    /**
        根据传入的类型type,为选择次设备号、设备节点序号作准备
        可见次设备号是分段使用的。
        minor = i + minor_offset
    */
    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

    mutex_lock(&videodev_lock);//获取互斥锁
    //获取一个没有被使用的设备节点序号
    //如果 上面传入的是 -1,VFL_TYPE_GRABBER,那么他会从0 - 64中选择
    nr = devnode_find(vdev,nr == -1 ? 0 : nr,minor_cnt);
    if (nr == minor_cnt) {
        printk(KERN_ERR "could not get a free device node number\n");
        mutex_unlock(&videodev_lock);
        return -ENFILE;
    }

    /**
        static struct video_device *video_device[256];
        从video_device[]数组中选择一个空缺项,这个空缺项的索引值放到i中
        为下面把video_device放入到video_device[i]中做准备,这个设计和registered_fb[]设计的类似
    */
    for (int i = 0; i < VIDEO_NUM_DEVICES; ++i)
    {
        if (video_device[i] == NULL)
        {
            break;
        }
    }
    //检查i的值有没有超过256,否则出错返回
    if (i == VIDEO_NUM_DEVICES) {
        mutex_unlock(&videodev_lock);
        printk(KERN_ERR "could not get a free minor\n");
        return -ENFILE;
    }
    //设备的次设备号
    vdev->minor = i + minor_offset;
    vdev->num = nr;
    devnode_set(vdev);

    //再一次测试这个video_device[i]是否是空缺项
    WARN_ON(video_device[vdev->minor] != NULL);
    vdev->index = get_index(vdev);
    mutex_unlock(&videodev_lock);//释放互斥锁

    //分配cdev结构体 cdev代表了字符设备的通用信息,通常被嵌入在一个更大的结构体中
    vdev->cdev = cdev_alloc();
    if (vdev->cdev == NULL)
    {
        ret = -ENOMEM;
        goto cleanup;
    }
    /**
        设置file_operations为v4l2_fops
        用户层调用open、mmap、ioctl、read...的时候
        这个v4l2_fops中的对应的方法会响应。

        如 : 
            用户层调用mmap(),
            那么v4l2_fops中的v4l2_mmap()会被调用
            在v4l2_mmap()中,会调用具体设备提供的mmap函数

            static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
            {
                ....
                    ret = vdev->fops->mmap(filp, vm);
                ....
            }
    */
    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;
    }
    //填充video_device中的成员值
    //设置video_device所属的类,会在/sys/class/下创建目录
    vdev->dev.class = &video_class;
    //主设备号
    vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
    if (vdev->parent)
        vdev->dev.parent = vdev->parent;
    //设置设备的名称
    dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);

    /**
        所有的设备注册的时候,都会经过这个方法。
        所有的驱动注册的时候,都会经过driver_register()
        所有的总线注册的时候,都会经过bus_register()
        这三个函数的浅析请见另篇博文
    */
    ret = device_register(&vdev->dev);
    if (ret < 0) {
        printk(KERN_ERR "%s: device_register failed\n", __func__);
        goto cleanup;
    }
    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));

    if (vdev->v4l2_dev)
        v4l2_device_get(vdev->v4l2_dev);

    #if defined(CONFIG_MEDIA_CONTROLLER)


    if (vdev->v4l2_dev && 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.v4l.major = VIDEO_MAJOR;
        vdev->entity.info.v4l.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

    /*将这个unsigned long flags 的第0 位 置1,表示这个video_device 是注册过的了,
    在其他位置,会调用video_is_registeried( ) 来判断,其依据 还是测试这个flags的第0位。
     video_is_registered( )
     {
        test_bit(  V4L2_FL_REGISTERED,  &vdev->flags  ) 
     }
    */
    set_bit(V4L2_FL_REGISTERED, &vdev->flags);

    //获取锁 ---- 访问临界区 -----释放锁
    mutex_lock(&videodev_lock);
    /**
        依据次设备号为下标,将设置好的video_device放入到video_device[]中
        其他函数会 依据次设备号 从这个数组中获取对应的video_device,这个和registered_fb[]设计的类似
        static struct video_device*video_device[256];
    */
    video_device[vdev->minor] = vdev;
    mutex_unlock(&videodev_lock);

}
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

V4L2系列之video_register_device函数分析

感觉写完了其实也没怎么理解这个函数,但是还是这里保存一下,希望大家予以拍砖指导: 下面分析一下video_register_device这个函数:位于include/media/v4l2-d...
  • urshine
  • urshine
  • 2013-04-01 15:00
  • 3632

深入理解l内核v4l2框架之video for linux 2(一)

在看了很多关于v4l2驱动的例程之后,想深入研究下linux内核的v4l2框架,顺便把这些记录下来,以备查用。 Video for Linux 2      随着一些视频或者图像硬件的...
  • ramon1892
  • ramon1892
  • 2012-12-27 15:15
  • 24789

Linux 中索引节点的理解

索引节点是指在许多类Unix文件系统中的一种数据结构。每个索引节点保存了文件系统中的一个文件系统对象的元信息数据,但不包括数据内容或者文件名。        linux中,文件查找不是通过文件名...
  • qq_16681169
  • qq_16681169
  • 2016-01-22 20:25
  • 1410

Linux下V4L2拍照测试用例

一、源码test.c #include #include #include #include int main(){ ////// int fd = open("/dev/video...
  • tankai19880619
  • tankai19880619
  • 2013-08-15 15:33
  • 6325

linux环境下使用V4L2及Qt设计简易照相机

最近放假,比较清闲,正好手上有一个USB的免驱摄像头,想了想打算做一个简易的照相机,后期移植到4412的板子上做实时监控。之后在网上找了找参考资料,发现需要用到V4L2还有其他的一些东西,就研究了几天...
  • define_me_freedom
  • define_me_freedom
  • 2016-07-12 09:20
  • 4134

采用V4L2读取的USB摄像头

Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。在Linux中,视频设备被视为设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。   使用...
  • lucykingljj
  • lucykingljj
  • 2014-12-08 15:28
  • 3281

Qt浅谈之四十Centos下Qt结合v4l2实现的视频显示

v4l2是针对uvc免驱usb设备的编程框架,主要用于采集usb摄像头。 可从网上下载最新的源码(包括v4l2.c和v4l2.h两个文件),本文中修改过。
  • taiyang1987912
  • taiyang1987912
  • 2016-01-20 18:20
  • 3372

深入理解l内核v4l2框架之video for linux 2(一)

在看了很多关于v4l2驱动的例程之后,想深入研究下linux内核的v4l2框架,顺便把这些记录下来,以备查用。 Video for Linux 2      随着一些视频或者图像硬件的复杂化,V...
  • liukun321
  • liukun321
  • 2013-11-21 20:40
  • 1809

摄像头驱动笔记1---V4L2框架分析

转自http://blog.csdn.net/rubyboss/article/details/14053523 1、概述 Video4Linux2是Linux内核中关于视频设备的内核驱动框架,为上...
  • qingkongyeyue
  • qingkongyeyue
  • 2016-08-30 22:07
  • 809

V4L2编程初体验

转自:http://www.cnblogs.com/lixiaoming90/archive/2012/08/25/2657019.html 内容摘要:      Video for Linux t...
  • pan_00_hao
  • pan_00_hao
  • 2013-03-31 21:45
  • 5112
    个人资料
    • 访问:74402次
    • 积分:1666
    • 等级:
    • 排名:千里之外
    • 原创:93篇
    • 转载:0篇
    • 译文:0篇
    • 评论:9条
    博客公告
    最新评论