vivid源码分析

简介: vivid源码分析

如何编写V4L2驱动

  1. 分配/设置/注册v4l2_device.v4l2_device_register,v4l2_device(辅助作用,提供自旋锁,引用计数等
  2. 分配video_device.video_device_alloc
  3. 设置
    vfd->v4l2_dev

分析vivid.c的open,read,write,ioctl过程

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 const struct v4l2_file_operations vivid_fops = {
    .owner        = THIS_MODULE,
    .open           = v4l2_fh_open,
    .release        = vivid_fop_release,
    .read           = vb2_fop_read,
    .write          = vb2_fop_write,
    .poll        = vb2_fop_poll,
    .unlocked_ioctl = video_ioctl2,
    .mmap           = vb2_fop_mmap,
};

open

app: open(“/dev/video0”,…)

drv: v4l2_fops.v4l2_open

vdev = video_devdata(filp); // 根据次设备号从数组中得到video_device

ret = vdev->fops->open(filp);

vivi_ioctl_ops.open

v4l2_fh_open

/* Override for the open function */
static int v4l2_open(struct inode *inode, struct file *filp)
{
    struct video_device *vdev;
    int ret = 0;
    // 检查视频设备是否可用
    mutex_lock(&videodev_lock);
    vdev = video_devdata(filp);
    // 如果视频设备已经被移除,则返回ENODEV。
    if (vdev == NULL || !video_is_registered(vdev)) {
        mutex_unlock(&videodev_lock);
        return -ENODEV;
    }
    // 增加设备引用计数
    video_get(vdev);
    mutex_unlock(&videodev_lock);
    if (vdev->fops->open) {
        // 如果视频设备已经注册,则调用open函数
        if (video_is_registered(vdev))
            ret = vdev->fops->open(filp);
        else
            ret = -ENODEV;
    }
    if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
        printk(KERN_DEBUG "%s: open (%d)\n",
            video_device_node_name(vdev), ret);
    // 如果出现错误,则减少引用计数
    if (ret)
        video_put(vdev);
    return ret;
}

根据次设备号从数组中得到video_device

vdev = video_devdata(filp);

struct video_device *video_devdata(struct file *file)
{
    return video_device[iminor(file_inode(file))];
}

该数组在__video_register_device中设置

__video_register_device
    get_index

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; // 将设备添加到 video_device 数组中
    mutex_unlock(&videodev_lock); // 解锁

以次设备号为下标存起来

static int get_index(struct video_device *vdev)
{
    /* This can be static since this function is called with the global
       videodev_lock held. */
    static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
    int i;
    // 初始化used数组
    bitmap_zero(used, VIDEO_NUM_DEVICES);
    // 遍历video_device数组,将v4l2_dev相同的设备的index标记在used数组中
    for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
        if (video_device[i] != NULL &&
            video_device[i]->v4l2_dev == vdev->v4l2_dev) {
            set_bit(video_device[i]->index, used);
        }
    }
    // 返回used数组中第一个为0的位的下标
    return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
}

下面调用到vivid_fops中的v4l2_fh_open函数(硬件相关层的函数)

if (vdev->fops->open) {
        // 如果视频设备已经注册,则调用open函数
        if (video_is_registered(vdev))
            ret = vdev->fops->open(filp);
static const struct v4l2_file_operations vivid_fops = {
    .owner        = THIS_MODULE,
    .open           = v4l2_fh_open,
    .release        = vivid_fop_release,
    .read           = vb2_fop_read,
    .write          = vb2_fop_write,
    .poll        = vb2_fop_poll,
    .unlocked_ioctl = video_ioctl2,
    .mmap           = vb2_fop_mmap,
};

read

app: read …

drv: v4l2_fops.v4l2_read

struct video_device *vdev = video_devdata(filp);

ret = vdev->fops->read(filp, buf, sz, off);

// 读取函数的实现
static ssize_t v4l2_read(struct file *filp, char __user *buf,
        size_t sz, loff_t *off)
{
    // 获取video_device结构体
    struct video_device *vdev = video_devdata(filp);
    // 初始化返回值
    int ret = -ENODEV;
    // 检查是否实现了read函数
    if (!vdev->fops->read)
        return -EINVAL;
    // 调用驱动程序的read函数
    if (video_is_registered(vdev))
        ret = vdev->fops->read(filp, buf, sz, off);
    // 打印调试信息
    if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) &&
        (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING))
        printk(KERN_DEBUG "%s: read: %zd (%d)\n",
            video_device_node_name(vdev), sz, ret);
    return ret;
}

