• V4L2学习(三)框架分析


    整个v4l2的框架分为三层:

        在应用层,我们可以在 /dev 目录发现 video0 类似的设备节点,上层的摄像头程序打开设备节点进行数据捕获,显示视频画面。设备节点的名字很统一,video0 video1 video2...这些设备节点在是核心层注册。

        核心层 v4l2-dev.c,承上启下,对于每一个硬件相关层注册进来的设备,设置一个统一的接口 v4l2_fops ,既然是统一的接口必然不是具体的视频设备的操作函数,应用层调用 v4l2_fops 中的函数最终将调用到硬件相关层的 video_device 的 fops 。

        硬件相关层,与具体的视频硬件打交道,分配、设置、注册 video_device 结构体。

    static int __init videodev_init(void)
    {
        /* 申请设备号,留给 video 设备使用 */
        dev_t dev = MKDEV(VIDEO_MAJOR, 0);
        ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
        /* 创建 video 类 */
        ret = class_register(&video_class);
        return 0;
    }
    struct video_device
    {
        /* device ops */
        const struct v4l2_file_operations *fops;
     
        /* sysfs */
        struct device dev;        /* v4l device */
        struct cdev *cdev;        /* character device */
     
        /* Set either parent or v4l2_dev if your driver uses v4l2_device */
        struct device *parent;            /* device parent */
        struct v4l2_device *v4l2_dev;    /* v4l2_device parent */
     
        /* Control handler associated with this device node. May be NULL. */
        struct v4l2_ctrl_handler *ctrl_handler;
     
        /* Priority state. If NULL, then v4l2_dev->prio will be used. */
        struct v4l2_prio_state *prio;
     
        /* device info */
        char name[32];
        int vfl_type;
        /* 'minor' is set to -1 if the registration failed */
        int minor;
        u16 num;
        /* use bitops to set/clear/test flags */
        unsigned long flags;
        /* attribute to differentiate multiple indices on one physical device */
        int index;
     
        /* V4L2 file handles */
        spinlock_t        fh_lock; /* Lock for all v4l2_fhs */
        struct list_head    fh_list; /* List of struct v4l2_fh */
     
        int debug;            /* Activates debug level*/
     
        /* Video standard vars */
        v4l2_std_id tvnorms;        /* Supported tv norms */
        v4l2_std_id current_norm;    /* Current tvnorm */
     
        /* callbacks */
        void (*release)(struct video_device *vdev);
     
        /* ioctl callbacks */
        const struct v4l2_ioctl_ops *ioctl_ops;
        DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
     
        /* serialization lock */
        DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
        struct mutex *lock;
    };
    struct v4l2_device {
        struct device *dev;
        /* used to keep track of the registered subdevs */
        struct list_head subdevs;
        spinlock_t lock;
        char name[V4L2_DEVICE_NAME_SIZE];
        void (*notify)(struct v4l2_subdev *sd, unsigned int notification, void *arg);
        struct v4l2_ctrl_handler *ctrl_handler;
        struct v4l2_prio_state prio;
        struct mutex ioctl_lock;
        struct kref ref;
        void (*release)(struct v4l2_device *v4l2_dev);
    };
    static inline int __must_check video_register_device(struct video_device *vdev,
            int type, int nr)
    {
        return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
    }
     
    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;
     
        /* 视频设备的设备节点一般为 video0 ..video 就是由此而来 */
        switch (type) {
        case VFL_TYPE_GRABBER:
            name_base = "video";
            break;
        case VFL_TYPE_VBI:
            name_base = "vbi";
            break;
        ...
        }
     
        vdev->vfl_type = type;
        vdev->cdev = NULL;
     
        /* Part 2: find a free minor, device node number and device index. */
    #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
        switch (type) {
        case VFL_TYPE_GRABBER:
            minor_offset = 0;
            minor_cnt = 64;
            break;
        ...
    #endif
     
        /* 寻找一个空的项,这个好像并不重要 */
        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
    ");
            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
        /* 在全局 video_deivce 数组中寻找一个空的项,下标+minor_offset作为设备的次设备号 */
        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
    ");
            return -ENFILE;
        }
    #endif
        vdev->minor = i + minor_offset;
        vdev->num = nr;
        devnode_set(vdev);
     
        if (vdev->ioctl_ops)
            determine_valid_ioctls(vdev);
     
        /* 注册字符设备 */
        vdev->cdev = cdev_alloc();
        vdev->cdev->ops = &v4l2_fops;
        vdev->cdev->owner = owner;
        ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
        
        /* 得把device注册进内核,mdev才能自动创建设备节点,/dev 目录下的video0 等就是来自这里 */
        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);
        ret = device_register(&vdev->dev);
     
        vdev->dev.release = v4l2_device_release;
     
        /* Part 6: Activate this minor. The char device can now be used. */
        set_bit(V4L2_FL_REGISTERED, &vdev->flags);
        mutex_lock(&videodev_lock);
        video_device[vdev->minor] = vdev;
        mutex_unlock(&videodev_lock);
     
        return 0;
     
    }
    EXPORT_SYMBOL(__video_register_device);
    static const struct file_operations v4l2_fops = {
        .owner = THIS_MODULE,
        .read = v4l2_read,
        .write = v4l2_write,
        .open = v4l2_open,
        .get_unmapped_area = v4l2_get_unmapped_area,
        .mmap = v4l2_mmap,
        .unlocked_ioctl = v4l2_ioctl,
    #ifdef CONFIG_COMPAT
        .compat_ioctl = v4l2_compat_ioctl32,
    #endif
        .release = v4l2_release,
        .poll = v4l2_poll,
        .llseek = no_llseek,
    };
    static int v4l2_open(struct inode *inode, struct file *filp)
    {
        struct video_device *vdev = video_devdata(filp);
        if (vdev->fops->open) {
     
            if (video_is_registered(vdev))
                ret = vdev->fops->open(filp);
        }
    }
    static ssize_t v4l2_read(struct file *filp, char __user *buf,
            size_t sz, loff_t *off)
    {
        struct video_device *vdev = video_devdata(filp);
        if (!vdev->fops->read)
            return -EINVAL;
        if (video_is_registered(vdev))
            ret = vdev->fops->read(filp, buf, sz, off);
    }
    static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
    {
        struct video_device *vdev = video_devdata(filp);
        if (!vdev->fops->mmap)
            return ret;
        ret = vdev->fops->mmap(filp, vm);
    }
     
    static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        struct video_device *vdev = video_devdata(filp);
        if (vdev->fops->unlocked_ioctl) {
            if (video_is_registered(vdev))
                ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
        } else if (vdev->fops->ioctl) {
            if (video_is_registered(vdev))
                ret = vdev->fops->ioctl(filp, cmd, arg);
        } 
    }
  • 相关阅读:
    Ubuntu 16.04安装迅雷(兼容性不高)
    Ubuntu 16.04安装QQ(不一定成功)
    Ubuntu查看隐藏文件夹的方法
    Ubuntu下非常规方法安装绿色软件(压缩包)
    Ubuntu下常规方法安装软件
    Ubuntu 16.04下截图工具Shutter
    java中 awt Graphics2D
    Vue2.0总结———vue使用过程常见的一些问题
    MySQL 中隔离级别 RC 与 RR 的区别
    DBAplus社群线上分享----Sharding-Sphere之Proxy初探
  • 原文地址:https://www.cnblogs.com/Lxk0825/p/10400013.html
Copyright © 2020-2023  润新知