• V4L2 driver -整体架构


    我的uvc开源地址:gitee-uvc

    • 字符设备驱动程序核心:V4L2本身就是一个字符设备,具有字符设备所有的特性,暴露接口给用户空间。
    • V4L2 驱动核心:主要是构建一个内核中标准视频设备驱动的框架,为视频操作提供统一的接口函数。
    • 平台V4L2设备驱动:在V4L2框架下,根据平台自身的特性实现与平台相关的V4L2驱动部分,包括注册video_device和v4l2_dev。
    • 具体的sensor驱动:主要上电、提供工作时钟、视频图像裁剪、流IO开启等,实现各种设备控制方法供上层调用并注册v4l2_subdev。

    1 从字符设备开始:

    熟悉v4l2用户空间编程的都知道, v4l2编程主要是调用一系列的ioctl函数去对v4l2设备进行打开, 关闭, 查询, 设置等操作. v4l2设备是一个字符设备, 而且其驱动的主要工作就是实现各种各样的ioctl.

    v4l2的整体框架如下图所示:

    V4L2 :video for linux version 2 ,是 linux 里一套标准的视频驱动。本文来分析一下它的核心框架。

    在v4l2的核心中对这个file_operations的实现如下:

    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,
    };
    

    这个v4l2_fops函数最终绑定在一个cdev上, 并注册到系统中。

    v4l2_open为例(代码在kerneldriversmediav4l2-core中):

    /* Override for the open function */
    static int v4l2_open(struct inode *inode, struct file *filp)
    {
    	struct video_device *vdev;
    	int ret = 0;
    
    	/* Check if the video device is available */
    	mutex_lock(&videodev_lock);
    	vdev = video_devdata(filp);
    	/* return ENODEV if the video device has already been removed. */
    	if (vdev == NULL || !video_is_registered(vdev)) {
    		mutex_unlock(&videodev_lock);
    		return -ENODEV;
    	}
    	/* and increase the device refcount */
    	video_get(vdev);
    	mutex_unlock(&videodev_lock);
    	if (vdev->fops->open) {
    		if (video_is_registered(vdev))
    		    //这里就是调用了file_operations的open函数
    			ret = vdev->fops->open(filp);
    		else
    			ret = -ENODEV;
    	}
    
    	if (vdev->debug)
    		printk(KERN_DEBUG "%s: open (%d)
    ",
    			video_device_node_name(vdev), ret);
    	/* decrease the refcount in case of an error */
    	if (ret)
    		video_put(vdev);
    	return ret;
    }
    

    2. video_device结构体:

    video_device结构体用于在/dev目录下生成设备节点文件,把操作设备的接口暴露给用户空间。

    我们是使用video_device来操作的,看看video_device这个结构体:

    struct video_device
    {
    #if defined(CONFIG_MEDIA_CONTROLLER)
    	struct media_entity entity;
    #endif
    	/* 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;
    
    	/* vb2_queue associated with this device node. May be NULL. */
    	struct vb2_queue *queue;
    
    	/* Priority state. If NULL, then v4l2_dev->prio will be used. */
    	struct v4l2_prio_state *prio;
    
    	/* device info */
    	char name[32];
    	int vfl_type;	/* device type */
    	int vfl_dir;	/* receiver, transmitter or m2m */
    	/* '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;
    };
    
    

    3. V4L2_device结构体:

    这个结构体中包含了一个非常重要的结构体v4l2_device

    struct v4l2_device {
    	/* dev->driver_data points to this struct.
    	   Note: dev might be NULL if there is no parent device
    	   as is the case with e.g. ISA devices. */
    	struct device *dev;
    #if defined(CONFIG_MEDIA_CONTROLLER)
    	struct media_device *mdev;
    #endif
    	/* used to keep track of the registered subdevs */
    	struct list_head subdevs;
    	/* lock this struct; can be used by the driver as well if this
    	   struct is embedded into a larger struct. */
    	spinlock_t lock;
    	/* unique device name, by default the driver name + bus ID */
    	char name[V4L2_DEVICE_NAME_SIZE];
    	/* notify callback called by some sub-devices. */
    	void (*notify)(struct v4l2_subdev *sd,
    			unsigned int notification, void *arg);
    	/* The control handler. May be NULL. */
    	struct v4l2_ctrl_handler *ctrl_handler;
    	/* Device's priority state */
    	struct v4l2_prio_state prio;
    	/* BKL replacement mutex. Temporary solution only. */
    	struct mutex ioctl_lock;
    	/* Keep track of the references to this struct. */
    	struct kref ref;
    	/* Release function that is called when the ref count goes to 0. */
    	void (*release)(struct v4l2_device *v4l2_dev);
    };
    

    3.1 v4l2_device的注册和注销:

    int v4l2_device_register(struct device*dev, struct v4l2_device *v4l2_dev)
    
    static void v4l2_device_release(struct kref *ref)
    

    4. v4l2_subdev结构体

    V4l2_subdev代表子设备,包含了子设备的相关属性和操作。先来看下结构体原型:

    struct v4l2_subdev {
     
             struct v4l2_device *v4l2_dev;  //指向父设备
     
             //提供一些控制v4l2设备的接口
     
             const struct v4l2_subdev_ops *ops;
     
             //向V4L2框架提供的接口函数
     
             const struct v4l2_subdev_internal_ops *internal_ops;
     
             //subdev控制接口
     
             struct v4l2_ctrl_handler *ctrl_handler;
     
             /* name must be unique */
     
             charname[V4L2_SUBDEV_NAME_SIZE];
     
             /*subdev device node */
     
             struct video_device *devnode;  
     
    };
    

    其中 list 域作为链表节点链接至 v4l2_dev 指向的 v4l2_device 结构中,这个结构中最重要的成员就是 struct v4l2_subdev_ops *ops,该域包含了 v4l2 设备支持的所有操作,定义如下:

    struct v4l2_subdev_ops {
    	const struct v4l2_subdev_core_ops  *core;   /* 通用操作合集 */
    	const struct v4l2_subdev_tuner_ops *tuner;  /* 调谐器操作合集 */
    	const struct v4l2_subdev_audio_ops *audio;  /* 音频操作合集 */
    	const struct v4l2_subdev_video_ops *video;  /* 视频操作合集 */
    };
    

    v4l2_subdev_core_ops 包含的操作合集是各种类型设备通用的:

    struct v4l2_subdev_core_ops {
    	int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);  /* 获取设备id */
    	int (*log_status)(struct v4l2_subdev *sd);                                      /* 状态消息 */
    	int (*s_config)(struct v4l2_subdev *sd, int irq, void *platform_data);          /* 设置配置信息 */
    	int (*init)(struct v4l2_subdev *sd, u32 val);                                   /* 初始化设备 */
    	int (*load_fw)(struct v4l2_subdev *sd);                                         /* 加载firmware */
    	int (*reset)(struct v4l2_subdev *sd, u32 val);                                  /* 重置设备 */
    	int (*s_gpio)(struct v4l2_subdev *sd, u32 val);                                 /* 设置gpio */
    	int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);            /* 查询设备支持的操作 */
    	int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);               /* 获取当前命令值 */
    	int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);               /* 设置当前命令值 */
    	int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);    /* 获取外置命令值 */
    	int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);    /* 设置外置命令值 */
    	int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
    	int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);            /* 查询操作菜单 */
    	int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm);                         /* 设置数据标准 */
    	long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);             /* 处理特殊命令 */
    #ifdef CONFIG_VIDEO_ADV_DEBUG
    	int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);       /* 获取寄存器值 */
    	int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg);       /* 设置寄存器值 */
    #endif
    };
    

    v4l2_subdev_tuner_ops 包含的操作合集则是调谐器独有的:

    struct v4l2_subdev_tuner_ops {
    	int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type);               /* 设置调谐器模式 */
    	int (*s_radio)(struct v4l2_subdev *sd);                                    /* 设置无线设备信息 */
    	int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);   /* 设置频率 */
    	int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq);   /* 获取频率 */
    	int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);             /* 获取调谐器信息 */
    	int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);             /* 设置调谐器信息 */
    	int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);     /* 获取调幅器信息 */
    	int (*s_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);     /* 设置调幅器信息 */
    	int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);      /* 安装调谐器 */
    	int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);   /* 设置配置信息 */
    	int (*s_standby)(struct v4l2_subdev *sd);                                  /* 设置标准 */
    };
    

    v4l2_subdev_audio_ops 包含的操作合集则是音频部分独有的:

    struct v4l2_subdev_audio_ops {
    	int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq);       /* 设置音频设备频率 */
    	int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq);   /* 设置i2s总线频率 */
    	int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);   /* 设置音频路由 */
    };
    

    v4l2_subdev_video_ops 包含的操作合集则是视频部分独有的:

    struct v4l2_subdev_video_ops {
    	int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config);             /* 设置视频路由 */
    	int (*s_crystal_freq)(struct v4l2_subdev *sd, u32 freq, u32 flags);                      /* 设置设备频率 */
    	int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line);   /* 消隐区信息解码 */
    	int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data);  /* 设置消隐区数据 */
    	int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);        /* 获取消隐区数据 */
    	int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
    	int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);                            /* 设置标准输出 */
    	int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);                               /* 查询标准 */
    	int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);                              /* 获取输入状态 */
    	int (*s_stream)(struct v4l2_subdev *sd, int enable);                                     /* 设置数据流 */
    	int (*enum_fmt)(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmtdesc);                   /* 枚举视频格式 */
    	int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);                           /* 获取视频格式 */
    	int (*try_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);                         /* 尝试设置视频格式 */
    	int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);                           /* 设置视频格式 */
    	int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);                         /* 视频剪辑功能 */
    	int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);                           /* 获取剪辑功能 */
    	int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);                           /* 设置剪辑功能 */
    	int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);                    /* 获取参数 */
    	int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);                    /* 设置参数 */
    	int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);          /* 枚举帧大小 */
    	int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);      /* 枚举帧间隔 */
    };
    

    4.1 subdev的注册和注销

    当我们把v4l2_subdev需要实现的成员都已经实现,就可以调用以下函数把子设备注册到V4L2核心层:

    int v4l2_device_register_subdev(struct v4l2_device*v4l2_dev, struct v4l2_subdev *sd)
    

    当卸载子设备时,可以调用以下函数进行注销:

    void v4l2_device_unregister_subdev(struct v4l2_subdev*sd)
    

    5. 应用层具体流程框架:

    我们使用一张图来体现吧:

  • 相关阅读:
    BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊(分块)
    BZOJ 2648 SJY摆棋子(KD Tree)
    Codeforces Round #441 D. Sorting the Coins(模拟)
    HDU 3400 Line belt (三分套三分)
    HDU 5919 Sequence II(主席树+区间不同数个数+区间第k小)
    HDU 5985 Lucky Coins(概率)
    HDU 5988 Coding Contest(浮点数费用流)
    HDU 5792 World is Exploding(树状数组+离散化)
    HDU 5791 Two(LCS求公共子序列个数)
    HDU 5787 K-wolf Number(数位dp)
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/9486511.html
Copyright © 2020-2023  润新知