ioctl

app: ioctl

drv: v4l2_fops.unlocked_ioctl

v4l2_ioctl

struct video_device *vdev = video_devdata(filp);

ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);

video_ioctl2

video_usercopy(file, cmd, arg, __video_do_ioctl);

__video_do_ioctl

struct video_device *vfd = video_devdata(file);

根据APP传入的cmd来获得、设置"某些属性"

v4l2_ctrl_handler的使用过程:

__video_do_ioctl

struct video_device *vfd = video_devdata(file);

case VIDIOC_QUERYCTRL:
    {
        struct v4l2_queryctrl *p = arg;
        if (vfh && vfh->ctrl_handler)
            ret = v4l2_queryctrl(vfh->ctrl_handler, p);
        else if (vfd->ctrl_handler)  // 在哪设置?在video_register_device
            ret = v4l2_queryctrl(vfd->ctrl_handler, p);
                        // 根据ID在ctrl_handler里找到v4l2_ctrl,返回它的值
// v4l2_ioctl函数的实现
static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    // 获取video_device结构体
    struct video_device *vdev = video_devdata(filp);
    // 初始化返回值
    int ret = -ENODEV;
    // 检查是否实现了unlocked_ioctl函数
    if (vdev->fops->unlocked_ioctl) {
        // 获取锁
        struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
        // 如果获取锁失败,则返回ERESTARTSYS
        if (lock && mutex_lock_interruptible(lock))
            return -ERESTARTSYS;
        // 调用驱动程序的unlocked_ioctl函数
        if (video_is_registered(vdev))
            ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
        // 释放锁
        if (lock)
            mutex_unlock(lock);
    } else if (vdev->fops->ioctl) {
        /* This code path is a replacement for the BKL. It is a major
         * hack but it will have to do for those drivers that are not
         * yet converted to use unlocked_ioctl.
         *
         * All drivers implement struct v4l2_device, so we use the
         * lock defined there to serialize the ioctls.
         *
         * However, if the driver sleeps, then it blocks all ioctls
         * since the lock is still held. This is very common for
         * VIDIOC_DQBUF since that normally waits for a frame to arrive.
         * As a result any other ioctl calls will proceed very, very
         * slowly since each call will have to wait for the VIDIOC_QBUF
         * to finish. Things that should take 0.01s may now take 10-20
         * seconds.
         *
         * The workaround is to *not* take the lock for VIDIOC_DQBUF.
         * This actually works OK for videobuf-based drivers, since
         * videobuf will take its own internal lock.
         */
        // 获取锁
        struct mutex *m = &vdev->v4l2_dev->ioctl_lock;
        // 如果获取锁失败,则返回ERESTARTSYS
        if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
            return -ERESTARTSYS;
        // 调用驱动程序的ioctl函数
        if (video_is_registered(vdev))
            ret = vdev->fops->ioctl(filp, cmd, arg);
        // 释放锁
        if (cmd != VIDIOC_DQBUF)
            mutex_unlock(m);
    } else
        ret = -ENOTTY;
    return ret;
}

// 调用驱动程序的ioctl函数

if (video_is_registered(vdev))

ret = vdev->fops->ioctl(filp, cmd, arg);

/*
 * video_ioctl2 - V4L2 ioctl handler
 *
 * @file: file pointer
 * @cmd: ioctl command
 * @arg: argument
 *
 * This function is the V4L2 ioctl handler. It calls video_usercopy to copy
 * the arguments from user space to kernel space, then calls __video_do_ioctl
 * to handle the ioctl command, and finally copies the results back to user
 * space.
 *
 * Return: 0 on success, negative error code on failure.
 */
long video_ioctl2(struct file *file,
           unsigned int cmd, unsigned long arg)
{
    return video_usercopy(file, cmd, arg, __video_do_ioctl);
}

video_ioctl2

__video_do_ioctl

