Chinaunix首页 | 论坛 | 博客
  • 博客访问: 713516
  • 博文数量: 240
  • 博客积分: 3616
  • 博客等级: 大校
  • 技术积分: 2663
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-21 23:59
文章分类

全部博文(240)

文章存档

2013年(6)

2012年(80)

2011年(119)

2010年(35)

分类:

2012-10-16 23:54:49

原文地址:V4L2机制 作者:steven_miao

V4L2机制

谨以此文纪念过往的岁月

一.前言
在上一篇中主要讲述了V4L2中几个比较中要的cmd,那在该篇中来看一下V4L2真正的面目,亦如以前看input子系统时一样,只有在知道了这些东东的内涵后才会知道这些东东真正的用途。请记住我们不是内核API调用工程师,而是驱动工程师,唯有真正的理解内核驱动才会有真正的突破,否则我们就仅仅是一个内核API调用工程师。

二.V4L2的本质
V4L2与input类似都是自己均会注册一个设备和驱动,然后再提供一些接口即API供驱动工程师使用。

2.1 V4L2设备注册以及注销
在v4l2-dev.c中实现了v4l2父设备的注册。
static int __init videodev_init(void)
{
    dev_t dev = MKDEV(VIDEO_MAJOR, 0);
    int ret;

    ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);   --注册一个主设备号
    if (ret < 0) {
        return ret;
    }
    ret = class_register(&video_class);                                 --注册设备类
    if (ret < 0) {
        unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
        return -EIO;
    }
    return 0;
}

static void __exit videodev_exit(void)
{
    dev_t dev = MKDEV(VIDEO_MAJOR, 0);
    class_unregister(&video_class);                             --注销设备类
    unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);           --注销设备号
}
上面就是v4l2核心的注册。

2.2 video设备的注册
那来看一下一个video设备的注册,即在v4l2主设备号下创建一个从设备。
int video_register_device_index(struct video_device *vfd, int type, int nr,int index)
{
    int i = 0;
    int ret;
    int minor_offset = 0;
    int minor_cnt = VIDEO_NUM_DEVICES;
    const char *name_base;
    void *priv = video_get_drvdata(vfd);

    /* the release callback MUST be present */
    BUG_ON(!vfd->release);

    if (vfd == NULL)
        return -EINVAL;

    switch (type) {
    case VFL_TYPE_GRABBER:
        name_base = "video";
        break;
    case VFL_TYPE_VTX:
        name_base = "vtx";
        break;
    case VFL_TYPE_VBI:
        name_base = "vbi";
        break;
    case VFL_TYPE_RADIO:
        name_base = "radio";
        break;
    default:
        printk(KERN_ERR "%s called with unknown type: %d\n",__func__, type);
        return -EINVAL;
    }

    vfd->vfl_type = type;

#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES    --以下是根据不同的设备类型来给从设备号分类
    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_VTX:
        minor_offset = 192;
        minor_cnt = 32;
        break;
    case VFL_TYPE_VBI:
        minor_offset = 224;
        minor_cnt = 32;
        break;
    default:
        minor_offset = 128;
        minor_cnt = 64;
        break;
    }
#endif

    cdev_init(&vfd->cdev, vfd->fops);        --很明显初始化一个cdev
    vfd->cdev.owner = vfd->fops->owner;
    
    mutex_lock(&videodev_lock);
    nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr);  --下面是寻找空的从设备号
    if (nr == minor_cnt)
        nr = find_first_zero_bit(video_nums[type], minor_cnt);
    if (nr == minor_cnt) {
        printk(KERN_ERR "could not get a free kernel number\n");
        mutex_unlock(&videodev_lock);
        return -ENFILE;
    }
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
    i = nr;
#else
    /* The kernel number and minor numbers are independent */
    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
    vfd->minor = i + minor_offset;
    vfd->num = nr;
    set_bit(nr, video_nums[type]);
    BUG_ON(video_device[vfd->minor]);
    video_device[vfd->minor] = vfd;        --用全局变量来存储

    ret = get_index(vfd, index);          --根据父流来查询空的index
    vfd->index = ret;

    mutex_unlock(&videodev_lock);

    if (ret < 0) {
        printk(KERN_ERR "%s: get_index failed\n", __func__);
        goto fail_minor;
    }

    ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1);  --添加cdev
    if (ret < 0) {
        printk(KERN_ERR "%s: cdev_add failed\n", __func__);
        goto fail_minor;
    }
    /* sysfs class */
    memset(&vfd->dev, 0, sizeof(vfd->dev));
    video_set_drvdata(vfd, priv);
    vfd->dev.class = &video_class;                           --设置父类
    vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor);
    if (vfd->parent)
        vfd->dev.parent = vfd->parent;
    sprintf(vfd->dev.bus_id, "%s%d", name_base, nr);
    ret = device_register(&vfd->dev);                       --注册设备
    if (ret < 0) {
        printk(KERN_ERR "%s: device_register failed\n", __func__);
        goto del_cdev;
    }
    vfd->cdev_release = vfd->cdev.kobj.ktype->release;      --这两段code比较难懂,其实还是需要回过头去看kobj等的真正含义。
    vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default;
    return 0;

del_cdev:
    cdev_del(&vfd->cdev);

fail_minor:
    mutex_lock(&videodev_lock);
    video_device[vfd->minor] = NULL;
    clear_bit(vfd->num, video_nums[type]);
    mutex_unlock(&videodev_lock);
    vfd->minor = -1;
    return ret;
}

到此为止那回过头去看v4l2机制,这些东西都是在最基础的cdev的类型上构造出一个更好的平台去使用。对于字符类型的驱动而言,其根本均是cdev。
阅读(575) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~