一、V4L2应用层调用流程
二、V4L2设备注册
三、video设备初始化
四、V4L2 control结构框架图
五、v4l2 ctrl 函数初始化—增加标准接口v4l2_ctrl_new_std
六、v4l2 ctrl 函数初始化—增加自定义接口v4l2_ctrl_new_custom
七、V4L2 ioctl 标准接口 调用流程
八、V4L2 ioctl 控制接口 调用流程
video_device结构体
struct video_device
{
#if defined (CONFIG_MEDIA_CONTROLLER)
struct media_entity entity;
struct media_intf_devnode *intf_devnode;
struct media_pipeline pipe;
#endif
const struct v4l2_file_operations *fops;
u32 device_caps;
/* sysfs /
struct device dev;
struct cdev cdev;
struct v4l2_device v4l2_dev;
struct device dev_parent;
struct v4l2_ctrl_handler ctrl_handler;
struct vb2_queue queue;
struct v4l2_prio_state prio;
/ device info /
char name[32];
int vfl_type;
int vfl_dir;
int minor;
u16 num;
unsigned long flags;
int index;
/ V4L2 file handles /
spinlock_t fh_lock;
struct list_head fh_list;
int dev_debug;
v4l2_std_id tvnorms;
/ callbacks /
void (release)(struct video_device vdev);
const struct v4l2_ioctl_ops ioctl_ops;
DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE);
struct mutex *lock;
};
video设备也是一个字符设备,具备字符设备的open、close、read、wirte、ioctl功能
v4l2_file_operations结构体初始化定义
struct v4l2_file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_close,
.read = xxx_read,
.poll = xxx_p_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = xxx_mmap,
};
v4l2_ioctl_ops结构体初始化定义
struct v4l2_ioctl_ops xxx_v4l2_ioctl_ops = { .vidioc_querycap = xxx_v4l2_querycap,
<span class="token comment">/* Per-stream config operations */</span> <span class="token punctuation">.</span>vidioc_g_fmt_vid_cap_mplane <span class="token operator">=</span> xxx_v4l2_g_fmt_vid_cap<span class="token punctuation">,</span> <span class="token punctuation">.</span>vidioc_enum_fmt_vid_cap <span class="token operator">=</span> xxx_v4l2_enum_fmt_vid_cap<span class="token punctuation">,</span> <span class="token punctuation">.</span>vidioc_try_fmt_vid_cap_mplane <span class="token operator">=</span> xxx_v4l2_try_fmt_vid_cap<span class="token punctuation">,</span> <span class="token punctuation">.</span>vidioc_s_fmt_vid_cap_mplane <span class="token operator">=</span> xxx_v4l2_s_fmt_vid_cap<span class="token punctuation">,</span>
.vidioc_g_fmt_vid_cap = xxx_v4l2_g_fmt_vid_cap,
.vidioc_enum_fmt_vid_cap = xxx_v4l2_enum_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = xxx_v4l2_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = xxx_v4l2_s_fmt_vid_cap,
.vidioc_enum_framesizes = xxx_v4l2_enum_framesizes,
/* Per-stream control operations /
.vidioc_streamon = xxx_v4l2_streamon,
.vidioc_streamoff = xxx_v4l2_streamoff,
/ input control /
.vidioc_enum_input = xxx_v4l2_enum_input,
.vidioc_g_input = xxx_v4l2_g_input,
.vidioc_s_input = xxx_v4l2_s_input,
/ vb2 customization for multi-stream support /
.vidioc_reqbufs = xxx_v4l2_reqbufs,
.vidioc_querybuf = xxx_v4l2_querybuf,
.vidioc_qbuf = xxx_v4l2_qbuf,
.vidioc_dqbuf = xxx_v4l2_dqbuf,
/ v4l2 event ioctls /
.vidioc_log_status = xxx_ctrl_log_status,
.vidioc_subscribe_event = xxx_ctrl_subscribe_event,
.vidioc_unsubscribe_event = xxx_event_unsubscribe,
/ crop ioctls */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
.vidioc_g_selection = xxx_v4l2_g_selection,
.vidioc_s_selection = xxx_v4l2_s_selection,
.vidioc_g_pixelaspect = xxx_v4l2_g_pixelaspect,
#else
.vidioc_cropcap = xxx_v4l2_cropcap,
.vidioc_g_crop = xxx_v4l2_g_crop,
.vidioc_s_crop = xxx_v4l2_s_crop,
#endif
.vidioc_expbuf = xxx_v4l2_expbuf,
};
以上的xxx函数都是各个平台最终实现的函数功能,左边的由用户程序调用到kernel驱动最终调用到平台函数
video_register_device注册
struct video_device *vdev;
vdev= &dev->video_dev;
strlcpy( vfd->name, "xxx", sizeof( vfd->name ) );
vdev->fops = &xxx_v4l2_fops;
vdev->ioctl_ops = &xxx_v4l2_ioctl_ops;
vdev->release = video_device_release_empty;
vdev->v4l2_dev = dev->v4l2_dev;
vdev->queue = NULL; // queue will be customized in file handle
vdev->tvnorms = tvnorms_cap;
vdev->vfl_type = VFL_TYPE_GRABBER;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
//VFL_TYPE_GRABBER代表注册的是video节点
//0代表的是注册video0节点
video_register_device( vdev, VFL_TYPE_GRABBER, 0);//代表创建注册video0节点
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_use, struct module *owner) { ... /* v4l2_fh support */ spin_lock_init(&vdev->fh_lock); INIT_LIST_HEAD(&vdev->fh_list); switch(type) { case VFL_TYPE_GRABBER: name_base = "video"; break; } ... vdev->vfl_type = type; vdev->cdev = NULL; if (vdev->dev_parent == NULL) vdev->dev_parent = vdev->v4l2_dev->dev; if (vdev->ctrl_handler == NULL) vdev->ctrl_handler = vdev->vdev_dev->ctrl_handler; if (vdev->prio == NULL) vdev->prio = &vdev->v4l2_dev->prio; switch (type) { case VFL_TYPE_GRABBER: minor_offset = 0; minor_cnt = 64; break; } ... vdev->minor = i + minor_offset; vdev->num = nr; devnode_set(vdev); video_device[vdev->minor] = vdev;//把vdev保存到了 video_device[x] ... vdev->cdev->ops = &v4l2_fops; ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); vdev->dev.parent = vdev->dev_parent; dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); }