static long __video_do_ioctl(struct file *file,
        unsigned int cmd, void *arg)
{
    // 获取video_device结构体
    struct video_device *vfd = video_devdata(file);
    // 获取v4l2_ioctl_ops结构体
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
    // 判断是否为只写操作
    bool write_only = false;
    // 定义默认的ioctl信息
    struct v4l2_ioctl_info default_info;
    // 定义ioctl信息
    const struct v4l2_ioctl_info *info;
    // 获取file结构体的私有数据
    void *fh = file->private_data;
    // 获取v4l2_fh结构体
    struct v4l2_fh *vfh = NULL;
    // 获取dev_debug
    int dev_debug = vfd->dev_debug;
    // 定义返回值
    long ret = -ENOTTY;
    // 判断是否有ioctl_ops
    if (ops == NULL) {
        pr_warn("%s: has no ioctl_ops.\n",
                video_device_node_name(vfd));
        return ret;
    }
    // 判断是否使用v4l2_fh
    if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
        vfh = file->private_data;
    // 判断是否为已知的ioctl
    if (v4l2_is_known_ioctl(cmd)) {
        // 获取ioctl信息
        info = &v4l2_ioctls[_IOC_NR(cmd)];
            // 判断是否为有效的ioctl
        if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
            !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
            goto done;
        // 判断是否需要检查优先级
        if (vfh && (info->flags & INFO_FL_PRIO)) {
            ret = v4l2_prio_check(vfd->prio, vfh->prio);
            if (ret)
                goto done;
        }
    } else {
        // 设置默认的ioctl信息
        default_info.ioctl = cmd;
        default_info.flags = 0;
        default_info.debug = v4l_print_default;
        info = &default_info;
    }
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
    // 判断是否为标准的ioctl
    if (info->flags & INFO_FL_STD) {
        // 定义vidioc_op函数指针类型
        typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
        // 获取ioctl_ops
        const void *p = vfd->ioctl_ops;
        // 获取vidioc函数指针
        const vidioc_op *vidioc = p + info->u.offset;
        // 调用vidioc函数
        ret = (*vidioc)(file, fh, arg);
    } 
    // 判断是否为函数ioctl
    else if (info->flags & INFO_FL_FUNC) {
        // 调用函数ioctl
        ret = info->u.func(ops, file, fh, arg);
    } 
    // 判断是否有默认的ioctl
    else if (!ops->vidioc_default) {
        ret = -ENOTTY;
    } 
    // 调用默认的ioctl
    else {
        ret = ops->vidioc_default(file, fh,
            vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
            cmd, arg);
    }
done:
    // 判断是否需要打印调试信息
    if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
        // 判断是否需要打印流信息
        if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
            (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
            return ret;
        // 打印ioctl信息
        v4l_printk_ioctl(video_device_node_name(vfd), cmd);
        if (ret < 0)
            pr_cont(": error %ld", ret);
        if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG))
            pr_cont("\n");
        else if (_IOC_DIR(cmd) == _IOC_NONE)
            info->debug(arg, write_only);
        else {
            pr_cont(": ");
            info->debug(arg, write_only);
        }
    }
}
    return ret;

v4l2_ctrl_handler使用过程

static long __video_do_ioctl(struct file *file,
        unsigned int cmd, void *arg)
{
    // 获取video_device结构体
    struct video_device *vfd = video_devdata(file);
    // 获取v4l2_ioctl_ops结构体
    const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
    // 判断是否为只写操作
    bool write_only = false;
    // 定义默认的ioctl信息
    struct v4l2_ioctl_info default_info;
    // 定义ioctl信息
    const struct v4l2_ioctl_info *info;
    // 获取file结构体的私有数据
    void *fh = file->private_data;
    // 获取v4l2_fh结构体
    struct v4l2_fh *vfh = NULL;
    // 获取dev_debug
    int dev_debug = vfd->dev_debug;
    // 定义返回值
    long ret = -ENOTTY;
    // 判断是否有ioctl_ops
    if (ops == NULL) {
        pr_warn("%s: has no ioctl_ops.\n",
                video_device_node_name(vfd));
        return ret;
    }
    // 判断是否使用v4l2_fh
    if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags))
        vfh = file->private_data;
    // 判断是否为已知的ioctl
    if (v4l2_is_known_ioctl(cmd)) {
        // 获取ioctl信息
        info = &v4l2_ioctls[_IOC_NR(cmd)];
            // 判断是否为有效的ioctl
        if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
            !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
            goto done;
        // 判断是否需要检查优先级
        if (vfh && (info->flags & INFO_FL_PRIO)) {
            ret = v4l2_prio_check(vfd->prio, vfh->prio);
            if (ret)
                goto done;
        }
    } else {
        // 设置默认的ioctl信息
        default_info.ioctl = cmd;
        default_info.flags = 0;
        default_info.debug = v4l_print_default;
        info = &default_info;
    }
write_only = _IOC_DIR(cmd) == _IOC_WRITE;
    // 判断是否为标准的ioctl
    if (info->flags & INFO_FL_STD) {
        // 定义vidioc_op函数指针类型
        typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
        // 获取ioctl_ops
        const void *p = vfd->ioctl_ops;
        // 获取vidioc函数指针
        const vidioc_op *vidioc = p + info->u.offset;
        // 调用vidioc函数
        ret = (*vidioc)(file, fh, arg);
    } 
    // 判断是否为函数ioctl
    else if (info->flags & INFO_FL_FUNC) {
        // 调用函数ioctl
        ret = info->u.func(ops, file, fh, arg);
    } 
    // 判断是否有默认的ioctl
    else if (!ops->vidioc_default) {
        ret = -ENOTTY;
    } 
    // 调用默认的ioctl
    else {
        ret = ops->vidioc_default(file, fh,
            vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
            cmd, arg);
    }
done:
    // 判断是否需要打印调试信息
    if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) {
        // 判断是否需要打印流信息
        if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) &&
            (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF))
            return ret;
        // 打印ioctl信息
        v4l_printk_ioctl(video_device_node_name(vfd), cmd);
        if (ret < 0)
            pr_cont(": error %ld", ret);
        if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG))
            pr_cont("\n");
        else if (_IOC_DIR(cmd) == _IOC_NONE)
            info->debug(arg, write_only);
        else {
            pr_cont(": ");
            info->debug(arg, write_only);
        }
    }
    return ret;
}

// 判断是否为有效的ioctl
        if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
            !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
            goto done;
ctrl_handler在video_register_device中设置

// 设置设备类型
vdev->vfl_type = type;
// 初始化字符设备指针
vdev->cdev = NULL;
// 如果设备的父设备指针为空,则将其指向 v4l2_dev 的设备指针
if (vdev->dev_parent == NULL)
    vdev->dev_parent = vdev->v4l2_dev->dev;
// 如果控制处理程序指针为空,则将其指向 v4l2_dev 的控制处理程序指针
if (vdev->ctrl_handler == NULL)
    vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* 如果优先级状态指针为空,则使用 v4l2_device 的优先级状态。*/
if (vdev->prio == NULL)
    vdev->prio = &vdev->v4l2_dev->prio;

在ctrl_handler里找到v4l2_ctrl,返回它的值


目录
相关文章
|
4月前
|
存储 缓存 移动开发
EaselJS 源码分析系列--第二篇
EaselJS 源码分析系列--第二篇
|
存储 算法
TreadLocal源码分析
TreadLocal源码分析
|
存储 缓存 监控
线程池原理初探以及源码分析(详解)
线程池原理初探以及源码分析(详解)
109 0
|
存储 Java 应用服务中间件
SpringMVC源码分析 RequestContextHolder使用与源码分析
SpringMVC源码分析 RequestContextHolder使用与源码分析
SpringMVC源码分析 RequestContextHolder使用与源码分析
|
存储 安全 Java
《从面试题来看源码》-LinkedBlockingQueue 源码分析
《从面试题来看源码》-LinkedBlockingQueue 源码分析
《从面试题来看源码》-LinkedBlockingQueue 源码分析
|
存储 算法 测试技术
源码分析 RateLimiter SmoothBursty 实现原理(文末附流程图)
源码分析 RateLimiter SmoothBursty 实现原理(文末附流程图)
源码分析 RateLimiter SmoothBursty 实现原理(文末附流程图)
|
存储 算法
源码分析RateLimiter SmoothWarmingUp 实现原理(文末附流程图)
源码分析RateLimiter SmoothWarmingUp 实现原理(文末附流程图)
源码分析RateLimiter SmoothWarmingUp 实现原理(文末附流程图)
|
SQL XML Java
Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
总的来说,这篇文章涉及到的内容都是比较简单的,通过这篇文章是希望让大家对Spring中的数据访问有一定了解,相当于热身吧,后面的文章难度会加大,下篇文章我们将介绍更高级的数据访问,`myBatis`的使用以及基本原理、事务管理以及它跟Spring的整合原理。
438 0
|
iOS开发
fishhook源码分析
最早了解到[fishhook](https://github.com/facebook/fishhook)是看了下面两篇文章之后,顿时让我觉得这是一个非常好的东西。总共210行代码,收获了1500+个star,神作啊。 1. [iOS Lazy Binding](http://www.atatech.org/articles/68014),使用fishhook拦截NSSetUncaughtE
2449 0

热门文章

最新文章